//------------------------------------------------------------------------------ // // Testbench: CPLD1 - RelayConTop // Project: NewCalBoard DIG // Tool: Lattice Diamond Verilog-2001 simulator // Purpose: Verify SPI interface, register file, and relay control logic // for CPLD1 (main relay controller + PMU output + DMM enable). // // Architecture (3 sub-modules): // RelayConTop // - BUS_Con - SPI Mode 0 master interface (32-bit transactions) // - Reg_file - 16-register file + execution FSM // - CPLD_Con - Relay control logic (freq + DC/PMU decoding) // // SPI Protocol: // Frame: [WR:1][Addr:7][Data:24] (MSB first, CPOL=0 CPHA=0) // Read response: [8'h00][24-bit rdata] // // DPS path is NOT implemented (stubbed out) - all DC tests use PMU mode. // //------------------------------------------------------------------------------ `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 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_rvalid; 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 ////========================================================================== // 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 (Verilog-2001 compatible) task tb_pass; input [132:1] 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 [132:1] msg; input [132:1] detail; begin test_fail = test_fail + 1; $display("[FAIL] Test %0d: %s >> %s", test_num, msg, detail); end endtask // Helper: assert a condition (Verilog-2001 compatible) task tb_assert; input condition; input [132:1] msg; input [132:1] 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); // CPLD uses sys_clk domain for internal logic. Add #1ns delay // after falling edge to let CPLD update o_miso before we sample. 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_wvalid = 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(" CPLD1 RelayConTop - Comprehensive Testbench"); $display(" System clock: %d MHz | SPI clock: %d MHz", 1000 / `SYS_CLK_PERIOD, 1000 / `SPI_CLK_PERIOD); $display("============================================================="); $display(""); test_reset_behavior(); test_register_rw(); test_exec_protocol(); test_freq_slots(); test_dc_slots_pmu(); test_rc_tx_output(); 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: CPLD1=bit21, version=1.1.0 = 24'h200010 spi_read(mk_read(7'd0), r_ident); r_ident = r_ident[23:0]; tb_assert(r_ident === 24'h200010, "IDENT register returns 24'h200010 (CPLD1, v1.1.0)", ""); tb_assert(o_DMM_EN === 19'b0, "o_DMM_EN is all zeros after reset", ""); tb_assert(o_RC_RLSel === 18'b0, "o_RC_RLSel is all zeros after reset", ""); tb_assert(o_PMU_OC_1 === 32'b0 && o_PMU_OC_2 === 32'b0 && o_PMU_OC_3 === 32'b0 && o_PMU_OC_4 === 32'b0, "All PMU_OC outputs are zero after reset", ""); #(`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 (PMU mode)", ""); 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 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: Frequency slot decoding (all 4 slots) ////========================================================================== task test_freq_slots; reg [1:0] done_r; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 4: Frequency slot decoding (all 4 slots)"); $display("-------------------------------------------------------------"); // Slot1 Ch5 (range 1-8): OC_x01=1, DMM_EN[0]=1 $display(" Slot1 Ch5 (range 1-8): OC_x01=1, DMM_EN[0]=1"); 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, "Slot1 Ch5 exec done", "timeout"); tb_assert(o_RC_T27 === 1'b0, "Slot1: RC_T27 idle", "RC_T27 mismatch"); tb_assert(o_PMU_OC_1[0] === 1'b1, "Slot1 Ch5: PMU_OC_1[0]=1", "PMU_OC_1[0] mismatch"); tb_assert(o_DMM_EN[0] === 1'b1, "Slot1 Ch5: DMM_EN[0]=1", "DMM_EN[0] mismatch"); tb_assert(o_PMU_OC_2[0] === 1'b0, "Slot1 Ch5: PMU_OC_2[0]=0", "PMU_OC_2[0] mismatch"); #(`SPI_CLK_PERIOD); // Slot1 Ch50 (range 9-72): DMM_EN[1]=1 // = (0<<10) | (50<<1) | 1 = 24'h000065 $display(" Slot1 Ch50 (range 9-72): DMM_EN[1]=1"); spi_write(mk_write(7'd2, 24'h000065)); @(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, "Slot1 Ch50 exec done", "timeout"); tb_assert(o_DMM_EN[1] === 1'b1, "Slot1 Ch50: DMM_EN[1]=1", "DMM_EN[1] mismatch"); tb_assert(o_PMU_OC_1[0] === 1'b0, "Slot1 Ch50: PMU_OC_1[0]=0", "PMU_OC_1[0] mismatch"); #(`SPI_CLK_PERIOD); // Slot1 Ch100 (range 73-136): OC_x17=1, DMM_EN[2]=1 // = (0<<10) | (100<<1) | 1 = 24'h0000C9 $display(" Slot1 Ch100 (range 73-136): OC_x17=1, DMM_EN[2]=1"); spi_write(mk_write(7'd2, 24'h0000C9)); @(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, "Slot1 Ch100 exec done", "timeout"); tb_assert(o_PMU_OC_1[16] === 1'b1, "Slot1 Ch100: PMU_OC_1[16]=1", "PMU_OC_1[16] mismatch"); tb_assert(o_DMM_EN[2] === 1'b1, "Slot1 Ch100: DMM_EN[2]=1", "DMM_EN[2] mismatch"); #(`SPI_CLK_PERIOD); // Slot1 Ch150 (range 137-200): DMM_EN[3]=1 // = (0<<10) | (150<<1) | 1 = 24'h00012D $display(" Slot1 Ch150 (range 137-200): DMM_EN[3]=1"); spi_write(mk_write(7'd2, 24'h00012D)); @(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, "Slot1 Ch150 exec done", "timeout"); tb_assert(o_DMM_EN[3] === 1'b1, "Slot1 Ch150: DMM_EN[3]=1", "DMM_EN[3] mismatch"); #(`SPI_CLK_PERIOD); // Slot2 Ch5 (range 1-8): OC_x01=2, DMM_EN[4]=1 // = (0<<10) | (5<<1) | 1 = 24'h000006 $display(" Slot2 Ch5 (range 1-8): OC_x01=2, DMM_EN[4]=1"); 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, "Slot2 Ch5 exec done", "timeout"); tb_assert(o_PMU_OC_2[0] === 1'b1, "Slot2 Ch5: PMU_OC_2[0]=1", "PMU_OC_2[0] mismatch"); tb_assert(o_DMM_EN[4] === 1'b1, "Slot2 Ch5: DMM_EN[4]=1", "DMM_EN[4] mismatch"); #(`SPI_CLK_PERIOD); // Slot3 Ch100 (range 73-136): OC_x17=4, DMM_EN[10]=1 // = (0<<10) | (100<<1) | 1 = 24'h0000C9 $display(" Slot3 Ch100 (range 73-136): OC_x17=4, DMM_EN[10]=1"); spi_write(mk_write(7'd4, 24'h0000C9)); @(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, "Slot3 Ch100 exec done", "timeout"); tb_assert(o_PMU_OC_3[16] === 1'b1, "Slot3 Ch100: PMU_OC_3[16]=1", "PMU_OC_3[16] mismatch"); tb_assert(o_DMM_EN[10] === 1'b1, "Slot3 Ch100: DMM_EN[10]=1", "DMM_EN[10] mismatch"); #(`SPI_CLK_PERIOD); // Slot4 Ch50 (range 9-72): DMM_EN[5]=1 // = (0<<10) | (50<<1) | 1 = 24'h000065 $display(" Slot4 Ch50 (range 9-72): DMM_EN[5]=1"); spi_write(mk_write(7'd5, 24'h000065)); @(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, "Slot4 Ch50 exec done", "timeout"); tb_assert(o_DMM_EN[5] === 1'b1, "Slot4 Ch50: DMM_EN[5]=1", "DMM_EN[5] mismatch"); #(`SPI_CLK_PERIOD * 2); end endtask ////========================================================================== // Test Group 5: DC slot PMU mode ////========================================================================== task test_dc_slots_pmu; reg [1:0] done_r; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 5: DC slot PMU mode (DPS is stubbed, not tested)"); $display("-------------------------------------------------------------"); // 5a. DC_Slot1: V mode, Rload=3, Ch=3, PMU // = (3<<17) | (3<<1) | 1 = 24'h060007 $display(" DC1: V mode, Rload=3, Ch=3, PMU"); 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, "DC1 V-mode exec done", "timeout"); tb_assert(o_RC_VSel === 1'b1, "DC1: RC_VSel=1 (V mode)", "RC_VSel mismatch"); tb_assert(o_RC_ISel === 1'b0, "DC1: RC_ISel=0 (V mode)", "RC_ISel mismatch"); tb_assert(o_RC_RLSel === 18'b100, "DC1: RC_RLSel=18'b100 (Rload=3)", "RC_RLSel mismatch"); tb_assert(o_PMU_OC_1[0] === 1'b1, "DC1: PMU_OC_1[0]=1 (Ch=3)", "PMU_OC_1[0] mismatch"); #(`SPI_CLK_PERIOD); // 5b. DC_Slot1: I mode, Rload=1, Ch=11, PMU // = (1<<22) | (1<<17) | (11<<1) | 1 = 24'h420017 $display(" DC1: I mode, Rload=1, Ch=11, PMU"); spi_write(mk_write(7'd6, 24'h420017)); @(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, "DC1 I-mode exec done", "timeout"); tb_assert(o_RC_VSel === 1'b0, "DC1: RC_VSel=0 (I mode)", "RC_VSel mismatch"); tb_assert(o_RC_ISel === 1'b1, "DC1: RC_ISel=1 (I mode)", "RC_ISel mismatch"); tb_assert(o_RC_RLSel[0] === 1'b1, "DC1: RC_RLSel[0]=1 (Rload=1)", "RC_RLSel mismatch"); tb_assert(o_PMU_OC_1[1] === 1'b1, "DC1: PMU_OC_1[1]=1 (Ch=11)", "PMU_OC_1[1] mismatch"); #(`SPI_CLK_PERIOD); // 5c. DC_Slot2: V mode, Rload=5, Ch=19, PMU // = (5<<17) | (19<<1) | 1 = 24'h0A0027 $display(" DC2: V mode, Rload=5, Ch=19, PMU"); 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, "DC2 V-mode exec done", "timeout"); tb_assert(o_RC_VSel === 1'b1, "DC2: RC_VSel=1 (V mode)", "RC_VSel mismatch"); tb_assert(o_PMU_OC_2[2] === 1'b1, "DC2: PMU_OC_2[2]=1 (Ch=19)", "PMU_OC_2[2] mismatch"); #(`SPI_CLK_PERIOD); // 5d. DC_Slot3: V mode, Rload=10, Ch=27, PMU // = (10<<17) | (27<<1) | 1 = 24'h140037 $display(" DC3: V mode, Rload=10, Ch=27, PMU"); 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, "DC3 V-mode exec done", "timeout"); tb_assert(o_PMU_OC_3[3] === 1'b1, "DC3: PMU_OC_3[3]=1 (Ch=27)", "PMU_OC_3[3] mismatch"); #(`SPI_CLK_PERIOD); // 5e. DC_Slot4: V mode, Rload=18, Ch=35, PMU // = (18<<17) | (35<<1) | 1 = 24'h240047 $display(" DC4: V mode, Rload=18, Ch=35, PMU"); 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, "DC4 V-mode exec done", "timeout"); tb_assert(o_RC_RLSel[17] === 1'b1, "DC4: RC_RLSel[17]=1 (Rload=18)", "RC_RLSel mismatch"); tb_assert(o_PMU_OC_4[4] === 1'b1, "DC4: PMU_OC_4[4]=1 (Ch=35)", "PMU_OC_4[4] mismatch"); #(`SPI_CLK_PERIOD); // 5f. RC_LOF/LOS when RC_VSel=1 (V mode) tb_assert(o_RC_LOF === 2'b01 && o_RC_LOS === 2'b01, "RC_LOF/LOS=01 when RC_VSel=1", ""); #(`SPI_CLK_PERIOD * 2); end endtask ////========================================================================== // Test Group 6: RC_Tx relay output (slot 4) ////========================================================================== task test_rc_tx_output; reg [1:0] done_r; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 6: RC_Tx relay output (slot 4)"); $display("-------------------------------------------------------------"); // 6a. RC_Tx pattern: bits 17:12 = 6'b101010 (42) // = (42<<12) | (5<<1) | 1 = 24'h02A00B $display(" Slot4: RC_Tx = 6'b101010"); spi_write(mk_write(7'd5, 24'h02A00B)); @(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, "Slot4 RC_Tx exec done", "timeout"); tb_assert(o_RC_T27 === 1'b1, "RC_T27=1 (RC_Tx[5])", "RC_T27 mismatch"); tb_assert(o_RC_T28 === 1'b0, "RC_T28=0 (RC_Tx[4])", "RC_T28 mismatch"); tb_assert(o_RC_T29 === 1'b1, "RC_T29=1 (RC_Tx[3])", "RC_T29 mismatch"); tb_assert(o_RC_T30 === 1'b0, "RC_T30=0 (RC_Tx[2])", "RC_T30 mismatch"); tb_assert(o_RC_T31 === 1'b1, "RC_T31=1 (RC_Tx[1])", "RC_T31 mismatch"); tb_assert(o_RC_T32 === 1'b0, "RC_T32=0 (RC_Tx[0])", "RC_T32 mismatch"); #(`SPI_CLK_PERIOD * 2); end endtask ////========================================================================== // Test Group 7: Error conditions ////========================================================================== task test_error_conditions; reg [1:0] done_r; reg [31:0] rdata; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 7: Error conditions"); $display("-------------------------------------------------------------"); // 7a. Short SPI transaction (16 bits) $display(" Error 7a: 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"); // 7b. Write to IDENT register (ADDR 0, read-only) $display(" Error 7b: 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); // 7c. Read out-of-bounds address (ADDR 16) $display(" Error 7c: 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); // 7d. Multiple slot enables (en_t > 1) $display(" Error 7d: 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); // 7e. Write to out-of-bounds address (ADDR 31) $display(" Error 7e: 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 8: Edge cases ////========================================================================== task test_edge_cases; reg [31:0] rdata; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 8: Edge cases"); $display("-------------------------------------------------------------"); // 8a. Brief CS pulse (too short for 32-bit) $display(" Edge 8a: 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); // 8b. Rapid successive SPI writes $display(" Edge 8b: 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); // 8c. Write to reserved registers (ADDR 10-15) - no error $display(" Edge 8c: 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"); #(`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