//------------------------------------------------------------------------------ // // Testbench: CPLD2 - RelayConTop // Project: NewCalBoard DIG // Tool: Lattice Diamond Verilog-2001 simulator // Purpose: Verify SPI interface, register file, and relay control logic // for CPLD2 (RC_F/RC_S/RC_T relay control + OC channels + PMU_OC). // // CPLD2-specific: // - Outputs: RC_F[7:0], RC_S[15:0], RC_T[25:0], OC_x09[3:0], OC_x25[3:0] // - PMU_OC_1-4[31:0] (from OC_x09/OC_x25 mapping) // - CPLD_Con handles freq relays only (DC slots are stubs) // - Signal names: i_wvaild (typo), i_rvaild (typo) // // Architecture (3 sub-modules): // RelayConTop // - BUS_Con - SPI Mode 0 master interface (32-bit transactions) // - Reg_file - 16-register file + execution FSM // - CPLD_Con - Freq relay control logic (no DC handling) // // 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 CPLD2 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 (CPLD2 uses typo names: i_wvaild, i_rvaild) reg i_rready; reg i_wvaild; // Status outputs (monitored by testbench) // Note: CPLD2 does NOT expose o_con_done as a top-level output wire o_miso; wire o_rvalid; wire o_wready; wire o_err; // Relay control outputs (CPLD2 specific) wire [7:0] o_RC_F; // RC_F relays (2 bits used per slot) wire [15:0] o_RC_S; // RC_S relays (4 bits used per slot) wire [25:0] o_RC_T; // RC_T relays (8 bits used per slot, only 26 bits) // OC channel selectors wire [3:0] o_OC_x09; // OC channel selector (range 65-72) wire [3:0] o_OC_x25; // OC channel selector (range 193-200) // PMU output channels (4 x 32-bit, mapped from OC_x09/OC_x25) 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; //========================================================================== // DUT instantiation - CPLD2 specific pin names // Note: CPLD2 does NOT expose o_con_done as a top-level output //========================================================================== 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), // CPLD2 typo: wvaild .o_wready (o_wready), .o_err (o_err), .o_RC_F (o_RC_F), .o_RC_S (o_RC_S), .o_RC_T (o_RC_T), .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 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 o_wready for HIGH (CPLD2 doesn't expose con_done) // When execution completes, the SPI interface becomes ready again (o_wready=1) task wait_con_done; input [15:0] max_cycles; output [1:0] result; integer i; begin result = 2'b0; // Wait for o_wready to go HIGH (SPI interface ready after execution) 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 // --- 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(" CPLD2 RelayConTop - Comprehensive Testbench"); $display(" System clock: %d MHz | SPI clock: %d MHz", 1000 / `SYS_CLK_PERIOD, 1000 / `SPI_CLK_PERIOD); $display(" Outputs: RC_F[7:0], RC_S[15:0], RC_T[25:0], OC_x09/25, PMU_OC[4x32]"); $display("============================================================="); $display(""); test_reset_behavior(); test_register_rw(); test_exec_protocol(); test_freq_slots(); test_rc_relay_outputs(); test_dc_stub_behavior(); 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_wready === 1'b1, "o_wready is HIGH after reset (SPI ready, 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: CPLD2=bit22, version=1.1.0 = 24'h400010 spi_read(mk_read(7'd0), r_ident); r_ident = r_ident[23:0]; tb_assert(r_ident === 24'h400010, "IDENT register returns 24'h400010 (CPLD2, v1.1.0)", ""); // All relay outputs should be zero after reset tb_assert(o_RC_F === 8'b0, "o_RC_F is all zeros after reset", ""); tb_assert(o_RC_S === 16'b0, "o_RC_S is all zeros after reset", ""); tb_assert(o_RC_T[25:0] === 26'b0, "o_RC_T[25:0] is all zeros after reset", ""); tb_assert(o_OC_x09 === 4'b0, "o_OC_x09 is all zeros after reset", ""); tb_assert(o_OC_x25 === 4'b0, "o_OC_x25 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", ""); 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 (should complete but produce no output) 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 (stub behavior)", ""); tb_assert(o_err === 1'b0, "No error after DC slot execution (stub)", ""); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 4: Frequency slot decoding (all 4 slots) // // Freq_Slot N bit layout: // Bit 23:10: Per Relay_Control (14 bits) // Bit 9:1: Channel_Number (9 bits) // Bit 0: Slot_EN (1=Enable) // // Channel ranges: // 1-8: OC_x09 = slot_bit, PMU_OC[0] bit 0 // 9-72: (no OC_x09/OC_x25), PMU_OC[0] from channel // 73-136: OC_x25 = slot_bit, PMU_OC[0] bit 16+ // 137-200: (no OC_x09/OC_x25), PMU_OC[0] bit 24+ //========================================================================== 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_x09 = 4'b0001 // = (1<<23) | (0<<22) | (5<<1) | 1 = 24'h08000B (sets RC_F[1:0]=01) $display(" Slot1 Ch5 (range 1-8): OC_x09=0001"); spi_write(mk_write(7'd2, 24'h08000B)); @(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_OC_x09[0] === 1'b1, "Slot1 Ch5: OC_x09[0]=1", "OC_x09[0] mismatch"); tb_assert(o_OC_x25 === 4'b0, "Slot1 Ch5: OC_x25=0", "OC_x25 mismatch"); tb_assert(o_RC_F[1:0] == 2'b01, "Slot1 Ch5: RC_F[1:0] has relay bits", "RC_F mismatch"); #(`SPI_CLK_PERIOD); // Slot1 Ch50 (range 9-72): no OC_x09/OC_x25 // = (0<<10) | (50<<1) | 1 = 24'h000065 $display(" Slot1 Ch50 (range 9-72): OC_x09=0, OC_x25=0"); 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_OC_x09 === 4'b0, "Slot1 Ch50: OC_x09=0", "OC_x09 mismatch"); tb_assert(o_OC_x25 === 4'b0, "Slot1 Ch50: OC_x25=0", "OC_x25 mismatch"); #(`SPI_CLK_PERIOD); // Slot1 Ch100 (range 73-136): OC_x25 = 4'b0001 // = (0<<10) | (100<<1) | 1 = 24'h0000C9 $display(" Slot1 Ch100 (range 73-136): OC_x25=0001"); 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_OC_x09 === 4'b0, "Slot1 Ch100: OC_x09=0", "OC_x09 mismatch"); tb_assert(o_OC_x25[0] === 1'b1, "Slot1 Ch100: OC_x25[0]=1", "OC_x25[0] mismatch"); tb_assert(o_RC_T[15:8] != 8'b0, "Slot1 Ch100: RC_T has relay bits", "RC_T mismatch"); #(`SPI_CLK_PERIOD); // Slot1 Ch150 (range 137-200): no OC_x09/OC_x25 // = (0<<10) | (150<<1) | 1 = 24'h00012D $display(" Slot1 Ch150 (range 137-200): OC_x09=0, OC_x25=0"); 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_OC_x09 === 4'b0, "Slot1 Ch150: OC_x09=0", "OC_x09 mismatch"); tb_assert(o_OC_x25 === 4'b0, "Slot1 Ch150: OC_x25=0", "OC_x25 mismatch"); #(`SPI_CLK_PERIOD); // Slot2 Ch5 (range 1-8): OC_x09 = 4'b0010 // = (1<<23) | (0<<22) | (5<<1) | 1 = 24'h08000B (sets RC_F[3:2]=01) $display(" Slot2 Ch5 (range 1-8): OC_x09=0010"); spi_write(mk_write(7'd3, 24'h08000B)); @(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_OC_x09[1] === 1'b1, "Slot2 Ch5: OC_x09[1]=1", "OC_x09[1] mismatch"); tb_assert(o_RC_F[5:4] == 2'b01, "Slot2 Ch5: RC_F[5:4] has relay bits", "RC_F mismatch"); #(`SPI_CLK_PERIOD); // Slot3 Ch100 (range 73-136): OC_x25 = 4'b0100 // = (0xAA<<10) | (100<<1) | 1 = 24'h02A8C9 (sets RC_T bits) $display(" Slot3 Ch100 (range 73-136): OC_x25=0100"); spi_write(mk_write(7'd4, 24'h02A8C9)); @(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_OC_x25[2] === 1'b1, "Slot3 Ch100: OC_x25[2]=1", "OC_x25[2] mismatch"); #(`SPI_CLK_PERIOD); // Slot4 Ch50 (range 9-72): no OC_x09/OC_x25 // = (0<<10) | (50<<1) | 1 = 24'h000065 $display(" Slot4 Ch50 (range 9-72): OC_x09=0, OC_x25=0"); 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_OC_x09 === 4'b0, "Slot4 Ch50: OC_x09=0", "OC_x09 mismatch"); tb_assert(o_OC_x25 === 4'b0, "Slot4 Ch50: OC_x25=0", "OC_x25 mismatch"); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 5: RC relay output patterns (RC_F, RC_S, RC_T) // // CPLD2 CPLD_Con relay mapping (frequency mode): // Slot1: RC_F[1:0], RC_S[3:0], RC_T[7:0] // Slot2: RC_F[5:4], RC_S[11:8], RC_T[23:16] // Slot3: RC_F[7:6], RC_S[15:12], RC_T[15:8] (bit-reordered) // Slot4: RC_F[1:0], RC_S[3:0], RC_T[7:0] // // Timing: relay_l1 at t=0, relay_l2 at t=500, relay_l3 at t=1000 // exec_done at t=1080 //========================================================================== task test_rc_relay_outputs; reg [1:0] done_r; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 5: RC relay output patterns (RC_F/RC_S/RC_T)"); $display("-------------------------------------------------------------"); // 5a. Slot4 Ch5: Verify RC_F, RC_S, RC_T relay bit extraction // Freq_Slot4 with RC_F[1:0]={1,0}, RC_S[3:0]={1,0,1,0}, Ch=5 // = (1<<23) | (0xA<<18) | (5<<1) | 1 = 24'hA8000B $display(" Slot4: RC_F/RC_S/RC_T relay extraction"); spi_write(mk_write(7'd5, 24'hA8000B)); @(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 relay exec done", "timeout"); // RC_F[1:0] should have relay bits from freq4[23:22] tb_assert(o_RC_F[1:0] != 2'b00, "Slot4: RC_F[1:0] has relay bits from freq4[23:22]", $sformatf("RC_F[1:0]=%b", o_RC_F[1:0])); // RC_S[3:0] should have relay bits from freq4[21:18] tb_assert(o_RC_S[3:0] != 4'b0000, "Slot4: RC_S[3:0] has relay bits from freq4[21:18]", $sformatf("RC_S[3:0]=%b", o_RC_S[3:0])); // RC_T[7:0] should have relay bits from freq4[17:10] tb_assert(o_RC_T[7:0] != 8'b00000000, "Slot4: RC_T[7:0] has relay bits from freq4[17:10]", $sformatf("RC_T[7:0]=%b", o_RC_T[7:0])); // 5b. Slot4 with specific relay pattern: bits 23=1, 22=0, 21=1, 20=0, 19=1, 18=0, Ch=5 // = (1<<23) | (0<<22) | (1<<21) | (0<<20) | (1<<19) | (0<<18) | (5<<1) | 1 // = 24'hA8000B $display(" Slot4: RC_F/RC_S/RC_T specific relay pattern"); spi_write(mk_write(7'd5, 24'hA8000B)); @(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 pattern exec done", "timeout"); // Verify RC_F[1:0] = {freq4[22], freq4[23]} = {0, 1} = 2'b01 tb_assert(o_RC_F[1:0] === 2'b01, "Slot4: RC_F[1:0] = {freq4[22], freq4[23]} = 01", $sformatf("RC_F[1:0]=%b", o_RC_F[1:0])); // Verify RC_S[3:0] = {freq4[21], freq4[20], freq4[19], freq4[18]} = {1, 0, 1, 0} = 4'b1010 tb_assert(o_RC_S[3:0] === 4'b1010, "Slot4: RC_S[3:0] = freq4[21:18] = 1010", $sformatf("RC_S[3:0]=%b", o_RC_S[3:0])); #(`SPI_CLK_PERIOD * 2); end endtask //========================================================================== // Test Group 6: DC stub behavior // // CPLD2 CPLD_Con has DC slot cases but they are stubs: // - No relay output changes // - No PMU_OC output changes // - Just sets exec_flag=0, exec_done_flag=1 // // The top module maps OC_x09/OC_x25 to PMU_OC via: // PMU_OC_N[8] = OC_x09[N-1] // PMU_OC_N[24] = OC_x25[N-1] // PMU_OC_N[7:0] = PMU_OC_internal[7:0] (always zero for DC stubs) // PMU_OC_N[23:9] = PMU_OC_internal[23:8] (always zero for DC stubs) // PMU_OC_N[31:25] = PMU_OC_internal[31:25] (always zero for DC stubs) //========================================================================== task test_dc_stub_behavior; reg [1:0] done_r; begin $display(""); $display("-------------------------------------------------------------"); $display(" Group 6: DC stub behavior (no output changes)"); $display("-------------------------------------------------------------"); // 6a. DC_Slot1: Should complete but produce no relay/PMU output $display(" DC1: Stub behavior - no relay or PMU output"); 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_r); tb_assert(done_r === 1'b1, "DC1 exec done (stub)", "timeout"); // RC outputs should remain at their previous state or be zero // (DC stub doesn't change relay outputs) tb_assert(o_err === 1'b0, "DC1 stub: no error flag", ""); // 6b. DC_Slot2: Same stub behavior $display(" DC2: Stub behavior - no relay or PMU output"); spi_write(mk_write(7'd7, 24'h004005)); @(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 exec done (stub)", "timeout"); tb_assert(o_err === 1'b0, "DC2 stub: no error flag", ""); // 6c. DC_Slot3: Same stub behavior $display(" DC3: Stub behavior - no relay or PMU output"); spi_write(mk_write(7'd8, 24'h006007)); @(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 exec done (stub)", "timeout"); tb_assert(o_err === 1'b0, "DC3 stub: no error flag", ""); // 6d. DC_Slot4: Same stub behavior $display(" DC4: Stub behavior - no relay or PMU output"); spi_write(mk_write(7'd9, 24'h008009)); @(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 exec done (stub)", "timeout"); tb_assert(o_err === 1'b0, "DC4 stub: no error flag", ""); #(`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"); #(`SPI_CLK_PERIOD); // 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 enable bits (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"); // 8d. Write STATE with EXEC=1 while already executing (should error) $display(" Edge 8d: 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