mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
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:
42 files changed
+5282
-1932
No files matched your search
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user