Files
NewInstrCalBoard_CPLD/CPLD1/tb_SPI_RegRW.sv
T
2026-06-01 18:03:16 +08:00

847 lines
32 KiB
Systemverilog

//------------------------------------------------------------------------------
//
// Testbench: CPLD1 - SPI Communication & Register R/W
// Project: NewCalBoard DIG
// Tool: Lattice Diamond Verilog-2001 simulator
// Purpose: Verify SPI protocol (MISO/MOSI timing) and register read/write
// on the full RelayConTop design.
//
// Protocol:
// Write: [WR=1][Addr:7b][Data:24b] = 32 bits
// Read: [WR=0][Addr:7b][Don'tCare:24b] -> MISO returns [8'h00][24-bit rdata]
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
`define SYS_CLK_PERIOD 20 // 50 MHz system clock
`define SPI_CLK_PERIOD 400 // 1 MHz SPI clock
module tb_SPI_RegRW;
////==========================================================================
// Signal declarations - mirrors RelayConTop pinout exactly
////==========================================================================
// Clock & reset
reg i_sys_clk;
reg i_rst_n;
// SPI interface (Zynq PS drives these)
reg i_sclk;
reg i_mosi;
reg i_cs;
// Flow control (from Zynq PS / SPI_Con IP)
reg i_rready;
reg i_wvalid;
// Status outputs (monitored by testbench)
wire o_miso;
wire o_wready;
wire o_err;
wire o_con_done;
// Relay control outputs
wire o_IO_RC1;
wire o_RC_VSel;
wire o_RC_ISel;
wire [17:0] o_RC_RLSel;
wire [1:0] o_RC_LOF;
wire [1:0] o_RC_LOS;
wire o_RC_T27;
wire o_RC_T28;
wire o_RC_T29;
wire o_RC_T30;
wire o_RC_T31;
wire o_RC_T32;
// PMU output channels (4 x 32-bit)
wire [31:0] o_PMU_OC_1;
wire [31:0] o_PMU_OC_2;
wire [31:0] o_PMU_OC_3;
wire [31:0] o_PMU_OC_4;
// DMM enable bus (19 bits)
wire [18:0] o_DMM_EN;
////==========================================================================
// DUT instantiation - connect every pin
////==========================================================================
RelayConTop DUT (
.i_sys_clk (i_sys_clk),
.i_rst_n (i_rst_n),
.i_sclk (i_sclk),
.i_mosi (i_mosi),
.i_cs (i_cs),
.o_miso (o_miso),
.i_rready (i_rready),
.i_wvalid (i_wvalid),
.o_wready (o_wready),
.o_err (o_err),
.o_con_done (o_con_done),
.o_DMM_EN (o_DMM_EN),
.o_IO_RC1 (o_IO_RC1),
.o_RC_T27 (o_RC_T27),
.o_RC_T28 (o_RC_T28),
.o_RC_T29 (o_RC_T29),
.o_RC_T30 (o_RC_T30),
.o_RC_T31 (o_RC_T31),
.o_RC_T32 (o_RC_T32),
.o_RC_RLSel (o_RC_RLSel),
.o_RC_VSel (o_RC_VSel),
.o_RC_ISel (o_RC_ISel),
.o_RC_LOF (o_RC_LOF),
.o_RC_LOS (o_RC_LOS),
.o_PMU_OC_1 (o_PMU_OC_1),
.o_PMU_OC_2 (o_PMU_OC_2),
.o_PMU_OC_3 (o_PMU_OC_3),
.o_PMU_OC_4 (o_PMU_OC_4)
);
//==========================================================================
// Clock Generation
//==========================================================================
initial begin
i_sys_clk = 0;
forever #(`SYS_CLK_PERIOD / 2) i_sys_clk = ~i_sys_clk;
end
initial begin
i_sclk = 0;
forever #(`SPI_CLK_PERIOD / 2) i_sclk = ~i_sclk;
end
//==========================================================================
// Test Statistics
//==========================================================================
integer test_pass;
integer test_fail;
integer test_num; // assertion count
integer test_case_num; // test case count (7 main tests + optional)
initial begin
test_pass = 0;
test_fail = 0;
test_num = 0;
test_case_num = 0;
end
task tb_pass;
input string msg;
begin
test_pass = test_pass + 1;
$display("[PASS] Assertion %0d: %s", test_num, msg);
end
endtask
task tb_fail;
input string msg;
input string detail;
begin
test_fail = test_fail + 1;
$display("[FAIL] Assertion %0d: %s >> %s", test_num, msg, detail);
end
endtask
task tb_assert;
input condition;
input string msg;
input string detail;
begin
test_num = test_num + 1;
if (condition) begin
tb_pass(msg);
end else begin
tb_fail(msg, detail);
end
end
endtask
//==========================================================================
// SPI Helper Tasks
//
// SPI Mode 0: CPOL=0, CPHA=0
// - SCLK idle LOW
// - MOSI sampled on RISING edge
// - MISO shifted on FALLING edge
// - MSB first, 32 bits per transaction
//
// MISO is tri-stated (high-Z) when CS is HIGH.
//==========================================================================
// --- spi_send: Drive a 32-bit word out through MOSI ---
task spi_send;
input [31:0] data;
integer i;
begin
@(negedge i_sclk);
i_cs = 1'b0;
@(negedge i_sclk);
for (i = 31; i >= 0; i = i - 1) begin
i_mosi = data[i];
@(posedge i_sclk);
end
@(negedge i_sclk);
i_cs = 1'b1;
i_mosi = 1'b0;
@(negedge i_sclk);
end
endtask
// --- spi_read: Send command, capture response on MISO ---
// C1 Fix: Added @(posedge i_sys_clk) after CS low to allow BUS_Con
// 2-stage sync chain (cs_sync/sclk_sync) to settle before MISO sampling.
// W2 Fix: #1 delay = 1ps (min time step) for clock-to-Q timing margin.
task spi_read;
input [31:0] cmd;
output [31:0] rdata;
integer i;
begin
@(negedge i_sclk);
i_cs = 1'b0;
@(posedge i_sys_clk); // C1: System clock sync for BUS_Con cross-domain settle
rdata = 32'b0;
for (i = 31; i >= 0; i = i - 1) begin
i_mosi = cmd[i];
@(posedge i_sclk);
@(negedge i_sclk);
#1; // W2: 1ps clock-to-Q timing margin for MISO sampling
rdata[i] = o_miso;
end
@(negedge i_sclk);
i_cs = 1'b1;
i_mosi = 1'b0;
@(negedge i_sclk);
end
endtask
// --- spi_write: Send a write command ---
task spi_write;
input [31:0] data;
begin
spi_send(data);
end
endtask
//--- wait_wready: Wait for SPI to be ready ---
task wait_wready;
input [15:0] max_cycles;
output [1:0] result;
integer i;
begin
result = 2'b0;
for (i = 0; i < max_cycles; i = i + 1) begin
#(`SYS_CLK_PERIOD);
if (o_wready) begin
result = 2'b1;
return;
end
end
end
endtask
//==========================================================================
// Convenience: Build SPI frames
//==========================================================================
function [31:0] mk_write;
input [6:0] addr;
input [23:0] data;
begin
mk_write = {1'b1, addr, data};
end
endfunction
function [31:0] mk_read;
input [6:0] addr;
begin
mk_read = {1'b0, addr, 24'b0};
end
endfunction
//==========================================================================
// Main Test Sequence
//==========================================================================
initial begin
// Initialize
i_rst_n = 1'b0;
i_cs = 1'b1;
i_mosi = 1'b0;
#(`SYS_CLK_PERIOD * 4);
i_rst_n = 1'b1;
#(`SYS_CLK_PERIOD * 10);
$display("");
$display("=============================================================");
$display(" CPLD1 SPI Communication & Register R/W Testbench");
$display(" System clock: %d MHz | SPI clock: %d MHz",
1000 / `SYS_CLK_PERIOD, 1000 / `SPI_CLK_PERIOD);
$display("=============================================================");
$display("");
test_spi_miso_mosi_timing();
test_ident_register();
test_register_rw_cycle();
test_write_then_read();
test_rapid_spi_transactions();
test_error_conditions();
test_miso_tri_state();
test_con_done_flow(); // S3: execution flow test
print_summary();
#(`SYS_CLK_PERIOD * 20);
$finish;
end
//==========================================================================
// Test 1: MISO/MOSI SPI Timing
//==========================================================================
task test_spi_miso_mosi_timing;
reg [31:0] rdata;
reg [1:0] wait_result;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 1: MISO/MOSI SPI Timing");
$display("-------------------------------------------------------------");
// 1a. MISO is high-Z when CS is HIGH
$display(" 1a. MISO tri-state when CS=HIGH");
i_cs = 1'b1;
@(negedge i_sclk);
tb_assert(o_miso === 1'bz,
"MISO is high-Z when CS=HIGH",
$sformatf("o_miso = %b (expected z)", o_miso));
// 1b. MISO responds after CS goes LOW
$display(" 1b. MISO active after CS=LOW");
spi_read(mk_read(7'd0), rdata);
tb_assert(o_miso !== 1'bz,
"MISO is active (not high-Z) when CS=LOW",
"");
// 1c. Verify data alignment: first 8 bits are 0, then 24-bit data
$display(" 1c. Verify read response alignment");
spi_read(mk_read(7'd0), rdata);
tb_assert(rdata[7:0] === 8'h00,
"First 8 bits of read response are 0 padding",
$sformatf("rdata[7:0] = 0x%02h (expected 0x00)", rdata[7:0]));
tb_assert(rdata[23:0] === 24'h200010,
"Last 24 bits are IDENT register value",
$sformatf("rdata[23:0] = 0x%06h (expected 0x200010)", rdata[23:0]));
// 1d. Write command: verify MOSI is sampled correctly
$display(" 1d. Write command sent correctly");
spi_write(mk_write(7'd2, (24'h0DEADBEE >> 8))); // W3: parentheses for clarity
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write",
$sformatf("wait_result = %b", wait_result));
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 2: IDENT Register (ADDR 0)
//==========================================================================
task test_ident_register;
reg [31:0] rdata;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 2: IDENT Register (ADDR 0)");
$display("-------------------------------------------------------------");
// 2a. Read IDENT register
$display(" 2a. Read IDENT register");
spi_read(mk_read(7'd0), rdata);
tb_assert(rdata[23:0] === 24'h200010,
"IDENT register returns 24'h200010 (CPLD1, v1.1.0)",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
// 2b. Read IDENT multiple times (should always return same value)
$display(" 2b. Read IDENT multiple times");
spi_read(mk_read(7'd0), rdata);
tb_assert(rdata[23:0] === 24'h200010,
"IDENT register returns same value on repeat read",
"");
spi_read(mk_read(7'd0), rdata);
tb_assert(rdata[23:0] === 24'h200010,
"IDENT register returns same value on 3rd read",
"");
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 3: Register Read/Write Cycle
//==========================================================================
task test_register_rw_cycle;
reg [31:0] rdata;
reg [1:0] wait_result;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 3: Register Read/Write Cycle");
$display("-------------------------------------------------------------");
// 3a. Write to ADDR 2 (Freq_Slot1), then read back
$display(" 3a. Write ADDR 2 = 0x000005, read back");
spi_write(mk_write(7'd2, 24'h000005));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 2",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd2), rdata);
tb_assert(rdata[23:0] === 24'h000005,
"ADDR 2: write 0x000005, read back 0x000005",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
// 3b. Write to ADDR 3, read back
$display(" 3b. Write ADDR 3 = 0x003001, read back");
spi_write(mk_write(7'd3, 24'h003001));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 3",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd3), rdata);
tb_assert(rdata[23:0] === 24'h003001,
"ADDR 3: write 0x003001, read back 0x003001",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
// 3c. Write to ADDR 4, read back
$display(" 3c. Write ADDR 4 = 0x001040, read back");
spi_write(mk_write(7'd4, 24'h001040));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 4",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd4), rdata);
tb_assert(rdata[23:0] === 24'h001040,
"ADDR 4: write 0x001040, read back 0x001040",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
// 3d. Write to ADDR 5, read back
$display(" 3d. Write ADDR 5 = 0x000802, read back");
spi_write(mk_write(7'd5, 24'h000802));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 5",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd5), rdata);
tb_assert(rdata[23:0] === 24'h000802,
"ADDR 5: write 0x000802, read back 0x000802",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
// 3e. Write to ADDR 6 (DC_Slot1), read back
$display(" 3e. Write ADDR 6 = 0x002003, read back");
spi_write(mk_write(7'd6, 24'h002003));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 6",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd6), rdata);
tb_assert(rdata[23:0] === 24'h002003,
"ADDR 6: write 0x002003, read back 0x002003",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
// 3f. Write all DC registers
$display(" 3f. Write ADDR 7-9, read back");
spi_write(mk_write(7'd7, 24'h004005));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 7",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd7), rdata);
tb_assert(rdata[23:0] === 24'h004005,
"ADDR 7: write 0x004005, read back 0x004005",
"");
spi_write(mk_write(7'd8, 24'h006007));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 8",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd8), rdata);
tb_assert(rdata[23:0] === 24'h006007,
"ADDR 8: write 0x006007, read back 0x006007",
"");
spi_write(mk_write(7'd9, 24'h008009));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 9",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd9), rdata);
tb_assert(rdata[23:0] === 24'h008009,
"ADDR 9: write 0x008009, read back 0x008009",
"");
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 4: Write Then Read (separate transactions)
//==========================================================================
task test_write_then_read;
reg [31:0] rdata;
reg [1:0] wait_result;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 4: Write Then Read (separate transactions)");
$display("-------------------------------------------------------------");
// 4a. Write value, wait, then read (verify persistence)
$display(" 4a. Write 0xABCDEF to ADDR 2, read after delay");
spi_write(mk_write(7'd2, 24'hABCDEF));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 2",
$sformatf("wait_result = %b", wait_result));
// Simulate some delay (multiple SPI clock cycles)
#(`SPI_CLK_PERIOD * 3);
spi_read(mk_read(7'd2), rdata);
tb_assert(rdata[23:0] === 24'hABCDEF,
"ADDR 2: value persists after delay",
$sformatf("rdata[23:0] = 0x%06h (expected 0xABCDEF)", rdata[23:0]));
// 4b. Write another value, read immediately
$display(" 4b. Write 0x112233 to ADDR 3, read immediately");
spi_write(mk_write(7'd3, 24'h112233));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 3",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd3), rdata);
tb_assert(rdata[23:0] === 24'h112233,
"ADDR 3: value reads back immediately",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
// 4c. Write 0 to register, read back
$display(" 4c. Write 0x000000 to ADDR 4, read back");
spi_write(mk_write(7'd4, 24'h000000));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 4",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd4), rdata);
tb_assert(rdata[23:0] === 24'h000000,
"ADDR 4: write 0x000000, read back 0x000000",
"");
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 5: Rapid SPI Transactions
//==========================================================================
task test_rapid_spi_transactions;
reg [31:0] rdata;
reg [1:0] wait_result;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 5: Rapid SPI Transactions");
$display("-------------------------------------------------------------");
// 5a. Multiple rapid writes
$display(" 5a. Rapid successive writes");
spi_write(mk_write(7'd2, 24'h000001));
spi_write(mk_write(7'd3, 24'h000002));
spi_write(mk_write(7'd4, 24'h000003));
spi_write(mk_write(7'd5, 24'h000004));
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after rapid writes",
$sformatf("wait_result = %b", wait_result));
// Verify all values
spi_read(mk_read(7'd2), rdata);
tb_assert(rdata[23:0] === 24'h000001,
"Rapid write: ADDR 2 = 0x000001",
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
spi_read(mk_read(7'd3), rdata);
tb_assert(rdata[23:0] === 24'h000002,
"Rapid write: ADDR 3 = 0x000002",
"");
spi_read(mk_read(7'd4), rdata);
tb_assert(rdata[23:0] === 24'h000003,
"Rapid write: ADDR 4 = 0x000003",
"");
spi_read(mk_read(7'd5), rdata);
tb_assert(rdata[23:0] === 24'h000004,
"Rapid write: ADDR 5 = 0x000004",
"");
// 5b. Interleaved write/read
$display(" 5b. Interleaved write/read");
spi_write(mk_write(7'd6, (24'h0DEADBEE >> 8))); // W3: parentheses for clarity
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 6",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd6), rdata);
tb_assert(rdata[23:0] === (24'h0DEADBEE >> 8),
"Interleaved: ADDR 6 correct",
"");
spi_write(mk_write(7'd7, (24'hCAFEBE >> 8))); // W3: parentheses for clarity
wait_wready(100, wait_result); // C2: explicit wready check
tb_assert(wait_result[1] === 1'b1,
"wready asserted after write to ADDR 7",
$sformatf("wait_result = %b", wait_result));
spi_read(mk_read(7'd7), rdata);
tb_assert(rdata[23:0] === (24'hCAFEBE >> 8),
"Interleaved: ADDR 7 correct",
"");
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 6: Error Conditions
//==========================================================================
task test_error_conditions;
reg [31:0] rdata;
reg [1:0] wait_result;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 6: Error Conditions");
$display("-------------------------------------------------------------");
// 6a. Read out-of-bounds address (ADDR 16)
$display(" 6a. Read ADDR 16 (out of bounds)");
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
"o_err should be low before Test 6a",
$sformatf("o_err = %b", o_err));
spi_read(mk_read(7'd16), rdata);
@(negedge i_sclk);
#(`SYS_CLK_PERIOD * 5);
tb_assert(o_err === 1'b1,
"o_err HIGH after reading ADDR 16",
"");
i_rst_n = 1'b0;
#(`SYS_CLK_PERIOD * 4);
i_rst_n = 1'b1;
#(`SYS_CLK_PERIOD * 4);
tb_assert(o_err === 1'b0,
"o_err cleared after reset",
"");
// 6b. Write to out-of-bounds address (ADDR 31)
$display(" 6b. Write ADDR 31 (out of bounds)");
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
"o_err should be low before Test 6b",
$sformatf("o_err = %b", o_err));
spi_write(mk_write(7'd31, 24'h123456));
wait_wready(100, wait_result); // C2: explicit wready check
#(`SYS_CLK_PERIOD * 5);
tb_assert(o_err === 1'b1,
"o_err HIGH after writing ADDR 31",
"");
i_rst_n = 1'b0;
#(`SYS_CLK_PERIOD * 4);
i_rst_n = 1'b1;
#(`SYS_CLK_PERIOD * 4);
tb_assert(o_err === 1'b0,
"o_err cleared after reset",
"");
// 6c. Short SPI transaction (16 bits)
$display(" 6c. Short SPI transaction (16 bits)");
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
"o_err should be low before Test 6c",
$sformatf("o_err = %b", o_err));
i_cs = 1'b0;
#(`SYS_CLK_PERIOD * 2); // W4: allow BUS_Con sync chain to settle
@(negedge i_sclk);
@(negedge i_sclk);
for (integer bi = 0; bi < 16; bi = bi + 1) begin
i_mosi = 1'b0;
@(posedge i_sclk);
@(negedge i_sclk);
end
i_cs = 1'b1;
@(negedge i_sclk);
#(`SYS_CLK_PERIOD * 5);
tb_assert(o_err === 1'b1,
"o_err HIGH after short SPI transaction",
"");
i_rst_n = 1'b0;
#(`SYS_CLK_PERIOD * 4);
i_rst_n = 1'b1;
#(`SYS_CLK_PERIOD * 4);
tb_assert(o_err === 1'b0,
"o_err cleared after reset",
"");
// 6d. Brief CS pulse (too short)
$display(" 6d. Brief CS pulse");
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
"o_err should be low before Test 6d",
$sformatf("o_err = %b", o_err));
i_cs = 1'b0;
#(`SYS_CLK_PERIOD * 2); // W4: allow BUS_Con sync chain to settle
@(negedge i_sclk);
@(negedge i_sclk);
i_cs = 1'b1;
@(negedge i_sclk);
#(`SYS_CLK_PERIOD * 5);
tb_assert(o_err === 1'b1,
"o_err detected brief CS pulse",
"");
i_rst_n = 1'b0;
#(`SYS_CLK_PERIOD * 4);
i_rst_n = 1'b1;
#(`SYS_CLK_PERIOD * 4);
tb_assert(o_err === 1'b0,
"o_err cleared after reset",
"");
#(`SPI_CLK_PERIOD * 2);
end
endtask
//==========================================================================
// Test 7: MISO Tri-State Behavior
//==========================================================================
task test_miso_tri_state;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 7: MISO Tri-State Behavior");
$display("-------------------------------------------------------------");
// 7a. MISO is high-Z when CS is HIGH
$display(" 7a. MISO high-Z when CS=HIGH");
i_cs = 1'b1;
@(negedge i_sclk);
tb_assert(o_miso === 1'bz,
"MISO is high-Z when CS=HIGH",
$sformatf("o_miso = %b (expected z)", o_miso));
// 7b. MISO is active when CS is LOW
$display(" 7b. MISO active when CS=LOW");
i_cs = 1'b0;
@(negedge i_sclk);
tb_assert(o_miso !== 1'bz,
"MISO is active (not high-Z) when CS=LOW",
"");
// 7c. MISO returns to high-Z when CS goes HIGH again
$display(" 7c. MISO returns to high-Z when CS=HIGH");
i_cs = 1'b1;
@(negedge i_sclk);
tb_assert(o_miso === 1'bz,
"MISO returns to high-Z when CS=HIGH",
$sformatf("o_miso = %b (expected z)", o_miso));
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 8: o_con_done Execution Flow (S3)
//==========================================================================
task test_con_done_flow;
reg [31:0] rdata;
reg [1:0] wait_result;
begin
test_case_num = test_case_num + 1;
$display("");
$display("-------------------------------------------------------------");
$display(" Test 8: o_con_done Execution Flow");
$display("-------------------------------------------------------------");
// 8a. Write EXEC bit to ADDR 1 (STATE_REG), then verify o_con_done
$display(" 8a. Write EXEC bit, wait for o_con_done");
spi_write(mk_write(7'd1, 24'h800001)); // Set EXEC bit
wait_wready(100, wait_result);
tb_assert(wait_result[1] === 1'b1,
"wready asserted after EXEC write",
$sformatf("wait_result = %b", wait_result));
// Wait for o_con_done to go high (controller execution complete)
// Poll o_con_done for up to 500 system clock cycles
begin
integer j;
reg found;
found = 1'b0;
for (j = 0; j < 500; j = j + 1) begin
#(`SYS_CLK_PERIOD);
if (o_con_done === 1'b1 && found === 1'b0) begin
tb_assert(1'b1,
"o_con_done asserted within 500 sys clock cycles",
"");
found = 1'b1;
end
end
tb_assert(found === 1'b1,
"o_con_done not asserted within 500 sys clock cycles",
"");
end
// 8b. Read back ADDR 1 to verify STATE bit changed
$display(" 8b. Read ADDR 1 to verify state change");
spi_read(mk_read(7'd1), rdata);
// After execution, bit 23 (ready bit) should be set
// and EXEC bit (bit 0) should be cleared
$display(" 8b. ADDR 1 read value = 0x%06h", rdata[23:0]);
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Summary
//==========================================================================
task print_summary;
begin
$display("");
$display("=============================================================");
$display(" Test Summary");
$display("=============================================================");
$display(" Test cases run: %0d", test_case_num);
$display(" Total assertions: %0d", test_pass + test_fail);
$display(" Passed: %0d", test_pass);
$display(" Failed: %0d", test_fail);
$display("=============================================================");
if (test_fail == 0) begin
$display(" *** ALL TESTS PASSED ***");
end else begin
$display(" *** SOME TESTS FAILED - CHECK DESIGN ***");
end
$display("=============================================================");
$display("");
end
endtask
endmodule