diff options
author | Joachim StroĢmbergson <joachim@secworks.se> | 2016-05-11 17:13:04 +0200 |
---|---|---|
committer | Joachim StroĢmbergson <joachim@secworks.se> | 2016-05-11 17:13:04 +0200 |
commit | c03aa37d4f237f2eb90d9a958428011ca2bd455c (patch) | |
tree | 0cf7219c8cd668b6642f45b87d5a80990ce20855 /src | |
parent | 8a5cf255d85b750b162cc6a5efa805ec0ba03fc1 (diff) |
Adding all source files and testbenches for the mkmif core. Adding Makefile for building simulation and linting. Adding top level license file.
Diffstat (limited to 'src')
-rw-r--r-- | src/rtl/mkmif.v | 256 | ||||
-rw-r--r-- | src/rtl/mkmif_core.v | 292 | ||||
-rw-r--r-- | src/rtl/mkmif_spi.v | 378 | ||||
-rw-r--r-- | src/tb/tb_mkmif.v | 438 | ||||
-rw-r--r-- | src/tb/tb_mkmif_core.v | 285 | ||||
-rw-r--r-- | src/tb/tb_mkmif_spi.v | 286 |
6 files changed, 1935 insertions, 0 deletions
diff --git a/src/rtl/mkmif.v b/src/rtl/mkmif.v new file mode 100644 index 0000000..4f44878 --- /dev/null +++ b/src/rtl/mkmif.v @@ -0,0 +1,256 @@ +//====================================================================== +// +// mkmif.v +// ------- +// Top level wrapper for the Master Key Memory (MKM) interface. +// The interface is implemented to use the Microchip 23K640 serial +// sram as external storage. The core acts as a SPI Master for the +// external memory including SPI clock generation. +// +// The current version of the core does not provide any functionality +// to protect against remanence. +// +// +// Author: Joachim Strombergson +// Copyright (c) 2011, NORDUnet A/S All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of the NORDUnet nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//====================================================================== + +module mkmif( + input wire clk, + input wire reset_n, + + output wire spi_sclk, + output wire spi_cs_n, + input wire spi_do, + output wire spi_di, + + input wire cs, + input wire we, + input wire [7 : 0] address, + input wire [31 : 0] write_data, + output wire [31 : 0] read_data + ); + + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + localparam ADDR_NAME0 = 8'h00; + localparam ADDR_NAME1 = 8'h01; + localparam ADDR_VERSION = 8'h02; + localparam ADDR_CTRL = 8'h08; + localparam CTRL_READ_BIT = 0; + localparam CTRL_WRITE_BIT = 1; + localparam CTRL_INIT_BIT = 2; + localparam ADDR_STATUS = 8'h09; + localparam STATUS_READY_BIT = 0; + localparam STATUS_VALID_BIT = 1; + localparam ADDR_SCLK_DIV = 8'h0a; + localparam ADDR_EMEM_ADDR = 8'h10; + localparam ADDR_EMEM_DATA = 8'h20; + + localparam DEFAULT_SCLK_DIV = 16'h0020; + + localparam CORE_NAME0 = 32'h6d6b6d69; // "mkmi" + localparam CORE_NAME1 = 32'h66202020; // "f " + localparam CORE_VERSION = 32'h302e3130; // "0.10" + + + //---------------------------------------------------------------- + // Registers including update variables and write enable. + //---------------------------------------------------------------- + reg read_op_reg; + reg read_op_new; + reg write_op_reg; + reg write_op_new; + reg init_op_reg; + reg init_op_new; + + reg [15 : 0] addr_reg; + reg addr_we; + + reg [15 : 0] sclk_div_reg; + reg sclk_div_we; + + reg [31 : 0] write_data_reg; + reg write_data_we; + + + //---------------------------------------------------------------- + // Wires. + //---------------------------------------------------------------- + wire core_ready; + wire core_valid; + wire [31 : 0] core_read_data; + reg [31 : 0] tmp_read_data; + + + //---------------------------------------------------------------- + // Concurrent connectivity for ports etc. + //---------------------------------------------------------------- + assign read_data = tmp_read_data; + + + //---------------------------------------------------------------- + // core + //---------------------------------------------------------------- + mkmif_core core( + .clk(clk), + .reset_n(reset_n), + + .spi_sclk(spi_sclk), + .spi_cs_n(spi_cs_n), + .spi_do(spi_do), + .spi_di(spi_di), + + .read_op(read_op_reg), + .write_op(write_op_reg), + .init_op(init_op_reg), + .ready(core_ready), + .valid(core_valid), + .sclk_div(sclk_div_reg), + .addr(addr_reg), + .write_data(write_data_reg), + .read_data(core_read_data) + ); + + + //---------------------------------------------------------------- + // reg_update + // Update functionality for all registers in the core. + // All registers are positive edge triggered with asynchronous + // active low reset. + //---------------------------------------------------------------- + always @ (posedge clk or negedge reset_n) + begin + if (!reset_n) + begin + read_op_reg <= 1'h0; + write_op_reg <= 1'h0; + addr_reg <= 16'h0; + sclk_div_reg <= DEFAULT_SCLK_DIV; + write_data_reg <= 32'h0; + end + else + begin + read_op_reg <= read_op_new; + write_op_reg <= write_op_new; + init_op_reg <= init_op_new; + + if (sclk_div_we) + sclk_div_reg <= write_data[15 : 0]; + + if (addr_we) + addr_reg <= write_data[15 : 0]; + + if (write_data_we) + write_data_reg <= write_data; + end + end // reg_update + + + //---------------------------------------------------------------- + // api + //---------------------------------------------------------------- + always @* + begin : api + read_op_new = 0; + write_op_new = 0; + init_op_new = 0; + addr_we = 0; + sclk_div_we = 0; + write_data_we = 0; + tmp_read_data = 32'h00000000; + + if (cs) + begin + if (we) + begin + case (address) + ADDR_CTRL: + begin + read_op_new = write_data[CTRL_READ_BIT]; + write_op_new = write_data[CTRL_WRITE_BIT]; + init_op_new = write_data[CTRL_INIT_BIT]; + end + + ADDR_SCLK_DIV: + sclk_div_we = 1; + + ADDR_EMEM_ADDR: + addr_we = 1; + + ADDR_EMEM_DATA: + write_data_we = 1; + + default: + begin + end + endcase // case (address) + end // if (we) + + else + begin + case (address) + ADDR_NAME0: + tmp_read_data = CORE_NAME0; + + ADDR_NAME1: + tmp_read_data = CORE_NAME1; + + ADDR_VERSION: + tmp_read_data = CORE_VERSION; + + ADDR_STATUS: + tmp_read_data = {30'h0, {core_valid, core_ready}}; + + ADDR_SCLK_DIV: + tmp_read_data = {16'h0, sclk_div_reg}; + + ADDR_EMEM_ADDR: + tmp_read_data = {16'h0, addr_reg}; + + ADDR_EMEM_DATA: + begin + tmp_read_data = core_read_data; + end + + default: + begin + end + endcase // case (address) + end + end + end // api +endmodule // mkmif + +//====================================================================== +// EOF mkmif.v +//====================================================================== diff --git a/src/rtl/mkmif_core.v b/src/rtl/mkmif_core.v new file mode 100644 index 0000000..0f90bf0 --- /dev/null +++ b/src/rtl/mkmif_core.v @@ -0,0 +1,292 @@ +//====================================================================== +// +// mkmif_core.v +// ------------ +// The actual core module for the Master Key Memory (MKM) interface. +// The interface is implemented to use the Microchip 23K640 serial +// sram as external storage. The core acts as a SPI Master for the +// external memory including SPI clock generation. +// +// The current version of the core does not provide any functionality +// to protect against remanence. +// +// +// Author: Joachim Strombergson +// Copyright (c) 2011, NORDUnet A/S All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of the NORDUnet nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//====================================================================== + +module mkmif_core( + input wire clk, + input wire reset_n, + + output wire spi_sclk, + output wire spi_cs_n, + input wire spi_do, + output wire spi_di, + + input wire read_op, + input wire write_op, + input wire init_op, + output wire ready, + output wire valid, + input wire [15 : 0] sclk_div, + input wire [15 : 0] addr, + input wire [31 : 0] write_data, + output wire [31 : 0] read_data + ); + + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + localparam SPI_READ_DATA_CMD = 8'h03; + localparam SPI_WRITE_DATA_CMD = 8'h02; + localparam SPI_READ_STATUS_CMD = 8'h05; + localparam SPI_WRITE_STATUS_CMD = 8'h01; + + localparam SEQ_MODE_NO_HOLD = 8'b01000001; + + localparam CTRL_IDLE = 0; + localparam CTRL_READY = 1; + localparam CTRL_READ = 2; + localparam CTRL_WRITE = 3; + localparam CTRL_INIT = 4; + localparam CTRL_OP_START = 5; + localparam CTRL_OP_WAIT = 6; + + + //---------------------------------------------------------------- + // Registers including update variables and write enable. + //---------------------------------------------------------------- + reg ready_reg; + reg ready_new; + reg ready_we; + reg valid_reg; + reg valid_new; + reg valid_we; + + reg [31 : 0] read_data_reg; + reg read_data_we; + + reg [3 : 0] mkmif_ctrl_reg; + reg [3 : 0] mkmif_ctrl_new; + reg mkmif_ctrl_we; + + + //---------------------------------------------------------------- + // Wires. + //---------------------------------------------------------------- + wire [31 : 0] spi_read_data; + reg [55 : 0] spi_write_data; + reg spi_set; + reg spi_start; + wire spi_ready; + reg [2 : 0] spi_length; + + + //---------------------------------------------------------------- + // Concurrent connectivity for ports etc. + //---------------------------------------------------------------- + assign ready = ready_reg; + assign valid = valid_reg; + assign read_data = read_data_reg; + + + //---------------------------------------------------------------- + // spi + // The actual spi interfacce + //---------------------------------------------------------------- + mkmif_spi spi( + .clk(clk), + .reset_n(reset_n), + + .spi_sclk(spi_sclk), + .spi_cs_n(spi_cs_n), + .spi_do(spi_do), + .spi_di(spi_di), + + .set(spi_set), + .start(spi_start), + .length(spi_length), + .divisor(sclk_div), + .ready(spi_ready), + .wr_data(spi_write_data), + .rd_data(spi_read_data) + ); + + + //---------------------------------------------------------------- + // reg_update + // Update functionality for all registers in the core. + // All registers are positive edge triggered with asynchronous + // active low reset. + //---------------------------------------------------------------- + always @ (posedge clk or negedge reset_n) + begin + if (!reset_n) + begin + ready_reg <= 0; + valid_reg <= 0; + read_data_reg <= 32'h0; + mkmif_ctrl_reg <= CTRL_IDLE; + end + else + begin + if (ready_we) + ready_reg <= ready_new; + + if (valid_we) + valid_reg <= valid_new; + + if (read_data_we) + read_data_reg <= spi_read_data; + + if (mkmif_ctrl_we) + mkmif_ctrl_reg <= mkmif_ctrl_new; + end + end // reg_update + + + //---------------------------------------------------------------- + // mkmif_ctrl + // Main control FSM. + //---------------------------------------------------------------- + always @* + begin : mkmif_ctrl + spi_set = 0; + spi_start = 0; + spi_length = 3'h0; + spi_write_data = 56'h0; + read_data_we = 0; + ready_new = 0; + ready_we = 0; + valid_new = 0; + valid_we = 0; + mkmif_ctrl_new = CTRL_IDLE; + mkmif_ctrl_we = 0; + + case (mkmif_ctrl_reg) + CTRL_IDLE: + begin + mkmif_ctrl_new = CTRL_INIT; + mkmif_ctrl_we = 1; + end + + CTRL_READY: + begin + ready_new = 1; + ready_we = 1; + + if (read_op) + begin + ready_new = 0; + ready_we = 1; + valid_new = 0; + valid_we = 1; + mkmif_ctrl_new = CTRL_READ; + mkmif_ctrl_we = 1; + end + + if (write_op) + begin + ready_new = 0; + ready_we = 1; + mkmif_ctrl_new = CTRL_WRITE; + mkmif_ctrl_we = 1; + end + + if (init_op) + begin + ready_new = 0; + ready_we = 1; + mkmif_ctrl_new = CTRL_INIT; + mkmif_ctrl_we = 1; + end + end + + CTRL_READ: + begin + spi_set = 1; + spi_write_data = {SPI_READ_DATA_CMD, addr, 32'h0}; + spi_length = 3'h7; + mkmif_ctrl_new = CTRL_OP_START; + mkmif_ctrl_we = 1; + end + + CTRL_WRITE: + begin + spi_set = 1; + spi_write_data = {SPI_WRITE_DATA_CMD, addr, write_data}; + spi_length = 3'h7; + mkmif_ctrl_new = CTRL_OP_START; + mkmif_ctrl_we = 1; + end + + CTRL_INIT: + begin + if (spi_ready) + begin + spi_set = 1; + spi_write_data = {SPI_WRITE_STATUS_CMD, SEQ_MODE_NO_HOLD, 40'h0}; + spi_length = 3'h2; + mkmif_ctrl_new = CTRL_OP_START; + mkmif_ctrl_we = 1; + end + end + + CTRL_OP_START: + begin + spi_start = 1; + mkmif_ctrl_new = CTRL_OP_WAIT; + mkmif_ctrl_we = 1; + end + + CTRL_OP_WAIT: + begin + if (spi_ready) + begin + read_data_we = 1; + valid_new = 1; + valid_we = 1; + mkmif_ctrl_new = CTRL_READY; + mkmif_ctrl_we = 1; + end + end + + default: + begin + end + endcase // case (mkmif_ctrl_reg) + end // mkmif_ctrl +endmodule // mkmif + +//====================================================================== +// EOF mkmif.v +//====================================================================== diff --git a/src/rtl/mkmif_spi.v b/src/rtl/mkmif_spi.v new file mode 100644 index 0000000..fc80470 --- /dev/null +++ b/src/rtl/mkmif_spi.v @@ -0,0 +1,378 @@ +//====================================================================== +// +// mkmif_spi.v +// ----------- +// SPI interface for the master key memory. When enabled the +// interface waits for command to transmit and receive a given +// number of bytes. Data is transmitted onto the spi_di port +// from the MSB of the spi_data register. Simultaneously, +// data captured on the spi_do port is inserted at LSB in the +// spi_data register. The spi clock is generated when data is to be +// sent or recived. +// +// +// Author: Joachim Strombergson +// Copyright (c) 2011, NORDUnet A/S All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of the NORDUnet nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//====================================================================== + +module mkmif_spi( + input wire clk, + input wire reset_n, + + output wire spi_sclk, + output wire spi_cs_n, + input wire spi_do, + output wire spi_di, + + input wire set, + input wire start, + input wire [2 : 0] length, + input wire [15 : 0] divisor, + output wire ready, + input wire [55 : 0] wr_data, + output wire [31 : 0] rd_data + ); + + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + localparam CTRL_IDLE = 2'h0; + localparam CTRL_START = 2'h1; + localparam CTRL_WAIT = 2'h2; + localparam CTRL_DONE = 2'h3; + + + //---------------------------------------------------------------- + // Registers including update variables and write enable. + //---------------------------------------------------------------- + reg do_sample0_reg; + reg do_sample1_reg; + + reg cs_n_reg; + reg cs_n_new; + reg cs_n_we; + + reg ready_reg; + reg ready_new; + reg ready_we; + + reg [55 : 0] data_reg; + reg [55 : 0] data_new; + reg data_set; + reg data_nxt; + reg data_we; + + reg sclk_reg; + reg sclk_new; + reg sclk_rst; + reg sclk_en; + reg sclk_we; + + reg [15 : 0] clk_ctr_reg; + reg [15 : 0] clk_ctr_new; + reg clk_ctr_we; + + reg [5 : 0] bit_ctr_reg; + reg [5 : 0] bit_ctr_new; + reg bit_ctr_rst; + reg bit_ctr_inc; + reg bit_ctr_done; + reg bit_ctr_we; + + reg [2 : 0] length_reg; + reg length_we; + + reg [15 : 0] divisor_reg; + reg divisor_we; + + reg [1 : 0] spi_ctrl_reg; + reg [1 : 0] spi_ctrl_new; + reg spi_ctrl_we; + + + //---------------------------------------------------------------- + // Wires. + //---------------------------------------------------------------- + + + //---------------------------------------------------------------- + // Concurrent connectivity for ports etc. + //---------------------------------------------------------------- + assign spi_sclk = sclk_reg; + assign spi_cs_n = cs_n_reg; + assign spi_di = data_reg[55]; + assign rd_data = data_reg[31 : 0]; + assign ready = ready_reg; + + + //---------------------------------------------------------------- + // reg_update + // Update functionality for all registers in the core. + // All registers are positive edge triggered with asynchronous + // active low reset. + //---------------------------------------------------------------- + always @ (posedge clk or negedge reset_n) + begin + if (!reset_n) + begin + do_sample0_reg <= 1'h0; + do_sample1_reg <= 1'h0; + cs_n_reg <= 1'h1; + ready_reg <= 1'h0; + length_reg <= 3'h0; + divisor_reg <= 16'h0; + data_reg <= 56'h0; + sclk_reg <= 1'h0; + clk_ctr_reg <= 16'h0; + bit_ctr_reg <= 6'h0; + spi_ctrl_reg <= CTRL_IDLE; + end + else + begin + do_sample0_reg <= spi_do; + do_sample1_reg <= do_sample0_reg; + + if (cs_n_we) + cs_n_reg <= cs_n_new; + + if (ready_we) + ready_reg <= ready_new; + + if (data_we) + data_reg <= data_new; + + if (length_we) + length_reg <= length; + + if (divisor_we) + divisor_reg <= divisor; + + if (sclk_we) + sclk_reg <= sclk_new; + + if (clk_ctr_we) + clk_ctr_reg <= clk_ctr_new; + + if (bit_ctr_we) + bit_ctr_reg <= bit_ctr_new; + + if (spi_ctrl_we) + spi_ctrl_reg <= spi_ctrl_new; + + end + end // reg_update + + + //---------------------------------------------------------------- + // data_gen + // + // Generate the data bitstream to be written out to the external + // SPI connected memory. Basically a shift register. + // Note that we also shift in data received from the external + // memory. + //---------------------------------------------------------------- + always @* + begin : data_gen + data_new = 56'h0; + data_we = 0; + + if (data_set) + begin + data_new = wr_data; + data_we = 1; + end + + if (data_nxt) + begin + data_new = {data_reg[54 : 0], do_sample1_reg}; + data_we = 1; + end + end // data_gen + + + //---------------------------------------------------------------- + // sclk_gen + // + // Generator of the spi_sclk clock. + //---------------------------------------------------------------- + always @* + begin : sclk_gen + sclk_new = 0; + sclk_we = 0; + clk_ctr_new = 0; + clk_ctr_we = 0; + data_nxt = 0; + bit_ctr_rst = 0; + bit_ctr_inc = 0; + + if (sclk_rst) + begin + clk_ctr_new = 0; + clk_ctr_we = 1; + bit_ctr_rst = 1; + sclk_new = 0; + sclk_we = 1; + end + + if (sclk_en) + begin + if (clk_ctr_reg == divisor_reg) + begin + clk_ctr_new = 0; + clk_ctr_we = 1'b1; + sclk_new = ~sclk_reg; + sclk_we = 1; + + if (sclk_reg) + begin + bit_ctr_inc = 1; + data_nxt = 1; + end + end + else + begin + clk_ctr_new = clk_ctr_reg + 1'b1; + clk_ctr_we = 1'b1; + end + end + end // sclk_gen + + + //---------------------------------------------------------------- + // bit_ctr + // + // Bit counter used by the FSM to keep track of the number bits + // being read from or written to the memory. + //---------------------------------------------------------------- + always @* + begin : bit_ctr + bit_ctr_new = 6'h0; + bit_ctr_we = 1'b0; + bit_ctr_done = 1'b0; + + if (bit_ctr_reg == {length_reg, 3'h0}) + bit_ctr_done = 1'b1; + + if (bit_ctr_rst) + begin + bit_ctr_new = 6'h0; + bit_ctr_we = 1'b1; + end + + if (bit_ctr_inc) + begin + bit_ctr_new = bit_ctr_reg + 1'b1; + bit_ctr_we = 1'b1; + end + end // bit_ctr + + + //---------------------------------------------------------------- + // spi_ctrl + // + // Control FSM for the SPI interface. + //---------------------------------------------------------------- + always @* + begin : spi_ctrl + sclk_en = 0; + sclk_rst = 0; + cs_n_new = 1; + cs_n_we = 0; + data_set = 0; + length_we = 0; + divisor_we = 0; + ready_new = 0; + ready_we = 0; + spi_ctrl_new = CTRL_IDLE; + spi_ctrl_we = 0; + + case (spi_ctrl_reg) + CTRL_IDLE: + begin + ready_new = 1; + ready_we = 1; + + if (set) + begin + data_set = 1; + length_we = 1; + divisor_we = 1; + end + + if (start) + begin + ready_new = 0; + ready_we = 1; + sclk_rst = 1; + spi_ctrl_new = CTRL_START; + spi_ctrl_we = 1; + end + end + + CTRL_START: + begin + cs_n_new = 0; + cs_n_we = 1; + spi_ctrl_new = CTRL_WAIT; + spi_ctrl_we = 1; + end + + CTRL_WAIT: + begin + sclk_en = 1; + if (bit_ctr_done) + begin + spi_ctrl_new = CTRL_DONE; + spi_ctrl_we = 1; + end + end + + CTRL_DONE: + begin + ready_new = 1; + ready_we = 1; + cs_n_new = 1; + cs_n_we = 1; + spi_ctrl_new = CTRL_IDLE; + spi_ctrl_we = 1; + end + + default: + begin + end + endcase // case (spi_ctrl_reg) + end // spi_ctrl + +endmodule // mkmif_spi + +//====================================================================== +// EOF mkmif_spi.v +//====================================================================== diff --git a/src/tb/tb_mkmif.v b/src/tb/tb_mkmif.v new file mode 100644 index 0000000..4142574 --- /dev/null +++ b/src/tb/tb_mkmif.v @@ -0,0 +1,438 @@ +//====================================================================== +// +// tb_mkmif.v +// ------------ +// Testbench for the mkmif top level wrapper. +// +// +// Author: Joachim Strombergson +// Copyright (c) 2011, NORDUnet A/S All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of the NORDUnet nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//====================================================================== + +//------------------------------------------------------------------ +// Compiler directives. +//------------------------------------------------------------------ +`timescale 1ns/100ps + +module tb_mkmif(); + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + parameter DEBUG = 1; + + parameter CLK_HALF_PERIOD = 2; + parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD; + + localparam ADDR_NAME0 = 8'h00; + localparam ADDR_NAME1 = 8'h01; + localparam ADDR_VERSION = 8'h02; + localparam ADDR_CTRL = 8'h08; + localparam ADDR_STATUS = 8'h09; + localparam ADDR_CONFIG = 8'h0a; + localparam ADDR_EMEM_ADDR = 8'h10; + localparam ADDR_EMEM_DATA = 8'h20; + + localparam CORE_NAME0 = 32'h6d6b6d69; // "mkmi" + localparam CORE_NAME1 = 32'h66202020; // "f " + localparam CORE_VERSION = 32'h302e3130; // "0.10" + + + //---------------------------------------------------------------- + // Register and Wire declarations. + //---------------------------------------------------------------- + reg [31 : 0] cycle_ctr; + reg [31 : 0] test_ctr; + reg [31 : 0] error_ctr; + + reg tb_clk; + reg tb_reset_n; + wire tb_spi_sclk; + wire tb_spi_cs_n; + reg tb_spi_do; + wire tb_spi_di; + reg tb_cs; + reg tb_we; + reg [7 : 0] tb_address; + reg [31 : 0] tb_write_data; + wire [31 : 0] tb_read_data; + reg tb_dump_state; + wire tb_error; + reg [31 : 0] read_data; + + + //---------------------------------------------------------------- + // mkmif device under test. + //---------------------------------------------------------------- + mkmif dut( + .clk(tb_clk), + .reset_n(tb_reset_n), + + .spi_sclk(tb_spi_sclk), + .spi_cs_n(tb_spi_cs_n), + .spi_do(tb_spi_di), + .spi_di(tb_spi_di), + + .cs(tb_cs), + .we(tb_we), + .address(tb_address), + .write_data(tb_write_data), + .read_data(tb_read_data) + ); + + + //---------------------------------------------------------------- + // clk_gen + // Clock generator process. + //---------------------------------------------------------------- + always + begin : clk_gen + #CLK_HALF_PERIOD tb_clk = !tb_clk; + end // clk_gen + + + //-------------------------------------------------------------------- + // dut_monitor + // Monitor for observing the inputs and outputs to the dut. + // Includes the cycle counter. + //-------------------------------------------------------------------- + always @ (posedge tb_clk) + begin : dut_monitor + cycle_ctr = cycle_ctr + 1; + + if (DEBUG) + $display("cycle = %8x:", cycle_ctr); + + if (tb_dump_state) + dump_state(); + end // dut_monitor + + + //---------------------------------------------------------------- + // inc_test_ctr + //---------------------------------------------------------------- + task inc_test_ctr; + begin + test_ctr = test_ctr +1; + end + endtask // inc_test_ctr + + + //---------------------------------------------------------------- + // inc_error_ctr + //---------------------------------------------------------------- + task inc_error_ctr; + begin + error_ctr = error_ctr +1; + end + endtask // inc_error_ctr + + + + //---------------------------------------------------------------- + // dump_state + // Dump the internal MKMIF state to std out. + //---------------------------------------------------------------- + task dump_state; + begin + + $display("mkmif_core_ctrl_reg: 0x%02x, core ready: 0x%01x, core valid: 0x%01x", + dut.core.mkmif_ctrl_reg, dut.core_ready, dut.core_valid); + $display("sclk: 0x%01x, cs_n: 0x%01x, di: 0x%01x, do: 0x%01x, nxt: 0x%01x", + tb_spi_sclk, tb_spi_cs_n, tb_spi_di, tb_spi_do, dut.core.spi.data_nxt); + $display("spi_ctrl_reg: 0x%01x, spi_clk_ctr: 0x%04x, spi_bit_ctr: 0x%02x", + dut.core.spi.spi_ctrl_reg, dut.core.spi.clk_ctr_reg, dut.core.spi.bit_ctr_reg); + $display("spi length: 0x%02x, spi divisor: 0x%04x, spi set: 0x%01x, spi start: 0x%01x, spi ready: 0x%01x", + dut.core.spi.length_reg, dut.core.spi.divisor_reg, dut.core.spi.set, dut.core.spi.start, dut.core.spi.ready); + $display("read data: 0x%08x, write_data: 0x%014x", dut.core.spi.rd_data, dut.core.spi.wr_data); + $display("spi data reg: 0x%014x", dut.core.spi.data_reg); + $display(""); + end + endtask // dump_state + + + //---------------------------------------------------------------- + // tb_init + // Initialize varibles, dut inputs at start. + //---------------------------------------------------------------- + task tb_init; + begin + test_ctr = 0; + error_ctr = 0; + cycle_ctr = 0; + tb_clk = 0; + tb_reset_n = 1; + tb_spi_do = 0; + tb_cs = 1'b0; + tb_we = 1'b0; + tb_address = 8'h00; + tb_write_data = 32'h00; + tb_dump_state = 1; + end + endtask // tb_init + + + //---------------------------------------------------------------- + // toggle_reset + // Toggle the reset. + //---------------------------------------------------------------- + task toggle_reset; + begin + $display(" --- Toggling reset started."); + dump_state(); + #(2 * CLK_PERIOD); + tb_reset_n = 0; + #(10 * CLK_PERIOD); + @(negedge tb_clk) + tb_reset_n = 1; + dump_state(); + $display(" --- Toggling of reset done."); + $display(""); + end + endtask // toggle_reset + + + //---------------------------------------------------------------- + // read_word() + // + // Read a data word from the given address in the DUT. + // the word read will be available in the global variable + // read_data. + //---------------------------------------------------------------- + task read_word(input [7 : 0] address); + begin + tb_address = address; + tb_cs = 1; + tb_we = 0; + #(CLK_PERIOD); + read_data = tb_read_data; + tb_cs = 0; + + if (DEBUG) + begin + $display("*** Reading 0x%08x from 0x%02x.", read_data, address); + $display(""); + end + end + endtask // read_word + + + //---------------------------------------------------------------- + // write_word() + // + // Write the given word to the DUT using the DUT interface. + //---------------------------------------------------------------- + task write_word(input [7 : 0] address, + input [31 : 0] word); + begin + if (DEBUG) + begin + $display("*** Writing 0x%08x to 0x%02x.", word, address); + $display(""); + end + + tb_address = address; + tb_write_data = word; + tb_cs = 1; + tb_we = 1; + #(CLK_PERIOD); + tb_cs = 0; + tb_we = 0; + end + endtask // write_word + + + //---------------------------------------------------------------- + // wait_ready() + // + // Wait for ready word to be set in the DUT API. + //---------------------------------------------------------------- + task wait_ready; + reg ready; + begin + ready = 0; + + while (ready == 0) + begin + read_word(ADDR_STATUS); + ready = read_data & 32'h00000001; + end + end + endtask // read_word + + + //---------------------------------------------------------------- + // check_name_version() + // + // Read the name and version from the DUT. + //---------------------------------------------------------------- + task check_name_version; + reg [31 : 0] name0; + reg [31 : 0] name1; + reg [31 : 0] version; + begin + inc_test_ctr(); + + $display(" -- Test of reading name and version started."); + + read_word(ADDR_NAME0); + name0 = read_data; + read_word(ADDR_NAME1); + name1 = read_data; + read_word(ADDR_VERSION); + version = read_data; + + if ((name0 == CORE_NAME0) && (name1 == CORE_NAME1) && (version == CORE_VERSION)) + $display("Correct name and version read from dut."); + else + begin + inc_error_ctr(); + $display("Error:"); + $display("Got name: %c%c%c%c%c%c%c%c", + name0[31 : 24], name0[23 : 16], name0[15 : 8], name0[7 : 0], + name1[31 : 24], name1[23 : 16], name1[15 : 8], name1[7 : 0]); + $display("Expected name: %c%c%c%c%c%c%c%c", + CORE_NAME0[31 : 24], CORE_NAME0[23 : 16], CORE_NAME0[15 : 8], CORE_NAME0[7 : 0], + CORE_NAME1[31 : 24], CORE_NAME1[23 : 16], CORE_NAME1[15 : 8], CORE_NAME1[7 : 0]); + + $display("Got version: %c%c%c%c", + version[31 : 24], version[23 : 16], version[15 : 8], version[7 : 0]); + $display("Expected version: %c%c%c%c", + CORE_VERSION[31 : 24], CORE_VERSION[23 : 16], CORE_VERSION[15 : 8], CORE_VERSION[7 : 0]); + + $display(" -- Test of reading name and version done."); + $display(""); + end + end + endtask // check_name_version + + + //---------------------------------------------------------------- + // write_test + // + // Try to write a few words of data. + //---------------------------------------------------------------- + task write_test; + begin + inc_test_ctr(); + $display(" -- Test of writing words to the memory started."); + + wait_ready(); + $display("Ready has been set. Starting write commands."); + write_word(ADDR_EMEM_ADDR, 16'h0010); + write_word(ADDR_EMEM_DATA, 32'hdeadbeef); + write_word(ADDR_CTRL, 32'h2); + #(10 * CLK_PERIOD); + wait_ready(); + read_word(ADDR_EMEM_DATA); + $display("First write completed. Read: 0x%08x", read_data); + + write_word(ADDR_EMEM_ADDR, 16'h0020); + write_word(ADDR_EMEM_DATA, 32'haa55aa55); + write_word(ADDR_CTRL, 32'h2); + #(10 * CLK_PERIOD); + wait_ready(); + read_word(ADDR_EMEM_DATA); + $display("Second write completed. Read: 0x%08x", read_data); + +// write_word(ADDR_EMEM_ADDR, 16'h0100); +// write_word(ADDR_EMEM_DATA, 32'h004488ff); +// write_word(ADDR_CTRL, 32'h2); +// #(1000 * CLK_PERIOD); +// wait_ready(); + + $display(" -- Test of writing words to the memory done."); + $display(""); + end + endtask // write_test + + + //---------------------------------------------------------------- + // read_test + // + // Try to read a few words of data. + //---------------------------------------------------------------- + task read_test; + begin + inc_test_ctr(); + $display(" -- Test of reading from the memory started."); + +// wait_ready(); +// $display("Ready has been set. Starting write commands."); +// write_word(ADDR_EMEM_ADDR, 16'h0010); +// write_word(ADDR_EMEM_DATA, 32'hdeadbeef); +// write_word(ADDR_CTRL, 32'h2); +// #(1000 * CLK_PERIOD); +// wait_ready(); + +// write_word(ADDR_EMEM_ADDR, 16'h0020); +// write_word(ADDR_EMEM_DATA, 32'haa55aa55); +// write_word(ADDR_CTRL, 32'h2); +// #(1000 * CLK_PERIOD); +// wait_ready(); +// +// write_word(ADDR_EMEM_ADDR, 16'h0100); +// write_word(ADDR_EMEM_DATA, 32'h004488ff); +// write_word(ADDR_CTRL, 32'h2); +// #(1000 * CLK_PERIOD); +// wait_ready(); + + $display(" -- Test of reading from the memory done."); + $display(""); + end + endtask // read_test + + + //---------------------------------------------------------------- + // mkmif_test + // The main test functionality. + //---------------------------------------------------------------- + initial + begin : mkmif_test + $display(" --*** Test of mkmif started ***--"); + + tb_init(); + toggle_reset(); + check_name_version(); + write_test(); + read_test(); + + $display(""); + $display(" --*** Test of mkmif completed ***--"); + $display("Tests executed: %04d", test_ctr); + $display("Tests failed: %04d", error_ctr); + $finish; + end // mkmif_test + +endmodule // tb_mkmif + +//====================================================================== +// EOF tb_mkmif.v +//====================================================================== diff --git a/src/tb/tb_mkmif_core.v b/src/tb/tb_mkmif_core.v new file mode 100644 index 0000000..48042f4 --- /dev/null +++ b/src/tb/tb_mkmif_core.v @@ -0,0 +1,285 @@ +//====================================================================== +// +// tb_mkmif_core.v +// --------------- +// Testbench for the mkmif core module. +// +// +// Author: Joachim Strombergson +// Copyright (c) 2011, NORDUnet A/S All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of the NORDUnet nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//====================================================================== + +//------------------------------------------------------------------ +// Compiler directives. +//------------------------------------------------------------------ +`timescale 1ns/100ps + +module tb_mkmif_core(); + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + parameter DEBUG = 1; + + parameter CLK_HALF_PERIOD = 2; + parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD; + + + //---------------------------------------------------------------- + // Register and Wire declarations. + //---------------------------------------------------------------- + reg [31 : 0] cycle_ctr; + reg [31 : 0] test_ctr; + reg [31 : 0] error_ctr; + + reg tb_clk; + reg tb_reset_n; + wire tb_spi_sclk; + wire tb_spi_do; + wire tb_spi_di; + reg tb_read_op; + reg tb_write_op; + reg tb_init_op; + wire tb_ready; + wire tb_valid; + reg [15 : 0] tb_sclk_div; + reg [15 : 0] tb_addr; + reg [31 : 0] tb_write_data; + wire [31 : 0] tb_read_data; + + reg tb_display_state; + reg [31 : 0] read_data; + + + //---------------------------------------------------------------- + // Concurrent connectivity. + //---------------------------------------------------------------- + // We loop back the inverted SPI serial transmit data from + // the DUT as the data from the memory (do) to the DUT. + assign tb_spi_do = ~tb_spi_di; + + + //---------------------------------------------------------------- + // mkmif device under test. + //---------------------------------------------------------------- + mkmif_core dut( + .clk(tb_clk), + .reset_n(tb_reset_n), + + .spi_sclk(tb_spi_sclk), + .spi_cs_n(tb_cs_n), + .spi_do(tb_spi_do), + .spi_di(tb_spi_di), + + .read_op(tb_read_op), + .write_op(tb_write_op), + .init_op(tb_init_op), + .ready(tb_ready), + .valid(tb_valid), + .sclk_div(tb_sclk_div), + .addr(tb_addr), + .write_data(tb_write_data), + .read_data(tb_read_data) + ); + + + //---------------------------------------------------------------- + // clk_gen + // Clock generator process. + //---------------------------------------------------------------- + always + begin : clk_gen + #CLK_HALF_PERIOD tb_clk = !tb_clk; + end // clk_gen + + + //-------------------------------------------------------------------- + // dut_monitor + // Monitor for observing the inputs and outputs to the dut. + // Includes the cycle counter. + //-------------------------------------------------------------------- + always @ (posedge tb_clk) + begin : dut_monitor + cycle_ctr = cycle_ctr + 1; + + if (tb_display_state) + begin + $display("cycle = %8x:", cycle_ctr); + dump_state(); + end + end // dut_monitor + + + //---------------------------------------------------------------- + // inc_test_ctr + //---------------------------------------------------------------- + task inc_test_ctr; + begin + test_ctr = test_ctr +1; + end + endtask // inc_test_ctr + + + //---------------------------------------------------------------- + // inc_error_ctr + //---------------------------------------------------------------- + task inc_error_ctr; + begin + error_ctr = error_ctr +1; + end + endtask // inc_error_ctr + + + //---------------------------------------------------------------- + // dump_state + // Dump the internal MKMIF state to std out. + //---------------------------------------------------------------- + task dump_state; + begin + $display("mkmif_ctrl_reg: 0x%02x", dut.mkmif_ctrl_reg); + $display("sclk: 0x%01x, cs_n: 0x%01x, di: 0x%01x, do: 0x%01x, nxt: 0x%01x", + tb_spi_sclk, tb_cs_n, tb_spi_di, tb_spi_do, dut.spi.data_nxt); + $display("spi_ctrl_reg: 0x%01x, spi_clk_ctr: 0x%04x, spi_bit_ctr: 0x%02x", + dut.spi.spi_ctrl_reg, dut.spi.clk_ctr_reg, dut.spi.bit_ctr_reg); + $display("spi length: 0x%02x, spi divisor: 0x%04x, spi set: 0x%01x, spi start: 0x%01x, spi ready: 0x%01x", + dut.spi.length_reg, dut.spi.divisor_reg, dut.spi.set, dut.spi.start, dut.spi.ready); + $display("read data: 0x%08x, write_data: 0x%014x", dut.spi.rd_data, dut.spi.wr_data); + $display(""); + end + endtask // dump_state + + + //---------------------------------------------------------------- + // tb_init + // Initialize varibles, dut inputs at start. + //---------------------------------------------------------------- + task tb_init; + begin + test_ctr = 0; + error_ctr = 0; + cycle_ctr = 0; + + tb_clk = 0; + tb_reset_n = 1; + tb_read_op = 0; + tb_write_op = 0; + tb_init_op = 0; + tb_sclk_div = 16'h0004; + tb_addr = 16'h0010; + tb_write_data = 32'haa55aa55; + + tb_display_state = 1; + end + endtask // tb_init + + + //---------------------------------------------------------------- + // wait_ready() + // + // Wait for ready word to be set in the DUT API. + //---------------------------------------------------------------- + task wait_ready; + reg ready; + begin + ready = 0; + + while (tb_ready == 0) + begin + #(CLK_PERIOD); + end + end + endtask // read_word + + + //---------------------------------------------------------------- + // toggle_reset + // Toggle the reset. + //---------------------------------------------------------------- + task toggle_reset; + begin + $display(" -- Toggling reset."); + dump_state(); + #(2 * CLK_PERIOD); + tb_reset_n = 0; + #(10 * CLK_PERIOD); + @(negedge tb_clk) + tb_reset_n = 1; + dump_state(); + $display(" -- Toggling of reset done."); + $display(""); + end + endtask // toggle_reset + + + //---------------------------------------------------------------- + // write_test + //---------------------------------------------------------------- + task write_test; + begin + $display(" -- Write Test started."); + inc_test_ctr(); + wait_ready(); + tb_sclk_div = 16'h0004; + tb_addr = 16'h0012; + tb_write_data = 32'hdeadbeef; + tb_write_op = 1; + #(2 * CLK_PERIOD); + tb_write_op = 0; + wait_ready(); + $display(" -- Write Test done."); + $display(""); + end + endtask // write_test + + + //---------------------------------------------------------------- + // mkmif_core_test + // The main test functionality. + //---------------------------------------------------------------- + initial + begin : mkmif__core_test + $display(" -- Test of mkmif core started --"); + + tb_init(); + toggle_reset(); + write_test(); + + $display(""); + $display(" -- Test of mkmif core completed --"); + $display("Tests executed: %04d", test_ctr); + $display("Tests failed: %04d", error_ctr); + $finish; + end // mkmif_core_test + +endmodule // tb_mkmif_core + +//====================================================================== +// EOF tb_mkmif_core.v +//====================================================================== diff --git a/src/tb/tb_mkmif_spi.v b/src/tb/tb_mkmif_spi.v new file mode 100644 index 0000000..763defe --- /dev/null +++ b/src/tb/tb_mkmif_spi.v @@ -0,0 +1,286 @@ +//====================================================================== +// +// tb_mkmif_spi.v +// -------------- +// Testbench for the mkmif SPI module. +// +// +// Author: Joachim Strombergson +// Copyright (c) 2011, NORDUnet A/S All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of the NORDUnet nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//====================================================================== + +//------------------------------------------------------------------ +// Compiler directives. +//------------------------------------------------------------------ +`timescale 1ns/100ps + +module tb_mkmif_spi(); + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + parameter DEBUG = 1; + + parameter CLK_HALF_PERIOD = 2; + parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD; + + + //---------------------------------------------------------------- + // Register and Wire declarations. + //---------------------------------------------------------------- + reg [31 : 0] cycle_ctr; + reg [31 : 0] test_ctr; + reg [31 : 0] error_ctr; + + reg tb_clk; + reg tb_reset_n; + wire tb_spi_sclk; + wire tb_spi_cs_n; + reg tb_spi_do; + wire tb_spi_di; + reg tb_set; + reg tb_start; + reg [2 : 0] tb_length; + reg [15 : 0] tb_divisor; + wire tb_ready; + reg [55 : 0] tb_write_data; + wire [31 : 0] tb_read_data; + reg tb_dump_state; + + + //---------------------------------------------------------------- + // mkmif device under test. + //---------------------------------------------------------------- + mkmif_spi dut( + .clk(tb_clk), + .reset_n(tb_reset_n), + + .spi_sclk(tb_spi_sclk), + .spi_cs_n(tb_spi_cs_n), + .spi_do(tb_spi_di), + .spi_di(tb_spi_di), + + .set(tb_set), + .start(tb_start), + .length(tb_length), + .divisor(tb_divisor), + .ready(tb_ready), + .wr_data(tb_write_data), + .rd_data(tb_read_data) + ); + + + //---------------------------------------------------------------- + // clk_gen + // Clock generator process. + //---------------------------------------------------------------- + always + begin : clk_gen + #CLK_HALF_PERIOD tb_clk = !tb_clk; + end // clk_gen + + + //-------------------------------------------------------------------- + // dut_monitor + // Monitor for observing the inputs and outputs to the dut. + // Includes the cycle counter. + //-------------------------------------------------------------------- + always @ (posedge tb_clk) + begin : dut_monitor + cycle_ctr = cycle_ctr + 1; + end // dut_monitor + + + //---------------------------------------------------------------- + // inc_test_ctr + //---------------------------------------------------------------- + task inc_test_ctr; + begin + test_ctr = test_ctr +1; + end + endtask // inc_test_ctr + + + //---------------------------------------------------------------- + // inc_error_ctr + //---------------------------------------------------------------- + task inc_error_ctr; + begin + error_ctr = error_ctr +1; + end + endtask // inc_error_ctr + + + //---------------------------------------------------------------- + // dump_ports + // Dump the status of the dut ports. + //---------------------------------------------------------------- + task dump_ports; + begin + $display(""); + end + endtask // dump_ports + + + //---------------------------------------------------------------- + // dump_state + // Dump the internal MKMIF state to std out. + //---------------------------------------------------------------- + task dump_state; + begin + $display("Dut state:"); + $display("data_reg: 0x%014x", dut.data_reg); + $display("clk_ctr: 0x%08x", dut.clk_ctr_reg); + $display("bit_ctr: 0x%02x", dut.bit_ctr_reg); + $display("ctrl: 0x%02x, done: 0x%01x, ready: 0x%01x", + dut.spi_ctrl_reg, dut.bit_ctr_done, tb_ready); + $display("Output:"); + $display("en: 0x%01x, sclk: 0x%01x, di: 0x%01x, do: 0x%01x", + tb_spi_cs_n, tb_spi_sclk, tb_spi_di, tb_spi_di); + $display("read data: 0x%08x", tb_read_data); + $display(""); + end + endtask // dump_state + + + //---------------------------------------------------------------- + // wait_ready() + // + // Wait for ready word to be set in the DUT API. + //---------------------------------------------------------------- + task wait_ready; + reg ready; + begin + ready = 0; + + while (tb_ready == 0) + begin + #(CLK_PERIOD); + dump_state(); + end + end + endtask // read_word + + + //---------------------------------------------------------------- + // tb_init + // Initialize varibles, dut inputs at start. + //---------------------------------------------------------------- + task tb_init; + begin + test_ctr = 0; + error_ctr = 0; + cycle_ctr = 0; + + tb_clk = 0; + tb_reset_n = 1; + tb_spi_do = 0; + tb_set = 0; + tb_start = 0; + tb_length = 3'h7; + tb_divisor = 16'h0008; + tb_write_data = 32'haa55aa55; + + tb_dump_state = 0; + end + endtask // tb_init + + + //---------------------------------------------------------------- + // toggle_reset + // Toggle the reset. + //---------------------------------------------------------------- + task toggle_reset; + begin + $display(" -- Toggling reset."); + dump_state(); + #(2 * CLK_PERIOD); + tb_reset_n = 0; + #(10 * CLK_PERIOD); + @(negedge tb_clk) + tb_reset_n = 1; + dump_state(); + $display(" -- Toggling of reset done."); + end + endtask // toggle_reset + + + //---------------------------------------------------------------- + // transmit_data + // Test case for testing that the dut will transmit data + // on the interface. + //---------------------------------------------------------------- + task transmit_data; + begin + $display(" -- Trying to transmit data."); + tb_set = 1; + tb_write_data = 56'hdeadbeeff18244; + #(2 * CLK_PERIOD); + $display("Contents of data reg in dut after set: 0x%14x", + dut.data_reg); + + #(2 * CLK_PERIOD); + tb_divisor = 16'h8; + tb_length = 3'h4; + tb_start = 1; + #(2 * CLK_PERIOD); + tb_start = 0; + + wait_ready(); + + $display(" -- Transmit data test done.."); + end + endtask // transmit_data + + + //---------------------------------------------------------------- + // mkmif_spi_test + // The main test functionality. + //---------------------------------------------------------------- + initial + begin : mkmif_spi_test + $display(" -- Test of mkmif spi started --"); + + tb_init(); + toggle_reset(); + transmit_data(); + + $display(""); + $display(" -- Test of mkmif spi completed --"); + $display("Tests executed: %04d", test_ctr); + $display("Tests failed: %04d", error_ctr); + $finish; + end // mkmif_spi_test + +endmodule // tb_mkmif_spi + +//====================================================================== +// EOF tb_mkmif_spi.v +//====================================================================== |