Adding testbench and simulation targets for the SPI master.

This commit is contained in:
Joachim Strömbergson 2024-04-18 14:16:11 +02:00 committed by dehanj
parent 3bc2453287
commit f61d254fda
No known key found for this signature in database
GPG Key ID: 3707A9DBF4BB8F1A
6 changed files with 844 additions and 12 deletions

2
.gitignore vendored
View File

@ -60,6 +60,6 @@ fp-info-cache
*.net *.net
*.dsn *.dsn
*.ses *.ses
__pycache__ __pycache__
application_fpga_par.json application_fpga_par.json
MEM.TXT

View File

@ -249,4 +249,17 @@ https://www.mouser.se/datasheet/2/949/w25q80dv_dl_revh_10022015-1489677.pdf
The core is implemented as a single module. Future versions will The core is implemented as a single module. Future versions will
probably be separated into separate modules. probably be separated into separate modules.
## Winbond Flash memory model
The testbench for the SPI master requires a memory model of the
Winbond Flash memory. The model [can be downloaded from
Winbond](https://www.winbond.com/hq/support/documentation/downloadV2022.jsp?__locale=en&xmlPath=/support/resources/.content/item/DA02-KAG049.html&level=2)
by providing the requested information and supplying the received
verification code.
From the downloaded file 'W25Q80DL.zip', please extract the file
W25Q80DL.v and place it in the 'tb' directory before building the
simulation model.
--- ---

View File

@ -58,6 +58,10 @@ module tb_tk1();
localparam ADDR_CPU_MON_FIRST = 8'h61; localparam ADDR_CPU_MON_FIRST = 8'h61;
localparam ADDR_CPU_MON_LAST = 8'h62; localparam ADDR_CPU_MON_LAST = 8'h62;
localparam ADDR_SPI_EN = 8'h80;
localparam ADDR_SPI_XFER = 8'h81;
localparam ADDR_SPI_DATA = 8'h82;
//---------------------------------------------------------------- //----------------------------------------------------------------
// Register and Wire declarations. // Register and Wire declarations.
@ -89,6 +93,11 @@ module tb_tk1();
wire tb_gpio3; wire tb_gpio3;
wire tb_gpio4; wire tb_gpio4;
wire tb_spi_ss;
wire tb_spi_sck;
wire tb_spi_mosi;
wire tb_spi_miso;
reg tb_cs; reg tb_cs;
reg tb_we; reg tb_we;
reg [7 : 0] tb_address; reg [7 : 0] tb_address;
@ -96,6 +105,12 @@ module tb_tk1();
wire [31 : 0] tb_read_data; wire [31 : 0] tb_read_data;
wire tb_ready; wire tb_ready;
//----------------------------------------------------------------
// Continuous assignments.
//----------------------------------------------------------------
// Inverted loopback of SPI data lines.
assign tb_spi_miso = ~tb_spi_mosi;
//---------------------------------------------------------------- //----------------------------------------------------------------
// Device Under Test. // Device Under Test.
@ -124,6 +139,11 @@ module tb_tk1();
.gpio3(tb_gpio3), .gpio3(tb_gpio3),
.gpio4(tb_gpio4), .gpio4(tb_gpio4),
.spi_ss(tb_spi_ss),
.spi_sck(tb_spi_sck),
.spi_mosi(tb_spi_mosi),
.spi_miso(tb_spi_miso),
.cs(tb_cs), .cs(tb_cs),
.we(tb_we), .we(tb_we),
.address(tb_address), .address(tb_address),
@ -614,7 +634,38 @@ module tb_tk1();
$display("--- test9: completed."); $display("--- test9: completed.");
$display(""); $display("");
end end
endtask // test8 endtask // test9
//----------------------------------------------------------------
// test10()
// SPI master loopback test.
//----------------------------------------------------------------
task test10;
begin
tc_ctr = tc_ctr + 1;
$display("");
$display("--- test10: Loopback in SPI Master started.");
$display("--- test10: Sending a byte.");
write_word(ADDR_SPI_EN, 32'h1);
write_word(ADDR_SPI_DATA, 32'ha7);
write_word(ADDR_SPI_XFER, 32'h1);
while (!dut.spi_ready) begin
#(CLK_PERIOD);
end
$display("--- test10: Byte should have been sent.");
write_word(ADDR_SPI_EN, 32'h0);
// 0x58 is the inverse of 0xa7.
read_word(ADDR_SPI_DATA, 32'h58);
$display("--- test10: completed.");
$display("");
end
endtask // test10
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -639,6 +690,8 @@ module tb_tk1();
test7(); test7();
test8(); test8();
test9(); test9();
test9();
test10();
display_test_result(); display_test_result();
$display(""); $display("");

View File

@ -0,0 +1,732 @@
//======================================================================
//
// tb_tk1_spi_master.v
// -------------------
// Testbench for the TK1_SPI_MASTER core.
//
//
// Author: Joachim Strombergson
// Copyright (C) 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
`default_nettype none
`timescale 1ns / 1ns
module tb_tk1_spi_master();
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
parameter DEBUG = 1;
parameter CLK_HALF_PERIOD = 1;
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
parameter MISO_ALL_ZERO = 0;
parameter MISO_ALL_ONE = 1;
parameter MISO_MOSI = 2;
parameter MISO_INV_MOSI = 3;
//----------------------------------------------------------------
// Register and Wire declarations.
//----------------------------------------------------------------
reg [31 : 0] cycle_ctr;
reg [31 : 0] error_ctr;
reg [31 : 0] tc_ctr;
reg monitor;
reg verbose;
reg tb_clk;
reg tb_reset_n;
wire tb_spi_ss;
wire tb_spi_sck;
wire tb_spi_mosi;
wire tb_spi_miso;
reg tb_spi_enable;
reg tb_spi_enable_vld;
reg tb_spi_start;
reg [7 : 0] tb_spi_tx_data;
reg tb_spi_tx_data_vld;
wire [7 : 0] tb_spi_rx_data;
wire tb_spi_ready;
wire mem_model_WPn;
wire mem_model_HOLDn;
reg [1 : 0] tb_miso_mux_ctrl;
reg my_tb_spi_ss;
//----------------------------------------------------------------
// Assignments.
//----------------------------------------------------------------
assign mem_model_WPn = 1'h1;
//----------------------------------------------------------------
// Device Under Test.
//----------------------------------------------------------------
tk1_spi_master dut(
.clk(tb_clk),
.reset_n(tb_reset_n),
.spi_ss(tb_spi_ss),
.spi_sck(tb_spi_sck),
.spi_mosi(tb_spi_mosi),
.spi_miso(tb_spi_miso),
.spi_enable(tb_spi_enable),
.spi_enable_vld(tb_spi_enable_vld),
.spi_start(tb_spi_start),
.spi_tx_data(tb_spi_tx_data),
.spi_tx_data_vld(tb_spi_tx_data_vld),
.spi_rx_data(tb_spi_rx_data),
.spi_ready(tb_spi_ready)
);
//----------------------------------------------------------------
// spi_memory
//----------------------------------------------------------------
W25Q80DL spi_memory(
.CSn(tb_spi_ss),
.CLK(tb_spi_sck),
.DIO(tb_spi_mosi),
.DO(tb_spi_miso),
.WPn(mem_model_WPn),
.HOLDn(mem_model_HOLDn)
);
//----------------------------------------------------------------
// clk_gen
//
// Always running clock generator process.
//----------------------------------------------------------------
always
begin : clk_gen
#CLK_HALF_PERIOD;
tb_clk = !tb_clk;
end // clk_gen
//----------------------------------------------------------------
// sys_monitor()
//
// An always running process that creates a cycle counter and
// conditionally displays information about the DUT.
//----------------------------------------------------------------
always
begin : sys_monitor
cycle_ctr = cycle_ctr + 1;
#(CLK_PERIOD);
if (monitor)
begin
dump_dut_state();
end
end
//----------------------------------------------------------------
// dump_dut_state()
//
// Dump the state of the dump when needed.
//----------------------------------------------------------------
task dump_dut_state;
begin : dump_dut_state
$display("");
$display("State of DUT at cycle: %08d", cycle_ctr);
$display("------------");
$display("Inputs and outputs:");
$display("spi_ss: 0x%1x, spi_sck: 0x%1x, spi_mosi: 0x%1x, spi_miso:0x%1x",
dut.spi_ss, dut.spi_sck, dut.spi_mosi, dut.spi_miso);
$display("spi_enable_vld: 0x%1x, spi_enable: 0x%1x",
dut.spi_enable_vld, dut.spi_enable);
$display("spi_tx_data_vld: 0x%1x, spi_tx_data: 0x%02x",
dut.spi_tx_data_vld, dut.spi_tx_data);
$display("spi_start: 0x%1x, spi_ready: 0x%1x, spi_rx_data: 0x%02x",
dut.spi_start, dut.spi_ready, dut.spi_rx_data);
$display("");
$display("");
$display("Internal state:");
$display("spi_clk_ctr_rst: 0x%1x, spi_clk_ctr_reg: 0x%02x",
dut.spi_clk_ctr_rst, dut.spi_clk_ctr_reg);
$display("");
$display("spi_bit_ctr_rst: 0x%1x, spi_bit_ctr_inc: 0x%1x, spi_bit_ctr_reg: 0x%02x",
dut.spi_bit_ctr_rst, dut.spi_bit_ctr_inc, dut.spi_bit_ctr_reg);
$display("");
$display("spi_ctrl_reg: 0x%02x, spi_ctrl_new: 0x%02x, spi_ctrl_we: 0x%1x",
dut.spi_ctrl_reg, dut.spi_ctrl_new, dut.spi_ctrl_we);
$display("");
$display("spi_tx_data_new: 0x%1x, spi_tx_data_nxt: 0x%1x, spi_tx_data_we: 0x%1x",
dut.spi_tx_data_new, dut.spi_tx_data_nxt, dut.spi_tx_data_we);
$display("spi_tx_data_reg: 0x%02x, spi_tx_data_new: 0x%02x",
dut.spi_tx_data_reg, dut.spi_tx_data_new);
$display("");
$display("spi_rx_data_nxt: 0x%1x, spi_rx_data_we: 0x%1x",
dut.spi_rx_data_nxt, dut.spi_rx_data_we);
$display("spi_rx_data_reg: 0x%02x, spi_rx_data_new: 0x%02x",
dut.spi_rx_data_reg, dut.spi_rx_data_new);
$display("spi_rx_data_reg0: 0x%1x, spi_rx_data_new0: 0x%1x",
dut.spi_rx_data_reg[0], dut.spi_rx_data_new[0]);
$display("spi_rx_data_reg1: 0x%1x, spi_rx_data_new1: 0x%1x",
dut.spi_rx_data_reg[1], dut.spi_rx_data_new[1]);
$display("spi_rx_data_reg2: 0x%1x, spi_rx_data_new2: 0x%1x",
dut.spi_rx_data_reg[2], dut.spi_rx_data_new[2]);
$display("spi_rx_data_reg3: 0x%1x, spi_rx_data_new3: 0x%1x",
dut.spi_rx_data_reg[3], dut.spi_rx_data_new[3]);
$display("spi_rx_data_reg4: 0x%1x, spi_rx_data_new4: 0x%1x",
dut.spi_rx_data_reg[4], dut.spi_rx_data_new[4]);
$display("spi_rx_data_reg5: 0x%1x, spi_rx_data_new5: 0x%1x",
dut.spi_rx_data_reg[5], dut.spi_rx_data_new[5]);
$display("spi_rx_data_reg6: 0x%1x, spi_rx_data_new6: 0x%1x",
dut.spi_rx_data_reg[6], dut.spi_rx_data_new[6]);
$display("spi_rx_data_reg7: 0x%1x, spi_rx_data_new7: 0x%1x",
dut.spi_rx_data_reg[7], dut.spi_rx_data_new[7]);
$display("");
end
endtask // dump_dut_state
//----------------------------------------------------------------
// reset_dut()
//
// Toggle reset to put the DUT into a well known state.
//----------------------------------------------------------------
task reset_dut;
begin
$display("--- Toggle reset.");
tb_reset_n = 0;
#(2 * CLK_PERIOD);
tb_reset_n = 1;
end
endtask // reset_dut
//----------------------------------------------------------------
// display_test_result()
//
// Display the accumulated test results.
//----------------------------------------------------------------
task display_test_result;
begin
if (error_ctr == 0)
begin
$display("--- All %02d test cases completed successfully", tc_ctr);
end
else
begin
$display("--- %02d tests completed - %02d test cases did not complete successfully.",
tc_ctr, error_ctr);
end
end
endtask // display_test_result
//----------------------------------------------------------------
// init_sim()
//
// Initialize all counters and testbed functionality as well
// as setting the DUT inputs to defined values.
//----------------------------------------------------------------
task init_sim;
begin
cycle_ctr = 0;
error_ctr = 0;
tc_ctr = 0;
monitor = 0;
tb_clk = 1'h0;
tb_reset_n = 1'h1;
tb_spi_enable = 1'h0;
tb_spi_enable_vld = 1'h0;
tb_spi_start = 1'h0;
tb_spi_tx_data = 8'h0;
tb_spi_tx_data_vld = 1'h0;
tb_miso_mux_ctrl = MISO_MOSI;
end
endtask // init_sim
//----------------------------------------------------------------
// enable_spi
//
// Enable the SPI-interface
//----------------------------------------------------------------
task enable_spi;
begin
if (verbose) begin
$display("enable_spi: Started");
end
tb_spi_enable = 1'h1;
tb_spi_enable_vld = 1'h1;
#(CLK_PERIOD);
tb_spi_enable_vld = 1'h0;
#(CLK_PERIOD);
if (verbose) begin
$display("enable_spi: Completed");
end
end
endtask // enable_spi
//----------------------------------------------------------------
// disable_spi
//
// Disable the SPI-interface
//----------------------------------------------------------------
task disable_spi;
begin
if (verbose) begin
$display("disable_spi: Started");
end
tb_spi_enable = 1'h0;
tb_spi_enable_vld = 1'h1;
#(CLK_PERIOD);
tb_spi_enable_vld = 1'h0;
#(CLK_PERIOD);
if (verbose) begin
$display("disable_spi: Completed");
end
end
endtask // disable_spi
//----------------------------------------------------------------
// xfer_byte
//
// Wait until the SPI-master is ready, then send input byte
// and return the received byte.
//----------------------------------------------------------------
task xfer_byte (input [7 : 0] to_mem, output [7 : 0] from_mem);
begin
if (verbose) begin
$display("xfer_byte: Trying to send 0x%02x to mem", to_mem);
end
tb_spi_tx_data = to_mem;
tb_spi_tx_data_vld = 1'h1;
#(CLK_PERIOD);
tb_spi_tx_data_vld = 1'h0;
#(CLK_PERIOD);
while (tb_spi_ready == 1'h0) begin
#(CLK_PERIOD);
end
#(CLK_PERIOD);
tb_spi_start = 1'h1;
#(CLK_PERIOD);
tb_spi_start = 1'h0;
#(CLK_PERIOD);
while (tb_spi_ready == 1'h0) begin
#(CLK_PERIOD);
end
#(CLK_PERIOD);
from_mem = tb_spi_rx_data;
#(CLK_PERIOD);
if (verbose) begin
$display("xfer_byte: Received 0x%02x from mem", from_mem);
end
end
endtask // xfer_byte
//----------------------------------------------------------------
// read_mem_range()
//
// Read out a specified memory range. Result is printed,
//----------------------------------------------------------------
task read_mem_range (input [23 : 0] address, input integer num_bytes);
begin : read_mem_range
reg [7 : 0] rx_byte;
integer i;
if (verbose) begin
$display("read_mem_range: Reading out %d bytes starting at address 0x%06x", num_bytes, address);
end
#(2 * CLK_PERIOD);
enable_spi();
#(2 * CLK_PERIOD);
// Send read command 0x03.
xfer_byte(8'h03, rx_byte);
// Send adress 0x000000.
xfer_byte(address[23 : 16], rx_byte);
xfer_byte(address[15 : 8], rx_byte);
xfer_byte(address[7 : 0], rx_byte);
// Read out num_bytes bytes.
for (i = 0 ; i < num_bytes ; i = i + 1) begin
xfer_byte(8'h00, rx_byte);
$display("--- tc_read_mem_range: Byte 0x%06x: 0x%02x", address + i, rx_byte);
end
disable_spi();
#(2 * CLK_PERIOD);
if (verbose) begin
$display("read_mem_range: Completed");
end
end
endtask // read_mem_range
//----------------------------------------------------------------
// read_status()
//----------------------------------------------------------------
task read_status ();
begin : read_status
reg [7 : 0] dummy;
reg [15 : 0] status;
enable_spi();
#(2 * CLK_PERIOD);
xfer_byte(8'h05, dummy);
xfer_byte(8'h00, status[15 : 8]);
xfer_byte(8'h00, status[7 : 0]);
#(2 * CLK_PERIOD);
disable_spi();
$display("--- read_status: 0x%04x", status);
end
endtask // read_status
//----------------------------------------------------------------
// tc_get_device_id()
//
// Test case that reads out the device ID.
//----------------------------------------------------------------
task tc_get_device_id;
begin : tc_get_id
reg [7 : 0] rx_byte;
tc_ctr = tc_ctr + 1;
monitor = 0;
$display("");
$display("--- tc_get_device_id: Read out device id from the memory.");
#(2 * CLK_PERIOD);
enable_spi();
#(2 * CLK_PERIOD);
// Send 0xab command.
$display("--- tc_get_device_id: Sending 0xab command.");
xfer_byte(8'hab, rx_byte);
#(CLK_PERIOD);
// Dummy bytes.
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got 0x%02x after dummy byte 1", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got 0x%02x after dummy byte 2", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got 0x%02x after dummy byte 3", rx_byte);
// Get the ID byte.
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got ID 0x%02x after dummy byte 4", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got ID 0x%02x after dummy byte 5", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got ID 0x%02x after dummy byte 6", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got ID 0x%02x after dummy byte 6", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_device_id: Got ID 0x%02x after dummy byte 6", rx_byte);
disable_spi();
#(2 * CLK_PERIOD);
$display("--- tc_get_device_id: completed.");
$display("");
end
endtask // tc_get_device_id
//----------------------------------------------------------------
// tc_get_jedec_id()
//
// Test case that reads out the JEDEC ID.
//----------------------------------------------------------------
task tc_get_jedec_id;
begin : tc_get_id
reg [7 : 0] rx_byte;
tc_ctr = tc_ctr + 1;
monitor = 0;
verbose = 0;
$display("");
$display("--- tc_get_jedec_id: Read out JEDEC device id, type and capacity from the memory.");
#(2 * CLK_PERIOD);
enable_spi();
#(2 * CLK_PERIOD);
// Send 0x9f command.
$display("--- tc_get_jedec_id: Sending 0xab command.");
xfer_byte(8'h9f, rx_byte);
// Send dummy bytes and get response back.
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_jedec_id: Got manufacture ID 0x%02x", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_jedec_id: Got memory type 0x%02x", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_jedec_id: Got memory capacity 0x%02x", rx_byte);
disable_spi();
#(2 * CLK_PERIOD);
$display("--- tc_get_jedec_id: completed.");
$display("");
verbose = 1;
end
endtask // tc_get_jedec_id
//----------------------------------------------------------------
// tc_get_unique_device_id()
//
// Test case that reads out the JEDEC ID.
// Expected: 0xdc02030405060708
//----------------------------------------------------------------
task tc_get_unique_device_id;
begin : tc_get_id
reg [7 : 0] rx_byte;
integer i;
tc_ctr = tc_ctr + 1;
monitor = 0;
verbose = 0;
$display("");
$display("--- tc_get_unique_device_id: Read out unique id from the memory");
$display("--- tc_get_unique_device_id: Expected result: 0x0102030405060708");
#(2 * CLK_PERIOD);
enable_spi();
#(2 * CLK_PERIOD);
// Send 0x9f command.
$display("--- tc_get_unique_device_id: Sending 0x4b command.");
xfer_byte(8'h4b, rx_byte);
// Send four dummy bytes and get response back.
xfer_byte(8'h00, rx_byte);
xfer_byte(8'h00, rx_byte);
xfer_byte(8'h00, rx_byte);
xfer_byte(8'h00, rx_byte);
// Send eight bytes and get unique device id back.
$display("--- tc_get_unique_device_id: reading out the unique device ID");
for (i = 0 ; i < 8 ; i = i + 1) begin
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_unique_device_id: 0x%02x", rx_byte);
end
disable_spi();
#(2 * CLK_PERIOD);
$display("--- tc_get_unique_device_id: completed.");
$display("");
verbose = 1;
end
endtask // tc_get_unique_device_id
//----------------------------------------------------------------
// tc_get_manufacturer_id()
//
// Test case that reads out the device ID.
//----------------------------------------------------------------
task tc_get_manufacturer_id;
begin : tc_get_id
reg [7 : 0] rx_byte;
tc_ctr = tc_ctr + 1;
monitor = 0;
$display("");
$display("--- tc_get_manufacturer_id: Read out device id from the memory.");
#(2 * CLK_PERIOD);
enable_spi();
#(2 * CLK_PERIOD);
// Send 0x90 command.
$display("--- tc_get_manufacturer_id: Sending 0xab command.");
xfer_byte(8'h90, rx_byte);
// Dummy bytes.
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_manufacturer_id: Got 0x%02x after dummy byte 1", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_manufacturer_id: Got 0x%02x after dummy byte 2", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_manufacturer_id: Got 0x%02x after dummy byte 3", rx_byte);
// Get the ID byte.
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_manufacturer_id: Got ID 0x%02x after dummy byte 4", rx_byte);
xfer_byte(8'h00, rx_byte);
$display("--- tc_get_manufacturer_id: Got ID 0x%02x after dummy byte 5", rx_byte);
disable_spi();
#(2 * CLK_PERIOD);
$display("--- tc_get_manufacturer_id: completed.");
$display("");
end
endtask // tc_get_manufacturer_id
//----------------------------------------------------------------
// tc_read_mem()
//
// Test case that reads out the first 16 bytes of the memory.
//----------------------------------------------------------------
task tc_read_mem;
begin : tc_get_id
reg [7 : 0] rx_byte;
integer i;
tc_ctr = tc_ctr + 1;
monitor = 0;
verbose = 0;
$display("");
$display("--- tc_read_mem: Read out the first 16 bytes from the memory.");
#(2 * CLK_PERIOD);
enable_spi();
#(2 * CLK_PERIOD);
// Send read command 0x03.
$display("--- tc_read_mem: Sending 0x03 command.");
xfer_byte(8'h03, rx_byte);
// Send adress 0x000000.
$display("--- tc_read_mem: Sending 24 bit address 0x000000.");
xfer_byte(8'h00, rx_byte);
xfer_byte(8'h00, rx_byte);
xfer_byte(8'h00, rx_byte);
// Read out 16 bytes.
$display("--- tc_read_mem: Reading out 16 bytes from the memory.");
for (i = 1 ; i < 17 ; i = i + 1) begin
xfer_byte(8'h00, rx_byte);
$display("--- tc_read_mem: Byte %d: 0x%02x", i, rx_byte);
end
disable_spi();
#(2 * CLK_PERIOD);
$display("--- tc_read_mem: completed.");
$display("");
end
endtask // tc_read_mem
//----------------------------------------------------------------
// tc_rmr_mem()
//
// Test case that reads out the first 16 bytes of the memory,
// erase the same area, reads out the contents again, writes
// a known pattern and the reads it out again.
//----------------------------------------------------------------
task tc_rmr_mem;
begin : tc_get_id
reg [7 : 0] rx_byte;
integer i;
tc_ctr = tc_ctr + 1;
monitor = 0;
verbose = 0;
$display("");
$display("--- tc_rmr_mem: Read out the first 16 bytes from the memory.");
read_mem_range(24'h000000, 16);
$display("");
$display("--- tc_rmr_mem: Status before write enable:");
read_status();
// Set write enable mode.
enable_spi();
// #(2 * CLK_PERIOD);
xfer_byte(8'h06, rx_byte);
// #(2 * CLK_PERIOD);
disable_spi();
#(2 * CLK_PERIOD);
$display("--- tc_rmr_mem: Status after write enable:");
read_status();
// Erase sector. Command 0x20 followed by 24 bit address.
enable_spi();
#(2 * CLK_PERIOD);
xfer_byte(8'h20, rx_byte);
xfer_byte(8'h00, rx_byte);
xfer_byte(8'h00, rx_byte);
xfer_byte(8'h00, rx_byte);
disable_spi();
#(4096 * CLK_PERIOD);
$display("--- tc_rmr_mem: Content of memory after erase.");
read_mem_range(24'h000000, 16);
disable_spi();
#(2 * CLK_PERIOD);
$display("--- tc_rmr_mem: completed.");
$display("");
end
endtask // tc_rmr_mem
//----------------------------------------------------------------
// tk1_spi_master_test
//----------------------------------------------------------------
initial
begin : tk1_spi_master_test
$display("");
$display(" -= Testbench for tk1_spi_master started =-");
$display(" =======================================");
$display("");
init_sim();
reset_dut();
disable_spi();
verbose = 1;
// tc_get_device_id();
tc_get_jedec_id();
// tc_get_manufacturer_id();
tc_get_unique_device_id();
tc_read_mem();
// tc_rmr_mem();
display_test_result();
$display("");
$display(" -= Testbench for tk1_spi_master completed =-");
$display(" =========================================");
$display("");
$finish;
end // tk1_spi_master_test
endmodule // tb_tk1_spi_master
//======================================================================
// EOF tb_tk1_spi_master.v
//======================================================================

View File

@ -11,9 +11,13 @@
# #
#=================================================================== #===================================================================
TOP_SRC=../rtl/tk1.v SPI_SRC=../rtl/tk1_spi_master.v
TB_SPI_SRC =../tb/tb_tk1_spi_master.v
MEM_MODEL_SRC =../tb/W25Q80DL.v
TOP_SRC=../rtl/tk1.v $(SPI_SRC)
TB_TOP_SRC =../tb/tb_tk1.v ../tb/sb_rgba_drv.v ../tb/udi_rom_sim.v TB_TOP_SRC =../tb/tb_tk1.v ../tb/sb_rgba_drv.v ../tb/udi_rom_sim.v
LINT_SRC=../rtl/tk1.v ../tb/sb_rgba_drv.v ../tb/udi_rom_sim.v LINT_SRC=$(TOP_SRC) ../tb/sb_rgba_drv.v ../tb/udi_rom_sim.v
CC = iverilog CC = iverilog
CC_FLAGS = -Wall CC_FLAGS = -Wall
@ -22,11 +26,23 @@ LINT = verilator
LINT_FLAGS = +1364-2005ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME LINT_FLAGS = +1364-2005ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME
all: top.sim all: MEM.TXT spi.sim top.sim
MEM.TXT:
../tools/mem_gen.py > MEM.TXT
spi.sim: $(TB_SPI_SRC) $(SPI_SRC) $(MEM_MODEL_SRC)
$(CC) $(CC_FLAGS) -o spi.sim $^
top.sim: $(TB_TOP_SRC) $(TOP_SRC) top.sim: $(TB_TOP_SRC) $(TOP_SRC)
$(CC) $(CC_FLAGS) -o top.sim $(TB_TOP_SRC) $(TOP_SRC) -DUDI_HEX=\"../tb/udi.hex\" $(CC) $(CC_FLAGS) -o top.sim $^ -DUDI_HEX=\"../tb/udi.hex\" -DINCLUDE_SPI_MASTER
sim-spi: spi.sim
./spi.sim
sim-top: top.sim sim-top: top.sim
@ -34,22 +50,21 @@ sim-top: top.sim
lint-top: $(LINT_SRC) lint-top: $(LINT_SRC)
$(LINT) $(LINT_FLAGS) $(LINT_SRC) $(LINT) $(LINT_FLAGS) $^ -DUDI_HEX=\"../tb/udi.hex\" -DINCLUDE_SPI_MASTER
lint-spi: $(SPI_SRC)
$(LINT) $(LINT_FLAGS) $^
clean: clean:
rm -f spi.sim
rm -f top.sim rm -f top.sim
rm -f MEM.TXT
help: help:
@echo "Build system for simulation of TK1 core" @echo "Build system for simulation of TK1 core"
@echo "" @echo ""
@echo "Supported targets:" @echo "Supported targets:"
@echo "------------------" @echo "------------------"
@echo "spi.sim: Build SPI simulation target."
@echo "sim-spi: Run SPI simulation."
@echo "top.sim: Build top level simulation target." @echo "top.sim: Build top level simulation target."
@echo "sim-top: Run top level simulation." @echo "sim-top: Run top level simulation."
@echo "lint-top: Lint top rtl source files." @echo "lint-top: Lint top rtl source files."

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#=======================================================================
#
# mem_gen.py
# ------
# Program that generates hex memory file read by the memory model.
#
# Copyright (C) 2024 - Tillitis AB
# SPDX-License-Identifier: GPL-2.0-only
#
#=======================================================================
# print("// Memory data loaded into the module at init.")
for i in range(int(16793600 / 4)):
print("de")
print("ad")
print("be")
print("ef")