Files
retroSoC/rtl/ip/storage/sdio_command.sv
T
maksyuki a8325ab6b7 feat: add SDIO support and related tests
- Introduced a new testbench for the SDIO wrapper () to validate APB interactions.
- Updated  to include IRQ handling for SDIO0 and SDIO1.
- Enhanced  to account for performance management of SDIO0 and SDIO1.
- Modified memory map tests to include SDIO base addresses and performance wait offsets.
- Updated pin mapping tests to include SDIO1 signals.
- Created a comprehensive suite of SDIO tests () covering various functionalities including CRC, command engine, DMA, and standalone operations.
- Added a new test for verifying SDIO register parity against the RTL definition.
- Adjusted SOC topology tests to reflect the addition of SDIO in the IRQ and APB fabric.
2026-08-20 10:03:51 +08:00

315 lines
11 KiB
Systemverilog

// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`timescale 1ns / 1ps
module sdio_command #(
parameter int TimeoutWidth = 32
) (
input logic clk_i,
input logic rst_n_i,
input logic launch_tick_i,
input logic sample_tick_i,
input logic start_i,
input logic abort_i,
input logic [ 5:0] cmd_index_i,
input logic [ 31:0] cmd_arg_i,
input sdio_pkg::sdio_resp_type_e resp_type_i,
input logic crc_check_i,
input logic index_check_i,
input logic [TimeoutWidth-1:0] timeout_cycles_i,
input logic [TimeoutWidth-1:0] busy_timeout_cycles_i,
input logic cmd_di_i,
input logic dat0_i,
output logic cmd_oe_o,
output logic cmd_do_o,
output logic busy_o,
output logic done_o,
output logic error_o,
output logic timeout_o,
output logic crc_error_o,
output logic index_error_o,
output logic busy_timeout_o,
output logic [ 135:0] response_o,
output logic [ 5:0] last_cmd_index_o
);
typedef enum logic [2:0] {
Idle,
Send,
WaitResponse,
CaptureResponse,
WaitBusy,
Complete
} command_state_e;
command_state_e s_state_d, s_state_q;
logic [47:0] s_tx_shift_d, s_tx_shift_q;
logic [135:0] s_resp_d, s_resp_q;
logic [5:0] s_cmd_index_d, s_cmd_index_q;
logic [3:0] s_resp_type_bits_d, s_resp_type_bits_q;
sdio_pkg::sdio_resp_type_e s_resp_type_q;
logic [7:0] s_resp_len_d, s_resp_len_q;
logic [7:0] s_bit_count_d, s_bit_count_q;
logic [5:0] s_tx_count_d, s_tx_count_q;
logic s_tx_started_d, s_tx_started_q;
logic [TimeoutWidth-1:0] s_timeout_count_d, s_timeout_count_q;
logic s_crc_check_d, s_crc_check_q;
logic s_index_check_d, s_index_check_q;
logic s_done_d, s_done_q;
logic s_err_d, s_err_q;
logic s_timeout_d, s_timeout_q;
logic s_crc_err_d, s_crc_err_q;
logic s_index_err_d, s_index_err_q;
logic s_busy_timeout_d, s_busy_timeout_q;
logic s_tx_final_hold_d, s_tx_final_hold_q;
logic [ 135:0] s_resp_shift_next;
logic s_timeout_expired;
logic s_frame_end_error;
logic s_frame_crc_error;
logic s_frame_index_error;
logic [ 6:0] s_frame_crc;
logic [TimeoutWidth-1:0] s_phase_timeout;
assign s_resp_shift_next = {s_resp_q[134:0], cmd_di_i};
assign s_phase_timeout = (s_state_q == WaitBusy) ? busy_timeout_cycles_i : timeout_cycles_i;
assign s_timeout_expired = (s_phase_timeout == '0) || (s_timeout_count_q >= s_phase_timeout);
assign s_frame_end_error = s_resp_shift_next[0] != 1'b1;
assign s_frame_crc = sdio_pkg::sdio_crc7_response(
s_resp_shift_next, s_resp_type_q == sdio_pkg::SdioRespR2
);
assign s_frame_crc_error = s_frame_crc != s_resp_shift_next[7:1];
assign s_frame_index_error = s_resp_shift_next[45:40] != s_cmd_index_q;
assign cmd_oe_o = (s_state_q == Send) || (s_tx_final_hold_q && !sample_tick_i);
assign cmd_do_o = ((s_state_q == Send) || s_tx_final_hold_q) ? s_tx_shift_q[47] : 1'b1;
assign busy_o = (s_state_q != Idle) && (s_state_q != Complete);
assign done_o = s_done_q;
assign error_o = s_err_q;
assign timeout_o = s_timeout_q;
assign crc_error_o = s_crc_err_q;
assign index_error_o = s_index_err_q;
assign busy_timeout_o = s_busy_timeout_q;
assign response_o = s_resp_q;
assign last_cmd_index_o = s_cmd_index_q;
always_comb begin
s_state_d = s_state_q;
s_tx_shift_d = s_tx_shift_q;
s_resp_d = s_resp_q;
s_cmd_index_d = s_cmd_index_q;
s_resp_type_bits_d = s_resp_type_q;
s_resp_len_d = s_resp_len_q;
s_bit_count_d = s_bit_count_q;
s_tx_count_d = s_tx_count_q;
s_tx_started_d = s_tx_started_q;
s_timeout_count_d = s_timeout_count_q;
s_crc_check_d = s_crc_check_q;
s_index_check_d = s_index_check_q;
s_done_d = 1'b0;
s_err_d = s_err_q;
s_timeout_d = s_timeout_q;
s_crc_err_d = s_crc_err_q;
s_index_err_d = s_index_err_q;
s_busy_timeout_d = s_busy_timeout_q;
s_tx_final_hold_d = s_tx_final_hold_q;
if (s_tx_final_hold_q && sample_tick_i) begin
s_tx_final_hold_d = 1'b0;
end
if (s_state_q != Idle && s_state_q != Complete) begin
if (s_timeout_expired) begin
s_state_d = Complete;
s_done_d = 1'b1;
s_err_d = 1'b1;
s_timeout_d = 1'b1;
if (s_state_q == WaitBusy) begin
s_busy_timeout_d = 1'b1;
end
end else begin
s_timeout_count_d = s_timeout_count_q + 1'b1;
end
end
if ((s_state_q != Idle) && (s_state_q != Complete) && abort_i) begin
s_state_d = Complete;
s_done_d = 1'b1;
s_err_d = 1'b1;
s_tx_final_hold_d = 1'b0;
end
if ((s_state_q == Send) && s_tx_started_q && sample_tick_i && (s_tx_count_q != 6'd47)) begin
s_tx_shift_d = {s_tx_shift_q[46:0], 1'b0};
s_tx_count_d = s_tx_count_q + 1'b1;
end
unique case (s_state_q)
Idle: begin
s_tx_final_hold_d = 1'b0;
s_tx_started_d = 1'b0;
if (start_i) begin
s_state_d = Send;
s_tx_shift_d = {
1'b0,
1'b1,
cmd_index_i,
cmd_arg_i,
sdio_pkg::sdio_crc7_calc({1'b0, 1'b1, cmd_index_i, cmd_arg_i}),
1'b1
};
s_resp_d = '0;
s_cmd_index_d = cmd_index_i;
s_resp_type_bits_d = resp_type_i;
s_resp_len_d = (resp_type_i == sdio_pkg::SdioRespR2) ? 8'd136 : 8'd48;
s_bit_count_d = '0;
s_tx_count_d = '0;
s_tx_started_d = 1'b0;
s_timeout_count_d = '0;
s_crc_check_d = crc_check_i;
s_index_check_d = index_check_i;
s_err_d = 1'b0;
s_timeout_d = 1'b0;
s_crc_err_d = 1'b0;
s_index_err_d = 1'b0;
s_busy_timeout_d = 1'b0;
end
end
Send: begin
if (launch_tick_i) begin
s_tx_started_d = 1'b1;
if (s_tx_count_q == 6'd47) begin
s_tx_final_hold_d = 1'b1;
if (s_resp_type_q == sdio_pkg::SdioRespNone) begin
s_state_d = Complete;
s_done_d = 1'b1;
end else begin
s_state_d = WaitResponse;
s_timeout_count_d = '0;
end
end
end
end
WaitResponse: begin
if (sample_tick_i && (cmd_di_i == 1'b0)) begin
s_resp_d = '0;
s_resp_d[0] = 1'b0;
s_bit_count_d = 8'd1;
s_timeout_count_d = '0;
s_state_d = CaptureResponse;
end
end
CaptureResponse: begin
if (sample_tick_i) begin
s_resp_d = s_resp_shift_next;
if (s_bit_count_q == (s_resp_len_q - 1'b1)) begin
if (s_frame_end_error) begin
s_err_d = 1'b1;
end
if (s_crc_check_q && sdio_pkg::sdio_response_has_crc(
s_resp_type_q
) && s_frame_crc_error) begin
s_err_d = 1'b1;
s_crc_err_d = 1'b1;
end
if (s_index_check_q && sdio_pkg::sdio_response_has_index(
s_resp_type_q
) && s_frame_index_error) begin
s_err_d = 1'b1;
s_index_err_d = 1'b1;
end
if ((s_resp_type_q == sdio_pkg::SdioRespR1b) && !s_frame_end_error &&
!(s_crc_check_q && s_frame_crc_error)) begin
s_state_d = WaitBusy;
s_timeout_count_d = '0;
end else begin
s_state_d = Complete;
s_done_d = 1'b1;
end
end else begin
s_bit_count_d = s_bit_count_q + 1'b1;
end
end
end
WaitBusy: begin
if (sample_tick_i && dat0_i) begin
s_state_d = Complete;
s_done_d = 1'b1;
end
end
Complete: begin
s_tx_final_hold_d = 1'b0;
s_tx_started_d = 1'b0;
s_state_d = Idle;
end
default: begin
s_state_d = Idle;
s_err_d = 1'b1;
end
endcase
end
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_state_q <= Idle;
s_tx_shift_q <= '0;
s_resp_q <= '0;
s_cmd_index_q <= '0;
s_resp_type_bits_q <= '0;
s_resp_len_q <= 8'd48;
s_bit_count_q <= '0;
s_tx_count_q <= '0;
s_tx_started_q <= 1'b0;
s_timeout_count_q <= '0;
s_crc_check_q <= 1'b0;
s_index_check_q <= 1'b0;
s_done_q <= 1'b0;
s_err_q <= 1'b0;
s_timeout_q <= 1'b0;
s_crc_err_q <= 1'b0;
s_index_err_q <= 1'b0;
s_busy_timeout_q <= 1'b0;
s_tx_final_hold_q <= 1'b0;
end else begin
s_state_q <= s_state_d;
s_tx_shift_q <= s_tx_shift_d;
s_resp_q <= s_resp_d;
s_cmd_index_q <= s_cmd_index_d;
s_resp_type_bits_q <= s_resp_type_bits_d;
s_resp_len_q <= s_resp_len_d;
s_bit_count_q <= s_bit_count_d;
s_tx_count_q <= s_tx_count_d;
s_tx_started_q <= s_tx_started_d;
s_timeout_count_q <= s_timeout_count_d;
s_crc_check_q <= s_crc_check_d;
s_index_check_q <= s_index_check_d;
s_done_q <= s_done_d;
s_err_q <= s_err_d;
s_timeout_q <= s_timeout_d;
s_crc_err_q <= s_crc_err_d;
s_index_err_q <= s_index_err_d;
s_busy_timeout_q <= s_busy_timeout_d;
s_tx_final_hold_q <= s_tx_final_hold_d;
end
end
always_comb begin
s_resp_type_q = sdio_pkg::sdio_resp_type_e'(s_resp_type_bits_q);
end
`ifndef SYNTHESIS
initial begin
if (TimeoutWidth < 1) begin
$fatal(1, "sdio_command: TimeoutWidth must be positive");
end
end
`endif
endmodule