Init Repo
This commit is contained in:
@@ -0,0 +1,914 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// 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
|
||||
Reference in New Issue
Block a user