// Testbench for strix_core: SPI register access and a small raw capture read back in // two transactions. Rings are replaced by the SIM model in ring_osc.v. `timescale 1ns / 1ps module tb_strix_core; localparam CAPTURE_ADDR_WIDTH = 6; // 64-byte buffer keeps the simulation short localparam CAPTURE_BYTES = 1 << CAPTURE_ADDR_WIDTH; localparam SCK_HALF = 62.5; // 8 MHz SCK reg clk = 1'b0; always #4.1667 clk = ~clk; // 120 MHz reg reset = 1'b1; reg sck = 1'b0, mosi = 1'b0, ss_n = 1'b1; wire miso, done; strix_core #(.CAPTURE_ADDR_WIDTH(CAPTURE_ADDR_WIDTH)) dut ( .clk(clk), .reset(reset), .spi_sck(sck), .spi_mosi(mosi), .spi_ss_n(ss_n), .spi_miso(miso), .capture_done(done) ); integer errors = 0; task spi_byte(input [7:0] out, output [7:0] in); integer i; begin for (i = 7; i >= 0; i = i - 1) begin mosi = out[i]; #SCK_HALF sck = 1'b1; in[i] = miso; #SCK_HALF sck = 1'b0; end end endtask task write_reg(input [6:0] addr, input [7:0] value); reg [7:0] dummy; begin ss_n = 1'b0; #SCK_HALF; spi_byte({1'b0, addr}, dummy); spi_byte(value, dummy); #SCK_HALF ss_n = 1'b1; #(4 * SCK_HALF); end endtask task read_reg(input [6:0] addr, output [7:0] value); reg [7:0] dummy; begin ss_n = 1'b0; #SCK_HALF; spi_byte({1'b1, addr}, dummy); spi_byte(8'h00, value); #SCK_HALF ss_n = 1'b1; #(4 * SCK_HALF); end endtask task read_data(input integer first, input integer count); reg [7:0] dummy, value; integer i; begin ss_n = 1'b0; #SCK_HALF; spi_byte({1'b1, 7'h10}, dummy); for (i = 0; i < count; i = i + 1) begin spi_byte(8'h00, value); if (value !== dut.capture.buffer[first + i]) begin $display("FAIL data[%0d]: got %02x, expected %02x", first + i, value, dut.capture.buffer[first + i]); errors = errors + 1; end end #SCK_HALF ss_n = 1'b1; #(4 * SCK_HALF); end endtask task expect(input [7:0] got, input [7:0] want, input [8*16-1:0] what); if (got !== want) begin $display("FAIL %0s: got %02x, expected %02x", what, got, want); errors = errors + 1; end endtask reg [7:0] v; integer n; initial begin #100 reset = 1'b0; #200; read_reg(7'h00, v); expect(v, "S", "ID0"); read_reg(7'h03, v); expect(v, "X", "ID3"); read_reg(7'h04, v); expect(v, 8'h01, "VERSION"); write_reg(7'h08, 8'h01); write_reg(7'h09, 8'h00); write_reg(7'h0A, 8'h03); read_reg(7'h08, v); expect(v, 8'h01, "SRC"); read_reg(7'h0A, v); expect(v, 8'h03, "DECIM_L"); write_reg(7'h0B, 8'h01); n = 0; read_reg(7'h0C, v); while (v[1] !== 1'b1 && n < 1000) begin read_reg(7'h0C, v); n = n + 1; end expect(v, 8'h02, "STATUS"); if (done !== 1'b1) begin $display("FAIL capture_done not set"); errors = errors + 1; end // Captured bytes must be defined and vary, otherwise the comparisons below prove nothing. n = 0; for (v = 0; v < CAPTURE_BYTES - 1; v = v + 1) begin if (^dut.capture.buffer[v] === 1'bx) begin $display("FAIL mem[%0d] undefined", v); errors = errors + 1; end if (dut.capture.buffer[v] !== dut.capture.buffer[v + 1]) n = n + 1; end if (n == 0) begin $display("FAIL capture buffer is constant"); errors = errors + 1; end // Read the buffer in two transactions; the second must continue where the first stopped. read_data(0, CAPTURE_BYTES / 2); read_data(CAPTURE_BYTES / 2, CAPTURE_BYTES / 2); // Rewind and read the first bytes again. write_reg(7'h0B, 8'h02); read_data(0, 4); if (errors == 0) $display("PASS"); else $display("%0d error(s)", errors); $finish; end endmodule