From d8da4c7f1f2f9e50852243e4b4121af21ee2b7c6 Mon Sep 17 00:00:00 2001 From: Polprzewodnikowy Date: Fri, 5 Feb 2021 22:38:17 +0100 Subject: [PATCH] command path works, data and dma not --- .gitignore | 1 + fw/SummerCart64.qsf | 17 +- fw/rtl/glue/device_arbiter.v | 19 +- fw/rtl/sd/sd_clk.v | 107 ++++++++ fw/rtl/sd/sd_cmd.v | 249 ++++++++++++++++++ fw/rtl/sd/sd_crc_16.v | 26 ++ fw/rtl/sd/sd_crc_7.v | 24 ++ fw/rtl/sd/sd_dat.v | 28 ++ fw/rtl/sd/sd_dma.v | 72 ++---- fw/rtl/sd/sd_fifo.v | 56 ++++ fw/rtl/sd/sd_interface.v | 482 +++++++++++++++++------------------ fw/rtl/sd/sd_regs.v | 222 ++++++++++++++++ fw/rtl/top.v | 30 +-- 13 files changed, 998 insertions(+), 335 deletions(-) create mode 100644 fw/rtl/sd/sd_clk.v create mode 100644 fw/rtl/sd/sd_cmd.v create mode 100644 fw/rtl/sd/sd_crc_16.v create mode 100644 fw/rtl/sd/sd_crc_7.v create mode 100644 fw/rtl/sd/sd_dat.v create mode 100644 fw/rtl/sd/sd_fifo.v create mode 100644 fw/rtl/sd/sd_regs.v diff --git a/.gitignore b/.gitignore index ce61271..d36fec2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /packages **/.vscode +/sw/test diff --git a/fw/SummerCart64.qsf b/fw/SummerCart64.qsf index da11885..5c847f7 100644 --- a/fw/SummerCart64.qsf +++ b/fw/SummerCart64.qsf @@ -64,8 +64,15 @@ set_global_assignment -name VERILOG_FILE rtl/memory/memory_sdram.v set_global_assignment -name VERILOG_FILE rtl/n64/n64_bank_decoder.v set_global_assignment -name VERILOG_FILE rtl/n64/n64_pi.v set_global_assignment -name VERILOG_FILE rtl/n64/n64_si.v +set_global_assignment -name VERILOG_FILE rtl/sd/sd_clk.v +set_global_assignment -name VERILOG_FILE rtl/sd/sd_cmd.v +set_global_assignment -name VERILOG_FILE rtl/sd/sd_crc_16.v +set_global_assignment -name VERILOG_FILE rtl/sd/sd_crc_7.v +set_global_assignment -name VERILOG_FILE rtl/sd/sd_dat.v set_global_assignment -name VERILOG_FILE rtl/sd/sd_dma.v +set_global_assignment -name VERILOG_FILE rtl/sd/sd_fifo.v set_global_assignment -name VERILOG_FILE rtl/sd/sd_interface.v +set_global_assignment -name VERILOG_FILE rtl/sd/sd_regs.v set_global_assignment -name VERILOG_FILE rtl/top.v set_global_assignment -name VERILOG_FILE rtl/usb/usb_ftdi_fsi.v set_global_assignment -name VERILOG_FILE rtl/usb/usb_pc.v @@ -91,8 +98,6 @@ set_location_assignment PIN_25 -to i_n64_nmi set_location_assignment PIN_26 -to i_clk set_location_assignment PIN_27 -to i_n64_reset set_location_assignment PIN_28 -to i_n64_si_clk -set_location_assignment PIN_29 -to i_n64_cic_clk -set_location_assignment PIN_30 -to io_n64_cic_dq set_location_assignment PIN_32 -to io_n64_pi_ad[7] set_location_assignment PIN_33 -to io_n64_pi_ad[8] set_location_assignment PIN_38 -to io_n64_pi_ad[6] @@ -161,12 +166,6 @@ set_location_assignment PIN_123 -to io_sram_dq[3] set_location_assignment PIN_124 -to o_sram_cs set_location_assignment PIN_126 -to io_sram_dq[1] set_location_assignment PIN_127 -to io_sram_dq[2] -set_location_assignment PIN_130 -to io_flash_dq[0] -set_location_assignment PIN_131 -to o_flash_clk -set_location_assignment PIN_132 -to io_flash_dq[3] -set_location_assignment PIN_134 -to o_flash_cs -set_location_assignment PIN_135 -to io_flash_dq[1] -set_location_assignment PIN_136 -to io_flash_dq[2] set_location_assignment PIN_138 -to io_pmod[0] set_location_assignment PIN_140 -to io_pmod[1] set_location_assignment PIN_141 -to io_pmod[2] @@ -257,7 +256,6 @@ set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" - # Fitter Assignments # ================== - set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_cic_clk set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_nmi set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_aleh set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_alel @@ -265,7 +263,6 @@ set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" - set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_pi_write set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_reset set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to i_n64_si_clk - set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to io_n64_cic_dq set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to io_n64_si_dq set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to i_clk set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "pll:sys_pll|altpll:altpll_component|pll_altpll:auto_generated|wire_pll1_clk[0]" diff --git a/fw/rtl/glue/device_arbiter.v b/fw/rtl/glue/device_arbiter.v index 993cae3..8ec7690 100644 --- a/fw/rtl/glue/device_arbiter.v +++ b/fw/rtl/glue/device_arbiter.v @@ -32,21 +32,24 @@ module device_arbiter #( reg [(NUM_CONTROLLERS - 1):0] r_ack_fifo_mem [0:(ACK_FIFO_LENGTH - 1)]; - reg [(FIFO_ADDRESS_WIDTH - 1):0] r_ack_fifo_wrptr; - reg [(FIFO_ADDRESS_WIDTH - 1):0] r_ack_fifo_rdptr; + reg [FIFO_ADDRESS_WIDTH:0] r_ack_fifo_wrptr; + reg [FIFO_ADDRESS_WIDTH:0] r_ack_fifo_rdptr; wire w_ack_fifo_wrreq = |(r_request_successful & ~i_write); wire w_ack_fifo_rdreq = i_device_ack; - - wire w_ack_fifo_full = (r_ack_fifo_wrptr + 1'd1) == r_ack_fifo_rdptr; + + wire w_empty = r_ack_fifo_wrptr[FIFO_ADDRESS_WIDTH] == r_ack_fifo_rdptr[FIFO_ADDRESS_WIDTH]; + wire w_full_or_empty = r_ack_fifo_wrptr[(FIFO_ADDRESS_WIDTH - 1):0] == r_ack_fifo_rdptr[(FIFO_ADDRESS_WIDTH - 1):0]; + + wire w_ack_fifo_full = !w_empty && w_full_or_empty; always @(posedge i_clk) begin if (i_reset) begin - r_ack_fifo_wrptr <= {FIFO_ADDRESS_WIDTH{1'b0}}; - r_ack_fifo_rdptr <= {FIFO_ADDRESS_WIDTH{1'b0}}; + r_ack_fifo_wrptr <= {(FIFO_ADDRESS_WIDTH + 1){1'b0}}; + r_ack_fifo_rdptr <= {(FIFO_ADDRESS_WIDTH + 1){1'b0}}; end else begin if (w_ack_fifo_wrreq) begin - r_ack_fifo_mem[r_ack_fifo_wrptr] <= r_request_successful & ~i_write; + r_ack_fifo_mem[r_ack_fifo_wrptr[(FIFO_ADDRESS_WIDTH - 1):0]] <= r_request_successful & ~i_write; r_ack_fifo_wrptr <= r_ack_fifo_wrptr + 1'd1; end if (w_ack_fifo_rdreq) begin @@ -66,7 +69,7 @@ module device_arbiter #( end r_request_successful = r_request & ~o_busy; - o_ack = {NUM_CONTROLLERS{i_device_ack}} & r_ack_fifo_mem[r_ack_fifo_rdptr]; + o_ack = {NUM_CONTROLLERS{i_device_ack}} & r_ack_fifo_mem[r_ack_fifo_rdptr[(FIFO_ADDRESS_WIDTH - 1):0]]; o_data = {NUM_CONTROLLERS{i_device_data}}; end diff --git a/fw/rtl/sd/sd_clk.v b/fw/rtl/sd/sd_clk.v new file mode 100644 index 0000000..8705dcc --- /dev/null +++ b/fw/rtl/sd/sd_clk.v @@ -0,0 +1,107 @@ +module sd_clk ( + input i_clk, + input i_reset, + + output reg o_sd_clk, + + input [1:0] i_sd_clk_config, + + output reg o_sd_clk_strobe_rising, + output reg o_sd_clk_strobe_falling +); + + // Clock configuration values + + localparam [1:0] SD_CLK_CONFIG_STOP = 2'd0; + localparam [1:0] SD_CLK_CONFIG_DIV_256 = 2'd1; + localparam [1:0] SD_CLK_CONFIG_DIV_4 = 2'd2; + localparam [1:0] SD_CLK_CONFIG_DIV_2 = 2'd3; + + + // Clock configuration change detection + + reg [1:0] r_prev_sd_clk_config; + + wire w_sd_clk_config_changed = r_prev_sd_clk_config != i_sd_clk_config; + + always @(posedge i_clk) begin + if (i_reset) begin + r_prev_sd_clk_config <= SD_CLK_CONFIG_STOP; + end else begin + r_prev_sd_clk_config <= i_sd_clk_config; + end + end + + + // Clock divider + + reg [7:0] r_sd_clk_counter; + + always @(posedge i_clk) begin + if (i_reset || w_sd_clk_config_changed) begin + r_sd_clk_counter <= 8'd0; + end else begin + r_sd_clk_counter <= r_sd_clk_counter + 1'd1; + end + end + + + // Clock divider selector + + reg r_selected_sd_clk; + + always @(*) begin + case (r_prev_sd_clk_config) + SD_CLK_CONFIG_STOP: + r_selected_sd_clk = 1'b0; + SD_CLK_CONFIG_DIV_256: + r_selected_sd_clk = r_sd_clk_counter[7]; + SD_CLK_CONFIG_DIV_4: + r_selected_sd_clk = r_sd_clk_counter[1]; + SD_CLK_CONFIG_DIV_2: + r_selected_sd_clk = r_sd_clk_counter[0]; + endcase + end + + + // Clock strobe generation + + reg r_prev_selected_sd_clk; + + always @(posedge i_clk) begin + o_sd_clk_strobe_rising <= 1'b0; + o_sd_clk_strobe_falling <= 1'b0; + + if (i_reset) begin + r_prev_selected_sd_clk <= 1'b0; + end else begin + r_prev_selected_sd_clk <= r_selected_sd_clk; + + if (!r_prev_selected_sd_clk && r_selected_sd_clk) begin + o_sd_clk_strobe_rising <= 1'b1; + end + + if (r_prev_selected_sd_clk && !r_selected_sd_clk) begin + o_sd_clk_strobe_falling <= 1'b1; + end + end + end + + + // SD clock generation + + always @(posedge i_clk) begin + if (i_reset) begin + o_sd_clk <= 1'b0; + end else begin + if (o_sd_clk_strobe_rising) begin + o_sd_clk <= 1'b1; + end + + if (o_sd_clk_strobe_falling) begin + o_sd_clk <= 1'b0; + end + end + end + +endmodule diff --git a/fw/rtl/sd/sd_cmd.v b/fw/rtl/sd/sd_cmd.v new file mode 100644 index 0000000..b9e2042 --- /dev/null +++ b/fw/rtl/sd/sd_cmd.v @@ -0,0 +1,249 @@ +module sd_cmd ( + input i_clk, + input i_reset, + + inout io_sd_cmd, + + input i_sd_clk_strobe_rising, + input i_sd_clk_strobe_falling, + + input [5:0] i_command_index, + input [31:0] i_command_argument, + input i_command_long_response, + input i_command_skip_response, + output reg [5:0] o_command_index, + output reg [31:0] o_command_response, + input i_command_start, + output o_command_busy, + output reg o_command_timeout, + output reg o_command_response_crc_error +); + + // Start synchronizer + + reg r_pending_start; + + wire w_start = r_pending_start && i_sd_clk_strobe_falling; + + always @(posedge i_clk) begin + if (i_reset) begin + r_pending_start <= 1'b0; + end else begin + if (i_command_start) begin + r_pending_start <= 1'b1; + end else if (i_sd_clk_strobe_falling) begin + r_pending_start <= 1'b0; + end + end + end + + + // Module state + + localparam STATE_IDLE = 0; + localparam STATE_SENDING = 1; + localparam STATE_RESP_WAIT = 2; + localparam STATE_RECEIVING = 3; + localparam STATE_HIZ_WAIT = 4; + + reg [4:0] r_state; + + + // Bit counter logic + + reg [8:0] r_bit_counter; + reg r_bit_done; + + wire w_command_start = w_start && r_state[STATE_IDLE]; + wire w_command_end = r_bit_done && r_state[STATE_SENDING]; + wire w_response_timeout = r_bit_done && r_state[STATE_RESP_WAIT]; + wire w_response_start = !io_sd_cmd && i_sd_clk_strobe_rising && r_state[STATE_RESP_WAIT]; + wire w_response_end = r_bit_done && r_state[STATE_RECEIVING]; + wire w_hiz_end = r_bit_done && r_state[STATE_HIZ_WAIT]; + + localparam [8:0] COMMAND_BIT_LENGTH = (9'd48 - 1'd1); + localparam [8:0] RESPONSE_WAIT_BIT_LENGTH = (9'd64 - 1'd1); + localparam [8:0] RESPONSE_SHORT_BIT_LENGTH = (9'd48 - 1'd1); + localparam [8:0] RESPONSE_LONG_BIT_LENGTH = (9'd136 - 1'd1); + localparam [8:0] HIZ_WAIT_BIT_LENGTH = (9'd8 - 1'd1); + + always @(posedge i_clk) begin + if (w_command_start) begin + r_bit_counter <= COMMAND_BIT_LENGTH; + r_bit_done <= 1'b0; + end else if (w_command_end) begin + r_bit_counter <= RESPONSE_WAIT_BIT_LENGTH; + r_bit_done <= 1'b0; + end else if (w_response_start) begin + r_bit_counter <= i_command_long_response ? RESPONSE_LONG_BIT_LENGTH : RESPONSE_SHORT_BIT_LENGTH; + r_bit_done <= 1'b0; + end else if (w_response_end) begin + r_bit_counter <= HIZ_WAIT_BIT_LENGTH; + r_bit_done <= 1'b0; + end else if (i_sd_clk_strobe_rising) begin + if (r_bit_counter > 9'd0) begin + r_bit_counter <= r_bit_counter - 1'd1; + end else begin + r_bit_done <= 1'b1; + end + end + end + + + // CRC7 generator + + reg [6:0] r_crc_7_received; + + wire w_crc_shift_reset = !(r_state[STATE_SENDING] || r_state[STATE_RECEIVING]); + wire w_crc_shift_enabled = (r_bit_counter <= 9'd127) && (r_bit_counter > (r_state[STATE_SENDING] ? 9'd7 : 9'd8)); + wire w_crc_shift = w_crc_shift_enabled && i_sd_clk_strobe_rising; + wire [6:0] w_crc_7_calculated; + + sd_crc_7 sd_crc_7_inst ( + .i_clk(i_clk), + .i_crc_reset(w_crc_shift_reset), + .i_crc_shift(w_crc_shift), + .i_crc_input(io_sd_cmd), + .o_crc_output(w_crc_7_calculated) + ); + + + // Control signals + + assign o_command_busy = r_pending_start || (!r_state[STATE_IDLE]); + + always @(posedge i_clk) begin + if (i_reset) begin + o_command_timeout <= 1'b0; + o_command_response_crc_error <= 1'b0; + end else begin + if (w_command_start) begin + o_command_timeout <= 1'b0; + end + + if (w_response_timeout) begin + o_command_timeout <= 1'b1; + end + + if (w_response_end) begin + o_command_response_crc_error <= (w_crc_7_calculated != r_crc_7_received); + end + end + end + + + // State machine + + always @(posedge i_clk) begin + if (i_reset) begin + r_state <= (1'b1 << STATE_IDLE); + end else begin + r_state <= 5'b00000; + + unique case (1'b1) + r_state[STATE_IDLE]: begin + if (w_start) begin + r_state[STATE_SENDING] <= 1'b1; + end else begin + r_state[STATE_IDLE] <= 1'b1; + end + end + + r_state[STATE_SENDING]: begin + if (w_command_end) begin + if (i_command_skip_response) begin + r_state[STATE_HIZ_WAIT] <= 1'b1; + end else begin + r_state[STATE_RESP_WAIT] <= 1'b1; + end + end else begin + r_state[STATE_SENDING] <= 1'b1; + end + end + + r_state[STATE_RESP_WAIT]: begin + if (w_response_timeout) begin + r_state[STATE_IDLE] <= 1'b1; + end else if (w_response_start) begin + r_state[STATE_RECEIVING] <= 1'b1; + end else begin + r_state[STATE_RESP_WAIT] <= 1'b1; + end + end + + r_state[STATE_RECEIVING]: begin + if (w_response_end) begin + r_state[STATE_HIZ_WAIT] <= 1'b1; + end else begin + r_state[STATE_RECEIVING] <= 1'b1; + end + end + + r_state[STATE_HIZ_WAIT]: begin + if (w_hiz_end) begin + r_state[STATE_IDLE] <= 1'b1; + end else begin + r_state[STATE_HIZ_WAIT] <= 1'b1; + end + end + endcase + end + end + + + // Shifting operation + + reg [7:0] r_shift; + + wire w_shift = (r_state[STATE_SENDING] && i_sd_clk_strobe_falling) || (r_state[STATE_RECEIVING] && i_sd_clk_strobe_rising); + + assign io_sd_cmd = r_state[STATE_SENDING] ? r_shift[7] : 1'bZ; + + always @(posedge i_clk) begin + if (w_command_start) begin + o_command_response <= 32'h0000_0000; + r_shift <= {2'b01, i_command_index}; + end else if (w_response_start) begin + r_shift <= 8'h00; + end else begin + if (w_shift) begin + r_shift <= {r_shift[6:0], io_sd_cmd}; + end + + if (i_sd_clk_strobe_falling && r_state[STATE_SENDING]) begin + if (r_bit_counter == 9'd39) begin + r_shift <= i_command_argument[31:24]; + end + + if (r_bit_counter == 9'd31) begin + r_shift <= i_command_argument[23:16]; + end + + if (r_bit_counter == 9'd23) begin + r_shift <= i_command_argument[15:8]; + end + + if (r_bit_counter == 9'd15) begin + r_shift <= i_command_argument[7:0]; + end + + if (r_bit_counter == 9'd7) begin + r_shift <= {w_crc_7_calculated, 1'b1}; + end + end + + if (i_sd_clk_strobe_rising && r_state[STATE_RECEIVING]) begin + if (r_bit_counter == (i_command_long_response ? 9'd128 : 9'd40)) begin + o_command_index <= r_shift[5:0]; + end else if (r_bit_counter[2:0] == 3'd0) begin + if (r_bit_counter > 9'd7) begin + o_command_response <= {o_command_response[23:0], r_shift}; + end else begin + r_crc_7_received <= r_shift[7:1]; + end + end + end + end + end + +endmodule diff --git a/fw/rtl/sd/sd_crc_16.v b/fw/rtl/sd/sd_crc_16.v new file mode 100644 index 0000000..65c6f68 --- /dev/null +++ b/fw/rtl/sd/sd_crc_16.v @@ -0,0 +1,26 @@ +module sd_crc_16 ( + input i_clk, + input i_crc_reset, + input i_crc_shift, + input i_crc_input, + output reg [15:0] o_crc_output +); + + wire w_crc_inv = o_crc_output[15] ^ i_crc_input; + + always @(posedge i_clk) begin + if (i_crc_reset) begin + o_crc_output <= 16'd0; + end else if (i_crc_shift) begin + o_crc_output <= { + o_crc_output[14:12], + o_crc_output[11] ^ w_crc_inv, + o_crc_output[10:5], + o_crc_output[4] ^ w_crc_inv, + o_crc_output[3:0], + w_crc_inv + }; + end + end + +endmodule diff --git a/fw/rtl/sd/sd_crc_7.v b/fw/rtl/sd/sd_crc_7.v new file mode 100644 index 0000000..b008719 --- /dev/null +++ b/fw/rtl/sd/sd_crc_7.v @@ -0,0 +1,24 @@ +module sd_crc_7 ( + input i_clk, + input i_crc_reset, + input i_crc_shift, + input i_crc_input, + output reg [6:0] o_crc_output +); + + wire w_crc_inv = o_crc_output[6] ^ i_crc_input; + + always @(posedge i_clk) begin + if (i_crc_reset) begin + o_crc_output <= 7'd0; + end else if (i_crc_shift) begin + o_crc_output <= { + o_crc_output[5:3], + o_crc_output[2] ^ w_crc_inv, + o_crc_output[1:0], + w_crc_inv + }; + end + end + +endmodule diff --git a/fw/rtl/sd/sd_dat.v b/fw/rtl/sd/sd_dat.v new file mode 100644 index 0000000..f7836b1 --- /dev/null +++ b/fw/rtl/sd/sd_dat.v @@ -0,0 +1,28 @@ +module sd_dat ( + input i_clk, + input i_reset, + + inout reg [3:0] io_sd_dat, + + input i_sd_clk_strobe_rising, + input i_sd_clk_strobe_falling, + + input i_dat_width, + input i_dat_direction, + input [6:0] i_dat_block_size, + input [10:0] i_dat_num_blocks, + input i_dat_start, + input i_dat_stop, + output o_dat_busy, + output o_dat_crc_error, + + output reg o_rx_fifo_push, + input i_rx_fifo_overrun, + output [31:0] o_rx_fifo_data, + + input i_tx_fifo_full, + output reg o_tx_fifo_pop, + input [31:0] i_tx_fifo_data +); + +endmodule diff --git a/fw/rtl/sd/sd_dma.v b/fw/rtl/sd/sd_dma.v index a5f8a7a..4af8933 100644 --- a/fw/rtl/sd/sd_dma.v +++ b/fw/rtl/sd/sd_dma.v @@ -2,61 +2,33 @@ module sd_dma ( input i_clk, input i_reset, - input i_fifo_flush, - input i_fifo_push, - output o_fifo_full, - output o_fifo_empty, - input [31:0] i_fifo_data, + input [3:0] i_dma_bank, + input [23:0] i_dma_address, + input [14:0] i_dma_length, + output reg [14:0] o_dma_left, + input i_dma_load_bank_address, + input i_dma_load_length, + input i_dma_direction, + input i_dma_start, + input i_dma_stop, + output reg o_dma_busy, + + output reg o_rx_fifo_pop, + input i_rx_fifo_empty, + input [31:0] i_rx_fifo_data, + + output reg o_tx_fifo_push, + input i_tx_fifo_full, + output reg [31:0] o_tx_fifo_data, output reg o_request, output reg o_write, input i_busy, + input i_ack, + output reg [3:0] o_bank, + output reg [23:0] o_address, + input [31:0] i_data, output reg [31:0] o_data ); - reg [31:0] r_dma_fifo_mem [0:127]; - - reg [6:0] r_dma_fifo_wrptr; - reg [6:0] r_dma_fifo_rdptr; - - assign o_fifo_full = (r_dma_fifo_wrptr + 1'd1) == r_dma_fifo_rdptr; - assign o_fifo_empty = r_dma_fifo_wrptr == r_dma_fifo_rdptr; - - wire [31:0] w_rddata = r_dma_fifo_mem[r_dma_fifo_rdptr]; - - wire w_request_successful = o_request && !i_busy; - - always @(posedge i_clk) begin - if (i_reset || i_fifo_flush) begin - r_dma_fifo_wrptr <= 7'd0; - end else begin - if (i_fifo_push) begin - r_dma_fifo_wrptr <= r_dma_fifo_wrptr + 1'd1; - r_dma_fifo_mem[r_dma_fifo_wrptr] <= i_fifo_data; - end - end - end - - always @(posedge i_clk) begin - if (i_reset || i_fifo_flush) begin - o_request <= 1'b0; - o_write <= 1'b1; - r_dma_fifo_rdptr <= 7'd0; - end else begin - if (!o_request && !o_fifo_empty) begin - o_request <= 1'b1; - o_data <= w_rddata; - r_dma_fifo_rdptr <= r_dma_fifo_rdptr + 1'd1; - end - if (w_request_successful) begin - if (o_fifo_empty) begin - o_request <= 1'b0; - end else begin - r_dma_fifo_rdptr <= r_dma_fifo_rdptr + 1'd1; - o_data <= w_rddata; - end - end - end - end - endmodule diff --git a/fw/rtl/sd/sd_fifo.v b/fw/rtl/sd/sd_fifo.v new file mode 100644 index 0000000..8b4606a --- /dev/null +++ b/fw/rtl/sd/sd_fifo.v @@ -0,0 +1,56 @@ +module sd_fifo ( + input i_clk, + input i_reset, + + input i_fifo_flush, + input i_fifo_push, + input i_fifo_pop, + output o_fifo_empty, + output o_fifo_full, + output [7:0] o_fifo_items, + output reg o_fifo_underrun, + output reg o_fifo_overrun, + input [31:0] i_fifo_data, + output [31:0] o_fifo_data +); + + reg [31:0] r_fifo_mem [0:127]; + + reg [7:0] r_fifo_wrptr; + reg [7:0] r_fifo_rdptr; + + assign o_fifo_data = r_fifo_mem[r_fifo_rdptr[6:0]]; + + wire w_empty = r_fifo_wrptr[7] == r_fifo_rdptr[7]; + wire w_full_or_empty = r_fifo_wrptr[6:0] == r_fifo_rdptr[6:0]; + + assign o_fifo_empty = w_empty && w_full_or_empty; + assign o_fifo_full = !w_empty && w_full_or_empty; + assign o_fifo_items = r_fifo_wrptr - r_fifo_rdptr; + + always @(posedge i_clk) begin + if (i_reset) begin + r_fifo_wrptr <= 8'd0; + r_fifo_rdptr <= 8'd0; + o_fifo_underrun <= 1'b0; + o_fifo_overrun <= 1'b0; + end else begin + if (i_fifo_flush) begin + r_fifo_wrptr <= 8'd0; + r_fifo_rdptr <= 8'd0; + o_fifo_underrun <= 1'b0; + o_fifo_overrun <= 1'b0; + end + if (i_fifo_push) begin + o_fifo_overrun <= o_fifo_overrun ? 1'b1 : o_fifo_full; + r_fifo_mem[r_fifo_wrptr[6:0]] <= i_fifo_data; + r_fifo_wrptr <= r_fifo_wrptr + 1'd1; + end + if (i_fifo_pop) begin + o_fifo_underrun <= o_fifo_underrun ? 1'b1 : o_fifo_empty; + r_fifo_rdptr <= r_fifo_rdptr + 1'd1; + end + end + end + +endmodule diff --git a/fw/rtl/sd/sd_interface.v b/fw/rtl/sd/sd_interface.v index 76f71c5..0430f69 100644 --- a/fw/rtl/sd/sd_interface.v +++ b/fw/rtl/sd/sd_interface.v @@ -1,19 +1,16 @@ -`include "../constants.vh" - module sd_interface ( input i_clk, input i_reset, - output reg o_sd_clk, - output reg o_sd_cs, - output reg o_sd_mosi, - input i_sd_miso, + output o_sd_clk, + inout io_sd_cmd, + inout [3:0] io_sd_dat, input i_request, input i_write, output o_busy, - output reg o_ack, - input [7:0] i_address, + output o_ack, + input [3:0] i_address, output [31:0] o_data, input [31:0] i_data, @@ -21,277 +18,278 @@ module sd_interface ( output o_dma_write, input i_dma_busy, input i_dma_ack, - output reg [3:0] o_dma_bank, - output reg [23:0] o_dma_address, + output [3:0] o_dma_bank, + output [23:0] o_dma_address, input [31:0] i_dma_data, output [31:0] o_dma_data ); - // Register offsets + // Clock generator - localparam [2:0] REG_SD_SCR = 3'd0; - localparam [2:0] REG_SD_CS = 3'd1; - localparam [2:0] REG_SD_DR = 3'd2; - localparam [2:0] REG_SD_MULTI = 3'd3; - localparam [2:0] REG_SD_DMA_SCR = 3'd4; - localparam [2:0] REG_SD_DMA_ADDR = 3'd5; + wire [1:0] w_sd_clk_config; + wire w_sd_clk_strobe_rising; + wire w_sd_clk_strobe_falling; - localparam [7:0] MEM_SD_BUFFER_BASE = 8'h80; + sd_clk sd_clk_inst ( + .i_clk(i_clk), + .i_reset(i_reset), + + .i_sd_clk_config(w_sd_clk_config), + + .o_sd_clk_strobe_rising(w_sd_clk_strobe_rising), + .o_sd_clk_strobe_falling(w_sd_clk_strobe_falling), + + .o_sd_clk(o_sd_clk) + ); - // Bus controller + // Command path - reg [8:0] r_o_data; + wire [5:0] w_command_index; + wire [31:0] w_command_argument; + wire w_command_skip_response; + wire w_command_long_response; + wire [5:0] w_command_response_index; + wire [31:0] w_command_response; + wire w_command_start; + wire w_command_busy; + wire w_command_timeout; + wire w_command_response_crc_error; - wire w_address_in_buffers = i_address >= MEM_SD_BUFFER_BASE; - wire [31:0] w_sd_buffer_rx_o_data; + sd_cmd sd_cmd_inst ( + .i_clk(i_clk), + .i_reset(i_reset), - assign o_busy = 1'b0; - assign o_data = w_address_in_buffers ? w_sd_buffer_rx_o_data : {23'd0, r_o_data}; + .io_sd_cmd(io_sd_cmd), - always @(posedge i_clk) begin - o_ack <= !i_reset && i_request && !i_write && !o_busy; + .i_sd_clk_strobe_rising(w_sd_clk_strobe_rising), + .i_sd_clk_strobe_falling(w_sd_clk_strobe_falling), + + .i_command_index(w_command_index), + .i_command_argument(w_command_argument), + .i_command_long_response(w_command_long_response), + .i_command_skip_response(w_command_skip_response), + .o_command_index(w_command_response_index), + .o_command_response(w_command_response), + .i_command_start(w_command_start), + .o_command_busy(w_command_busy), + .o_command_timeout(w_command_timeout), + .o_command_response_crc_error(w_command_response_crc_error) + ); + + + // SD to FPGA (RX) data path FIFO + + wire w_rx_fifo_flush; + wire w_rx_fifo_push; + wire w_rx_fifo_regs_pop; + wire w_rx_fifo_dma_pop; + wire w_rx_fifo_empty; + wire [7:0] w_rx_fifo_items; + wire w_rx_fifo_overrun; + wire [31:0] w_rx_fifo_i_data; + wire [31:0] w_rx_fifo_o_data; + + sd_fifo sd_fifo_rx_inst ( + .i_clk(i_clk), + .i_reset(i_reset), + + .i_fifo_flush(w_rx_fifo_flush), + .i_fifo_push(w_rx_fifo_push), + .i_fifo_pop(w_rx_fifo_regs_pop || w_rx_fifo_dma_pop), + .o_fifo_empty(w_rx_fifo_empty), + // .o_fifo_full(), + .o_fifo_items(w_rx_fifo_items), + // .o_fifo_underrun(), + .o_fifo_overrun(w_rx_fifo_overrun), + .i_fifo_data(w_rx_fifo_i_data), + .o_fifo_data(w_rx_fifo_o_data) + ); + + + // FPGA to SD (TX) data path FIFO + + wire w_tx_fifo_flush; + wire w_tx_fifo_regs_push; + wire w_tx_fifo_dma_push; + wire w_tx_fifo_pop; + wire w_tx_fifo_empty; + wire w_tx_fifo_full; + reg [31:0] r_tx_fifo_i_data; + wire [31:0] w_tx_fifo_o_data; + + wire [31:0] w_tx_fifo_i_data_regs; + wire [31:0] w_tx_fifo_i_data_dma; + + always @(*) begin + r_tx_fifo_i_data = 32'h0000_0000; + if (w_tx_fifo_regs_push) r_tx_fifo_i_data = w_tx_fifo_i_data_regs; + if (w_tx_fifo_dma_push) r_tx_fifo_i_data = w_tx_fifo_i_data_dma; end + sd_fifo sd_fifo_tx_inst ( + .i_clk(i_clk), + .i_reset(i_reset), - // DMA controller + .i_fifo_flush(w_tx_fifo_flush), + .i_fifo_push(w_tx_fifo_regs_push || w_tx_fifo_dma_push), + .i_fifo_pop(w_tx_fifo_pop), + .o_fifo_empty(w_tx_fifo_empty), + .o_fifo_full(w_tx_fifo_full), + // .o_fifo_items(), + // .o_fifo_underrun(), + // .o_fifo_overrun(), + .i_fifo_data(r_tx_fifo_i_data), + .o_fifo_data(w_tx_fifo_o_data) + ); - reg r_dma_fifo_flush; - reg r_dma_fifo_push; - reg [31:0] r_dma_fifo_data; - wire w_dma_fifo_full; - wire w_dma_fifo_empty; - + // Data path + + wire w_dat_width; + wire w_dat_direction; + wire [6:0] w_dat_block_size; + wire [10:0] w_dat_num_blocks; + wire w_dat_start; + wire w_dat_stop; + wire w_dat_busy; + wire w_dat_crc_error; + + sd_dat sd_dat_inst ( + .i_clk(i_clk), + .i_reset(i_reset), + + .io_sd_dat(io_sd_dat), + + .i_sd_clk_strobe_rising(w_sd_clk_strobe_rising), + .i_sd_clk_strobe_falling(w_sd_clk_strobe_falling), + + .i_dat_width(w_dat_width), + .i_dat_direction(w_dat_direction), + .i_dat_block_size(w_dat_block_size), + .i_dat_num_blocks(w_dat_num_blocks), + .i_dat_start(w_dat_start), + .i_dat_stop(w_dat_stop), + .o_dat_busy(w_dat_busy), + .o_dat_crc_error(w_dat_crc_error), + + .o_rx_fifo_push(w_rx_fifo_push), + .i_rx_fifo_overrun(w_rx_fifo_overrun), + .o_rx_fifo_data(w_rx_fifo_i_data), + + .i_tx_fifo_full(w_tx_fifo_full), + .o_tx_fifo_pop(w_tx_fifo_pop), + .i_tx_fifo_data(w_tx_fifo_o_data) + ); + + + // DMA + + wire [3:0] w_dma_bank; + wire [23:0] w_dma_address; + wire [14:0] w_dma_length; + wire [14:0] w_dma_left; + wire w_dma_load_bank_address; + wire w_dma_load_length; + wire w_dma_direction; + wire w_dma_start; + wire w_dma_stop; + wire w_dma_busy; + sd_dma sd_dma_inst ( .i_clk(i_clk), .i_reset(i_reset), - .i_fifo_flush(r_dma_fifo_flush), - .i_fifo_push(r_dma_fifo_push), - .o_fifo_full(w_dma_fifo_full), - .o_fifo_empty(w_dma_fifo_empty), - .i_fifo_data(r_dma_fifo_data), + .i_dma_bank(w_dma_bank), + .i_dma_address(w_dma_address), + .i_dma_length(w_dma_length), + .o_dma_left(w_dma_left), + .i_dma_load_bank_address(w_dma_load_bank_address), + .i_dma_load_length(w_dma_load_length), + .i_dma_direction(w_dma_direction), + .i_dma_start(w_dma_start), + .i_dma_stop(w_dma_stop), + .o_dma_busy(w_dma_busy), + + .o_rx_fifo_pop(w_rx_fifo_dma_pop), + .i_rx_fifo_empty(w_rx_fifo_empty), + .i_rx_fifo_data(w_rx_fifo_o_data), + + .o_tx_fifo_push(w_tx_fifo_dma_push), + .i_tx_fifo_full(w_tx_fifo_full), + .o_tx_fifo_data(w_tx_fifo_i_data_dma), .o_request(o_dma_request), .o_write(o_dma_write), .i_busy(i_dma_busy), + .i_ack(i_dma_ack), + .o_bank(o_dma_bank), + .o_address(o_dma_address), + .i_data(i_dma_data), .o_data(o_dma_data) ); - // Bus <-> peripheral interface registers + // Peripheral registers - reg r_spi_busy; + sd_regs sd_regs_inst ( + .i_clk(i_clk), + .i_reset(i_reset), - reg [2:0] r_spi_clk_div; - reg r_spi_start; - reg [7:0] r_spi_tx_data; - reg [7:0] r_spi_rx_data; + .o_sd_clk_config(w_sd_clk_config), - reg r_spi_rx_only; + .o_command_index(w_command_index), + .o_command_argument(w_command_argument), + .o_command_long_response(w_command_long_response), + .o_command_skip_response(w_command_skip_response), + .i_command_index(w_command_response_index), + .i_command_response(w_command_response), + .o_command_start(w_command_start), + .i_command_busy(w_command_busy), + .i_command_timeout(w_command_timeout), + .i_command_response_crc_error(w_command_response_crc_error), - reg r_spi_start_multi; - reg [8:0] r_spi_multi_length; - reg r_spi_multi_dma; + .o_dat_width(w_dat_width), + .o_dat_direction(w_dat_direction), + .o_dat_block_size(w_dat_block_size), + .o_dat_num_blocks(w_dat_num_blocks), + .o_dat_start(w_dat_start), + .o_dat_stop(w_dat_stop), + .i_dat_busy(w_dat_busy), + .i_dat_crc_error(w_dat_crc_error), + .o_rx_fifo_flush(w_rx_fifo_flush), + .o_rx_fifo_pop(w_rx_fifo_regs_pop), + .i_rx_fifo_items(w_rx_fifo_items), + .i_rx_fifo_overrun(w_rx_fifo_overrun), + .i_rx_fifo_data(w_rx_fifo_o_data), - // Write logic + .o_tx_fifo_flush(w_tx_fifo_flush), + .o_tx_fifo_push(w_tx_fifo_regs_push), + .i_tx_fifo_empty(w_tx_fifo_empty), + .i_tx_fifo_full(w_tx_fifo_full), + .o_tx_fifo_data(w_tx_fifo_i_data_regs), - wire w_dma_request_successful = o_dma_request && !i_dma_busy; + .o_dma_bank(w_dma_bank), + .o_dma_address(w_dma_address), + .o_dma_length(w_dma_length), + .i_dma_bank(o_dma_bank), + .i_dma_address(o_dma_address), + .i_dma_left(w_dma_left), + .o_dma_load_bank_address(w_dma_load_bank_address), + .o_dma_load_length(w_dma_load_length), + .o_dma_direction(w_dma_direction), + .o_dma_start(w_dma_start), + .o_dma_stop(w_dma_stop), + .i_dma_busy(w_dma_busy), - always @(posedge i_clk) begin - r_spi_start <= 1'b0; - r_spi_start_multi <= 1'b0; - r_dma_fifo_flush <= 1'b0; - - if (i_reset) begin - o_sd_cs <= 1'b1; - o_dma_bank <= 4'd0; - o_dma_address <= 24'd0; - r_spi_clk_div <= 3'b111; - end else if (i_request && i_write && !o_busy && !w_address_in_buffers) begin - case (i_address[2:0]) - REG_SD_SCR: begin - r_spi_clk_div <= i_data[3:1]; - end - REG_SD_CS: if (!r_spi_busy) begin - o_sd_cs <= i_data[0]; - end - REG_SD_DR: if (!r_spi_busy) begin - r_spi_start <= 1'b1; - r_spi_tx_data <= i_data[7:0]; - r_spi_rx_only <= 1'b0; - r_spi_multi_length <= 9'd0; - r_spi_multi_dma <= 1'b0; - end - REG_SD_MULTI: if (!r_spi_busy) begin - r_spi_start_multi <= 1'b1; - {r_spi_multi_dma, r_spi_rx_only, r_spi_multi_length} <= i_data[10:0]; - end - REG_SD_DMA_SCR: begin - {r_dma_fifo_flush} <= i_data[0]; - end - REG_SD_DMA_ADDR: begin - {o_dma_bank, o_dma_address} <= {i_data[31:28], i_data[25:2]}; - end - default: begin - end - endcase - end - - if (w_dma_request_successful) begin - o_dma_address <= o_dma_address + 1'd1; - end - end - - - // Read logic - - always @(posedge i_clk) begin - if (!i_reset && i_request && !i_write && !o_busy) begin - if (!w_address_in_buffers) begin - case (i_address[2:0]) - REG_SD_SCR: begin - r_o_data[3:0] <= {r_spi_clk_div, r_spi_busy}; - end - REG_SD_DR: begin - r_o_data[8:0] <= {r_spi_busy, r_spi_rx_data}; - end - REG_SD_DMA_SCR: begin - r_o_data[1:0] <= {w_dma_fifo_full, w_dma_fifo_empty}; - end - default: begin - end - endcase - end - end - end - - - // Clock divider - - reg [7:0] r_spi_clk_div_counter; - reg r_spi_clk_prev_value; - reg r_spi_clk_strobe; - - wire w_spi_clk_div_selected = r_spi_clk_div_counter[r_spi_clk_div]; - - always @(posedge i_clk) begin - r_spi_clk_div_counter <= r_spi_clk_div_counter + 1'd1; - r_spi_clk_prev_value <= w_spi_clk_div_selected; - r_spi_clk_strobe <= (w_spi_clk_div_selected != r_spi_clk_prev_value) && !w_dma_fifo_full; - end - - - // Buffers - - reg [8:0] r_spi_multi_tx_address; - wire [7:0] w_spi_multi_tx_data; - - ram_sd_buffer ram_sd_buffer_tx_inst ( - .clock(i_clk), - - .address_a(r_spi_multi_tx_address), - .q_a(w_spi_multi_tx_data), - - .wren_b(!i_reset && i_request && i_write && !o_busy && w_address_in_buffers), - .address_b(i_address[6:0]), - .data_b({i_data[7:0], i_data[15:8], i_data[23:16], i_data[31:24]}) + .i_request(i_request), + .i_write(i_write), + .o_busy(o_busy), + .o_ack(o_ack), + .i_address(i_address), + .o_data(o_data), + .i_data(i_data) ); - reg r_spi_multi_rx_byte_write; - reg [8:0] r_spi_multi_rx_address; - - ram_sd_buffer ram_sd_buffer_rx_inst ( - .clock(i_clk), - - .wren_a(r_spi_multi_rx_byte_write), - .address_a(r_spi_multi_rx_address), - .data_a(r_spi_rx_data), - - .address_b(i_address[6:0]), - .q_b({w_sd_buffer_rx_o_data[7:0], w_sd_buffer_rx_o_data[15:8], w_sd_buffer_rx_o_data[23:16], w_sd_buffer_rx_o_data[31:24]}) - ); - - - // Shifting operation - - reg r_spi_multi; - reg r_spi_first_bit; - reg [3:0] r_spi_bit_clk; - reg [6:0] r_spi_tx_shift; - - wire [7:0] w_next_spi_tx_data = r_spi_rx_only ? 8'hFF : (r_spi_multi ? w_spi_multi_tx_data : r_spi_tx_data); - - always @(posedge i_clk) begin - r_spi_multi_rx_byte_write <= 1'b0; - - if (i_reset) begin - o_sd_clk <= 1'b0; - r_spi_bit_clk <= 4'd8; - r_spi_multi_tx_address <= 9'd0; - r_spi_multi_rx_address <= 9'd0; - end else begin - if (!r_spi_busy && (r_spi_start || r_spi_start_multi)) begin - r_spi_busy <= 1'b1; - r_spi_multi <= r_spi_start_multi; - r_spi_first_bit <= 1'b1; - end else if (r_spi_busy && r_spi_clk_strobe) begin - if (r_spi_first_bit) begin - r_spi_first_bit <= 1'b0; - {o_sd_mosi, r_spi_tx_shift} <= w_next_spi_tx_data; - r_spi_multi_tx_address <= r_spi_multi_tx_address + 1'd1; - end else if (!o_sd_clk) begin - o_sd_clk <= 1'b1; - r_spi_rx_data <= {r_spi_rx_data[6:0], i_sd_miso}; - r_spi_bit_clk <= r_spi_bit_clk - 1'd1; - if (r_spi_bit_clk == 4'd1) begin - r_spi_multi_rx_byte_write <= 1'b1; - end - end else begin - o_sd_clk <= 1'b0; - if (r_spi_bit_clk == 4'd0) begin - {o_sd_mosi, r_spi_tx_shift} <= w_next_spi_tx_data; - r_spi_bit_clk <= 4'd8; - r_spi_multi_tx_address <= r_spi_multi_tx_address + 1'd1; - r_spi_multi_rx_address <= r_spi_multi_rx_address + 1'd1; - if (r_spi_multi_rx_address == r_spi_multi_length) begin - r_spi_busy <= 1'b0; - r_spi_multi_tx_address <= 9'd0; - r_spi_multi_rx_address <= 9'd0; - end - end else begin - {o_sd_mosi, r_spi_tx_shift} <= {r_spi_tx_shift, 1'b0}; - end - end - end - end - end - - - // DMA RX FIFO controller - - reg [1:0] r_dma_byte_counter; - - always @(posedge i_clk) begin - r_dma_fifo_push <= 1'b0; - - if (i_reset) begin - r_dma_byte_counter <= 2'd0; - end else begin - if (!r_spi_busy || (r_spi_busy && !r_spi_multi_dma)) begin - if (r_dma_fifo_flush) begin - r_dma_byte_counter <= 2'd0; - end - end else if (r_spi_multi_rx_byte_write && r_spi_multi_dma) begin - r_dma_byte_counter <= r_dma_byte_counter + 1'd1; - r_dma_fifo_data <= {r_dma_fifo_data[23:0], r_spi_rx_data}; - if (r_dma_byte_counter == 2'd3) begin - r_dma_fifo_push <= 1'b1; - end - end - end - end - endmodule diff --git a/fw/rtl/sd/sd_regs.v b/fw/rtl/sd/sd_regs.v new file mode 100644 index 0000000..4a2ae9e --- /dev/null +++ b/fw/rtl/sd/sd_regs.v @@ -0,0 +1,222 @@ +module sd_regs ( + input i_clk, + input i_reset, + + output reg [1:0] o_sd_clk_config, + + output reg [5:0] o_command_index, + output reg [31:0] o_command_argument, + output reg o_command_long_response, + output reg o_command_skip_response, + input [5:0] i_command_index, + input [31:0] i_command_response, + output reg o_command_start, + input i_command_busy, + input i_command_timeout, + input i_command_response_crc_error, + + output reg o_dat_width, + output reg o_dat_direction, + output reg [6:0] o_dat_block_size, + output reg [10:0] o_dat_num_blocks, + output reg o_dat_start, + output reg o_dat_stop, + input i_dat_busy, + input i_dat_crc_error, + + output reg o_rx_fifo_flush, + output reg o_rx_fifo_pop, + input [7:0] i_rx_fifo_items, + input i_rx_fifo_overrun, + input [31:0] i_rx_fifo_data, + + output reg o_tx_fifo_flush, + output reg o_tx_fifo_push, + input i_tx_fifo_empty, + input i_tx_fifo_full, + output reg [31:0] o_tx_fifo_data, + + output reg [3:0] o_dma_bank, + output reg [23:0] o_dma_address, + output reg [14:0] o_dma_length, + input [3:0] i_dma_bank, + input [23:0] i_dma_address, + input [14:0] i_dma_left, + output reg o_dma_load_bank_address, + output reg o_dma_load_length, + output reg o_dma_direction, + output reg o_dma_start, + output reg o_dma_stop, + input i_dma_busy, + + input i_request, + input i_write, + output o_busy, + output reg o_ack, + input [3:0] i_address, + output reg [31:0] o_data, + input [31:0] i_data +); + + localparam [2:0] SD_REG_SCR = 3'd0; + localparam [2:0] SD_REG_ARG = 3'd1; + localparam [2:0] SD_REG_CMD = 3'd2; + localparam [2:0] SD_REG_RSP = 3'd3; + localparam [2:0] SD_REG_DAT = 3'd4; + localparam [2:0] SD_REG_DMA_SCR = 3'd5; + localparam [2:0] SD_REG_DMA_ADDR = 3'd6; + localparam [2:0] SD_REG_DMA_LENGTH = 3'd7; + + wire w_write_request = i_request && i_write && !o_busy; + wire w_read_request = i_request && !i_write && !o_busy; + + always @(*) begin + o_dma_bank = i_data[31:28]; + o_dma_address = i_data[25:2]; + o_dma_length = i_data[14:0]; + o_dma_load_bank_address = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_ADDR); + o_dma_load_length = w_write_request && !i_address[3] && (i_address[2:0] == SD_REG_DMA_LENGTH); + o_busy = 1'b0; + end + + always @(posedge i_clk) begin + o_command_start <= 1'b0; + o_dat_start <= 1'b0; + o_rx_fifo_flush <= 1'b0; + o_tx_fifo_flush <= 1'b0; + o_tx_fifo_push <= 1'b0; + o_dma_start <= 1'b0; + o_dat_stop <= 1'b0; + o_dma_stop <= 1'b0; + + if (i_reset) begin + o_sd_clk_config <= 2'd0; + o_dat_width <= 1'b0; + o_dat_direction <= 1'b0; + o_dat_block_size <= 7'd0; + o_dat_num_blocks <= 11'd0; + o_dma_direction <= 1'b0; + end else if (w_write_request) begin + if (!i_address[3]) begin + case (i_address[2:0]) + SD_REG_SCR: begin + {o_dat_width, o_sd_clk_config} <= i_data[2:0]; + end + + SD_REG_ARG: begin + o_command_argument <= i_data; + end + + SD_REG_CMD: begin + { + o_command_skip_response, + o_command_long_response, + o_command_start, + o_command_index + } <= i_data[8:0]; + end + + SD_REG_RSP: begin + end + + SD_REG_DAT: begin + { + o_tx_fifo_flush, + o_rx_fifo_flush, + o_dat_num_blocks, + o_dat_block_size, + o_dat_direction, + o_dat_stop, + o_dat_start + } <= i_data[22:0]; + end + + SD_REG_DMA_SCR: begin + { + o_dma_direction, + o_dma_stop, + o_dma_start + } <= i_data[2:0]; + end + + SD_REG_DMA_ADDR: begin + end + + SD_REG_DMA_LENGTH: begin + end + endcase + end else begin + o_tx_fifo_push <= 1'b1; + o_tx_fifo_data <= i_data; + end + end + end + + always @(posedge i_clk) begin + o_rx_fifo_pop <= 1'b0; + o_ack <= 1'b0; + + if (i_reset) begin + o_data <= 32'h0000_0000; + end else if (w_read_request) begin + o_ack <= 1'b1; + + if (!i_address[3]) begin + case (i_address[2:0]) + SD_REG_SCR: begin + o_data <= {30'd0, o_sd_clk_config}; + end + + SD_REG_ARG: begin + o_data <= o_command_argument; + end + + SD_REG_CMD: begin + o_data <= { + 21'd0, + i_command_response_crc_error, + i_command_timeout, + 2'b00, + i_command_busy, + i_command_index + }; + end + + SD_REG_RSP: begin + o_data <= i_command_response; + end + + SD_REG_DAT: begin + o_data <= { + i_rx_fifo_items, + i_tx_fifo_full, + i_tx_fifo_empty, + i_rx_fifo_overrun, + o_dat_num_blocks, + o_dat_block_size, + o_dat_direction, + i_dat_crc_error, + i_dat_busy + }; + end + + SD_REG_DMA_SCR: begin + o_data <= {29'd0, o_dma_direction, 1'b0, i_dma_busy}; + end + + SD_REG_DMA_ADDR: begin + o_data <= {i_dma_bank, 2'd0, i_dma_address, 2'b00}; + end + + SD_REG_DMA_LENGTH: begin + o_data <= {17'd0, i_dma_left}; + end + endcase + end else begin + o_rx_fifo_pop <= 1'b1; + o_data <= i_rx_fifo_data; + end + end + end + +endmodule diff --git a/fw/rtl/top.v b/fw/rtl/top.v index db936fb..65a3cb3 100644 --- a/fw/rtl/top.v +++ b/fw/rtl/top.v @@ -20,9 +20,6 @@ module top ( input i_n64_si_clk, inout io_n64_si_dq, - input i_n64_cic_clk, // TODO: to be removed - inout io_n64_cic_dq, // TODO: to be removed - output o_sdram_clk, output o_sdram_cs, output o_sdram_ras, @@ -36,10 +33,6 @@ module top ( inout io_sd_cmd, inout [3:0] io_sd_dat, - output o_flash_clk, // TODO: to be removed - output o_flash_cs, // TODO: to be removed - inout [3:0] io_flash_dq, // TODO: to be removed - output o_sram_clk, output o_sram_cs, inout [3:0] io_sram_dq, @@ -49,7 +42,7 @@ module top ( output o_led, - inout [7:0] io_pmod // TODO: to be removed + inout [7:0] io_pmod ); // Clock and reset signals @@ -64,8 +57,6 @@ module top ( wire w_n64_reset_btn; - assign io_n64_cic_dq = 1'bZ; - assign {o_flash_clk, o_flash_cs, io_flash_dq} = 6'bZZZZZZ; assign {o_sram_clk, o_sram_cs, io_sram_dq} = 6'bZZZZZZ; assign {o_rtc_scl, io_rtc_sda} = 2'bZZ; assign io_pmod[3] = w_n64_reset_btn ? 1'bZ : 1'b0; @@ -363,16 +354,6 @@ module top ( // SD card - wire w_sd_clk; - wire w_sd_cs; - wire w_sd_mosi; - wire w_sd_miso; - - assign o_sd_clk = w_sd_clk; - assign io_sd_dat = {w_sd_cs, 3'bZZZ}; - assign io_sd_cmd = w_sd_mosi; - assign w_sd_miso = io_sd_dat[0]; - wire w_n64_request_sd = w_n64_request && w_n64_bank == `BANK_SD; wire w_sd_dma_request; @@ -404,16 +385,15 @@ module top ( .i_clk(w_sys_clk), .i_reset(w_sys_reset), - .o_sd_clk(w_sd_clk), - .o_sd_cs(w_sd_cs), - .o_sd_mosi(w_sd_mosi), - .i_sd_miso(w_sd_miso), + .o_sd_clk(o_sd_clk), + .io_sd_cmd(io_sd_cmd), + .io_sd_dat(io_sd_dat), .i_request(w_n64_request_sd), .i_write(w_n64_write), .o_busy(w_n64_busy_sd), .o_ack(w_n64_ack_sd), - .i_address(w_n64_address[9:2]), + .i_address({w_n64_address[9], w_n64_address[4:2]}), .o_data(w_n64_i_data_sd), .i_data(w_n64_o_data),