Files
Jeremy Shen ed3fbd7cf5 Fix the SPI MISO logic
Modify the tb to make it easier from all aspects
2026-06-02 11:23:34 +08:00

293 lines
11 KiB
Systemverilog

//------------------------------------------------------------------------------
//
// Testbench: CPLD1 - SPI Register R/W (Simplified)
// Project: NewCalBoard DIG
// Tool: Lattice Diamond Verilog-2001 simulator
// Purpose: Verify SPI register read/write using only i_sclk, i_cs, i_mosi, o_miso
//
// Protocol:
// Write: [1][Addr:7b][Data:24b] = 32 bits
// Read: [0][Addr:7b][Don'tCare:24b] -> MISO returns [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
module tb_SPI_RegRW;
// Clock & reset
reg i_sys_clk;
reg i_rst_n;
// SPI interface - only 4 signals used in reality
reg i_sclk;
reg i_mosi;
reg i_cs;
wire o_miso;
// Clock generation
initial begin
i_sys_clk = 0;
forever #(`SYS_CLK_PERIOD / 2) i_sys_clk = ~i_sys_clk;
end
initial begin
i_sclk = 0;
forever #(`SPI_CLK_PERIOD / 2) i_sclk = ~i_sclk;
end
// DUT instantiation - only essential pins
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)
);
//==========================================================================
// SPI Helper Tasks - Mode 0 (CPOL=0, CPHA=0), MSB first, 32 bits
//==========================================================================
// Build 32-bit write frame: {1'b1, addr[6:0], data[23:0]}
function [31:0] mk_write;
input [6:0] addr;
input [23:0] data;
begin
mk_write = {1'b1, addr, data};
end
endfunction
// Build 32-bit read frame: {1'b0, addr[6:0], 24'b0}
function [31:0] mk_read;
input [6:0] addr;
begin
mk_read = {1'b0, addr, 24'b0};
end
endfunction
// Drive a 32-bit word out through MOSI
// SPI Mode 0: data prepared on falling edge, sampled on rising edge
task spi_send;
input [31:0] data;
integer i;
begin
@(negedge i_sclk); // Wait for any negedge to enter
i_cs = 1'b0;
i_mosi = data[31]; // Drive first bit on SAME edge as CS
for (i = 31; i >= 0; i = i - 1) begin
@(posedge i_sclk); // DUT samples MOSI on rising edge
if (i > 0) begin
@(negedge i_sclk); // Next falling edge
i_mosi = data[i - 1];// Setup next bit for next rising edge
end
end
@(negedge i_sclk);
i_cs = 1'b1;
i_mosi = 1'b0;
@(negedge i_sclk);
end
endtask
// Send command, capture response on MISO
// SPI Mode 0: data prepared on falling edge, sampled on rising edge
// DUT shifts MISO out on falling edge → read MISO on falling edge
task spi_read;
input [31:0] cmd;
output [31:0] rdata;
integer i;
begin
@(negedge i_sclk); // Wait for any negedge to enter
i_cs = 1'b0;
i_mosi = cmd[31]; // Drive first bit on SAME edge as CS
rdata = 32'b0;
for (i = 31; i >= 0; i = i - 1) begin
@(posedge i_sclk); // DUT samples MOSI on rising edge
@(negedge i_sclk); // DUT shifts MISO out on falling edge
#1; // clock-to-Q timing margin
rdata[i] = o_miso; // Read shifted-out bit
if (i > 0) begin
i_mosi = cmd[i - 1]; // Setup next bit for next rising edge
end
end
@(negedge i_sclk);
i_cs = 1'b1;
i_mosi = 1'b0;
@(negedge i_sclk);
end
endtask
// Send a write command
task spi_write;
input [31:0] data;
begin
spi_send(data);
end
endtask
//==========================================================================
// Main Test Sequence
//==========================================================================
initial begin
// Initialize
i_rst_n = 1'b0;
i_cs = 1'b1;
i_mosi = 1'b0;
#(`SYS_CLK_PERIOD * 4);
i_rst_n = 1'b1;
#(`SYS_CLK_PERIOD * 10);
$display("");
$display("=============================================================");
$display(" CPLD1 SPI Register R/W Testbench (Simplified)");
$display(" System clock: %d MHz | SPI clock: %d MHz",
1000 / `SYS_CLK_PERIOD, 1000 / `SPI_CLK_PERIOD);
$display("=============================================================");
$display("");
test_read_registers();
test_write_registers();
test_write_then_read();
$display("");
$display("=============================================================");
$display(" *** ALL TESTS COMPLETED ***");
$display("=============================================================");
$display("");
#(`SYS_CLK_PERIOD * 20);
$finish;
end
//==========================================================================
// Test 1: Read different registers
//==========================================================================
task test_read_registers;
reg [31:0] rdata;
begin
$display("");
$display("-------------------------------------------------------------");
$display(" Test 1: Read Different Registers");
$display("-------------------------------------------------------------");
// Read IDENT register (ADDR 0) - must return 24'h200010
$display(" 1a. Read IDENT register (ADDR 0)");
spi_read(mk_read(7'd0), rdata);
$display(" ADDR 0 (IDENT) = 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h200110 ? " [OK]" : " [FAIL]");
// Read same register again (should be identical)
$display(" 1b. Read IDENT register again");
spi_read(mk_read(7'd0), rdata);
$display(" ADDR 0 (IDENT) = 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h200110 ? " [OK]" : " [FAIL]");
// Read ADDR 1 (STATE_REG)
$display(" 1c. Read STATE register (ADDR 1)");
spi_read(mk_read(7'd1), rdata);
$display(" ADDR 1 (STATE) = 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h800000 ? " [OK]" : " [FAIL]");
// Read ADDR 2 (Freq_Slot1)
$display(" 1d. Read Freq_Slot1 register (ADDR 2)");
spi_read(mk_read(7'd2), rdata);
$display(" ADDR 2 (FreqSlot1) = 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'd0 ? " [OK]" : " [FAIL]");
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 2: Write registers (verify via read-back)
//==========================================================================
task test_write_registers;
reg [31:0] rdata;
begin
$display("");
$display("-------------------------------------------------------------");
$display(" Test 2: Write Registers (verify via read-back)");
$display("-------------------------------------------------------------");
// Write to ADDR 2, then read back
$display(" 2a. Write 0x000005 to ADDR 2, read back");
spi_write(mk_write(7'd2, 24'h000005));
spi_read(mk_read(7'd2), rdata);
$display(" Wrote 0x000005, read 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h000005 ? " [OK]" : " [FAIL]");
// Write to ADDR 3, then read back
$display(" 2b. Write 0x003001 to ADDR 3, read back");
spi_write(mk_write(7'd3, 24'h003001));
spi_read(mk_read(7'd3), rdata);
$display(" Wrote 0x003001, read 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h003001 ? " [OK]" : " [FAIL]");
// Write to ADDR 4, then read back
$display(" 2c. Write 0xABCDEF to ADDR 4, read back");
spi_write(mk_write(7'd4, 24'hABCDEF));
spi_read(mk_read(7'd4), rdata);
$display(" Wrote 0xABCDEF, read 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'hABCDEF ? " [OK]" : " [FAIL]");
// Write 0 to ADDR 5, then read back
$display(" 2d. Write 0x000000 to ADDR 5, read back");
spi_write(mk_write(7'd5, 24'h000000));
spi_read(mk_read(7'd5), rdata);
$display(" Wrote 0x000000, read 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h000000 ? " [OK]" : " [FAIL]");
#(`SPI_CLK_PERIOD);
end
endtask
//==========================================================================
// Test 3: Write then Read / Read then Write (interleaved)
//==========================================================================
task test_write_then_read;
reg [31:0] rdata;
begin
$display("");
$display("-------------------------------------------------------------");
$display(" Test 3: Write-Then-Read / Read-Then-Write (Interleaved)");
$display("-------------------------------------------------------------");
// 3a. Write, delay, then read (verify persistence)
$display(" 3a. Write 0x112233 to ADDR 6, read after delay");
spi_write(mk_write(7'd6, 24'h112233));
#(`SPI_CLK_PERIOD * 3);
spi_read(mk_read(7'd6), rdata);
$display(" Wrote 0x112233, read 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h112233 ? " [OK]" : " [FAIL]");
// 3b. Read current value, then write new value, then read again
$display(" 3b. Read ADDR 7, write 0x445566, read again");
spi_read(mk_read(7'd7), rdata);
$display(" Before write: ADDR 7 = 0x%06h", rdata[23:0]);
spi_write(mk_write(7'd7, 24'h445566));
spi_read(mk_read(7'd7), rdata);
$display(" After write: ADDR 7 = 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h445566 ? " [OK]" : " [FAIL]");
// 3c. Rapid successive writes to multiple registers
$display(" 3c. Rapid writes to ADDR 8, 9, then read-back");
spi_write(mk_write(7'd8, 24'h8899AA));
spi_write(mk_write(7'd9, 24'hBBCCDD));
spi_read(mk_read(7'd8), rdata);
$display(" ADDR 8 = 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'h8899AA ? " [OK]" : " [FAIL]");
spi_read(mk_read(7'd9), rdata);
$display(" ADDR 9 = 0x%06h %s", rdata[23:0],
rdata[23:0] === 24'hBBCCDD ? " [OK]" : " [FAIL]");
#(`SPI_CLK_PERIOD);
end
endtask
endmodule