feat: add SPI-SD RTL tests and enhance interconnect testbench

- Increased the number of masters in the AXI4 interconnect testbench from 5 to 6.
- Added new testbench for SPI-SD clock functionality (spisd_clock_tb).
- Introduced testbench for SPI-SD command engine (spisd_command_tb).
- Created testbench for SPI-SD CRC calculation (spisd_crc_tb).
- Implemented testbench for SPI-SD data handling (spisd_data_tb).
- Developed wrapper testbench for SPI-SD with APB and training checks (spisd_wrapper_tb).
- Added Python test suite for SPI-SD RTL regressions (test_spisd.py).
- Created a test for verifying register parity between RTL and C header definitions (test_spisd_register_parity.py).
This commit is contained in:
maksyuki committed 2026-08-20 16:12:15 +08:00
1 parent 25e911a503
commit 075344df58
42 files changed
+5282 -1932

No files matched your search

+5 -3
View File
@@ -4,7 +4,7 @@
`include "rib_defs.svh"
module axi4_interconnect_tb;
localparam int NumMasters = 5;
localparam int NumMasters = 6;
localparam int NumTargets = 9;
logic clk_i = 1'b0;
@@ -193,8 +193,9 @@ module axi4_interconnect_tb;
cycles = 0;
while (!masters[0].rvalid && cycles < 100) begin
if (masters[3].arvalid || masters[3].awvalid || masters[3].wvalid ||
masters[4].arvalid || masters[4].awvalid || masters[4].wvalid) begin
$fatal(1, "inactive SDIO master drove a request during management boot traffic");
masters[4].arvalid || masters[4].awvalid || masters[4].wvalid ||
masters[5].arvalid || masters[5].awvalid || masters[5].wvalid) begin
$fatal(1, "inactive storage master drove a request during management boot traffic");
end
@(negedge clk_i);
cycles = cycles + 1;
@@ -291,6 +292,7 @@ module axi4_interconnect_tb;
`INIT_MASTER(2)
`INIT_MASTER(3)
`INIT_MASTER(4)
`INIT_MASTER(5)
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
+68
View File
@@ -0,0 +1,68 @@
`timescale 1ns / 1ps
module spisd_clock_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic enable_i = 1'b0;
logic pause_i = 1'b0;
logic [15:0] half_period_i = 16'd2;
logic sck_o;
logic rise_tick_o;
logic fall_tick_o;
logic running_o;
integer s_rise_count;
integer s_fall_count;
always #1 clk_i = ~clk_i;
spisd_clock u_spisd_clock (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.enable_i (enable_i),
.pause_i (pause_i),
.half_period_i(half_period_i),
.sck_o (sck_o),
.rise_tick_o (rise_tick_o),
.fall_tick_o (fall_tick_o),
.running_o (running_o)
);
always @(posedge clk_i) begin
if (rise_tick_o) s_rise_count = s_rise_count + 1;
if (fall_tick_o) s_fall_count = s_fall_count + 1;
if (rise_tick_o && fall_tick_o) $fatal(1, "both SPI phases asserted");
end
initial begin
s_rise_count = 0;
s_fall_count = 0;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
enable_i = 1'b1;
repeat (16) @(posedge clk_i);
if ((s_rise_count != 4) || (s_fall_count != 4) || !running_o) begin
$fatal(1, "phase counts mismatch: rise=%0d fall=%0d", s_rise_count, s_fall_count);
end
wait (!sck_o);
pause_i = 1'b1;
repeat (8) @(posedge clk_i);
if (sck_o || !running_o || (s_rise_count != 4) || (s_fall_count != 4)) begin
$fatal(1, "low-phase pause failed");
end
pause_i = 1'b0;
wait (sck_o);
enable_i = 1'b0;
repeat (6) @(posedge clk_i);
if (sck_o || running_o || (s_rise_count != 5) || (s_fall_count != 5)) begin
$fatal(1, "clock did not finish the active phase before stopping");
end
half_period_i = 16'd0;
enable_i = 1'b1;
repeat (4) @(posedge clk_i);
if ((s_rise_count != 7) || (s_fall_count != 7)) begin
$fatal(1, "zero divider did not map to one cycle");
end
$display("SPISD phase clock and low-only pause test passed");
$finish;
end
endmodule
+168
View File
@@ -0,0 +1,168 @@
`timescale 1ns / 1ps
module spisd_command_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic clock_enable_i = 1'b0;
logic [15:0] half_period_i = 16'd1;
logic rise_tick;
logic fall_tick;
logic sck;
logic running;
logic start_i = 1'b0;
logic abort_i = 1'b0;
logic [ 5:0] cmd_index_i = 6'd0;
logic [31:0] cmd_arg_i = 32'd0;
spisd_pkg::spisd_resp_type_e resp_type_i = spisd_pkg::SpisdRespNone;
logic stuff_byte_i = 1'b0;
logic [31:0] timeout_cycles_i = 32'd1000;
logic [31:0] busy_timeout_cycles_i = 32'd1000;
logic miso_i = 1'b1;
logic mosi_o;
logic busy_o;
logic done_o;
logic error_o;
logic timeout_o;
logic busy_timeout_o;
logic [ 7:0] error_code_o;
logic [39:0] response_o;
logic [ 5:0] last_cmd_index_o;
logic [47:0] s_captured_cmd;
logic [ 7:0] s_model_resp;
integer s_captured_bits;
integer s_resp_bit;
logic s_capture_cmd;
logic s_send_resp;
always #1 clk_i = ~clk_i;
spisd_clock u_spisd_clock (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.enable_i (clock_enable_i),
.pause_i (1'b0),
.half_period_i(half_period_i),
.sck_o (sck),
.rise_tick_o (rise_tick),
.fall_tick_o (fall_tick),
.running_o (running)
);
spisd_command u_spisd_command (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.rise_tick_i (rise_tick),
.fall_tick_i (fall_tick),
.start_i (start_i),
.abort_i (abort_i),
.cmd_index_i (cmd_index_i),
.cmd_arg_i (cmd_arg_i),
.resp_type_i (resp_type_i),
.stuff_byte_i (stuff_byte_i),
.timeout_cycles_i (timeout_cycles_i),
.busy_timeout_cycles_i(busy_timeout_cycles_i),
.miso_i (miso_i),
.mosi_o (mosi_o),
.busy_o (busy_o),
.done_o (done_o),
.error_o (error_o),
.timeout_o (timeout_o),
.busy_timeout_o (busy_timeout_o),
.error_code_o (error_code_o),
.response_o (response_o),
.last_cmd_index_o (last_cmd_index_o)
);
always @(posedge sck) begin
if (s_capture_cmd && (s_captured_bits < 48)) begin
s_captured_cmd = {s_captured_cmd[46:0], mosi_o};
s_captured_bits = s_captured_bits + 1;
end
end
always @(negedge sck) begin
if (s_send_resp && (s_captured_bits == 48) && (s_resp_bit < 8)) begin
miso_i <= s_model_resp[7-s_resp_bit];
s_resp_bit = s_resp_bit + 1;
end else begin
miso_i <= 1'b1;
end
end
task automatic start_command;
begin
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
end
endtask
task automatic wait_complete;
begin
wait (done_o);
clock_enable_i = 1'b0;
@(posedge clk_i);
wait (!done_o);
wait (!running);
end
endtask
initial begin
s_captured_cmd = '0;
s_captured_bits = 0;
s_resp_bit = 0;
s_capture_cmd = 1'b1;
s_send_resp = 1'b0;
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
clock_enable_i = 1'b1;
cmd_index_i = 6'd0;
cmd_arg_i = 32'd0;
resp_type_i = spisd_pkg::SpisdRespNone;
start_command();
wait (done_o);
if (error_o || (s_captured_bits != 48) ||
(s_captured_cmd != {2'b01, 6'd0, 32'd0, 7'h4A, 1'b1}) ||
(last_cmd_index_o != 6'd0)) begin
$fatal(1, "command frame failed: frame=%h bits=%0d err=%b", s_captured_cmd, s_captured_bits,
error_o);
end
wait_complete();
s_captured_cmd = '0;
s_captured_bits = 0;
s_resp_bit = 0;
s_model_resp = 8'h01;
s_send_resp = 1'b1;
cmd_index_i = 6'd8;
cmd_arg_i = 32'h0000_01AA;
resp_type_i = spisd_pkg::SpisdRespR1;
start_command();
clock_enable_i = 1'b1;
wait (done_o);
if (error_o || (response_o[7:0] != 8'h01) ||
(s_captured_cmd != {2'b01, 6'd8, 32'h0000_01AA, 7'h43, 1'b1})) begin
$fatal(1, "R1 command/response failed: frame=%h response=%h", s_captured_cmd, response_o);
end
wait_complete();
s_captured_bits = 0;
s_capture_cmd = 1'b0;
s_send_resp = 1'b0;
miso_i = 1'b1;
timeout_cycles_i = 32'd12;
cmd_index_i = 6'd13;
cmd_arg_i = 32'd0;
resp_type_i = spisd_pkg::SpisdRespR1;
start_command();
clock_enable_i = 1'b1;
wait (done_o);
if (!error_o || !timeout_o || busy_timeout_o ||
(error_code_o != spisd_pkg::SpisdErrCmdTimeout)) begin
$fatal(1, "response timeout classification failed");
end
$display("SPISD command frame, R1 response, and timeout test passed");
$finish;
end
endmodule
+29
View File
@@ -0,0 +1,29 @@
`timescale 1ns / 1ps
module spisd_crc_tb;
logic [15:0] s_crc16;
initial begin
if (spisd_pkg::spisd_crc7_calc({2'b01, 6'd0, 32'd0}) != 7'h4A) begin
$fatal(1, "CMD0 CRC7 vector failed");
end
if (spisd_pkg::spisd_crc7_calc({2'b01, 6'd8, 32'h0000_01AA}) != 7'h43) begin
$fatal(1, "CMD8 CRC7 vector failed");
end
s_crc16 = 16'h0000;
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "1");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "2");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "3");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "4");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "5");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "6");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "7");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "8");
s_crc16 = spisd_pkg::spisd_crc16_byte(s_crc16, "9");
if (s_crc16 != 16'h31C3) begin
$fatal(1, "CRC16 vector failed: %h", s_crc16);
end
$display("SPISD CRC vectors passed");
$finish;
end
endmodule
+220
View File
@@ -0,0 +1,220 @@
`timescale 1ns / 1ps
module spisd_data_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic rise_tick_i = 1'b0;
logic fall_tick_i = 1'b0;
logic start_i = 1'b0;
logic abort_i = 1'b0;
spisd_pkg::spisd_data_direction_e direction_i = spisd_pkg::SpisdDataFromCard;
logic multi_block_i = 1'b0;
logic crc_check_i = 1'b1;
logic [15:0] block_size_i = 16'd4;
logic [15:0] block_count_i = 16'd1;
logic [31:0] timeout_cycles_i = 32'd1000;
logic [31:0] busy_timeout_cycles_i = 32'd1000;
logic miso_i = 1'b1;
logic mosi_o;
logic clock_pause_o;
logic tx_valid_i = 1'b0;
logic tx_ready_o;
logic [31:0] tx_data_i = 32'd0;
logic [ 3:0] tx_strb_i = 4'd0;
logic tx_last_i = 1'b0;
logic rx_valid_o;
logic rx_ready_i = 1'b0;
logic [31:0] rx_data_o;
logic [ 3:0] rx_strb_o;
logic rx_last_o;
logic busy_o;
logic done_o;
logic error_o;
logic timeout_o;
logic crc_error_o;
logic busy_timeout_o;
logic [ 7:0] error_code_o;
logic [15:0] blocks_done_o;
logic [87:0] s_write_serial;
logic [15:0] s_write_crc;
always #1 clk_i = ~clk_i;
spisd_data u_spisd_data (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.rise_tick_i (rise_tick_i),
.fall_tick_i (fall_tick_i),
.start_i (start_i),
.abort_i (abort_i),
.direction_i (direction_i),
.multi_block_i (multi_block_i),
.crc_check_i (crc_check_i),
.block_size_i (block_size_i),
.block_count_i (block_count_i),
.timeout_cycles_i (timeout_cycles_i),
.busy_timeout_cycles_i(busy_timeout_cycles_i),
.miso_i (miso_i),
.mosi_o (mosi_o),
.clock_pause_o (clock_pause_o),
.tx_valid_i (tx_valid_i),
.tx_ready_o (tx_ready_o),
.tx_data_i (tx_data_i),
.tx_strb_i (tx_strb_i),
.tx_last_i (tx_last_i),
.rx_valid_o (rx_valid_o),
.rx_ready_i (rx_ready_i),
.rx_data_o (rx_data_o),
.rx_strb_o (rx_strb_o),
.rx_last_o (rx_last_o),
.busy_o (busy_o),
.done_o (done_o),
.error_o (error_o),
.timeout_o (timeout_o),
.crc_error_o (crc_error_o),
.busy_timeout_o (busy_timeout_o),
.error_code_o (error_code_o),
.blocks_done_o (blocks_done_o)
);
task automatic pulse_start;
begin
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
end
endtask
task automatic rise_bit(input logic value);
begin
@(negedge clk_i);
miso_i = value;
rise_tick_i = 1'b1;
@(negedge clk_i);
rise_tick_i = 1'b0;
end
endtask
task automatic rise_byte(input logic [7:0] value);
begin
for (integer bit_index = 7; bit_index >= 0; bit_index--) begin
rise_bit(value[bit_index]);
end
end
endtask
task automatic fall_capture;
begin
@(negedge clk_i);
s_write_serial = {s_write_serial[86:0], mosi_o};
fall_tick_i = 1'b1;
@(negedge clk_i);
fall_tick_i = 1'b0;
end
endtask
task automatic consume_read_word;
begin
wait (rx_valid_o);
if ((rx_data_o != 32'h4433_2211) || (rx_strb_o != 4'hF) || !rx_last_o || !clock_pause_o) begin
$fatal(1, "read stream packing/backpressure failed: data=%h strb=%h last=%b pause=%b",
rx_data_o, rx_strb_o, rx_last_o, clock_pause_o);
end
@(negedge clk_i);
rx_ready_i = 1'b1;
@(negedge clk_i);
rx_ready_i = 1'b0;
end
endtask
initial begin
repeat (2) @(posedge clk_i);
rst_n_i = 1'b1;
direction_i = spisd_pkg::SpisdDataFromCard;
pulse_start();
rise_byte(8'hFF);
rise_byte(8'hFE);
rise_byte(8'h11);
rise_byte(8'h22);
rise_byte(8'h33);
rise_byte(8'h44);
consume_read_word();
rise_byte(8'hDD);
rise_byte(8'h33);
wait (done_o);
if (error_o || (blocks_done_o != 16'd1)) begin
$fatal(1, "valid read block failed: err=%b code=%h blocks=%0d", error_o, error_code_o,
blocks_done_o);
end
@(posedge clk_i);
wait (!done_o);
direction_i = spisd_pkg::SpisdDataFromCard;
pulse_start();
rise_byte(8'hFE);
rise_byte(8'h11);
rise_byte(8'h22);
rise_byte(8'h33);
rise_byte(8'h44);
consume_read_word();
rise_byte(8'h00);
rise_byte(8'h00);
wait (done_o);
if (!error_o || !crc_error_o || (error_code_o != spisd_pkg::SpisdErrDataCrc)) begin
$fatal(1, "read CRC fault was not classified");
end
@(posedge clk_i);
wait (!done_o);
direction_i = spisd_pkg::SpisdDataToCard;
block_size_i = 16'd8;
tx_data_i = 32'h4433_2211;
tx_strb_i = 4'hF;
tx_last_i = 1'b0;
s_write_crc = 16'h0000;
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h11);
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h22);
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h33);
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h44);
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h55);
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h66);
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h77);
s_write_crc = spisd_pkg::spisd_crc16_byte(s_write_crc, 8'h88);
s_write_serial = '0;
pulse_start();
wait (tx_ready_o);
@(negedge clk_i);
tx_valid_i = 1'b1;
@(negedge clk_i);
tx_valid_i = 1'b0;
repeat (40) fall_capture();
wait (tx_ready_o);
tx_data_i = 32'h8877_6655;
tx_last_i = 1'b1;
@(negedge clk_i);
tx_valid_i = 1'b1;
@(negedge clk_i);
tx_valid_i = 1'b0;
repeat (48) fall_capture();
if (s_write_serial != {8'hFE, 64'h1122_3344_5566_7788, s_write_crc}) begin
$fatal(1, "write token/payload/CRC serialization failed: got=%h crc=%h", s_write_serial,
s_write_crc);
end
rise_bit(1'b0);
rise_bit(1'b0);
rise_bit(1'b1);
rise_bit(1'b0);
rise_bit(1'b1);
rise_bit(1'b0);
rise_bit(1'b1);
wait (done_o);
if (error_o || timeout_o || busy_timeout_o || (blocks_done_o != 16'd1)) begin
$fatal(1, "write response/busy completion failed: err=%b code=%h blocks=%0d", error_o,
error_code_o, blocks_done_o);
end
$display("SPISD read/write data, CRC, and backpressure test passed");
$finish;
end
endmodule
+234
View File
@@ -0,0 +1,234 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
module spisd_wrapper_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
integer s_train_rise_count;
integer s_training_timeout;
integer s_command_timeout;
logic [31:0] s_read_data;
logic s_access_error;
logic s_card_miso;
logic s_card_power_on;
logic s_count_training;
apb4_if apb4 (
.pclk (clk_i),
.presetn(rst_n_i)
);
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) dma_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
spi_if spi ();
always #5 clk_i = ~clk_i;
assign spi.miso_i = s_card_miso;
assign dma_axi4.awready = 1'b1;
assign dma_axi4.wready = 1'b1;
assign dma_axi4.bid = '0;
assign dma_axi4.bresp = `AXI4_RESP_OKAY;
assign dma_axi4.buser = '0;
assign dma_axi4.bvalid = 1'b0;
assign dma_axi4.arready = 1'b1;
assign dma_axi4.rid = '0;
assign dma_axi4.rdata = '0;
assign dma_axi4.rresp = `AXI4_RESP_OKAY;
assign dma_axi4.rlast = 1'b1;
assign dma_axi4.ruser = '0;
assign dma_axi4.rvalid = 1'b0;
apb4_spisd #(
.InputClockHz(72_000_000),
.AddrWidth (32),
.DataWidth (32),
.DescCount (16),
.FifoDepth (16)
) u_apb4_spisd (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.apb4 (apb4),
.dma_axi4(dma_axi4),
.spi (spi)
);
spisd_card u_spisd_card (
.sck (spi.sck_o),
.cs_n (spi.nss_o),
.mosi (spi.mosi_o),
.miso (s_card_miso),
.power_on(s_card_power_on)
);
always @(posedge spi.sck_o) begin
if (s_count_training) begin
s_train_rise_count = s_train_rise_count + 1;
if (!spi.nss_o) $fatal(1, "chip select asserted during power-up clock training");
end
end
task automatic apb_write(input logic [31:0] addr, input logic [31:0] data,
output logic access_error);
begin
@(negedge clk_i);
apb4.paddr = addr;
apb4.pwrite = 1'b1;
apb4.pwdata = data;
apb4.pstrb = 4'hF;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk_i);
apb4.penable = 1'b1;
@(posedge clk_i);
while (!apb4.pready) @(posedge clk_i);
#1;
access_error = apb4.pslverr;
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
end
endtask
task automatic apb_read(input logic [31:0] addr, output logic [31:0] data,
output logic access_error);
begin
@(negedge clk_i);
apb4.paddr = addr;
apb4.pwrite = 1'b0;
apb4.pstrb = 4'h0;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk_i);
apb4.penable = 1'b1;
@(posedge clk_i);
while (!apb4.pready) @(posedge clk_i);
#1;
data = apb4.prdata;
access_error = apb4.pslverr;
@(negedge clk_i);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
initial begin
s_train_rise_count = 0;
s_count_training = 1'b1;
s_card_power_on = 1'b0;
apb4.paddr = '0;
apb4.pprot = '0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = '0;
apb4.pstrb = '0;
repeat (3) @(posedge clk_i);
rst_n_i = 1'b1;
s_card_power_on = 1'b1;
@(posedge clk_i);
s_card_power_on = 1'b0;
apb_read(32'h0000_0000, s_read_data, s_access_error);
if (s_access_error || (s_read_data != 32'h5350_4953)) begin
$fatal(1, "IP identification read failed: data=%h err=%b", s_read_data, s_access_error);
end
apb_read(32'h0000_0008, s_read_data, s_access_error);
if (s_access_error || (s_read_data[7:0] != 8'hFF)) begin
$fatal(1, "capability read failed: data=%h err=%b", s_read_data, s_access_error);
end
apb_write(32'h0000_0000, 32'd0, s_access_error);
if (!s_access_error) $fatal(1, "read-only register write did not return PSLVERR");
apb_read(32'h0000_03FC, s_read_data, s_access_error);
if (!s_access_error) $fatal(1, "undefined register read did not return PSLVERR");
apb_write(32'h0000_000C, 32'h0000_0001, s_access_error);
if (s_access_error) $fatal(1, "HOST_CTRL write failed");
apb_write(32'h0000_0010, 32'h0000_0103, s_access_error);
if (s_access_error) $fatal(1, "CLOCK_CTRL training write failed");
s_training_timeout = 0;
while ((s_train_rise_count < 80) && (s_training_timeout < 500)) begin
@(posedge clk_i);
s_training_timeout = s_training_timeout + 1;
end
if (s_train_rise_count != 80) begin
$fatal(1, "power-up clock training timed out at %0d rising edges", s_train_rise_count);
end
repeat (6) @(posedge clk_i);
if ((s_train_rise_count != 80) || spi.sck_o || !spi.nss_o) begin
$fatal(1, "training completion failed: edges=%0d sck=%b cs_n=%b", s_train_rise_count,
spi.sck_o, spi.nss_o);
end
apb_read(32'h0000_0024, s_read_data, s_access_error);
if (s_access_error || s_read_data[0]) begin
$fatal(1, "controller remained busy after training: status=%h", s_read_data);
end
apb_read(32'h0000_0014, s_read_data, s_access_error);
if (s_access_error || (s_read_data != 32'd36_000_000)) begin
$fatal(1, "actual clock reporting failed: %0d", s_read_data);
end
s_count_training = 1'b0;
apb_write(32'h0000_0040, 32'd0, s_access_error);
if (s_access_error) $fatal(1, "CMD_ARG write failed");
apb_write(32'h0000_0044, 32'h0000_0100, s_access_error);
if (s_access_error) $fatal(1, "CMD_CFG write failed");
apb_write(32'h0000_0048, 32'd1, s_access_error);
if (s_access_error) $fatal(1, "CMD_START write failed");
s_command_timeout = 0;
apb_read(32'h0000_004C, s_read_data, s_access_error);
while (s_read_data[0] && (s_command_timeout < 500)) begin
apb_read(32'h0000_004C, s_read_data, s_access_error);
s_command_timeout = s_command_timeout + 1;
end
if (s_access_error || s_read_data[2:1] || (s_command_timeout >= 500)) begin
$fatal(1, "CMD0 failed: status=%h timeout=%0d", s_read_data, s_command_timeout);
end
apb_read(32'h0000_0050, s_read_data, s_access_error);
if (s_access_error || (s_read_data[7:0] != 8'h01)) begin
$fatal(1, "CMD0 response failed: response=%h", s_read_data);
end
u_spisd_card.initialized = 1'b1;
apb_write(32'h0000_0080, 32'd512, s_access_error);
apb_write(32'h0000_0084, 32'd1, s_access_error);
apb_write(32'h0000_0088, 32'h0000_0050, s_access_error);
apb_write(32'h0000_00C0, 32'h0000_0001, s_access_error);
apb_write(32'h0000_00C4, 32'd1, s_access_error);
apb_write(32'h0000_0040, 32'd0, s_access_error);
apb_write(32'h0000_0044, 32'h0000_2111, s_access_error);
if (s_access_error) $fatal(1, "DMA command setup failed");
apb_write(32'h0000_00C8, 32'd1, s_access_error);
if (s_access_error) $fatal(1, "DMA command alias start failed");
s_command_timeout = 0;
apb_read(32'h0000_0024, s_read_data, s_access_error);
while (s_read_data[0] && (s_command_timeout < 500)) begin
apb_read(32'h0000_0024, s_read_data, s_access_error);
s_command_timeout = s_command_timeout + 1;
end
if (s_access_error || (s_command_timeout >= 500)) begin
$fatal(1, "descriptor error did not terminate the transaction");
end
apb_read(32'h0000_004C, s_read_data, s_access_error);
if (s_access_error || !s_read_data[1] || (s_read_data[13:6] != 8'h08)) begin
$fatal(1, "descriptor error did not abort the command engine: %h", s_read_data);
end
apb_read(32'h0000_00CC, s_read_data, s_access_error);
if (s_access_error || !s_read_data[2] || (s_read_data[10:3] != 8'h02)) begin
$fatal(1, "descriptor error classification failed: %h", s_read_data);
end
apb_read(32'h0000_010C, s_read_data, s_access_error);
if (s_access_error || !s_read_data[6]) begin
$fatal(1, "descriptor error was not latched: %h", s_read_data);
end
$display("SPISD APB ABI, training, card model, and DMA abort test passed");
$finish;
end
endmodule