mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
- 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.
366 lines
11 KiB
Systemverilog
366 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 the Mulan PSL v2 at:
|
|
// http://license.coscl.org.cn/MulanPSL2
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
// 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_dual_tb;
|
|
localparam logic [31:0] HostCtrl = 32'h00C;
|
|
localparam logic [31:0] ClockCtrl = 32'h010;
|
|
localparam logic [31:0] BusCtrl = 32'h018;
|
|
localparam logic [31:0] CmdArg = 32'h040;
|
|
localparam logic [31:0] CmdCfg = 32'h044;
|
|
localparam logic [31:0] CmdStart = 32'h048;
|
|
localparam logic [31:0] CmdStatus = 32'h04C;
|
|
localparam logic [31:0] Resp0 = 32'h050;
|
|
localparam logic [31:0] BlockSize = 32'h080;
|
|
localparam logic [31:0] BlockCount = 32'h084;
|
|
localparam logic [31:0] DataCfg = 32'h088;
|
|
localparam logic [31:0] DataStart = 32'h08C;
|
|
localparam logic [31:0] PioData = 32'h090;
|
|
localparam logic [31:0] Status = 32'h028;
|
|
localparam logic [31:0] ErrorStatus = 32'h10C;
|
|
|
|
logic clk_i = 1'b0;
|
|
logic rst0_n_i = 1'b0;
|
|
logic rst1_n_i = 1'b0;
|
|
apb4_if apb0 (
|
|
.pclk (clk_i),
|
|
.presetn(rst0_n_i)
|
|
);
|
|
apb4_if apb1 (
|
|
.pclk (clk_i),
|
|
.presetn(rst1_n_i)
|
|
);
|
|
axi4_if #(
|
|
.ADDR_WIDTH(32),
|
|
.DATA_WIDTH(32),
|
|
.ID_WIDTH (1),
|
|
.USER_WIDTH(1)
|
|
) axi0 (
|
|
.aclk (clk_i),
|
|
.aresetn(rst0_n_i)
|
|
);
|
|
axi4_if #(
|
|
.ADDR_WIDTH(32),
|
|
.DATA_WIDTH(32),
|
|
.ID_WIDTH (1),
|
|
.USER_WIDTH(1)
|
|
) axi1 (
|
|
.aclk (clk_i),
|
|
.aresetn(rst1_n_i)
|
|
);
|
|
sdio_if sdio0 ();
|
|
sdio_if sdio1 ();
|
|
logic card0_cmd_do;
|
|
logic [ 3:0] card0_dat_do;
|
|
logic card0_irq;
|
|
logic card1_cmd_do;
|
|
logic [ 3:0] card1_dat_do;
|
|
logic card1_irq;
|
|
logic [31:0] value0;
|
|
logic [31:0] value1;
|
|
integer guard0;
|
|
integer guard1;
|
|
|
|
assign sdio0.cmd_di_i = sdio0.cmd_oe_o ? sdio0.cmd_do_o : card0_cmd_do;
|
|
assign sdio0.dat_di_i = (sdio0.dat_oe_o & sdio0.dat_do_o) | (~sdio0.dat_oe_o & card0_dat_do);
|
|
assign sdio1.cmd_di_i = sdio1.cmd_oe_o ? sdio1.cmd_do_o : card1_cmd_do;
|
|
assign sdio1.dat_di_i = (sdio1.dat_oe_o & sdio1.dat_do_o) | (~sdio1.dat_oe_o & card1_dat_do);
|
|
|
|
always #5 clk_i = ~clk_i;
|
|
|
|
apb4_sdio u_sdio0 (
|
|
.clk_i (clk_i),
|
|
.rst_n_i (rst0_n_i),
|
|
.apb4 (apb0),
|
|
.dma_axi4(axi0),
|
|
.sdio (sdio0)
|
|
);
|
|
apb4_sdio u_sdio1 (
|
|
.clk_i (clk_i),
|
|
.rst_n_i (rst1_n_i),
|
|
.apb4 (apb1),
|
|
.dma_axi4(axi1),
|
|
.sdio (sdio1)
|
|
);
|
|
|
|
sdio_sd_memory_model #(
|
|
.HighCapacity(1'b1),
|
|
.BusyCycles (8)
|
|
) u_card0 (
|
|
.rst_n_i (rst0_n_i),
|
|
.sck_i (sdio0.sck_o),
|
|
.cmd_oe_i(sdio0.cmd_oe_o),
|
|
.cmd_do_i(sdio0.cmd_do_o),
|
|
.cmd_do_o(card0_cmd_do),
|
|
.dat_oe_i(sdio0.dat_oe_o),
|
|
.dat_do_i(sdio0.dat_do_o),
|
|
.dat_do_o(card0_dat_do),
|
|
.irq_o (card0_irq)
|
|
);
|
|
sdio_sdio_model #(
|
|
.FunctionCount(2),
|
|
.FunctionBytes(4096),
|
|
.BusyCycles (8)
|
|
) u_card1 (
|
|
.rst_n_i (rst1_n_i),
|
|
.sck_i (sdio1.sck_o),
|
|
.cmd_oe_i(sdio1.cmd_oe_o),
|
|
.cmd_do_i(sdio1.cmd_do_o),
|
|
.cmd_do_o(card1_cmd_do),
|
|
.dat_oe_i(sdio1.dat_oe_o),
|
|
.dat_do_i(sdio1.dat_do_o),
|
|
.dat_do_o(card1_dat_do),
|
|
.irq_o (card1_irq)
|
|
);
|
|
sdio_axi_memory_responder u_mem0 (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst0_n_i),
|
|
.axi4 (axi0)
|
|
);
|
|
sdio_axi_memory_responder u_mem1 (
|
|
.clk_i (clk_i),
|
|
.rst_n_i(rst1_n_i),
|
|
.axi4 (axi1)
|
|
);
|
|
|
|
task automatic apb0_idle;
|
|
begin
|
|
apb0.psel = 1'b0;
|
|
apb0.penable = 1'b0;
|
|
apb0.pwrite = 1'b0;
|
|
apb0.paddr = '0;
|
|
apb0.pwdata = '0;
|
|
apb0.pstrb = '0;
|
|
end
|
|
endtask
|
|
task automatic apb1_idle;
|
|
begin
|
|
apb1.psel = 1'b0;
|
|
apb1.penable = 1'b0;
|
|
apb1.pwrite = 1'b0;
|
|
apb1.paddr = '0;
|
|
apb1.pwdata = '0;
|
|
apb1.pstrb = '0;
|
|
end
|
|
endtask
|
|
|
|
task automatic apb0_write(input logic [31:0] address_i, input logic [31:0] data_i);
|
|
begin
|
|
@(negedge clk_i);
|
|
apb0.paddr = address_i;
|
|
apb0.pwdata = data_i;
|
|
apb0.pstrb = 4'hF;
|
|
apb0.pwrite = 1'b1;
|
|
apb0.psel = 1'b1;
|
|
apb0.penable = 1'b0;
|
|
@(negedge clk_i);
|
|
apb0.penable = 1'b1;
|
|
@(posedge clk_i);
|
|
#1;
|
|
if (!apb0.pready || apb0.pslverr) $fatal(1, "dual APB0 write failed %h", address_i);
|
|
apb0_idle();
|
|
end
|
|
endtask
|
|
task automatic apb1_write(input logic [31:0] address_i, input logic [31:0] data_i);
|
|
begin
|
|
@(negedge clk_i);
|
|
apb1.paddr = address_i;
|
|
apb1.pwdata = data_i;
|
|
apb1.pstrb = 4'hF;
|
|
apb1.pwrite = 1'b1;
|
|
apb1.psel = 1'b1;
|
|
apb1.penable = 1'b0;
|
|
@(negedge clk_i);
|
|
apb1.penable = 1'b1;
|
|
@(posedge clk_i);
|
|
#1;
|
|
if (!apb1.pready || apb1.pslverr) $fatal(1, "dual APB1 write failed %h", address_i);
|
|
apb1_idle();
|
|
end
|
|
endtask
|
|
task automatic apb0_read(input logic [31:0] address_i, output logic [31:0] data_o);
|
|
begin
|
|
@(negedge clk_i);
|
|
apb0.paddr = address_i;
|
|
apb0.pwrite = 1'b0;
|
|
apb0.psel = 1'b1;
|
|
apb0.penable = 1'b0;
|
|
apb0.pstrb = '0;
|
|
@(negedge clk_i);
|
|
apb0.penable = 1'b1;
|
|
@(posedge clk_i);
|
|
#1;
|
|
if (!apb0.pready || apb0.pslverr) $fatal(1, "dual APB0 read failed %h", address_i);
|
|
data_o = apb0.prdata;
|
|
apb0_idle();
|
|
end
|
|
endtask
|
|
task automatic apb1_read(input logic [31:0] address_i, output logic [31:0] data_o);
|
|
begin
|
|
@(negedge clk_i);
|
|
apb1.paddr = address_i;
|
|
apb1.pwrite = 1'b0;
|
|
apb1.psel = 1'b1;
|
|
apb1.penable = 1'b0;
|
|
apb1.pstrb = '0;
|
|
@(negedge clk_i);
|
|
apb1.penable = 1'b1;
|
|
@(posedge clk_i);
|
|
#1;
|
|
if (!apb1.pready || apb1.pslverr) $fatal(1, "dual APB1 read failed %h", address_i);
|
|
data_o = apb1.prdata;
|
|
apb1_idle();
|
|
end
|
|
endtask
|
|
|
|
task automatic wait0_command;
|
|
logic seen_busy;
|
|
begin
|
|
guard0 = 0;
|
|
seen_busy = 1'b0;
|
|
while (guard0 < 20000) begin
|
|
@(posedge clk_i);
|
|
guard0 = guard0 + 1;
|
|
if (u_sdio0.u_sdio_core.s_cmd_busy) seen_busy = 1'b1;
|
|
if (seen_busy && (u_sdio0.u_sdio_core.u_sdio_command.s_state_q == 3'd0)) begin
|
|
break;
|
|
end
|
|
end
|
|
if (guard0 >= 20000) $fatal(1, "dual SDIO0 command did not drain");
|
|
end
|
|
endtask
|
|
task automatic wait1_command;
|
|
logic seen_busy;
|
|
begin
|
|
guard1 = 0;
|
|
seen_busy = 1'b0;
|
|
while (guard1 < 20000) begin
|
|
@(posedge clk_i);
|
|
guard1 = guard1 + 1;
|
|
if (u_sdio1.u_sdio_core.s_cmd_busy) seen_busy = 1'b1;
|
|
if (seen_busy && (u_sdio1.u_sdio_core.u_sdio_command.s_state_q == 3'd0)) begin
|
|
break;
|
|
end
|
|
end
|
|
if (guard1 >= 20000) $fatal(1, "dual SDIO1 command did not drain");
|
|
end
|
|
endtask
|
|
task automatic wait0_data;
|
|
logic seen_busy;
|
|
begin
|
|
guard0 = 0;
|
|
seen_busy = 1'b0;
|
|
while (guard0 < 200000) begin
|
|
@(posedge clk_i);
|
|
guard0 = guard0 + 1;
|
|
if (u_sdio0.u_sdio_core.s_data_busy || u_sdio0.u_sdio_core.s_dma_busy) begin
|
|
seen_busy = 1'b1;
|
|
end
|
|
if (seen_busy && !u_sdio0.u_sdio_core.s_data_busy && !u_sdio0.u_sdio_core.s_dma_busy) begin
|
|
break;
|
|
end
|
|
end
|
|
if (guard0 >= 200000) $fatal(1, "dual SDIO0 data did not drain");
|
|
end
|
|
endtask
|
|
task automatic wait1_data;
|
|
logic seen_busy;
|
|
begin
|
|
guard1 = 0;
|
|
seen_busy = 1'b0;
|
|
while (guard1 < 200000) begin
|
|
@(posedge clk_i);
|
|
guard1 = guard1 + 1;
|
|
if (u_sdio1.u_sdio_core.s_data_busy || u_sdio1.u_sdio_core.s_dma_busy) begin
|
|
seen_busy = 1'b1;
|
|
end
|
|
if (seen_busy && !u_sdio1.u_sdio_core.s_data_busy && !u_sdio1.u_sdio_core.s_dma_busy) begin
|
|
break;
|
|
end
|
|
end
|
|
if (guard1 >= 200000) $fatal(1, "dual SDIO1 data did not drain");
|
|
end
|
|
endtask
|
|
|
|
task automatic run_memory_context;
|
|
begin
|
|
apb0_write(ClockCtrl, 32'h0000_0101);
|
|
apb0_write(HostCtrl, 32'h0000_0005);
|
|
apb0_write(CmdArg, 32'd2);
|
|
apb0_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd1, 2'd0, 6'd17});
|
|
apb0_write(CmdStart, 32'd1);
|
|
wait0_command();
|
|
apb0_read(CmdStatus, value0);
|
|
if ((value0 & 32'h1E) != 0) $fatal(1, "dual SDIO0 CMD17 failed");
|
|
apb0_write(BusCtrl, 32'd0);
|
|
apb0_write(BlockSize, 32'd4);
|
|
apb0_write(BlockCount, 32'd1);
|
|
apb0_write(DataCfg, 32'd0);
|
|
u_card0.arm_read(1024, 4, 1'b0, 1'b0);
|
|
apb0_write(DataStart, 32'd1);
|
|
wait0_data();
|
|
apb0_read(PioData, value0);
|
|
if (value0 != 32'h0000_0000) $fatal(1, "dual SDIO0 data mismatch");
|
|
end
|
|
endtask
|
|
|
|
task automatic run_sdio_context;
|
|
begin
|
|
apb1_write(ClockCtrl, 32'h0000_0101);
|
|
apb1_write(HostCtrl, 32'h0000_0005);
|
|
apb1_write(CmdArg, 32'd0);
|
|
apb1_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd5, 2'd0, 6'd5});
|
|
apb1_write(CmdStart, 32'd1);
|
|
wait1_command();
|
|
apb1_write(CmdArg, {1'b0, 3'd0, 1'b0, 1'b0, 17'h00000, 1'b0, 8'h00});
|
|
apb1_write(CmdCfg, {18'd0, 1'b1, 1'b1, 4'd6, 2'd0, 6'd52});
|
|
apb1_write(CmdStart, 32'd1);
|
|
wait1_command();
|
|
apb1_write(CmdArg, {1'b0, 3'd1, 1'b0, 1'b1, 17'h00020, 9'd4});
|
|
apb1_write(CmdCfg, {18'd0, 1'b0, 1'b0, 4'd0, 2'd0, 6'd53});
|
|
apb1_write(CmdStart, 32'd1);
|
|
wait1_command();
|
|
apb1_write(BusCtrl, 32'd1);
|
|
apb1_write(BlockSize, 32'd4);
|
|
apb1_write(BlockCount, 32'd1);
|
|
apb1_write(DataCfg, 32'h0000_0002);
|
|
u_card1.arm_read(4096 + 32, 4, 1'b1, 1'b0);
|
|
apb1_write(DataStart, 32'd1);
|
|
wait1_data();
|
|
apb1_read(PioData, value1);
|
|
if (value1 != 32'd0) $fatal(1, "dual SDIO1 data mismatch");
|
|
u_card1.set_irq();
|
|
end
|
|
endtask
|
|
|
|
initial begin
|
|
apb0_idle();
|
|
apb1_idle();
|
|
repeat (4) @(posedge clk_i);
|
|
rst0_n_i = 1'b1;
|
|
rst1_n_i = 1'b1;
|
|
fork
|
|
run_memory_context();
|
|
run_sdio_context();
|
|
join
|
|
apb0_read(Status, value0);
|
|
apb1_read(Status, value1);
|
|
if (value0 == value1) $fatal(1, "dual status contexts were aliased");
|
|
if (sdio1.dat_di_i[1] != 1'b0) begin
|
|
$fatal(1, "dual SDIO1 DAT1 context is not isolated");
|
|
end
|
|
apb0_read(ErrorStatus, value0);
|
|
if (value0 != 0) $fatal(1, "SDIO0 inherited SDIO1 error state: %h", value0);
|
|
$display("SDIO dual-instance isolation and concurrent traffic test passed");
|
|
$finish;
|
|
end
|
|
endmodule
|