//------------------------------------------------------------------------------ // // 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