From 34b8bdbdd89fdb036acef73572467b0b04789e0b Mon Sep 17 00:00:00 2001 From: Jeremy Shen Date: Mon, 1 Jun 2026 18:03:16 +0800 Subject: [PATCH] Add SPI_Reg testbench --- CPLD1/tb_SPI_RegRW.sv | 846 ++++++++++++++++++++++++++++ CPLD1/tb_SPI_RegRW/tb_SPI_RegRW.spf | 11 + 2 files changed, 857 insertions(+) create mode 100644 CPLD1/tb_SPI_RegRW.sv create mode 100644 CPLD1/tb_SPI_RegRW/tb_SPI_RegRW.spf diff --git a/CPLD1/tb_SPI_RegRW.sv b/CPLD1/tb_SPI_RegRW.sv new file mode 100644 index 0000000..3895924 --- /dev/null +++ b/CPLD1/tb_SPI_RegRW.sv @@ -0,0 +1,846 @@ +//------------------------------------------------------------------------------ +// +// 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 diff --git a/CPLD1/tb_SPI_RegRW/tb_SPI_RegRW.spf b/CPLD1/tb_SPI_RegRW/tb_SPI_RegRW.spf new file mode 100644 index 0000000..2850eba --- /dev/null +++ b/CPLD1/tb_SPI_RegRW/tb_SPI_RegRW.spf @@ -0,0 +1,11 @@ + + + + + + + + + + +