mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-12-24 12:01:56 +01:00
[SC64][FW][SW] SDIO interface instead of slow SPI (#4)
This commit is contained in:
parent
22c670ef29
commit
1a02a73228
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/packages
|
||||
**/.vscode
|
||||
/sw/test
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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]"
|
||||
|
@ -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
|
||||
|
||||
|
107
fw/rtl/sd/sd_clk.v
Normal file
107
fw/rtl/sd/sd_clk.v
Normal file
@ -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
|
249
fw/rtl/sd/sd_cmd.v
Normal file
249
fw/rtl/sd/sd_cmd.v
Normal file
@ -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
|
26
fw/rtl/sd/sd_crc_16.v
Normal file
26
fw/rtl/sd/sd_crc_16.v
Normal file
@ -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
|
24
fw/rtl/sd/sd_crc_7.v
Normal file
24
fw/rtl/sd/sd_crc_7.v
Normal file
@ -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
|
208
fw/rtl/sd/sd_dat.v
Normal file
208
fw/rtl/sd/sd_dat.v
Normal file
@ -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
|
@ -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
|
||||
|
56
fw/rtl/sd/sd_fifo.v
Normal file
56
fw/rtl/sd/sd_fifo.v
Normal file
@ -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
|
@ -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
|
||||
|
223
fw/rtl/sd/sd_regs.v
Normal file
223
fw/rtl/sd/sd_regs.v
Normal file
@ -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
|
30
fw/rtl/top.v
30
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),
|
||||
|
||||
|
3
sw/bootloader/build.sh
Normal file
3
sw/bootloader/build.sh
Normal file
@ -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"
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,331 +1,457 @@
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user