Files
retroSoC/tests/rtl/sdio_dma_tb.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

325 lines
11 KiB
Systemverilog

`timescale 1ns / 1ps
`include "axi4_define.svh"
module sdio_dma_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic start_i = 1'b0;
logic abort_i = 1'b0;
logic direction_i = 1'b0;
logic [31:0] desc_base_i = 32'd0;
logic [15:0] desc_count_i = 16'd1;
logic [31:0] total_bytes_i = 32'd5;
logic data_in_valid_i = 1'b0;
logic data_in_ready_o;
logic [31:0] data_in_i = '0;
logic [ 3:0] data_in_strb_i = '0;
logic data_in_last_i = 1'b0;
logic data_out_valid_o;
logic data_out_ready_i = 1'b1;
logic [31:0] data_out_o;
logic [ 3:0] data_out_strb_o;
logic data_out_last_o;
logic busy_o;
logic done_o;
logic error_o;
logic [ 7:0] error_code_o;
logic [31:0] current_desc_o;
logic [31:0] bytes_done_o;
logic [31:0] error_addr_o;
logic [31:0] memory [0:2047];
logic read_active_q = 1'b0;
logic [31:0] read_addr_q = '0;
logic [ 4:0] read_beats_q = '0;
logic [ 4:0] read_index_q = '0;
logic write_active_q = 1'b0;
logic [31:0] write_addr_q = '0;
logic write_response_q = 1'b0;
integer read_bursts;
integer max_read_beats;
integer output_count;
logic descriptor_irq_seen;
logic [31:0] output_data [ 0:3];
logic [ 3:0] output_strb [ 0:3];
logic output_last [ 0:3];
logic error_mode = 1'b0;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) dma_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
assign dma_axi4.arready = !read_active_q;
assign dma_axi4.rid = 1'b0;
assign dma_axi4.rdata = memory[(read_addr_q>>2)+read_index_q];
assign dma_axi4.rresp =
error_mode && (read_addr_q == 32'h0000_0200) ? `AXI4_RESP_SLAVE_ERROR :
`AXI4_RESP_OKAY;
assign dma_axi4.rlast = (read_index_q + 1'b1) >= read_beats_q;
assign dma_axi4.ruser = '0;
assign dma_axi4.rvalid = read_active_q;
assign dma_axi4.awready = !write_active_q && !write_response_q;
assign dma_axi4.wready = write_active_q;
assign dma_axi4.bid = 1'b0;
assign dma_axi4.bresp = `AXI4_RESP_OKAY;
assign dma_axi4.buser = '0;
assign dma_axi4.bvalid = write_response_q;
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
read_active_q <= 1'b0;
read_addr_q <= '0;
read_beats_q <= '0;
read_index_q <= '0;
write_active_q <= 1'b0;
write_addr_q <= '0;
write_response_q <= 1'b0;
read_bursts <= 0;
max_read_beats <= 0;
end else begin
if (dma_axi4.arvalid && dma_axi4.arready) begin
read_active_q <= 1'b1;
read_addr_q <= dma_axi4.araddr;
read_beats_q <= dma_axi4.arlen + 1'b1;
read_index_q <= '0;
read_bursts <= read_bursts + 1;
if ((dma_axi4.arlen + 1'b1) > max_read_beats) begin
max_read_beats <= dma_axi4.arlen + 1'b1;
end
end
if (dma_axi4.rvalid && dma_axi4.rready) begin
if (dma_axi4.rlast) begin
read_active_q <= 1'b0;
end else begin
read_index_q <= read_index_q + 1'b1;
end
end
if (dma_axi4.awvalid && dma_axi4.awready) begin
write_active_q <= 1'b1;
write_addr_q <= dma_axi4.awaddr;
end
if (dma_axi4.wvalid && dma_axi4.wready) begin
for (integer byte_index = 0; byte_index < 4; byte_index++) begin
if (dma_axi4.wstrb[byte_index]) begin
memory[(write_addr_q>>2)][byte_index*8+:8] <= dma_axi4.wdata[byte_index*8+:8];
end
end
if (dma_axi4.wlast) begin
write_active_q <= 1'b0;
write_response_q <= 1'b1;
end else begin
write_addr_q <= write_addr_q + 4;
end
end
if (dma_axi4.bvalid && dma_axi4.bready) begin
write_response_q <= 1'b0;
end
end
end
always_ff @(posedge clk_i) begin
if (data_out_valid_o && data_out_ready_i) begin
output_data[output_count] <= data_out_o;
output_strb[output_count] <= data_out_strb_o;
output_last[output_count] <= data_out_last_o;
output_count <= output_count + 1;
end
if (u_sdio_dma.descriptor_irq_o) begin
descriptor_irq_seen <= 1'b1;
end
end
sdio_dma #(
.AddrWidth(32),
.DataWidth(32),
.DescCount(16)
) u_sdio_dma (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (start_i),
.abort_i (abort_i),
.direction_i (direction_i),
.desc_base_i (desc_base_i),
.desc_count_i (desc_count_i),
.total_bytes_i (total_bytes_i),
.data_in_valid_i (data_in_valid_i),
.data_in_ready_o (data_in_ready_o),
.data_in_i (data_in_i),
.data_in_strb_i (data_in_strb_i),
.data_in_last_i (data_in_last_i),
.data_out_valid_o(data_out_valid_o),
.data_out_ready_i(data_out_ready_i),
.data_out_o (data_out_o),
.data_out_strb_o (data_out_strb_o),
.data_out_last_o (data_out_last_o),
.busy_o (busy_o),
.done_o (done_o),
.error_o (error_o),
.error_code_o (error_code_o),
.current_desc_o (current_desc_o),
.bytes_done_o (bytes_done_o),
.error_addr_o (error_addr_o),
.dma_axi4 (dma_axi4)
);
initial begin
for (integer index = 0; index < 2048; index++) memory[index] = '0;
output_count = 0;
descriptor_irq_seen = 1'b0;
direction_i = 1'b1;
memory[0] = 32'h0000_0FFC;
memory[1] = 32'd5;
memory[2] = 32'd0;
memory[3] = 32'h0000_000D;
memory[32'h0FFC>>2] = 32'h4433_2211;
memory[32'h1000>>2] = 32'h0000_0055;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
wait (done_o);
if (error_o || (output_count != 2) || (bytes_done_o != 32'd5) ||
(read_bursts < 3) || (max_read_beats > 16) ||
(output_strb[0] != 4'hF) || (output_strb[1] != 4'h1) ||
(output_data[0] != 32'h4433_2211) || (output_data[1] != 32'h0000_0055) ||
!output_last[1] || (memory[3] != 32'h0001_000C)) begin
$fatal(1, "DMA test failed err=%b code=%h outputs=%0d bytes=%0d bursts=%0d max=%0d wb=%h",
error_o, error_code_o, output_count, bytes_done_o, read_bursts, max_read_beats,
memory[3]);
end
@(posedge clk_i);
#1;
if (!descriptor_irq_seen) $fatal(1, "descriptor IRQ event was not visible");
// Card-to-host is the opposite stream direction: input stream bytes are
// AXI-written with low lane zero as the first byte.
rst_n_i = 1'b0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
output_count = 0;
direction_i = 1'b0;
desc_base_i = 32'd0;
total_bytes_i = 32'd5;
error_mode = 1'b0;
memory[0] = 32'h0000_0200;
memory[1] = 32'd5;
memory[2] = 32'd0;
memory[3] = 32'h0000_000D;
memory[32'h0200>>2] = '0;
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
@(negedge clk_i);
data_in_i = 32'h8877_6655;
data_in_strb_i = 4'hF;
data_in_last_i = 1'b0;
data_in_valid_i = 1'b1;
while (u_sdio_dma.s_state_q != 4'd7) @(posedge clk_i);
@(posedge clk_i);
@(negedge clk_i);
data_in_i = 32'h0000_0099;
data_in_strb_i = 4'h1;
data_in_last_i = 1'b1;
while (u_sdio_dma.s_state_q != 4'd7) @(posedge clk_i);
@(posedge clk_i);
@(negedge clk_i);
data_in_valid_i = 1'b0;
wait (done_o);
if (error_o || (bytes_done_o != 32'd5) ||
(memory[32'h0200>>2] != 32'h8877_6655) ||
(memory[32'h0204>>2][7:0] != 8'h99) ||
(memory[3] != 32'h0001_000C) || !descriptor_irq_seen) begin
$fatal(1, "card-to-host DMA failed err=%b code=%h bytes=%0d data=%h tail=%h wb=%h", error_o,
error_code_o, bytes_done_o, memory[32'h0200>>2], memory[32'h0204>>2], memory[3]);
end
for (integer tail_bytes = 1; tail_bytes <= 3; tail_bytes++) begin
rst_n_i = 1'b0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
data_in_valid_i = 1'b0;
direction_i = 1'b0;
desc_base_i = 32'd0;
total_bytes_i = tail_bytes;
memory[0] = 32'h0000_0300 + (tail_bytes * 32);
memory[1] = tail_bytes;
memory[2] = 32'd0;
memory[3] = 32'h0000_0005;
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
while (u_sdio_dma.s_state_q != 4'd7) @(posedge clk_i);
data_in_i = 32'h0403_0201;
data_in_strb_i = (4'b0001 << tail_bytes) - 1'b1;
data_in_last_i = 1'b1;
data_in_valid_i = 1'b1;
@(posedge clk_i);
@(negedge clk_i);
data_in_valid_i = 1'b0;
wait (done_o);
for (integer byte_index = 0; byte_index < tail_bytes; byte_index++) begin
if (memory[(32'h0000_0300 + (tail_bytes * 32) >> 2) + (byte_index >> 2)]
[(byte_index % 4)*8+:8] != byte_index + 1) begin
$fatal(1, "DMA tail readback failed bytes=%0d byte=%0d", tail_bytes, byte_index);
end
end
if (error_o || (bytes_done_o != tail_bytes) || (memory[3] != 32'h0001_0004)) begin
$fatal(1, "DMA tail transfer failed bytes=%0d err=%b wb=%h", tail_bytes, error_o,
memory[3]);
end
end
// A descriptor at ...FF0 is rejected before an AXI fetch.
rst_n_i = 1'b0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
desc_base_i = 32'h0000_0FF0;
direction_i = 1'b1;
total_bytes_i = 32'd4;
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
wait (done_o);
if (!error_o || (read_bursts != 0)) begin
$fatal(1, "descriptor boundary rejection failed err=%b bursts=%0d", error_o, read_bursts);
end
// Restore a clean responder state for the directed AXI error check.
rst_n_i = 1'b0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
memory[0] = 32'h0000_0200;
memory[1] = 32'd4;
memory[2] = 32'd0;
memory[3] = 32'h0000_0005;
memory[32'h0200>>2] = 32'hAABB_CCDD;
desc_base_i = 32'd0;
total_bytes_i = 32'd4;
direction_i = 1'b1;
error_mode = 1'b1;
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
wait (done_o);
if (!error_o || (memory[3] != 32'h0002_0004)) begin
$fatal(1, "AXI error was not written back: err=%b code=%h wb=%h", error_o, error_code_o,
memory[3]);
end
$display("SDIO DMA descriptor, tail, and 4 KiB split test passed");
$finish;
end
endmodule