969 lines
37 KiB
Systemverilog
969 lines
37 KiB
Systemverilog
//------------------------------------------------------------------------------
|
|
//
|
|
// 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 == 1, version 1.1.0
|
|
// Bug: uses || (logical OR) instead of | (bitwise OR), result = 24'h000001
|
|
spi_read(mk_read(7'd0), r_ident);
|
|
r_ident = r_ident[23:0];
|
|
tb_assert(r_ident === 24'h000001,
|
|
"IDENT register returns 24'h000001 (known || bug)", "");
|
|
|
|
// 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
|
|
// = (0<<10) | (5<<1) | 1 = 24'h000005
|
|
$display(" Slot1 Ch5 (range 1-8): OC_x09=0001");
|
|
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_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'h001905
|
|
$display(" Slot1 Ch50 (range 9-72): OC_x09=0, OC_x25=0");
|
|
spi_write(mk_write(7'd2, 24'h001905));
|
|
@(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'h006505
|
|
$display(" Slot1 Ch100 (range 73-136): OC_x25=0001");
|
|
spi_write(mk_write(7'd2, 24'h006505));
|
|
@(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[7:0] != 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'h009705
|
|
$display(" Slot1 Ch150 (range 137-200): OC_x09=0, OC_x25=0");
|
|
spi_write(mk_write(7'd2, 24'h009705));
|
|
@(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
|
|
// = (0<<10) | (5<<1) | 1 = 24'h000006
|
|
$display(" Slot2 Ch5 (range 1-8): OC_x09=0010");
|
|
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_OC_x09[1] === 1'b1, "Slot2 Ch5: OC_x09[1]=1", "OC_x09[1] mismatch");
|
|
tb_assert(o_RC_F[3:2] == 2'b01, "Slot2 Ch5: RC_F[3:2] has relay bits", "RC_F mismatch");
|
|
#(`SPI_CLK_PERIOD);
|
|
|
|
// Slot3 Ch100 (range 73-136): OC_x25 = 4'b0100
|
|
// = (0<<10) | (100<<1) | 1 = 24'h006507
|
|
$display(" Slot3 Ch100 (range 73-136): OC_x25=0100");
|
|
spi_write(mk_write(7'd4, 24'h006507));
|
|
@(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'h003208
|
|
$display(" Slot4 Ch50 (range 9-72): OC_x09=0, OC_x25=0");
|
|
spi_write(mk_write(7'd5, 24'h003208));
|
|
@(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 = (0<<10) | (5<<1) | 1 = 24'h00000B
|
|
// relay_l1 = {freq4[22], freq4[23], 6'd0} = {0,0,6'd0} = 8'd0
|
|
// But with Per Relay_Control bits 23:10, we need non-zero relay bits
|
|
// Set bits 23:10 = 2'b01 (relay control bits)
|
|
// = (1<<10) | (5<<1) | 1 = 24'h04000B
|
|
$display(" Slot4: RC_F/RC_S/RC_T relay extraction");
|
|
spi_write(mk_write(7'd5, 24'h04000B));
|
|
@(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
|
|
// = (1<<23) | (0<<22) | (1<<21) | (0<<20) | (1<<19) | (0<<18) | (5<<1) | 1
|
|
// = 24'h84400B
|
|
$display(" Slot4: RC_F/RC_S/RC_T specific relay pattern");
|
|
spi_write(mk_write(7'd5, 24'h84400B));
|
|
@(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[23], freq4[22]} = {1, 0} = 2'b10
|
|
tb_assert(o_RC_F[1:0] === 2'b10,
|
|
"Slot4: RC_F[1:0] = {freq4[23], freq4[22]} = 10",
|
|
$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
|