From c03aa37d4f237f2eb90d9a958428011ca2bd455c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joachim=20Stro=CC=88mbergson?= <joachim@secworks.se>
Date: Wed, 11 May 2016 17:13:04 +0200
Subject: Adding all source files and testbenches for the mkmif core. Adding
 Makefile for building simulation and linting. Adding top level license file.

---
 src/rtl/mkmif.v        | 256 +++++++++++++++++++++++++++++
 src/rtl/mkmif_core.v   | 292 +++++++++++++++++++++++++++++++++
 src/rtl/mkmif_spi.v    | 378 ++++++++++++++++++++++++++++++++++++++++++
 src/tb/tb_mkmif.v      | 438 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/tb/tb_mkmif_core.v | 285 ++++++++++++++++++++++++++++++++
 src/tb/tb_mkmif_spi.v  | 286 ++++++++++++++++++++++++++++++++
 6 files changed, 1935 insertions(+)
 create mode 100644 src/rtl/mkmif.v
 create mode 100644 src/rtl/mkmif_core.v
 create mode 100644 src/rtl/mkmif_spi.v
 create mode 100644 src/tb/tb_mkmif.v
 create mode 100644 src/tb/tb_mkmif_core.v
 create mode 100644 src/tb/tb_mkmif_spi.v

(limited to 'src')

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
+//======================================================================
-- 
cgit v1.2.3