SummerCart64/fw/rtl/sd/sd_cmd.v

250 lines
7.8 KiB
Coq
Raw Normal View History

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