//------------------------------------------------------------------------------ // // Testbench: CPLD3 - RelayConTop // Project: NewCalBoard DIG // Tool: Lattice Diamond Verilog-2001 simulator // Purpose: Verify SPI interface, register file, and PMU relay control logic // for CPLD3 (PMU_RC[3:0] only). // // CPLD3-specific: // - Outputs: PMU_RC[3:0] only (PMU relay control) // - CPLD_Con handles PMU_RC only (no relay outputs, no DC handling) // - Minimal CPLD_Con: freq slot masks 2 PMU channels, DC slot selects 1 // // Architecture (3 sub-modules): // RelayConTop // - BUS_Con - SPI Mode 0 master interface (32-bit transactions) // - Reg_file - 16-register file + execution FSM // - CPLD_Con - PMU_RC relay control logic // // SPI Protocol: // Frame: [WR:1][Addr:7][Data:24] (MSB first, CPOL=0 CPHA=0) // Read response: [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 (200x slower than sys clk) module tb_RelayConTop; //========================================================================== // Signal declarations - mirrors CPLD3 RelayConTop pinout //========================================================================== // 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 (CPLD3 top module uses typo: i_wvaild) reg i_rready; reg i_wvaild; // Status outputs (monitored by testbench) wire o_miso; wire o_rvalid; wire o_wready; wire o_err; wire o_con_done; // PMU relay control (CPLD3 specific) wire [3:0] o_PMU_RC; // PMU relay control (4-bit) //========================================================================== // DUT instantiation - CPLD3 specific pin names // Note: CPLD3 top module port: i_wvaild (typo), o_rvalid (correct) //========================================================================== 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), .o_rvalid (o_rvalid), .i_rready (i_rready), .i_wvaild (i_wvaild), .o_wready (o_wready), .o_err (o_err), .o_con_done (o_con_done), .o_PMU_RC (o_PMU_RC) ); //========================================================================== // Clock generation //========================================================================== // System clock: 50 MHz (period = 20 ns) initial begin i_sys_clk = 0; forever #(`SYS_CLK_PERIOD / 2) i_sys_clk = ~i_sys_clk; end // SPI clock: 1 MHz (period = 400 ns) 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; initial begin test_pass = 0; test_fail = 0; test_num = 0; end // Helper: log a pass task tb_pass; input string msg; begin test_pass = test_pass + 1; $display("[PASS] Test %0d: %s", test_num, msg); end endtask // Helper: log a fail with optional detail task tb_fail; input string msg; input string detail; begin test_fail = test_fail + 1; $display("[FAIL] Test %0d: %s >> %s", test_num, msg, detail); end endtask // Helper: assert a condition 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 timing: // - CPOL = 0: SCLK idle LOW // - CPHA = 0: data sampled on RISING edge, shifted on FALLING edge // - MSB first, 32 bits per transaction // // Frame layout: [bit31: WR] [bit30:24: Addr(7b)] [bit23:0: Data(24b)] // Read response: [8'h00][24-bit rdata] shifted out on falling SCLK edges // // 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 32-bit response on MISO --- task spi_read; input [31:0] cmd; output [31:0] rdata; integer i; begin @(negedge i_sclk); i_cs = 1'b0; @(negedge i_sclk); rdata = 32'b0; for (i = 31; i >= 0; i = i - 1) begin i_mosi = cmd[i]; @(posedge i_sclk); @(negedge i_sclk); #1; 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 (no response capture needed) --- task spi_write; input [31:0] data; begin spi_send(data); end endtask // --- wait_con_done: Poll con_done for LOW->HIGH cycle --- task wait_con_done; input [15:0] max_cycles; output [1:0] result; integer i; begin result = 2'b0; // Phase 1: Wait for con_done to go LOW (exec started) for (i = 0; i < max_cycles; i = i + 1) begin #(`SYS_CLK_PERIOD); if (!o_con_done) break; end // Phase 2: Wait for con_done to go HIGH (exec finished) for (i = 0; i < max_cycles; i = i + 1) begin #(`SYS_CLK_PERIOD); if (o_con_done) begin result = 2'b1; return; end end end endtask // --- wait_wready: Poll o_wready until HIGH --- 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 frame fields //========================================================================== 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 all signals to safe defaults i_rst_n = 1'b0; i_cs = 1'b1; i_mosi = 1'b0; i_rready = 1'b0; i_wvaild = 1'b0; // Hold reset for 4 system clock cycles (80 ns) #(`SYS_CLK_PERIOD * 4); i_rst_n = 1'b1; // Allow CPLD internal state machines to settle #(`SYS_CLK_PERIOD * 10); $display(""); $display("============================================================="); $display(" CPLD3 RelayConTop - Comprehensive Testbench"); $display(" System clock: %d MHz | SPI clock: %d MHz", 1000 / `SYS_CLK_PERIOD, 1000 / `SPI_CLK_PERIOD); $display(" Outputs: PMU_RC[3:0] (PMU relay control)"); $display("============================================================="); $display(""); test_reset_behavior(); test_register_rw(); test_exec_protocol(); test_pmu_rc_freq_slots(); test_pmu_rc_dc_slots(); test_error_conditions(); test_edge_cases(); print_summary(); #(`SYS_CLK_PERIOD * 20); $finish; end //========================================================================== // Test Group 1: Reset behavior & IDENT register (ADDR 0) //========================================================================== task test_reset_behavior; reg [31:0] r_ident; begin $display("-------------------------------------------------------------"); $display(" Group 1: Reset behavior & IDENT register"); $display("-------------------------------------------------------------"); tb_assert(o_con_done === 1'b1, "o_con_done is HIGH after reset (controller idle)", ""); tb_assert(o_err === 1'b0, "o_err is LOW after reset", ""); tb_assert(o_wready === 1'b1, "o_wready is HIGH after reset (SPI ready)", ""); // IDENT register: CPLD3=bit23, version=1.0.0 = 24'h800100 spi_read(mk_read(7'd0), r_ident); r_ident = r_ident[23:0]; tb_assert(r_ident === 24'h800100, "IDENT register returns 24'h800100 (CPLD3, v1.0.0)", ""); // PMU_RC should be 4'b1111 after reset (CPLD_Con initializes to 1111) tb_assert(o_PMU_RC === 4'b1111, "o_PMU_RC is 4'b1111 after reset (initial state)", ""); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 2: Register read/write (ADDR 0-9) //========================================================================== task test_register_rw; reg [31:0] rdata; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 2: Register read/write (ADDR 0-9)"); $display("-------------------------------------------------------------"); spi_write(mk_write(7'd2, 24'h000005)); @(negedge i_sclk); spi_read(mk_read(7'd2), rdata); tb_assert(rdata === 24'h000005, "Freq_Slot1 (ADDR 2) write/read back", ""); spi_write(mk_write(7'd3, 24'h003001)); @(negedge i_sclk); spi_read(mk_read(7'd3), rdata); tb_assert(rdata === 24'h003001, "Freq_Slot2 (ADDR 3) write/read back", ""); spi_write(mk_write(7'd4, 24'h001040)); @(negedge i_sclk); spi_read(mk_read(7'd4), rdata); tb_assert(rdata === 24'h001040, "Freq_Slot3 (ADDR 4) write/read back", ""); spi_write(mk_write(7'd5, 24'h000802)); @(negedge i_sclk); spi_read(mk_read(7'd5), rdata); tb_assert(rdata === 24'h000802, "Freq_Slot4 (ADDR 5) write/read back", ""); spi_write(mk_write(7'd6, 24'h002003)); @(negedge i_sclk); spi_read(mk_read(7'd6), rdata); tb_assert(rdata === 24'h002003, "DC_Slot1 (ADDR 6) write/read back", ""); spi_write(mk_write(7'd7, 24'h004005)); @(negedge i_sclk); spi_read(mk_read(7'd7), rdata); tb_assert(rdata === 24'h004005, "DC_Slot2 (ADDR 7) write/read back", ""); spi_write(mk_write(7'd8, 24'h006007)); @(negedge i_sclk); spi_read(mk_read(7'd8), rdata); tb_assert(rdata === 24'h006007, "DC_Slot3 (ADDR 8) write/read back", ""); spi_write(mk_write(7'd9, 24'h008009)); @(negedge i_sclk); spi_read(mk_read(7'd9), rdata); tb_assert(rdata === 24'h008009, "DC_Slot4 (ADDR 9) write/read back", ""); spi_read(mk_read(7'd1), rdata); tb_assert(rdata === 24'h800000, "STATE register (ADDR 1) default (RDY=1)", ""); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 3: Execution protocol //========================================================================== task test_exec_protocol; reg [1:0] done_result; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 3: Execution protocol"); $display("-------------------------------------------------------------"); // Configure and trigger execution with Freq_Slot1 spi_write(mk_write(7'd2, 24'h000005)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); // EXEC=1 @(negedge i_sclk); wait_con_done(500, done_result); tb_assert(done_result === 1'b1, "con_done goes HIGH after execution completes", ""); tb_assert(o_err === 1'b0, "No error flag set after successful execution", ""); wait_wready(100, done_result); tb_assert(done_result === 1'b1, "o_wready is HIGH after execution (SPI ready)", ""); // Execute with DC_Slot1 spi_write(mk_write(7'd6, 24'h002003)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_result); tb_assert(done_result === 1'b1, "DC slot execution completes successfully", ""); tb_assert(o_err === 1'b0, "No error after DC slot execution", ""); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 4: PMU_RC freq slot decoding // // CPLD3 CPLD_Con PMU_RC logic (frequency mode): // Slot1 (freq_slot_flag=0001): PMU_RC = 4'b1111 (mask all 4 PMU) // Slot2 (freq_slot_flag=0010): PMU_RC = 4'b1100 (mask PMU 3,4) // Slot3 (freq_slot_flag=0100): PMU_RC = 4'b1010 (mask PMU 1,3) // Slot4 (freq_slot_flag=1000): PMU_RC = 4'b0110 (mask PMU 2,4) // // CPLD3 CPLD_Con PMU_RC logic (DC mode): // Slot1 (dc_slot_flag=0001): PMU_RC = 4'b0001 (select PMU 1) // Slot2 (dc_slot_flag=0010): PMU_RC = 4'b0010 (select PMU 2) // Slot3 (dc_slot_flag=0100): PMU_RC = 4'b0100 (select PMU 3) // Slot4 (dc_slot_flag=1000): PMU_RC = 4'b1000 (select PMU 4) // // Note: PMU_RC is 4-bit where each bit represents a PMU channel. // Freq mode: bits are MASK bits (1 = disabled/masked) // DC mode: bits are SELECT bits (1 = selected channel) //========================================================================== task test_pmu_rc_freq_slots; reg [1:0] done_r; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 4: PMU_RC freq slot decoding"); $display("-------------------------------------------------------------"); // 4a. Freq_Slot1: PMU_RC = 4'b1111 (all masked) // Freq_Slot1 = (0<<10) | (5<<1) | 1 = 24'h000005 $display(" Freq Slot1: PMU_RC = 1111 (all masked)"); spi_write(mk_write(7'd2, 24'h000005)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "Freq Slot1 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b1111, "Freq Slot1: PMU_RC = 4'b1111", $sformatf("PMU_RC=%b", o_PMU_RC)); // 4b. Freq_Slot2: PMU_RC = 4'b1100 (mask PMU 3,4) // Freq_Slot2 = (0<<10) | (5<<1) | 1 = 24'h000006 $display(" Freq Slot2: PMU_RC = 1100 (mask PMU3,4)"); spi_write(mk_write(7'd3, 24'h000006)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "Freq Slot2 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b1100, "Freq Slot2: PMU_RC = 4'b1100", $sformatf("PMU_RC=%b", o_PMU_RC)); // 4c. Freq_Slot3: PMU_RC = 4'b1010 (mask PMU 1,3) // Freq_Slot3 = (0<<10) | (5<<1) | 1 = 24'h000007 $display(" Freq Slot3: PMU_RC = 1010 (mask PMU1,3)"); spi_write(mk_write(7'd4, 24'h000007)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "Freq Slot3 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b1010, "Freq Slot3: PMU_RC = 4'b1010", $sformatf("PMU_RC=%b", o_PMU_RC)); // 4d. Freq_Slot4: PMU_RC = 4'b0110 (mask PMU 2,4) // Freq_Slot4 = (0<<10) | (5<<1) | 1 = 24'h000008 $display(" Freq Slot4: PMU_RC = 0110 (mask PMU2,4)"); spi_write(mk_write(7'd5, 24'h000008)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "Freq Slot4 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b0110, "Freq Slot4: PMU_RC = 4'b0110", $sformatf("PMU_RC=%b", o_PMU_RC)); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 5: PMU_RC DC slot decoding // // CPLD3 CPLD_Con PMU_RC logic (DC mode): // Slot1: PMU_RC = 4'b0001 (select PMU 1) // Slot2: PMU_RC = 4'b0010 (select PMU 2) // Slot3: PMU_RC = 4'b0100 (select PMU 3) // Slot4: PMU_RC = 4'b1000 (select PMU 4) // // DC_Slot N bit layout: // Bit 23: PMU(0)/DPS(1) // Bit 22: V(0)/I(1) // Bit 21:17: Rload_Sel (0-31) // Bit 9:1: Channel_Number (1-256) // Bit 0: Slot_EN (1=Enable) //========================================================================== task test_pmu_rc_dc_slots; reg [1:0] done_r; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 5: PMU_RC DC slot decoding"); $display("-------------------------------------------------------------"); // 5a. DC_Slot1: PMU_RC = 4'b0001 (select PMU 1) // DC_Slot1 = PMU mode, V mode, Rload=3, Ch=3 // = (0<<23) | (0<<22) | (3<<17) | (3<<1) | 1 = 24'h060007 $display(" DC Slot1: PMU_RC = 0001 (select PMU1)"); spi_write(mk_write(7'd6, 24'h060007)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "DC Slot1 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b0001, "DC Slot1: PMU_RC = 4'b0001", $sformatf("PMU_RC=%b", o_PMU_RC)); // 5b. DC_Slot2: PMU_RC = 4'b0010 (select PMU 2) // DC_Slot2 = PMU mode, V mode, Rload=5, Ch=19 // = (0<<23) | (0<<22) | (5<<17) | (19<<1) | 1 = 24'h0A0027 $display(" DC Slot2: PMU_RC = 0010 (select PMU2)"); spi_write(mk_write(7'd7, 24'h0A0027)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "DC Slot2 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b0010, "DC Slot2: PMU_RC = 4'b0010", $sformatf("PMU_RC=%b", o_PMU_RC)); // 5c. DC_Slot3: PMU_RC = 4'b0100 (select PMU 3) // DC_Slot3 = PMU mode, V mode, Rload=10, Ch=27 // = (0<<23) | (0<<22) | (10<<17) | (27<<1) | 1 = 24'h140037 $display(" DC Slot3: PMU_RC = 0100 (select PMU3)"); spi_write(mk_write(7'd8, 24'h140037)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "DC Slot3 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b0100, "DC Slot3: PMU_RC = 4'b0100", $sformatf("PMU_RC=%b", o_PMU_RC)); // 5d. DC_Slot4: PMU_RC = 4'b1000 (select PMU 4) // DC_Slot4 = PMU mode, V mode, Rload=18, Ch=35 // = (0<<23) | (0<<22) | (18<<17) | (35<<1) | 1 = 24'h240047 $display(" DC Slot4: PMU_RC = 1000 (select PMU4)"); spi_write(mk_write(7'd9, 24'h240047)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "DC Slot4 exec done", "timeout"); tb_assert(o_PMU_RC === 4'b1000, "DC Slot4: PMU_RC = 4'b1000", $sformatf("PMU_RC=%b", o_PMU_RC)); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 6: Error conditions //========================================================================== task test_error_conditions; reg [1:0] done_r; reg [31:0] rdata; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 6: Error conditions"); $display("-------------------------------------------------------------"); // 6a. Short SPI transaction (16 bits) $display(" Error 6a: Short SPI transaction (16 bits)"); i_cs = 1'b0; @(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", "o_err not cleared"); #(`SPI_CLK_PERIOD); // 6b. Write to IDENT register (ADDR 0, read-only) $display(" Error 6b: Write to IDENT register (ADDR 0)"); spi_write(mk_write(7'd0, 24'hDEADBEE >> 8)); @(negedge i_sclk); tb_assert(o_err === 1'b1, "o_err HIGH after write to IDENT register", ""); 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", "o_err not cleared"); #(`SPI_CLK_PERIOD); // 6c. Read out-of-bounds address (ADDR 16) $display(" Error 6c: Read out-of-bounds address (ADDR 16)"); 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", "o_err not cleared"); #(`SPI_CLK_PERIOD); // 6d. Multiple slot enable bits (en_t > 1) $display(" Error 6d: Multiple slot enable bits"); spi_write(mk_write(7'd2, 24'h000005)); @(negedge i_sclk); spi_write(mk_write(7'd3, 24'h000006)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); @(negedge i_sclk); wait_con_done(500, done_r); tb_assert(done_r === 1'b1, "Execution completes (even with error)", ""); tb_assert(o_err === 1'b1, "o_err HIGH with multiple slot enables", ""); 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", "o_err not cleared"); #(`SPI_CLK_PERIOD); // 6e. Write to out-of-bounds address (ADDR 31) $display(" Error 6e: Write to out-of-bounds address (ADDR 31)"); spi_write(mk_write(7'd31, 24'h123456)); @(negedge i_sclk); #(`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", "o_err not cleared"); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 7: Edge cases //========================================================================== task test_edge_cases; reg [31:0] rdata; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 7: Edge cases"); $display("-------------------------------------------------------------"); // 7a. Brief CS pulse (too short for 32-bit) $display(" Edge 7a: Brief CS pulse"); i_cs = 1'b0; @(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", "o_err not cleared"); #(`SPI_CLK_PERIOD); // 7b. Rapid successive SPI writes $display(" Edge 7b: Rapid successive SPI 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)); @(negedge i_sclk); spi_read(mk_read(7'd2), rdata); tb_assert(rdata === 24'h000001, "Rapid write: ADDR 2 correct", "ADDR 2 mismatch"); spi_read(mk_read(7'd3), rdata); tb_assert(rdata === 24'h000002, "Rapid write: ADDR 3 correct", "ADDR 3 mismatch"); spi_read(mk_read(7'd4), rdata); tb_assert(rdata === 24'h000003, "Rapid write: ADDR 4 correct", "ADDR 4 mismatch"); spi_read(mk_read(7'd5), rdata); tb_assert(rdata === 24'h000004, "Rapid write: ADDR 5 correct", "ADDR 5 mismatch"); #(`SPI_CLK_PERIOD); // 7c. Write to reserved registers (ADDR 10-15) - no error $display(" Edge 7c: Write to reserved registers"); spi_write(mk_write(7'd10, 24'hABCDEF)); @(negedge i_sclk); spi_write(mk_write(7'd15, 24'h123456)); @(negedge i_sclk); tb_assert(o_err === 1'b0, "No error writing to reserved ADDR 10 and 15", "Unexpected error"); // 7d. Write STATE with EXEC=1 while already executing (should error) $display(" Edge 7d: Write STATE while already executing"); spi_write(mk_write(7'd2, 24'h000005)); @(negedge i_sclk); spi_write(mk_write(7'd1, 24'h000001)); // EXEC=1 @(negedge i_sclk); // Try to write another register while EXEC is still active spi_write(mk_write(7'd3, 24'h000006)); @(negedge i_sclk); #(`SYS_CLK_PERIOD * 5); tb_assert(o_err === 1'b1, "o_err HIGH when writing during execution", ""); 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", "o_err not cleared"); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Summary //========================================================================== task print_summary; begin $display(""); $display("============================================================="); $display(" Test Summary"); $display("============================================================="); $display(" Total tests: %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