From 1a02a73228699f6eb8dd79477974641520b135c5 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sat, 6 Feb 2021 19:35:50 +0100 Subject: [PATCH] [SC64][FW][SW] SDIO interface instead of slow SPI (#4) --- .gitignore | 1 + build_release.sh | 2 +- 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 | 208 +++++++++ fw/rtl/sd/sd_dma.v | 109 +++-- fw/rtl/sd/sd_fifo.v | 56 +++ fw/rtl/sd/sd_interface.v | 482 ++++++++++--------- fw/rtl/sd/sd_regs.v | 223 +++++++++ fw/rtl/top.v | 30 +- sw/bootloader/build.sh | 3 + sw/bootloader/src/fatfs/diskio.c | 39 +- sw/bootloader/src/sc64/sc64.c | 26 +- sw/bootloader/src/sc64/sc64_regs.h | 115 ++--- sw/bootloader/src/sc64/sc64_sd.c | 694 ++++++++++++++++------------ sw/bootloader/src/sc64/sc64_sd.h | 66 +-- sw/bootloader/src/sc64/sc64_sd_fs.c | 29 +- 21 files changed, 1736 insertions(+), 789 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 create mode 100644 sw/bootloader/build.sh 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/build_release.sh b/build_release.sh index 1b40417..706c5eb 100755 --- a/build_release.sh +++ b/build_release.sh @@ -19,7 +19,7 @@ fi # Build bootloader echo "Building bootloader" pushd sw/bootloader -docker run -t --mount type=bind,src="$(pwd)",target="/libdragon" anacierdem/libdragon:4.1.1 /bin/bash -c "/usr/bin/make clean; /usr/bin/make all N64_BYTE_SWAP=false" +./build.sh popd 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..00ddbb9 --- /dev/null +++ b/fw/rtl/sd/sd_dat.v @@ -0,0 +1,208 @@ +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 reg o_dat_crc_error, + + output reg o_rx_fifo_push, + input i_rx_fifo_overrun, + output reg [31:0] o_rx_fifo_data, + + input i_tx_fifo_full, + output reg o_tx_fifo_pop, + input [31:0] i_tx_fifo_data +); + + // Module state + + localparam STATE_IDLE = 0; + localparam STATE_READ_WAIT = 1; + localparam STATE_RECEIVING = 2; + + reg [2:0] r_state; + + + // Bit counter logic + + reg [12:0] r_bit_counter; + reg r_bit_done; + + wire w_start_bit = !io_sd_dat[0] && i_sd_clk_strobe_rising && r_state[STATE_READ_WAIT]; + wire w_data_end = r_bit_done && r_state[STATE_RECEIVING]; + + assign o_dat_busy = !r_state[STATE_IDLE]; + + always @(posedge i_clk) begin + if (w_start_bit) begin + r_bit_counter <= (i_dat_width ? ( + {2'b00, {1'b0, i_dat_block_size} + 1'd1, 3'b000} + ) : ( + {{1'b0, i_dat_block_size} + 1'd1, 5'b00000} + )) + 13'd16; + r_bit_done <= 1'b0; + end else if (i_sd_clk_strobe_rising) begin + if (r_bit_counter > 13'd0) begin + r_bit_counter <= r_bit_counter - 1'd1; + end else begin + r_bit_done <= 1'b1; + end + end + end + + + // Block counter logic + + reg [10:0] r_block_counter; + + wire w_read_start = i_dat_start && r_state[STATE_IDLE]; + wire w_read_stop = r_block_counter == 11'd0; + + always @(posedge i_clk) begin + if (w_read_start) begin + r_block_counter <= i_dat_num_blocks; + end else if (w_data_end) begin + if (r_block_counter > 11'd0) begin + r_block_counter <= r_block_counter - 1'd1; + end + end + end + + + // CRC16 generator + + reg [15:0] r_crc_16_received [0:3]; + + wire w_crc_shift_reset = !r_state[STATE_RECEIVING]; + wire w_crc_shift_enabled = r_bit_counter > 13'd16; + wire w_crc_shift = w_crc_shift_enabled && i_sd_clk_strobe_rising; + wire [15:0] w_crc_16_calculated [0:3]; + + wire w_crc_error = (r_bit_counter == 13'd0) && (i_dat_width ? ( + (w_crc_16_calculated[0] != r_crc_16_received[0]) && + (w_crc_16_calculated[1] != r_crc_16_received[1]) && + (w_crc_16_calculated[2] != r_crc_16_received[2]) && + (w_crc_16_calculated[3] != r_crc_16_received[3]) + ) : ( + w_crc_16_calculated[0] != r_crc_16_received[0] + )); + + genvar dat_index; + generate + for (dat_index = 0; dat_index < 4; dat_index = dat_index + 1) begin : crc_16_loop + sd_crc_16 sd_crc_16_inst ( + .i_clk(i_clk), + .i_crc_reset(w_crc_shift_reset), + .i_crc_shift(w_crc_shift), + .i_crc_input(io_sd_dat[dat_index]), + .o_crc_output(w_crc_16_calculated[dat_index]) + ); + end + endgenerate + + + // Control signals + + always @(posedge i_clk) begin + if (i_reset) begin + o_dat_crc_error <= 1'b0; + end else begin + if (w_data_end) begin + o_dat_crc_error <= w_crc_error; + 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 <= 3'b000; + + if (i_dat_stop) begin + r_state[STATE_IDLE] <= 1'b1; + end else begin + unique case (1'b1) + r_state[STATE_IDLE]: begin + if (i_dat_start) begin + if (i_dat_direction) begin + r_state[STATE_IDLE] <= 1'b1; // TODO: Sending + end else begin + r_state[STATE_READ_WAIT] <= 1'b1; + end + end else begin + r_state[STATE_IDLE] <= 1'b1; + end + end + + r_state[STATE_READ_WAIT]: begin + if (w_start_bit) begin + r_state[STATE_RECEIVING] <= 1'b1; + end else begin + r_state[STATE_READ_WAIT] <= 1'b1; + end + end + + r_state[STATE_RECEIVING]: begin + if (w_crc_error || i_rx_fifo_overrun) begin + r_state[STATE_IDLE] <= 1'b1; + end else if (w_data_end) begin + if (w_read_stop) begin + r_state[STATE_IDLE] <= 1'b1; + end else begin + r_state[STATE_READ_WAIT] <= 1'b1; + end + end else begin + r_state[STATE_RECEIVING] <= 1'b1; + end + end + endcase + end + end + end + + + // Shifting operation + + wire [31:0] w_shift_1_bit = {o_rx_fifo_data[30:0], io_sd_dat[0]}; + wire [31:0] w_shift_4_bit = {o_rx_fifo_data[27:0], io_sd_dat}; + + always @(posedge i_clk) begin + o_rx_fifo_push <= 1'b0; + + if (i_sd_clk_strobe_rising && r_state[STATE_RECEIVING]) begin + if (r_bit_counter > 13'd16) begin + if (i_dat_width) begin + o_rx_fifo_data <= w_shift_4_bit; + if (r_bit_counter[2:0] == 3'd1) begin + o_rx_fifo_push <= 1'b1; + end + end else begin + o_rx_fifo_data <= w_shift_1_bit; + if (r_bit_counter[4:0] == 5'd17) begin + o_rx_fifo_push <= 1'b1; + end + end + end else if (r_bit_counter > 13'd0) begin + for (integer i = 0; i < 4; i = i + 1) begin + r_crc_16_received[i] <= {r_crc_16_received[i][14:0], io_sd_dat[i]}; + end + end + end + end + +endmodule diff --git a/fw/rtl/sd/sd_dma.v b/fw/rtl/sd/sd_dma.v index a5f8a7a..64167ad 100644 --- a/fw/rtl/sd/sd_dma.v +++ b/fw/rtl/sd/sd_dma.v @@ -2,61 +2,92 @@ 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 [17:0] i_dma_length, + output [17: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_request, + output 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 [31:0] o_tx_fifo_data, + + output o_request, output reg o_write, input i_busy, - output reg [31:0] o_data + input i_ack, + output reg [3:0] o_bank, + output reg [23:0] o_address, + input [31:0] i_data, + output [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 + if (i_dma_load_bank_address && !o_dma_busy) begin + o_bank <= i_dma_bank; 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; + if (i_dma_load_bank_address && !o_dma_busy) begin + o_address <= i_dma_address; + end else if (w_request_successful) begin + o_address <= o_address + 1'd1; + end + end + + reg [17:0] r_remaining; + + assign o_dma_left = r_remaining; + + always @(posedge i_clk) begin + if (i_dma_load_length && !o_dma_busy) begin + r_remaining <= i_dma_length; + end else if (w_request_successful && r_remaining > 18'd0) begin + r_remaining <= r_remaining - 1'd1; + end + end + + always @(posedge i_clk) begin + if (i_reset) begin + o_dma_busy <= 1'b0; 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; + if (i_dma_start && !o_dma_busy) begin + o_dma_busy <= 1'b1; 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 + if (i_dma_stop || (w_request_successful && r_remaining == 18'd0)) begin + o_dma_busy <= 1'b0; end end end + assign o_rx_fifo_pop = o_dma_busy && o_write && w_request_successful; + + assign o_tx_fifo_data = i_data; + + assign o_request = o_dma_busy && (o_write ? ( + !i_rx_fifo_empty + ) : ( + 1'b0 // TODO: Reading + )); + + always @(posedge i_clk) begin + if (i_dma_start) begin + o_write <= i_dma_direction; + end + end + + assign o_data = i_rx_fifo_data; + 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..ac2d622 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 [17:0] w_dma_length; + wire [17: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..4ae4434 --- /dev/null +++ b/fw/rtl/sd/sd_regs.v @@ -0,0 +1,223 @@ +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 [17:0] o_dma_length, + input [3:0] i_dma_bank, + input [23:0] i_dma_address, + input [17: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_LEN = 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[17: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_LEN); + 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_LEN: 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 <= {29'd0, o_dat_width, 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, + o_command_skip_response, + o_command_long_response, + 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_LEN: begin + o_data <= {14'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), diff --git a/sw/bootloader/build.sh b/sw/bootloader/build.sh new file mode 100644 index 0000000..b3af794 --- /dev/null +++ b/sw/bootloader/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run -t --mount type=bind,src="$(pwd)",target="/libdragon" anacierdem/libdragon:4.1.1 /bin/bash -c "/usr/bin/make clean; /usr/bin/make all N64_BYTE_SWAP=false" diff --git a/sw/bootloader/src/fatfs/diskio.c b/sw/bootloader/src/fatfs/diskio.c index f94efd3..ca2fd7c 100644 --- a/sw/bootloader/src/fatfs/diskio.c +++ b/sw/bootloader/src/fatfs/diskio.c @@ -29,8 +29,7 @@ DSTATUS disk_initialize(BYTE pdrv) { } DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { - uint8_t success; - uint8_t response; + sc64_sd_err_t error; if ((pdrv > 0) || (count == 0)) { return RES_PARERR; @@ -40,40 +39,10 @@ DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) { return RES_NOTRDY; } - if (count == 1) { - success = sc64_sd_cmd_send(CMD17, sector, &response); + error = sc64_sd_read_sectors(sector, count, buff); - if (!success || response) { - return RES_PARERR; - } - - success = sc64_sd_block_read(buff, SD_BLOCK_SIZE, FALSE); - - if (!success) { - return RES_ERROR; - } - } else { - success = sc64_sd_cmd_send(CMD18, sector, &response); - - if (!success || response) { - return RES_PARERR; - } - - for (size_t i = 0; i < count; i++) { - success = sc64_sd_block_read(buff, SD_BLOCK_SIZE, FALSE); - - if (!success) { - return RES_ERROR; - } - - buff += SD_BLOCK_SIZE; - } - - success = sc64_sd_cmd_send(CMD12, 0, &response); - - if (!success || response) { - return RES_ERROR; - } + if (error != E_OK) { + return RES_ERROR; } return RES_OK; diff --git a/sw/bootloader/src/sc64/sc64.c b/sw/bootloader/src/sc64/sc64.c index 3cf6a11..aedc3b8 100644 --- a/sw/bootloader/src/sc64/sc64.c +++ b/sw/bootloader/src/sc64/sc64.c @@ -2,27 +2,27 @@ static void sc64_enable_peripheral(uint32_t mask) { - uint32_t config = platform_pi_io_read(&SC64_CART->scr); + uint32_t config = platform_pi_io_read(&SC64_CART->SCR); config |= mask; - platform_pi_io_write(&SC64_CART->scr, config); + platform_pi_io_write(&SC64_CART->SCR, config); } static void sc64_disable_peripheral(uint32_t mask) { - uint32_t config = platform_pi_io_read(&SC64_CART->scr); + uint32_t config = platform_pi_io_read(&SC64_CART->SCR); config &= ~mask; - platform_pi_io_write(&SC64_CART->scr, config); + platform_pi_io_write(&SC64_CART->SCR, config); } uint32_t sc64_get_scr(void) { - return platform_pi_io_read(&SC64_CART->scr); + return platform_pi_io_read(&SC64_CART->SCR); } void sc64_set_scr(uint32_t scr) { - platform_pi_io_write(&SC64_CART->scr, scr); + platform_pi_io_write(&SC64_CART->SCR, scr); } void sc64_enable_flashram(void) { @@ -100,29 +100,29 @@ void sc64_disable_sdram_writable(void) { } uint32_t sc64_get_boot_mode(void) { - return platform_pi_io_read(&SC64_CART->boot); + return platform_pi_io_read(&SC64_CART->BOOT); } void sc64_set_boot_mode(uint32_t boot) { - platform_pi_io_write(&SC64_CART->boot, boot); + platform_pi_io_write(&SC64_CART->BOOT, boot); } uint32_t sc64_get_version(void) { - return platform_pi_io_read(&SC64_CART->version); + return platform_pi_io_read(&SC64_CART->VERSION); } uint32_t sc64_get_ddipl_address(void) { - return platform_pi_io_read(&SC64_CART->ddipl_addr) & SC64_CART_DDIPL_ADDR_ADDRESS_MASK; + return platform_pi_io_read(&SC64_CART->DDIPL_ADDR) & SC64_CART_DDIPL_ADDR_ADDRESS_MASK; } void sc64_set_ddipl_address(uint32_t address) { - platform_pi_io_write(&SC64_CART->ddipl_addr, address & SC64_CART_DDIPL_ADDR_ADDRESS_MASK); + platform_pi_io_write(&SC64_CART->DDIPL_ADDR, address & SC64_CART_DDIPL_ADDR_ADDRESS_MASK); } uint32_t sc64_get_sram_address(void) { - return platform_pi_io_read(&SC64_CART->sram_addr) & SC64_CART_SRAM_ADDR_ADDRESS_MASK; + return platform_pi_io_read(&SC64_CART->SRAM_ADDR) & SC64_CART_SRAM_ADDR_ADDRESS_MASK; } void sc64_set_sram_address(uint32_t address) { - platform_pi_io_write(&SC64_CART->sram_addr, address & SC64_CART_SRAM_ADDR_ADDRESS_MASK); + platform_pi_io_write(&SC64_CART->SRAM_ADDR, address & SC64_CART_SRAM_ADDR_ADDRESS_MASK); } diff --git a/sw/bootloader/src/sc64/sc64_regs.h b/sw/bootloader/src/sc64/sc64_regs.h index b2dbdf2..399bb64 100644 --- a/sw/bootloader/src/sc64/sc64_regs.h +++ b/sw/bootloader/src/sc64/sc64_regs.h @@ -18,17 +18,17 @@ // Cart Interface Registers typedef struct sc64_cart_registers { - __IO reg_t scr; // Cart status and config - __IO reg_t boot; // Boot behavior override - __IO reg_t version; // Cart firmware version - __IO reg_t gpio; // Fixed I/O control - __IO reg_t usb_scr; // USB interface status and control - __IO reg_t usb_dma_addr; // USB bank and address for DMA to PC - __IO reg_t usb_dma_len; // USB transfer length for DMA to PC - __IO reg_t ddipl_addr; // 64 Disk Drive IPL location in SDRAM - __IO reg_t sram_addr; // SRAM save emulation location in SDRAM + __IO reg_t SCR; // Cart status and config + __IO reg_t BOOT; // Boot behavior override + __IO reg_t VERSION; // Cart firmware version + __IO reg_t GPIO; // Fixed I/O control + __IO reg_t USB_SCR; // USB interface status and control + __IO reg_t USB_DMA_ADDR; // USB bank and address for DMA to PC + __IO reg_t USB_DMA_LEN; // USB transfer length for DMA to PC + __IO reg_t DDIPL_ADDR; // 64 Disk Drive IPL location in SDRAM + __IO reg_t SRAM_ADDR; // SRAM save emulation location in SDRAM __IO reg_t __reserved[1015]; - __IO reg_t usb_fifo[1024]; // USB data from PC read FIFO memory end + __IO reg_t USB_FIFO[1024]; // USB data from PC read FIFO memory end } sc64_cart_registers_t; #define SC64_CART_BASE (0x1E000000) @@ -96,68 +96,77 @@ typedef struct sc64_cart_registers { // EEPROM Registers typedef struct sc64_eeprom_registers { - __IO reg_t mem[512]; // EEPROM memory + __IO reg_t MEM[512]; // EEPROM memory } sc64_eeprom_registers_t; #define SC64_EEPROM_BASE (0x1E004000) -#define SC64_EEPROM ((__IO sc64_sd_registers_t *) SC64_EEPROM_BASE) +#define SC64_EEPROM ((__IO sc64_eeprom_registers_t *) SC64_EEPROM_BASE) // SD Card Interface Registers -typedef struct sc64_sd_registers { - __IO reg_t scr; // Clock prescaler and busy flag - __IO reg_t cs; // Chip select pin control - __IO reg_t dr; // Data to be sent and return value - __IO reg_t multi; // Multi byte transmission - __IO reg_t dma_scr; // DMA configuration - __IO reg_t dma_addr; // DMA starting address - __IO reg_t __reserved[122]; - __IO reg_t buffer[128]; // Multi byte transmission buffer +typedef struct sc64_sd_registers_s { + __IO reg_t SCR; // Clock control and bus width selection + __IO reg_t ARG; // SD command argument + __IO reg_t CMD; // SD command index and flags + __IO reg_t RSP; // SD command response + __IO reg_t DAT; // SD data path control + __IO reg_t DMA_SCR; // DMA status and configuration + __IO reg_t DMA_ADDR; // DMA current address + __IO reg_t DMA_LEN; // DMA remaining length + __IO reg_t __unused[120]; + __IO reg_t FIFO[128]; // SD data path FIFO buffer } sc64_sd_registers_t; #define SC64_SD_BASE (0x1E008000) #define SC64_SD ((__IO sc64_sd_registers_t *) SC64_SD_BASE) -#define SC64_SC_SCR_PRESCALER_BIT (1) -#define SC64_SD_SCR_PRESCALER_MASK (0x7 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_2 (0 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_4 (1 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_8 (2 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_16 (3 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_32 (4 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_64 (5 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_128 (6 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_PRESCALER_256 (7 << SC64_SC_SCR_PRESCALER_BIT) -#define SC64_SD_SCR_BUSY (1 << 0) +#define SC64_SD_SCR_DAT_WIDTH (1 << 2) +#define SC64_SD_SCR_CLK_MASK (0x3 << 0) +#define SC64_SD_SCR_CLK_STOP (0 << 0) +#define SC64_SD_SCR_CLK_400_KHZ (1 << 0) +#define SC64_SD_SCR_CLK_25_MHZ (2 << 0) +#define SC64_SD_SCR_CLK_50_MHZ (3 << 0) -#define SC64_SD_CS_PIN (1 << 0) +#define SC64_SD_CMD_RESPONSE_CRC_ERROR (1 << 10) +#define SC64_SD_CMD_TIMEOUT (1 << 9) +#define SC64_SD_CMD_SKIP_RESPONSE (1 << 8) +#define SC64_SD_CMD_LONG_RESPONSE (1 << 7) +#define SC64_SD_CMD_BUSY (1 << 6) +#define SC64_SD_CMD_START (1 << 6) +#define SC64_SD_CMD_INDEX_GET(cmd) ((cmd) & 0x3F) +#define SC64_SD_CMD_INDEX(i) ((i) & 0x3F) -#define SC64_SD_DR_BUSY (1 << 8) -#define SC64_SD_DR_DATA_BIT (0) -#define SC64_SD_DR_DATA_MASK (0xFF << SC64_SD_DR_DATA_BIT) +#define SC64_SD_DAT_RX_FIFO_FULL (1 << 31) +#define SC64_SD_DAT_RX_FIFO_ITEMS_GET(dat) (((dat) >> 24) & 0xFF) +#define SC64_SD_DAT_TX_FIFO_FULL (1 << 23) +#define SC64_SD_DAT_TX_FIFO_EMPTY (1 << 22) +#define SC64_SD_DAT_TX_FIFO_FLUSH (1 << 22) +#define SC64_SD_DAT_RX_FIFO_OVERRUN (1 << 21) +#define SC64_SD_DAT_RX_FIFO_FLUSH (1 << 21) +#define SC64_SD_DAT_NUM_BLOCKS_GET(dat) ((((dat) >> 10) & 0x7FF) + 1) +#define SC64_SD_DAT_NUM_BLOCKS(nb) ((((nb) - 1) & 0x7FF) << 10) +#define SC64_SD_DAT_BLOCK_SIZE_GET(dat) (((((dat) >> 3) & 0x7F) + 1) * 4) +#define SC64_SD_DAT_BLOCK_SIZE(bs) (((((bs) / 4) - 1) & 0x7F) << 3) +#define SC64_SD_DAT_DIRECTION (1 << 2) +#define SC64_SD_DAT_CRC_ERROR (1 << 1) +#define SC64_SD_DAT_STOP (1 << 1) +#define SC64_SD_DAT_BUSY (1 << 0) +#define SC64_SD_DAT_START (1 << 0) -#define SC64_SD_DR_DATA(d) (((d) & SC64_SD_DR_DATA_MASK) >> SC64_SD_DR_DATA_BIT) +#define SC64_SD_DMA_SCR_DIRECTION (1 << 2) +#define SC64_SD_DMA_SCR_STOP (1 << 1) +#define SC64_SD_DMA_SCR_BUSY (1 << 0) +#define SC64_SD_DMA_SCR_START (1 << 0) -#define SC64_SD_MULTI_DMA (1 << 10) -#define SC64_SD_MULTI_RX_ONLY (1 << 9) -#define SC64_SD_MULTI_LENGTH_BIT (0) -#define SC64_SD_MULTI_LENGTH_MASK (0x1FF << SC64_SD_MULTI_LENGTH_BIT) +#define SC64_SD_DMA_ADDR_GET(addr) ((addr) & 0x3FFFFFC) +#define SC64_SD_DMA_BANK_GET(addr) (((addr) >> 28) & 0xF) +#define SC64_SD_DMA_BANK_ADDR(b, a) ((((b) & 0xF) << 28) | ((a) & 0x3FFFFFC)) -#define SC64_SD_MULTI(d, r, l) ((d ? (SC64_SD_MULTI_DMA) : 0) | ((r) ? (SC64_SD_MULTI_RX_ONLY) : 0) | ((((l) - 1) & SC64_SD_MULTI_LENGTH_MASK) << SC64_SD_MULTI_LENGTH_BIT)) - -#define SC64_SD_DMA_SCR_FLUSH (1 << 0) -#define SC64_SD_DMA_SCR_FIFO_FULL (1 << 1) -#define SC64_SD_DMA_SCR_FIFO_EMPTY (1 << 0) - -#define SC64_SD_DMA_ADDR_BANK_BIT (28) -#define SC64_SD_DMA_ADDR_BANK_MASK (0xF << SC64_SD_DMA_ADDR_BANK_BIT) -#define SC64_SD_DMA_ADDR_ADDRESS_BIT (0) -#define SC64_SD_DMA_ADDR_ADDRESS_MASK (0x3FFFFFC << SC64_SD_DMA_ADDR_ADDRESS_BIT) - -#define SC64_SD_DMA_ADDR(b, a) ((((b) << SC64_SD_DMA_ADDR_BANK_BIT) & SC64_SD_DMA_ADDR_BANK_MASK) | (((a) << SC64_SD_DMA_ADDR_ADDRESS_BIT) & SC64_SD_DMA_ADDR_ADDRESS_MASK)) +#define SC64_SD_DMA_LEN_GET(len) (((len) & 0x3FFFF) * 4) +#define SC64_SD_DMA_LEN(l) ((((l) / 4) - 1) & 0x3FFFF) #endif diff --git a/sw/bootloader/src/sc64/sc64_sd.c b/sw/bootloader/src/sc64/sc64_sd.c index 97b293c..a8ae0ca 100644 --- a/sw/bootloader/src/sc64/sc64_sd.c +++ b/sw/bootloader/src/sc64/sc64_sd.c @@ -1,331 +1,457 @@ -#include - #include "sc64.h" #include "sc64_sd.h" -static const size_t SC64_SD_BUFFER_SIZE = sizeof(SC64_SD->buffer); +#define CMD8_ARG_SUPPLY_VOLTAGE_27_36_V (1 << 8) +#define CMD8_ARG_CHECK_PATTERN_AA (0xAA << 0) -static uint8_t sd_spi_dma_buffer[sizeof(SC64_SD->buffer)] __attribute__((aligned(16))); -static uint8_t sd_card_initialized = FALSE; -static uint8_t sd_card_type_block = FALSE; +#define ACMD41_ARG_HCS (1 << 30) + +#define R3_CCS (1 << 30) +#define R3_BUSY (1 << 31) + +#define R7_SUPPLY_VOLTAGE_27_36_V (1 << 8) +#define R7_CHECK_PATTERN_AA (0xAA << 0) + +#define SD_BLOCK_SIZE (512) -static void sc64_sd_spi_set_prescaler(uint8_t prescaler) { - uint32_t scr = platform_pi_io_read(&SC64_SD->scr); +typedef enum sc64_sd_clock_e { + CLOCK_STOP, + CLOCK_400_KHZ, + CLOCK_25_MHZ, + CLOCK_50_MHZ, +} sc64_sd_clock_t; - scr = (scr & ~SC64_SD_SCR_PRESCALER_MASK) | (prescaler & SC64_SD_SCR_PRESCALER_MASK); +typedef enum sc64_sd_dat_width_e { + DAT_WIDTH_1BIT, + DAT_WIDTH_4BIT, +} sc64_sd_dat_width_t; - platform_pi_io_write(&SC64_SD->scr, scr); -} +typedef enum sc64_sd_cmd_flags_e { + NO_FLAGS = 0, + ACMD = (1 << 0), + SKIP_RESPONSE = (1 << 1), + LONG_RESPONSE = (1 << 2), + IGNORE_CRC = (1 << 3), + IGNORE_INDEX = (1 << 4), +} sc64_sd_cmd_flags_t; -static void sc64_sd_spi_set_chip_select(uint8_t select) { - platform_pi_io_write(&SC64_SD->cs, (select == TRUE) ? 0 : SC64_SD_CS_PIN); -} +typedef enum sc64_sd_dat_direction_e { + DAT_DIR_RX, + DAT_DIR_TX, +} sc64_sd_dat_direction_t; -static uint8_t sc64_sd_spi_exchange(uint8_t data) { - uint32_t dr; - platform_pi_io_write(&SC64_SD->dr, (uint32_t) (data & SC64_SD_DR_DATA_MASK)); +static bool sd_card_initialized = false; +static bool sd_card_type_block = false; +static bool sd_card_selected = false; +static uint32_t rca = 0; - do { - dr = platform_pi_io_read(&SC64_SD->dr); - } while (dr & SC64_SD_DR_BUSY); - return (uint8_t) SC64_SD_DR_DATA(dr); -} +static void sc64_sd_set_clock(sc64_sd_clock_t clock) { + uint32_t scr = platform_pi_io_read(&SC64_SD->SCR); -static uint8_t sc64_sd_spi_read(void) { - return sc64_sd_spi_exchange(0xFF); -} + scr &= ~SC64_SD_SCR_CLK_MASK; -static void sc64_sd_spi_release(void) { - sc64_sd_spi_set_chip_select(FALSE); - sc64_sd_spi_read(); -} - -static void sc64_sd_spi_multi_write(uint8_t *data, size_t length) { - size_t remaining = length; - size_t offset = 0; - - do { - size_t block_size = remaining < SC64_SD_BUFFER_SIZE ? remaining : SC64_SD_BUFFER_SIZE; - uint32_t dma_transfer_length = block_size + ((block_size % 4) ? (4 - (block_size % 4)) : 0); - - memcpy(sd_spi_dma_buffer, data + offset, block_size); - - platform_cache_writeback(sd_spi_dma_buffer, dma_transfer_length); - platform_pi_dma_write(sd_spi_dma_buffer, &SC64_SD->buffer, dma_transfer_length); - - platform_pi_io_write(&SC64_SD->multi, SC64_SD_MULTI(FALSE, FALSE, block_size)); - - while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY); - - remaining -= block_size; - offset += block_size; - } while (remaining > 0); -} - -static void sc64_sd_spi_multi_read(uint8_t *data, size_t length, uint8_t is_buffer_aligned) { - size_t remaining = length; - size_t offset = 0; - - do { - size_t block_size = remaining < SC64_SD_BUFFER_SIZE ? remaining : SC64_SD_BUFFER_SIZE; - uint32_t dma_transfer_length = block_size + ((block_size % 4) ? (4 - (block_size % 4)) : 0); - uint8_t *buffer_pointer = is_buffer_aligned ? (data + offset) : sd_spi_dma_buffer; - - platform_pi_io_write(&SC64_SD->multi, SC64_SD_MULTI(FALSE, TRUE, block_size)); - - while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY); - - platform_pi_dma_read(buffer_pointer, &SC64_SD->buffer, dma_transfer_length); - platform_cache_invalidate(buffer_pointer, dma_transfer_length); - - if (!is_buffer_aligned) { - memcpy(data + offset, sd_spi_dma_buffer, block_size); - } - - remaining -= block_size; - offset += block_size; - } while (remaining > 0); -} - -static void sc64_sd_spi_multi_read_dma(size_t length) { - size_t remaining = length; - - do { - size_t block_size = remaining < SC64_SD_BUFFER_SIZE ? remaining : SC64_SD_BUFFER_SIZE; - - platform_pi_io_write(&SC64_SD->multi, SC64_SD_MULTI(TRUE, TRUE, block_size)); - - while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY); - - remaining -= block_size; - } while (remaining > 0); -} - -static uint8_t sc64_sd_wait_ready(void) { - uint8_t response; - - for (size_t attempts = 10000; attempts > 0; --attempts) { - response = sc64_sd_spi_read(); - - if (response == SD_NOT_BUSY_RESPONSE) { + switch (clock) { + case CLOCK_400_KHZ: + scr |= SC64_SD_SCR_CLK_400_KHZ; + break; + case CLOCK_25_MHZ: + scr |= SC64_SD_SCR_CLK_25_MHZ; + break; + case CLOCK_50_MHZ: + scr |= SC64_SD_SCR_CLK_50_MHZ; + break; + default: break; - } } - return response; + platform_pi_io_write(&SC64_SD->SCR, scr); } -uint8_t sc64_sd_init(void) { - uint32_t arg; - uint8_t success; - uint8_t response[5]; - uint8_t *short_response = &response[0]; - uint32_t *extended_response = (uint32_t *) &response[1]; - uint8_t sd_version_2_or_later; - uint8_t status_data[64] __attribute__((aligned(16))); - - if (!sd_card_initialized) { - sc64_enable_sd(); - - platform_pi_io_write(&SC64_SD->dma_scr, SC64_SD_DMA_SCR_FLUSH); - - sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_256); - sc64_sd_spi_release(); - - sc64_sd_spi_set_chip_select(TRUE); - - for (uint8_t dummy_clocks = 10; dummy_clocks > 0; --dummy_clocks) { - sc64_sd_spi_read(); - } - - do { - success = sc64_sd_cmd_send(CMD0, 0, response); - if (!(success && *short_response == R1_IN_IDLE_STATE)) break; - - arg = CMD8_ARG_SUPPLY_VOLTAGE_27_36_V | CMD8_ARG_CHECK_PATTERN_AA; - success = sc64_sd_cmd_send(CMD8, CMD8_ARG_SUPPLY_VOLTAGE_27_36_V | CMD8_ARG_CHECK_PATTERN_AA, response); - if (!success) { - break; - } else if (*short_response & R1_ILLEGAL_COMMAND) { - sd_version_2_or_later = FALSE; - } else if ( - ((*extended_response & R7_VOLTAGE_ACCEPTED_MASK) == R7_SUPPLY_VOLTAGE_27_36_V) && - ((*extended_response & R7_CHECK_PATTERN_MASK) == R7_CHECK_PATTERN_AA) - ) { - sd_version_2_or_later = TRUE; - } else { - break; - } - - success = sc64_sd_cmd_send(CMD58, 0, response); - if (!success || (!(*extended_response & R3_27_36_V_MASK))) break; - - arg = sd_version_2_or_later ? ACMD41_ARG_HCS : 0; - for (size_t attempts = 0; attempts < 10000; attempts++) { - success = sc64_sd_cmd_send(ACMD41, arg, response); - if (!success || (!(*short_response & R1_IN_IDLE_STATE))) { - break; - } - } - if (!success) break; - - success = sc64_sd_cmd_send(CMD58, 0, response); - if (!success) break; - sd_card_type_block = (*extended_response & R3_CCS) ? TRUE : FALSE; - - sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_4); - - // Set high speed mode - success = sc64_sd_cmd_send(CMD6, 0x00000001, response); - if (!success) break; - success = sc64_sd_block_read(status_data, 64, FALSE); - if (!success) break; - if (status_data[13] & 0x02) { - success = sc64_sd_cmd_send(CMD6, 0x80000001, response); - if (!success) break; - success = sc64_sd_block_read(status_data, 64, FALSE); - if (!success) break; - sc64_sd_spi_read(); - sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_2); - } - - sd_card_initialized = TRUE; - - return TRUE; - } while (0); - - sc64_sd_deinit(); - - return FALSE; - } - - return TRUE; -} - -void sc64_sd_deinit(void) { - sd_card_initialized = FALSE; - - sc64_sd_spi_release(); - - while (platform_pi_io_read(&SC64_SD->scr) & SC64_SD_SCR_BUSY); - - platform_pi_io_write(&SC64_SD->dma_scr, SC64_SD_DMA_SCR_FLUSH); +static void sc64_sd_set_dat_width(sc64_sd_dat_width_t dat_width) { + uint32_t scr = platform_pi_io_read(&SC64_SD->SCR); - sc64_sd_spi_set_prescaler(SC64_SD_SCR_PRESCALER_256); + scr &= ~SC64_SD_SCR_DAT_WIDTH; + + if (dat_width == DAT_WIDTH_4BIT) { + scr |= SC64_SD_SCR_DAT_WIDTH; + } + + platform_pi_io_write(&SC64_SD->SCR, scr); +} + +static void sc64_sd_hw_init(void) { + sc64_enable_sd(); + + platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP); + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH | SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP); + platform_pi_io_write(&SC64_SD->SCR, 0); + + sc64_sd_set_clock(CLOCK_400_KHZ); + sc64_sd_set_dat_width(DAT_WIDTH_1BIT); +} + +static void sc64_sd_hw_deinit(void) { + if (sc64_get_scr() & SC64_CART_SCR_SD_ENABLE) { + while (platform_pi_io_read(&SC64_SD->CMD) & SC64_SD_CMD_BUSY); + platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP); + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH | SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP); + platform_pi_io_write(&SC64_SD->SCR, 0); + } sc64_disable_sd(); } -uint8_t sc64_sd_get_status(void) { - return sd_card_initialized ? TRUE : FALSE; -} +static sc64_sd_err_t sc64_sd_cmd_send(uint8_t index, uint32_t arg, sc64_sd_cmd_flags_t flags, uint32_t *response) { + sc64_sd_err_t error; + uint32_t reg; -void sc64_sd_dma_wait_for_finish(void) { - while (!(platform_pi_io_read(&SC64_SD->dma_scr) & SC64_SD_DMA_SCR_FIFO_EMPTY)); -} + if (flags & ACMD) { + error = sc64_sd_cmd_send(55, rca, NO_FLAGS, response); -void sc64_sd_dma_set_address(uint8_t bank, uint32_t address) { - platform_pi_io_write(&SC64_SD->dma_addr, SC64_SD_DMA_ADDR(bank, address)); -} - -uint8_t sc64_sd_cmd_send(uint8_t cmd, uint32_t arg, uint8_t *response) { - uint8_t cmd_data[7]; - uint8_t cmd_data_length; - uint8_t is_long_response; - - if (cmd & ACMD_MARK) { - sc64_sd_cmd_send(CMD55, 0, response); - - if (*response & ~(R1_IN_IDLE_STATE)) { - return FALSE; + if (error != E_OK) { + return error; } } - if (sc64_sd_wait_ready() != SD_NOT_BUSY_RESPONSE) { - return FALSE; + platform_pi_io_write(&SC64_SD->ARG, arg); + + reg = SC64_SD_CMD_START | SC64_SD_CMD_INDEX(index); + + if (flags & SKIP_RESPONSE) { + reg |= SC64_SD_CMD_SKIP_RESPONSE; } - if ((cmd == CMD17 || cmd == CMD18) && sd_card_type_block) { + if (flags & LONG_RESPONSE) { + reg |= SC64_SD_CMD_LONG_RESPONSE; + } + + platform_pi_io_write(&SC64_SD->CMD, reg); + + do { + reg = platform_pi_io_read(&SC64_SD->CMD); + } while (reg & SC64_SD_CMD_BUSY); + + *response = platform_pi_io_read(&SC64_SD->RSP); + + if (reg & SC64_SD_CMD_TIMEOUT) { + return E_TIMEOUT; + } + + if ((!(flags & IGNORE_CRC)) && (!(flags & SKIP_RESPONSE)) && (reg & SC64_SD_CMD_RESPONSE_CRC_ERROR)) { + return E_CRC_ERROR; + } + + if ((!(flags & SKIP_RESPONSE)) && (!(flags & IGNORE_INDEX)) && (SC64_SD_CMD_INDEX_GET(reg) != index)) { + return E_BAD_INDEX; + } + + return E_OK; +} + +static void sc64_sd_dat_prepare(size_t num_blocks, size_t block_size, sc64_sd_dat_direction_t direction) { + platform_pi_io_write(&SC64_SD->DAT, ( + SC64_SD_DAT_NUM_BLOCKS(num_blocks) | + SC64_SD_DAT_BLOCK_SIZE(block_size) | + (direction ? SC64_SD_DAT_DIRECTION : 0) | + SC64_SD_DAT_START + )); +} + +static void sc64_sd_dat_abort(void) { + platform_pi_io_write(&SC64_SD->DAT, ( + SC64_SD_DAT_TX_FIFO_FLUSH | + SC64_SD_DAT_RX_FIFO_FLUSH | + SC64_SD_DAT_STOP + )); +} + +static sc64_sd_err_t sc64_sd_dat_read(size_t block_size, void *buffer) { + int timeout; + uint32_t reg; + + timeout = 100000; + do { + reg = platform_pi_io_read(&SC64_SD->DAT); + if (SC64_SD_DAT_RX_FIFO_ITEMS_GET(reg) >= block_size) { + break; + } + } while ((reg & SC64_SD_DAT_BUSY) && (timeout--)); + + if (reg & SC64_SD_DAT_CRC_ERROR) { + return E_CRC_ERROR; + } + + if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH); + + return E_FIFO_ERROR; + } + + if (timeout == 0) { + sc64_sd_dat_abort(); + + return E_TIMEOUT; + } + + platform_pi_dma_read(buffer, &SC64_SD->FIFO, block_size); + platform_cache_invalidate(buffer, block_size); + + return E_OK; +} + + +bool sc64_sd_init(void) { + sc64_sd_err_t error; + uint32_t response; + uint32_t argument; + bool sd_version_2_or_later; + uint8_t buffer[64] __attribute__((aligned(16))); + + if (sd_card_initialized) { + return true; + } + + sc64_sd_hw_init(); + + do { + error = sc64_sd_cmd_send(0, 0, SKIP_RESPONSE, &response); + + argument = CMD8_ARG_SUPPLY_VOLTAGE_27_36_V | CMD8_ARG_CHECK_PATTERN_AA; + error = sc64_sd_cmd_send(8, argument, NO_FLAGS, &response); + sd_version_2_or_later = (error == E_OK); + if (sd_version_2_or_later && (response != (R7_SUPPLY_VOLTAGE_27_36_V | R7_CHECK_PATTERN_AA))) { + break; + } + + argument = (sd_version_2_or_later ? ACMD41_ARG_HCS : 0) | 0x00FF8000; + for (int i = 0; i < 4000; i++) { + error = sc64_sd_cmd_send(41, argument, ACMD | IGNORE_CRC | IGNORE_INDEX, &response); + if ((error != E_OK) || (response & R3_BUSY)) { + break; + } + } + if ((error != E_OK) || ((response & 0x00FF8000) == 0)) { + break; + } + sd_card_type_block = (response & R3_CCS) ? true : false; + + error = sc64_sd_cmd_send(2, 0, LONG_RESPONSE | IGNORE_INDEX, &response); + if (error != E_OK) { + break; + } + + error = sc64_sd_cmd_send(3, 0, NO_FLAGS, &response); + if (error != E_OK) { + break; + } + rca = response & 0xFFFF0000; + + error = sc64_sd_cmd_send(7, rca, NO_FLAGS, &response); + if (error != E_OK) { + break; + } + sd_card_selected = true; + + error = sc64_sd_cmd_send(6, 2, ACMD, &response); + if (error != E_OK) { + break; + } + + sc64_sd_set_clock(CLOCK_25_MHZ); + sc64_sd_set_dat_width(DAT_WIDTH_4BIT); + + sc64_sd_dat_prepare(1, 64, DAT_DIR_RX); + error = sc64_sd_cmd_send(6, 0x00000001, NO_FLAGS, &response); + if (error != E_OK) { + sc64_sd_dat_abort(); + break; + } + error = sc64_sd_dat_read(64, buffer); + if (error != E_OK) { + break; + } + if (buffer[13] & 0x02) { + sc64_sd_dat_prepare(1, 64, DAT_DIR_RX); + error = sc64_sd_cmd_send(6, 0x80000001, NO_FLAGS, &response); + if (error != E_OK) { + sc64_sd_dat_abort(); + break; + } + error = sc64_sd_dat_read(64, buffer); + if (error != E_OK) { + break; + } + + sc64_sd_set_clock(CLOCK_50_MHZ); + } + + sd_card_initialized = true; + + return true; + } while(0); + + sc64_sd_deinit(); + + return false; +} + +void sc64_sd_deinit(void) { + uint32_t response; + + if (sd_card_selected) { + sc64_sd_cmd_send(7, rca, NO_FLAGS, &response); + + sd_card_selected = false; + } + + sc64_sd_cmd_send(0, 0, SKIP_RESPONSE, &response); + + sc64_sd_hw_deinit(); +} + +bool sc64_sd_get_status(void) { + return sd_card_initialized; +} + +sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void *buffer) { + sc64_sd_err_t error; + uint32_t response; + uint32_t current_sector; + uint32_t reg; + int timeout; + + if (!sd_card_initialized) { + return E_NO_INIT; + } + + if ((count == 0) || (buffer == NULL)) { + return E_PAR_ERROR; + } + + current_sector = starting_sector; + if (!sd_card_type_block) { + current_sector *= SD_BLOCK_SIZE; + } + + for (size_t i = 0; i < count; i++) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_NUM_BLOCKS(1) | SC64_SD_DAT_BLOCK_SIZE(SD_BLOCK_SIZE) | SC64_SD_DAT_START); - } + error = sc64_sd_cmd_send(17, current_sector, NO_FLAGS, &response); + if (error != E_OK) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP); - cmd_data[0] = cmd & ~ACMD_MARK; // Command index - cmd_data[5] = 0x01; // CRC7 and stop bit - cmd_data[6] = 0xFF; // CMD12 stuff byte - - cmd_data_length = 6; - is_long_response = FALSE; - - switch (cmd) { - case CMD0: - cmd_data[5] = 0x95; - break; - case CMD8: - cmd_data[5] = 0x87; - is_long_response = TRUE; - break; - case CMD12: - cmd_data_length = 7; - break; - case CMD17: - case CMD18: - if (!sd_card_type_block) { - arg *= SD_BLOCK_SIZE; - } - break; - case CMD58: - is_long_response = TRUE; - break; - } - - cmd_data[1] = (uint8_t) (arg >> 24); // Command argument - cmd_data[2] = (uint8_t) (arg >> 16); - cmd_data[3] = (uint8_t) (arg >> 8); - cmd_data[4] = (uint8_t) (arg >> 0); - - sc64_sd_spi_multi_write(cmd_data, cmd_data_length); - - for (uint32_t response_timeout = 0; response_timeout < 8; response_timeout++) { - *response = sc64_sd_spi_read(); - - if (!(*response & RESPONSE_START_BIT)) { - if (is_long_response) { - sc64_sd_spi_multi_read(response + 1, 4, FALSE); - } - - return TRUE; + return error; } + + timeout = 100000; + do { + reg = platform_pi_io_read(&SC64_SD->DAT); + } while ((reg & SC64_SD_DAT_BUSY) && (timeout--)); + + if (reg & SC64_SD_DAT_CRC_ERROR) { + return E_CRC_ERROR; + } + + if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH); + + return E_FIFO_ERROR; + } + + if (timeout == 0) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP); + + return E_TIMEOUT; + } + + platform_pi_dma_read(buffer, &SC64_SD->FIFO, SD_BLOCK_SIZE); + platform_cache_invalidate(buffer, SD_BLOCK_SIZE); + + buffer += SD_BLOCK_SIZE; + current_sector += sd_card_type_block ? 1 : SD_BLOCK_SIZE; } - return FALSE; + return E_OK; } -uint8_t sc64_sd_block_read(uint8_t *buffer, size_t length, uint8_t dma) { - uint8_t token; - uint8_t crc[2]; +sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address) { + size_t sectors_left; + uint32_t current_sector; + uint32_t current_address; + uint32_t num_blocks; + uint32_t reg; + sc64_sd_err_t error; + uint32_t response; + int timeout; - for (size_t attempts = 10000; attempts > 0; --attempts) { - token = sc64_sd_spi_read(); + if (!sd_card_initialized) { + return E_NO_INIT; + } - if (token != SD_NOT_BUSY_RESPONSE) { - break; + if (count == 0) { + return E_PAR_ERROR; + } + + sectors_left = count; + current_sector = starting_sector; + if (!sd_card_type_block) { + current_sector *= SD_BLOCK_SIZE; + } + current_address = address; + + do { + num_blocks = (sectors_left > 2048) ? 2048 : sectors_left; + + platform_pi_io_write(&SC64_SD->DMA_ADDR, SC64_SD_DMA_BANK_ADDR(bank, current_address)); + platform_pi_io_write(&SC64_SD->DMA_LEN, SC64_SD_DMA_LEN(num_blocks * SD_BLOCK_SIZE)); + platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_DIRECTION | SC64_SD_DMA_SCR_START); + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_NUM_BLOCKS(num_blocks) | SC64_SD_DAT_BLOCK_SIZE(SD_BLOCK_SIZE) | SC64_SD_DAT_START); + + error = sc64_sd_cmd_send(18, current_sector, NO_FLAGS, &response); + if (error != E_OK) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP); + + return error; } - } - if (token != DATA_TOKEN_BLOCK_READ) { - return FALSE; - } + timeout = 1000000; + do { + reg = platform_pi_io_read(&SC64_SD->DAT); + } while ((reg & SC64_SD_DAT_BUSY) && (timeout--)); - if (dma) { - sc64_sd_spi_multi_read_dma(length); - } else { - sc64_sd_spi_multi_read(buffer, length, TRUE); - } - sc64_sd_spi_multi_read(crc, 2, FALSE); + error = sc64_sd_cmd_send(12, 0, NO_FLAGS, &response); + if (error != E_OK) { + return error; + } - return TRUE; + if (reg & SC64_SD_DAT_CRC_ERROR) { + return E_CRC_ERROR; + } + + if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH); + platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP); + + return E_FIFO_ERROR; + } + + if (timeout == 0) { + platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP); + platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP); + + return E_TIMEOUT; + } + + while (platform_pi_io_read(&SC64_SD->DMA_SCR) & SC64_SD_DMA_SCR_BUSY); + + sectors_left -= num_blocks; + current_sector += num_blocks * (sd_card_type_block ? 1 : SD_BLOCK_SIZE); + current_address += num_blocks * SD_BLOCK_SIZE; + } while (sectors_left > 0); + + return E_OK; } diff --git a/sw/bootloader/src/sc64/sc64_sd.h b/sw/bootloader/src/sc64/sc64_sd.h index 078ab02..131d5dc 100644 --- a/sw/bootloader/src/sc64/sc64_sd.h +++ b/sw/bootloader/src/sc64/sc64_sd.h @@ -5,62 +5,22 @@ #include "platform.h" -#define RESPONSE_START_BIT (1 << 7) - -#define R1_PARAMETER_ERROR (1 << 6) -#define R1_ADDRESS_ERROR (1 << 5) -#define R1_ERASE_SEQUENCE_ERROR (1 << 4) -#define R1_COM_CRC_ERROR (1 << 3) -#define R1_ILLEGAL_COMMAND (1 << 2) -#define R1_ERASE_RESET (1 << 1) -#define R1_IN_IDLE_STATE (1 << 0) - -#define R3_27_36_V_BIT (15) -#define R3_27_36_V_MASK (0x1FF << R3_27_36_V_BIT) -#define R3_CCS (1 << 30) - -#define R7_COMMAND_VERSION_BIT (28) -#define R7_COMMAND_VERSION_MASK (0xF << R7_COMMAND_VERSION_BIT) -#define R7_VOLTAGE_ACCEPTED_BIT (8) -#define R7_VOLTAGE_ACCEPTED_MASK (0xF << R7_VOLTAGE_ACCEPTED_BIT) -#define R7_CHECK_PATTERN_BIT (0) -#define R7_CHECK_PATTERN_MASK (0xFF << R7_CHECK_PATTERN_BIT) - -#define R7_SUPPLY_VOLTAGE_27_36_V (1 << 8) -#define R7_CHECK_PATTERN_AA (0xAA << 0) - -#define SD_NOT_BUSY_RESPONSE (0xFF) - -#define CMD_START_BIT (1 << 6) -#define ACMD_MARK (1 << 7) - -#define CMD0 (CMD_START_BIT | 0) -#define CMD6 (CMD_START_BIT | 6) -#define CMD8 (CMD_START_BIT | 8) -#define CMD12 (CMD_START_BIT | 12) -#define CMD17 (CMD_START_BIT | 17) -#define CMD18 (CMD_START_BIT | 18) -#define CMD55 (CMD_START_BIT | 55) -#define CMD58 (CMD_START_BIT | 58) -#define ACMD41 (ACMD_MARK | CMD_START_BIT | 41) - -#define CMD8_ARG_SUPPLY_VOLTAGE_27_36_V (1 << 8) -#define CMD8_ARG_CHECK_PATTERN_AA (0xAA << 0) - -#define ACMD41_ARG_HCS (1 << 30) - -#define DATA_TOKEN_BLOCK_READ (0xFE) - -#define SD_BLOCK_SIZE (512) +typedef enum sc64_sd_err_e { + E_OK, + E_TIMEOUT, + E_CRC_ERROR, + E_BAD_INDEX, + E_PAR_ERROR, + E_FIFO_ERROR, + E_NO_INIT, +} sc64_sd_err_t; -uint8_t sc64_sd_init(void); +bool sc64_sd_init(void); void sc64_sd_deinit(void); -uint8_t sc64_sd_get_status(void); -void sc64_sd_dma_wait_for_finish(void); -void sc64_sd_dma_set_address(uint8_t bank, uint32_t address); -uint8_t sc64_sd_cmd_send(uint8_t cmd, uint32_t arg, uint8_t *response); -uint8_t sc64_sd_block_read(uint8_t *buffer, size_t length, uint8_t dma); +bool sc64_sd_get_status(void); +sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void *buffer); +sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address); #endif diff --git a/sw/bootloader/src/sc64/sc64_sd_fs.c b/sw/bootloader/src/sc64/sc64_sd_fs.c index 685c404..6d38ce8 100644 --- a/sw/bootloader/src/sc64/sc64_sd_fs.c +++ b/sw/bootloader/src/sc64/sc64_sd_fs.c @@ -6,10 +6,7 @@ #include "sc64_sd_fs.h" -static DRESULT sc64_sd_fs_load_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, UINT count) { - uint8_t success; - uint8_t response; - +static DRESULT sc64_sd_fs_load_rom_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, UINT count) { if ((pdrv > 0) || (count == 0)) { return RES_PARERR; } @@ -18,30 +15,10 @@ static DRESULT sc64_sd_fs_load_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, return RES_NOTRDY; } - sc64_sd_dma_set_address(SC64_BANK_SDRAM, (uint32_t) offset); - - success = sc64_sd_cmd_send(CMD18, sector, &response); - - if (!success || response) { - return RES_PARERR; - } - - for (size_t i = 0; i < count; i++) { - success = sc64_sd_block_read(NULL, SD_BLOCK_SIZE, TRUE); - - if (!success) { - return RES_ERROR; - } - } - - success = sc64_sd_cmd_send(CMD12, 0, &response); - - if (!success || response) { + if (sc64_sd_read_sectors_dma(sector, count, SC64_BANK_SDRAM, offset) != E_OK) { return RES_ERROR; } - sc64_sd_dma_wait_for_finish(); - return RES_OK; } @@ -69,7 +46,7 @@ sc64_sd_fs_error_t sc64_sd_fs_load_rom(const char *path) { break; } - fresult = fe_load(path, SC64_SDRAM_SIZE, sc64_sd_fs_load_with_dma); + fresult = fe_load(path, SC64_SDRAM_SIZE, sc64_sd_fs_load_rom_with_dma); if (fresult) { switch (fresult) { case FR_DISK_ERR: