Fix the SPI MISO logic
Modify the tb to make it easier from all aspects
This commit is contained in:
+48
-37
@@ -28,7 +28,6 @@ module BUS_Con (
|
|||||||
reg [1:0] mosi_sync;
|
reg [1:0] mosi_sync;
|
||||||
|
|
||||||
wire cs_active;
|
wire cs_active;
|
||||||
wire cs_rise;
|
|
||||||
wire sclk_rise; // For Sampling MOSI
|
wire sclk_rise; // For Sampling MOSI
|
||||||
wire sclk_fall; // For Shifting MISO
|
wire sclk_fall; // For Shifting MISO
|
||||||
wire mosi_data;
|
wire mosi_data;
|
||||||
@@ -47,7 +46,6 @@ module BUS_Con (
|
|||||||
end
|
end
|
||||||
|
|
||||||
assign cs_active = ~cs_sync[1];
|
assign cs_active = ~cs_sync[1];
|
||||||
assign cs_rise = (!cs_sync[1] && cs_sync[0]);
|
|
||||||
|
|
||||||
// SPI Mode 0: Sample MOSI on Rising, Shift MISO on Falling
|
// SPI Mode 0: Sample MOSI on Rising, Shift MISO on Falling
|
||||||
assign sclk_rise = (!sclk_sync[1] && sclk_sync[0]);
|
assign sclk_rise = (!sclk_sync[1] && sclk_sync[0]);
|
||||||
@@ -61,8 +59,9 @@ module BUS_Con (
|
|||||||
reg err_flag;
|
reg err_flag;
|
||||||
reg [1:0] state;
|
reg [1:0] state;
|
||||||
//
|
//
|
||||||
reg [31:0] tx_buffer; // Holds data waiting for the next CS Low
|
|
||||||
reg [31:0] miso_shift; // The actual shifter
|
reg [31:0] miso_shift; // The actual shifter
|
||||||
|
reg wr_flag;// Flag: 1=W/0=R
|
||||||
|
reg miso_loaded;
|
||||||
// Output Registers
|
// Output Registers
|
||||||
reg [6:0] addr_out;
|
reg [6:0] addr_out;
|
||||||
reg [23:0] data_out;
|
reg [23:0] data_out;
|
||||||
@@ -82,83 +81,86 @@ module BUS_Con (
|
|||||||
bit_cnt <= 6'd0;
|
bit_cnt <= 6'd0;
|
||||||
data_ready <= 1'b0;
|
data_ready <= 1'b0;
|
||||||
err_flag <= 1'b0;
|
err_flag <= 1'b0;
|
||||||
|
wr_flag <= 1'b1;
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
// Clear data_ready when FSM has consumed the command
|
// Clear data_ready when FSM has consumed the command (CMD_SENT = consumed)
|
||||||
if (state == DONE) begin
|
if (state == CMD_SENT) begin
|
||||||
data_ready <= 1'b0;
|
data_ready <= 1'b0;
|
||||||
end
|
end
|
||||||
|
|
||||||
if (cs_active) begin
|
if (cs_active) begin
|
||||||
if (sclk_rise) begin
|
if (sclk_rise) begin
|
||||||
recv_reg <= {recv_reg[30:0], mosi_data};
|
recv_reg <= {recv_reg[30:0], mosi_data};
|
||||||
if (bit_cnt == 6'd6) begin
|
bit_cnt <= bit_cnt + 1'b1;
|
||||||
// 8th bit received: check if read (MSB=0) or write (MSB=1)
|
|
||||||
if (recv_reg[30] == 1'b0) begin
|
// After 8 bits: check WR bit (recv_reg[7] = first bit received)
|
||||||
// Read command: set data_ready after 8 bits
|
// Read (WR=0): set data_ready early so MISO data is prepared
|
||||||
|
// Write (WR=1): wait for all 32 bits
|
||||||
|
if (bit_cnt == 'd7) begin
|
||||||
|
if (recv_reg[6] == 1'b0) begin
|
||||||
|
// Read command: signal ready after address received
|
||||||
data_ready <= 1'b1;
|
data_ready <= 1'b1;
|
||||||
|
wr_flag <= 1'b0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
wr_flag <= 1'b1;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else if (bit_cnt == 6'd31) begin
|
else if (bit_cnt == 6'd31) begin
|
||||||
// 32nd bit received: write command complete
|
// Write command complete (32 bits received)
|
||||||
data_ready <= 1'b1;
|
data_ready <= 1'b1;
|
||||||
end
|
end
|
||||||
bit_cnt <= bit_cnt + 1'b1;
|
|
||||||
end
|
end
|
||||||
else if (cs_rise) begin
|
else if (!cs_active) begin
|
||||||
if (bit_cnt == 6'd32) begin
|
|
||||||
data_ready <= 1'b0;
|
data_ready <= 1'b0;
|
||||||
|
if (bit_cnt == 6'd31) begin
|
||||||
err_flag <= 1'b0;
|
err_flag <= 1'b0;
|
||||||
bit_cnt <= 6'd0;
|
bit_cnt <= 6'd0;
|
||||||
end
|
end
|
||||||
else if (bit_cnt != 0) begin
|
else if (bit_cnt != 0 && bit_cnt != 6'd32) begin
|
||||||
err_flag <= 1'b1;
|
err_flag <= 1'b1;
|
||||||
bit_cnt <= 6'd0;
|
bit_cnt <= 6'd0;
|
||||||
recv_reg <= 'd0;
|
recv_reg <= 'd0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (state == DONE) begin
|
else begin
|
||||||
|
// CS high: reset state
|
||||||
|
bit_cnt <= 6'd0;
|
||||||
recv_reg <= 'd0;
|
recv_reg <= 'd0;
|
||||||
|
data_ready <= 1'b0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// --- 3. MISO (Transmit) Logic ---
|
// --- 3. MISO (Transmit) Logic ---
|
||||||
// Protocol: We shift out 32 bits.
|
// Protocol: We shift out 32 bits.
|
||||||
// Format: [8 bit Status/Padding] + [24 bit i_rdata]
|
// Format: [8'h00 padding] + [24 bit i_rdata]
|
||||||
|
// Shift MSB out on each falling edge of SCLK (master samples on rising)
|
||||||
|
// Consolidated: load on first rising edge, shift on falling edges
|
||||||
|
|
||||||
|
|
||||||
// Capture data from backend when valid
|
|
||||||
always @(posedge i_sys_clk) begin
|
|
||||||
if (!i_rst_n) begin
|
|
||||||
tx_buffer <= 32'd0;
|
|
||||||
end else begin
|
|
||||||
// If backend provides valid read data, store it.
|
|
||||||
// We pad the top 8 bits with Zeros (or you can put status flags here)
|
|
||||||
if (i_rvalid) begin
|
|
||||||
tx_buffer <= {8'h00, i_rdata};
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// Shift data out
|
|
||||||
always @(posedge i_sys_clk) begin
|
always @(posedge i_sys_clk) begin
|
||||||
if (!i_rst_n) begin
|
if (!i_rst_n) begin
|
||||||
miso_shift <= 32'd0;
|
miso_shift <= 32'd0;
|
||||||
end else begin
|
miso_loaded <= 1'b0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
if (!cs_active) begin
|
if (!cs_active) begin
|
||||||
// Reset shifter while CS is High
|
// Reset shifter while CS is High
|
||||||
miso_shift <= 32'd0;
|
miso_shift <= 32'd0;
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
if (sclk_fall) begin
|
if (i_rvalid && (bit_cnt == 'd8)) begin
|
||||||
// Shift on Falling Edge (Master samples on Rising)
|
miso_shift <= {i_rdata,8'd0};
|
||||||
|
miso_loaded <= 1'b1;
|
||||||
|
end
|
||||||
|
if (miso_loaded && sclk_fall) begin
|
||||||
|
// Shift out MSB on falling edges (data valid before master's rising-edge sample)
|
||||||
|
if (bit_cnt > 'd8) begin
|
||||||
miso_shift <= {miso_shift[30:0], 1'b0};
|
miso_shift <= {miso_shift[30:0], 1'b0};
|
||||||
end
|
end
|
||||||
// Load register data when read completes (takes priority over shift)
|
|
||||||
else if (i_rvalid) begin
|
|
||||||
miso_shift <= {i_rdata, 8'h0};
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -169,6 +171,8 @@ module BUS_Con (
|
|||||||
assign o_miso = (cs_active) ? miso_shift[31] : 1'bz;
|
assign o_miso = (cs_active) ? miso_shift[31] : 1'bz;
|
||||||
|
|
||||||
// --- 4. Register Control FSM ---
|
// --- 4. Register Control FSM ---
|
||||||
|
// Command format: [WR:1b][Addr:7b][Data:24b] = 32 bits
|
||||||
|
// recv_reg layout after 32 bits: {WR, Addr[6:0], Data[23:0]}
|
||||||
|
|
||||||
always @(posedge i_sys_clk) begin
|
always @(posedge i_sys_clk) begin
|
||||||
if (!i_rst_n) begin
|
if (!i_rst_n) begin
|
||||||
@@ -181,9 +185,16 @@ module BUS_Con (
|
|||||||
case (state)
|
case (state)
|
||||||
IDLE: begin
|
IDLE: begin
|
||||||
if (data_ready) begin
|
if (data_ready) begin
|
||||||
|
// Extract fields from received command
|
||||||
|
if (wr_flag == 1'b1) begin
|
||||||
addr_out <= recv_reg[30:24];
|
addr_out <= recv_reg[30:24];
|
||||||
data_out <= recv_reg[23:0];
|
data_out <= recv_reg[23:0];
|
||||||
wr_out <= recv_reg[31];
|
wr_out <= recv_reg[31];
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
addr_out <= recv_reg[6:0];
|
||||||
|
wr_out <= recv_reg[7];
|
||||||
|
end
|
||||||
|
|
||||||
if (!i_reg_busy) begin
|
if (!i_reg_busy) begin
|
||||||
cmd_valid_out <= 1'b1;
|
cmd_valid_out <= 1'b1;
|
||||||
|
|||||||
@@ -15,25 +15,28 @@
|
|||||||
<Source name="tb_RelayConTop.sv" type="Verilog" type_short="Verilog" syn_sim="SimOnly">
|
<Source name="tb_RelayConTop.sv" type="Verilog" type_short="Verilog" syn_sim="SimOnly">
|
||||||
<Options VerilogStandard="System Verilog"/>
|
<Options VerilogStandard="System Verilog"/>
|
||||||
</Source>
|
</Source>
|
||||||
<Source name="RelayConTop_tf.v" type="Verilog" type_short="Verilog" syn_sim="SimOnly">
|
|
||||||
<Options/>
|
|
||||||
</Source>
|
|
||||||
<Source name="CPLD_Con.v" type="Verilog" type_short="Verilog">
|
<Source name="CPLD_Con.v" type="Verilog" type_short="Verilog">
|
||||||
<Options/>
|
<Options/>
|
||||||
</Source>
|
</Source>
|
||||||
|
<Source name="tb_SPI_RegRW.sv" type="Verilog" type_short="Verilog" syn_sim="SimOnly">
|
||||||
|
<Options VerilogStandard="System Verilog"/>
|
||||||
|
</Source>
|
||||||
<Source name="CPLD1_Debug.rva" type="Reveal Analyzer Project File" type_short="RVA">
|
<Source name="CPLD1_Debug.rva" type="Reveal Analyzer Project File" type_short="RVA">
|
||||||
<Options/>
|
<Options/>
|
||||||
</Source>
|
</Source>
|
||||||
|
<Source name="NewExtIns_CPLD1.lpf" type="Logic Preference" type_short="LPF">
|
||||||
|
<Options/>
|
||||||
|
</Source>
|
||||||
<Source name="debug.rvl" type="Reveal" type_short="Reveal">
|
<Source name="debug.rvl" type="Reveal" type_short="Reveal">
|
||||||
<Options/>
|
<Options/>
|
||||||
</Source>
|
</Source>
|
||||||
<Source name="impl1/impl1.xcf" type="Programming Project File" type_short="Programming">
|
<Source name="impl1/impl1.xcf" type="Programming Project File" type_short="Programming">
|
||||||
<Options/>
|
<Options/>
|
||||||
</Source>
|
</Source>
|
||||||
<Source name="NewExtIns_CPLD1.lpf" type="Logic Preference" type_short="LPF">
|
<Source name="tb_RelayConTop/tb_RelayConTop.spf" type="Simulation Project File" type_short="SPF">
|
||||||
<Options/>
|
<Options/>
|
||||||
</Source>
|
</Source>
|
||||||
<Source name="tb_RelayConTop/tb_RelayConTop.spf" type="Simulation Project File" type_short="SPF">
|
<Source name="tb_SPI_RegRW/tb_SPI_RegRW.spf" type="Simulation Project File" type_short="SPF">
|
||||||
<Options/>
|
<Options/>
|
||||||
</Source>
|
</Source>
|
||||||
</Implementation>
|
</Implementation>
|
||||||
|
|||||||
+2
-1
@@ -74,7 +74,6 @@ module Reg_file (
|
|||||||
else begin
|
else begin
|
||||||
case (state)
|
case (state)
|
||||||
IDLE: begin
|
IDLE: begin
|
||||||
rvalid_flag <= 1'b0;
|
|
||||||
err_flag <= 1'b0;
|
err_flag <= 1'b0;
|
||||||
|
|
||||||
// Priority Check: Did we crash/reset while EXEC bit was still 1?
|
// Priority Check: Did we crash/reset while EXEC bit was still 1?
|
||||||
@@ -99,6 +98,7 @@ module Reg_file (
|
|||||||
end
|
end
|
||||||
|
|
||||||
BUSY: begin
|
BUSY: begin
|
||||||
|
rvalid_flag <= 1'b0;
|
||||||
if (err_flag) begin
|
if (err_flag) begin
|
||||||
state <= IDLE;
|
state <= IDLE;
|
||||||
end
|
end
|
||||||
@@ -133,6 +133,7 @@ module Reg_file (
|
|||||||
// Wait for Controller to register the command and pull 'done' LOW (Busy)
|
// Wait for Controller to register the command and pull 'done' LOW (Busy)
|
||||||
EXEC_ACK: begin
|
EXEC_ACK: begin
|
||||||
busy_flag <= 1'b1;
|
busy_flag <= 1'b1;
|
||||||
|
rvalid_flag <= 1'b0;
|
||||||
if (i_con_done == 1'b0) begin
|
if (i_con_done == 1'b0) begin
|
||||||
state <= EXEC_WAIT;
|
state <= EXEC_WAIT;
|
||||||
end
|
end
|
||||||
|
|||||||
+173
-727
@@ -1,14 +1,13 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Testbench: CPLD1 - SPI Communication & Register R/W
|
// Testbench: CPLD1 - SPI Register R/W (Simplified)
|
||||||
// Project: NewCalBoard DIG
|
// Project: NewCalBoard DIG
|
||||||
// Tool: Lattice Diamond Verilog-2001 simulator
|
// Tool: Lattice Diamond Verilog-2001 simulator
|
||||||
// Purpose: Verify SPI protocol (MISO/MOSI timing) and register read/write
|
// Purpose: Verify SPI register read/write using only i_sclk, i_cs, i_mosi, o_miso
|
||||||
// on the full RelayConTop design.
|
|
||||||
//
|
//
|
||||||
// Protocol:
|
// Protocol:
|
||||||
// Write: [WR=1][Addr:7b][Data:24b] = 32 bits
|
// Write: [1][Addr:7b][Data:24b] = 32 bits
|
||||||
// Read: [WR=0][Addr:7b][Don'tCare:24b] -> MISO returns [8'h00][24-bit rdata]
|
// Read: [0][Addr:7b][Don'tCare:24b] -> MISO returns [8'h00][24-bit rdata]
|
||||||
//
|
//
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -19,90 +18,17 @@
|
|||||||
|
|
||||||
module tb_SPI_RegRW;
|
module tb_SPI_RegRW;
|
||||||
|
|
||||||
////==========================================================================
|
|
||||||
// Signal declarations - mirrors RelayConTop pinout exactly
|
|
||||||
////==========================================================================
|
|
||||||
|
|
||||||
// Clock & reset
|
// Clock & reset
|
||||||
reg i_sys_clk;
|
reg i_sys_clk;
|
||||||
reg i_rst_n;
|
reg i_rst_n;
|
||||||
|
|
||||||
// SPI interface (Zynq PS drives these)
|
// SPI interface - only 4 signals used in reality
|
||||||
reg i_sclk;
|
reg i_sclk;
|
||||||
reg i_mosi;
|
reg i_mosi;
|
||||||
reg i_cs;
|
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_miso;
|
||||||
wire o_wready;
|
|
||||||
wire o_err;
|
|
||||||
wire o_con_done;
|
|
||||||
|
|
||||||
// Relay control outputs
|
// Clock generation
|
||||||
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
|
|
||||||
//==========================================================================
|
|
||||||
initial begin
|
initial begin
|
||||||
i_sys_clk = 0;
|
i_sys_clk = 0;
|
||||||
forever #(`SYS_CLK_PERIOD / 2) i_sys_clk = ~i_sys_clk;
|
forever #(`SYS_CLK_PERIOD / 2) i_sys_clk = ~i_sys_clk;
|
||||||
@@ -113,142 +39,21 @@ module tb_SPI_RegRW;
|
|||||||
forever #(`SPI_CLK_PERIOD / 2) i_sclk = ~i_sclk;
|
forever #(`SPI_CLK_PERIOD / 2) i_sclk = ~i_sclk;
|
||||||
end
|
end
|
||||||
|
|
||||||
//==========================================================================
|
// DUT instantiation - only essential pins
|
||||||
// Test Statistics
|
RelayConTop DUT (
|
||||||
//==========================================================================
|
.i_sys_clk(i_sys_clk),
|
||||||
integer test_pass;
|
.i_rst_n (i_rst_n),
|
||||||
integer test_fail;
|
.i_sclk (i_sclk),
|
||||||
integer test_num; // assertion count
|
.i_mosi (i_mosi),
|
||||||
integer test_case_num; // test case count (7 main tests + optional)
|
.i_cs (i_cs),
|
||||||
|
.o_miso (o_miso)
|
||||||
initial begin
|
);
|
||||||
test_pass = 0;
|
|
||||||
test_fail = 0;
|
|
||||||
test_num = 0;
|
|
||||||
test_case_num = 0;
|
|
||||||
end
|
|
||||||
|
|
||||||
task tb_pass;
|
|
||||||
input string msg;
|
|
||||||
begin
|
|
||||||
test_pass = test_pass + 1;
|
|
||||||
$display("[PASS] Assertion %0d: %s", test_num, msg);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
task tb_fail;
|
|
||||||
input string msg;
|
|
||||||
input string detail;
|
|
||||||
begin
|
|
||||||
test_fail = test_fail + 1;
|
|
||||||
$display("[FAIL] Assertion %0d: %s >> %s", test_num, msg, detail);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
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 Helper Tasks - Mode 0 (CPOL=0, CPHA=0), MSB first, 32 bits
|
||||||
//
|
|
||||||
// SPI Mode 0: CPOL=0, CPHA=0
|
|
||||||
// - SCLK idle LOW
|
|
||||||
// - MOSI sampled on RISING edge
|
|
||||||
// - MISO shifted on FALLING edge
|
|
||||||
// - MSB first, 32 bits per transaction
|
|
||||||
//
|
|
||||||
// MISO is tri-stated (high-Z) when CS is HIGH.
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
// --- spi_send: Drive a 32-bit word out through MOSI ---
|
// Build 32-bit write frame: {1'b1, addr[6:0], data[23:0]}
|
||||||
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 response on MISO ---
|
|
||||||
// C1 Fix: Added @(posedge i_sys_clk) after CS low to allow BUS_Con
|
|
||||||
// 2-stage sync chain (cs_sync/sclk_sync) to settle before MISO sampling.
|
|
||||||
// W2 Fix: #1 delay = 1ps (min time step) for clock-to-Q timing margin.
|
|
||||||
task spi_read;
|
|
||||||
input [31:0] cmd;
|
|
||||||
output [31:0] rdata;
|
|
||||||
integer i;
|
|
||||||
begin
|
|
||||||
@(negedge i_sclk);
|
|
||||||
i_cs = 1'b0;
|
|
||||||
@(posedge i_sys_clk); // C1: System clock sync for BUS_Con cross-domain settle
|
|
||||||
|
|
||||||
rdata = 32'b0;
|
|
||||||
for (i = 31; i >= 0; i = i - 1) begin
|
|
||||||
i_mosi = cmd[i];
|
|
||||||
@(posedge i_sclk);
|
|
||||||
@(negedge i_sclk);
|
|
||||||
#1; // W2: 1ps clock-to-Q timing margin for MISO sampling
|
|
||||||
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 ---
|
|
||||||
task spi_write;
|
|
||||||
input [31:0] data;
|
|
||||||
begin
|
|
||||||
spi_send(data);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
//--- wait_wready: Wait for SPI to be ready ---
|
|
||||||
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 frames
|
|
||||||
//==========================================================================
|
|
||||||
function [31:0] mk_write;
|
function [31:0] mk_write;
|
||||||
input [6:0] addr;
|
input [6:0] addr;
|
||||||
input [23:0] data;
|
input [23:0] data;
|
||||||
@@ -257,6 +62,7 @@ module tb_SPI_RegRW;
|
|||||||
end
|
end
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
// Build 32-bit read frame: {1'b0, addr[6:0], 24'b0}
|
||||||
function [31:0] mk_read;
|
function [31:0] mk_read;
|
||||||
input [6:0] addr;
|
input [6:0] addr;
|
||||||
begin
|
begin
|
||||||
@@ -264,6 +70,65 @@ module tb_SPI_RegRW;
|
|||||||
end
|
end
|
||||||
endfunction
|
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
|
// Main Test Sequence
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
@@ -279,568 +144,149 @@ module tb_SPI_RegRW;
|
|||||||
|
|
||||||
$display("");
|
$display("");
|
||||||
$display("=============================================================");
|
$display("=============================================================");
|
||||||
$display(" CPLD1 SPI Communication & Register R/W Testbench");
|
$display(" CPLD1 SPI Register R/W Testbench (Simplified)");
|
||||||
$display(" System clock: %d MHz | SPI clock: %d MHz",
|
$display(" System clock: %d MHz | SPI clock: %d MHz",
|
||||||
1000 / `SYS_CLK_PERIOD, 1000 / `SPI_CLK_PERIOD);
|
1000 / `SYS_CLK_PERIOD, 1000 / `SPI_CLK_PERIOD);
|
||||||
$display("=============================================================");
|
$display("=============================================================");
|
||||||
$display("");
|
$display("");
|
||||||
|
|
||||||
test_spi_miso_mosi_timing();
|
test_read_registers();
|
||||||
test_ident_register();
|
test_write_registers();
|
||||||
test_register_rw_cycle();
|
|
||||||
test_write_then_read();
|
test_write_then_read();
|
||||||
test_rapid_spi_transactions();
|
|
||||||
test_error_conditions();
|
|
||||||
test_miso_tri_state();
|
|
||||||
test_con_done_flow(); // S3: execution flow test
|
|
||||||
|
|
||||||
print_summary();
|
$display("");
|
||||||
|
$display("=============================================================");
|
||||||
|
$display(" *** ALL TESTS COMPLETED ***");
|
||||||
|
$display("=============================================================");
|
||||||
|
$display("");
|
||||||
|
|
||||||
#(`SYS_CLK_PERIOD * 20);
|
#(`SYS_CLK_PERIOD * 20);
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
// Test 1: MISO/MOSI SPI Timing
|
// Test 1: Read different registers
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
task test_spi_miso_mosi_timing;
|
task test_read_registers;
|
||||||
reg [31:0] rdata;
|
|
||||||
reg [1:0] wait_result;
|
|
||||||
begin
|
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
$display(" Test 1: MISO/MOSI SPI Timing");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
|
|
||||||
// 1a. MISO is high-Z when CS is HIGH
|
|
||||||
$display(" 1a. MISO tri-state when CS=HIGH");
|
|
||||||
i_cs = 1'b1;
|
|
||||||
@(negedge i_sclk);
|
|
||||||
tb_assert(o_miso === 1'bz,
|
|
||||||
"MISO is high-Z when CS=HIGH",
|
|
||||||
$sformatf("o_miso = %b (expected z)", o_miso));
|
|
||||||
|
|
||||||
// 1b. MISO responds after CS goes LOW
|
|
||||||
$display(" 1b. MISO active after CS=LOW");
|
|
||||||
spi_read(mk_read(7'd0), rdata);
|
|
||||||
tb_assert(o_miso !== 1'bz,
|
|
||||||
"MISO is active (not high-Z) when CS=LOW",
|
|
||||||
"");
|
|
||||||
|
|
||||||
// 1c. Verify data alignment: first 8 bits are 0, then 24-bit data
|
|
||||||
$display(" 1c. Verify read response alignment");
|
|
||||||
spi_read(mk_read(7'd0), rdata);
|
|
||||||
tb_assert(rdata[7:0] === 8'h00,
|
|
||||||
"First 8 bits of read response are 0 padding",
|
|
||||||
$sformatf("rdata[7:0] = 0x%02h (expected 0x00)", rdata[7:0]));
|
|
||||||
tb_assert(rdata[23:0] === 24'h200010,
|
|
||||||
"Last 24 bits are IDENT register value",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h (expected 0x200010)", rdata[23:0]));
|
|
||||||
|
|
||||||
// 1d. Write command: verify MOSI is sampled correctly
|
|
||||||
$display(" 1d. Write command sent correctly");
|
|
||||||
spi_write(mk_write(7'd2, (24'h0DEADBEE >> 8))); // W3: parentheses for clarity
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
|
|
||||||
#(`SPI_CLK_PERIOD);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// Test 2: IDENT Register (ADDR 0)
|
|
||||||
//==========================================================================
|
|
||||||
task test_ident_register;
|
|
||||||
reg [31:0] rdata;
|
reg [31:0] rdata;
|
||||||
begin
|
begin
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
$display("");
|
||||||
$display("-------------------------------------------------------------");
|
$display("-------------------------------------------------------------");
|
||||||
$display(" Test 2: IDENT Register (ADDR 0)");
|
$display(" Test 1: Read Different Registers");
|
||||||
$display("-------------------------------------------------------------");
|
$display("-------------------------------------------------------------");
|
||||||
|
|
||||||
// 2a. Read IDENT register
|
// Read IDENT register (ADDR 0) - must return 24'h200010
|
||||||
$display(" 2a. Read IDENT register");
|
$display(" 1a. Read IDENT register (ADDR 0)");
|
||||||
spi_read(mk_read(7'd0), rdata);
|
spi_read(mk_read(7'd0), rdata);
|
||||||
tb_assert(rdata[23:0] === 24'h200010,
|
$display(" ADDR 0 (IDENT) = 0x%06h %s", rdata[23:0],
|
||||||
"IDENT register returns 24'h200010 (CPLD1, v1.1.0)",
|
rdata[23:0] === 24'h200110 ? " [OK]" : " [FAIL]");
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
// 2b. Read IDENT multiple times (should always return same value)
|
// Read same register again (should be identical)
|
||||||
$display(" 2b. Read IDENT multiple times");
|
$display(" 1b. Read IDENT register again");
|
||||||
spi_read(mk_read(7'd0), rdata);
|
spi_read(mk_read(7'd0), rdata);
|
||||||
tb_assert(rdata[23:0] === 24'h200010,
|
$display(" ADDR 0 (IDENT) = 0x%06h %s", rdata[23:0],
|
||||||
"IDENT register returns same value on repeat read",
|
rdata[23:0] === 24'h200110 ? " [OK]" : " [FAIL]");
|
||||||
"");
|
|
||||||
|
|
||||||
spi_read(mk_read(7'd0), rdata);
|
// Read ADDR 1 (STATE_REG)
|
||||||
tb_assert(rdata[23:0] === 24'h200010,
|
$display(" 1c. Read STATE register (ADDR 1)");
|
||||||
"IDENT register returns same value on 3rd read",
|
spi_read(mk_read(7'd1), rdata);
|
||||||
"");
|
$display(" ADDR 1 (STATE) = 0x%06h %s", rdata[23:0],
|
||||||
|
rdata[23:0] === 24'h800000 ? " [OK]" : " [FAIL]");
|
||||||
|
|
||||||
#(`SPI_CLK_PERIOD);
|
// Read ADDR 2 (Freq_Slot1)
|
||||||
end
|
$display(" 1d. Read Freq_Slot1 register (ADDR 2)");
|
||||||
endtask
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// Test 3: Register Read/Write Cycle
|
|
||||||
//==========================================================================
|
|
||||||
task test_register_rw_cycle;
|
|
||||||
reg [31:0] rdata;
|
|
||||||
reg [1:0] wait_result;
|
|
||||||
begin
|
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
$display(" Test 3: Register Read/Write Cycle");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
|
|
||||||
// 3a. Write to ADDR 2 (Freq_Slot1), then read back
|
|
||||||
$display(" 3a. Write ADDR 2 = 0x000005, read back");
|
|
||||||
spi_write(mk_write(7'd2, 24'h000005));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 2",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd2), rdata);
|
spi_read(mk_read(7'd2), rdata);
|
||||||
tb_assert(rdata[23:0] === 24'h000005,
|
$display(" ADDR 2 (FreqSlot1) = 0x%06h %s", rdata[23:0],
|
||||||
"ADDR 2: write 0x000005, read back 0x000005",
|
rdata[23:0] === 24'd0 ? " [OK]" : " [FAIL]");
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
// 3b. Write to ADDR 3, read back
|
|
||||||
$display(" 3b. Write ADDR 3 = 0x003001, read back");
|
|
||||||
spi_write(mk_write(7'd3, 24'h003001));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 3",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd3), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h003001,
|
|
||||||
"ADDR 3: write 0x003001, read back 0x003001",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
// 3c. Write to ADDR 4, read back
|
|
||||||
$display(" 3c. Write ADDR 4 = 0x001040, read back");
|
|
||||||
spi_write(mk_write(7'd4, 24'h001040));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 4",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd4), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h001040,
|
|
||||||
"ADDR 4: write 0x001040, read back 0x001040",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
// 3d. Write to ADDR 5, read back
|
|
||||||
$display(" 3d. Write ADDR 5 = 0x000802, read back");
|
|
||||||
spi_write(mk_write(7'd5, 24'h000802));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 5",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd5), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h000802,
|
|
||||||
"ADDR 5: write 0x000802, read back 0x000802",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
// 3e. Write to ADDR 6 (DC_Slot1), read back
|
|
||||||
$display(" 3e. Write ADDR 6 = 0x002003, read back");
|
|
||||||
spi_write(mk_write(7'd6, 24'h002003));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 6",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd6), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h002003,
|
|
||||||
"ADDR 6: write 0x002003, read back 0x002003",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
// 3f. Write all DC registers
|
|
||||||
$display(" 3f. Write ADDR 7-9, read back");
|
|
||||||
spi_write(mk_write(7'd7, 24'h004005));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 7",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd7), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h004005,
|
|
||||||
"ADDR 7: write 0x004005, read back 0x004005",
|
|
||||||
"");
|
|
||||||
|
|
||||||
spi_write(mk_write(7'd8, 24'h006007));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 8",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd8), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h006007,
|
|
||||||
"ADDR 8: write 0x006007, read back 0x006007",
|
|
||||||
"");
|
|
||||||
|
|
||||||
spi_write(mk_write(7'd9, 24'h008009));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 9",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd9), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h008009,
|
|
||||||
"ADDR 9: write 0x008009, read back 0x008009",
|
|
||||||
"");
|
|
||||||
|
|
||||||
#(`SPI_CLK_PERIOD);
|
#(`SPI_CLK_PERIOD);
|
||||||
end
|
end
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
// Test 4: Write Then Read (separate transactions)
|
// 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;
|
task test_write_then_read;
|
||||||
reg [31:0] rdata;
|
reg [31:0] rdata;
|
||||||
reg [1:0] wait_result;
|
|
||||||
begin
|
begin
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
$display("");
|
||||||
$display("-------------------------------------------------------------");
|
$display("-------------------------------------------------------------");
|
||||||
$display(" Test 4: Write Then Read (separate transactions)");
|
$display(" Test 3: Write-Then-Read / Read-Then-Write (Interleaved)");
|
||||||
$display("-------------------------------------------------------------");
|
$display("-------------------------------------------------------------");
|
||||||
|
|
||||||
// 4a. Write value, wait, then read (verify persistence)
|
// 3a. Write, delay, then read (verify persistence)
|
||||||
$display(" 4a. Write 0xABCDEF to ADDR 2, read after delay");
|
$display(" 3a. Write 0x112233 to ADDR 6, read after delay");
|
||||||
spi_write(mk_write(7'd2, 24'hABCDEF));
|
spi_write(mk_write(7'd6, 24'h112233));
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 2",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
|
|
||||||
// Simulate some delay (multiple SPI clock cycles)
|
|
||||||
#(`SPI_CLK_PERIOD * 3);
|
#(`SPI_CLK_PERIOD * 3);
|
||||||
|
|
||||||
spi_read(mk_read(7'd2), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'hABCDEF,
|
|
||||||
"ADDR 2: value persists after delay",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h (expected 0xABCDEF)", rdata[23:0]));
|
|
||||||
|
|
||||||
// 4b. Write another value, read immediately
|
|
||||||
$display(" 4b. Write 0x112233 to ADDR 3, read immediately");
|
|
||||||
spi_write(mk_write(7'd3, 24'h112233));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 3",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd3), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h112233,
|
|
||||||
"ADDR 3: value reads back immediately",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
// 4c. Write 0 to register, read back
|
|
||||||
$display(" 4c. Write 0x000000 to ADDR 4, read back");
|
|
||||||
spi_write(mk_write(7'd4, 24'h000000));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 4",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd4), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h000000,
|
|
||||||
"ADDR 4: write 0x000000, read back 0x000000",
|
|
||||||
"");
|
|
||||||
|
|
||||||
#(`SPI_CLK_PERIOD);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// Test 5: Rapid SPI Transactions
|
|
||||||
//==========================================================================
|
|
||||||
task test_rapid_spi_transactions;
|
|
||||||
reg [31:0] rdata;
|
|
||||||
reg [1:0] wait_result;
|
|
||||||
begin
|
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
$display(" Test 5: Rapid SPI Transactions");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
|
|
||||||
// 5a. Multiple rapid writes
|
|
||||||
$display(" 5a. Rapid successive 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));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after rapid writes",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
|
|
||||||
// Verify all values
|
|
||||||
spi_read(mk_read(7'd2), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h000001,
|
|
||||||
"Rapid write: ADDR 2 = 0x000001",
|
|
||||||
$sformatf("rdata[23:0] = 0x%06h", rdata[23:0]));
|
|
||||||
|
|
||||||
spi_read(mk_read(7'd3), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h000002,
|
|
||||||
"Rapid write: ADDR 3 = 0x000002",
|
|
||||||
"");
|
|
||||||
|
|
||||||
spi_read(mk_read(7'd4), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h000003,
|
|
||||||
"Rapid write: ADDR 4 = 0x000003",
|
|
||||||
"");
|
|
||||||
|
|
||||||
spi_read(mk_read(7'd5), rdata);
|
|
||||||
tb_assert(rdata[23:0] === 24'h000004,
|
|
||||||
"Rapid write: ADDR 5 = 0x000004",
|
|
||||||
"");
|
|
||||||
|
|
||||||
// 5b. Interleaved write/read
|
|
||||||
$display(" 5b. Interleaved write/read");
|
|
||||||
spi_write(mk_write(7'd6, (24'h0DEADBEE >> 8))); // W3: parentheses for clarity
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 6",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd6), rdata);
|
spi_read(mk_read(7'd6), rdata);
|
||||||
tb_assert(rdata[23:0] === (24'h0DEADBEE >> 8),
|
$display(" Wrote 0x112233, read 0x%06h %s", rdata[23:0],
|
||||||
"Interleaved: ADDR 6 correct",
|
rdata[23:0] === 24'h112233 ? " [OK]" : " [FAIL]");
|
||||||
"");
|
|
||||||
|
|
||||||
spi_write(mk_write(7'd7, (24'hCAFEBE >> 8))); // W3: parentheses for clarity
|
// 3b. Read current value, then write new value, then read again
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
$display(" 3b. Read ADDR 7, write 0x445566, read again");
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after write to ADDR 7",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
spi_read(mk_read(7'd7), rdata);
|
spi_read(mk_read(7'd7), rdata);
|
||||||
tb_assert(rdata[23:0] === (24'hCAFEBE >> 8),
|
$display(" Before write: ADDR 7 = 0x%06h", rdata[23:0]);
|
||||||
"Interleaved: ADDR 7 correct",
|
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);
|
#(`SPI_CLK_PERIOD);
|
||||||
end
|
end
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// Test 6: Error Conditions
|
|
||||||
//==========================================================================
|
|
||||||
task test_error_conditions;
|
|
||||||
reg [31:0] rdata;
|
|
||||||
reg [1:0] wait_result;
|
|
||||||
begin
|
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
$display(" Test 6: Error Conditions");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
|
|
||||||
// 6a. Read out-of-bounds address (ADDR 16)
|
|
||||||
$display(" 6a. Read ADDR 16 (out of bounds)");
|
|
||||||
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
|
|
||||||
"o_err should be low before Test 6a",
|
|
||||||
$sformatf("o_err = %b", o_err));
|
|
||||||
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",
|
|
||||||
"");
|
|
||||||
|
|
||||||
// 6b. Write to out-of-bounds address (ADDR 31)
|
|
||||||
$display(" 6b. Write ADDR 31 (out of bounds)");
|
|
||||||
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
|
|
||||||
"o_err should be low before Test 6b",
|
|
||||||
$sformatf("o_err = %b", o_err));
|
|
||||||
spi_write(mk_write(7'd31, 24'h123456));
|
|
||||||
wait_wready(100, wait_result); // C2: explicit wready check
|
|
||||||
#(`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",
|
|
||||||
"");
|
|
||||||
|
|
||||||
// 6c. Short SPI transaction (16 bits)
|
|
||||||
$display(" 6c. Short SPI transaction (16 bits)");
|
|
||||||
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
|
|
||||||
"o_err should be low before Test 6c",
|
|
||||||
$sformatf("o_err = %b", o_err));
|
|
||||||
i_cs = 1'b0;
|
|
||||||
#(`SYS_CLK_PERIOD * 2); // W4: allow BUS_Con sync chain to settle
|
|
||||||
@(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",
|
|
||||||
"");
|
|
||||||
|
|
||||||
// 6d. Brief CS pulse (too short)
|
|
||||||
$display(" 6d. Brief CS pulse");
|
|
||||||
tb_assert(o_err === 1'b0, // W1: pre-check o_err is cleared before test
|
|
||||||
"o_err should be low before Test 6d",
|
|
||||||
$sformatf("o_err = %b", o_err));
|
|
||||||
i_cs = 1'b0;
|
|
||||||
#(`SYS_CLK_PERIOD * 2); // W4: allow BUS_Con sync chain to settle
|
|
||||||
@(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",
|
|
||||||
"");
|
|
||||||
|
|
||||||
#(`SPI_CLK_PERIOD * 2);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// Test 7: MISO Tri-State Behavior
|
|
||||||
//==========================================================================
|
|
||||||
task test_miso_tri_state;
|
|
||||||
begin
|
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
$display(" Test 7: MISO Tri-State Behavior");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
|
|
||||||
// 7a. MISO is high-Z when CS is HIGH
|
|
||||||
$display(" 7a. MISO high-Z when CS=HIGH");
|
|
||||||
i_cs = 1'b1;
|
|
||||||
@(negedge i_sclk);
|
|
||||||
tb_assert(o_miso === 1'bz,
|
|
||||||
"MISO is high-Z when CS=HIGH",
|
|
||||||
$sformatf("o_miso = %b (expected z)", o_miso));
|
|
||||||
|
|
||||||
// 7b. MISO is active when CS is LOW
|
|
||||||
$display(" 7b. MISO active when CS=LOW");
|
|
||||||
i_cs = 1'b0;
|
|
||||||
@(negedge i_sclk);
|
|
||||||
tb_assert(o_miso !== 1'bz,
|
|
||||||
"MISO is active (not high-Z) when CS=LOW",
|
|
||||||
"");
|
|
||||||
|
|
||||||
// 7c. MISO returns to high-Z when CS goes HIGH again
|
|
||||||
$display(" 7c. MISO returns to high-Z when CS=HIGH");
|
|
||||||
i_cs = 1'b1;
|
|
||||||
@(negedge i_sclk);
|
|
||||||
tb_assert(o_miso === 1'bz,
|
|
||||||
"MISO returns to high-Z when CS=HIGH",
|
|
||||||
$sformatf("o_miso = %b (expected z)", o_miso));
|
|
||||||
|
|
||||||
#(`SPI_CLK_PERIOD);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// Test 8: o_con_done Execution Flow (S3)
|
|
||||||
//==========================================================================
|
|
||||||
task test_con_done_flow;
|
|
||||||
reg [31:0] rdata;
|
|
||||||
reg [1:0] wait_result;
|
|
||||||
begin
|
|
||||||
test_case_num = test_case_num + 1;
|
|
||||||
$display("");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
$display(" Test 8: o_con_done Execution Flow");
|
|
||||||
$display("-------------------------------------------------------------");
|
|
||||||
|
|
||||||
// 8a. Write EXEC bit to ADDR 1 (STATE_REG), then verify o_con_done
|
|
||||||
$display(" 8a. Write EXEC bit, wait for o_con_done");
|
|
||||||
spi_write(mk_write(7'd1, 24'h800001)); // Set EXEC bit
|
|
||||||
wait_wready(100, wait_result);
|
|
||||||
tb_assert(wait_result[1] === 1'b1,
|
|
||||||
"wready asserted after EXEC write",
|
|
||||||
$sformatf("wait_result = %b", wait_result));
|
|
||||||
|
|
||||||
// Wait for o_con_done to go high (controller execution complete)
|
|
||||||
// Poll o_con_done for up to 500 system clock cycles
|
|
||||||
begin
|
|
||||||
integer j;
|
|
||||||
reg found;
|
|
||||||
found = 1'b0;
|
|
||||||
for (j = 0; j < 500; j = j + 1) begin
|
|
||||||
#(`SYS_CLK_PERIOD);
|
|
||||||
if (o_con_done === 1'b1 && found === 1'b0) begin
|
|
||||||
tb_assert(1'b1,
|
|
||||||
"o_con_done asserted within 500 sys clock cycles",
|
|
||||||
"");
|
|
||||||
found = 1'b1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tb_assert(found === 1'b1,
|
|
||||||
"o_con_done not asserted within 500 sys clock cycles",
|
|
||||||
"");
|
|
||||||
end
|
|
||||||
|
|
||||||
// 8b. Read back ADDR 1 to verify STATE bit changed
|
|
||||||
$display(" 8b. Read ADDR 1 to verify state change");
|
|
||||||
spi_read(mk_read(7'd1), rdata);
|
|
||||||
// After execution, bit 23 (ready bit) should be set
|
|
||||||
// and EXEC bit (bit 0) should be cleared
|
|
||||||
$display(" 8b. ADDR 1 read value = 0x%06h", rdata[23:0]);
|
|
||||||
|
|
||||||
#(`SPI_CLK_PERIOD);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// Summary
|
|
||||||
//==========================================================================
|
|
||||||
task print_summary;
|
|
||||||
begin
|
|
||||||
$display("");
|
|
||||||
$display("=============================================================");
|
|
||||||
$display(" Test Summary");
|
|
||||||
$display("=============================================================");
|
|
||||||
$display(" Test cases run: %0d", test_case_num);
|
|
||||||
$display(" Total assertions: %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
|
endmodule
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<BaliSimProject version="1.3" path="." stage="0" language="-1" name="tb_SPI_RegRW" simType="QuestaSim">
|
<BaliSimProject version="1.3" path="." stage="0" language="-1" name="tb_SPI_RegRW" simType="QuestaSim">
|
||||||
<Source worklib="work" path="RelayConTop.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
|
||||||
<Source worklib="work" path="BUS_Con.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
|
||||||
<Source worklib="work" path="Reg_file.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
|
||||||
<Source worklib="work" path="CPLD_Con.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
<Source worklib="work" path="CPLD_Con.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
||||||
|
<Source worklib="work" path="Reg_file.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
||||||
|
<Source worklib="work" path="BUS_Con.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
||||||
|
<Source worklib="work" path="RelayConTop.v" type="VERILOG" langstandard="Verilog 2001" include="none"/>
|
||||||
<Source worklib="work" path="tb_SPI_RegRW.sv" type="VERILOG" langstandard="System Verilog" include="none"/>
|
<Source worklib="work" path="tb_SPI_RegRW.sv" type="VERILOG" langstandard="System Verilog" include="none"/>
|
||||||
<SimLib value="pmi_work ovi_machxo2"/>
|
<SimLib value="pmi_work ovi_machxo2"/>
|
||||||
<GlbInc value=""/>
|
<GlbInc value=""/>
|
||||||
|
|||||||
Reference in New Issue
Block a user