[Cryptech-Commits] [user/paul/core/coretest_hashes] 01/01: code reorg to bring novena* projects under coretest_hashes/toolruns/xilinx/novena

git at cryptech.is git at cryptech.is
Thu Nov 20 00:12:29 UTC 2014


This is an automated email from the git hooks/post-receive script.

paul at psgd.org pushed a commit to branch master
in repository user/paul/core/coretest_hashes.

commit f84a778db87b41e6e5e1d34596b22eb3d40c85ad
Author: Paul Selkirk <paul at psgd.org>
Date:   Wed Nov 19 19:07:17 2014 -0500

    code reorg to bring novena* projects under coretest_hashes/toolruns/xilinx/novena
---
 LICENSE                                            |   24 +
 README.md                                          |   58 +
 src/rtl/coretest_hashes_eim.v                      |  274 ++++
 src/rtl/coretest_hashes_i2c.v                      |  321 +++++
 src/rtl/coretest_hashes_i2c_simple.v               | 1514 ++++++++++++++++++++
 src/rtl/coretest_hashes_uart.v                     |  315 ++++
 src/sw/hash_i2c.c                                  |  620 ++++++++
 src/sw/hash_i2c_simple.c                           |  337 +++++
 src/sw/hash_tester_eim.c                           |  962 +++++++++++++
 src/sw/hash_tester_i2c.c                           |  981 +++++++++++++
 src/sw/hash_tester_i2c.py                          |  693 +++++++++
 src/sw/hash_tester_i2c_simple.c                    |  688 +++++++++
 src/sw/hash_tester_uart.py                         | 1021 +++++++++++++
 toolruns/Makefile                                  |   72 +
 toolruns/quartus/terasic_c5g/coretest_hashes.qpf   |   30 +
 toolruns/quartus/terasic_c5g/coretest_hashes.qsf   |   89 ++
 toolruns/quartus/terasic_c5g/coretest_hashes.sdc   |   40 +
 .../cryptech_pre_build_image/coretest_hashes.sof   |  Bin 0 -> 3993967 bytes
 toolruns/xilinx/novena/.gitignore                  |   58 +
 toolruns/xilinx/novena/Makefile.eim                |   28 +
 toolruns/xilinx/novena/Makefile.i2c                |   27 +
 toolruns/xilinx/novena/Makefile.i2c_simple         |   23 +
 toolruns/xilinx/novena/common/sync_reset.v         |   61 +
 toolruns/xilinx/novena/ip/bclk_dll.v               |  148 ++
 toolruns/xilinx/novena/ip/clk_dll.v                |  162 +++
 toolruns/xilinx/novena/ip/dcm_delay.v              |  168 +++
 toolruns/xilinx/novena/novena_eim.bmm              |    0
 toolruns/xilinx/novena/novena_eim.ucf              |  653 +++++++++
 toolruns/xilinx/novena/novena_eim.v                |  567 ++++++++
 toolruns/xilinx/novena/novena_i2c.bmm              |    0
 toolruns/xilinx/novena/novena_i2c.ucf              |  594 ++++++++
 toolruns/xilinx/novena/novena_i2c.v                |  147 ++
 toolruns/xilinx/novena/novena_i2c_simple.bmm       |    1 +
 toolruns/xilinx/novena/novena_i2c_simple.ucf       |    1 +
 toolruns/xilinx/novena/xilinx.mk                   |  176 +++
 toolruns/xilinx/novena/xilinx.opt                  |   42 +
 36 files changed, 10895 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8d0d6ec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Author: Joachim Strömbergson
+Copyright (c) 2014, SUNET
+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.
+
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..68ee511
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+coretest_hashes
+===============
+
+The coretest system combined with cryptographic hash functions.
+
+## Introduction ##
+
+This is a HW subsystem that includes the coretest module connected to SHA-1,
+SHA-256, and SHA-512 hash function cores. It can be connected to a number of
+different communication channels, depending on what the target board supports.
+Currently supported communication channels include UART, I2C, and EIM.
+
+## Building firmware ##
+
+All build configurations are found under toolruns/<toolset>/<board>.
+Currently this is limited to:
+- Altera Quartus for TerasIC C5G
+- Xilinx ISE for Novena PVT1
+
+To build for TerasIC C5G, start Quartus, and load the files in
+toolruns/quartus/terasic_c5g.
+See http://trac.cryptech.is/wiki/CoretestHashesC5G for more details.
+
+To build for Novena PVT1, go to toolruns/xilinx/novena and run one of
+    make -f Makefile.i2c
+    make -f Makefile.i2c_simple
+    make -f Makefile.eim
+
+## Software ##
+
+src/sw/ contains test and utility programs for all four communication
+channels.
+
+hash_tester_uart.py runs on a host system, and talks to the TerasIC dev board
+over a UART.
+
+All the other apps run in a shell on the Novena board.
+
+## Status ##*
+***(2014-11-04)***
+EIM version. Built using Xilinx ISE 14.3 for Novena PVT1.
+
+***(2014-09-18)***
+Simplifid I2C version. Built using Xilinx ISE 14.3 for Novena PVT1.
+
+***(2014-08-27)***
+I2C version. Built using Xilinx ISE 14.3 for Novena PVT1.
+
+**(2014-03-07)***
+Initial version. Build using Altera Quarus 13.1.
+
+- Cyclone 5 GX device
+- 2847 ALMs and
+- 3665 registers
+- 86 MHz
+
+
+
diff --git a/src/rtl/coretest_hashes_eim.v b/src/rtl/coretest_hashes_eim.v
new file mode 100644
index 0000000..fdcf8a0
--- /dev/null
+++ b/src/rtl/coretest_hashes_eim.v
@@ -0,0 +1,274 @@
+//======================================================================
+//
+// coretest_hashes.v
+// -----------------
+// Top level wrapper that creates the Cryptech coretest system.
+// The wrapper contains instances of external interface, coretest
+// and the core to be tested. And if more than one core is
+// present the wrapper also includes address and data muxes.
+//
+//
+// Authors: Joachim Strombergson, Paul Selkirk
+// Copyright (c) 2014, 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 coretest_hashes(
+                       // Clock and reset.
+                       input wire        clk,
+                       input wire        reset_n,
+
+                       // Control.
+                       input wire        cs,
+                       input wire        we,
+
+                       // Data ports.
+                       input wire [18:0] address,
+                       input wire [15:0] write_data,
+                       output reg [15:0] read_data
+                       );
+
+
+   //----------------------------------------------------------------
+   // Internal constant and parameter definitions.
+   //----------------------------------------------------------------
+   parameter SHA1_ADDR_PREFIX   = 7'h42;
+   parameter SHA256_ADDR_PREFIX = 7'h43;
+   parameter SHA512_ADDR_PREFIX = 7'h44;
+
+
+   //----------------------------------------------------------------
+   // Wires and registers
+   //----------------------------------------------------------------
+//   reg [18:0]                            address_reg;
+   reg [31:0]                            write_reg;
+   reg [31:0]                            read_reg;
+   reg                                   cs_reg;
+   reg                                   error_reg;
+
+   // sha1 connections.
+   reg                                   sha1_cs;
+   reg                                   sha1_we;
+   reg [7:0]                             sha1_address;
+   reg [31:0]                            sha1_write_data;
+   wire [31:0]                           sha1_read_data;
+   wire                                  sha1_error;
+
+   // sha256 connections.
+   reg                                   sha256_cs;
+   reg                                   sha256_we;
+   reg [7:0]                             sha256_address;
+   reg [31:0]                            sha256_write_data;
+   wire [31:0]                           sha256_read_data;
+   wire                                  sha256_error;
+
+   // sha512 connections.
+   reg                                   sha512_cs;
+   reg                                   sha512_we;
+   reg [7:0]                             sha512_address;
+   reg [31:0]                            sha512_write_data;
+   wire [31:0]                           sha512_read_data;
+   wire                                  sha512_error;
+
+
+   //----------------------------------------------------------------
+   // Concurrent assignment.
+   //----------------------------------------------------------------
+
+
+   //----------------------------------------------------------------
+   // Core instantiations.
+   //----------------------------------------------------------------
+   sha1 sha1(
+             // Clock and reset.
+             .clk(clk),
+             .reset_n(reset_n),
+
+             // Control.
+             .cs(sha1_cs),
+             .we(sha1_we),
+
+             // Data ports.
+             .address(sha1_address),
+             .write_data(sha1_write_data),
+             .read_data(sha1_read_data),
+             .error(sha1_error)
+             );
+
+
+   sha256 sha256(
+                 // Clock and reset.
+                 .clk(clk),
+                 .reset_n(reset_n),
+
+                 // Control.
+                 .cs(sha256_cs),
+                 .we(sha256_we),
+
+                 // Data ports.
+                 .address(sha256_address),
+                 .write_data(sha256_write_data),
+                 .read_data(sha256_read_data),
+                 .error(sha256_error)
+                 );
+
+
+   sha512 sha512(
+                 // Clock and reset.
+                 .clk(clk),
+                 .reset_n(reset_n),
+
+                 // Control.
+                 .cs(sha512_cs),
+                 .we(sha512_we),
+
+                 // Data ports.
+                 .address(sha512_address),
+                 .write_data(sha512_write_data),
+                 .read_data(sha512_read_data),
+                 .error(sha512_error)
+                 );
+
+   //----------------------------------------------------------------
+   // address_mux
+   //
+   // Combinational data mux that handles addressing between
+   // cores using the 32-bit memory like interface.
+   //----------------------------------------------------------------
+   always @*
+     begin : address_mux
+        // Default assignments.
+        cs_reg             = cs;
+        
+        sha1_cs            = 0;
+        sha1_we            = 0;
+        sha1_address       = 8'h00;
+        sha1_write_data    = 32'h00000000;
+
+        sha256_cs          = 0;
+        sha256_we          = 0;
+        sha256_address     = 8'h00;
+        sha256_write_data  = 32'h00000000;
+
+        sha512_cs          = 0;
+        sha512_we          = 0;
+        sha512_address     = 8'h00;
+        sha512_write_data  = 32'h00000000;
+
+        // Handle the awkward interface between 16-bit EIM operations and
+        // 32-bit SHA core wrapper operations.
+        // XXX assume that all reads and writes will be sequential and aligned
+
+        // write operations
+        if (cs && we && address[1:0] == 2'b00)
+          begin
+             // save the first word of the write data
+//             address_reg = address;
+             write_reg[31:16] = write_data;
+             cs_reg = 0;
+          end
+        else if (cs && we && address[1:0] == 2'b10)
+          begin
+             // write out the complete 4 bytes
+//             if (address[18:2] == address_reg[18:2])
+               begin
+                  write_reg[15:0] = write_data;
+               end
+          end
+        else
+          begin
+//             address_reg = address_reg;
+             write_reg = write_reg;
+          end
+
+        // address mux
+        case (address[18:12])
+          SHA1_ADDR_PREFIX:
+            begin
+               sha1_cs            = cs_reg;
+               sha1_we            = we;
+               sha1_address       = address[9:2];
+               sha1_write_data    = write_reg;
+               read_reg           = sha1_read_data;
+               error_reg          = sha1_error;
+            end
+
+          SHA256_ADDR_PREFIX:
+            begin
+               sha256_cs          = cs_reg;
+               sha256_we          = we;
+               sha256_address     = address[9:2];
+               sha256_write_data  = write_reg;
+               read_reg           = sha256_read_data;
+               error_reg          = sha256_error;
+            end
+
+          SHA512_ADDR_PREFIX:
+            begin
+               sha512_cs          = cs_reg;
+               sha512_we          = we;
+               sha512_address     = address[9:2];
+               sha512_write_data  = write_reg;
+               read_reg           = sha512_read_data;
+               error_reg          = sha512_error;
+            end
+
+        // read operations
+          default:
+            begin
+               read_reg           = 32'h0;
+            end
+        endcase
+
+        if (cs && !we && address[1:0] == 2'b00)
+          begin
+             // read the first word
+//             address_reg = address;
+             read_data = read_reg[31:16];
+          end
+        else if (cs && !we && address[1:0] == 2'b10)
+          begin
+             // read the second word
+             // note this is done as two separate reads for convenience
+//             if (address[18:2] == address_reg[18:2])
+               begin
+                  read_data = read_reg[15:0];
+               end
+          end
+        else
+          begin
+             read_data = 16'hZZZZ;
+          end // else: !if(cs && !we && address[1:0] == 2'b10)
+     end // address_mux
+
+endmodule // coretest_hashes
+
+//======================================================================
+// EOF coretest_hashes.v
+//======================================================================
diff --git a/src/rtl/coretest_hashes_i2c.v b/src/rtl/coretest_hashes_i2c.v
new file mode 100644
index 0000000..fc2ccff
--- /dev/null
+++ b/src/rtl/coretest_hashes_i2c.v
@@ -0,0 +1,321 @@
+//======================================================================
+//
+// coretest_hashes.v
+// -----------------
+// Top level wrapper that creates the Cryptech coretest system.
+// The wrapper contains instances of external interface, coretest
+// and the core to be tested. And if more than one core is
+// present the wrapper also includes address and data muxes.
+//
+//
+// Author: Joachim Strombergson
+// Copyright (c) 2014, SUNET
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or 
+// without modification, are permitted provided that the following 
+// conditions are met: 
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer. 
+// 
+// 2. 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. 
+// 
+// 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 OWNER 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 coretest_hashes(
+                       input wire          clk,
+                       input wire          reset_n,
+                       
+                       // External interface.
+                       input wire          SCL,
+                       input wire          SDA,
+                       output wire         SDA_pd,
+                       
+                       output wire [7 : 0] debug
+                      );
+
+  
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter I2C_DEVICE_ADDR    = 7'h0f;
+  parameter I2C_ADDR_PREFIX    = 8'h00;
+  parameter SHA1_ADDR_PREFIX   = 8'h10;
+  parameter SHA256_ADDR_PREFIX = 8'h20;
+  parameter SHA512_ADDR_PREFIX = 8'h30;
+  
+  
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  // Coretest connections.
+  wire          coretest_reset_n;
+  wire          coretest_cs;
+  wire          coretest_we;
+  wire [15 : 0] coretest_address;
+  wire [31 : 0] coretest_write_data;
+  reg [31 : 0]  coretest_read_data;
+  reg           coretest_error;
+
+  // i2c connections
+  wire [6:0] 	i2c_device_addr;
+  wire          i2c_rxd_syn;
+  wire [7 : 0]  i2c_rxd_data;
+  wire          i2c_rxd_ack;
+  wire          i2c_txd_syn;
+  wire [7 : 0]  i2c_txd_data;
+  wire          i2c_txd_ack;
+  reg           i2c_cs;
+  reg           i2c_we;
+  reg [7 : 0]   i2c_address;
+  reg [31 : 0]  i2c_write_data;
+  wire [31 : 0] i2c_read_data;
+  wire          i2c_error;
+  wire [7 : 0]  i2c_debug;
+
+  // sha1 connections.
+  reg           sha1_cs;
+  reg           sha1_we;
+  reg [7 : 0]   sha1_address;
+  reg [31 : 0]  sha1_write_data;
+  wire [31 : 0] sha1_read_data;
+  wire          sha1_error;
+  wire [7 : 0]  sha1_debug;
+
+  // sha256 connections.
+  reg           sha256_cs;
+  reg           sha256_we;
+  reg [7 : 0]   sha256_address;
+  reg [31 : 0]  sha256_write_data;
+  wire [31 : 0] sha256_read_data;
+  wire          sha256_error;
+  wire [7 : 0]  sha256_debug;
+
+  // sha512 connections.
+  reg           sha512_cs;
+  reg           sha512_we;
+  reg [7 : 0]   sha512_address;
+  reg [31 : 0]  sha512_write_data;
+  wire [31 : 0] sha512_read_data;
+  wire          sha512_error;
+  wire [7 : 0]  sha512_debug;
+  
+  
+  //----------------------------------------------------------------
+  // Concurrent assignment.
+  //----------------------------------------------------------------
+  assign debug = i2c_debug;
+  
+  
+  //----------------------------------------------------------------
+  // Core instantiations.
+  //----------------------------------------------------------------
+  coretest coretest(
+                    .clk(clk),
+                    .reset_n(reset_n),
+                         
+                    .rx_syn(i2c_rxd_syn),
+                    .rx_data(i2c_rxd_data),
+                    .rx_ack(i2c_rxd_ack),
+                    
+                    .tx_syn(i2c_txd_syn),
+                    .tx_data(i2c_txd_data),
+                    .tx_ack(i2c_txd_ack),
+                    
+                    // Interface to the core being tested.
+                    .core_reset_n(coretest_reset_n),
+                    .core_cs(coretest_cs),
+                    .core_we(coretest_we),
+                    .core_address(coretest_address),
+                    .core_write_data(coretest_write_data),
+                    .core_read_data(coretest_read_data),
+                    .core_error(coretest_error)
+                   );
+
+
+  i2c i2c(
+          .clk(clk),
+          .reset_n(!reset_n),	// active high
+          
+          .SCL(SCL),
+          .SDA(SDA),
+          .SDA_pd(SDA_pd),
+          .i2c_device_addr(i2c_device_addr),
+
+          .rxd_syn(i2c_rxd_syn),
+          .rxd_data(i2c_rxd_data),
+          .rxd_ack(i2c_rxd_ack),
+
+          .txd_syn(i2c_txd_syn),
+          .txd_data(i2c_txd_data),
+          .txd_ack(i2c_txd_ack),
+     
+          .cs(i2c_cs),
+          .we(i2c_we),
+          .address(i2c_address),
+          .write_data(i2c_write_data),
+          .read_data(i2c_read_data),
+          .error(i2c_error),
+
+          .debug(i2c_debug)
+          );
+
+  
+  sha1 sha1(
+            // Clock and reset.
+            .clk(clk),
+            .reset_n(reset_n),
+            
+            // Control.
+            .cs(sha1_cs),
+            .we(sha1_we),
+              
+            // Data ports.
+            .address(sha1_address),
+            .write_data(sha1_write_data),
+            .read_data(sha1_read_data),
+            .error(sha1_error)
+           );
+
+  
+  sha256 sha256(
+                // Clock and reset.
+                .clk(clk),
+                .reset_n(reset_n),
+                
+                // Control.
+                .cs(sha256_cs),
+                .we(sha256_we),
+              
+                // Data ports.
+                .address(sha256_address),
+                .write_data(sha256_write_data),
+                .read_data(sha256_read_data),
+                .error(sha256_error)
+               );
+
+  
+  sha512 sha512(
+                // Clock and reset.
+                .clk(clk),
+                .reset_n(reset_n),
+
+                // Control.
+                .cs(sha512_cs),
+                .we(sha512_we),
+
+                // Data ports.
+                .address(sha512_address),
+                .write_data(sha512_write_data),
+                .read_data(sha512_read_data),
+                .error(sha512_error)
+               );
+
+
+  //----------------------------------------------------------------
+  // address_mux
+  //
+  // Combinational data mux that handles addressing between
+  // cores using the 32-bit memory like interface.
+  //----------------------------------------------------------------
+  always @*
+    begin : address_mux
+      // Default assignments.
+      coretest_read_data = 32'h00000000;
+      coretest_error     = 0;
+
+      i2c_cs            = 0;
+      i2c_we            = 0;
+      i2c_address       = 8'h00;
+      i2c_write_data    = 32'h00000000;
+
+      sha1_cs            = 0;
+      sha1_we            = 0;
+      sha1_address       = 8'h00;
+      sha1_write_data    = 32'h00000000;
+
+      sha256_cs          = 0;
+      sha256_we          = 0;
+      sha256_address     = 8'h00;
+      sha256_write_data  = 32'h00000000;
+
+      sha512_cs          = 0;
+      sha512_we          = 0;
+      sha512_address     = 8'h00;
+      sha512_write_data  = 32'h00000000;
+
+
+      if (i2c_device_addr == I2C_DEVICE_ADDR)
+      case (coretest_address[15 : 8])
+        I2C_ADDR_PREFIX:
+          begin
+            i2c_cs             = coretest_cs;
+            i2c_we             = coretest_we;
+            i2c_address        = coretest_address[7 : 0];
+            i2c_write_data     = coretest_write_data;
+            coretest_read_data = i2c_read_data;
+            coretest_error     = i2c_error;
+          end
+
+        
+        SHA1_ADDR_PREFIX:
+          begin
+            sha1_cs            = coretest_cs;
+            sha1_we            = coretest_we;
+            sha1_address       = coretest_address[7 : 0];
+            sha1_write_data    = coretest_write_data;
+            coretest_read_data = sha1_read_data;
+            coretest_error     = sha1_error;
+          end
+
+        
+        SHA256_ADDR_PREFIX:
+          begin
+            sha256_cs          = coretest_cs;
+            sha256_we          = coretest_we;
+            sha256_address     = coretest_address[7 : 0];
+            sha256_write_data  = coretest_write_data;
+            coretest_read_data = sha256_read_data;
+            coretest_error     = sha256_error;
+          end
+
+
+        SHA512_ADDR_PREFIX:
+          begin
+            sha512_cs          = coretest_cs;
+            sha512_we          = coretest_we;
+            sha512_address     = coretest_address[7 : 0];
+            sha512_write_data  = coretest_write_data;
+            coretest_read_data = sha512_read_data;
+            coretest_error     = sha512_error;
+          end
+        
+        
+        default:
+          begin
+          end
+      endcase // case (coretest_address[15 : 8])
+    end // address_mux
+  
+endmodule // coretest_hashes
+
+//======================================================================
+// EOF coretest_hashes.v
+//======================================================================
diff --git a/src/rtl/coretest_hashes_i2c_simple.v b/src/rtl/coretest_hashes_i2c_simple.v
new file mode 100644
index 0000000..09eac57
--- /dev/null
+++ b/src/rtl/coretest_hashes_i2c_simple.v
@@ -0,0 +1,1514 @@
+//======================================================================
+//
+// coretest_hashes.v
+// -----------------
+// Top level wrapper that creates the Cryptech coretest system.
+// The wrapper contains instances of the external interface and the
+// cores to be tested, as well as address and data muxes.
+//
+//
+// Authors: Joachim Strombergson, Paul Selkirk
+// Copyright (c) 2014, SUNET
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or 
+// without modification, are permitted provided that the following 
+// conditions are met: 
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer. 
+// 
+// 2. 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. 
+// 
+// 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 OWNER 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 coretest_hashes(
+                       input wire          clk,
+                       input wire          reset_n,
+
+                       // External interface.
+                       input wire          SCL,
+                       input wire          SDA,
+                       output wire         SDA_pd,
+
+                       output wire [7 : 0] debug
+                      );
+
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter I2C_SHA1_ADDR       = 7'h1e;
+  parameter I2C_SHA256_ADDR     = 7'h1f;
+  parameter I2C_SHA512_224_ADDR = 7'h20;
+  parameter I2C_SHA512_256_ADDR = 7'h21;
+  parameter I2C_SHA384_ADDR     = 7'h22;
+  parameter I2C_SHA512_ADDR     = 7'h23;
+
+  parameter MODE_SHA_512_224    = 2'h0;
+  parameter MODE_SHA_512_256    = 2'h1;
+  parameter MODE_SHA_384        = 2'h2;
+  parameter MODE_SHA_512        = 2'h3;
+
+
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  // i2c connections
+  wire          i2c_rxd_syn;
+  wire [7 : 0]  i2c_rxd_data;
+  reg           i2c_rxd_ack;
+  reg           i2c_txd_syn;
+  reg  [7 : 0]  i2c_txd_data;
+  wire          i2c_txd_ack;
+  wire [6 : 0]  i2c_device_addr;
+
+  // sha1 connections.
+  reg           sha1_rxd_syn;
+  reg  [7 : 0]  sha1_rxd_data;
+  wire          sha1_rxd_ack;
+  wire          sha1_txd_syn;
+  wire [7 : 0]  sha1_txd_data;
+  reg           sha1_txd_ack;
+
+  // sha256 connections.
+  reg           sha256_rxd_syn;
+  reg  [7 : 0]  sha256_rxd_data;
+  wire          sha256_rxd_ack;
+  wire          sha256_txd_syn;
+  wire [7 : 0]  sha256_txd_data;
+  reg           sha256_txd_ack;
+
+  // sha512 connections.
+  reg  [1 : 0]  sha512_mode;
+  reg           sha512_rxd_syn;
+  reg  [7 : 0]  sha512_rxd_data;
+  wire          sha512_rxd_ack;
+  wire          sha512_txd_syn;
+  wire [7 : 0]  sha512_txd_data;
+  reg           sha512_txd_ack;
+
+
+  //----------------------------------------------------------------
+  // Core instantiations.
+  //----------------------------------------------------------------
+  i2c_core i2c(
+               .clk(clk),
+               .reset(!reset_n), // active high
+
+               .SCL(SCL),
+               .SDA(SDA),
+               .SDA_pd(SDA_pd),
+               .i2c_device_addr(i2c_device_addr),
+
+               .rxd_syn(i2c_rxd_syn),
+               .rxd_data(i2c_rxd_data),
+               .rxd_ack(i2c_rxd_ack),
+
+               .txd_syn(i2c_txd_syn),
+               .txd_data(i2c_txd_data),
+               .txd_ack(i2c_txd_ack)
+               );
+
+  sha1 sha1(
+            .clk(clk),
+            .reset_n(reset_n),
+
+            .rx_syn(sha1_rxd_syn),
+            .rx_data(sha1_rxd_data),
+            .rx_ack(sha1_rxd_ack),
+
+            .tx_syn(sha1_txd_syn),
+            .tx_data(sha1_txd_data),
+            .tx_ack(sha1_txd_ack)
+           );
+
+  sha256 sha256(
+                .clk(clk),
+                .reset_n(reset_n),
+
+                .rx_syn(sha256_rxd_syn),
+                .rx_data(sha256_rxd_data),
+                .rx_ack(sha256_rxd_ack),
+
+                .tx_syn(sha256_txd_syn),
+                .tx_data(sha256_txd_data),
+                .tx_ack(sha256_txd_ack)
+                );
+
+  sha512 sha512(
+                .clk(clk),
+                .reset_n(reset_n),
+
+                .mode(sha512_mode),
+
+                .rx_syn(sha512_rxd_syn),
+                .rx_data(sha512_rxd_data),
+                .rx_ack(sha512_rxd_ack),
+
+                .tx_syn(sha512_txd_syn),
+                .tx_data(sha512_txd_data),
+                .tx_ack(sha512_txd_ack)
+                );
+
+  //----------------------------------------------------------------
+  // address_mux
+  //
+  // Combinational data mux that handles addressing between
+  // cores using the I2C device address.
+  //----------------------------------------------------------------
+  always @*
+    begin : address_mux
+      // Default assignments.
+      i2c_rxd_ack     = 0;
+      i2c_txd_syn     = 0;
+      i2c_txd_data    = 8'h00;
+
+      sha1_rxd_syn    = 0;
+      sha1_rxd_data   = 8'h00;
+      sha1_txd_ack    = 0;
+
+      sha256_rxd_syn  = 0;
+      sha256_rxd_data = 8'h00;
+      sha256_txd_ack  = 0;
+
+      sha512_rxd_syn  = 0;
+      sha512_rxd_data = 8'h00;
+      sha512_txd_ack  = 0;
+      sha512_mode     = 2'b00;
+
+      case (i2c_device_addr)
+        I2C_SHA1_ADDR:
+          begin
+            sha1_rxd_syn    = i2c_rxd_syn;
+            sha1_rxd_data   = i2c_rxd_data;
+            i2c_rxd_ack     = sha1_rxd_ack;
+            i2c_txd_syn     = sha1_txd_syn;
+            i2c_txd_data    = sha1_txd_data;
+            sha1_txd_ack    = i2c_txd_ack;
+          end
+
+        I2C_SHA256_ADDR:
+          begin
+            sha256_rxd_syn  = i2c_rxd_syn;
+            sha256_rxd_data = i2c_rxd_data;
+            i2c_rxd_ack     = sha256_rxd_ack;
+            i2c_txd_syn     = sha256_txd_syn;
+            i2c_txd_data    = sha256_txd_data;
+            sha256_txd_ack  = i2c_txd_ack;
+          end
+
+        I2C_SHA512_224_ADDR:
+          begin
+            sha512_rxd_syn  = i2c_rxd_syn;
+            sha512_rxd_data = i2c_rxd_data;
+            i2c_rxd_ack     = sha512_rxd_ack;
+            i2c_txd_syn     = sha512_txd_syn;
+            i2c_txd_data    = sha512_txd_data;
+            sha512_txd_ack  = i2c_txd_ack;
+            sha512_mode     = MODE_SHA_512_224;
+          end
+
+        I2C_SHA512_256_ADDR:
+          begin
+            sha512_rxd_syn  = i2c_rxd_syn;
+            sha512_rxd_data = i2c_rxd_data;
+            i2c_rxd_ack     = sha512_rxd_ack;
+            i2c_txd_syn     = sha512_txd_syn;
+            i2c_txd_data    = sha512_txd_data;
+            sha512_txd_ack  = i2c_txd_ack;
+            sha512_mode     = MODE_SHA_512_256;
+          end
+
+        I2C_SHA384_ADDR:
+          begin
+            sha512_rxd_syn  = i2c_rxd_syn;
+            sha512_rxd_data = i2c_rxd_data;
+            i2c_rxd_ack     = sha512_rxd_ack;
+            i2c_txd_syn     = sha512_txd_syn;
+            i2c_txd_data    = sha512_txd_data;
+            sha512_txd_ack  = i2c_txd_ack;
+            sha512_mode     = MODE_SHA_384;
+          end
+
+        I2C_SHA512_ADDR:
+          begin
+            sha512_rxd_syn  = i2c_rxd_syn;
+            sha512_rxd_data = i2c_rxd_data;
+            i2c_rxd_ack     = sha512_rxd_ack;
+            i2c_txd_syn     = sha512_txd_syn;
+            i2c_txd_data    = sha512_txd_data;
+            sha512_txd_ack  = i2c_txd_ack;
+            sha512_mode     = MODE_SHA_512;
+          end
+
+        default:
+          begin
+          end
+      endcase // case (i2c_device_addr)
+    end // address_mux
+
+endmodule // coretest_hashes
+
+//======================================================================
+// sha1.v
+// ------
+// Top level wrapper for the SHA-1 hash function providing
+// a simple write()/read() interface with 8 bit data access.
+//======================================================================
+module sha1(
+            // Clock and reset.
+            input wire          clk,
+            input wire          reset_n,
+
+            // Interface to communication core
+            input wire          rx_syn,
+            input wire [7 : 0]  rx_data,
+            output wire         rx_ack,
+
+            output wire         tx_syn,
+            output wire [7 : 0] tx_data,
+            input wire          tx_ack
+            );
+
+   //----------------------------------------------------------------
+   // Internal constant and parameter definitions.
+   //----------------------------------------------------------------
+   parameter RX_INIT = 0;
+   parameter RX_SYN  = 1;
+   parameter RX_ACK  = 2;
+   parameter RX_WAIT = 3;
+
+   parameter TX_INIT = 0;
+   parameter TX_SYN  = 1;
+   parameter TX_ACK  = 2;
+
+   parameter BLOCK_BITS   = 512;
+   parameter BLOCK_BYTES  = BLOCK_BITS / 8;
+   parameter DIGEST_BITS  = 160;
+   parameter DIGEST_BYTES = DIGEST_BITS / 8;
+
+   //----------------------------------------------------------------
+   // Registers including update variables and write enable.
+   //----------------------------------------------------------------
+   reg                          rx_ack_reg;
+   reg                          rx_ack_new;
+   reg                          rx_ack_we;
+
+   reg [6 : 0]                  rx_ptr_reg;
+   reg [7 : 0]                  rx_ptr_new;
+   reg                          rx_ptr_we;
+
+   reg [1 : 0]                  rx_ctrl_reg;
+   reg [1 : 0]                  rx_ctrl_new;
+   reg                          rx_ctrl_we;
+
+   reg [7 : 0]                  block_reg [0 : BLOCK_BYTES - 1];
+   reg [7 : 0]                  block_new;
+   reg                          block_we;
+
+   reg                          init_reg;
+   reg                          init_new;
+   reg                          init_we;
+
+   reg                          next_reg;
+   reg                          next_new;
+   reg                          next_we;
+
+   reg                          initnext_reg;
+   reg                          initnext_new;
+   reg                          initnext_we;
+
+   reg                          tx_syn_reg;
+   reg                          tx_syn_new;
+   reg                          tx_syn_we;
+
+   reg [6 : 0]                  tx_ptr_reg;
+   reg [7 : 0]                  tx_ptr_new;
+   reg                          tx_ptr_we;
+
+   reg [1 : 0]                  tx_ctrl_reg;
+   reg [1 : 0]                  tx_ctrl_new;
+   reg                          tx_ctrl_we;
+
+   reg                          tx_active_reg;
+   reg                          tx_active_new;
+   reg                          tx_active_we;
+
+   wire [7 : 0]                 digest_reg [0 : DIGEST_BYTES - 1];
+
+   //----------------------------------------------------------------
+   // Wires.
+   //----------------------------------------------------------------
+   wire                         core_init;
+   wire                         core_next;
+   wire                         core_ready;
+   wire [BLOCK_BITS - 1 : 0]    core_block;
+   wire [DIGEST_BITS - 1 : 0]   core_digest;
+   wire                         core_digest_valid;
+
+   //----------------------------------------------------------------
+   // Concurrent connectivity for ports etc.
+   //----------------------------------------------------------------
+   assign core_init  = init_reg;
+   assign core_next  = next_reg;
+   assign core_block = {block_reg[0], block_reg[1], block_reg[2], block_reg[3],
+                        block_reg[4], block_reg[5], block_reg[6], block_reg[7],
+                        block_reg[8], block_reg[9], block_reg[10], block_reg[11],
+                        block_reg[12], block_reg[13], block_reg[14], block_reg[15],
+                        block_reg[16], block_reg[17], block_reg[18], block_reg[19],
+                        block_reg[20], block_reg[21], block_reg[22], block_reg[23],
+                        block_reg[24], block_reg[25], block_reg[26], block_reg[27],
+                        block_reg[28], block_reg[29], block_reg[30], block_reg[31],
+                        block_reg[32], block_reg[33], block_reg[34], block_reg[35],
+                        block_reg[36], block_reg[37], block_reg[38], block_reg[39],
+                        block_reg[40], block_reg[41], block_reg[42], block_reg[43],
+                        block_reg[44], block_reg[45], block_reg[46], block_reg[47],
+                        block_reg[48], block_reg[49], block_reg[50], block_reg[51],
+                        block_reg[52], block_reg[53], block_reg[54], block_reg[55],
+                        block_reg[56], block_reg[57], block_reg[58], block_reg[59],
+                        block_reg[60], block_reg[61], block_reg[62], block_reg[63]};
+
+   assign rx_ack     = rx_ack_reg;
+   assign tx_syn     = tx_syn_reg;
+   assign tx_data    = digest_reg[tx_ptr_reg];
+
+   assign digest_reg[0]  = core_digest[159 : 152];
+   assign digest_reg[1]  = core_digest[151 : 144];
+   assign digest_reg[2]  = core_digest[143 : 136];
+   assign digest_reg[3]  = core_digest[135 : 128];
+   assign digest_reg[4]  = core_digest[127 : 120];
+   assign digest_reg[5]  = core_digest[119 : 112];
+   assign digest_reg[6]  = core_digest[111 : 104];
+   assign digest_reg[7]  = core_digest[103 : 96];
+   assign digest_reg[8]  = core_digest[95 : 88];
+   assign digest_reg[9]  = core_digest[87 : 80];
+   assign digest_reg[10] = core_digest[79 : 72];
+   assign digest_reg[11] = core_digest[71 : 64];
+   assign digest_reg[12] = core_digest[63 : 56];
+   assign digest_reg[13] = core_digest[55 : 48];
+   assign digest_reg[14] = core_digest[47 : 40];
+   assign digest_reg[15] = core_digest[39 : 32];
+   assign digest_reg[16] = core_digest[31 : 24];
+   assign digest_reg[17] = core_digest[23 : 16];
+   assign digest_reg[18] = core_digest[15 : 8];
+   assign digest_reg[19] = core_digest[7 : 0];
+
+   //----------------------------------------------------------------
+   // core instantiation.
+   //----------------------------------------------------------------
+   sha1_core core(
+                  .clk(clk),
+                  .reset_n(reset_n),
+
+                  .init(core_init),
+                  .next(core_next),
+
+                  .block(core_block),
+
+                  .ready(core_ready),
+
+                  .digest(core_digest),
+                  .digest_valid(core_digest_valid)
+                  );
+
+   //----------------------------------------------------------------
+   // reg_update
+   // Update functionality for all registers in the core.
+   // All registers are positive edge triggered with synchronous
+   // active low reset. All registers have write enable.
+   //----------------------------------------------------------------
+
+   always @ (posedge clk)
+     begin: reg_update
+        if (!reset_n)
+          begin: reset_reg
+             reg[7:0] i;
+             for (i = 0; i < BLOCK_BYTES; i = i + 1)
+               block_reg[i] <= 0;
+             rx_ack_reg   <= 0;
+             rx_ptr_reg   <= 0;
+             rx_ctrl_reg  <= RX_INIT;
+             tx_syn_reg   <= 0;
+             tx_ptr_reg   <= 0;
+             tx_ctrl_reg  <= TX_INIT;
+             initnext_reg <= 0;
+          end
+        else
+          begin
+             if (rx_ack_we)
+               begin
+                  rx_ack_reg <= rx_ack_new;
+               end
+
+             if (rx_ptr_we)
+               begin
+                  rx_ptr_reg <= rx_ptr_new[6:0];
+               end
+
+             if (rx_ctrl_we)
+               begin
+                  rx_ctrl_reg <= rx_ctrl_new;
+               end
+
+             if (block_we)
+               begin
+                  block_reg[rx_ptr_reg] <= block_new;
+               end
+
+             if (init_we)
+               begin
+                  init_reg <= init_new;
+               end
+
+             if (next_we)
+               begin
+                  next_reg <= next_new;
+               end
+
+             if (initnext_we)
+               begin
+                  initnext_reg <= initnext_new;
+               end
+
+             if (tx_syn_we)
+               begin
+                  tx_syn_reg <= tx_syn_new;
+               end
+
+             if (tx_ptr_we)
+               begin
+                  tx_ptr_reg <= tx_ptr_new[6:0];
+               end
+
+             if (tx_ctrl_we)
+               begin
+                  tx_ctrl_reg <= tx_ctrl_new;
+               end
+
+             if (tx_active_we)
+               begin
+                  tx_active_reg <= tx_active_new;
+               end
+          end
+     end // reg_update
+
+   //----------------------------------------------------------------
+   // rx_engine
+   //----------------------------------------------------------------
+   always @*
+     begin: rx_engine
+        rx_ack_new = 0;
+        rx_ack_we = 0;
+        rx_ptr_new = 0;
+        rx_ptr_we = 0;
+        rx_ctrl_new = 0;
+        rx_ctrl_we = 0;
+        block_new = 0;
+        block_we = 0;
+        init_new = 0;
+        init_we = 0;
+        next_new = 0;
+        next_we = 0;
+        initnext_new = 0;
+        initnext_we = 0;
+
+        if (tx_active_reg)
+          begin
+             initnext_we = 1;
+          end
+
+        case (rx_ctrl_reg)
+          RX_INIT:
+            if (core_ready)
+              begin
+                 rx_ctrl_new = RX_SYN;
+                 rx_ctrl_we = 1;
+              end
+          RX_SYN:
+            if (rx_syn)
+              begin
+                 rx_ack_new = 1;
+                 rx_ack_we = 1;
+                 block_new = rx_data;
+                 block_we = 1;
+                 rx_ctrl_new = RX_ACK;
+                 rx_ctrl_we = 1;
+              end
+          RX_ACK:
+            if (!rx_syn)
+              begin
+                 rx_ack_new = 0;
+                 rx_ack_we = 1;
+                 rx_ptr_new = rx_ptr_reg + 1;
+                 rx_ptr_we = 1;
+                 rx_ctrl_new = RX_SYN;
+                 rx_ctrl_we = 1;
+                 if (rx_ptr_new == BLOCK_BYTES)
+                   begin
+                      rx_ptr_new = 0;
+                      rx_ctrl_new = RX_WAIT;
+                      if (initnext_reg == 0)
+                        begin
+                           init_new = 1;
+                           init_we = 1;
+                           initnext_new = 1;
+                           initnext_we = 1;
+                        end
+                      else
+                        begin
+                           next_new = 1;
+                           next_we = 1;
+                        end
+                   end
+              end
+          RX_WAIT:
+            if (!core_ready)
+              begin
+                 init_new = 0;
+                 init_we = 1;
+                 next_new = 0;
+                 next_we = 1;
+                 rx_ctrl_new = RX_INIT;
+                 rx_ctrl_we = 1;
+              end
+        endcase // case (rx_ctrl_reg)
+     end // rx_engine
+
+   //----------------------------------------------------------------
+   // tx_engine
+   //----------------------------------------------------------------
+   always @*
+     begin: tx_engine
+        tx_syn_new = 0;
+        tx_syn_we = 0;
+        tx_ptr_new = 0;
+        tx_ptr_we = 0;
+        tx_ctrl_new = 0;
+        tx_ctrl_we = 0;
+        tx_active_new = 0;
+        tx_active_we = 0;
+
+        case (tx_ctrl_reg)
+          TX_INIT:
+            if (core_digest_valid)
+              begin
+                 tx_ctrl_new = TX_SYN;
+                 tx_ctrl_we = 1;
+              end
+          TX_SYN:
+            begin
+               tx_syn_new = 1;
+               tx_syn_we = 1;
+               tx_ctrl_new = TX_ACK;
+               tx_ctrl_we = 1;
+            end
+          TX_ACK:
+            if (!core_digest_valid)
+              begin
+                 tx_syn_new = 0;
+                 tx_syn_we = 1;
+                 tx_ptr_new = 0;
+                 tx_ptr_we = 1;
+                 tx_ctrl_new = TX_INIT;
+                 tx_ctrl_we = 1;
+              end
+            else if (tx_ack)
+              begin
+                 tx_active_new = 1;
+                 tx_active_we = 1;
+                 tx_syn_new = 0;
+                 tx_syn_we = 1;
+                 tx_ptr_new = tx_ptr_reg + 1;
+                 tx_ptr_we = 1;
+                 tx_ctrl_new = TX_SYN;
+                 tx_ctrl_we = 1;
+                 if (tx_ptr_new == DIGEST_BYTES)
+                   begin
+                      tx_active_new = 0;
+                      tx_ptr_new = 0;
+                      tx_ctrl_new = TX_INIT;
+                   end
+              end
+        endcase // case (tx_ctrl_reg)
+     end // tx_engine
+
+endmodule // sha1
+
+//======================================================================
+// sha256.v
+// --------
+// Top level wrapper for the SHA-256 hash function providing
+// a simple write()/read() interface with 8 bit data access.
+//======================================================================
+module sha256(
+              // Clock and reset.
+              input wire          clk,
+              input wire          reset_n,
+
+              // Interface to communication core
+              input wire          rx_syn,
+              input wire [7 : 0]  rx_data,
+              output wire         rx_ack,
+
+              output wire         tx_syn,
+              output wire [7 : 0] tx_data,
+              input wire          tx_ack
+              );
+
+   //----------------------------------------------------------------
+   // Internal constant and parameter definitions.
+   //----------------------------------------------------------------
+   parameter RX_INIT = 0;
+   parameter RX_SYN  = 1;
+   parameter RX_ACK  = 2;
+   parameter RX_WAIT = 3;
+
+   parameter TX_INIT = 0;
+   parameter TX_SYN  = 1;
+   parameter TX_ACK  = 2;
+
+   parameter BLOCK_BITS   = 512;
+   parameter BLOCK_BYTES  = BLOCK_BITS / 8;
+   parameter DIGEST_BITS  = 256;
+   parameter DIGEST_BYTES = DIGEST_BITS / 8;
+
+   //----------------------------------------------------------------
+   // Registers including update variables and write enable.
+   //----------------------------------------------------------------
+   reg                          rx_ack_reg;
+   reg                          rx_ack_new;
+   reg                          rx_ack_we;
+
+   reg [6 : 0]                  rx_ptr_reg;
+   reg [7 : 0]                  rx_ptr_new;
+   reg                          rx_ptr_we;
+
+   reg [1 : 0]                  rx_ctrl_reg;
+   reg [1 : 0]                  rx_ctrl_new;
+   reg                          rx_ctrl_we;
+
+   reg [7 : 0]                  block_reg [0 : BLOCK_BYTES - 1];
+   reg [7 : 0]                  block_new;
+   reg                          block_we;
+
+   reg                          init_reg;
+   reg                          init_new;
+   reg                          init_we;
+
+   reg                          next_reg;
+   reg                          next_new;
+   reg                          next_we;
+
+   reg                          initnext_reg;
+   reg                          initnext_new;
+   reg                          initnext_we;
+
+   reg                          tx_syn_reg;
+   reg                          tx_syn_new;
+   reg                          tx_syn_we;
+
+   reg [6 : 0]                  tx_ptr_reg;
+   reg [7 : 0]                  tx_ptr_new;
+   reg                          tx_ptr_we;
+
+   reg [1 : 0]                  tx_ctrl_reg;
+   reg [1 : 0]                  tx_ctrl_new;
+   reg                          tx_ctrl_we;
+
+   reg                          tx_active_reg;
+   reg                          tx_active_new;
+   reg                          tx_active_we;
+
+   wire [7 : 0]                 digest_reg [0 : DIGEST_BYTES - 1];
+
+   //----------------------------------------------------------------
+   // Wires.
+   //----------------------------------------------------------------
+   wire                         core_init;
+   wire                         core_next;
+   wire                         core_ready;
+   wire [BLOCK_BITS - 1 : 0]    core_block;
+   wire [DIGEST_BITS - 1 : 0]   core_digest;
+   wire                         core_digest_valid;
+
+   //----------------------------------------------------------------
+   // Concurrent connectivity for ports etc.
+   //----------------------------------------------------------------
+   assign core_init  = init_reg;
+   assign core_next  = next_reg;
+   assign core_block = {block_reg[0], block_reg[1], block_reg[2], block_reg[3],
+                        block_reg[4], block_reg[5], block_reg[6], block_reg[7],
+                        block_reg[8], block_reg[9], block_reg[10], block_reg[11],
+                        block_reg[12], block_reg[13], block_reg[14], block_reg[15],
+                        block_reg[16], block_reg[17], block_reg[18], block_reg[19],
+                        block_reg[20], block_reg[21], block_reg[22], block_reg[23],
+                        block_reg[24], block_reg[25], block_reg[26], block_reg[27],
+                        block_reg[28], block_reg[29], block_reg[30], block_reg[31],
+                        block_reg[32], block_reg[33], block_reg[34], block_reg[35],
+                        block_reg[36], block_reg[37], block_reg[38], block_reg[39],
+                        block_reg[40], block_reg[41], block_reg[42], block_reg[43],
+                        block_reg[44], block_reg[45], block_reg[46], block_reg[47],
+                        block_reg[48], block_reg[49], block_reg[50], block_reg[51],
+                        block_reg[52], block_reg[53], block_reg[54], block_reg[55],
+                        block_reg[56], block_reg[57], block_reg[58], block_reg[59],
+                        block_reg[60], block_reg[61], block_reg[62], block_reg[63]};
+
+   assign rx_ack     = rx_ack_reg;
+   assign tx_syn     = tx_syn_reg;
+   assign tx_data    = digest_reg[tx_ptr_reg];
+
+   assign digest_reg[0]  = core_digest[255 : 248];
+   assign digest_reg[1]  = core_digest[247 : 240];
+   assign digest_reg[2]  = core_digest[239 : 232];
+   assign digest_reg[3]  = core_digest[231 : 224];
+   assign digest_reg[4]  = core_digest[223 : 216];
+   assign digest_reg[5]  = core_digest[215 : 208];
+   assign digest_reg[6]  = core_digest[207 : 200];
+   assign digest_reg[7]  = core_digest[199 : 192];
+   assign digest_reg[8]  = core_digest[191 : 184];
+   assign digest_reg[9]  = core_digest[183 : 176];
+   assign digest_reg[10] = core_digest[175 : 168];
+   assign digest_reg[11] = core_digest[167 : 160];
+   assign digest_reg[12] = core_digest[159 : 152];
+   assign digest_reg[13] = core_digest[151 : 144];
+   assign digest_reg[14] = core_digest[143 : 136];
+   assign digest_reg[15] = core_digest[135 : 128];
+   assign digest_reg[16] = core_digest[127 : 120];
+   assign digest_reg[17] = core_digest[119 : 112];
+   assign digest_reg[18] = core_digest[111 : 104];
+   assign digest_reg[19] = core_digest[103 : 96];
+   assign digest_reg[20] = core_digest[95 : 88];
+   assign digest_reg[21] = core_digest[87 : 80];
+   assign digest_reg[22] = core_digest[79 : 72];
+   assign digest_reg[23] = core_digest[71 : 64];
+   assign digest_reg[24] = core_digest[63 : 56];
+   assign digest_reg[25] = core_digest[55 : 48];
+   assign digest_reg[26] = core_digest[47 : 40];
+   assign digest_reg[27] = core_digest[39 : 32];
+   assign digest_reg[28] = core_digest[31 : 24];
+   assign digest_reg[29] = core_digest[23 : 16];
+   assign digest_reg[30] = core_digest[15 : 8];
+   assign digest_reg[31] = core_digest[7 : 0];
+
+   //----------------------------------------------------------------
+   // core instantiation.
+   //----------------------------------------------------------------
+   sha256_core core(
+                    .clk(clk),
+                    .reset_n(reset_n),
+
+                    .init(core_init),
+                    .next(core_next),
+
+                    .block(core_block),
+
+                    .ready(core_ready),
+
+                    .digest(core_digest),
+                    .digest_valid(core_digest_valid)
+                    );
+
+   //----------------------------------------------------------------
+   // reg_update
+   // Update functionality for all registers in the core.
+   // All registers are positive edge triggered with synchronous
+   // active low reset. All registers have write enable.
+   //----------------------------------------------------------------
+
+   always @ (posedge clk)
+     begin: reg_update
+        if (!reset_n)
+          begin: reset_reg
+             reg[7:0] i;
+             for (i = 0; i < BLOCK_BYTES; i = i + 1)
+               block_reg[i] <= 0;
+             rx_ack_reg   <= 0;
+             rx_ptr_reg   <= 0;
+             rx_ctrl_reg  <= RX_INIT;
+             tx_syn_reg   <= 0;
+             tx_ptr_reg   <= 0;
+             tx_ctrl_reg  <= TX_INIT;
+             initnext_reg <= 0;
+          end
+        else
+          begin
+             if (rx_ack_we)
+               begin
+                  rx_ack_reg <= rx_ack_new;
+               end
+
+             if (rx_ptr_we)
+               begin
+                  rx_ptr_reg <= rx_ptr_new[6:0];
+               end
+
+             if (rx_ctrl_we)
+               begin
+                  rx_ctrl_reg <= rx_ctrl_new;
+               end
+
+             if (block_we)
+               begin
+                  block_reg[rx_ptr_reg] <= block_new;
+               end
+
+             if (init_we)
+               begin
+                  init_reg <= init_new;
+               end
+
+             if (next_we)
+               begin
+                  next_reg <= next_new;
+               end
+
+             if (initnext_we)
+               begin
+                  initnext_reg <= initnext_new;
+               end
+
+             if (tx_syn_we)
+               begin
+                  tx_syn_reg <= tx_syn_new;
+               end
+
+             if (tx_ptr_we)
+               begin
+                  tx_ptr_reg <= tx_ptr_new[6:0];
+               end
+
+             if (tx_ctrl_we)
+               begin
+                  tx_ctrl_reg <= tx_ctrl_new;
+               end
+
+             if (tx_active_we)
+               begin
+                  tx_active_reg <= tx_active_new;
+               end
+          end
+     end // reg_update
+
+   //----------------------------------------------------------------
+   // rx_engine
+   //----------------------------------------------------------------
+   always @*
+     begin: rx_engine
+        rx_ack_new = 0;
+        rx_ack_we = 0;
+        rx_ptr_new = 0;
+        rx_ptr_we = 0;
+        rx_ctrl_new = 0;
+        rx_ctrl_we = 0;
+        block_new = 0;
+        block_we = 0;
+        init_new = 0;
+        init_we = 0;
+        next_new = 0;
+        next_we = 0;
+        initnext_new = 0;
+        initnext_we = 0;
+
+        if (tx_active_reg)
+          begin
+             initnext_we = 1;
+          end
+
+        case (rx_ctrl_reg)
+          RX_INIT:
+            if (core_ready)
+              begin
+                 rx_ctrl_new = RX_SYN;
+                 rx_ctrl_we = 1;
+              end
+          RX_SYN:
+            if (rx_syn)
+              begin
+                 rx_ack_new = 1;
+                 rx_ack_we = 1;
+                 block_new = rx_data;
+                 block_we = 1;
+                 rx_ctrl_new = RX_ACK;
+                 rx_ctrl_we = 1;
+              end
+          RX_ACK:
+            if (!rx_syn)
+              begin
+                 rx_ack_new = 0;
+                 rx_ack_we = 1;
+                 rx_ptr_new = rx_ptr_reg + 1;
+                 rx_ptr_we = 1;
+                 rx_ctrl_new = RX_SYN;
+                 rx_ctrl_we = 1;
+                 if (rx_ptr_new == BLOCK_BYTES)
+                   begin
+                      rx_ptr_new = 0;
+                      rx_ctrl_new = RX_WAIT;
+                      if (initnext_reg == 0)
+                        begin
+                           init_new = 1;
+                           init_we = 1;
+                           initnext_new = 1;
+                           initnext_we = 1;
+                        end
+                      else
+                        begin
+                           next_new = 1;
+                           next_we = 1;
+                        end
+                   end
+              end
+          RX_WAIT:
+            if (!core_ready)
+              begin
+                 init_new = 0;
+                 init_we = 1;
+                 next_new = 0;
+                 next_we = 1;
+                 rx_ctrl_new = RX_INIT;
+                 rx_ctrl_we = 1;
+              end
+        endcase // case (rx_ctrl_reg)
+     end // rx_engine
+
+   //----------------------------------------------------------------
+   // tx_engine
+   //----------------------------------------------------------------
+   always @*
+     begin: tx_engine
+        tx_syn_new = 0;
+        tx_syn_we = 0;
+        tx_ptr_new = 0;
+        tx_ptr_we = 0;
+        tx_ctrl_new = 0;
+        tx_ctrl_we = 0;
+        tx_active_new = 0;
+        tx_active_we = 0;
+
+        case (tx_ctrl_reg)
+          TX_INIT:
+            if (core_digest_valid)
+              begin
+                 tx_ctrl_new = TX_SYN;
+                 tx_ctrl_we = 1;
+              end
+          TX_SYN:
+            begin
+               tx_syn_new = 1;
+               tx_syn_we = 1;
+               tx_ctrl_new = TX_ACK;
+               tx_ctrl_we = 1;
+            end
+          TX_ACK:
+            if (!core_digest_valid)
+              begin
+                 tx_syn_new = 0;
+                 tx_syn_we = 1;
+                 tx_ptr_new = 0;
+                 tx_ptr_we = 1;
+                 tx_ctrl_new = TX_INIT;
+                 tx_ctrl_we = 1;
+              end
+            else if (tx_ack)
+              begin
+                 tx_active_new = 1;
+                 tx_active_we = 1;
+                 tx_syn_new = 0;
+                 tx_syn_we = 1;
+                 tx_ptr_new = tx_ptr_reg + 1;
+                 tx_ptr_we = 1;
+                 tx_ctrl_new = TX_SYN;
+                 tx_ctrl_we = 1;
+                 if (tx_ptr_new == DIGEST_BYTES)
+                   begin
+                      tx_active_new = 0;
+                      tx_ptr_new = 0;
+                      tx_ctrl_new = TX_INIT;
+                   end
+              end
+        endcase // case (tx_ctrl_reg)
+     end // tx_engine
+
+endmodule // sha256
+
+//======================================================================
+// sha512.v
+// --------
+// Top level wrapper for the SHA-512 hash function providing
+// a simple write()/read() interface with 8 bit data access.
+//======================================================================
+module sha512(
+              // Clock and reset.
+              input wire          clk,
+              input wire          reset_n,
+
+              // SHA-512 mode
+              input wire [1 : 0]  mode,
+
+              // Interface to communication core
+              input wire          rx_syn,
+              input wire [7 : 0]  rx_data,
+              output wire         rx_ack,
+
+              output wire         tx_syn,
+              output wire [7 : 0] tx_data,
+              input wire          tx_ack
+              );
+
+   //----------------------------------------------------------------
+   // Internal constant and parameter definitions.
+   //----------------------------------------------------------------
+   parameter RX_INIT = 0;
+   parameter RX_SYN  = 1;
+   parameter RX_ACK  = 2;
+   parameter RX_WAIT = 3;
+
+   parameter TX_INIT = 0;
+   parameter TX_SYN  = 1;
+   parameter TX_ACK  = 2;
+
+   parameter MODE_SHA_512_224   = 2'h0;
+   parameter MODE_SHA_512_256   = 2'h1;
+   parameter MODE_SHA_384       = 2'h2;
+   parameter MODE_SHA_512       = 2'h3;
+
+   parameter BLOCK_BITS           = 1024;
+   parameter BLOCK_BYTES          = BLOCK_BITS / 8;
+   parameter DIGEST_BITS          = 512;
+   parameter DIGEST_BYTES         = DIGEST_BITS / 8;
+   parameter DIGEST_BYTES_512_224 = 224 / 8;
+   parameter DIGEST_BYTES_512_256 = 256 / 8;
+   parameter DIGEST_BYTES_384     = 384 / 8;
+   parameter DIGEST_BYTES_512     = 512 / 8;
+
+   //----------------------------------------------------------------
+   // Registers including update variables and write enable.
+   //----------------------------------------------------------------
+   reg                          rx_ack_reg;
+   reg                          rx_ack_new;
+   reg                          rx_ack_we;
+
+   reg [6 : 0]                  rx_ptr_reg;
+   reg [7 : 0]                  rx_ptr_new;
+   reg                          rx_ptr_we;
+
+   reg [1 : 0]                  rx_ctrl_reg;
+   reg [1 : 0]                  rx_ctrl_new;
+   reg                          rx_ctrl_we;
+
+   reg [7 : 0]                  block_reg [0 : BLOCK_BYTES - 1];
+   reg [7 : 0]                  block_new;
+   reg                          block_we;
+
+   reg                          init_reg;
+   reg                          init_new;
+   reg                          init_we;
+
+   reg                          next_reg;
+   reg                          next_new;
+   reg                          next_we;
+
+   reg                          initnext_reg;
+   reg                          initnext_new;
+   reg                          initnext_we;
+
+   reg                          tx_syn_reg;
+   reg                          tx_syn_new;
+   reg                          tx_syn_we;
+
+   reg [6 : 0]                  tx_ptr_reg;
+   reg [7 : 0]                  tx_ptr_new;
+   reg                          tx_ptr_we;
+
+   reg [1 : 0]                  tx_ctrl_reg;
+   reg [1 : 0]                  tx_ctrl_new;
+   reg                          tx_ctrl_we;
+
+   reg                          tx_active_reg;
+   reg                          tx_active_new;
+   reg                          tx_active_we;
+
+   wire [7 : 0]                 digest_reg [0 : DIGEST_BYTES - 1];
+
+   //----------------------------------------------------------------
+   // Wires.
+   //----------------------------------------------------------------
+   wire                         core_init;
+   wire                         core_next;
+   wire                         core_ready;
+   wire [BLOCK_BITS - 1 : 0]    core_block;
+   wire [DIGEST_BITS - 1 : 0]   core_digest;
+   wire                         core_digest_valid;
+
+   //----------------------------------------------------------------
+   // Concurrent connectivity for ports etc.
+   //----------------------------------------------------------------
+   assign core_init  = init_reg;
+   assign core_next  = next_reg;
+   assign core_block = {block_reg[0], block_reg[1], block_reg[2], block_reg[3],
+                        block_reg[4], block_reg[5], block_reg[6], block_reg[7],
+                        block_reg[8], block_reg[9], block_reg[10], block_reg[11],
+                        block_reg[12], block_reg[13], block_reg[14], block_reg[15],
+                        block_reg[16], block_reg[17], block_reg[18], block_reg[19],
+                        block_reg[20], block_reg[21], block_reg[22], block_reg[23],
+                        block_reg[24], block_reg[25], block_reg[26], block_reg[27],
+                        block_reg[28], block_reg[29], block_reg[30], block_reg[31],
+                        block_reg[32], block_reg[33], block_reg[34], block_reg[35],
+                        block_reg[36], block_reg[37], block_reg[38], block_reg[39],
+                        block_reg[40], block_reg[41], block_reg[42], block_reg[43],
+                        block_reg[44], block_reg[45], block_reg[46], block_reg[47],
+                        block_reg[48], block_reg[49], block_reg[50], block_reg[51],
+                        block_reg[52], block_reg[53], block_reg[54], block_reg[55],
+                        block_reg[56], block_reg[57], block_reg[58], block_reg[59],
+                        block_reg[60], block_reg[61], block_reg[62], block_reg[63],
+                        block_reg[64], block_reg[65], block_reg[66], block_reg[67],
+                        block_reg[68], block_reg[69], block_reg[70], block_reg[71],
+                        block_reg[72], block_reg[73], block_reg[74], block_reg[75],
+                        block_reg[76], block_reg[77], block_reg[78], block_reg[79],
+                        block_reg[80], block_reg[81], block_reg[82], block_reg[83],
+                        block_reg[84], block_reg[85], block_reg[86], block_reg[87],
+                        block_reg[88], block_reg[89], block_reg[90], block_reg[91],
+                        block_reg[92], block_reg[93], block_reg[94], block_reg[95],
+                        block_reg[96], block_reg[97], block_reg[98], block_reg[99],
+                        block_reg[100], block_reg[101], block_reg[102], block_reg[103],
+                        block_reg[104], block_reg[105], block_reg[106], block_reg[107],
+                        block_reg[108], block_reg[109], block_reg[110], block_reg[111],
+                        block_reg[112], block_reg[113], block_reg[114], block_reg[115],
+                        block_reg[116], block_reg[117], block_reg[118], block_reg[119],
+                        block_reg[120], block_reg[121], block_reg[122], block_reg[123],
+                        block_reg[124], block_reg[125], block_reg[126], block_reg[127]};
+
+   assign rx_ack     = rx_ack_reg;
+   assign tx_syn     = tx_syn_reg;
+   assign tx_data    = digest_reg[tx_ptr_reg];
+
+   assign digest_reg[0]  = core_digest[511 : 504];
+   assign digest_reg[1]  = core_digest[503 : 496];
+   assign digest_reg[2]  = core_digest[495 : 488];
+   assign digest_reg[3]  = core_digest[487 : 480];
+   assign digest_reg[4]  = core_digest[479 : 472];
+   assign digest_reg[5]  = core_digest[471 : 464];
+   assign digest_reg[6]  = core_digest[463 : 456];
+   assign digest_reg[7]  = core_digest[455 : 448];
+   assign digest_reg[8]  = core_digest[447 : 440];
+   assign digest_reg[9]  = core_digest[439 : 432];
+   assign digest_reg[10] = core_digest[431 : 424];
+   assign digest_reg[11] = core_digest[423 : 416];
+   assign digest_reg[12] = core_digest[415 : 408];
+   assign digest_reg[13] = core_digest[407 : 400];
+   assign digest_reg[14] = core_digest[399 : 392];
+   assign digest_reg[15] = core_digest[391 : 384];
+   assign digest_reg[16] = core_digest[383 : 376];
+   assign digest_reg[17] = core_digest[375 : 368];
+   assign digest_reg[18] = core_digest[367 : 360];
+   assign digest_reg[19] = core_digest[359 : 352];
+   assign digest_reg[20] = core_digest[351 : 344];
+   assign digest_reg[21] = core_digest[343 : 336];
+   assign digest_reg[22] = core_digest[335 : 328];
+   assign digest_reg[23] = core_digest[327 : 320];
+   assign digest_reg[24] = core_digest[319 : 312];
+   assign digest_reg[25] = core_digest[311 : 304];
+   assign digest_reg[26] = core_digest[303 : 296];
+   assign digest_reg[27] = core_digest[295 : 288];
+   assign digest_reg[28] = core_digest[287 : 280];
+   assign digest_reg[29] = core_digest[279 : 272];
+   assign digest_reg[30] = core_digest[271 : 264];
+   assign digest_reg[31] = core_digest[263 : 256];
+   assign digest_reg[32] = core_digest[255 : 248];
+   assign digest_reg[33] = core_digest[247 : 240];
+   assign digest_reg[34] = core_digest[239 : 232];
+   assign digest_reg[35] = core_digest[231 : 224];
+   assign digest_reg[36] = core_digest[223 : 216];
+   assign digest_reg[37] = core_digest[215 : 208];
+   assign digest_reg[38] = core_digest[207 : 200];
+   assign digest_reg[39] = core_digest[199 : 192];
+   assign digest_reg[40] = core_digest[191 : 184];
+   assign digest_reg[41] = core_digest[183 : 176];
+   assign digest_reg[42] = core_digest[175 : 168];
+   assign digest_reg[43] = core_digest[167 : 160];
+   assign digest_reg[44] = core_digest[159 : 152];
+   assign digest_reg[45] = core_digest[151 : 144];
+   assign digest_reg[46] = core_digest[143 : 136];
+   assign digest_reg[47] = core_digest[135 : 128];
+   assign digest_reg[48] = core_digest[127 : 120];
+   assign digest_reg[49] = core_digest[119 : 112];
+   assign digest_reg[50] = core_digest[111 : 104];
+   assign digest_reg[51] = core_digest[103 : 96];
+   assign digest_reg[52] = core_digest[95 : 88];
+   assign digest_reg[53] = core_digest[87 : 80];
+   assign digest_reg[54] = core_digest[79 : 72];
+   assign digest_reg[55] = core_digest[71 : 64];
+   assign digest_reg[56] = core_digest[63 : 56];
+   assign digest_reg[57] = core_digest[55 : 48];
+   assign digest_reg[58] = core_digest[47 : 40];
+   assign digest_reg[59] = core_digest[39 : 32];
+   assign digest_reg[60] = core_digest[31 : 24];
+   assign digest_reg[61] = core_digest[23 : 16];
+   assign digest_reg[62] = core_digest[15 : 8];
+   assign digest_reg[63] = core_digest[7 : 0];
+
+   //----------------------------------------------------------------
+   // core instantiation.
+   //----------------------------------------------------------------
+   sha512_core core(
+                    .clk(clk),
+                    .reset_n(reset_n),
+
+                    .init(core_init),
+                    .next(core_next),
+                    .mode(mode),
+
+                    .block(core_block),
+
+                    .ready(core_ready),
+
+                    .digest(core_digest),
+                    .digest_valid(core_digest_valid)
+                    );
+
+   //----------------------------------------------------------------
+   // reg_update
+   // Update functionality for all registers in the core.
+   // All registers are positive edge triggered with synchronous
+   // active low reset. All registers have write enable.
+   //----------------------------------------------------------------
+
+   always @ (posedge clk)
+     begin: reg_update
+        if (!reset_n)
+          begin: reset_reg
+             reg[7:0] i;
+             for (i = 0; i < BLOCK_BYTES; i = i + 1)
+               block_reg[i] <= 0;
+             rx_ack_reg   <= 0;
+             rx_ptr_reg   <= 0;
+             rx_ctrl_reg  <= RX_INIT;
+             tx_syn_reg   <= 0;
+             tx_ptr_reg   <= 0;
+             tx_ctrl_reg  <= TX_INIT;
+             initnext_reg <= 0;
+          end
+        else
+          begin
+             if (rx_ack_we)
+               begin
+                  rx_ack_reg <= rx_ack_new;
+               end
+
+             if (rx_ptr_we)
+               begin
+                  rx_ptr_reg <= rx_ptr_new[6:0];
+               end
+
+             if (rx_ctrl_we)
+               begin
+                  rx_ctrl_reg <= rx_ctrl_new;
+               end
+
+             if (block_we)
+               begin
+                  block_reg[rx_ptr_reg] <= block_new;
+               end
+
+             if (init_we)
+               begin
+                  init_reg <= init_new;
+               end
+
+             if (next_we)
+               begin
+                  next_reg <= next_new;
+               end
+
+             if (initnext_we)
+               begin
+                  initnext_reg <= initnext_new;
+               end
+
+             if (tx_syn_we)
+               begin
+                  tx_syn_reg <= tx_syn_new;
+               end
+
+             if (tx_ptr_we)
+               begin
+                  tx_ptr_reg <= tx_ptr_new[6:0];
+               end
+
+             if (tx_ctrl_we)
+               begin
+                  tx_ctrl_reg <= tx_ctrl_new;
+               end
+
+             if (tx_active_we)
+               begin
+                  tx_active_reg <= tx_active_new;
+               end
+          end
+     end // reg_update
+
+   //----------------------------------------------------------------
+   // rx_engine
+   //----------------------------------------------------------------
+   always @*
+     begin: rx_engine
+        rx_ack_new = 0;
+        rx_ack_we = 0;
+        rx_ptr_new = 0;
+        rx_ptr_we = 0;
+        rx_ctrl_new = 0;
+        rx_ctrl_we = 0;
+        block_new = 0;
+        block_we = 0;
+        init_new = 0;
+        init_we = 0;
+        next_new = 0;
+        next_we = 0;
+        initnext_new = 0;
+        initnext_we = 0;
+
+        if (tx_active_reg)
+          begin
+             initnext_we = 1;
+          end
+
+        case (rx_ctrl_reg)
+          RX_INIT:
+            if (core_ready)
+              begin
+                 rx_ctrl_new = RX_SYN;
+                 rx_ctrl_we = 1;
+              end
+          RX_SYN:
+            if (rx_syn)
+              begin
+                 rx_ack_new = 1;
+                 rx_ack_we = 1;
+                 block_new = rx_data;
+                 block_we = 1;
+                 rx_ctrl_new = RX_ACK;
+                 rx_ctrl_we = 1;
+              end
+          RX_ACK:
+            if (!rx_syn)
+              begin
+                 rx_ack_new = 0;
+                 rx_ack_we = 1;
+                 rx_ptr_new = rx_ptr_reg + 1;
+                 rx_ptr_we = 1;
+                 rx_ctrl_new = RX_SYN;
+                 rx_ctrl_we = 1;
+                 if (rx_ptr_new == BLOCK_BYTES)
+                   begin
+                      rx_ptr_new = 0;
+                      rx_ctrl_new = RX_WAIT;
+                      if (initnext_reg == 0)
+                        begin
+                           init_new = 1;
+                           init_we = 1;
+                           initnext_new = 1;
+                           initnext_we = 1;
+                        end
+                      else
+                        begin
+                           next_new = 1;
+                           next_we = 1;
+                        end
+                   end
+              end
+          RX_WAIT:
+            if (!core_ready)
+              begin
+                 init_new = 0;
+                 init_we = 1;
+                 next_new = 0;
+                 next_we = 1;
+                 rx_ctrl_new = RX_INIT;
+                 rx_ctrl_we = 1;
+              end
+        endcase // case (rx_ctrl_reg)
+     end // rx_engine
+
+   //----------------------------------------------------------------
+   // tx_engine
+   //----------------------------------------------------------------
+   always @*
+     begin: tx_engine
+        tx_syn_new = 0;
+        tx_syn_we = 0;
+        tx_ptr_new = 0;
+        tx_ptr_we = 0;
+        tx_ctrl_new = 0;
+        tx_ctrl_we = 0;
+        tx_active_new = 0;
+        tx_active_we = 0;
+
+        case (tx_ctrl_reg)
+          TX_INIT:
+            if (core_digest_valid)
+              begin
+                 tx_ctrl_new = TX_SYN;
+                 tx_ctrl_we = 1;
+              end
+          TX_SYN:
+            begin
+               tx_syn_new = 1;
+               tx_syn_we = 1;
+               tx_ctrl_new = TX_ACK;
+               tx_ctrl_we = 1;
+            end
+          TX_ACK:
+            if (!core_digest_valid)
+              begin
+                 tx_syn_new = 0;
+                 tx_syn_we = 1;
+                 tx_ptr_new = 0;
+                 tx_ptr_we = 1;
+                 tx_ctrl_new = TX_INIT;
+                 tx_ctrl_we = 1;
+              end
+            else if (tx_ack)
+              begin
+                 tx_active_new = 1;
+                 tx_active_we = 1;
+                 tx_syn_new = 0;
+                 tx_syn_we = 1;
+                 tx_ptr_new = tx_ptr_reg + 1;
+                 tx_ptr_we = 1;
+                 tx_ctrl_new = TX_SYN;
+                 tx_ctrl_we = 1;
+                 if (((mode == MODE_SHA_512_224) && (tx_ptr_new == DIGEST_BYTES_512_224)) ||
+                     ((mode == MODE_SHA_512_256) && (tx_ptr_new == DIGEST_BYTES_512_256)) ||
+                     ((mode == MODE_SHA_384) && (tx_ptr_new == DIGEST_BYTES_384)) ||
+                     ((mode == MODE_SHA_512) && (tx_ptr_new == DIGEST_BYTES_512)))
+                   begin
+                      tx_active_new = 0;
+                      tx_ptr_new = 0;
+                      tx_ctrl_new = TX_INIT;
+                   end
+              end
+        endcase // case (tx_ctrl_reg)
+     end // tx_engine
+
+endmodule // sha512
+
+//======================================================================
+// EOF coretest_hashes.v
+//======================================================================
diff --git a/src/rtl/coretest_hashes_uart.v b/src/rtl/coretest_hashes_uart.v
new file mode 100644
index 0000000..34208c5
--- /dev/null
+++ b/src/rtl/coretest_hashes_uart.v
@@ -0,0 +1,315 @@
+//======================================================================
+//
+// coretest_hashes.v
+// -----------------
+// Top level wrapper that creates the Cryptech coretest system.
+// The wrapper contains instances of external interface, coretest
+// and the core to be tested. And if more than one core is
+// present the wrapper also includes address and data muxes.
+//
+//
+// Author: Joachim Strombergson
+// Copyright (c) 2014, SUNET
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or 
+// without modification, are permitted provided that the following 
+// conditions are met: 
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer. 
+// 
+// 2. 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. 
+// 
+// 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 OWNER 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 coretest_hashes(
+                       input wire          clk,
+                       input wire          reset_n,
+                       
+                       // External interface.
+                       input wire          rxd,
+                       output wire         txd,
+                       
+                       output wire [7 : 0] debug
+                      );
+
+  
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter UART_ADDR_PREFIX   = 8'h00;
+  parameter SHA1_ADDR_PREFIX   = 8'h10;
+  parameter SHA256_ADDR_PREFIX = 8'h20;
+  parameter SHA512_ADDR_PREFIX = 8'h30;
+  
+  
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  // Coretest connections.
+  wire          coretest_reset_n;
+  wire          coretest_cs;
+  wire          coretest_we;
+  wire [15 : 0] coretest_address;
+  wire [31 : 0] coretest_write_data;
+  reg [31 : 0]  coretest_read_data;
+  reg           coretest_error;
+
+  // uart connections
+  wire          uart_rxd_syn;
+  wire [7 : 0]  uart_rxd_data;
+  wire          uart_rxd_ack;
+  wire          uart_txd_syn;
+  wire [7 : 0]  uart_txd_data;
+  wire          uart_txd_ack;
+  reg           uart_cs;
+  reg           uart_we;
+  reg [7 : 0]   uart_address;
+  reg [31 : 0]  uart_write_data;
+  wire [31 : 0] uart_read_data;
+  wire          uart_error;
+  wire [7 : 0]  uart_debug;
+
+  // sha1 connections.
+  reg           sha1_cs;
+  reg           sha1_we;
+  reg [7 : 0]   sha1_address;
+  reg [31 : 0]  sha1_write_data;
+  wire [31 : 0] sha1_read_data;
+  wire          sha1_error;
+  wire [7 : 0]  sha1_debug;
+
+  // sha256 connections.
+  reg           sha256_cs;
+  reg           sha256_we;
+  reg [7 : 0]   sha256_address;
+  reg [31 : 0]  sha256_write_data;
+  wire [31 : 0] sha256_read_data;
+  wire          sha256_error;
+  wire [7 : 0]  sha256_debug;
+
+  // sha512 connections.
+  reg           sha512_cs;
+  reg           sha512_we;
+  reg [7 : 0]   sha512_address;
+  reg [31 : 0]  sha512_write_data;
+  wire [31 : 0] sha512_read_data;
+  wire          sha512_error;
+  wire [7 : 0]  sha512_debug;
+  
+  
+  //----------------------------------------------------------------
+  // Concurrent assignment.
+  //----------------------------------------------------------------
+  assign debug = uart_debug;
+  
+  
+  //----------------------------------------------------------------
+  // Core instantiations.
+  //----------------------------------------------------------------
+  coretest coretest(
+                    .clk(clk),
+                    .reset_n(reset_n),
+                         
+                    .rx_syn(uart_rxd_syn),
+                    .rx_data(uart_rxd_data),
+                    .rx_ack(uart_rxd_ack),
+                    
+                    .tx_syn(uart_txd_syn),
+                    .tx_data(uart_txd_data),
+                    .tx_ack(uart_txd_ack),
+                    
+                    // Interface to the core being tested.
+                    .core_reset_n(coretest_reset_n),
+                    .core_cs(coretest_cs),
+                    .core_we(coretest_we),
+                    .core_address(coretest_address),
+                    .core_write_data(coretest_write_data),
+                    .core_read_data(coretest_read_data),
+                    .core_error(coretest_error)
+                   );
+
+
+  uart uart(
+            .clk(clk),
+            .reset_n(reset_n),
+            
+            .rxd(rxd),
+            .txd(txd),
+
+            .rxd_syn(uart_rxd_syn),
+            .rxd_data(uart_rxd_data),
+            .rxd_ack(uart_rxd_ack),
+
+            .txd_syn(uart_txd_syn),
+            .txd_data(uart_txd_data),
+            .txd_ack(uart_txd_ack),
+            
+            .cs(uart_cs),
+            .we(uart_we),
+            .address(uart_address),
+            .write_data(uart_write_data),
+            .read_data(uart_read_data),
+            .error(uart_error),
+
+            .debug(uart_debug)
+           );
+
+  
+  sha1 sha1(
+            // Clock and reset.
+            .clk(clk),
+            .reset_n(reset_n),
+            
+            // Control.
+            .cs(sha1_cs),
+            .we(sha1_we),
+              
+            // Data ports.
+            .address(sha1_address),
+            .write_data(sha1_write_data),
+            .read_data(sha1_read_data),
+            .error(sha1_error)
+           );
+
+  
+  sha256 sha256(
+                // Clock and reset.
+                .clk(clk),
+                .reset_n(reset_n),
+                
+                // Control.
+                .cs(sha256_cs),
+                .we(sha256_we),
+              
+                // Data ports.
+                .address(sha256_address),
+                .write_data(sha256_write_data),
+                .read_data(sha256_read_data),
+                .error(sha256_error)
+               );
+
+  
+  sha512 sha512(
+                // Clock and reset.
+                .clk(clk),
+                .reset_n(reset_n),
+
+                // Control.
+                .cs(sha512_cs),
+                .we(sha512_we),
+
+                // Data ports.
+                .address(sha512_address),
+                .write_data(sha512_write_data),
+                .read_data(sha512_read_data),
+                .error(sha512_error)
+               );
+
+
+  //----------------------------------------------------------------
+  // address_mux
+  //
+  // Combinational data mux that handles addressing between
+  // cores using the 32-bit memory like interface.
+  //----------------------------------------------------------------
+  always @*
+    begin : address_mux
+      // Default assignments.
+      coretest_read_data = 32'h00000000;
+      coretest_error     = 0;
+
+      uart_cs            = 0;
+      uart_we            = 0;
+      uart_address       = 8'h00;
+      uart_write_data    = 32'h00000000;
+
+      sha1_cs            = 0;
+      sha1_we            = 0;
+      sha1_address       = 8'h00;
+      sha1_write_data    = 32'h00000000;
+
+      sha256_cs          = 0;
+      sha256_we          = 0;
+      sha256_address     = 8'h00;
+      sha256_write_data  = 32'h00000000;
+
+      sha512_cs          = 0;
+      sha512_we          = 0;
+      sha512_address     = 8'h00;
+      sha512_write_data  = 32'h00000000;
+
+
+      case (coretest_address[15 : 8])
+        UART_ADDR_PREFIX:
+          begin
+            uart_cs            = coretest_cs;
+            uart_we            = coretest_we;
+            uart_address       = coretest_address[7 : 0];
+            uart_write_data    = coretest_write_data;
+            coretest_read_data = uart_read_data;
+            coretest_error     = uart_error;
+          end
+
+        
+        SHA1_ADDR_PREFIX:
+          begin
+            sha1_cs            = coretest_cs;
+            sha1_we            = coretest_we;
+            sha1_address       = coretest_address[7 : 0];
+            sha1_write_data    = coretest_write_data;
+            coretest_read_data = sha1_read_data;
+            coretest_error     = sha1_error;
+          end
+
+        
+        SHA256_ADDR_PREFIX:
+          begin
+            sha256_cs          = coretest_cs;
+            sha256_we          = coretest_we;
+            sha256_address     = coretest_address[7 : 0];
+            sha256_write_data  = coretest_write_data;
+            coretest_read_data = sha256_read_data;
+            coretest_error     = sha256_error;
+          end
+
+
+        SHA512_ADDR_PREFIX:
+          begin
+            sha512_cs          = coretest_cs;
+            sha512_we          = coretest_we;
+            sha512_address     = coretest_address[7 : 0];
+            sha512_write_data  = coretest_write_data;
+            coretest_read_data = sha512_read_data;
+            coretest_error     = sha512_error;
+          end
+        
+        
+        default:
+          begin
+          end
+      endcase // case (coretest_address[15 : 8])
+    end // address_mux
+  
+endmodule // coretest_hashes
+
+//======================================================================
+// EOF coretest_hashes.v
+//======================================================================
diff --git a/src/sw/hash_i2c.c b/src/sw/hash_i2c.c
new file mode 100644
index 0000000..29110eb
--- /dev/null
+++ b/src/sw/hash_i2c.c
@@ -0,0 +1,620 @@
+/* 
+ * hash.c
+ * ------
+ * This program uses the coretest_hashes subsystem to produce a
+ * cryptographic hash of a file or input stream. It is a generalization
+ * of the hash_tester.c test program.
+ * 
+ * Authors: Joachim Strömbergson, Paul Selkirk
+ * Copyright (c) 2014, SUNET
+ * 
+ * Redistribution and use in source and binary forms, with or 
+ * without modification, are permitted provided that the following 
+ * conditions are met: 
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. 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. 
+ * 
+ * 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 OWNER 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.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <assert.h>
+
+char *usage = 
+"Usage: %s [-d] [-v] [-q] [-i I2C_device] [-a I2C_addr] [algorithm [file]]\n"
+"algorithms: sha-1, sha-256, sha-512/224, sha-512/256, sha-384, sha-512\n";
+
+/* I2C configuration */
+#define I2C_dev  "/dev/i2c-2"
+#define I2C_addr 0x0f
+
+/* command codes */
+#define SOC       0x55
+#define EOC       0xaa
+#define READ_CMD  0x10
+#define WRITE_CMD 0x11
+#define RESET_CMD 0x01
+
+/* response codes */
+#define SOR       0xaa
+#define EOR       0x55
+#define READ_OK   0x7f
+#define WRITE_OK  0x7e
+#define RESET_OK  0x7d
+#define UNKNOWN   0xfe
+#define ERROR     0xfd
+
+/* addresses and codes common to all hash cores */
+#define ADDR_NAME0              0x00
+#define ADDR_NAME1              0x01
+#define ADDR_VERSION            0x02
+#define ADDR_CTRL               0x08
+#define CTRL_INIT_CMD           1
+#define CTRL_NEXT_CMD           2
+#define ADDR_STATUS             0x09
+#define STATUS_READY_BIT        0
+#define STATUS_VALID_BIT        1
+
+/* addresses and codes for the specific hash cores */
+/* block and digest lengths are number of bytes */
+#define SHA1_ADDR_PREFIX        0x10
+#define SHA1_ADDR_BLOCK         0x10
+#define SHA1_BLOCK_LEN          512/8
+#define SHA1_ADDR_DIGEST        0x20
+#define SHA1_DIGEST_LEN         160/8
+
+#define SHA256_ADDR_PREFIX      0x20
+#define SHA256_ADDR_BLOCK       0x10
+#define SHA256_BLOCK_LEN        512/8
+#define SHA256_ADDR_DIGEST      0x20
+#define SHA256_DIGEST_LEN       256/8
+
+#define SHA512_ADDR_PREFIX      0x30
+#define SHA512_CTRL_MODE_LOW    2
+#define SHA512_CTRL_MODE_HIGH   3
+#define SHA512_ADDR_BLOCK       0x10
+#define SHA512_BLOCK_LEN        1024/8
+#define SHA512_ADDR_DIGEST      0x40
+#define SHA512_224_DIGEST_LEN   224/8
+#define SHA512_256_DIGEST_LEN   256/8
+#define SHA384_DIGEST_LEN       384/8
+#define SHA512_DIGEST_LEN       512/8
+#define MODE_SHA512_224         0
+#define MODE_SHA512_256         1
+#define MODE_SHA384             2
+#define MODE_SHA512             3
+
+int i2cfd;
+int debug = 0;
+int verbose = 0;
+
+/* ---------------- algorithm lookup code ---------------- */
+
+struct ctrl {
+    char *name;
+    uint8_t addr_prefix;
+    uint8_t addr_block;
+    uint8_t block_len;
+    uint8_t addr_digest;
+    uint8_t digest_len;
+    uint8_t mode;
+} ctrl[] = {
+    { "sha-1",       SHA1_ADDR_PREFIX, SHA1_ADDR_BLOCK, SHA1_BLOCK_LEN,
+                     SHA1_ADDR_DIGEST, SHA1_DIGEST_LEN, 0 },
+    { "sha-256",     SHA256_ADDR_PREFIX, SHA256_ADDR_BLOCK, SHA256_BLOCK_LEN,
+                     SHA256_ADDR_DIGEST, SHA256_DIGEST_LEN, 0 },
+    { "sha-512/224", SHA512_ADDR_PREFIX, SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
+                     SHA512_ADDR_DIGEST, SHA512_224_DIGEST_LEN, MODE_SHA512_224 },
+    { "sha-512/256", SHA512_ADDR_PREFIX, SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
+                     SHA512_ADDR_DIGEST, SHA512_256_DIGEST_LEN, MODE_SHA512_256 },
+    { "sha-384",     SHA512_ADDR_PREFIX, SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
+                     SHA512_ADDR_DIGEST, SHA384_DIGEST_LEN, MODE_SHA384 },
+    { "sha-512",     SHA512_ADDR_PREFIX, SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
+                     SHA512_ADDR_DIGEST, SHA512_DIGEST_LEN, MODE_SHA512 },
+    { NULL, 0, 0, 0 }
+};
+
+/* return the control structure for the given algorithm */
+struct ctrl *find_algo(char *algo)
+{
+    int i;
+
+    for (i = 0; ctrl[i].name != NULL; ++i)
+        if (strcmp(ctrl[i].name, algo) == 0)
+            return &ctrl[i];
+
+    fprintf(stderr, "algorithm \"%s\" not found\n\n", algo);
+    fprintf(stderr, usage, "hash");
+    return NULL;
+}
+
+/* ---------------- I2C low-level code ---------------- */
+
+int i2c_open(char *dev, int addr)
+{
+    i2cfd = open(dev, O_RDWR);
+    if (i2cfd < 0) {
+        fprintf(stderr, "Unable to open %s: ", dev);
+        perror("");
+        i2cfd = 0;
+        return 1;
+    }
+
+    if (ioctl(i2cfd, I2C_SLAVE, addr) < 0) {
+        fprintf(stderr, "Unable to set I2C slave device 0x%02x: ", addr);
+        perror("");
+        return 1;
+    }
+
+    return 0;
+}
+
+void i2c_close(void)
+{
+    close(i2cfd);
+}
+
+int i2c_write(uint8_t *buf, int len)
+{
+    if (debug) {
+        int i;
+        printf("write [");
+        for (i = 0; i < len; ++i)
+            printf(" %02x", buf[i]);
+        printf(" ]\n");
+    }
+
+    if (write(i2cfd, buf, len) != len) {
+        perror("i2c write failed");
+        return 1;
+    }
+
+    return 0;
+}
+
+int i2c_read(uint8_t *b)
+{
+    /* read() on the i2c device only returns one byte at a time,
+     * and tc_get_resp() needs to parse the response one byte at a time
+     */
+    if (read(i2cfd, b, 1) != 1) {
+        perror("i2c read failed");
+        return 1;
+    }
+
+    return 0;
+}
+
+/* ---------------- test-case low-level code ---------------- */
+
+int tc_send_write_cmd(uint8_t addr0, uint8_t addr1, uint8_t *data)
+{
+    uint8_t buf[9] = { SOC, WRITE_CMD, addr0, addr1,
+                       data[0], data[1], data[2], data[3], EOC };
+
+    return i2c_write(buf, sizeof(buf));
+}
+
+int tc_send_read_cmd(uint8_t addr0, uint8_t addr1)
+{
+    uint8_t buf[5] = { SOC, READ_CMD, addr0, addr1, EOC };
+
+    return i2c_write(buf, sizeof(buf));
+}
+
+int tc_get_resp(uint8_t *buf, int len)
+{
+    int i;
+
+    for (i = 0; i < len; ++i) {
+        if (i2c_read(&buf[i]) != 0)
+            return 1;
+        if ((i == 0) && (buf[i] != SOR)) {
+            /* we've gotten out of sync, and there's probably nothing we can do */
+            fprintf(stderr, "response byte 0: expected 0x%02x (SOR), got 0x%02x\n",
+                    SOR, buf[0]);
+            return 1;
+        }
+        else if (i == 1) {      /* response code */
+            switch (buf[i]) {
+            case READ_OK:
+                len = 9;
+                break;
+            case WRITE_OK:
+                len = 5;
+                break;
+            case RESET_OK:
+                len = 3;
+                break;
+            case ERROR:
+            case UNKNOWN:
+                len = 4;
+                break;
+            default:
+                /* we've gotten out of sync, and there's probably nothing we can do */
+                fprintf(stderr, "unknown response code 0x%02x\n", buf[i]);
+                return 1;
+            }
+        }
+    }
+
+    if (debug) {
+        printf("read  [");
+        for (i = 0; i < len; ++i)
+            printf(" %02x", buf[i]);
+        printf(" ]\n");
+    }
+
+    return 0;
+}
+
+int tc_compare(uint8_t *buf, uint8_t *expected, int len)
+{
+    int i;
+
+    /* start at byte 1 because SOR has already been tested */
+    for (i = 1; i < len; ++i) {
+        if (buf[i] != expected[i]) {
+            fprintf(stderr, "response byte %d: expected 0x%02x, got 0x%02x\n",
+                    i, expected[i], buf[i]);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+int tc_get_write_resp(uint8_t addr0, uint8_t addr1)
+{
+    uint8_t buf[5];
+    uint8_t expected[5] = { SOR, WRITE_OK, addr0, addr1, EOR };
+
+    return
+        tc_get_resp(buf, sizeof(buf)) ||
+        tc_compare(buf, expected, sizeof(expected));
+}
+
+int tc_get_read_resp(uint8_t addr0, uint8_t addr1, uint8_t *data)
+{
+    uint8_t buf[9];
+    uint8_t expected[4] = { SOR, READ_OK, addr0, addr1 };
+
+    if ((tc_get_resp(buf, sizeof(buf)) != 0) ||
+        (tc_compare(buf, expected, 4) != 0) || buf[8] != EOR)
+        return 1;
+
+    data[0] = buf[4];
+    data[1] = buf[5];
+    data[2] = buf[6];
+    data[3] = buf[7];
+
+    return 0;
+}
+
+int tc_write(uint8_t addr0, uint8_t addr1, uint8_t *data)
+{
+    return (tc_send_write_cmd(addr0, addr1, data) ||
+            tc_get_write_resp(addr0, addr1));
+}
+
+int tc_read(uint8_t addr0, uint8_t addr1, uint8_t *data)
+{
+    return (tc_send_read_cmd(addr0, addr1) ||
+            tc_get_read_resp(addr0, addr1, data));
+}
+
+int tc_init(uint8_t addr0, uint8_t mode)
+{
+    uint8_t buf[4] = { 0, 0, 0, CTRL_INIT_CMD };
+
+    if (addr0 == SHA512_ADDR_PREFIX)
+        buf[3] += (mode << SHA512_CTRL_MODE_LOW);
+
+    return tc_write(addr0, ADDR_CTRL, buf);
+}
+
+int tc_next(uint8_t addr0, uint8_t mode)
+{
+    uint8_t buf[4] = { 0, 0, 0, CTRL_NEXT_CMD };
+
+    if (addr0 == SHA512_ADDR_PREFIX)
+        buf[3] += (mode << SHA512_CTRL_MODE_LOW);
+
+    return tc_write(addr0, ADDR_CTRL, buf);
+}
+
+int tc_wait(uint8_t addr0, uint8_t status)
+{
+    uint8_t buf[4];
+
+    do {
+        if (tc_read(addr0, ADDR_STATUS, &buf[0]) != 0)
+            return 1;
+    } while ((buf[3] & status) != status);
+
+    return 0;
+}
+
+int tc_wait_ready(uint8_t addr0)
+{
+    return tc_wait(addr0, STATUS_READY_BIT);
+}
+
+int tc_wait_valid(uint8_t addr0)
+{
+    return tc_wait(addr0, STATUS_VALID_BIT);
+}
+
+int tc_write_block(uint8_t addr0, uint8_t addr1, uint8_t *buf, int len)
+{
+    int i;
+
+    for (i = 0; i < len/4; ++i) {
+        if (tc_write(addr0, addr1 + i, &buf[i*4]) != 0)
+            return 1;
+    }
+
+    return 0;
+}
+
+int tc_read_digest(uint8_t addr0, uint8_t addr1, uint8_t *buf, int len)
+{
+    int i;
+
+    for (i = 0; i < len/4; ++i) {
+        if (tc_read(addr0, addr1 + i, &buf[i*4]) != 0)
+            return 1;
+    }
+
+    return 0;
+}
+
+/* ---------------- hash ---------------- */
+
+int transmit(uint8_t *block, uint8_t blen, uint8_t addr0, uint8_t baddr,
+             uint8_t mode, int first)
+{
+    int i;
+
+    if (debug) {
+        printf("write [");
+        for (i = 0; i < blen; ++i)
+            printf(" %02x", block[i]);
+        printf(" ]\n");
+    }
+
+    if (tc_write_block(addr0, baddr, block, blen) != 0) {
+        return 1;
+    }
+    if (first) {
+        if (tc_init(addr0, mode) != 0)
+            return 1;
+    }
+    else {
+        if (tc_next(addr0, mode) != 0)
+            return 1;
+    }
+    if (tc_wait_ready(addr0) != 0)
+        return 1;
+
+    return 0;
+}
+
+int pad_transmit(uint8_t *block, uint8_t flen, uint8_t blen,
+                 uint8_t addr0, uint8_t baddr, uint8_t mode,
+                 long long tlen, int first)
+{
+    assert(flen < blen);
+
+    block[flen++] = 0x80;
+    memset(block + flen, 0, blen - flen);
+
+    if (blen - flen < ((blen == 64) ? 8 : 16)) {
+        if (transmit(block, blen, addr0, baddr, mode, first) != 0)
+            return 1;
+        first = 0;
+        memset(block, 0, blen);
+    }
+
+    /* properly the length is 128 bits for sha-512, but we can't
+     * actually count above 64 bits
+     */
+    ((uint32_t *)block)[blen/4 - 2] = htonl((tlen >> 32) & 0xffff);
+    ((uint32_t *)block)[blen/4 - 1] = htonl(tlen & 0xffff);
+
+    return transmit(block, blen, addr0, baddr, mode, first);
+}
+
+/* return number of digest bytes read */
+int hash(char *algo, char *file, uint8_t *digest)
+{
+    uint8_t block[SHA512_BLOCK_LEN];
+    struct ctrl *ctrl;
+    int in_fd = 0;      /* stdin */
+    uint8_t addr0, baddr, blen, daddr, dlen, mode;
+    int nblk, nread, first;
+    int ret = -1;
+    struct timeval start, stop, difftime;
+
+    ctrl = find_algo(algo);
+    if (ctrl == NULL)
+        return -1;
+    addr0 = ctrl->addr_prefix;
+    baddr = ctrl->addr_block;
+    blen = ctrl->block_len;
+    daddr = ctrl->addr_digest;
+    dlen = ctrl->digest_len;
+    mode = ctrl->mode;
+
+    if (strcmp(file, "-") != 0) {
+        in_fd = open(file, O_RDONLY);
+        if (in_fd < 0) {
+            perror("open");
+            return -1;
+        }
+    }
+
+    if (verbose) {
+        if (gettimeofday(&start, NULL) < 0) {
+            perror("gettimeofday");
+            goto out;
+        }
+    }
+
+    for (nblk = 0, first = 1; ; ++nblk, first = 0) {
+        nread = read(in_fd, block, blen);
+        if (nread < 0) {
+            /* read error */
+            perror("read");
+            goto out;
+        }
+        else if (nread < blen) {
+            /* partial read = last block */
+            if (pad_transmit(block, nread, blen, addr0, baddr, mode,
+                             (nblk * blen + nread) * 8, first) != 0)
+                goto out;
+            break;
+        }
+        else {
+            /* full block read */
+            if (transmit(block, blen, addr0, baddr, mode, first) != 0)
+                goto out;
+        }
+    }
+
+    if (tc_wait_valid(addr0) != 0)
+        goto out;
+    if (tc_read_digest(addr0, daddr, digest, dlen) != 0) {
+        perror("i2c read failed");
+        goto out;
+    }
+
+    if (verbose) {
+        if (gettimeofday(&stop, NULL) < 0) {
+            perror("gettimeofday");
+            goto out;
+        }
+        timersub(&stop, &start, &difftime);
+        printf("%d blocks written in %d.%03d sec (%.3f blocks/sec)\n",
+               nblk, (int)difftime.tv_sec, (int)difftime.tv_usec/1000,
+               (float)nblk / ((float)difftime.tv_sec + ((float)difftime.tv_usec)/1000000));
+    }
+
+    ret = dlen;
+out:
+    if (in_fd != 0)
+        close(in_fd);
+    return ret;
+}
+
+/* ---------------- main ---------------- */
+
+int main(int argc, char *argv[])
+{
+    char *dev = I2C_dev;
+    int addr = I2C_addr;
+    int i, opt, quiet = 0;
+    char *algo = "sha-1";
+    char *file = "-";
+    uint8_t digest[512/8];
+    int dlen;
+
+    while ((opt = getopt(argc, argv, "h?dvqi:a:")) != -1) {
+        switch (opt) {
+        case 'h':
+        case '?':
+            printf(usage, argv[0]);
+            return 0;
+        case 'd':
+            debug = 1;
+            break;
+        case 'v':
+            verbose = 1;
+            break;
+        case 'q':
+            quiet = 1;
+            break;
+        case 'i':
+            dev = optarg;
+            break;
+        case 'a':
+            addr = (int)strtol(optarg, NULL, 0);
+            if ((addr < 0x03) || (addr > 0x77)) {
+                fprintf(stderr, "addr must be between 0x03 and 0x77\n");
+                return 1;
+            }
+            break;
+        default:
+            fprintf(stderr, usage, argv[0]);
+            return 1;
+        }
+    }
+
+    if (optind < argc) {
+        algo = argv[optind];
+        ++optind;
+    }
+    else {
+        if (!quiet)
+            printf("defaulting to algorithm \"%s\"\n", algo);
+    }
+
+    if (optind < argc) {
+        file = argv[optind];
+        ++optind;
+    }
+    else {
+        if (!quiet)
+            printf("reading from stdin\n");
+    }
+
+    if (i2c_open(dev, addr) != 0)
+        return -1;
+
+    dlen = hash(algo, file, digest);
+    if (dlen < 0)
+        return 1;
+
+    for (i = 0; i < dlen; ++i) {
+        printf("%02x", digest[i]);
+        if (i % 16 == 15)
+            printf("\n");
+        else if (i % 4 == 3)
+            printf(" ");
+    }
+    if (dlen % 16 != 0)
+        printf("\n");
+
+    i2c_close();
+
+    return 0;
+}
diff --git a/src/sw/hash_i2c_simple.c b/src/sw/hash_i2c_simple.c
new file mode 100644
index 0000000..f9bb3b8
--- /dev/null
+++ b/src/sw/hash_i2c_simple.c
@@ -0,0 +1,337 @@
+/* 
+ * hash.c
+ * ------
+ * This program uses the coretest_hashes subsystem to produce a
+ * cryptographic hash of a file or input stream. It is a generalization
+ * of the hash_tester.c test program.
+ * 
+ * Author: Paul Selkirk
+ * Copyright (c) 2014, SUNET
+ * 
+ * Redistribution and use in source and binary forms, with or 
+ * without modification, are permitted provided that the following 
+ * conditions are met: 
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. 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. 
+ * 
+ * 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 OWNER 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.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <assert.h>
+
+char *usage = 
+"Usage: %s [-d] [-v] [-q] [-i I2C_device] [-a I2C_addr] [algorithm [file]]\n"
+"algorithms: sha-1, sha-256, sha-512/224, sha-512/256, sha-384, sha-512\n";
+
+/* I2C configuration */
+#define I2C_dev  "/dev/i2c-2"
+
+int debug = 0;
+int verbose = 0;
+
+/* block and digest lengths are number of bytes */
+#define SHA1_BLOCK_LEN          512/8
+#define SHA1_DIGEST_LEN         160/8
+#define SHA256_BLOCK_LEN        512/8
+#define SHA256_DIGEST_LEN       256/8
+#define SHA512_BLOCK_LEN        1024/8
+#define SHA512_224_DIGEST_LEN   224/8
+#define SHA512_256_DIGEST_LEN   256/8
+#define SHA384_DIGEST_LEN       384/8
+#define SHA512_DIGEST_LEN       512/8
+
+/* ---------------- algorithm lookup code ---------------- */
+
+struct ctrl {
+    char *name;
+    uint8_t i2c_addr;
+    uint8_t block_len;
+    uint8_t digest_len;
+} ctrl[] = {
+    { "sha-1",       0x1e, SHA1_BLOCK_LEN,   SHA1_DIGEST_LEN },
+    { "sha-256",     0x1f, SHA256_BLOCK_LEN, SHA256_DIGEST_LEN },
+    { "sha-512/224", 0x20, SHA512_BLOCK_LEN, SHA512_224_DIGEST_LEN },
+    { "sha-512/256", 0x21, SHA512_BLOCK_LEN, SHA512_256_DIGEST_LEN },
+    { "sha-384",     0x22, SHA512_BLOCK_LEN, SHA384_DIGEST_LEN },
+    { "sha-512",     0x23, SHA512_BLOCK_LEN, SHA512_DIGEST_LEN },
+    { NULL, 0, 0, 0 }
+};
+
+/* return the control structure for the given algorithm */
+struct ctrl *find_algo(char *algo)
+{
+    int i;
+
+    for (i = 0; ctrl[i].name != NULL; ++i)
+        if (strcmp(ctrl[i].name, algo) == 0)
+            return &ctrl[i];
+
+    fprintf(stderr, "algorithm \"%s\" not found\n\n", algo);
+    fprintf(stderr, usage, "hash");
+    return NULL;
+}
+
+/* ---------------- I2C low-level code ---------------- */
+
+/* return file descriptor for i2c device */
+int i2c_open(char *dev, int addr)
+{
+    int fd;
+
+    fd = open(dev, O_RDWR);
+    if (fd < 0) {
+        fprintf(stderr, "Unable to open %s: ", dev);
+        perror("");
+        return -1;
+    }
+
+    if (ioctl(fd, I2C_SLAVE, addr) < 0) {
+        fprintf(stderr, "Unable to set I2C slave device 0x%02x: ", addr);
+        perror("");
+        close(fd);
+        return -1;
+    }
+
+    return fd;
+}
+
+void i2c_close(int ifd)
+{
+    close(ifd);
+}
+
+/* ---------------- hash ---------------- */
+
+int transmit(uint8_t *block, uint8_t blen, int fd)
+{
+    int i;
+
+    if (debug) {
+        printf("write [");
+        for (i = 0; i < blen; ++i)
+            printf(" %02x", block[i]);
+        printf(" ]\n");
+    }
+
+    if (write(fd, block, blen) != blen) {
+        return 1;
+    }
+
+    return 0;
+}
+
+int pad_transmit(uint8_t *block, uint8_t flen, uint8_t blen, int fd, long long tlen)
+{
+    assert(flen < blen);
+
+    block[flen++] = 0x80;
+    memset(block + flen, 0, blen - flen);
+
+    if (blen - flen < ((blen == 64) ? 8 : 16)) {
+        if (transmit(block, blen, fd) != 0)
+            return 1;
+        memset(block, 0, blen);
+    }
+
+    /* properly the length is 128 bits for sha-512, but we can't
+     * actually count above 64 bits
+     */
+    ((uint32_t *)block)[blen/4 - 2] = htonl((tlen >> 32) & 0xffff);
+    ((uint32_t *)block)[blen/4 - 1] = htonl(tlen & 0xffff);
+
+    return transmit(block, blen, fd);
+}
+
+/* return number of digest bytes read */
+int hash(char *dev, char *algo, char *file, uint8_t *digest)
+{
+    uint8_t block[SHA512_BLOCK_LEN];
+    struct ctrl *ctrl;
+    int i2c_fd, in_fd = 0;
+    int addr, blen, dlen;
+    int nblk, nread;
+    int i, ret = -1;
+    struct timeval start, stop, difftime;
+
+    ctrl = find_algo(algo);
+    if (ctrl == NULL)
+        return -1;
+    addr = ctrl->i2c_addr;
+    blen = ctrl->block_len;
+    dlen = ctrl->digest_len;
+
+    i2c_fd = i2c_open(dev, addr);
+    if (i2c_fd < 0)
+        return -1;
+
+    if (strcmp(file, "-") != 0) {
+        in_fd = open(file, O_RDONLY);
+        if (in_fd < 0) {
+            perror("open");
+            goto out2;
+        }
+    }
+
+    if (verbose) {
+        if (gettimeofday(&start, NULL) < 0) {
+            perror("gettimeofday");
+            goto out;
+        }
+    }
+
+    for (nblk = 0; ; ++nblk) {
+        nread = read(in_fd, block, blen);
+        if (nread < 0) {
+            /* read error */
+            perror("read");
+            goto out;
+        }
+        else if (nread < blen) {
+            /* partial read = last block */
+            if (pad_transmit(block, nread, blen, i2c_fd,
+                             (nblk * blen + nread) * 8) != 0)
+                goto out;
+            break;
+        }
+        else {
+            /* full block read */
+            if (transmit(block, blen, i2c_fd) != 0)
+                goto out;
+        }
+    }
+
+    for (i = 0; i < dlen; ++i) {
+        /* read() on the i2c device only returns one byte at a time */
+        if (read(i2c_fd, &digest[i], 1) != 1) {
+            perror("i2c read failed");
+            goto out;
+        }
+    }
+
+    if (verbose) {
+        if (gettimeofday(&stop, NULL) < 0) {
+            perror("gettimeofday");
+            goto out;
+        }
+        timersub(&stop, &start, &difftime);
+        printf("%d blocks written in %d.%03d sec (%.3f blocks/sec)\n",
+               nblk, (int)difftime.tv_sec, (int)difftime.tv_usec/1000,
+               (float)nblk / ((float)difftime.tv_sec + ((float)difftime.tv_usec)/1000000));
+    }
+
+    ret = dlen;
+out:
+    if (in_fd != 0)
+        close(in_fd);
+out2:
+    i2c_close(i2c_fd);
+    return ret;
+}
+
+/* ---------------- main ---------------- */
+
+int main(int argc, char *argv[])
+{
+    char *dev = I2C_dev;
+    int addr = 0;
+    int i, opt, quiet = 0;
+    char *algo = "sha-1";
+    char *file = "-";
+    uint8_t digest[512/8];
+    int dlen;
+
+    while ((opt = getopt(argc, argv, "h?dvqi:a:")) != -1) {
+        switch (opt) {
+        case 'h':
+        case '?':
+            printf(usage, argv[0]);
+            return 0;
+        case 'd':
+            debug = 1;
+            break;
+        case 'v':
+            verbose = 1;
+            break;
+        case 'q':
+            quiet = 1;
+            break;
+        case 'i':
+            dev = optarg;
+            break;
+        case 'a':
+            addr = (int)strtol(optarg, NULL, 0);
+            if ((addr < 0x03) || (addr > 0x77)) {
+                fprintf(stderr, "addr must be between 0x03 and 0x77\n");
+                return 1;
+            }
+            break;
+        default:
+            fprintf(stderr, usage, argv[0]);
+            return 1;
+        }
+    }
+
+    if (optind < argc) {
+        algo = argv[optind];
+        ++optind;
+    }
+    else {
+        if (!quiet)
+            printf("defaulting to algorithm \"%s\"\n", algo);
+    }
+
+    if (optind < argc) {
+        file = argv[optind];
+        ++optind;
+    }
+    else {
+        if (!quiet)
+            printf("reading from stdin\n");
+    }
+
+    dlen = hash(dev, algo, file, digest);
+    if (dlen < 0)
+        return 1;
+
+    for (i = 0; i < dlen; ++i) {
+        printf("%02x", digest[i]);
+        if (i % 16 == 15)
+            printf("\n");
+        else if (i % 4 == 3)
+            printf(" ");
+    }
+    if (dlen % 16 != 0)
+        printf("\n");
+
+    return 0;
+}
diff --git a/src/sw/hash_tester_eim.c b/src/sw/hash_tester_eim.c
new file mode 100644
index 0000000..9d86fa8
--- /dev/null
+++ b/src/sw/hash_tester_eim.c
@@ -0,0 +1,962 @@
+/* 
+ * hash_tester.c
+ * --------------
+ * This program sends several commands to the coretest_hashes subsystem
+ * in order to verify the SHA-1, SHA-256 and SHA-512/x hash function
+ * cores.
+ *
+ * Note: This version of the program talks to the FPGA over an EIM bus.
+ *
+ * The single and dual block test cases are taken from the
+ * NIST KAT document:
+ * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf
+ *
+ * 
+ * Authors: Joachim Strömbergson, Paul Selkirk
+ * Copyright (c) 2014, 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.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+int debug = 0;
+
+#define EIM_ADDR_BASE           0x08040000
+
+/* addresses and codes common to all hash cores */
+#define ADDR_NAME0              0x00
+#define ADDR_NAME1              0x04
+#define ADDR_VERSION            0x08
+#define ADDR_CTRL               0x20
+#define CTRL_INIT_CMD           1
+#define CTRL_NEXT_CMD           2
+#define ADDR_STATUS             0x24
+#define STATUS_READY_BIT        1
+#define STATUS_VALID_BIT        2
+#define ADDR_BLOCK              0x40
+#define ADDR_DIGEST             0x80
+
+/* addresses and codes for the specific hash cores */
+#define SHA1_ADDR_BASE          0x2000
+#define SHA1_ADDR_NAME0         SHA1_ADDR_BASE + ADDR_NAME0
+#define SHA1_ADDR_NAME1         SHA1_ADDR_BASE + ADDR_NAME1
+#define SHA1_ADDR_VERSION       SHA1_ADDR_BASE + ADDR_VERSION
+#define SHA1_ADDR_CTRL          SHA1_ADDR_BASE + ADDR_CTRL
+#define SHA1_ADDR_STATUS        SHA1_ADDR_BASE + ADDR_STATUS
+#define SHA1_ADDR_BLOCK         SHA1_ADDR_BASE + ADDR_BLOCK
+#define SHA1_ADDR_DIGEST        SHA1_ADDR_BASE + ADDR_DIGEST
+#define SHA1_BLOCK_LEN          512 / 8
+#define SHA1_DIGEST_LEN         160 / 8
+
+#define SHA256_ADDR_BASE        0x3000
+#define SHA256_ADDR_NAME0       SHA256_ADDR_BASE + ADDR_NAME0
+#define SHA256_ADDR_NAME1       SHA256_ADDR_BASE + ADDR_NAME1
+#define SHA256_ADDR_VERSION     SHA256_ADDR_BASE + ADDR_VERSION
+#define SHA256_ADDR_CTRL        SHA256_ADDR_BASE + ADDR_CTRL
+#define SHA256_ADDR_STATUS      SHA256_ADDR_BASE + ADDR_STATUS
+#define SHA256_ADDR_BLOCK       SHA256_ADDR_BASE + ADDR_BLOCK
+#define SHA256_ADDR_DIGEST      SHA256_ADDR_BASE + ADDR_DIGEST
+#define SHA256_BLOCK_LEN        512 / 8
+#define SHA256_DIGEST_LEN       256 / 8
+
+#define SHA512_ADDR_BASE        0x4000
+#define SHA512_ADDR_NAME0       SHA512_ADDR_BASE + ADDR_NAME0
+#define SHA512_ADDR_NAME1       SHA512_ADDR_BASE + ADDR_NAME1
+#define SHA512_ADDR_VERSION     SHA512_ADDR_BASE + ADDR_VERSION
+#define SHA512_ADDR_CTRL        SHA512_ADDR_BASE + ADDR_CTRL
+#define SHA512_ADDR_STATUS      SHA512_ADDR_BASE + ADDR_STATUS
+#define SHA512_ADDR_BLOCK       SHA512_ADDR_BASE + ADDR_BLOCK
+#define SHA512_ADDR_DIGEST      SHA512_ADDR_BASE + 0x100
+#define SHA512_BLOCK_LEN        1024 / 8
+#define SHA512_224_DIGEST_LEN   224 / 8
+#define SHA512_256_DIGEST_LEN   256 / 8
+#define SHA384_DIGEST_LEN       384 / 8
+#define SHA512_DIGEST_LEN       512 / 8
+#define MODE_SHA_512_224        0 << 2
+#define MODE_SHA_512_256        1 << 2
+#define MODE_SHA_384            2 << 2
+#define MODE_SHA_512            3 << 2
+
+/* SHA-1/SHA-256 One Block Message Sample
+   Input Message: "abc" */
+const uint8_t NIST_512_SINGLE[] =
+{ 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18 };
+
+const uint8_t SHA1_SINGLE_DIGEST[] =
+{ 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a,
+  0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c,
+  0x9c, 0xd0, 0xd8, 0x9d };
+
+const uint8_t SHA256_SINGLE_DIGEST[] =
+{ 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA,
+  0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23,
+  0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C,
+  0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD };
+
+/* SHA-1/SHA-256 Two Block Message Sample
+   Input Message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" */
+const uint8_t NIST_512_DOUBLE0[] =
+{ 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+  0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+  0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+  0x67, 0x68, 0x69, 0x6A, 0x68, 0x69, 0x6A, 0x6B,
+  0x69, 0x6A, 0x6B, 0x6C, 0x6A, 0x6B, 0x6C, 0x6D,
+  0x6B, 0x6C, 0x6D, 0x6E, 0x6C, 0x6D, 0x6E, 0x6F,
+  0x6D, 0x6E, 0x6F, 0x70, 0x6E, 0x6F, 0x70, 0x71,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+const uint8_t NIST_512_DOUBLE1[] =
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0 };
+
+const uint8_t SHA1_DOUBLE_DIGEST[] =
+{ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E,
+  0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5,
+  0xE5, 0x46, 0x70, 0xF1 };
+
+const uint8_t SHA256_DOUBLE_DIGEST[] =
+{ 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8,
+  0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39,
+  0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67,
+  0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 };
+
+/* SHA-512 One Block Message Sample
+   Input Message: "abc" */
+const uint8_t NIST_1024_SINGLE[] =
+{ 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18 };
+
+const uint8_t SHA512_224_SINGLE_DIGEST[] =
+{ 0x46, 0x34, 0x27, 0x0f, 0x70, 0x7b, 0x6a, 0x54,
+  0xda, 0xae, 0x75, 0x30, 0x46, 0x08, 0x42, 0xe2,
+  0x0e, 0x37, 0xed, 0x26, 0x5c, 0xee, 0xe9, 0xa4,
+  0x3e, 0x89, 0x24, 0xaa };
+const uint8_t SHA512_256_SINGLE_DIGEST[] =
+{ 0x53, 0x04, 0x8e, 0x26, 0x81, 0x94, 0x1e, 0xf9,
+  0x9b, 0x2e, 0x29, 0xb7, 0x6b, 0x4c, 0x7d, 0xab,
+  0xe4, 0xc2, 0xd0, 0xc6, 0x34, 0xfc, 0x6d, 0x46,
+  0xe0, 0xe2, 0xf1, 0x31, 0x07, 0xe7, 0xaf, 0x23 };
+const uint8_t SHA384_SINGLE_DIGEST[] =
+{ 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
+  0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
+  0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+  0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
+  0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
+  0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 };
+const uint8_t SHA512_SINGLE_DIGEST[] =
+{ 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+  0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+  0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+  0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+  0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+  0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+  0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+  0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f };
+
+/* SHA-512 Two Block Message Sample
+   Input Message: "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+   "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" */
+const uint8_t NIST_1024_DOUBLE0[] =
+{ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+  0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+  0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+  0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+  0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+  0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+  0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+  0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+  0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
+  0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+  0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+  0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+  0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+const uint8_t NIST_1024_DOUBLE1[] =
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80 };
+
+const uint8_t SHA512_224_DOUBLE_DIGEST[] = 
+{ 0x23, 0xfe, 0xc5, 0xbb, 0x94, 0xd6, 0x0b, 0x23,
+  0x30, 0x81, 0x92, 0x64, 0x0b, 0x0c, 0x45, 0x33,
+  0x35, 0xd6, 0x64, 0x73, 0x4f, 0xe4, 0x0e, 0x72,
+  0x68, 0x67, 0x4a, 0xf9 };
+const uint8_t SHA512_256_DOUBLE_DIGEST[] =
+{ 0x39, 0x28, 0xe1, 0x84, 0xfb, 0x86, 0x90, 0xf8,
+  0x40, 0xda, 0x39, 0x88, 0x12, 0x1d, 0x31, 0xbe,
+  0x65, 0xcb, 0x9d, 0x3e, 0xf8, 0x3e, 0xe6, 0x14,
+  0x6f, 0xea, 0xc8, 0x61, 0xe1, 0x9b, 0x56, 0x3a };
+const uint8_t SHA384_DOUBLE_DIGEST[] =
+{ 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8,
+  0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47,
+  0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2,
+  0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12,
+  0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9,
+  0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39 };
+const uint8_t SHA512_DOUBLE_DIGEST[] =
+{ 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
+  0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
+  0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+  0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
+  0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+  0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+  0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
+  0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 };
+
+/* ---------------- EIM low-level code ---------------- */
+
+/* Here be dragons.
+ *
+ * 1. setup_fpga() is some serious rubber-chicken voodoo, and I'm not
+ * convinced it's 100% correct. In particular, memcpy() seems to result in
+ * stuttering.
+ *
+ * 2. The native transfer size for EIM is 16 bits, and it *byte swaps* on the
+ * wire. Again, this has been observed with memcpy. I hope it's a peculiarity
+ * of the implementation, and not a "feature" of the protocol.
+ *
+ * 3. Remember that this is mmap'd memory, and you have to calculate the
+ * offset from the base of the block. If you try to access address 0x08042000
+ * directly, Bad Things will happen (really, it will just seg-fault).
+ */
+
+static int fd = 0;
+static uint32_t *mem_32 = NULL;
+static uint16_t *mem_16 = NULL;
+static uint8_t  *mem_8  = NULL;
+static void *prev_mem_range = NULL;
+
+void map_mem(off_t offset)
+{
+    void *mem_range = (void *)(offset & ~0xFFFF);
+
+    if (mem_range == prev_mem_range)
+        return;
+
+    //fprintf(stderr, "New range detected.  Reopening at memory range %p\n", mem_range);
+    prev_mem_range = mem_range;
+
+    if (mem_32)
+        munmap((void *)mem_32, 0xFFFF);
+    if (fd)
+        close(fd);
+
+    fd = open("/dev/mem", O_RDWR);
+    if (fd < 0) {
+        perror("Unable to open /dev/mem");
+        fd = 0;
+        return;
+    }
+
+    mem_32 = (uint32_t *)mmap(NULL, 0xffff, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset&~0xFFFF);
+    if (mem_32 == MAP_FAILED) {
+        perror("Unable to mmap file");
+
+        if (close(fd) < 0)
+            perror("Also couldn't close file");
+
+        fd=0;
+        return;
+    }
+    mem_16 = (uint16_t *)mem_32;
+    mem_8  = (uint8_t *)mem_32;
+}
+
+uint32_t read_kernel_memory(off_t offset, size_t size)
+{
+  uint32_t result;
+
+  //fprintf(stderr, "read_kernel_memory(0x%04x, %d)\n", (unsigned int)offset, (int)size);
+  map_mem(offset);
+
+  off_t scaled_offset = (offset-(offset&~0xFFFF));
+  //fprintf(stderr, "Returning offset 0x%08x\n", scaled_offset);
+  if (size == 1)
+    result = mem_8[scaled_offset/sizeof(uint8_t)];
+  else if (size == 2)
+    result = mem_16[scaled_offset/sizeof(uint16_t)];
+  else
+    result = mem_32[scaled_offset/sizeof(uint32_t)];
+
+  return result;
+}
+
+uint32_t write_kernel_memory(off_t offset, uint32_t value, size_t size)
+{
+  uint32_t old_value = read_kernel_memory(offset, size);
+  off_t scaled_offset = (offset-(offset&~0xFFFF));
+  if (size == 1)
+    mem_8[scaled_offset/sizeof(uint8_t)]   = value;
+  else if (size == 2)
+    mem_16[scaled_offset/sizeof(uint16_t)] = value;
+  else
+    mem_32[scaled_offset/sizeof(uint32_t)] = value;
+  return old_value;
+}
+
+void setup_fpga() {
+  int i;
+  //printf( "setting up EIM CS0 (register interface) pads and configuring timing\n" );
+  // set up pads to be mapped to EIM
+  for( i = 0; i < 16; i++ ) {
+    write_kernel_memory( 0x20e0114 + i*4, 0x0, 4 );  // mux mapping
+    write_kernel_memory( 0x20e0428 + i*4, 0xb0b1, 4 ); // pad strength config'd for a 100MHz rate 
+  }
+
+  // mux mapping
+  write_kernel_memory( 0x20e046c - 0x314, 0x0, 4 ); // BCLK
+  write_kernel_memory( 0x20e040c - 0x314, 0x0, 4 ); // CS0
+  write_kernel_memory( 0x20e0410 - 0x314, 0x0, 4 ); // CS1
+  write_kernel_memory( 0x20e0414 - 0x314, 0x0, 4 ); // OE
+  write_kernel_memory( 0x20e0418 - 0x314, 0x0, 4 ); // RW
+  write_kernel_memory( 0x20e041c - 0x314, 0x0, 4 ); // LBA
+  write_kernel_memory( 0x20e0468 - 0x314, 0x0, 4 ); // WAIT
+  write_kernel_memory( 0x20e0408 - 0x314, 0x0, 4 ); // A16
+  write_kernel_memory( 0x20e0404 - 0x314, 0x0, 4 ); // A17
+  write_kernel_memory( 0x20e0400 - 0x314, 0x0, 4 ); // A18
+
+  // pad strength
+  write_kernel_memory( 0x20e046c, 0xb0b1, 4 ); // BCLK
+  write_kernel_memory( 0x20e040c, 0xb0b1, 4 ); // CS0
+  write_kernel_memory( 0x20e0410, 0xb0b1, 4 ); // CS1
+  write_kernel_memory( 0x20e0414, 0xb0b1, 4 ); // OE
+  write_kernel_memory( 0x20e0418, 0xb0b1, 4 ); // RW
+  write_kernel_memory( 0x20e041c, 0xb0b1, 4 ); // LBA
+  write_kernel_memory( 0x20e0468, 0xb0b1, 4 ); // WAIT
+  write_kernel_memory( 0x20e0408, 0xb0b1, 4 ); // A16
+  write_kernel_memory( 0x20e0404, 0xb0b1, 4 ); // A17
+  write_kernel_memory( 0x20e0400, 0xb0b1, 4 ); // A18
+
+  write_kernel_memory( 0x020c4080, 0xcf3, 4 ); // ungate eim slow clocks
+
+  // rework timing for sync use
+  // EIM_CS0GCR1   
+  // 0011 0  001 1   001    0   001 00  00  1  011  1    0   1   1   1   1   1   1
+  // PSZ  WP GBC AUS CSREC  SP  DSZ BCS BCD WC BL   CREP CRE RFL WFL MUM SRD SWR CSEN
+  //
+  // PSZ = 0011  64 words page size
+  // WP = 0      (not protected)
+  // GBC = 001   min 1 cycles between chip select changes
+  // AUS = 0     address shifted according to port size
+  // CSREC = 001 min 1 cycles between CS, OE, WE signals
+  // SP = 0      no supervisor protect (user mode access allowed)
+  // DSZ = 001   16-bit port resides on DATA[15:0]
+  // BCS = 00    0 clock delay for burst generation
+  // BCD = 00    divide EIM clock by 0 for burst clock
+  // WC = 1      write accesses are continuous burst length
+  // BL = 011    32 word memory wrap length
+  // CREP = 1    non-PSRAM, set to 1
+  // CRE = 0     CRE is disabled
+  // RFL = 1     fixed latency reads
+  // WFL = 1     fixed latency writes
+  // MUM = 1     multiplexed mode enabled
+  // SRD = 1     synch reads
+  // SWR = 1     synch writes
+  // CSEN = 1    chip select is enabled
+
+  //  write_kernel_memory( 0x21b8000, 0x5191C0B9, 4 );
+  write_kernel_memory( 0x21b8000, 0x31910BBF, 4 );
+
+  // EIM_CS0GCR2   
+  //  MUX16_BYP_GRANT = 1
+  //  ADH = 1 (1 cycles)
+  //  0x1001
+  write_kernel_memory( 0x21b8004, 0x1000, 4 );
+
+
+  // EIM_CS0RCR1   
+  // 00 000101 0 000   0   000   0 000 0 000 0 000 0 000
+  //    RWSC     RADVA RAL RADVN   OEA   OEN   RCSA  RCSN
+  // RWSC 000101    5 cycles for reads to happen
+  //
+  // 0000 0111 0000   0011   0000 0000 0000 0000
+  //  0    7     0     3      0  0    0    0
+  // 0000 0101 0000   0000   0 000 0 000 0 000 0 000
+//  write_kernel_memory( 0x21b8008, 0x05000000, 4 );
+//  write_kernel_memory( 0x21b8008, 0x0A024000, 4 );
+  write_kernel_memory( 0x21b8008, 0x09014000, 4 );
+  // EIM_CS0RCR2  
+  // 0000 0000 0   000 00 00 0 010  0 001 
+  //           APR PAT    RL   RBEA   RBEN
+  // APR = 0   mandatory because MUM = 1
+  // PAT = XXX because APR = 0
+  // RL = 00   because async mode
+  // RBEA = 000  these match RCSA/RCSN from previous field
+  // RBEN = 000
+  // 0000 0000 0000 0000 0000  0000
+  write_kernel_memory( 0x21b800c, 0x00000000, 4 );
+
+  // EIM_CS0WCR1
+  // 0   0    000100 000   000   000  000  010 000 000  000
+  // WAL WBED WWSC   WADVA WADVN WBEA WBEN WEA WEN WCSA WCSN
+  // WAL = 0       use WADVN
+  // WBED = 0      allow BE during write
+  // WWSC = 000100 4 write wait states
+  // WADVA = 000   same as RADVA
+  // WADVN = 000   this sets WE length to 1 (this value +1)
+  // WBEA = 000    same as RBEA
+  // WBEN = 000    same as RBEN
+  // WEA = 010     2 cycles between beginning of access and WE assertion
+  // WEN = 000     1 cycles to end of WE assertion
+  // WCSA = 000    cycles to CS assertion
+  // WCSN = 000    cycles to CS negation
+  // 1000 0111 1110 0001 0001  0100 0101 0001
+  // 8     7    E    1    1     4    5    1
+  // 0000 0111 0000 0100 0000  1000 0000 0000
+  // 0      7    0   4    0     8    0     0
+  // 0000 0100 0000 0000 0000  0100 0000 0000
+  //  0    4    0    0     0    4     0    0
+
+  write_kernel_memory( 0x21b8010, 0x09080800, 4 );
+  //  write_kernel_memory( 0x21b8010, 0x02040400, 4 );
+
+  // EIM_WCR
+  // BCM = 1   free-run BCLK
+  // GBCD = 0  don't divide the burst clock
+  write_kernel_memory( 0x21b8090, 0x701, 4 );
+
+  // EIM_WIAR 
+  // ACLK_EN = 1
+  write_kernel_memory( 0x21b8094, 0x10, 4 );
+
+  //printf( "done.\n" );
+
+  map_mem(EIM_ADDR_BASE);
+}
+
+void eim_copy(void *dest, void *src, int len)
+{
+    int i;
+    for (i = 0; i < len/2; ++i) {
+        ((uint16_t *)dest)[i] = htons(((uint16_t *)src)[i]);
+    }
+}
+
+/* ---------------- test-case low-level code ---------------- */
+
+int tc_write(off_t offset, const uint8_t *buf, int len)
+{
+    if (debug) {
+        int i;
+        printf("write  [");
+        for (i = 0; i < len; ++i)
+            printf(" %02x", buf[i]);
+        printf(" ]\n");
+    }
+
+    eim_copy((void *)(mem_8 + offset), (void *)buf, len);
+
+    return 0;
+}
+
+int tc_read(off_t offset, uint8_t *buf, int len)
+{
+    eim_copy((void *)buf, (void *)(mem_8 + offset), len);
+
+    if (debug) {
+        int i;
+        printf("read   [");
+        for (i = 0; i < len; ++i)
+            printf(" %02x", buf[i]);
+        printf(" ]\n");
+    }
+
+    return 0;
+}
+
+int tc_expected(off_t offset, const uint8_t *expected, int len)
+{
+    uint8_t *buf;
+    int i;
+
+    buf = malloc(len);
+    if (buf == NULL) {
+        perror("malloc");
+        return 1;
+    }
+    if (debug) {
+        int i;
+        printf("expect [");
+        for (i = 0; i < len; ++i)
+            printf(" %02x", expected[i]);
+        printf(" ]\n");
+    }
+
+    if (tc_read(offset, buf, len) != 0)
+        goto errout;
+
+    for (i = 0; i < len; ++i)
+        if (buf[i] != expected[i]) {
+            fprintf(stderr, "response byte %d: expected 0x%02x, got 0x%02x\n",
+                    i, expected[i], buf[i]);
+            goto errout;
+        }
+
+    free(buf);
+    return 0;
+errout:
+    free(buf);
+    return 1;
+}
+
+int tc_init(off_t offset)
+{
+    uint8_t buf[4] = { 0, 0, 0, CTRL_INIT_CMD };
+
+    return tc_write(offset, buf, 4);
+}
+
+int tc_next(off_t offset)
+{
+    uint8_t buf[4] = { 0, 0, 0, CTRL_NEXT_CMD };
+
+    return tc_write(offset, buf, 4);
+}
+
+int tc_wait(off_t offset, uint8_t status)
+{
+    uint8_t buf[4];
+
+    do {
+        if (tc_read(offset, buf, 4) != 0)
+            return 1;
+    } while (!(buf[3] & status));
+
+    return 0;
+}
+        
+int tc_wait_ready(off_t offset)
+{
+    return tc_wait(offset, STATUS_READY_BIT);
+}
+
+int tc_wait_valid(off_t offset)
+{
+    return tc_wait(offset, STATUS_VALID_BIT);
+}
+
+/* ---------------- SHA-1 test cases ---------------- */
+
+/* TC1: Read name and version from SHA-1 core. */
+int TC1(void)
+{
+    uint8_t name0[4]   = { 0x73, 0x68, 0x61, 0x31 };    /* "sha1" */
+    uint8_t name1[4]   = { 0x20, 0x20, 0x20, 0x20 };    /* "    " */
+    uint8_t version[4] = { 0x30, 0x2e, 0x35, 0x30 };    /* "0.50" */
+
+    printf("TC1: Reading name, type and version words from SHA-1 core.\n");
+
+    return
+        tc_expected(SHA1_ADDR_NAME0, name0, 4) ||
+        tc_expected(SHA1_ADDR_NAME1, name1, 4) ||
+        tc_expected(SHA1_ADDR_VERSION, version, 4);
+}
+
+/* TC2: SHA-1 Single block message test as specified by NIST. */
+int TC2(void)
+{
+    const uint8_t *block = NIST_512_SINGLE;
+    const uint8_t *expected = SHA1_SINGLE_DIGEST;
+
+    printf("TC2: Single block message test for SHA-1.\n");
+
+    return
+        /* Write block to SHA-1. */
+        tc_write(SHA1_ADDR_BLOCK, block, SHA1_BLOCK_LEN) ||
+        /* Start initial block hashing, wait and check status. */
+        tc_init(SHA1_ADDR_CTRL) ||
+        tc_wait_valid(SHA1_ADDR_STATUS) ||
+        /* Extract the digest. */
+        tc_expected(SHA1_ADDR_DIGEST, expected, SHA1_DIGEST_LEN);
+}
+
+/* TC3: SHA-1 Double block message test as specified by NIST. */
+int TC3(void)
+{
+    const uint8_t *block[2] = { NIST_512_DOUBLE0, NIST_512_DOUBLE1 };
+    static const uint8_t block0_expected[] =
+        { 0xF4, 0x28, 0x68, 0x18, 0xC3, 0x7B, 0x27, 0xAE,
+          0x04, 0x08, 0xF5, 0x81, 0x84, 0x67, 0x71, 0x48,
+          0x4A, 0x56, 0x65, 0x72 };
+    const uint8_t *expected = SHA1_DOUBLE_DIGEST;
+
+    printf("TC3: Double block message test for SHA-1.\n");
+
+    return
+        /* Write first block to SHA-1. */
+        tc_write(SHA1_ADDR_BLOCK, block[0], SHA1_BLOCK_LEN) ||
+        /* Start initial block hashing, wait and check status. */
+        tc_init(SHA1_ADDR_CTRL) ||
+        tc_wait_valid(SHA1_ADDR_STATUS) ||
+        /* Extract the first digest. */
+        tc_expected(SHA1_ADDR_DIGEST, block0_expected, SHA1_DIGEST_LEN) ||
+        /* Write second block to SHA-1. */
+        tc_write(SHA1_ADDR_BLOCK, block[1], SHA1_BLOCK_LEN) ||
+        /* Start next block hashing, wait and check status. */
+        tc_next(SHA1_ADDR_CTRL) ||
+        tc_wait_valid(SHA1_ADDR_STATUS) ||
+        /* Extract the second digest. */
+        tc_expected(SHA1_ADDR_DIGEST, expected, SHA1_DIGEST_LEN);
+}
+
+/* ---------------- SHA-256 test cases ---------------- */
+
+/* TC4: Read name and version from SHA-256 core. */
+int TC4(void)
+{
+    uint8_t name0[4]   = { 0x73, 0x68, 0x61, 0x32 };    /* "sha2" */
+    uint8_t name1[4]   = { 0x2d, 0x32, 0x35, 0x36 };    /* "-256" */
+    uint8_t version[4] = { 0x30, 0x2e, 0x38, 0x30 };    /* "0.80" */
+
+    printf("TC4: Reading name, type and version words from SHA-256 core.\n");
+
+    return
+        tc_expected(SHA256_ADDR_NAME0, name0, 4) ||
+        tc_expected(SHA256_ADDR_NAME1, name1, 4) ||
+        tc_expected(SHA256_ADDR_VERSION, version, 4);
+}
+
+/* TC5: SHA-256 Single block message test as specified by NIST. */
+int TC5()
+{
+    const uint8_t *block = NIST_512_SINGLE;
+    const uint8_t *expected = SHA256_SINGLE_DIGEST;
+
+    printf("TC5: Single block message test for SHA-256.\n");
+
+    return
+        /* Write block to SHA-256. */
+        tc_write(SHA256_ADDR_BLOCK, block, SHA256_BLOCK_LEN) ||
+        /* Start initial block hashing, wait and check status. */
+        tc_init(SHA256_ADDR_CTRL) ||
+        tc_wait_valid(SHA256_ADDR_STATUS) ||
+        /* Extract the digest. */
+        tc_expected(SHA256_ADDR_DIGEST, expected, SHA256_DIGEST_LEN);
+}
+
+/* TC6: SHA-256 Double block message test as specified by NIST. */
+int TC6()
+{
+    const uint8_t *block[2] = { NIST_512_DOUBLE0, NIST_512_DOUBLE1 };
+    static const uint8_t block0_expected[] = 
+        { 0x85, 0xE6, 0x55, 0xD6, 0x41, 0x7A, 0x17, 0x95,
+          0x33, 0x63, 0x37, 0x6A, 0x62, 0x4C, 0xDE, 0x5C,
+          0x76, 0xE0, 0x95, 0x89, 0xCA, 0xC5, 0xF8, 0x11,
+          0xCC, 0x4B, 0x32, 0xC1, 0xF2, 0x0E, 0x53, 0x3A };
+    const uint8_t *expected = SHA256_DOUBLE_DIGEST;
+
+    printf("TC6: Double block message test for SHA-256.\n");
+
+    return
+        /* Write first block to SHA-256. */
+        tc_write(SHA256_ADDR_BLOCK, block[0], SHA256_BLOCK_LEN) ||
+        /* Start initial block hashing, wait and check status. */
+        tc_init(SHA256_ADDR_CTRL) ||
+        tc_wait_valid(SHA256_ADDR_STATUS) ||
+        /* Extract the first digest. */
+        tc_expected(SHA256_ADDR_DIGEST, block0_expected, SHA256_DIGEST_LEN) ||
+        /* Write second block to SHA-256. */
+        tc_write(SHA256_ADDR_BLOCK, block[1], SHA256_BLOCK_LEN) ||
+        /* Start next block hashing, wait and check status. */
+        tc_next(SHA256_ADDR_CTRL) ||
+        tc_wait_valid(SHA256_ADDR_STATUS) ||
+        /* Extract the second digest. */
+        tc_expected(SHA256_ADDR_DIGEST, expected, SHA256_DIGEST_LEN);
+}
+
+/* TC7: SHA-256 Huge message test. */
+int TC7()
+{
+    static const uint8_t block[] =
+        { 0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f,
+          0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f,
+          0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f,
+          0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f };
+
+    /* final digest after 1000 iterations */
+    static const uint8_t expected[] = 
+        { 0x76, 0x38, 0xf3, 0xbc, 0x50, 0x0d, 0xd1, 0xa6,
+          0x58, 0x6d, 0xd4, 0xd0, 0x1a, 0x15, 0x51, 0xaf,
+          0xd8, 0x21, 0xd2, 0x35, 0x2f, 0x91, 0x9e, 0x28,
+          0xd5, 0x84, 0x2f, 0xab, 0x03, 0xa4, 0x0f, 0x2a };
+
+    int i, n = 1000;
+
+    printf("TC7: Message with %d blocks test for SHA-256.\n", n);
+
+    /* Write block data to SHA-256. */
+    if (tc_write(SHA256_ADDR_BLOCK, block, SHA256_BLOCK_LEN))
+	return 1;
+
+    /* Start initial block hashing, wait and check status. */
+    if (tc_init(SHA256_ADDR_CTRL) ||
+        tc_wait_ready(SHA256_ADDR_STATUS))
+        return 1;
+
+    /* First block done. Do the rest. */
+    for (i = 1; i < n; ++i) {
+        /* Start next block hashing, wait and check status. */
+        if (tc_next(SHA256_ADDR_CTRL) ||
+            tc_wait_ready(SHA256_ADDR_STATUS))
+            return 1;
+    }
+
+    /* XXX valid is probably set at the same time as ready */
+    if (tc_wait_valid(SHA256_ADDR_STATUS))
+        return 1;
+    /* Extract the final digest. */
+    return tc_expected(SHA256_ADDR_DIGEST, expected, SHA256_DIGEST_LEN);
+}
+
+/* ---------------- SHA-512 test cases ---------------- */
+
+/* TC8: Read name and version from SHA-512 core. */
+int TC8()
+{
+    uint8_t name0[4]   = { 0x73, 0x68, 0x61, 0x32 };	/* "sha2" */
+    uint8_t name1[4]   = { 0x2d, 0x35, 0x31, 0x32 };	/* "-512" */
+    uint8_t version[4] = { 0x30, 0x2e, 0x38, 0x30 };	/* "0.80" */
+
+    printf("TC8: Reading name, type and version words from SHA-512 core.\n");
+
+    return
+        tc_expected(SHA512_ADDR_NAME0, name0, 4) ||
+        tc_expected(SHA512_ADDR_NAME1, name1, 4) ||
+        tc_expected(SHA512_ADDR_VERSION, version, 4);
+}
+
+/* TC9: SHA-512 Single block message test as specified by NIST.
+   We do this for all modes. */
+int tc9(int mode, const uint8_t *expected, int digest_len)
+{
+    const uint8_t *block = NIST_1024_SINGLE;
+    uint8_t init[4] = { 0, 0, 0, CTRL_INIT_CMD + mode };
+
+    return
+        /* Write block to SHA-512. */
+        tc_write(SHA512_ADDR_BLOCK, block, SHA512_BLOCK_LEN) ||
+        /* Start initial block hashing, wait and check status. */
+        tc_write(SHA512_ADDR_CTRL, init, 4) ||
+        tc_wait_valid(SHA512_ADDR_STATUS) ||
+        /* Extract the digest. */
+        tc_expected(SHA512_ADDR_DIGEST, expected, digest_len);
+}
+
+int TC9()
+{
+    printf("TC9-1: Single block message test for SHA-512/224.\n");
+    if (tc9(MODE_SHA_512_224, SHA512_224_SINGLE_DIGEST, SHA512_224_DIGEST_LEN) != 0)
+        return 1;
+
+    printf("TC9-2: Single block message test for SHA-512/256.\n");
+    if (tc9(MODE_SHA_512_256, SHA512_256_SINGLE_DIGEST, SHA512_256_DIGEST_LEN) != 0)
+        return 1;
+
+    printf("TC9-3: Single block message test for SHA-384.\n");
+    if (tc9(MODE_SHA_384, SHA384_SINGLE_DIGEST, SHA384_DIGEST_LEN) != 0)
+        return 1;
+
+    printf("TC9-4: Single block message test for SHA-512.\n");
+    if (tc9(MODE_SHA_512, SHA512_SINGLE_DIGEST, SHA512_DIGEST_LEN) != 0)
+        return 1;
+
+    return 0;
+}
+
+/* TC10: SHA-512 Double block message test as specified by NIST.
+   We do this for all modes. */
+int tc10(int mode, const uint8_t *expected, int digest_len)
+{
+    const uint8_t *block[2] = { NIST_1024_DOUBLE0, NIST_1024_DOUBLE1 };
+    uint8_t init[4] = { 0, 0, 0, CTRL_INIT_CMD + mode };
+    uint8_t next[4] = { 0, 0, 0, CTRL_NEXT_CMD + mode };
+
+    return
+        /* Write first block to SHA-512. */
+        tc_write(SHA512_ADDR_BLOCK, block[0], SHA512_BLOCK_LEN) ||
+        /* Start initial block hashing, wait and check status. */
+        tc_write(SHA512_ADDR_CTRL, init, 4) ||
+        tc_wait_ready(SHA512_ADDR_STATUS) ||
+        /* Write second block to SHA-512. */
+        tc_write(SHA512_ADDR_BLOCK, block[1], SHA512_BLOCK_LEN) ||
+        /* Start next block hashing, wait and check status. */
+        tc_write(SHA512_ADDR_CTRL, next, 4) ||
+        tc_wait_valid(SHA512_ADDR_STATUS) ||
+        /* Extract the digest. */
+        tc_expected(SHA512_ADDR_DIGEST, expected, digest_len);
+}
+
+int TC10()
+{
+    printf("TC10-1: Double block message test for SHA-512/224.\n");
+    if (tc10(MODE_SHA_512_224, SHA512_224_DOUBLE_DIGEST, SHA512_224_DIGEST_LEN) != 0)
+        return 1;
+
+    printf("TC10-2: Double block message test for SHA-512/256.\n");
+    if (tc10(MODE_SHA_512_256, SHA512_256_DOUBLE_DIGEST, SHA512_256_DIGEST_LEN) != 0)
+        return 1;
+
+    printf("TC10-3: Double block message test for SHA-384.\n");
+    if (tc10(MODE_SHA_384, SHA384_DOUBLE_DIGEST, SHA384_DIGEST_LEN) != 0)
+        return 1;
+
+    printf("TC10-4: Double block message test for SHA-512.\n");
+    if (tc10(MODE_SHA_512, SHA512_DOUBLE_DIGEST, SHA512_DIGEST_LEN) != 0)
+        return 1;
+
+    return 0;
+}
+
+/* ---------------- main ---------------- */
+
+int main(int argc, char *argv[])
+{
+    typedef int (*tcfp)(void);
+    tcfp sha1_tests[] = { TC1, TC2, TC3 };
+    tcfp sha256_tests[] = { TC4, TC5, TC6, TC7 };
+    tcfp sha512_tests[] = { TC8, TC9, TC10 };
+    tcfp all_tests[] = { TC1, TC2, TC3, TC4, TC5, TC6, TC7, TC8, TC9, TC10 };
+
+    char *usage = "Usage: %s [-h] [-d] tc...\n";
+    int i, j, opt;
+
+    while ((opt = getopt(argc, argv, "h?d")) != -1) {
+        switch (opt) {
+        case 'h':
+        case '?':
+            printf(usage, argv[0]);
+            return 0;
+        case 'd':
+            debug = 1;
+            break;
+        default:
+            fprintf(stderr, usage, argv[0]);
+            return 1;
+        }
+    }
+
+    setup_fpga();
+
+    /* no args == run all tests */
+    if (optind >= argc) {
+        for (j = 0; j < sizeof(all_tests)/sizeof(all_tests[0]); ++j)
+            if (all_tests[j]() != 0)
+                return 1;
+        return 0;
+    }
+
+    for (i = optind; i < argc; ++i) {
+        if (strcmp(argv[i], "sha1") == 0) {
+            for (j = 0; j < sizeof(sha1_tests)/sizeof(sha1_tests[0]); ++j)
+                if (sha1_tests[j]() != 0)
+                    return 1;
+        }
+        else if (strcmp(argv[i], "sha256") == 0) {
+            for (j = 0; j < sizeof(sha256_tests)/sizeof(sha256_tests[0]); ++j)
+                if (sha256_tests[j]() != 0)
+                    return 1;
+        }
+        else if (strcmp(argv[i], "sha512") == 0) {
+            for (j = 0; j < sizeof(sha512_tests)/sizeof(sha512_tests[0]); ++j)
+                if (sha512_tests[j]() != 0)
+                    return 1;
+        }
+        else if (strcmp(argv[i], "all") == 0) {
+            for (j = 0; j < sizeof(all_tests)/sizeof(all_tests[0]); ++j)
+                if (all_tests[j]() != 0)
+                    return 1;
+        }
+        else if (isdigit(argv[i][0]) &&
+                 (((j = atoi(argv[i])) > 0) &&
+                  (j <= sizeof(all_tests)/sizeof(all_tests[0])))) {
+            if (all_tests[j - 1]() != 0)
+                return 1;
+        }
+        else {
+            fprintf(stderr, "unknown test case %s\n", argv[i]);
+            return 1;
+        }
+    }
+
+    return 0;
+}
diff --git a/src/sw/hash_tester_i2c.c b/src/sw/hash_tester_i2c.c
new file mode 100644
index 0000000..04f6c6d
--- /dev/null
+++ b/src/sw/hash_tester_i2c.c
@@ -0,0 +1,981 @@
+/* 
+ * hash_tester.c
+ * --------------
+ * This program sends several commands to the coretest_hashes subsystem
+ * in order to verify the SHA-1, SHA-256 and SHA-512/x hash function
+ * cores.
+ *
+ * Note: This version of the program talks to the FPGA over an I2C bus.
+ *
+ * The single and dual block test cases are taken from the
+ * NIST KAT document:
+ * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf
+ *
+ * 
+ * Authors: Joachim Strömbergson, Paul Selkirk
+ * Copyright (c) 2014, SUNET
+ * 
+ * Redistribution and use in source and binary forms, with or 
+ * without modification, are permitted provided that the following 
+ * conditions are met: 
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. 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. 
+ * 
+ * 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 OWNER 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.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+/* I2C configuration */
+#define I2C_dev  "/dev/i2c-2"
+#define I2C_addr 0x0f
+
+/* command codes */
+#define SOC       0x55
+#define EOC       0xaa
+#define READ_CMD  0x10
+#define WRITE_CMD 0x11
+#define RESET_CMD 0x01
+
+/* response codes */
+#define SOR       0xaa
+#define EOR       0x55
+#define READ_OK   0x7f
+#define WRITE_OK  0x7e
+#define RESET_OK  0x7d
+#define UNKNOWN   0xfe
+#define ERROR     0xfd
+
+/* addresses and codes common to all hash cores */
+#define ADDR_NAME0              0x00
+#define ADDR_NAME1              0x01
+#define ADDR_VERSION            0x02
+#define ADDR_CTRL               0x08
+#define CTRL_INIT_CMD           1
+#define CTRL_NEXT_CMD           2
+#define ADDR_STATUS             0x09
+#define STATUS_READY_BIT        1
+#define STATUS_VALID_BIT        2
+
+/* addresses and codes for the specific hash cores */
+#define SHA1_ADDR_PREFIX        0x10
+#define SHA1_ADDR_BLOCK         0x10
+#define SHA1_BLOCK_LEN          16
+#define SHA1_ADDR_DIGEST        0x20
+#define SHA1_DIGEST_LEN         5
+
+#define SHA256_ADDR_PREFIX      0x20
+#define SHA256_ADDR_BLOCK       0x10
+#define SHA256_BLOCK_LEN        16
+#define SHA256_ADDR_DIGEST      0x20
+#define SHA256_DIGEST_LEN       8
+
+#define SHA512_ADDR_PREFIX      0x30
+#define SHA512_CTRL_MODE_LOW    2
+#define SHA512_CTRL_MODE_HIGH   3
+#define SHA512_ADDR_BLOCK       0x10
+#define SHA512_BLOCK_LEN        32
+#define SHA512_ADDR_DIGEST      0x40
+#define SHA512_DIGEST_LEN       16
+#define MODE_SHA_512_224        0
+#define MODE_SHA_512_256        1
+#define MODE_SHA_384            2
+#define MODE_SHA_512            3
+
+int i2cfd;
+int debug = 0;
+
+/* SHA-1/SHA-256 One Block Message Sample
+   Input Message: "abc" */
+const uint32_t NIST_512_SINGLE[] =
+{ 0x61626380, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000018 };
+
+const uint32_t SHA1_SINGLE_DIGEST[] =
+{ 0xa9993e36, 0x4706816a, 0xba3e2571, 0x7850c26c,
+  0x9cd0d89d };
+
+const uint32_t SHA256_SINGLE_DIGEST[] =
+{ 0xBA7816BF, 0x8F01CFEA, 0x414140DE, 0x5DAE2223,
+  0xB00361A3, 0x96177A9C, 0xB410FF61, 0xF20015AD };
+
+/* SHA-1/SHA-256 Two Block Message Sample
+   Input Message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" */
+const uint32_t NIST_512_DOUBLE0[] =
+{ 0x61626364, 0x62636465, 0x63646566, 0x64656667,
+  0x65666768, 0x66676869, 0x6768696A, 0x68696A6B,
+  0x696A6B6C, 0x6A6B6C6D, 0x6B6C6D6E, 0x6C6D6E6F,
+  0x6D6E6F70, 0x6E6F7071, 0x80000000, 0x00000000 };
+const uint32_t NIST_512_DOUBLE1[] =
+{ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x000001C0 };
+
+const uint32_t SHA1_DOUBLE_DIGEST[] =
+{ 0x84983E44, 0x1C3BD26E, 0xBAAE4AA1, 0xF95129E5,
+  0xE54670F1 };
+
+const uint32_t SHA256_DOUBLE_DIGEST[] =
+{ 0x248D6A61, 0xD20638B8, 0xE5C02693, 0x0C3E6039,
+  0xA33CE459, 0x64FF2167, 0xF6ECEDD4, 0x19DB06C1 };
+
+/* SHA-512 One Block Message Sample
+   Input Message: "abc" */
+const uint32_t NIST_1024_SINGLE[] =
+{ 0x61626380, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000018 };
+
+const uint32_t SHA512_224_SINGLE_DIGEST[] =
+{ 0x4634270f, 0x707b6a54, 0xdaae7530, 0x460842e2,
+  0x0e37ed26, 0x5ceee9a4, 0x3e8924aa };
+const uint32_t SHA512_256_SINGLE_DIGEST[] =
+{ 0x53048e26, 0x81941ef9, 0x9b2e29b7, 0x6b4c7dab,
+  0xe4c2d0c6, 0x34fc6d46, 0xe0e2f131, 0x07e7af23 };
+const uint32_t SHA384_SINGLE_DIGEST[] =
+{ 0xcb00753f, 0x45a35e8b, 0xb5a03d69, 0x9ac65007,
+  0x272c32ab, 0x0eded163, 0x1a8b605a, 0x43ff5bed,
+  0x8086072b, 0xa1e7cc23, 0x58baeca1, 0x34c825a7 };
+const uint32_t SHA512_SINGLE_DIGEST[] =
+{ 0xddaf35a1, 0x93617aba, 0xcc417349, 0xae204131,
+  0x12e6fa4e, 0x89a97ea2, 0x0a9eeee6, 0x4b55d39a,
+  0x2192992a, 0x274fc1a8, 0x36ba3c23, 0xa3feebbd,
+  0x454d4423, 0x643ce80e, 0x2a9ac94f, 0xa54ca49f };
+
+/* SHA-512 Two Block Message Sample
+   Input Message: "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+   "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" */
+const uint32_t NIST_1024_DOUBLE0[] =
+{ 0x61626364, 0x65666768, 0x62636465, 0x66676869,
+  0x63646566, 0x6768696a, 0x64656667, 0x68696a6b,
+  0x65666768, 0x696a6b6c, 0x66676869, 0x6a6b6c6d,
+  0x6768696a, 0x6b6c6d6e, 0x68696a6b, 0x6c6d6e6f,
+  0x696a6b6c, 0x6d6e6f70, 0x6a6b6c6d, 0x6e6f7071,
+  0x6b6c6d6e, 0x6f707172, 0x6c6d6e6f, 0x70717273,
+  0x6d6e6f70, 0x71727374, 0x6e6f7071, 0x72737475,
+  0x80000000, 0x00000000, 0x00000000, 0x00000000 };
+const uint32_t NIST_1024_DOUBLE1[] =
+{ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000000,
+  0x00000000, 0x00000000, 0x00000000, 0x00000380 };
+
+const uint32_t SHA512_224_DOUBLE_DIGEST[] = 
+{ 0x23fec5bb, 0x94d60b23, 0x30819264, 0x0b0c4533,
+  0x35d66473, 0x4fe40e72, 0x68674af9 };
+const uint32_t SHA512_256_DOUBLE_DIGEST[] =
+{ 0x3928e184, 0xfb8690f8, 0x40da3988, 0x121d31be,
+  0x65cb9d3e, 0xf83ee614, 0x6feac861, 0xe19b563a };
+const uint32_t SHA384_DOUBLE_DIGEST[] =
+{ 0x09330c33, 0xf71147e8, 0x3d192fc7, 0x82cd1b47,
+  0x53111b17, 0x3b3b05d2, 0x2fa08086, 0xe3b0f712,
+  0xfcc7c71a, 0x557e2db9, 0x66c3e9fa, 0x91746039 };
+const uint32_t SHA512_DOUBLE_DIGEST[] =
+{ 0x8e959b75, 0xdae313da, 0x8cf4f728, 0x14fc143f,
+  0x8f7779c6, 0xeb9f7fa1, 0x7299aead, 0xb6889018,
+  0x501d289e, 0x4900f7e4, 0x331b99de, 0xc4b5433a,
+  0xc7d329ee, 0xb6dd2654, 0x5e96e55b, 0x874be909 };
+
+/* ---------------- I2C low-level code ---------------- */
+int i2c_setup(char *dev, int addr)
+{
+    i2cfd = open(dev, O_RDWR);
+    if (i2cfd < 0) {
+	fprintf(stderr, "Unable to open %s: ", dev);
+	perror("");
+	i2cfd = 0;
+	return 1;
+    }
+
+    if (ioctl(i2cfd, I2C_SLAVE, addr) < 0) {
+	fprintf(stderr, "Unable to set I2C slave device 0x%02x: ", addr);
+	perror("");
+	return 1;
+    }
+
+    return 0;
+}
+
+int i2c_write(uint8_t *buf, int len)
+{
+    if (debug) {
+	int i;
+	printf("write [");
+	for (i = 0; i < len; ++i)
+	    printf(" %02x", buf[i]);
+	printf(" ]\n");
+    }
+
+    if (write(i2cfd, buf, len) != len) {
+	perror("i2c write failed");
+	return 1;
+    }
+
+    return 0;
+}
+
+int i2c_read(uint8_t *b)
+{
+    /* read() on the i2c device only returns one byte at a time,
+     * and tc_get_resp() needs to parse the response one byte at a time
+     */
+    if (read(i2cfd, b, 1) != 1) {
+	perror("i2c read failed");
+	return 1;
+    }
+
+    return 0;
+}
+
+/* ---------------- test-case low-level code ---------------- */
+int tc_send_write_cmd(uint8_t addr0, uint8_t addr1, uint32_t data)
+{
+    uint8_t buf[9];
+
+    buf[0] = SOC;
+    buf[1] = WRITE_CMD;
+    buf[2] = addr0;
+    buf[3] = addr1;
+    buf[4] = (data >> 24) & 0xff;
+    buf[5] = (data >> 16) & 0xff;
+    buf[6] = (data >> 8) & 0xff;
+    buf[7] = data & 0xff;
+    buf[8] = EOC;
+
+    return i2c_write(buf, sizeof(buf));
+}
+
+int tc_send_read_cmd(uint8_t addr0, uint8_t addr1)
+{
+    uint8_t buf[5];
+
+    buf[0] = SOC;
+    buf[1] = READ_CMD;
+    buf[2] = addr0;
+    buf[3] = addr1;
+    buf[4] = EOC;
+
+    return i2c_write(buf, sizeof(buf));
+}
+
+int tc_get_resp(uint8_t *buf, int len)
+{
+    int i;
+
+    for (i = 0; i < len; ++i) {
+	if (i2c_read(&buf[i]) != 0)
+	    return 1;
+	if ((i == 0) && (buf[i] != SOR)) {
+	    /* we've gotten out of sync, and there's probably nothing we can do */
+	    fprintf(stderr, "response byte 0: expected 0x%02x (SOR), got 0x%02x\n",
+		    SOR, buf[0]);
+	    return 1;
+	}
+	else if (i == 1) {	/* response code */
+	    switch (buf[i]) {
+	    case READ_OK:
+		len = 9;
+		break;
+	    case WRITE_OK:
+		len = 5;
+		break;
+	    case RESET_OK:
+		len = 3;
+		break;
+	    case ERROR:
+	    case UNKNOWN:
+		len = 4;
+		break;
+	    default:
+		/* we've gotten out of sync, and there's probably nothing we can do */
+		fprintf(stderr, "unknown response code 0x%02x\n", buf[i]);
+		return 1;
+	    }
+	}
+    }
+
+    if (debug) {
+	printf("read  [");
+	for (i = 0; i < len; ++i)
+	    printf(" %02x", buf[i]);
+	printf(" ]\n");
+    }
+
+    return 0;
+}
+
+int tc_get_expected(uint8_t *expected, int len)
+{
+    uint8_t buf[9];
+    int i;
+
+    if (tc_get_resp(buf, sizeof(buf)) != 0)
+	return 1;
+
+    for (i = 0; i < len; ++i) {
+	if (buf[i] != expected[i]) {
+	    fprintf(stderr, "response byte %d: expected 0x%02x, got 0x%02x\n",
+		    i, expected[i], buf[i]);
+	    return 1;
+	}
+    }
+
+    return 0;
+}
+
+int tc_get_write_resp(uint8_t addr0, uint8_t addr1)
+{
+    uint8_t expected[5];
+
+    expected[0] = SOR;
+    expected[1] = WRITE_OK;
+    expected[2] = addr0;
+    expected[3] = addr1;
+    expected[4] = EOR;
+
+    return tc_get_expected(expected, sizeof(expected));
+}
+
+int tc_get_read_resp(uint8_t addr0, uint8_t addr1, uint32_t data)
+{
+    uint8_t expected[9];
+
+    expected[0] = SOR;
+    expected[1] = READ_OK;
+    expected[2] = addr0;
+    expected[3] = addr1;
+    expected[4] = (data >> 24) & 0xff;
+    expected[5] = (data >> 16) & 0xff;
+    expected[6] = (data >> 8) & 0xff;
+    expected[7] = data & 0xff;
+    expected[8] = EOR;
+
+    return tc_get_expected(expected, sizeof(expected));
+}
+
+int tc_write(uint8_t addr0, uint8_t addr1, uint32_t data)
+{
+    return (tc_send_write_cmd(addr0, addr1, data) ||
+	    tc_get_write_resp(addr0, addr1));
+}
+
+int tc_read(uint8_t addr0, uint8_t addr1, uint32_t data)
+{
+    return (tc_send_read_cmd(addr0, addr1) ||
+	    tc_get_read_resp(addr0, addr1, data));
+}
+
+int tc_init(uint8_t addr0)
+{
+    return tc_write(addr0, ADDR_CTRL, CTRL_INIT_CMD);
+}
+
+int tc_next(uint8_t addr0)
+{
+    return tc_write(addr0, ADDR_CTRL, CTRL_NEXT_CMD);
+}
+
+int tc_wait(uint8_t addr0, uint8_t status)
+{
+    uint8_t buf[9];
+
+    do {
+	if (tc_send_read_cmd(addr0, ADDR_STATUS) != 0)
+	    return 1;
+	if (tc_get_resp(buf, 9) != 0)
+	    return 1;
+	if (buf[1] != READ_OK)
+	    return 1;
+    } while ((buf[7] & status) != status);
+
+    return 0;
+}
+
+int tc_wait_ready(uint8_t addr0)
+{
+    return tc_wait(addr0, STATUS_READY_BIT);
+}
+
+int tc_wait_valid(uint8_t addr0)
+{
+    return tc_wait(addr0, STATUS_VALID_BIT);
+}
+
+/* ---------------- SHA-1 test cases ---------------- */
+
+int sha1_read(uint8_t addr, uint32_t data)
+{
+    return tc_read(SHA1_ADDR_PREFIX, addr, data);
+}
+
+int sha1_write(uint8_t addr, uint32_t data)
+{
+    return tc_write(SHA1_ADDR_PREFIX, addr, data);
+}
+
+int sha1_init(void)
+{
+    return tc_init(SHA1_ADDR_PREFIX);
+}
+
+int sha1_next(void)
+{
+    return tc_next(SHA1_ADDR_PREFIX);
+}
+
+int sha1_wait_ready(void)
+{
+    return tc_wait_ready(SHA1_ADDR_PREFIX);
+}
+
+int sha1_wait_valid(void)
+{
+    return tc_wait_valid(SHA1_ADDR_PREFIX);
+}
+
+/* TC1: Read name and version from SHA-1 core. */
+int TC1(void)
+{
+    uint32_t name0   = 0x73686131;	/* "sha1" */
+    uint32_t name1   = 0x20202020;	/* "    " */
+    uint32_t version = 0x302e3530;	/* "0.50" */
+
+    printf("TC1: Reading name, type and version words from SHA-1 core.\n");
+
+    return 
+	sha1_read(ADDR_NAME0, name0) ||
+	sha1_read(ADDR_NAME1, name1) ||
+	sha1_read(ADDR_VERSION, version);
+}
+
+/* TC2: SHA-1 Single block message test as specified by NIST. */
+int TC2(void)
+{
+    const uint32_t *block = NIST_512_SINGLE;
+    const uint32_t *expected = SHA1_SINGLE_DIGEST;
+    int i;
+
+    printf("TC2: Single block message test for SHA-1.\n");
+
+    /* Write block to SHA-1. */
+    for (i = 0; i < SHA1_BLOCK_LEN; ++i) {
+	if (sha1_write(SHA1_ADDR_BLOCK + i, block[i]) != 0)
+	    return 1;
+    }
+
+    /* Start initial block hashing, wait and check status. */
+    if ((sha1_init() != 0) || (sha1_wait_valid() != 0))
+	return 1;
+
+    /* Extract the digest. */
+    for (i = 0; i < SHA1_DIGEST_LEN; ++i) {
+	if (sha1_read(SHA1_ADDR_DIGEST + i, expected[i]) != 0)
+	    return 1;
+    }
+
+    return 0;
+}
+
+/* TC3: SHA-1 Double block message test as specified by NIST. */
+int TC3(void)
+{
+    const uint32_t *block[2] = { NIST_512_DOUBLE0, NIST_512_DOUBLE1 };
+    static const uint32_t block0_expected[] =
+	{ 0xF4286818, 0xC37B27AE, 0x0408F581, 0x84677148, 0x4A566572 };
+    const uint32_t *expected = SHA1_DOUBLE_DIGEST;
+    int i;
+
+    printf("TC3: Double block message test for SHA-1.\n");
+
+    /* Write first block to SHA-1. */
+    for (i = 0; i < SHA1_BLOCK_LEN; ++i) {
+	if (sha1_write(SHA1_ADDR_BLOCK + i, block[0][i]) != 0)
+	    return 1;
+    }
+
+    /* Start initial block hashing, wait and check status. */
+    if ((sha1_init() != 0) || (sha1_wait_valid() != 0))
+	return 1;
+
+    /* Extract the first digest. */
+    for (i = 0; i < SHA1_DIGEST_LEN; ++i) {
+	if (sha1_read(SHA1_ADDR_DIGEST + i, block0_expected[i]) != 0)
+	    return 1;
+    }
+
+    /* Write second block to SHA-1. */
+    for (i = 0; i < SHA1_BLOCK_LEN; ++i) {
+	if (sha1_write(SHA1_ADDR_BLOCK + i, block[1][i]) != 0)
+	    return 1;
+    }
+
+    /* Start next block hashing, wait and check status. */
+    if ((sha1_next() != 0) || (sha1_wait_valid() != 0))
+	return 1;
+
+    /* Extract the second digest. */
+    for (i = 0; i < SHA1_DIGEST_LEN; ++i) {
+	if (sha1_read(SHA1_ADDR_DIGEST + i, expected[i]) != 0)
+	    return 1;
+    }
+
+    return 0;
+}
+
+/* ---------------- SHA-256 test cases ---------------- */
+
+int sha256_read(uint8_t addr, uint32_t data)
+{
+    return tc_read(SHA256_ADDR_PREFIX, addr, data);
+}
+
+int sha256_write(uint8_t addr, uint32_t data)
+{
+    return tc_write(SHA256_ADDR_PREFIX, addr, data);
+}
+
+int sha256_init(void)
+{
+    return tc_init(SHA256_ADDR_PREFIX);
+}
+
+int sha256_next(void)
+{
+    return tc_next(SHA256_ADDR_PREFIX);
+}
+
+int sha256_wait_ready(void)
+{
+    return tc_wait_ready(SHA256_ADDR_PREFIX);
+}
+
+int sha256_wait_valid(void)
+{
+    return tc_wait_valid(SHA256_ADDR_PREFIX);
+}
+
+/* TC4: Read name and version from SHA-256 core. */
+int TC4(void)
+{
+    uint32_t name0     = 0x73686132;	/* "sha2" */
+    uint32_t name1     = 0x2d323536;	/* "-256" */
+    uint32_t version   = 0x302e3830;	/* "0.80" */
+
+    printf("TC4: Reading name, type and version words from SHA-256 core.\n");
+
+    return
+	sha256_read(ADDR_NAME0, name0) ||
+	sha256_read(ADDR_NAME1, name1) ||
+	sha256_read(ADDR_VERSION, version);
+}
+
+/* TC5: SHA-256 Single block message test as specified by NIST. */
+int TC5(void)
+{
+    const uint32_t *block = NIST_512_SINGLE;
+    const uint32_t *expected = SHA256_SINGLE_DIGEST;
+    int i;
+
+    printf("TC5: Single block message test for SHA-256.\n");
+
+    /* Write block to SHA-256. */
+    for (i = 0; i < SHA256_BLOCK_LEN; ++i) {
+	if (sha256_write(SHA256_ADDR_BLOCK + i, block[i]) != 0)
+	    return 1;
+    }
+
+    /* Start initial block hashing, wait and check status. */
+    if ((sha256_init() != 0) || (sha256_wait_valid() != 0))
+	return 1;
+
+    /* Extract the digest. */
+    for (i = 0; i < SHA256_DIGEST_LEN; ++i) {
+	if (sha256_read(SHA256_ADDR_DIGEST + i, expected[i]) != 0)
+	    return 1;
+    }
+
+
+    return 0;
+}
+
+/* TC6: SHA-1 Double block message test as specified by NIST. */
+int TC6(void)
+{
+    const uint32_t *block[2] = { NIST_512_DOUBLE0, NIST_512_DOUBLE1 };
+    static const uint32_t block0_expected[] = 
+	{ 0x85E655D6, 0x417A1795, 0x3363376A, 0x624CDE5C,
+	  0x76E09589, 0xCAC5F811, 0xCC4B32C1, 0xF20E533A };
+    const uint32_t *expected = SHA256_DOUBLE_DIGEST;
+    int i;
+
+    printf("TC6: Double block message test for SHA-256.\n");
+
+    /* Write first block to SHA-256. */
+    for (i = 0; i < SHA256_BLOCK_LEN; ++i) {
+	if (sha256_write(SHA256_ADDR_BLOCK + i, block[0][i]) != 0)
+	    return 1;
+    }
+
+    /* Start initial block hashing, wait and check status. */
+    if ((sha256_init() != 0) || (sha256_wait_valid() != 0))
+	return 1;
+
+    /* Extract the first digest. */
+    for (i = 0; i < SHA256_DIGEST_LEN; ++i) {
+	if (sha256_read(SHA256_ADDR_DIGEST + i, block0_expected[i]) != 0)
+	    return 1;
+    }
+
+    /* Write second block to SHA-256. */
+    for (i = 0; i < SHA256_BLOCK_LEN; ++i) {
+	if (sha256_write(SHA256_ADDR_BLOCK + i, block[1][i]) != 0)
+	    return 1;
+    }
+
+    /* Start next block hashing, wait and check status. */
+    if ((sha256_next() != 0) || (sha256_wait_valid() != 0))
+	return 1;
+
+    /* Extract the second digest. */
+    for (i = 0; i < SHA256_DIGEST_LEN; ++i) {
+	if (sha256_read(SHA256_ADDR_DIGEST + i, expected[i]) != 0)
+	    return 1;
+    }
+
+    return 0;
+}
+
+/* TC7: SHA-256 Huge message test. */
+int TC7(void)
+{
+    static const uint32_t block[] =
+	{ 0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f,
+	  0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f,
+	  0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f,
+	  0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f };
+
+    /* final digest after 1000 iterations */
+    static const uint32_t expected[] = 
+	{ 0x7638f3bc, 0x500dd1a6, 0x586dd4d0, 0x1a1551af,
+	  0xd821d235, 0x2f919e28, 0xd5842fab, 0x03a40f2a };
+
+    int i, n = 1000;
+
+    printf("TC7: Message with %d blocks test for SHA-256.\n", n);
+
+    /* Write first block to SHA-256. */
+    for (i = 0; i < SHA256_BLOCK_LEN; ++i) {
+	if (sha256_write( SHA256_ADDR_BLOCK + i, block[i]) != 0)
+	    return 1;
+    }
+
+    /* Start initial block hashing, wait and check status. */
+    if ((sha256_init() != 0) || (sha256_wait_ready() != 0))
+	return 1;
+
+    /* First block done. Do the rest. */
+    for (i = 1; i < n; ++i) {
+	/* Start next block hashing, wait and check status. */
+	if ((sha256_next() != 0) || (sha256_wait_ready() != 0))
+	    return 1;
+    }
+
+    /* XXX valid is probably set at the same time as ready */
+    if (sha256_wait_valid() != 0)
+	return 1;
+    /* Extract the final digest. */
+    for (i = 0; i < SHA256_DIGEST_LEN; ++i) {
+	if (sha256_read(SHA256_ADDR_DIGEST + i, expected[i]) != 0)
+	    return 1;
+    }
+
+    return 0;
+}
+
+/* ---------------- SHA-512 test cases ---------------- */
+
+int sha512_read(uint8_t addr, uint32_t data)
+{
+    return tc_read(SHA512_ADDR_PREFIX, addr, data);
+}
+
+int sha512_write(uint8_t addr, uint32_t data)
+{
+    return tc_write(SHA512_ADDR_PREFIX, addr, data);
+}
+
+int sha512_init(uint8_t mode)
+{
+    return tc_write(SHA512_ADDR_PREFIX, ADDR_CTRL,
+		    CTRL_INIT_CMD + (mode << SHA512_CTRL_MODE_LOW));
+}
+
+int sha512_next(uint8_t mode)
+{
+    return tc_write(SHA512_ADDR_PREFIX, ADDR_CTRL,
+		    CTRL_NEXT_CMD + (mode << SHA512_CTRL_MODE_LOW));
+}
+
+int sha512_wait_ready(void)
+{
+    return tc_wait_ready(SHA512_ADDR_PREFIX);
+}
+
+int sha512_wait_valid(void)
+{
+    return tc_wait_valid(SHA512_ADDR_PREFIX);
+}
+
+/* TC8: Read name and version from SHA-512 core. */
+int TC8(void)
+{
+    uint32_t name0         = 0x73686132;	/* "sha2" */
+    uint32_t name1         = 0x2d353132;	/* "-512" */
+    uint32_t version       = 0x302e3830;	/* "0.80" */
+
+    printf("TC8: Reading name, type and version words from SHA-512 core.\n");
+
+    return 
+	sha512_read(ADDR_NAME0, name0) ||
+	sha512_read(ADDR_NAME1, name1) ||
+	sha512_read(ADDR_VERSION, version);
+}
+
+/* TC9: SHA-512 Single block message test as specified by NIST.
+   We do this for all modes. */
+int tc9(uint8_t mode, const uint32_t *expected, int len)
+{
+    const uint32_t *block = NIST_1024_SINGLE;
+    int i;
+
+    /* Write block to SHA-512. */
+    for (i = 0; i < SHA512_BLOCK_LEN; ++i) {
+	if (sha512_write(SHA512_ADDR_BLOCK + i, block[i]) != 0)
+	    return 1;
+    }
+
+    /* Start initial block hashing, wait and check status. */
+    if ((sha512_init(mode) != 0) || (sha512_wait_valid() != 0))
+	return 1;
+
+    /* Extract the digest. */
+    for (i = 0; i < len/4; ++i) {
+	if (sha512_read(SHA512_ADDR_DIGEST + i, expected[i]) != 0)
+	    return 1;
+    }
+
+    return 0;
+}
+
+int TC9(void)
+{
+    printf("TC9-1: Single block message test for SHA-512/224.\n");
+    if (tc9(MODE_SHA_512_224, SHA512_224_SINGLE_DIGEST,
+	    sizeof(SHA512_224_SINGLE_DIGEST)) != 0)
+	return 1;
+
+    printf("TC9-2: Single block message test for SHA-512/256.\n");
+    if (tc9(MODE_SHA_512_256, SHA512_256_SINGLE_DIGEST,
+	    sizeof(SHA512_256_SINGLE_DIGEST)) != 0)
+	return 1;
+
+    printf("TC9-3: Single block message test for SHA-384.\n");
+    if (tc9(MODE_SHA_384, SHA384_SINGLE_DIGEST,
+	    sizeof(SHA384_SINGLE_DIGEST)) != 0)
+	return 1;
+
+    printf("TC9-4: Single block message test for SHA-512.\n");
+    if (tc9(MODE_SHA_512, SHA512_SINGLE_DIGEST,
+	    sizeof(SHA512_SINGLE_DIGEST)) != 0)
+	return 1;
+
+    return 0;
+}
+
+/* TC10: SHA-512 Double block message test as specified by NIST.
+   We do this for all modes. */
+int tc10(uint8_t mode, const uint32_t *expected, int len)
+{
+    const uint32_t *block[2] = { NIST_1024_DOUBLE0, NIST_1024_DOUBLE1 };
+    int i;
+
+    /* Write first block to SHA-512. */
+    for (i = 0; i < SHA512_BLOCK_LEN; ++i) {
+	if (sha512_write(SHA512_ADDR_BLOCK + i, block[0][i]) != 0)
+	    return 1;
+    }
+
+    /* Start initial block hashing, wait and check status. */
+    if ((sha512_init(mode) != 0) || (sha512_wait_ready() != 0))
+	return 1;
+
+    /* Write second block to SHA-512. */
+    for (i = 0; i < SHA512_BLOCK_LEN; ++i) {
+	if (sha512_write(SHA512_ADDR_BLOCK + i, block[1][i]) != 0)
+	    return 1;
+    }
+
+    /* Start next block hashing, wait and check status. */
+    if ((sha512_next(mode) != 0) || (sha512_wait_valid() != 0))
+	return 1;
+
+    /* Extract the digest. */
+    for (i = 0; i < len/4; ++i) {
+	if (sha512_read(SHA512_ADDR_DIGEST + i, expected[i]) != 0)
+	    return 1;
+    }
+
+    return 0;
+}
+
+int TC10(void)
+{
+    printf("TC10-1: Double block message test for SHA-512/224.\n");
+    if (tc10(MODE_SHA_512_224, SHA512_224_DOUBLE_DIGEST,
+	     sizeof(SHA512_224_DOUBLE_DIGEST)) != 0)
+	return 1;
+
+    printf("TC10-2: Double block message test for SHA-512/256.\n");
+    if (tc10(MODE_SHA_512_256, SHA512_256_DOUBLE_DIGEST,
+	     sizeof(SHA512_256_DOUBLE_DIGEST)) != 0)
+	return 1;
+
+    printf("TC10-3: Double block message test for SHA-384.\n");
+    if (tc10(MODE_SHA_384, SHA384_DOUBLE_DIGEST,
+	     sizeof(SHA384_DOUBLE_DIGEST)) != 0)
+	return 1;
+
+    printf("TC10-4: Double block message test for SHA-512.\n");
+    if (tc10(MODE_SHA_512, SHA512_DOUBLE_DIGEST,
+	     sizeof(SHA512_DOUBLE_DIGEST)) != 0)
+	return 1;
+
+    return 0;
+}
+
+/* ---------------- main ---------------- */
+
+int main(int argc, char *argv[])
+{
+    typedef int (*tcfp)(void);
+    tcfp all_tests[] = { TC1, TC2, TC3, TC4, TC5, TC6, TC7, TC8, TC9, TC10 };
+    tcfp sha1_tests[] = { TC1, TC2, TC3 };
+    tcfp sha256_tests[] = { TC4, TC5, TC6, TC7 };
+    tcfp sha512_tests[] = { TC8, TC9, TC10 };
+
+    char *usage = "Usage: %s [-d] [-i I2C_device] [-a I2C_addr] tc...\n";
+    char *dev = I2C_dev;
+    int addr = I2C_addr;
+    int i, j, opt;
+
+    while ((opt = getopt(argc, argv, "h?di:a:")) != -1) {
+	switch (opt) {
+	case 'h':
+	case '?':
+	    printf(usage, argv[0]);
+	    return 0;
+	case 'd':
+	    debug = 1;
+	    break;
+	case 'i':
+	    dev = optarg;
+	    break;
+	case 'a':
+	    addr = (int)strtol(optarg, NULL, 0);
+	    if ((addr < 0x03) || (addr > 0x77)) {
+		fprintf(stderr, "addr must be between 0x03 and 0x77\n");
+		return 1;
+	    }
+	    break;
+	default:
+	    fprintf(stderr, usage, argv[0]);
+	    return 1;
+	}
+    }
+
+    if (i2c_setup(dev, addr) != 0)
+	return 1;
+
+    /* no args == run all tests */
+    if (optind >= argc) {
+	for (j = 0; j < sizeof(all_tests)/sizeof(all_tests[0]); ++j)
+	    if (all_tests[j]() != 0)
+		return 1;
+	return 0;
+    }
+
+    for (i = optind; i < argc; ++i) {
+	if (strcmp(argv[i], "sha1") == 0) {
+	    for (j = 0; j < sizeof(sha1_tests)/sizeof(sha1_tests[0]); ++j)
+		if (sha1_tests[j]() != 0)
+		    return 1;
+	}
+	else if (strcmp(argv[i], "sha256") == 0) {
+	    for (j = 0; j < sizeof(sha256_tests)/sizeof(sha256_tests[0]); ++j)
+		if (sha256_tests[j]() != 0)
+		    return 1;
+	}
+	else if (strcmp(argv[i], "sha512") == 0) {
+	    for (j = 0; j < sizeof(sha512_tests)/sizeof(sha512_tests[0]); ++j)
+		if (sha512_tests[j]() != 0)
+		    return 1;
+	}
+	else if (strcmp(argv[i], "all") == 0) {
+	    for (j = 0; j < sizeof(all_tests)/sizeof(all_tests[0]); ++j)
+		if (all_tests[j]() != 0)
+		    return 1;
+	}
+	else if (isdigit(argv[i][0]) &&
+		 (((j = atoi(argv[i])) > 0) &&
+		  (j <= sizeof(all_tests)/sizeof(all_tests[0])))) {
+	    if (all_tests[j - 1]() != 0)
+		return 1;
+	}
+	else {
+	    fprintf(stderr, "unknown test case %s\n", argv[i]);
+	    return 1;
+	}
+    }
+
+    return 0;
+}
diff --git a/src/sw/hash_tester_i2c.py b/src/sw/hash_tester_i2c.py
new file mode 100755
index 0000000..a1b838b
--- /dev/null
+++ b/src/sw/hash_tester_i2c.py
@@ -0,0 +1,693 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#=======================================================================
+#
+# hash_tester.py
+# --------------
+# This program sends several commands to the coretest_hashes subsystem
+# in order to verify the SHA-1, SHA-256 and SHA-512/x hash function
+# cores. The program will use the built in hash implementations in
+# Python to do functional comparison and validation.
+#
+# This version of the program talks to the FPGA over an I2C bus, but
+# does not require any additional modules.
+#
+# The single and dual block test cases are taken from the
+# NIST KAT document:
+# http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf
+#
+# 
+# Authors: Joachim Strömbergson, Paul Selkirk
+# Copyright (c) 2014, SUNET
+# 
+# Redistribution and use in source and binary forms, with or 
+# without modification, are permitted provided that the following 
+# conditions are met: 
+# 
+# 1. Redistributions of source code must retain the above copyright 
+#    notice, this list of conditions and the following disclaimer. 
+# 
+# 2. 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. 
+# 
+# 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 OWNER 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.
+#
+#=======================================================================
+ 
+#-------------------------------------------------------------------
+# Python module imports.
+#-------------------------------------------------------------------
+import sys
+import io
+import fcntl
+
+#-------------------------------------------------------------------
+# Defines.
+#-------------------------------------------------------------------
+
+# addresses and codes common to all hash cores
+ADDR_NAME0       = 0x00
+ADDR_NAME1       = 0x01
+ADDR_VERSION     = 0x02
+ADDR_CTRL        = 0x08
+CTRL_INIT_CMD    = 1
+CTRL_NEXT_CMD    = 2
+ADDR_STATUS      = 0x09
+STATUS_READY_BIT = 1
+STATUS_VALID_BIT = 2
+
+#----------------------------------------------------------------
+# NIST sample message blocks and expected digests
+#----------------------------------------------------------------
+
+# SHA-1/SHA-256 One Block Message Sample
+# Input Message: "abc"
+NIST_512_SINGLE = [ 0x61626380, 0x00000000, 0x00000000, 0x00000000,
+                    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                    0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                    0x00000000, 0x00000000, 0x00000000, 0x00000018 ]
+
+SHA1_SINGLE_DIGEST   = [ 0xa9993e36, 0x4706816a, 0xba3e2571, 0x7850c26c,
+                         0x9cd0d89d ]
+
+SHA256_SINGLE_DIGEST = [ 0xBA7816BF, 0x8F01CFEA, 0x414140DE, 0x5DAE2223,
+                         0xB00361A3, 0x96177A9C, 0xB410FF61, 0xF20015AD ]
+
+# SHA-1/SHA-256 Two Block Message Sample
+# Input Message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+NIST_512_DOUBLE0 = [ 0x61626364, 0x62636465, 0x63646566, 0x64656667,
+                     0x65666768, 0x66676869, 0x6768696A, 0x68696A6B,
+                     0x696A6B6C, 0x6A6B6C6D, 0x6B6C6D6E, 0x6C6D6E6F,
+                     0x6D6E6F70, 0x6E6F7071, 0x80000000, 0x00000000 ]
+NIST_512_DOUBLE1 = [ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x000001C0 ]
+
+SHA1_DOUBLE_DIGEST   = [ 0x84983E44, 0x1C3BD26E, 0xBAAE4AA1, 0xF95129E5,
+                         0xE54670F1 ]
+
+SHA256_DOUBLE_DIGEST = [ 0x248D6A61, 0xD20638B8, 0xE5C02693, 0x0C3E6039,
+                         0xA33CE459, 0x64FF2167, 0xF6ECEDD4, 0x19DB06C1 ]
+
+# SHA-512 One Block Message Sample
+# Input Message: "abc"
+NIST_1024_SINGLE = [ 0x61626380, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                     0x00000000, 0x00000000, 0x00000000, 0x00000018 ]
+
+SHA512_224_SINGLE_DIGEST = [0x4634270f, 0x707b6a54, 0xdaae7530, 0x460842e2,
+                            0x0e37ed26, 0x5ceee9a4, 0x3e8924aa]
+SHA512_256_SINGLE_DIGEST = [0x53048e26, 0x81941ef9, 0x9b2e29b7, 0x6b4c7dab,
+                            0xe4c2d0c6, 0x34fc6d46, 0xe0e2f131, 0x07e7af23]
+SHA384_SINGLE_DIGEST     = [0xcb00753f, 0x45a35e8b, 0xb5a03d69, 0x9ac65007,
+                            0x272c32ab, 0x0eded163, 0x1a8b605a, 0x43ff5bed,
+                            0x8086072b, 0xa1e7cc23, 0x58baeca1, 0x34c825a7]
+SHA512_SINGLE_DIGEST     = [0xddaf35a1, 0x93617aba, 0xcc417349, 0xae204131,
+                            0x12e6fa4e, 0x89a97ea2, 0x0a9eeee6, 0x4b55d39a,
+                            0x2192992a, 0x274fc1a8, 0x36ba3c23, 0xa3feebbd,
+                            0x454d4423, 0x643ce80e, 0x2a9ac94f, 0xa54ca49f]
+
+# SHA-512 Two Block Message Sample
+# Input Message: "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+# "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+NIST_1024_DOUBLE0 = [ 0x61626364, 0x65666768, 0x62636465, 0x66676869,
+                      0x63646566, 0x6768696a, 0x64656667, 0x68696a6b,
+                      0x65666768, 0x696a6b6c, 0x66676869, 0x6a6b6c6d,
+                      0x6768696a, 0x6b6c6d6e, 0x68696a6b, 0x6c6d6e6f,
+                      0x696a6b6c, 0x6d6e6f70, 0x6a6b6c6d, 0x6e6f7071,
+                      0x6b6c6d6e, 0x6f707172, 0x6c6d6e6f, 0x70717273,
+                      0x6d6e6f70, 0x71727374, 0x6e6f7071, 0x72737475,
+                      0x80000000, 0x00000000, 0x00000000, 0x00000000 ]
+NIST_1024_DOUBLE1 = [ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                      0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                      0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                      0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                      0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                      0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                      0x00000000, 0x00000000, 0x00000000, 0x00000000,
+                      0x00000000, 0x00000000, 0x00000000, 0x00000380 ]
+
+SHA512_224_DOUBLE_DIGEST = [ 0x23fec5bb, 0x94d60b23, 0x30819264, 0x0b0c4533,
+                             0x35d66473, 0x4fe40e72, 0x68674af9 ]
+SHA512_256_DOUBLE_DIGEST = [ 0x3928e184, 0xfb8690f8, 0x40da3988, 0x121d31be,
+                             0x65cb9d3e, 0xf83ee614, 0x6feac861, 0xe19b563a ]
+SHA384_DOUBLE_DIGEST     = [ 0x09330c33, 0xf71147e8, 0x3d192fc7, 0x82cd1b47,
+                             0x53111b17, 0x3b3b05d2, 0x2fa08086, 0xe3b0f712,
+                             0xfcc7c71a, 0x557e2db9, 0x66c3e9fa, 0x91746039 ]
+SHA512_DOUBLE_DIGEST     = [ 0x8e959b75, 0xdae313da, 0x8cf4f728, 0x14fc143f,
+                             0x8f7779c6, 0xeb9f7fa1, 0x7299aead, 0xb6889018,
+                             0x501d289e, 0x4900f7e4, 0x331b99de, 0xc4b5433a,
+                             0xc7d329ee, 0xb6dd2654, 0x5e96e55b, 0x874be909 ]
+
+#----------------------------------------------------------------
+# I2C class
+#----------------------------------------------------------------
+
+# default configuration
+I2C_dev = "/dev/i2c-2"
+I2C_addr = 0x0f
+
+# from /usr/include/linux/i2c-dev.h
+I2C_SLAVE = 0x0703
+
+def hexlist(list):
+    return "[ " + ' '.join('%02x' % b for b in list) + " ]"
+
+class I2C:
+    # file handle for the i2c device
+    file = None
+
+    # constructor: initialize the i2c communications channel
+    def __init__(self, dev=I2C_dev, addr=I2C_addr):
+        self.dev = dev
+        self.addr = addr
+        try:
+            self.file = io.FileIO(self.dev, 'r+b')
+        except IOError as e:
+            print "Unable to open %s: %s" % (self.dev, e.strerror)
+            sys.exit(1)
+        try:
+            fcntl.ioctl(self.file, I2C_SLAVE, self.addr)
+        except IOError as e:
+            print "Unable to set I2C slave device 0x%02x: %s" % (self.addr, e.strerror)
+            sys.exit(1)
+
+    # destructor: close the i2c communications channel
+    def __del__(self):
+        if (self.file):
+            self.file.close()
+            
+    # write a command to the i2c device
+    def write(self, buf):
+        if DEBUG:
+            print "write %s" % hexlist(buf)
+        self.file.write(bytearray(buf))
+
+    # read one response byte from the i2c device
+    def read(self):
+        # read() on the i2c device will only return one byte at a time,
+        # and tc.get_resp() needs to parse the response one byte at a time
+        return ord(self.file.read(1))
+
+#----------------------------------------------------------------
+# test-case class
+#----------------------------------------------------------------
+
+# command codes
+SOC                   = 0x55
+EOC                   = 0xaa
+READ_CMD              = 0x10
+WRITE_CMD             = 0x11
+RESET_CMD             = 0x01
+
+# response codes
+SOR                   = 0xaa
+EOR                   = 0x55
+READ_OK               = 0x7f
+WRITE_OK              = 0x7e
+RESET_OK              = 0x7d
+UNKNOWN               = 0xfe
+ERROR                 = 0xfd
+
+class TcError(Exception):
+    pass
+
+class tc:
+    def __init__(self, i2c, addr0, addr1):
+        self.i2c = i2c
+        self.addr0 = addr0
+        self.addr1 = addr1
+
+    def send_write_cmd(self, data):
+        buf = [SOC, WRITE_CMD, self.addr0, self.addr1]
+        for s in (24, 16, 8, 0):
+            buf.append(data >> s & 0xff)
+        buf.append(EOC)
+        self.i2c.write(buf)
+
+    def send_read_cmd(self):
+        buf = [SOC, READ_CMD, self.addr0, self.addr1, EOC]
+        self.i2c.write(buf)
+
+    def get_resp(self):
+        buf = []
+        len = 2
+        i = 0
+        while i < len:
+            b = self.i2c.read()
+            if ((i == 0) and (b != SOR)):
+                # we've gotten out of sync, and there's probably nothing we can do
+                print "response byte 0: expected 0x%02x (SOR), got 0x%02x" % (SOR, b)
+                raise TcError()
+            elif (i == 1):        # response code
+                try:
+                    # anonymous dictionary of message lengths
+                    len = {READ_OK:9, WRITE_OK:5, RESET_OK:3, ERROR:4, UNKNOWN:4}[b]
+                except KeyError:  # unknown response code
+                    # we've gotten out of sync, and there's probably nothing we can do
+                    print "unknown response code 0x%02x" % b
+                    raise TcError()
+            buf.append(b)
+            i += 1
+        if DEBUG:
+            print "read  %s" % hexlist(buf)
+        return buf
+
+    def get_expected(self, expected):
+        buf = self.get_resp()
+        if (buf != expected):
+            print "expected %s,\nreceived %s" % (hexlist(expected), hexlist(buf))
+            raise TcError()
+
+    def get_write_resp(self):
+        expected = [SOR, WRITE_OK, self.addr0, self.addr1, EOR]
+        self.get_expected(expected)
+
+    def get_read_resp(self, data):
+        expected = [SOR, READ_OK, self.addr0, self.addr1]
+        for s in (24, 16, 8, 0):
+            expected.append(data >> s & 0xff)
+        expected.append(EOR)
+        self.get_expected(expected)
+
+    def write(self, data):
+        self.send_write_cmd(data)
+        self.get_write_resp()
+
+    def read(self, data):
+        self.send_read_cmd()
+        self.get_read_resp(data)
+
+def tc_write(i2c, addr0, addr1, data):
+    tc(i2c, addr0, addr1).write(data)
+
+def tc_read(i2c, addr0, addr1, data):
+    tc(i2c, addr0, addr1).read(data)
+
+def tc_init(i2c, addr0):
+    tc(i2c, addr0, ADDR_CTRL).write(CTRL_INIT_CMD)
+
+def tc_next(i2c, addr0):
+    tc(i2c, addr0, ADDR_CTRL).write(CTRL_NEXT_CMD)
+
+def tc_wait(i2c, addr0, status):
+    t = tc(i2c, addr0, ADDR_STATUS)
+    while 1:
+        t.send_read_cmd()
+        buf = t.get_resp()
+        if ((buf[7] & status) == status):
+            break
+
+def tc_wait_ready(i2c, addr0):
+    tc_wait(i2c, addr0, STATUS_READY_BIT)
+
+def tc_wait_valid(i2c, addr0):
+    tc_wait(i2c, addr0, STATUS_VALID_BIT)
+
+#----------------------------------------------------------------
+# SHA-1 test cases
+#----------------------------------------------------------------
+SHA1_ADDR_PREFIX      = 0x10
+SHA1_ADDR_BLOCK       = 0x10
+SHA1_ADDR_DIGEST      = 0x20
+
+def sha1_read(i2c, addr, data):
+    tc_read(i2c, SHA1_ADDR_PREFIX, addr, data)
+
+def sha1_write(i2c, addr, data):
+    tc_write(i2c, SHA1_ADDR_PREFIX, addr, data)
+
+def sha1_init(i2c):
+    tc_init(i2c, SHA1_ADDR_PREFIX)
+
+def sha1_next(i2c):
+    tc_next(i2c, SHA1_ADDR_PREFIX)
+
+def sha1_wait_ready(i2c):
+    tc_wait_ready(i2c, SHA1_ADDR_PREFIX)
+
+def sha1_wait_valid(i2c):
+    tc_wait_valid(i2c, SHA1_ADDR_PREFIX)
+
+# TC1: Read name and version from SHA-1 core.
+def TC1(i2c):
+    print "TC1: Reading name, type and version words from SHA-1 core."
+
+    sha1_read(i2c, ADDR_NAME0,   0x73686131)    # "sha1"
+    sha1_read(i2c, ADDR_NAME1,   0x20202020)    # "    "
+    sha1_read(i2c, ADDR_VERSION, 0x302e3530)    # "0.50"
+
+# TC2: SHA-1 Single block message test as specified by NIST.
+def TC2(i2c):
+    block = NIST_512_SINGLE
+    expected = SHA1_SINGLE_DIGEST
+
+    print "TC2: Single block message test for SHA-1."
+
+    # Write block to SHA-1.
+    for i in range(len(block)):
+        sha1_write(i2c, SHA1_ADDR_BLOCK + i, block[i])
+
+    # Start initial block hashing, wait and check status.
+    sha1_init(i2c)
+    sha1_wait_valid(i2c)
+
+    # Extract the digest.
+    for i in range(len(expected)):
+        sha1_read(i2c, SHA1_ADDR_DIGEST + i, expected[i])
+
+# TC3: SHA-1 Double block message test as specified by NIST.
+def TC3(i2c):
+    block = [ NIST_512_DOUBLE0, NIST_512_DOUBLE1 ]
+    block0_expected = [ 0xF4286818, 0xC37B27AE, 0x0408F581, 0x84677148,
+                        0x4A566572 ]
+    expected = SHA1_DOUBLE_DIGEST
+
+    print "TC3: Double block message test for SHA-1."
+
+    # Write first block to SHA-1.
+    for i in range(len(block[0])):
+        sha1_write(i2c, SHA1_ADDR_BLOCK + i, block[0][i])
+
+    # Start initial block hashing, wait and check status.
+    sha1_init(i2c)
+    sha1_wait_valid(i2c)
+
+    # Extract the first digest.
+    for i in range(len(block0_expected)):
+        sha1_read(i2c, SHA1_ADDR_DIGEST + i, block0_expected[i])
+
+    # Write second block to SHA-1.
+    for i in range(len(block[1])):
+        sha1_write(i2c, SHA1_ADDR_BLOCK + i, block[1][i])
+
+    # Start next block hashing, wait and check status.
+    sha1_next(i2c)
+    sha1_wait_valid(i2c)
+
+    # Extract the second digest.
+    for i in range(len(expected)):
+        sha1_read(i2c, SHA1_ADDR_DIGEST + i, expected[i])
+
+#----------------------------------------------------------------
+# SHA-256 test cases
+#----------------------------------------------------------------
+SHA256_ADDR_PREFIX      = 0x20
+SHA256_ADDR_BLOCK       = 0x10
+SHA256_ADDR_DIGEST      = 0x20
+
+def sha256_read(i2c, addr, data):
+    tc_read(i2c, SHA256_ADDR_PREFIX, addr, data)
+
+def sha256_write(i2c, addr, data):
+    tc_write(i2c, SHA256_ADDR_PREFIX, addr, data)
+
+def sha256_init(i2c):
+    tc_init(i2c, SHA256_ADDR_PREFIX)
+
+def sha256_next(i2c):
+    tc_next(i2c, SHA256_ADDR_PREFIX)
+
+def sha256_wait_ready(i2c):
+    tc_wait_ready(i2c, SHA256_ADDR_PREFIX)
+
+def sha256_wait_valid(i2c):
+    tc_wait_valid(i2c, SHA256_ADDR_PREFIX)
+
+# TC4: Read name and version from SHA-256 core.
+def TC4(i2c):
+    print "TC4: Reading name, type and version words from SHA-256 core."
+
+    sha256_read(i2c, ADDR_NAME0,   0x73686132)  # "sha2"
+    sha256_read(i2c, ADDR_NAME1,   0x2d323536)  # "-256"
+    sha256_read(i2c, ADDR_VERSION, 0x302e3830)  # "0.80"
+
+# TC5: SHA-256 Single block message test as specified by NIST.
+def TC5(i2c):
+    block = NIST_512_SINGLE
+    expected = SHA256_SINGLE_DIGEST
+
+    print "TC5: Single block message test for SHA-256."
+
+    # Write block to SHA-256.
+    for i in range(len(block)):
+        sha256_write(i2c, SHA256_ADDR_BLOCK + i, block[i])
+
+    # Start initial block hashing, wait and check status.
+    sha256_init(i2c)
+    sha256_wait_valid(i2c)
+
+    # Extract the digest.
+    for i in range(len(expected)):
+        sha256_read(i2c, SHA256_ADDR_DIGEST + i, expected[i])
+
+# TC6: SHA-256 Double block message test as specified by NIST.
+def TC6(i2c):
+    block = [ NIST_512_DOUBLE0, NIST_512_DOUBLE1 ]
+    block0_expected = [ 0x85E655D6, 0x417A1795, 0x3363376A, 0x624CDE5C,
+                        0x76E09589, 0xCAC5F811, 0xCC4B32C1, 0xF20E533A ]
+    expected = SHA256_DOUBLE_DIGEST
+
+    print "TC6: Double block message test for SHA-256."
+
+    # Write first block to SHA-256.
+    for i in range(len(block[0])):
+        sha256_write(i2c, SHA256_ADDR_BLOCK + i, block[0][i])
+
+    # Start initial block hashing, wait and check status.
+    sha256_init(i2c)
+    sha256_wait_valid(i2c)
+
+    # Extract the first digest.
+    for i in range(len(block0_expected)):
+        sha256_read(i2c, SHA256_ADDR_DIGEST + i, block0_expected[i])
+
+    # Write second block to SHA-256.
+    for i in range(len(block[1])):
+        sha256_write(i2c, SHA256_ADDR_BLOCK + i, block[1][i])
+
+    # Start next block hashing, wait and check status.
+    sha256_next(i2c)
+    sha256_wait_valid(i2c)
+
+    # Extract the second digest.
+    for i in range(len(expected)):
+        sha256_read(i2c, SHA256_ADDR_DIGEST + i, expected[i])
+
+# TC7: SHA-256 Huge message test.
+def TC7(i2c):
+    block = [ 0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f,
+              0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f,
+              0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f,
+              0xaa55aa55, 0xdeadbeef, 0x55aa55aa, 0xf00ff00f ]
+    expected = [ 0x7638f3bc, 0x500dd1a6, 0x586dd4d0, 0x1a1551af,
+                 0xd821d235, 0x2f919e28, 0xd5842fab, 0x03a40f2a ]
+    n = 1000
+
+    print "TC7: Message with %d blocks test for SHA-256." % n
+
+    # Write first block to SHA-256.
+    for i in range(len(block)):
+        sha256_write(i2c, SHA256_ADDR_BLOCK + i, block[i])
+
+    # Start initial block hashing, wait and check status.
+    sha256_init(i2c)
+    sha256_wait_ready(i2c)
+
+    # First block done. Do the rest.
+    for i in range(n - 1):
+        # Start next block hashing, wait and check status.
+        sha256_next(i2c)
+        sha256_wait_ready(i2c)
+
+    # XXX valid is probably set at the same time as ready
+    sha256_wait_valid(i2c)
+
+    # Extract the final digest.
+    for i in range(len(expected)):
+        sha256_read(i2c, SHA256_ADDR_DIGEST + i, expected[i])
+
+#----------------------------------------------------------------
+# SHA-512 test cases
+#----------------------------------------------------------------
+SHA512_ADDR_PREFIX      = 0x30
+SHA512_ADDR_BLOCK       = 0x10
+SHA512_ADDR_DIGEST      = 0x40
+SHA512_CTRL_MODE_LOW    = 2
+SHA512_CTRL_MODE_HIGH   = 3
+MODE_SHA_512_224 = 0
+MODE_SHA_512_256 = 1
+MODE_SHA_384     = 2
+MODE_SHA_512     = 3
+
+def sha512_read(i2c, addr, data):
+    tc_read(i2c, SHA512_ADDR_PREFIX, addr, data)
+
+def sha512_write(i2c, addr, data):
+    tc_write(i2c, SHA512_ADDR_PREFIX, addr, data)
+
+def sha512_init(i2c, mode):
+    tc_write(i2c, SHA512_ADDR_PREFIX, ADDR_CTRL,
+             CTRL_INIT_CMD + (mode << SHA512_CTRL_MODE_LOW))
+
+def sha512_next(i2c, mode):
+    tc_write(i2c, SHA512_ADDR_PREFIX, ADDR_CTRL,
+             CTRL_NEXT_CMD + (mode << SHA512_CTRL_MODE_LOW))
+
+def sha512_wait_ready(i2c):
+    tc_wait_ready(i2c, SHA512_ADDR_PREFIX)
+
+def sha512_wait_valid(i2c):
+    tc_wait_valid(i2c, SHA512_ADDR_PREFIX)
+
+# TC8: Read name and version from SHA-512 core.
+def TC8(i2c):
+    print "TC8: Reading name, type and version words from SHA-512 core."
+
+    sha512_read(i2c, ADDR_NAME0,   0x73686132)  # "sha2"
+    sha512_read(i2c, ADDR_NAME1,   0x2d353132)  # "-512"
+    sha512_read(i2c, ADDR_VERSION, 0x302e3830)  # "0.80"
+
+# TC9: SHA-512 Single block message test as specified by NIST.
+# We do this for all modes.
+def TC9(i2c):
+    def tc9(i2c, mode, expected):
+        block = NIST_1024_SINGLE
+
+        # Write block to SHA-512.
+        for i in range(len(block)):
+            sha512_write(i2c, SHA512_ADDR_BLOCK + i, block[i])
+
+        # Start initial block hashing, wait and check status.
+        sha512_init(i2c, mode)
+        sha512_wait_valid(i2c)
+
+        # Extract the digest.
+        for i in range(len(expected)):
+            sha512_read(i2c, SHA512_ADDR_DIGEST + i, expected[i])
+
+    print "TC9-1: Single block message test for SHA-512/224."
+    tc9(i2c, MODE_SHA_512_224, SHA512_224_SINGLE_DIGEST)
+
+    print "TC9-2: Single block message test for SHA-512/256."
+    tc9(i2c, MODE_SHA_512_256, SHA512_256_SINGLE_DIGEST)
+
+    print "TC9-3: Single block message test for SHA-384."
+    tc9(i2c, MODE_SHA_384, SHA384_SINGLE_DIGEST)
+
+    print "TC9-4: Single block message test for SHA-512."
+    tc9(i2c, MODE_SHA_512, SHA512_SINGLE_DIGEST)
+
+# TC10: SHA-512 Single block message test as specified by NIST.
+# We do this for all modes.
+def TC10(i2c):
+    def tc10(i2c, mode, expected):
+        block = [ NIST_1024_DOUBLE0, NIST_1024_DOUBLE1 ]
+
+        # Write first block to SHA-512.
+        for i in range(len(block[0])):
+            sha512_write(i2c, SHA512_ADDR_BLOCK + i, block[0][i])
+
+        # Start initial block hashing, wait and check status.
+        sha512_init(i2c, mode)
+        sha512_wait_ready(i2c)
+
+        # Write second block to SHA-512.
+        for i in range(len(block[1])):
+            sha512_write(i2c, SHA512_ADDR_BLOCK + i, block[1][i])
+
+        # Start next block hashing, wait and check status.
+        sha512_next(i2c, mode)
+        sha512_wait_valid(i2c)
+
+        # Extract the digest.
+        for i in range(len(expected)):
+            sha512_read(i2c, SHA512_ADDR_DIGEST + i, expected[i])
+
+    print "TC10-1: Double block message test for SHA-512/224."
+    tc10(i2c, MODE_SHA_512_224, SHA512_224_DOUBLE_DIGEST)
+
+    print "TC10-2: Double block message test for SHA-512/256."
+    tc10(i2c, MODE_SHA_512_256, SHA512_256_DOUBLE_DIGEST)
+
+    print "TC10-3: Double block message test for SHA-384."
+    tc10(i2c, MODE_SHA_384, SHA384_DOUBLE_DIGEST)
+
+    print "TC10-4: Double block message test for SHA-512."
+    tc10(i2c, MODE_SHA_512, SHA512_DOUBLE_DIGEST)
+
+
+#----------------------------------------------------------------
+# main
+#----------------------------------------------------------------
+if __name__ == '__main__':
+    import argparse
+
+    all_tests = [ TC1, TC2, TC3, TC4, TC5, TC6, TC7, TC8, TC9, TC10 ]
+    sha1_tests = all_tests[0:3]
+    sha256_tests = all_tests[3:7]
+    sha512_tests = all_tests[7:]
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-d', '--debug', action='store_true',
+                        help='add debugging/trace information')
+    parser.add_argument('-i', dest='dev', default=I2C_dev,
+                        help='I2C device name (default ' + I2C_dev + ')')
+    parser.add_argument('-a', dest='addr', type=lambda x:int(x,0), default=I2C_addr,
+                        help='I2C device address (default ' + hex(I2C_addr) + ')')
+    parser.add_argument('test_cases', metavar='TC', nargs='*',
+                        help='test case number, "sha1", "sha256", "sha512", or "all"')
+    args = parser.parse_args()
+    DEBUG = args.debug
+    i = I2C(args.dev, args.addr)
+
+    if (not args.test_cases):
+        for TC in all_tests:
+            TC(i)
+    else:
+        for t in args.test_cases:
+            if (t == 'sha1'):
+                for TC in sha1_tests:
+                    TC(i)
+            elif (t == 'sha256'):
+                for TC in sha256_tests:
+                    TC(i)
+            elif (t == 'sha512'):
+                for TC in sha512_tests:
+                    TC(i)
+            elif (t == 'all'):
+                for TC in all_tests:
+                    TC(i)
+            else:
+                try:
+                    n = int(t)
+                except:
+                    print 'invalid test case %s' % t
+                else:
+                    if ((n < 1) or (n > len(all_tests))):
+                        print 'invalid test case %d' % n
+                    else:
+                        all_tests[n-1](i)
+
+#=======================================================================
+# EOF hash_tester.py
+#=======================================================================
diff --git a/src/sw/hash_tester_i2c_simple.c b/src/sw/hash_tester_i2c_simple.c
new file mode 100644
index 0000000..994c305
--- /dev/null
+++ b/src/sw/hash_tester_i2c_simple.c
@@ -0,0 +1,688 @@
+/* 
+ * hash_tester.c
+ * --------------
+ * This program sends several commands to the coretest_hashes subsystem
+ * in order to verify the SHA-1, SHA-256 and SHA-512/x hash function
+ * cores.
+ *
+ * Note: This version of the program talks to the FPGA over an I2C bus.
+ *
+ * The single and dual block test cases are taken from the
+ * NIST KAT document:
+ * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf
+ *
+ * 
+ * Authors: Joachim Strömbergson, Paul Selkirk
+ * Copyright (c) 2014, SUNET
+ * 
+ * Redistribution and use in source and binary forms, with or 
+ * without modification, are permitted provided that the following 
+ * conditions are met: 
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. 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. 
+ * 
+ * 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 OWNER 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.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+/* I2C configuration */
+#define I2C_DEV  "/dev/i2c-2"
+
+#define I2C_SHA1_ADDR    0x1e
+#define I2C_SHA256_ADDR  0x1f
+#define I2C_SHA512_ADDR  0x20
+
+#define MODE_SHA_512_224 0x00
+#define MODE_SHA_512_256 0x01
+#define MODE_SHA_384     0x02
+#define MODE_SHA_512     0x03
+
+int i2cfd;
+int debug = 0;
+
+#define SHA1_BLOCK_BITS         512
+#define SHA1_BLOCK_BYTES        SHA1_BLOCK_BITS/8
+#define SHA1_DIGEST_BITS        160
+#define SHA1_DIGEST_BYTES       SHA1_DIGEST_BITS/8
+
+#define SHA256_BLOCK_BITS       512
+#define SHA256_BLOCK_BYTES      SHA256_BLOCK_BITS/8
+#define SHA256_DIGEST_BITS      256
+#define SHA256_DIGEST_BYTES     SHA256_DIGEST_BITS/8
+
+#define SHA512_BLOCK_BITS       1024
+#define SHA512_BLOCK_BYTES      SHA512_BLOCK_BITS/8
+#define SHA512_224_DIGEST_BITS  224
+#define SHA512_224_DIGEST_BYTES SHA512_224_DIGEST_BITS/8
+#define SHA512_256_DIGEST_BITS  256
+#define SHA512_256_DIGEST_BYTES SHA512_256_DIGEST_BITS/8
+#define SHA384_DIGEST_BITS      384
+#define SHA384_DIGEST_BYTES     SHA384_DIGEST_BITS/8
+#define SHA512_DIGEST_BITS      512
+#define SHA512_DIGEST_BYTES     SHA512_DIGEST_BITS/8
+
+/* SHA-1/SHA-256 One Block Message Sample
+   Input Message: "abc" */
+const uint8_t NIST_512_SINGLE[] =
+{ 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18 };
+
+const uint8_t SHA1_SINGLE_DIGEST[] =
+{ 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a,
+  0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c,
+  0x9c, 0xd0, 0xd8, 0x9d };
+
+const uint8_t SHA256_SINGLE_DIGEST[] =
+{ 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA,
+  0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23,
+  0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C,
+  0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD };
+
+/* SHA-1/SHA-256 Two Block Message Sample
+   Input Message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" */
+const uint8_t NIST_512_DOUBLE0[] =
+{ 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
+  0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
+  0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+  0x67, 0x68, 0x69, 0x6A, 0x68, 0x69, 0x6A, 0x6B,
+  0x69, 0x6A, 0x6B, 0x6C, 0x6A, 0x6B, 0x6C, 0x6D,
+  0x6B, 0x6C, 0x6D, 0x6E, 0x6C, 0x6D, 0x6E, 0x6F,
+  0x6D, 0x6E, 0x6F, 0x70, 0x6E, 0x6F, 0x70, 0x71,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+const uint8_t NIST_512_DOUBLE1[] =
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0 };
+
+const uint8_t SHA1_DOUBLE_DIGEST[] =
+{ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E,
+  0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5,
+  0xE5, 0x46, 0x70, 0xF1 };
+
+const uint8_t SHA256_DOUBLE_DIGEST[] =
+{ 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8,
+  0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39,
+  0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67,
+  0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 };
+
+/* SHA-512 One Block Message Sample
+   Input Message: "abc" */
+const uint8_t NIST_1024_SINGLE[] =
+{ 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18 };
+
+const uint8_t SHA512_224_SINGLE_DIGEST[] =
+{ 0x46, 0x34, 0x27, 0x0f, 0x70, 0x7b, 0x6a, 0x54,
+  0xda, 0xae, 0x75, 0x30, 0x46, 0x08, 0x42, 0xe2,
+  0x0e, 0x37, 0xed, 0x26, 0x5c, 0xee, 0xe9, 0xa4,
+  0x3e, 0x89, 0x24, 0xaa };
+const uint8_t SHA512_256_SINGLE_DIGEST[] =
+{ 0x53, 0x04, 0x8e, 0x26, 0x81, 0x94, 0x1e, 0xf9,
+  0x9b, 0x2e, 0x29, 0xb7, 0x6b, 0x4c, 0x7d, 0xab,
+  0xe4, 0xc2, 0xd0, 0xc6, 0x34, 0xfc, 0x6d, 0x46,
+  0xe0, 0xe2, 0xf1, 0x31, 0x07, 0xe7, 0xaf, 0x23 };
+const uint8_t SHA384_SINGLE_DIGEST[] =
+{ 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b,
+  0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07,
+  0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+  0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed,
+  0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23,
+  0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 };
+const uint8_t SHA512_SINGLE_DIGEST[] =
+{ 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+  0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+  0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+  0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+  0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+  0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+  0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+  0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f };
+
+/* SHA-512 Two Block Message Sample
+   Input Message: "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+   "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" */
+const uint8_t NIST_1024_DOUBLE0[] =
+{ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+  0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+  0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+  0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+  0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+  0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+  0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+  0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+  0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
+  0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+  0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+  0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+  0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+const uint8_t NIST_1024_DOUBLE1[] =
+{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80 };
+
+const uint8_t SHA512_224_DOUBLE_DIGEST[] = 
+{ 0x23, 0xfe, 0xc5, 0xbb, 0x94, 0xd6, 0x0b, 0x23,
+  0x30, 0x81, 0x92, 0x64, 0x0b, 0x0c, 0x45, 0x33,
+  0x35, 0xd6, 0x64, 0x73, 0x4f, 0xe4, 0x0e, 0x72,
+  0x68, 0x67, 0x4a, 0xf9 };
+const uint8_t SHA512_256_DOUBLE_DIGEST[] =
+{ 0x39, 0x28, 0xe1, 0x84, 0xfb, 0x86, 0x90, 0xf8,
+  0x40, 0xda, 0x39, 0x88, 0x12, 0x1d, 0x31, 0xbe,
+  0x65, 0xcb, 0x9d, 0x3e, 0xf8, 0x3e, 0xe6, 0x14,
+  0x6f, 0xea, 0xc8, 0x61, 0xe1, 0x9b, 0x56, 0x3a };
+const uint8_t SHA384_DOUBLE_DIGEST[] =
+{ 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8,
+  0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47,
+  0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2,
+  0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12,
+  0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9,
+  0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39 };
+const uint8_t SHA512_DOUBLE_DIGEST[] =
+{ 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
+  0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
+  0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+  0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
+  0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+  0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+  0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
+  0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 };
+
+/* ---------------- I2C low-level code ---------------- */
+
+int i2c_open(char *dev)
+{
+    i2cfd = open(dev, O_RDWR);
+    if (i2cfd < 0) {
+        fprintf(stderr, "Unable to open %s: ", dev);
+        perror("");
+        i2cfd = 0;
+        return 1;
+    }
+
+    return 0;
+}
+
+int i2c_addr(int addr)
+{
+    static int cur_addr = 0;
+
+    if (addr != cur_addr) {
+        if (ioctl(i2cfd, I2C_SLAVE, addr) < 0) {
+            fprintf(stderr, "Unable to set I2C slave device 0x%02x: ", addr);
+            perror("");
+            return 1;
+        }
+        addr = cur_addr;
+    }
+
+    return 0;
+}
+
+int i2c_close(void)
+{
+    return close(i2cfd);
+}
+
+int i2c_write(const uint8_t *buf, int len)
+{
+    if (write(i2cfd, buf, len) != len) {
+        perror("i2c write failed");
+        return 1;
+    }
+
+    return 0;
+}
+
+int i2c_read(uint8_t *b)
+{
+    /* read() on the i2c device only returns one byte at a time,
+     * and tc_get_resp() needs to parse the response one byte at a time
+     */
+    if (read(i2cfd, b, 1) != 1) {
+        perror("i2c read failed");
+        return 1;
+    }
+
+    return 0;
+}
+
+/* ---------------- test-case low-level code ---------------- */
+
+int tc_write(int addr, const uint8_t *buf, int len)
+{
+    /* we only set the i2c device addr on tc_write, because test cases always
+     * write before reading, plus test cases never call tc_read directly
+     */
+    if (i2c_addr(addr) != 0)
+        return 1;
+
+    if (debug) {
+        int i;
+        printf("write [");
+        for (i = 0; i < len; ++i)
+            printf(" %02x", buf[i]);
+        printf(" ]\n");
+    }
+
+    return i2c_write(buf, len);
+}
+
+int tc_read(uint8_t *buf, int len)
+{
+    int i;
+
+    if (debug) printf("read  [");
+
+    for (i = 0; i < len; ++i) {
+        if (i2c_read(&buf[i]) != 0) {
+            if (debug) printf(" ]\n");
+            return 1;
+        }
+        if (debug) printf(" %02x", buf[i]);
+    }
+
+    if (debug) printf(" ]\n");
+
+    return 0;
+}
+
+int tc_expected(const uint8_t *expected, int len)
+{
+    uint8_t *buf;
+    int i;
+
+    buf = malloc(len);
+    if (buf == NULL) {
+        perror("malloc");
+        return 1;
+    }
+
+    if (tc_read(buf, len) != 0)
+        goto errout;
+
+    for (i = 0; i < len; ++i)
+        if (buf[i] != expected[i]) {
+            fprintf(stderr, "response byte %d: expected 0x%02x, got 0x%02x\n",
+                    i, expected[i], buf[i]);
+            goto errout;
+        }
+
+    free(buf);
+    return 0;
+errout:
+    free(buf);
+    return 1;
+}
+
+/* ---------------- SHA-1 test cases ---------------- */
+
+#define BLOCK_LEN  SHA1_BLOCK_BYTES
+#define DIGEST_LEN SHA1_DIGEST_BYTES
+
+/* TC1: Read name and version from SHA-1 core. */
+int TC1(int addr)
+{
+    return 0;
+}
+
+/* TC2: SHA-1 Single block message test as specified by NIST. */
+int TC2(int addr)
+{
+    const uint8_t *block = NIST_512_SINGLE;
+    const uint8_t *expected = SHA1_SINGLE_DIGEST;
+
+    printf("TC2: Single block message test for SHA-1.\n");
+
+    if (addr == 0)
+        addr = I2C_SHA1_ADDR;
+
+    return
+        tc_write(addr, block, BLOCK_LEN) ||
+        tc_expected(expected, DIGEST_LEN);
+}
+
+/* TC3: SHA-1 Double block message test as specified by NIST. */
+int TC3(int addr)
+{
+    const uint8_t *block[2] = { NIST_512_DOUBLE0, NIST_512_DOUBLE1 };
+    const uint8_t *expected = SHA1_DOUBLE_DIGEST;
+
+    printf("TC3: Double block message test for SHA-1.\n");
+
+    if (addr == 0)
+        addr = I2C_SHA1_ADDR;
+
+    return
+        tc_write(addr, block[0], BLOCK_LEN) ||
+        tc_write(addr, block[1], BLOCK_LEN) ||
+        tc_expected(expected, DIGEST_LEN);
+}
+
+/* ---------------- SHA-256 test cases ---------------- */
+
+#undef  BLOCK_LEN
+#define BLOCK_LEN  SHA256_BLOCK_BYTES
+#undef  DIGEST_LEN
+#define DIGEST_LEN SHA256_DIGEST_BYTES
+
+int TC4(int addr)
+{
+    return 0;
+}
+
+/* TC5: SHA-256 Single block message test as specified by NIST. */
+int TC5(int addr)
+{
+    const uint8_t *block = NIST_512_SINGLE;
+    const uint8_t *expected = SHA256_SINGLE_DIGEST;
+
+    printf("TC5: Single block message test for SHA-256.\n");
+
+    if (addr == 0)
+        addr = I2C_SHA256_ADDR;
+
+    return
+        tc_write(addr, block, BLOCK_LEN) ||
+        tc_expected(expected, DIGEST_LEN);
+}
+
+/* TC6: SHA-256 Double block message test as specified by NIST. */
+int TC6(int addr)
+{
+    const uint8_t *block[2] = { NIST_512_DOUBLE0, NIST_512_DOUBLE1 };
+    const uint8_t *expected = SHA256_DOUBLE_DIGEST;
+
+    printf("TC6: Double block message test for SHA-256.\n");
+
+    if (addr == 0)
+        addr = I2C_SHA256_ADDR;
+
+    return
+        tc_write(addr, block[0], BLOCK_LEN) ||
+        tc_write(addr, block[1], BLOCK_LEN) ||
+        tc_expected(expected, DIGEST_LEN);
+}
+
+/* TC7: SHA-256 Huge message test. */
+int TC7(int addr)
+{
+    static const uint8_t block[] =
+        { 0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f,
+          0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f,
+          0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f,
+          0xaa, 0x55, 0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef,
+          0x55, 0xaa, 0x55, 0xaa, 0xf0, 0x0f, 0xf0, 0x0f };
+
+    /* final digest after 1000 iterations */
+    static const uint8_t expected[] = 
+        { 0x76, 0x38, 0xf3, 0xbc, 0x50, 0x0d, 0xd1, 0xa6,
+          0x58, 0x6d, 0xd4, 0xd0, 0x1a, 0x15, 0x51, 0xaf,
+          0xd8, 0x21, 0xd2, 0x35, 0x2f, 0x91, 0x9e, 0x28,
+          0xd5, 0x84, 0x2f, 0xab, 0x03, 0xa4, 0x0f, 0x2a };
+
+    int i, n = 1000;
+
+    printf("TC7: Message with %d blocks test for SHA-256.\n", n);
+
+    if (addr == 0)
+        addr = I2C_SHA256_ADDR;
+
+    /* Write blocks to SHA-256. */
+    for (i = 0; i < n; ++i)
+        if (tc_write(addr, block, BLOCK_LEN) != 0)
+            return 1;
+
+    /* Extract the final digest. */
+    return tc_expected(expected, DIGEST_LEN);
+}
+
+/* ---------------- SHA-512 test cases ---------------- */
+
+#undef  BLOCK_LEN
+#define BLOCK_LEN  SHA512_BLOCK_BYTES
+
+/* TC8: Read name and version from SHA-512 core. */
+int TC8(int addr)
+{
+    return 0;
+}
+
+/* TC9: SHA-512 Single block message test as specified by NIST.
+   We do this for all modes. */
+int tc9(int addr, const uint8_t *expected, int digest_len)
+{
+    const uint8_t *block = NIST_1024_SINGLE;
+
+    return
+        tc_write(addr, block, BLOCK_LEN) ||
+        tc_expected(expected, digest_len);
+}
+
+int TC9(int addr)
+{
+    if (addr == 0)
+        addr = I2C_SHA512_ADDR;
+
+    printf("TC9-1: Single block message test for SHA-512/224.\n");
+    if (tc9(addr + MODE_SHA_512_224, SHA512_224_SINGLE_DIGEST,
+            sizeof(SHA512_224_SINGLE_DIGEST)) != 0)
+        return 1;
+
+    printf("TC9-2: Single block message test for SHA-512/256.\n");
+    if (tc9(addr + MODE_SHA_512_256, SHA512_256_SINGLE_DIGEST,
+            sizeof(SHA512_256_SINGLE_DIGEST)) != 0)
+        return 1;
+
+    printf("TC9-3: Single block message test for SHA-384.\n");
+    if (tc9(addr + MODE_SHA_384, SHA384_SINGLE_DIGEST,
+            sizeof(SHA384_SINGLE_DIGEST)) != 0)
+        return 1;
+
+    printf("TC9-4: Single block message test for SHA-512.\n");
+    if (tc9(addr + MODE_SHA_512, SHA512_SINGLE_DIGEST,
+            sizeof(SHA512_SINGLE_DIGEST)) != 0)
+        return 1;
+
+    return 0;
+}
+
+/* TC10: SHA-512 Double block message test as specified by NIST.
+   We do this for all modes. */
+int tc10(int addr, const uint8_t *expected, int digest_len)
+{
+    const uint8_t *block[2] = { NIST_1024_DOUBLE0, NIST_1024_DOUBLE1 };
+
+    return
+        tc_write(addr, block[0], BLOCK_LEN) ||
+        tc_write(addr, block[1], BLOCK_LEN) ||
+        tc_expected(expected, digest_len);
+}
+
+int TC10(int addr)
+{
+    if (addr == 0)
+        addr = I2C_SHA512_ADDR;
+
+    printf("TC10-1: Double block message test for SHA-512/224.\n");
+    if (tc10(addr + MODE_SHA_512_224, SHA512_224_DOUBLE_DIGEST,
+             sizeof(SHA512_224_DOUBLE_DIGEST)) != 0)
+        return 1;
+
+    printf("TC10-2: Double block message test for SHA-512/256.\n");
+    if (tc10(addr + MODE_SHA_512_256, SHA512_256_DOUBLE_DIGEST,
+             sizeof(SHA512_256_DOUBLE_DIGEST)) != 0)
+        return 1;
+
+    printf("TC10-3: Double block message test for SHA-384.\n");
+    if (tc10(addr + MODE_SHA_384, SHA384_DOUBLE_DIGEST,
+             sizeof(SHA384_DOUBLE_DIGEST)) != 0)
+        return 1;
+
+    printf("TC10-4: Double block message test for SHA-512.\n");
+    if (tc10(addr + MODE_SHA_512, SHA512_DOUBLE_DIGEST,
+             sizeof(SHA512_DOUBLE_DIGEST)) != 0)
+        return 1;
+
+    return 0;
+}
+
+/* ---------------- main ---------------- */
+
+int main(int argc, char *argv[])
+{
+    typedef int (*tcfp)(int);
+    tcfp all_tests[] = { TC1, TC2, TC3, TC4, TC5, TC6, TC7, TC9, TC10
+    };
+    tcfp sha1_tests[] = { TC1, TC2, TC3 };
+    tcfp sha256_tests[] = { TC4, TC5, TC6, TC7 };
+    tcfp sha512_tests[] = { TC9, TC10 };
+
+    char *usage = "Usage: %s [-h] [-d] [-i I2C_device] [-a I2C_addr] tc...\n";
+    char *dev = I2C_DEV;
+    int addr = 0;
+    int i, j, opt;
+
+    while ((opt = getopt(argc, argv, "h?di:a:")) != -1) {
+        switch (opt) {
+        case 'h':
+        case '?':
+            printf(usage, argv[0]);
+            return 0;
+        case 'd':
+            debug = 1;
+            break;
+        case 'i':
+            dev = optarg;
+            break;
+        case 'a':
+            addr = (int)strtol(optarg, NULL, 0);
+            if ((addr < 0x03) || (addr > 0x77)) {
+                fprintf(stderr, "addr must be between 0x03 and 0x77\n");
+                return 1;
+            }
+            break;
+        default:
+            fprintf(stderr, usage, argv[0]);
+            return 1;
+        }
+    }
+
+    if (i2c_open(dev) != 0)
+        return 1;
+
+    /* no args == run all tests */
+    if (optind >= argc) {
+        for (j = 0; j < sizeof(all_tests)/sizeof(all_tests[0]); ++j)
+            if (all_tests[j](addr) != 0)
+                return 1;
+        return 0;
+    }
+
+    for (i = optind; i < argc; ++i) {
+        if (strcmp(argv[i], "sha1") == 0) {
+            for (j = 0; j < sizeof(sha1_tests)/sizeof(sha1_tests[0]); ++j)
+                if (sha1_tests[j](addr) != 0)
+                    return 1;
+        }
+        else if (strcmp(argv[i], "sha256") == 0) {
+            for (j = 0; j < sizeof(sha256_tests)/sizeof(sha256_tests[0]); ++j)
+                if (sha256_tests[j](addr) != 0)
+                    return 1;
+        }
+        else if (strcmp(argv[i], "sha512") == 0) {
+            for (j = 0; j < sizeof(sha512_tests)/sizeof(sha512_tests[0]); ++j)
+                if (sha512_tests[j](addr) != 0)
+                    return 1;
+        }
+        else if (strcmp(argv[i], "all") == 0) {
+            for (j = 0; j < sizeof(all_tests)/sizeof(all_tests[0]); ++j)
+                if (all_tests[j](addr) != 0)
+                    return 1;
+        }
+        else if (isdigit(argv[i][0]) &&
+                 (((j = atoi(argv[i])) > 0) &&
+                  (j <= sizeof(all_tests)/sizeof(all_tests[0])))) {
+            if (all_tests[j - 1](addr) != 0)
+                return 1;
+        }
+        else {
+            fprintf(stderr, "unknown test case %s\n", argv[i]);
+            return 1;
+        }
+    }
+
+    return 0;
+}
diff --git a/src/sw/hash_tester_uart.py b/src/sw/hash_tester_uart.py
new file mode 100755
index 0000000..d91d906
--- /dev/null
+++ b/src/sw/hash_tester_uart.py
@@ -0,0 +1,1021 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#=======================================================================
+#
+# hash_tester.py
+# --------------
+# This program sends several commands to the coretest_hashed subsystem
+# in order to verify the SHA-1, SHA-256 and SHA-512/x hash function
+# cores. The program will use the built in hash implementations in
+# Python to do functional comparison and validation.
+#
+# Note: This program requires the PySerial module.
+# http://pyserial.sourceforge.net/
+#
+# The single and dual block test cases are taken from the
+# NIST KAT document:
+# http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf
+#
+# 
+# Author: Joachim Strömbergson
+# Copyright (c) 2014, SUNET
+# 
+# Redistribution and use in source and binary forms, with or 
+# without modification, are permitted provided that the following 
+# conditions are met: 
+# 
+# 1. Redistributions of source code must retain the above copyright 
+#    notice, this list of conditions and the following disclaimer. 
+# 
+# 2. 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. 
+# 
+# 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 OWNER 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.
+#
+#=======================================================================
+ 
+#-------------------------------------------------------------------
+# Python module imports.
+#-------------------------------------------------------------------
+import sys
+import serial
+import os
+import time
+import threading
+import hashlib
+
+ 
+#-------------------------------------------------------------------
+# Defines.
+#-------------------------------------------------------------------
+# Serial port defines.
+# CONFIGURE YOUR DEVICE HERE!
+SERIAL_DEVICE = '/dev/cu.usbserial-A801SA6T'
+BAUD_RATE = 9600
+BIT_RATE  = int(50E6 / BAUD_RATE)
+
+BAUD_RATE2 = 256000
+BIT_RATE2  = int(50E6 / BAUD_RATE2)
+
+DATA_BITS = 8
+STOP_BITS = 1
+
+
+# Verbose operation on/off
+VERBOSE = False
+
+# Delay times we wait
+PROC_DELAY_TIME = 0.0001
+COMM_DELAY_TIME = 0.01
+
+# Memory map.
+SOC                   = '\x55'
+EOC                   = '\xaa'
+READ_CMD              = '\x10'
+WRITE_CMD             = '\x11'
+
+UART_ADDR_PREFIX      = '\x00'
+UART_ADDR_BIT_RATE    = '\x10'
+UART_ADDR_DATA_BITS   = '\x11'
+UART_ADDR_STOP_BITS   = '\x12'
+
+SHA1_ADDR_PREFIX      = '\x10'
+SHA1_ADDR_NAME0       = '\x00'
+SHA1_ADDR_NAME1       = '\x01'
+SHA1_ADDR_VERSION     = '\x02'
+SHA1_ADDR_CTRL        = '\x08'
+SHA1_CTRL_INIT_CMD    = '\x01'
+SHA1_CTRL_NEXT_CMD    = '\x02'
+SHA1_ADDR_STATUS      = '\x09'
+SHA1_STATUS_READY_BIT = 0
+SHA1_STATUS_VALID_BIT = 1
+SHA1_ADDR_BLOCK0      = '\x10'
+SHA1_ADDR_BLOCK1      = '\x11'
+SHA1_ADDR_BLOCK2      = '\x12'
+SHA1_ADDR_BLOCK3      = '\x13'
+SHA1_ADDR_BLOCK4      = '\x14'
+SHA1_ADDR_BLOCK5      = '\x15'
+SHA1_ADDR_BLOCK6      = '\x16'
+SHA1_ADDR_BLOCK7      = '\x17'
+SHA1_ADDR_BLOCK8      = '\x18'
+SHA1_ADDR_BLOCK9      = '\x19'
+SHA1_ADDR_BLOCK10     = '\x1a'
+SHA1_ADDR_BLOCK11     = '\x1b'
+SHA1_ADDR_BLOCK12     = '\x1c'
+SHA1_ADDR_BLOCK13     = '\x1d'
+SHA1_ADDR_BLOCK14     = '\x1e'
+SHA1_ADDR_BLOCK15     = '\x1f'
+SHA1_ADDR_DIGEST0     = '\x20'
+SHA1_ADDR_DIGEST1     = '\x21'
+SHA1_ADDR_DIGEST2     = '\x22'
+SHA1_ADDR_DIGEST3     = '\x23'
+SHA1_ADDR_DIGEST4     = '\x24'
+
+SHA256_ADDR_PREFIX      = '\x20'
+SHA256_ADDR_NAME0       = '\x00'
+SHA256_ADDR_NAME1       = '\x01'
+SHA256_ADDR_VERSION     = '\x02'
+SHA256_ADDR_CTRL        = '\x08'
+SHA256_CTRL_INIT_CMD    = '\x01'
+SHA256_CTRL_NEXT_CMD    = '\x02'
+SHA256_ADDR_STATUS      = '\x09'
+SHA256_STATUS_READY_BIT = 0
+SHA256_STATUS_VALID_BIT = 1
+SHA256_ADDR_BLOCK0      = '\x10'
+SHA256_ADDR_BLOCK1      = '\x11'
+SHA256_ADDR_BLOCK2      = '\x12'
+SHA256_ADDR_BLOCK3      = '\x13'
+SHA256_ADDR_BLOCK4      = '\x14'
+SHA256_ADDR_BLOCK5      = '\x15'
+SHA256_ADDR_BLOCK6      = '\x16'
+SHA256_ADDR_BLOCK7      = '\x17'
+SHA256_ADDR_BLOCK8      = '\x18'
+SHA256_ADDR_BLOCK9      = '\x19'
+SHA256_ADDR_BLOCK10     = '\x1a'
+SHA256_ADDR_BLOCK11     = '\x1b'
+SHA256_ADDR_BLOCK12     = '\x1c'
+SHA256_ADDR_BLOCK13     = '\x1d'
+SHA256_ADDR_BLOCK14     = '\x1e'
+SHA256_ADDR_BLOCK15     = '\x1f'
+SHA256_ADDR_DIGEST0     = '\x20'
+SHA256_ADDR_DIGEST1     = '\x21'
+SHA256_ADDR_DIGEST2     = '\x22'
+SHA256_ADDR_DIGEST3     = '\x23'
+SHA256_ADDR_DIGEST4     = '\x24'
+SHA256_ADDR_DIGEST5     = '\x25'
+SHA256_ADDR_DIGEST6     = '\x26'
+SHA256_ADDR_DIGEST7     = '\x27'
+
+SHA512_ADDR_PREFIX      = '\x30'
+SHA512_ADDR_NAME0       = '\x00'
+SHA512_ADDR_NAME1       = '\x01'
+SHA512_ADDR_VERSION     = '\x02'
+SHA512_ADDR_CTRL        = '\x08'
+SHA512_CTRL_INIT_CMD    = '\x01'
+SHA512_CTRL_NEXT_CMD    = '\x02'
+SHA512_CTRL_MODE_LOW    = 2
+SHA512_CTRL_MODE_HIGH   = 3
+SHA512_ADDR_STATUS      = '\x09'
+SHA512_STATUS_READY_BIT = 0
+SHA512_STATUS_VALID_BIT = 1
+SHA512_ADDR_BLOCK0      = '\x10'
+SHA512_ADDR_BLOCK1      = '\x11'
+SHA512_ADDR_BLOCK2      = '\x12'
+SHA512_ADDR_BLOCK3      = '\x13'
+SHA512_ADDR_BLOCK4      = '\x14'
+SHA512_ADDR_BLOCK5      = '\x15'
+SHA512_ADDR_BLOCK6      = '\x16'
+SHA512_ADDR_BLOCK7      = '\x17'
+SHA512_ADDR_BLOCK8      = '\x18'
+SHA512_ADDR_BLOCK9      = '\x19'
+SHA512_ADDR_BLOCK10     = '\x1a'
+SHA512_ADDR_BLOCK11     = '\x1b'
+SHA512_ADDR_BLOCK12     = '\x1c'
+SHA512_ADDR_BLOCK13     = '\x1d'
+SHA512_ADDR_BLOCK14     = '\x1e'
+SHA512_ADDR_BLOCK15     = '\x1f'
+SHA512_ADDR_BLOCK16     = '\x20'
+SHA512_ADDR_BLOCK17     = '\x21'
+SHA512_ADDR_BLOCK18     = '\x22'
+SHA512_ADDR_BLOCK19     = '\x23'
+SHA512_ADDR_BLOCK20     = '\x24'
+SHA512_ADDR_BLOCK21     = '\x25'
+SHA512_ADDR_BLOCK22     = '\x26'
+SHA512_ADDR_BLOCK23     = '\x27'
+SHA512_ADDR_BLOCK24     = '\x28'
+SHA512_ADDR_BLOCK25     = '\x29'
+SHA512_ADDR_BLOCK26     = '\x2a'
+SHA512_ADDR_BLOCK27     = '\x2b'
+SHA512_ADDR_BLOCK28     = '\x2c'
+SHA512_ADDR_BLOCK29     = '\x2d'
+SHA512_ADDR_BLOCK30     = '\x2e'
+SHA512_ADDR_BLOCK31     = '\x2f'
+SHA512_ADDR_DIGEST0     = '\x40'
+SHA512_ADDR_DIGEST1     = '\x41'
+SHA512_ADDR_DIGEST2     = '\x42'
+SHA512_ADDR_DIGEST3     = '\x43'
+SHA512_ADDR_DIGEST4     = '\x44'
+SHA512_ADDR_DIGEST5     = '\x45'
+SHA512_ADDR_DIGEST6     = '\x46'
+SHA512_ADDR_DIGEST7     = '\x47'
+SHA512_ADDR_DIGEST8     = '\x48'
+SHA512_ADDR_DIGEST9     = '\x49'
+SHA512_ADDR_DIGEST10    = '\x4a'
+SHA512_ADDR_DIGEST11    = '\x4b'
+SHA512_ADDR_DIGEST12    = '\x4c'
+SHA512_ADDR_DIGEST13    = '\x4d'
+SHA512_ADDR_DIGEST14    = '\x4e'
+SHA512_ADDR_DIGEST15    = '\x4f'
+
+MODE_SHA_512_224 = '\x00'
+MODE_SHA_512_256 = '\x01'
+MODE_SHA_384     = '\x02'
+MODE_SHA_512     = '\x03'
+
+sha1_block_addr = [SHA1_ADDR_BLOCK0,  SHA1_ADDR_BLOCK1,
+                   SHA1_ADDR_BLOCK2,  SHA1_ADDR_BLOCK3,
+                   SHA1_ADDR_BLOCK4,  SHA1_ADDR_BLOCK5,
+                   SHA1_ADDR_BLOCK6,  SHA1_ADDR_BLOCK7,
+                   SHA1_ADDR_BLOCK8,  SHA1_ADDR_BLOCK9,
+                   SHA1_ADDR_BLOCK10, SHA1_ADDR_BLOCK11,
+                   SHA1_ADDR_BLOCK12, SHA1_ADDR_BLOCK13,
+                   SHA1_ADDR_BLOCK14, SHA1_ADDR_BLOCK15]
+
+sha1_digest_addr = [SHA1_ADDR_DIGEST0, SHA1_ADDR_DIGEST1,
+                    SHA1_ADDR_DIGEST2, SHA1_ADDR_DIGEST3,
+                    SHA1_ADDR_DIGEST4]
+
+sha256_block_addr = [SHA256_ADDR_BLOCK0,  SHA256_ADDR_BLOCK1,
+                     SHA256_ADDR_BLOCK2,  SHA256_ADDR_BLOCK3,
+                     SHA256_ADDR_BLOCK4,  SHA256_ADDR_BLOCK5,
+                     SHA256_ADDR_BLOCK6,  SHA256_ADDR_BLOCK7,
+                     SHA256_ADDR_BLOCK8,  SHA256_ADDR_BLOCK9,
+                     SHA256_ADDR_BLOCK10, SHA256_ADDR_BLOCK11,
+                     SHA256_ADDR_BLOCK12, SHA256_ADDR_BLOCK13,
+                     SHA256_ADDR_BLOCK14, SHA256_ADDR_BLOCK15]
+
+sha256_digest_addr = [SHA256_ADDR_DIGEST0,  SHA256_ADDR_DIGEST1,
+                      SHA256_ADDR_DIGEST2,  SHA256_ADDR_DIGEST3,
+                      SHA256_ADDR_DIGEST4,  SHA256_ADDR_DIGEST5,
+                      SHA256_ADDR_DIGEST6,  SHA256_ADDR_DIGEST7]
+
+sha512_block_addr = [SHA512_ADDR_BLOCK0,  SHA512_ADDR_BLOCK1,
+                     SHA512_ADDR_BLOCK2,  SHA512_ADDR_BLOCK3,
+                     SHA512_ADDR_BLOCK4,  SHA512_ADDR_BLOCK5,
+                     SHA512_ADDR_BLOCK6,  SHA512_ADDR_BLOCK7,
+                     SHA512_ADDR_BLOCK8,  SHA512_ADDR_BLOCK9,
+                     SHA512_ADDR_BLOCK10, SHA512_ADDR_BLOCK11,
+                     SHA512_ADDR_BLOCK12, SHA512_ADDR_BLOCK13,
+                     SHA512_ADDR_BLOCK14, SHA512_ADDR_BLOCK15,
+                     SHA512_ADDR_BLOCK16, SHA512_ADDR_BLOCK17,
+                     SHA512_ADDR_BLOCK18, SHA512_ADDR_BLOCK19,
+                     SHA512_ADDR_BLOCK20, SHA512_ADDR_BLOCK21,
+                     SHA512_ADDR_BLOCK22, SHA512_ADDR_BLOCK23,
+                     SHA512_ADDR_BLOCK24, SHA512_ADDR_BLOCK25,
+                     SHA512_ADDR_BLOCK26, SHA512_ADDR_BLOCK27,
+                     SHA512_ADDR_BLOCK28, SHA512_ADDR_BLOCK29,
+                     SHA512_ADDR_BLOCK30, SHA512_ADDR_BLOCK31]
+
+sha512_digest_addr = [SHA512_ADDR_DIGEST0,  SHA512_ADDR_DIGEST1,
+                      SHA512_ADDR_DIGEST2,  SHA512_ADDR_DIGEST3,
+                      SHA512_ADDR_DIGEST4,  SHA512_ADDR_DIGEST5,
+                      SHA512_ADDR_DIGEST6,  SHA512_ADDR_DIGEST7,
+                      SHA512_ADDR_DIGEST8,  SHA512_ADDR_DIGEST9,
+                      SHA512_ADDR_DIGEST10, SHA512_ADDR_DIGEST11,
+                      SHA512_ADDR_DIGEST12, SHA512_ADDR_DIGEST13,
+                      SHA512_ADDR_DIGEST14, SHA512_ADDR_DIGEST15]
+
+NIST_512_SINGLE = ['\x61', '\x62', '\x63', '\x80', '\x00', '\x00', '\x00', '\x00',
+                   '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                   '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                   '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                   '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                   '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                   '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                   '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x18']
+
+NIST_512_DOUBLE0 = ['\x61', '\x62', '\x63', '\x64', '\x62', '\x63', '\x64', '\x65',
+                    '\x63', '\x64', '\x65', '\x66', '\x64', '\x65', '\x66', '\x67',
+                    '\x65', '\x66', '\x67', '\x68', '\x66', '\x67', '\x68', '\x69',
+                    '\x67', '\x68', '\x69', '\x6A', '\x68', '\x69', '\x6A', '\x6B',
+                    '\x69', '\x6A', '\x6B', '\x6C', '\x6A', '\x6B', '\x6C', '\x6D',
+                    '\x6B', '\x6C', '\x6D', '\x6E', '\x6C', '\x6D', '\x6E', '\x6F',
+                    '\x6D', '\x6E', '\x6F', '\x70', '\x6E', '\x6F', '\x70', '\x71',
+                    '\x80', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']
+
+NIST_512_DOUBLE1 = ['\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\xC0']
+
+NIST_1024_SINGLE = ['\x61', '\x62', '\x63', '\x80', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x18']
+
+NIST_1024_DOUBLE0 = ['\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68',
+                     '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69',
+                     '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6a',
+                     '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6a', '\x6b',
+                     '\x65', '\x66', '\x67', '\x68', '\x69', '\x6a', '\x6b', '\x6c',
+                     '\x66', '\x67', '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d',
+                     '\x67', '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e',
+                     '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
+                     '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', '\x70',
+                     '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', '\x70', '\x71',
+                     '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', '\x70', '\x71', '\x72',
+                     '\x6c', '\x6d', '\x6e', '\x6f', '\x70', '\x71', '\x72', '\x73',
+                     '\x6d', '\x6e', '\x6f', '\x70', '\x71', '\x72', '\x73', '\x74',
+                     '\x6e', '\x6f', '\x70', '\x71', '\x72', '\x73', '\x74', '\x75',
+                     '\x80', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']
+
+NIST_1024_DOUBLE1 = ['\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+                     '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x03', '\x80']
+
+
+#-------------------------------------------------------------------
+# print_response()
+#
+# Parses a received buffer and prints the response.
+#-------------------------------------------------------------------
+def print_response(buffer):
+    if VERBOSE:
+        print "Length of response: %d" % len(buffer)
+        if buffer[0] == '\xaa':
+            print "Response contains correct Start of Response (SOR)"
+        if buffer[-1] == '\x55':
+            print "Response contains correct End of Response (EOR)"
+
+    response_code = ord(buffer[1])
+
+    if response_code == 0xfe:
+        print "UNKNOWN response code received."
+
+    elif response_code == 0xfd:
+        print "ERROR response code received."
+
+    elif response_code == 0x7f:
+        read_addr = ord(buffer[2]) * 256 + ord(buffer[3])
+        read_data = (ord(buffer[4]) * 16777216) + (ord(buffer[5]) * 65536) +\
+                    (ord(buffer[6]) * 256) + ord(buffer[7])
+        print "READ_OK. address 0x%02x = 0x%08x." % (read_addr, read_data)
+
+    elif response_code == 0x7e:
+        read_addr = ord(buffer[2]) * 256 + ord(buffer[3])
+        print "WRITE_OK. address 0x%02x." % (read_addr)
+
+    elif response_code == 0x7d:
+        print "RESET_OK."
+
+    else:
+        print "Response 0x%02x is unknown." % response_code
+        print buffer
+        
+
+#-------------------------------------------------------------------
+# read_serial_thread()
+#
+# Function used in a thread to read from the serial port and
+# collect response from coretest.
+#-------------------------------------------------------------------
+def read_serial_thread(serialport):
+    if VERBOSE:
+        print "Serial port response thread started. Waiting for response..."
+        
+    buffer = []
+    while True:
+        if serialport.isOpen():
+            response = serialport.read()
+            buffer.append(response)
+            if ((response == '\x55') and len(buffer) > 7):
+                print_response(buffer)
+                buffer = []
+        else:
+            print "No open device yet."
+            time.sleep(COMM_DELAY_TIME)
+            
+
+#-------------------------------------------------------------------
+# write_serial_bytes()
+#
+# Send the bytes in the buffer to coretest over the serial port.
+#-------------------------------------------------------------------
+def write_serial_bytes(tx_cmd, serialport):
+    if VERBOSE:
+        print "Command to be sent:", tx_cmd
+    
+    for tx_byte in tx_cmd:
+        serialport.write(tx_byte)
+
+    # Allow the device to complete the transaction.
+    time.sleep(COMM_DELAY_TIME)
+
+
+#-------------------------------------------------------------------
+# single_block_test_sha512x()
+#
+# Write a given block to SHA-512/x and perform single block
+# processing for the given mode.
+#-------------------------------------------------------------------
+def single_block_test_sha512x(block, mode, ser):
+    # Write block to SHA-512.
+    for i in range(len(block) / 4):
+        message = [SOC, WRITE_CMD, SHA512_ADDR_PREFIX,] + [sha512_block_addr[i]] +\
+                  block[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start initial block hashing, wait and check status.
+    mode_cmd = chr(ord(SHA512_CTRL_INIT_CMD) + (ord(mode) << SHA512_CTRL_MODE_LOW))
+    write_serial_bytes([SOC, WRITE_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', mode_cmd, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_STATUS, EOC], ser)
+
+    # Select the correct number of digest addresses to read.
+    if (mode == MODE_SHA_512_224):
+        mode_digest_addr = sha512_digest_addr[0 : 7]
+    elif (mode == MODE_SHA_512_256):
+        mode_digest_addr = sha512_digest_addr[0 : 8]
+    elif (mode == MODE_SHA_384):
+        mode_digest_addr = sha512_digest_addr[0 : 12]
+    elif (mode == MODE_SHA_512):
+        mode_digest_addr = sha512_digest_addr
+
+    # Extract the digest.
+    for digest_addr in mode_digest_addr:
+        message = [SOC, READ_CMD, SHA512_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# dual_block_test_sha512x()
+#
+# Write a given block to SHA-512/x and perform single block
+# processing for the given mode.
+#-------------------------------------------------------------------
+def dual_block_test_sha512x(block0, block1, mode, ser):
+    # Write block0 to SHA-512.
+    for i in range(len(block0) / 4):
+        message = [SOC, WRITE_CMD, SHA512_ADDR_PREFIX,] + [sha512_block_addr[i]] +\
+                  block0[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start initial block hashing, wait and check status.
+    mode_cmd = chr(ord(SHA512_CTRL_INIT_CMD) + (ord(mode) << SHA512_CTRL_MODE_LOW))
+    write_serial_bytes([SOC, WRITE_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', mode_cmd, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_STATUS, EOC], ser)
+
+    # Write block1 to SHA-512.
+    for i in range(len(block1) / 4):
+        message = [SOC, WRITE_CMD, SHA512_ADDR_PREFIX,] + [sha512_block_addr[i]] +\
+                  block1[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start next block hashing, wait and check status.
+    mode_cmd = chr(ord(SHA512_CTRL_NEXT_CMD) + (ord(mode) << SHA512_CTRL_MODE_LOW))
+    write_serial_bytes([SOC, WRITE_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', mode_cmd, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_STATUS, EOC], ser)
+
+    # Select the correct number of digest addresses to read.
+    if (mode == MODE_SHA_512_224):
+        mode_digest_addr = sha512_digest_addr[0 : 7]
+    elif (mode == MODE_SHA_512_256):
+        mode_digest_addr = sha512_digest_addr[0 : 8]
+    elif (mode == MODE_SHA_384):
+        mode_digest_addr = sha512_digest_addr[0 : 12]
+    elif (mode == MODE_SHA_512):
+        mode_digest_addr = sha512_digest_addr
+
+    # Extract the digest.
+    for digest_addr in mode_digest_addr:
+        message = [SOC, READ_CMD, SHA512_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# single_block_test_sha256()
+#
+# Write a given block to SHA-256 and perform single block
+# processing.
+#-------------------------------------------------------------------
+def single_block_test_sha256(block, ser):
+    # Write block to SHA-2.
+    for i in range(len(block) / 4):
+        message = [SOC, WRITE_CMD, SHA256_ADDR_PREFIX,] + [sha256_block_addr[i]] +\
+                  block[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start initial block hashing, wait and check status.
+    write_serial_bytes([SOC, WRITE_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', SHA256_CTRL_INIT_CMD, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_STATUS, EOC], ser)
+
+    # Extract the digest.
+    for digest_addr in sha256_digest_addr:
+        message = [SOC, READ_CMD, SHA256_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# double_block_test_sha256()
+#
+# Run double block message test.
+#-------------------------------------------------------------------
+def double_block_test_sha256(block1, block2, ser):
+    # Write block1 to SHA-256.
+    for i in range(len(block1) / 4):
+        message = [SOC, WRITE_CMD, SHA256_ADDR_PREFIX,] + [sha256_block_addr[i]] +\
+                  block1[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start initial block hashing, wait and check status.
+    write_serial_bytes([SOC, WRITE_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', SHA256_CTRL_INIT_CMD, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_STATUS, EOC], ser)
+
+    # Extract the first digest.
+    for digest_addr in sha256_digest_addr:
+        message = [SOC, READ_CMD, SHA256_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+    # Write block2 to SHA-256.
+    for i in range(len(block2) / 4):
+        message = [SOC, WRITE_CMD, SHA256_ADDR_PREFIX,] + [sha256_block_addr[i]] +\
+                  block2[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start next block hashing, wait and check status.
+    write_serial_bytes([SOC, WRITE_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', SHA256_CTRL_NEXT_CMD, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_STATUS, EOC], ser)
+
+    # Extract the second digest.
+    for digest_addr in sha256_digest_addr:
+        message = [SOC, READ_CMD, SHA256_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# huge_message_test_sha256()
+#
+# Test with a message with a huge number (n) number of blocks.
+#-------------------------------------------------------------------
+def huge_message_test_sha256(block, n, ser):
+    # Write block to SHA-256.
+    for i in range(len(block) / 4):
+        message = [SOC, WRITE_CMD, SHA256_ADDR_PREFIX,] + [sha256_block_addr[i]] +\
+                  block[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start initial block hashing, wait and check status.
+    write_serial_bytes([SOC, WRITE_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', SHA256_CTRL_INIT_CMD, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_STATUS, EOC], ser)
+
+    # Extract the first digest.
+    print "Digest for block 0000:"
+    for digest_addr in sha256_digest_addr:
+        message = [SOC, READ_CMD, SHA256_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+    # First block done. Do the rest.
+    for i in range(n - 1):
+        # Start next block hashing, wait and check status.
+        write_serial_bytes([SOC, WRITE_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_CTRL,
+                            '\x00', '\x00', '\x00', SHA256_CTRL_NEXT_CMD, EOC], ser)
+        time.sleep(PROC_DELAY_TIME)
+        write_serial_bytes([SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_STATUS, EOC], ser)
+
+        # Extract the second digest.
+        print "Digest for block %04d" % (i + 1)
+        for digest_addr in sha256_digest_addr:
+            message = [SOC, READ_CMD, SHA256_ADDR_PREFIX] + [digest_addr] + [EOC]
+            write_serial_bytes(message, ser)
+        print""
+
+
+#-------------------------------------------------------------------
+# single_block_test_sha1()
+#
+# Write a given block to SHA-1 and perform single block
+# processing.
+#-------------------------------------------------------------------
+def single_block_test_sha1(block, ser):
+    # Write block to SHA-1.
+    for i in range(len(block) / 4):
+        message = [SOC, WRITE_CMD, SHA1_ADDR_PREFIX,] + [sha1_block_addr[i]] +\
+                  block[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start initial block hashing, wait and check status.
+    write_serial_bytes([SOC, WRITE_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', SHA1_CTRL_INIT_CMD, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_STATUS,   EOC], ser)
+
+    # Extract the digest.
+    for digest_addr in sha1_digest_addr:
+        message = [SOC, READ_CMD, SHA1_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+    
+
+#-------------------------------------------------------------------
+# double_block_test_sha1
+#
+# Run double block message test for SHA-1.
+#-------------------------------------------------------------------
+def double_block_test_sha1(block1, block2, ser):
+    # Write block1 to SHA-1.
+    for i in range(len(block1) / 4):
+        message = [SOC, WRITE_CMD, SHA1_ADDR_PREFIX,] + [sha1_block_addr[i]] +\
+                  block1[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start initial block hashing, wait and check status.
+    write_serial_bytes([SOC, WRITE_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', SHA1_CTRL_INIT_CMD, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_STATUS,   EOC], ser)
+
+    # Extract the first digest.
+    for digest_addr in sha1_digest_addr:
+        message = [SOC, READ_CMD, SHA1_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+    # Write block2 to SHA-1.
+    for i in range(len(block2) / 4):
+        message = [SOC, WRITE_CMD, SHA1_ADDR_PREFIX,] + [sha1_block_addr[i]] +\
+                  block2[(i * 4) : ((i * 4 ) + 4)] + [EOC]
+        write_serial_bytes(message, ser)
+
+    # Start next block hashing, wait and check status.
+    write_serial_bytes([SOC, WRITE_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_CTRL,
+                        '\x00', '\x00', '\x00', SHA1_CTRL_NEXT_CMD, EOC], ser)
+    time.sleep(PROC_DELAY_TIME)
+    write_serial_bytes([SOC, READ_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_STATUS,   EOC], ser)
+
+    # Extract the second digest.
+    for digest_addr in sha1_digest_addr:
+        message = [SOC, READ_CMD, SHA1_ADDR_PREFIX] + [digest_addr] + [EOC]
+        write_serial_bytes(message, ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# TC1: Read name and version from SHA-1 core.
+#-------------------------------------------------------------------
+def tc1(ser):
+    print "TC1: Reading name, type and version words from SHA-1 core."
+    write_serial_bytes([SOC, READ_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_NAME0, EOC], ser)
+    write_serial_bytes([SOC, READ_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_NAME1, EOC], ser)
+    write_serial_bytes([SOC, READ_CMD, SHA1_ADDR_PREFIX, SHA1_ADDR_VERSION, EOC], ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# TC2: SHA-1 Single block message test as specified by NIST.
+#-------------------------------------------------------------------
+def tc2(ser):
+    print "TC2: Single block message test for SHA-1."
+
+    tc2_sha1_expected = [0xa9993e36, 0x4706816a, 0xba3e2571,
+                         0x7850c26c, 0x9cd0d89d]
+
+    print "TC2: Expected digest values as specified by NIST:"
+    for i in tc2_sha1_expected:
+        print("0x%08x " % i)
+    print("")
+    single_block_test_sha1(NIST_512_SINGLE, ser)
+    
+
+#-------------------------------------------------------------------
+# TC3: SHA-1 Double block message test as specified by NIST.
+#-------------------------------------------------------------------
+def tc3(ser):
+    print "TC3: Double block message test for SHA-1."
+
+    tc3_1_sha1_expected = [0xF4286818, 0xC37B27AE, 0x0408F581,
+                           0x84677148, 0x4A566572]
+
+    tc3_2_sha1_expected = [0x84983E44, 0x1C3BD26E, 0xBAAE4AA1,
+                           0xF95129E5, 0xE54670F1]
+
+    print "TC3: Expected digest values for first block as specified by NIST:"
+    for i in tc3_1_sha1_expected:
+        print("0x%08x " % i)
+    print("")
+    print "TC3: Expected digest values for second block as specified by NIST:"
+    for i in tc3_2_sha1_expected:
+        print("0x%08x " % i)
+    print("")
+    double_block_test_sha1(NIST_512_DOUBLE0, NIST_512_DOUBLE1, ser)
+
+
+#-------------------------------------------------------------------
+# TC4: Read name and version from SHA-256 core.
+#-------------------------------------------------------------------
+def tc4(ser):
+    print "TC4: Reading name, type and version words from SHA-256 core."
+    my_cmd = [SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_NAME0, EOC]
+    write_serial_bytes(my_cmd, ser)
+    my_cmd = [SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_NAME1, EOC]
+    write_serial_bytes(my_cmd, ser)
+    my_cmd = [SOC, READ_CMD, SHA256_ADDR_PREFIX, SHA256_ADDR_VERSION, EOC]
+    write_serial_bytes(my_cmd, ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# TC5: SHA-256 Single block message test as specified by NIST.
+#-------------------------------------------------------------------
+def tc5(ser):
+    print "TC5: Single block message test for SHA-256."
+
+    tc5_sha256_expected = [0xBA7816BF, 0x8F01CFEA, 0x414140DE, 0x5DAE2223,
+                           0xB00361A3, 0x96177A9C, 0xB410FF61, 0xF20015AD]
+
+    print "TC5: Expected digest values as specified by NIST:"
+    for i in tc5_sha256_expected:
+        print("0x%08x " % i)
+    print("")
+    single_block_test_sha256(NIST_512_SINGLE, ser)
+
+
+#-------------------------------------------------------------------
+# TC6: SHA-256 Double block message test as specified by NIST.
+#-------------------------------------------------------------------
+def tc6(ser):
+    print "TC6: Double block message test for SHA-256."
+
+    tc6_1_sha256_expected = [0x85E655D6, 0x417A1795, 0x3363376A, 0x624CDE5C,
+                             0x76E09589, 0xCAC5F811, 0xCC4B32C1, 0xF20E533A]
+
+    tc6_2_sha256_expected = [0x248D6A61, 0xD20638B8, 0xE5C02693, 0x0C3E6039,
+                             0xA33CE459, 0x64FF2167, 0xF6ECEDD4, 0x19DB06C1]
+
+    print "TC6: Expected digest values for first block as specified by NIST:"
+    for i in tc6_1_sha256_expected:
+        print("0x%08x " % i)
+    print("")
+    print "TC6: Expected digest values for second block as specified by NIST:"
+    for i in tc6_2_sha256_expected:
+        print("0x%08x " % i)
+    print("")
+    double_block_test_sha256(NIST_512_DOUBLE0, NIST_512_DOUBLE1, ser)
+
+
+#-------------------------------------------------------------------
+# TC7: SHA-256 Huge message test.
+#-------------------------------------------------------------------
+def tc7(ser):
+    n = 1000
+    print "TC7: Message with %d blocks test for SHA-256." % n
+    tc7_block = ['\xaa', '\x55', '\xaa', '\x55', '\xde', '\xad', '\xbe', '\xef',
+                 '\x55', '\xaa', '\x55', '\xaa', '\xf0', '\x0f', '\xf0', '\x0f',
+
+                 '\xaa', '\x55', '\xaa', '\x55', '\xde', '\xad', '\xbe', '\xef',
+                 '\x55', '\xaa', '\x55', '\xaa', '\xf0', '\x0f', '\xf0', '\x0f',
+
+                 '\xaa', '\x55', '\xaa', '\x55', '\xde', '\xad', '\xbe', '\xef',
+                 '\x55', '\xaa', '\x55', '\xaa', '\xf0', '\x0f', '\xf0', '\x0f',
+
+                 '\xaa', '\x55', '\xaa', '\x55', '\xde', '\xad', '\xbe', '\xef',
+                 '\x55', '\xaa', '\x55', '\xaa', '\xf0', '\x0f', '\xf0', '\x0f']
+
+    tc7_expected = [0x7638f3bc, 0x500dd1a6, 0x586dd4d0, 0x1a1551af,
+                    0xd821d235, 0x2f919e28, 0xd5842fab, 0x03a40f2a]
+
+    huge_message_test_sha256(tc7_block, n, ser)
+
+    print "TC7: Expected digest values after %d blocks:" %n
+    for i in tc7_expected:
+        print("0x%08x " % i)
+    print("")
+
+
+#-------------------------------------------------------------------
+# TC8: Read name and version from SHA-512 core.
+#-------------------------------------------------------------------
+def tc8(ser):
+    print "TC8: Reading name, type and version words from SHA-512 core."
+    my_cmd = [SOC, READ_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_NAME0, EOC]
+    write_serial_bytes(my_cmd, ser)
+    my_cmd = [SOC, READ_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_NAME1, EOC]
+    write_serial_bytes(my_cmd, ser)
+    my_cmd = [SOC, READ_CMD, SHA512_ADDR_PREFIX, SHA512_ADDR_VERSION, EOC]
+    write_serial_bytes(my_cmd, ser)
+    print""
+
+
+#-------------------------------------------------------------------
+# TC9: Single block tests of SHA-512/x
+#
+# We do this for all modes.
+#-------------------------------------------------------------------
+def tc9(ser):
+    print "TC9: Single block message test for SHA-512/x."
+
+    tc9_224_expected = [0x4634270f, 0x707b6a54, 0xdaae7530, 0x460842e2,
+                        0x0e37ed26, 0x5ceee9a4, 0x3e8924aa]
+
+    tc9_256_expected = [0x53048e26, 0x81941ef9, 0x9b2e29b7, 0x6b4c7dab,
+                        0xe4c2d0c6, 0x34fc6d46, 0xe0e2f131, 0x07e7af23]
+
+    tc9_384_expected = [0xcb00753f, 0x45a35e8b, 0xb5a03d69, 0x9ac65007,
+                        0x272c32ab, 0x0eded163, 0x1a8b605a, 0x43ff5bed,
+                        0x8086072b, 0xa1e7cc23, 0x58baeca1, 0x34c825a7]
+
+    tc9_512_expected = [0xddaf35a1, 0x93617aba, 0xcc417349, 0xae204131,
+                        0x12e6fa4e, 0x89a97ea2, 0x0a9eeee6, 0x4b55d39a,
+                        0x2192992a, 0x274fc1a8, 0x36ba3c23, 0xa3feebbd,
+                        0x454d4423, 0x643ce80e, 0x2a9ac94f, 0xa54ca49f]
+
+    print "TC9-1: Expected digest values for SHA-512/224 as specified by NIST:"
+    for i in tc9_224_expected:
+        print("0x%08x " % i)
+    single_block_test_sha512x(NIST_1024_SINGLE, MODE_SHA_512_224, ser)
+    print("")
+
+    print "TC9-2: Expected digest values for SHA-512/256 as specified by NIST:"
+    for i in tc9_256_expected:
+        print("0x%08x " % i)
+    single_block_test_sha512x(NIST_1024_SINGLE, MODE_SHA_512_256, ser)
+    print("")
+
+    print "TC9-3: Expected digest values for SHA-384 as specified by NIST:"
+    for i in tc9_384_expected:
+        print("0x%08x " % i)
+    single_block_test_sha512x(NIST_1024_SINGLE, MODE_SHA_384, ser)
+    print("")
+
+    print "TC9-4: Expected digest values for SHA-512 as specified by NIST:"
+    for i in tc9_512_expected:
+        print("0x%08x " % i)
+    single_block_test_sha512x(NIST_1024_SINGLE, MODE_SHA_512, ser)
+    print("")
+
+
+#-------------------------------------------------------------------
+# TC10: Dual block tests of SHA-512/x
+#
+# We do this for all modes.
+#-------------------------------------------------------------------
+def tc10(ser):
+    print "TC9: Single block message test for SHA-512/x."
+
+    tc10_224_expected = [0x23fec5bb, 0x94d60b23, 0x30819264, 0x0b0c4533,
+                         0x35d66473, 0x4fe40e72, 0x68674af9]
+
+    tc10_256_expected = [0x3928e184, 0xfb8690f8, 0x40da3988, 0x121d31be,
+                         0x65cb9d3e, 0xf83ee614, 0x6feac861, 0xe19b563a]
+
+    tc10_384_expected = [0x09330c33, 0xf71147e8, 0x3d192fc7, 0x82cd1b47,
+                         0x53111b17, 0x3b3b05d2, 0x2fa08086, 0xe3b0f712,
+                         0xfcc7c71a, 0x557e2db9, 0x66c3e9fa, 0x91746039]
+
+    tc10_512_expected = [0x8e959b75, 0xdae313da, 0x8cf4f728, 0x14fc143f,
+                         0x8f7779c6, 0xeb9f7fa1, 0x7299aead, 0xb6889018,
+                         0x501d289e, 0x4900f7e4, 0x331b99de, 0xc4b5433a,
+                         0xc7d329ee, 0xb6dd2654, 0x5e96e55b, 0x874be909]
+
+    print "TC10-1: Expected digest values for SHA-512/224 as specified by NIST:"
+    for i in tc10_224_expected:
+        print("0x%08x " % i)
+    dual_block_test_sha512x(NIST_1024_DOUBLE0, NIST_1024_DOUBLE1, MODE_SHA_512_224, ser)
+    print("")
+
+    print "TC10-2: Expected digest values for SHA-512/256 as specified by NIST:"
+    for i in tc10_256_expected:
+        print("0x%08x " % i)
+    dual_block_test_sha512x(NIST_1024_DOUBLE0, NIST_1024_DOUBLE1, MODE_SHA_512_256, ser)
+    print("")
+
+    print "TC10-3: Expected digest values for SHA-384 as specified by NIST:"
+    for i in tc10_384_expected:
+        print("0x%08x " % i)
+    dual_block_test_sha512x(NIST_1024_DOUBLE0, NIST_1024_DOUBLE1, MODE_SHA_384, ser)
+    print("")
+
+    print "TC10-4: Expected digest values for SHA-512 as specified by NIST:"
+    for i in tc10_512_expected:
+        print("0x%08x " % i)
+    dual_block_test_sha512x(NIST_1024_DOUBLE0, NIST_1024_DOUBLE1, MODE_SHA_512, ser)
+    print("")
+
+
+#-------------------------------------------------------------------
+# main()
+#
+# Parse any arguments and run the tests.
+#-------------------------------------------------------------------
+def main():
+    # Open device
+    ser = serial.Serial()
+    ser.port=SERIAL_DEVICE
+    ser.baudrate=BAUD_RATE
+    ser.bytesize=DATA_BITS
+    ser.parity='N'
+    ser.stopbits=STOP_BITS
+    ser.timeout=1
+    ser.writeTimeout=0
+
+    if VERBOSE:
+        print "Setting up a serial port and starting a receive thread."
+
+    try:
+        ser.open()
+    except:
+        print "Error: Can't open serial device."
+        sys.exit(1)
+
+    # Try and switch baud rate in the FPGA and then here.
+    bit_rate_high = chr((BIT_RATE2 >> 8) & 0xff)
+    bit_rate_low = chr(BIT_RATE2 & 0xff)
+
+    if VERBOSE:
+        print("Changing to new baud rate.")
+        print("Baud rate: %d" % BAUD_RATE2)
+        print("Bit rate high byte: 0x%02x" % ord(bit_rate_high))
+        print("Bit rate low byte:  0x%02x" % ord(bit_rate_low))
+
+    write_serial_bytes([SOC, WRITE_CMD, UART_ADDR_PREFIX, UART_ADDR_BIT_RATE,
+                        '\x00', '\x00', bit_rate_high, bit_rate_low, EOC], ser)
+    ser.baudrate=BAUD_RATE2
+
+    try:
+        my_thread = threading.Thread(target=read_serial_thread, args=(ser,))
+    except:
+        print "Error: Can't start thread."
+        sys.exit()
+        
+    my_thread.daemon = True
+    my_thread.start()
+
+    # Run the enabled test cases.
+    tc_list = [(tc1, False), (tc2, False), (tc3, False), (tc4, False),
+               (tc5, False), (tc6, False), (tc7, True), (tc8, False),
+               (tc9, False), (tc10, False)]
+    for (test_case, action) in tc_list:
+        if action:
+            test_case(ser)
+
+    # Exit nicely.
+    time.sleep(50 * COMM_DELAY_TIME)
+    if VERBOSE:
+        print "Done. Closing device."
+    ser.close()
+
+
+#-------------------------------------------------------------------
+# __name__
+# Python thingy which allows the file to be run standalone as
+# well as parsed from within a Python interpreter.
+#-------------------------------------------------------------------
+if __name__=="__main__": 
+    # Run the main function.
+    sys.exit(main())
+
+
+#=======================================================================
+# EOF hash_tester.py
+#=======================================================================
diff --git a/toolruns/Makefile b/toolruns/Makefile
new file mode 100755
index 0000000..fcccaa9
--- /dev/null
+++ b/toolruns/Makefile
@@ -0,0 +1,72 @@
+#===================================================================
+#
+# Makefile
+# --------
+# Makefile for building coretest_hashes simulations.
+#
+#
+# Author: Joachim Strombergson
+# Copyright (c) 2014, SUNET
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or 
+# without modification, are permitted provided that the following 
+# conditions are met: 
+# 
+# 1. Redistributions of source code must retain the above copyright 
+#    notice, this list of conditions and the following disclaimer. 
+# 
+# 2. 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. 
+# 
+# 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 OWNER 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.
+#
+#===================================================================
+
+CORETEST_HASHES_SRC=../src/rtl/coretest_hashes.v
+
+CORETEST_SRC=../../coretest/src/rtl/coretest.v
+
+UART_SRC=../../uart/src/rtl/uart.v ../../uart/src/rtl/uart_core.v
+
+SHA1_SRC=../../sha1/src/rtl/sha1.v ../../sha1/src/rtl/sha1_core.v  ../../sha1/src/rtl/sha1_w_mem.v
+
+SHA256_SRC=../../sha256/src/rtl/sha256.v ../../sha256/src/rtl/sha256_core.v  ../../sha256/src/rtl/sha256_w_mem.v ../../sha256/src/rtl/sha256_k_constants.v
+
+CC=iverilog
+
+all: coretest_hashes
+
+
+coretest_hashes: $(CORETEST_HASHES_SRC) $(CORETEST_SRC) $(UART_SRC) $(SHA1_SRC) $(SHA256_SRC)
+	$(CC) -o coretest_hashes.sim  $(CORETEST_HASHES_SRC) $(CORETEST_SRC) $(UART_SRC) $(SHA1_SRC) $(SHA256_SRC)
+
+
+clean:
+	rm -f coretest_hashes.sim
+
+
+help:
+	@echo "Supported targets:"
+	@echo "------------------"
+	@echo "all:                 Build all targets."
+	@echo "coretest_hashes:     Build the coretest hashes target."
+	@echo "clean:               Delete all built files."
+
+#===================================================================
+# EOF Makefile
+#===================================================================
+
diff --git a/toolruns/quartus/terasic_c5g/coretest_hashes.qpf b/toolruns/quartus/terasic_c5g/coretest_hashes.qpf
new file mode 100644
index 0000000..6542ebb
--- /dev/null
+++ b/toolruns/quartus/terasic_c5g/coretest_hashes.qpf
@@ -0,0 +1,30 @@
+# -------------------------------------------------------------------------- #
+#
+# Copyright (C) 1991-2014 Altera Corporation
+# Your use of Altera Corporation's design tools, logic functions 
+# and other software and tools, and its AMPP partner logic 
+# functions, and any output files from any of the foregoing 
+# (including device programming or simulation files), and any 
+# associated documentation or information are expressly subject 
+# to the terms and conditions of the Altera Program License 
+# Subscription Agreement, Altera MegaCore Function License 
+# Agreement, or other applicable license agreement, including, 
+# without limitation, that your use is for the sole purpose of 
+# programming logic devices manufactured by Altera and sold by 
+# Altera or its authorized distributors.  Please refer to the 
+# applicable agreement for further details.
+#
+# -------------------------------------------------------------------------- #
+#
+# Quartus II 64-Bit
+# Version 13.1.2 Build 173 01/15/2014 SJ Web Edition
+# Date created = 08:59:21  March 17, 2014
+#
+# -------------------------------------------------------------------------- #
+
+QUARTUS_VERSION = "13.1"
+DATE = "08:59:21  March 17, 2014"
+
+# Revisions
+
+PROJECT_REVISION = "coretest_hashes"
diff --git a/toolruns/quartus/terasic_c5g/coretest_hashes.qsf b/toolruns/quartus/terasic_c5g/coretest_hashes.qsf
new file mode 100644
index 0000000..92ba7f8
--- /dev/null
+++ b/toolruns/quartus/terasic_c5g/coretest_hashes.qsf
@@ -0,0 +1,89 @@
+# -------------------------------------------------------------------------- #
+#
+# Copyright (C) 1991-2014 Altera Corporation
+# Your use of Altera Corporation's design tools, logic functions 
+# and other software and tools, and its AMPP partner logic 
+# functions, and any output files from any of the foregoing 
+# (including device programming or simulation files), and any 
+# associated documentation or information are expressly subject 
+# to the terms and conditions of the Altera Program License 
+# Subscription Agreement, Altera MegaCore Function License 
+# Agreement, or other applicable license agreement, including, 
+# without limitation, that your use is for the sole purpose of 
+# programming logic devices manufactured by Altera and sold by 
+# Altera or its authorized distributors.  Please refer to the 
+# applicable agreement for further details.
+#
+# -------------------------------------------------------------------------- #
+#
+# Quartus II 64-Bit
+# Version 13.1.2 Build 173 01/15/2014 SJ Web Edition
+# Date created = 08:59:21  March 17, 2014
+#
+# -------------------------------------------------------------------------- #
+#
+# Notes:
+#
+# 1) The default values for assignments are stored in the file:
+#		coretest_hashes_assignment_defaults.qdf
+#    If this file doesn't exist, see file:
+#		assignment_defaults.qdf
+#
+# 2) Altera recommends that you do not modify this file. This
+#    file is updated automatically by the Quartus II software
+#    and any changes you make may be lost or overwritten.
+#
+# -------------------------------------------------------------------------- #
+
+
+set_global_assignment -name FAMILY "Cyclone V"
+set_global_assignment -name DEVICE 5CGXFC5C6F27C7
+set_global_assignment -name TOP_LEVEL_ENTITY coretest_hashes
+set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1
+set_global_assignment -name PROJECT_CREATION_TIME_DATE "08:59:21  MARCH 17, 2014"
+set_global_assignment -name LAST_QUARTUS_VERSION 13.1
+set_global_assignment -name VERILOG_FILE ../../../../uart/src/rtl/uart_core.v
+set_global_assignment -name VERILOG_FILE ../../../../uart/src/rtl/uart.v
+set_global_assignment -name VERILOG_FILE ../../../../sha256/src/rtl/sha256_w_mem.v
+set_global_assignment -name VERILOG_FILE ../../../../sha256/src/rtl/sha256_k_constants.v
+set_global_assignment -name VERILOG_FILE ../../../../sha256/src/rtl/sha256_core.v
+set_global_assignment -name VERILOG_FILE ../../../../sha256/src/rtl/sha256.v
+set_global_assignment -name VERILOG_FILE ../../../../sha1/src/rtl/sha1_w_mem.v
+set_global_assignment -name VERILOG_FILE ../../../../sha1/src/rtl/sha1_core.v
+set_global_assignment -name VERILOG_FILE ../../../../sha1/src/rtl/sha1.v
+set_global_assignment -name VERILOG_FILE ../../../../coretest/src/rtl/coretest.v
+set_global_assignment -name VERILOG_FILE ../../../src/rtl/coretest_hashes.v
+set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
+set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
+set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
+set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 256
+set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
+set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
+set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
+set_location_assignment PIN_R20 -to clk
+set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk
+set_location_assignment PIN_P11 -to reset_n
+set_instance_assignment -name IO_STANDARD "1.2 V" -to reset_n
+set_location_assignment PIN_M9 -to rxd
+set_instance_assignment -name IO_STANDARD "2.5 V" -to rxd
+set_location_assignment PIN_L9 -to txd
+set_instance_assignment -name IO_STANDARD "2.5 V" -to txd
+set_location_assignment PIN_L7 -to debug[0]
+set_location_assignment PIN_K6 -to debug[1]
+set_location_assignment PIN_D8 -to debug[2]
+set_location_assignment PIN_E9 -to debug[3]
+set_location_assignment PIN_A5 -to debug[4]
+set_location_assignment PIN_B6 -to debug[5]
+set_location_assignment PIN_H8 -to debug[6]
+set_location_assignment PIN_H9 -to debug[7]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[1]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[2]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[3]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[4]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[5]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[6]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[7]
+set_instance_assignment -name IO_STANDARD "2.5 V" -to debug[0]
+
+set_global_assignment -name SDC_FILE coretest_hashes.sdc
+set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
\ No newline at end of file
diff --git a/toolruns/quartus/terasic_c5g/coretest_hashes.sdc b/toolruns/quartus/terasic_c5g/coretest_hashes.sdc
new file mode 100644
index 0000000..93e1282
--- /dev/null
+++ b/toolruns/quartus/terasic_c5g/coretest_hashes.sdc
@@ -0,0 +1,40 @@
+#************************************************************
+# THIS IS A WIZARD-GENERATED FILE.                           
+#
+# Version 13.1.2 Build 173 01/15/2014 SJ Web Edition
+#
+#************************************************************
+
+# Copyright (C) 1991-2014 Altera Corporation
+# Your use of Altera Corporation's design tools, logic functions 
+# and other software and tools, and its AMPP partner logic 
+# functions, and any output files from any of the foregoing 
+# (including device programming or simulation files), and any 
+# associated documentation or information are expressly subject 
+# to the terms and conditions of the Altera Program License 
+# Subscription Agreement, Altera MegaCore Function License 
+# Agreement, or other applicable license agreement, including, 
+# without limitation, that your use is for the sole purpose of 
+# programming logic devices manufactured by Altera and sold by 
+# Altera or its authorized distributors.  Please refer to the 
+# applicable agreement for further details.
+
+
+
+# Clock constraints
+
+create_clock -name "clk" -period 20.000ns [get_ports {clk}]
+
+
+# Automatically constrain PLL and other generated clocks
+derive_pll_clocks -create_base_clocks
+
+# Automatically calculate clock uncertainty to jitter and other effects.
+derive_clock_uncertainty
+
+# tsu/th constraints
+
+# tco constraints
+
+# tpd constraints
+
diff --git a/toolruns/quartus/terasic_c5g/cryptech_pre_build_image/coretest_hashes.sof b/toolruns/quartus/terasic_c5g/cryptech_pre_build_image/coretest_hashes.sof
new file mode 100644
index 0000000..d223599
Binary files /dev/null and b/toolruns/quartus/terasic_c5g/cryptech_pre_build_image/coretest_hashes.sof differ
diff --git a/toolruns/xilinx/novena/.gitignore b/toolruns/xilinx/novena/.gitignore
new file mode 100644
index 0000000..e8874ca
--- /dev/null
+++ b/toolruns/xilinx/novena/.gitignore
@@ -0,0 +1,58 @@
+coregen-tmp
+_xmsgs
+default.xreport
+netlist.lst
+*.lso
+*.xrpt
+*.bgn
+*.bit
+*.bld
+*.cfi
+*.drc
+*.lso
+*.map
+*.mcs
+*.mrp
+*.ncd
+*.ngc
+*.ngc_xst.xrpt
+*.ngd
+*.ngm
+*.pcf
+*.post_map.twr
+*.post_map.twx
+*.prj
+*.prm
+*.psr
+*.scr
+*.srp
+*.twr
+*.twx
+*_bd.bmm
+*_bitgen.xwb
+*_bitgen.xwbt
+*_err.twr
+*_err.twx
+*_ngdbuild.xrpt
+*_par.grf
+*_par.ncd
+*_par.pad
+*_par.par
+*_par.ptwx
+*_par.unroutes
+*_par.xpi
+*_par_pad.csv
+*_par_pad.txt
+*_simple.ngc_xst.xrpt
+*_simple_ngdbuild.xrpt
+*_summary.xml
+*_usage.xml
+par_usage_statistics.html
+programming_files
+smartguide.ncd
+smartpreview.twr
+smartpreview.twr
+usage_statistics_webtalk.html
+webtalk.log
+xlnx_auto_0_xdb
+xst
diff --git a/toolruns/xilinx/novena/Makefile.eim b/toolruns/xilinx/novena/Makefile.eim
new file mode 100644
index 0000000..848a2b2
--- /dev/null
+++ b/toolruns/xilinx/novena/Makefile.eim
@@ -0,0 +1,28 @@
+project = novena_eim
+vendor = xilinx
+family = spartan6
+part = xc6slx45csg324-3
+top_module = novena_fpga
+isedir = /opt/Xilinx/14.3/ISE_DS
+xil_env = . $(isedir)/settings64.sh
+
+vfiles = novena_eim.v \
+	../../../src/rtl/coretest_hashes_eim.v \
+	./common/sync_reset.v \
+	./ip/bclk_dll.v \
+	./ip/clk_dll.v \
+	./ip/dcm_delay.v \
+	../../../../sha1/src/rtl/sha1.v \
+	../../../../sha1/src/rtl/sha1_core.v \
+	../../../../sha1/src/rtl/sha1_w_mem.v \
+	../../../../sha256/src/rtl/sha256.v \
+	../../../../sha256/src/rtl/sha256_core.v \
+	../../../../sha256/src/rtl/sha256_k_constants.v \
+	../../../../sha256/src/rtl/sha256_w_mem.v \
+	../../../../sha512/src/rtl/sha512.v \
+	../../../../sha512/src/rtl/sha512_core.v \
+	../../../../sha512/src/rtl/sha512_h_constants.v \
+	../../../../sha512/src/rtl/sha512_k_constants.v \
+	../../../../sha512/src/rtl/sha512_w_mem.v
+
+include xilinx.mk
diff --git a/toolruns/xilinx/novena/Makefile.i2c b/toolruns/xilinx/novena/Makefile.i2c
new file mode 100644
index 0000000..81000ed
--- /dev/null
+++ b/toolruns/xilinx/novena/Makefile.i2c
@@ -0,0 +1,27 @@
+project = novena_i2c
+vendor = xilinx
+family = spartan6
+part = xc6slx45csg324-3
+top_module = novena_fpga
+isedir = /opt/Xilinx/14.3/ISE_DS
+xil_env = . $(isedir)/settings64.sh
+
+vfiles = novena_i2c.v \
+	../../../src/rtl/coretest_hashes_i2c.v \
+	../../../../coretest/src/rtl/coretest.v \
+	../../../../i2c/src/rtl/i2c_core.v \
+	../../../../i2c/src/rtl/i2c.v \
+	../../../../sha1/src/rtl/sha1_core.v \
+	../../../../sha1/src/rtl/sha1.v \
+	../../../../sha1/src/rtl/sha1_w_mem.v \
+	../../../../sha256/src/rtl/sha256_core.v \
+	../../../../sha256/src/rtl/sha256_k_constants.v \
+	../../../../sha256/src/rtl/sha256.v \
+	../../../../sha256/src/rtl/sha256_w_mem.v \
+	../../../../sha512/src/rtl/sha512_core.v \
+	../../../../sha512/src/rtl/sha512_h_constants.v \
+	../../../../sha512/src/rtl/sha512_k_constants.v \
+	../../../../sha512/src/rtl/sha512.v \
+	../../../../sha512/src/rtl/sha512_w_mem.v
+
+include xilinx.mk
diff --git a/toolruns/xilinx/novena/Makefile.i2c_simple b/toolruns/xilinx/novena/Makefile.i2c_simple
new file mode 100644
index 0000000..e3fedf4
--- /dev/null
+++ b/toolruns/xilinx/novena/Makefile.i2c_simple
@@ -0,0 +1,23 @@
+project = novena_i2c_simple
+vendor = xilinx
+family = spartan6
+part = xc6slx45csg324-3
+top_module = novena_fpga
+isedir = /opt/Xilinx/14.3/ISE_DS
+xil_env = . $(isedir)/settings64.sh
+
+vfiles = novena_i2c.v \
+	../../../src/rtl/coretest_hashes_i2c_simple.v \
+	../../../../coretest/src/rtl/coretest.v \
+	../../../../i2c/src/rtl/i2c_core.v \
+	../../../../sha1/src/rtl/sha1_core.v \
+	../../../../sha1/src/rtl/sha1_w_mem.v \
+	../../../../sha256/src/rtl/sha256_core.v \
+	../../../../sha256/src/rtl/sha256_k_constants.v \
+	../../../../sha256/src/rtl/sha256_w_mem.v \
+	../../../../sha512/src/rtl/sha512_core.v \
+	../../../../sha512/src/rtl/sha512_h_constants.v \
+	../../../../sha512/src/rtl/sha512_k_constants.v \
+	../../../../sha512/src/rtl/sha512_w_mem.v
+
+include xilinx.mk
diff --git a/toolruns/xilinx/novena/common/sync_reset.v b/toolruns/xilinx/novena/common/sync_reset.v
new file mode 100755
index 0000000..9cdbd6d
--- /dev/null
+++ b/toolruns/xilinx/novena/common/sync_reset.v
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////
+// Copyright (c) 2012, Andrew "bunnie" Huang  
+// (bunnie _aht_ bunniestudios "dote" com)
+// 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.
+//
+// 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.
+////////////////////////////////////////////////
+
+/// according to Xilinx WP272, all flip flops are reset to a "known value"
+/// by GSR. You're supposed to trust that. Of course, this "known value"
+/// isn't very explicitly stated, searching through the xilinx manuals
+/// it seems everything defaults to 0 except for stuff that's presetable.
+
+/// anyways, this module generates a local, synchronized reset based upon
+/// a global reset. The idea is to instantiate one of these near every
+/// terminal reset sink, so as to avoid loading down a global reset network.
+
+/// this should optimize utilization and speed a bit, and also allow the
+/// synthesizer to get more aggressive about using larger primitives
+
+//////////
+// the input is the asychronous reset of interest
+// and the clock to synchronize it to
+// the output is a synchronized reset that is at least four clock cycles wide
+module sync_reset (
+		   input wire glbl_reset, // async reset
+		   input wire clk,
+		   output wire reset 
+	      );
+
+   wire [3:0]       reschain;
+   
+   FDPE fdres0( .Q(reschain[0]), .C(clk), .CE(1'b1), .D(1'b0), .PRE(glbl_reset) );
+   FDPE fdres1( .Q(reschain[1]), .C(clk), .CE(1'b1), .D(reschain[0]), .PRE(glbl_reset) );
+   FDPE fdres2( .Q(reschain[2]), .C(clk), .CE(1'b1), .D(reschain[1]), .PRE(glbl_reset) );
+   FDPE fdres3( .Q(reschain[3]), .C(clk), .CE(1'b1), .D(reschain[2]), .PRE(glbl_reset) );
+
+   assign reset = reschain[3];
+
+endmodule // sync_reset
diff --git a/toolruns/xilinx/novena/ip/bclk_dll.v b/toolruns/xilinx/novena/ip/bclk_dll.v
new file mode 100755
index 0000000..cd7aa5b
--- /dev/null
+++ b/toolruns/xilinx/novena/ip/bclk_dll.v
@@ -0,0 +1,148 @@
+// file: bclk_dll.v
+// 
+// (c) Copyright 2008 - 2011 Xilinx, Inc. All rights reserved.
+// 
+// This file contains confidential and proprietary information
+// of Xilinx, Inc. and is protected under U.S. and
+// international copyright and other intellectual property
+// laws.
+// 
+// DISCLAIMER
+// This disclaimer is not a license and does not grant any
+// rights to the materials distributed herewith. Except as
+// otherwise provided in a valid license issued to you by
+// Xilinx, and to the maximum extent permitted by applicable
+// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
+// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
+// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
+// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
+// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
+// (2) Xilinx shall not be liable (whether in contract or tort,
+// including negligence, or under any other theory of
+// liability) for any loss or damage of any kind or nature
+// related to, arising under or in connection with these
+// materials, including for any direct, or any indirect,
+// special, incidental, or consequential loss or damage
+// (including loss of data, profits, goodwill, or any type of
+// loss or damage suffered as a result of any action brought
+// by a third party) even if such damage or loss was
+// reasonably foreseeable or Xilinx had been advised of the
+// possibility of the same.
+// 
+// CRITICAL APPLICATIONS
+// Xilinx products are not designed or intended to be fail-
+// safe, or for use in any application requiring fail-safe
+// performance, such as life-support or safety devices or
+// systems, Class III medical devices, nuclear facilities,
+// applications related to the deployment of airbags, or any
+// other applications that could lead to death, personal
+// injury, or severe property or environmental damage
+// (individually and collectively, "Critical
+// Applications"). Customer assumes the sole risk and
+// liability of any use of Xilinx products in Critical
+// Applications, subject only to applicable laws and
+// regulations governing limitations on product liability.
+// 
+// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
+// PART OF THIS FILE AT ALL TIMES.
+// 
+//----------------------------------------------------------------------------
+// User entered comments
+//----------------------------------------------------------------------------
+// None
+//
+//----------------------------------------------------------------------------
+// "Output    Output      Phase     Duty      Pk-to-Pk        Phase"
+// "Clock    Freq (MHz) (degrees) Cycle (%) Jitter (ps)  Error (ps)"
+//----------------------------------------------------------------------------
+// CLK_OUT1___133.014______0.000______50.0______350.360____150.000
+//
+//----------------------------------------------------------------------------
+// "Input Clock   Freq (MHz)    Input Jitter (UI)"
+//----------------------------------------------------------------------------
+// __primary_________133.014_____________0.01
+
+`timescale 1ps/1ps
+
+(* CORE_GENERATION_INFO = "bclk_dll,clk_wiz_v4_1,{component_name=bclk_dll,use_phase_alignment=false,use_min_o_jitter=false,use_max_i_jitter=false,use_dyn_phase_shift=false,use_inclk_switchover=false,use_dyn_reconfig=false,feedback_source=FDBK_AUTO,primtype_sel=DCM_SP,num_out_clk=1,clkin1_period=7.518,clkin2_period=7.518,use_power_down=false,use_reset=true,use_locked=true,use_inclk_stopped=false,use_status=false,use_freeze=false,use_clk_valid=false,feedback_type=SINGLE,clock_mgr_type=AUTO [...]
+module bclk_dll
+ (// Clock in ports
+  input         clk133in,
+  // Clock out ports
+  output        clk133,
+  // Status and control signals
+  input         RESET,
+  output        LOCKED
+ );
+
+  // Input buffering
+  //------------------------------------
+  assign clkin1 = clk133in;
+
+
+  // Clocking primitive
+  //------------------------------------
+
+  // Instantiation of the DCM primitive
+  //    * Unused inputs are tied off
+  //    * Unused outputs are labeled unused
+  wire        psdone_unused;
+  wire        locked_int;
+  wire [7:0]  status_int;
+  wire clkfb;
+  wire clk0;
+  wire clkfx;
+
+  DCM_SP
+  #(.CLKDV_DIVIDE          (2.000),
+    .CLKFX_DIVIDE          (2),
+    .CLKFX_MULTIPLY        (2),
+    .CLKIN_DIVIDE_BY_2     ("FALSE"),
+    .CLKIN_PERIOD          (7.518),
+    .CLKOUT_PHASE_SHIFT    ("NONE"),
+    .CLK_FEEDBACK          ("NONE"),
+    .DESKEW_ADJUST         ("SYSTEM_SYNCHRONOUS"),
+    .PHASE_SHIFT           (0),
+    .STARTUP_WAIT          ("FALSE"))
+  dcm_sp_inst
+    // Input clock
+   (.CLKIN                 (clkin1),
+    .CLKFB                 (clkfb),
+    // Output clocks
+    .CLK0                  (clk0),
+    .CLK90                 (),
+    .CLK180                (),
+    .CLK270                (),
+    .CLK2X                 (),
+    .CLK2X180              (),
+    .CLKFX                 (clkfx),
+    .CLKFX180              (),
+    .CLKDV                 (),
+    // Ports for dynamic phase shift
+    .PSCLK                 (1'b0),
+    .PSEN                  (1'b0),
+    .PSINCDEC              (1'b0),
+    .PSDONE                (),
+    // Other control and status signals
+    .LOCKED                (locked_int),
+    .STATUS                (status_int),
+ 
+    .RST                   (RESET),
+    // Unused pin- tie low
+    .DSSEN                 (1'b0));
+
+    assign LOCKED = locked_int;
+
+  // Output buffering
+  //-----------------------------------
+  // no phase alignment active, connect to ground
+  assign clkfb = 1'b0;
+
+  BUFG clkout1_buf
+   (.O   (clk133),
+    .I   (clkfx));
+
+
+
+
+endmodule
diff --git a/toolruns/xilinx/novena/ip/clk_dll.v b/toolruns/xilinx/novena/ip/clk_dll.v
new file mode 100755
index 0000000..1cd297c
--- /dev/null
+++ b/toolruns/xilinx/novena/ip/clk_dll.v
@@ -0,0 +1,162 @@
+// file: clk_dll.v
+// 
+// (c) Copyright 2008 - 2011 Xilinx, Inc. All rights reserved.
+// 
+// This file contains confidential and proprietary information
+// of Xilinx, Inc. and is protected under U.S. and
+// international copyright and other intellectual property
+// laws.
+// 
+// DISCLAIMER
+// This disclaimer is not a license and does not grant any
+// rights to the materials distributed herewith. Except as
+// otherwise provided in a valid license issued to you by
+// Xilinx, and to the maximum extent permitted by applicable
+// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
+// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
+// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
+// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
+// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
+// (2) Xilinx shall not be liable (whether in contract or tort,
+// including negligence, or under any other theory of
+// liability) for any loss or damage of any kind or nature
+// related to, arising under or in connection with these
+// materials, including for any direct, or any indirect,
+// special, incidental, or consequential loss or damage
+// (including loss of data, profits, goodwill, or any type of
+// loss or damage suffered as a result of any action brought
+// by a third party) even if such damage or loss was
+// reasonably foreseeable or Xilinx had been advised of the
+// possibility of the same.
+// 
+// CRITICAL APPLICATIONS
+// Xilinx products are not designed or intended to be fail-
+// safe, or for use in any application requiring fail-safe
+// performance, such as life-support or safety devices or
+// systems, Class III medical devices, nuclear facilities,
+// applications related to the deployment of airbags, or any
+// other applications that could lead to death, personal
+// injury, or severe property or environmental damage
+// (individually and collectively, "Critical
+// Applications"). Customer assumes the sole risk and
+// liability of any use of Xilinx products in Critical
+// Applications, subject only to applicable laws and
+// regulations governing limitations on product liability.
+// 
+// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
+// PART OF THIS FILE AT ALL TIMES.
+// 
+//----------------------------------------------------------------------------
+// User entered comments
+//----------------------------------------------------------------------------
+// None
+//
+//----------------------------------------------------------------------------
+// "Output    Output      Phase     Duty      Pk-to-Pk        Phase"
+// "Clock    Freq (MHz) (degrees) Cycle (%) Jitter (ps)  Error (ps)"
+//----------------------------------------------------------------------------
+// CLK_OUT1____50.000______0.000______50.0______200.000____150.000
+// CLK_OUT2____25.000______0.000______50.0______300.000____150.000
+// CLK_OUT3___100.000______0.000______50.0______400.000____150.000
+//
+//----------------------------------------------------------------------------
+// "Input Clock   Freq (MHz)    Input Jitter (UI)"
+//----------------------------------------------------------------------------
+// __primary______________50____________0.010
+
+`timescale 1ps/1ps
+
+(* CORE_GENERATION_INFO = "clk_dll,clk_wiz_v3_6,{component_name=clk_dll,use_phase_alignment=true,use_min_o_jitter=false,use_max_i_jitter=false,use_dyn_phase_shift=false,use_inclk_switchover=false,use_dyn_reconfig=false,feedback_source=FDBK_AUTO,primtype_sel=DCM_SP,num_out_clk=3,clkin1_period=20.0,clkin2_period=20.0,use_power_down=false,use_reset=true,use_locked=true,use_inclk_stopped=false,use_status=false,use_freeze=false,use_clk_valid=false,feedback_type=SINGLE,clock_mgr_type=AUTO,manu [...]
+module clk_dll
+ (// Clock in ports
+  input         clk50in,
+  // Clock out ports
+  output        clk50,
+  output        clk25,
+  output        clk3p2,
+  // Status and control signals
+  input         RESET,
+  output        LOCKED
+ );
+
+  // Input buffering
+  //------------------------------------
+  BUFG clkin1_buf
+   (.O (clkin1),
+    .I (clk50in));
+
+
+  // Clocking primitive
+  //------------------------------------
+
+  // Instantiation of the DCM primitive
+  //    * Unused inputs are tied off
+  //    * Unused outputs are labeled unused
+  wire        psdone_unused;
+  wire        locked_int;
+  wire [7:0]  status_int;
+  wire clkfb;
+  wire clk0;
+  wire clk2x;
+  wire clkdv;
+
+  DCM_SP
+  #(.CLKDV_DIVIDE          (2.000),
+    .CLKFX_DIVIDE          (1),
+    .CLKFX_MULTIPLY        (4),
+    .CLKIN_DIVIDE_BY_2     ("FALSE"),
+    .CLKIN_PERIOD          (20.0),
+    .CLKOUT_PHASE_SHIFT    ("NONE"),
+    .CLK_FEEDBACK          ("1X"),
+    .DESKEW_ADJUST         ("SYSTEM_SYNCHRONOUS"),
+    .PHASE_SHIFT           (0),
+    .STARTUP_WAIT          ("FALSE"))
+  dcm_sp_inst
+    // Input clock
+   (.CLKIN                 (clkin1),
+    .CLKFB                 (clkfb),
+    // Output clocks
+    .CLK0                  (clk0),
+    .CLK90                 (),
+    .CLK180                (),
+    .CLK270                (),
+    .CLK2X                 (clk2x),
+    .CLK2X180              (),
+    .CLKFX                 (),
+    .CLKFX180              (),
+    .CLKDV                 (clkdv),
+    // Ports for dynamic phase shift
+    .PSCLK                 (1'b0),
+    .PSEN                  (1'b0),
+    .PSINCDEC              (1'b0),
+    .PSDONE                (),
+    // Other control and status signals
+    .LOCKED                (locked_int),
+    .STATUS                (status_int),
+ 
+    .RST                   (RESET),
+    // Unused pin- tie low
+    .DSSEN                 (1'b0));
+
+    assign LOCKED = locked_int;
+
+  // Output buffering
+  //-----------------------------------
+  assign clkfb = clk50;
+
+  BUFG clkout1_buf
+   (.O   (clk50),
+    .I   (clk0));
+
+
+  BUFG clkout2_buf
+   (.O   (clk25),
+    .I   (clkdv));
+
+  BUFG clkout3_buf
+   (.O   (clk3p2),
+    .I   (clk2x));
+
+
+
+endmodule
diff --git a/toolruns/xilinx/novena/ip/dcm_delay.v b/toolruns/xilinx/novena/ip/dcm_delay.v
new file mode 100755
index 0000000..d422d2c
--- /dev/null
+++ b/toolruns/xilinx/novena/ip/dcm_delay.v
@@ -0,0 +1,168 @@
+// file: dcm_delay.v
+// 
+// (c) Copyright 2008 - 2011 Xilinx, Inc. All rights reserved.
+// 
+// This file contains confidential and proprietary information
+// of Xilinx, Inc. and is protected under U.S. and
+// international copyright and other intellectual property
+// laws.
+// 
+// DISCLAIMER
+// This disclaimer is not a license and does not grant any
+// rights to the materials distributed herewith. Except as
+// otherwise provided in a valid license issued to you by
+// Xilinx, and to the maximum extent permitted by applicable
+// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
+// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
+// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
+// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
+// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
+// (2) Xilinx shall not be liable (whether in contract or tort,
+// including negligence, or under any other theory of
+// liability) for any loss or damage of any kind or nature
+// related to, arising under or in connection with these
+// materials, including for any direct, or any indirect,
+// special, incidental, or consequential loss or damage
+// (including loss of data, profits, goodwill, or any type of
+// loss or damage suffered as a result of any action brought
+// by a third party) even if such damage or loss was
+// reasonably foreseeable or Xilinx had been advised of the
+// possibility of the same.
+// 
+// CRITICAL APPLICATIONS
+// Xilinx products are not designed or intended to be fail-
+// safe, or for use in any application requiring fail-safe
+// performance, such as life-support or safety devices or
+// systems, Class III medical devices, nuclear facilities,
+// applications related to the deployment of airbags, or any
+// other applications that could lead to death, personal
+// injury, or severe property or environmental damage
+// (individually and collectively, "Critical
+// Applications"). Customer assumes the sole risk and
+// liability of any use of Xilinx products in Critical
+// Applications, subject only to applicable laws and
+// regulations governing limitations on product liability.
+// 
+// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
+// PART OF THIS FILE AT ALL TIMES.
+// 
+//----------------------------------------------------------------------------
+// User entered comments
+//----------------------------------------------------------------------------
+// None
+//
+//----------------------------------------------------------------------------
+// "Output    Output      Phase     Duty      Pk-to-Pk        Phase"
+// "Clock    Freq (MHz) (degrees) Cycle (%) Jitter (ps)  Error (ps)"
+//----------------------------------------------------------------------------
+// CLK_OUT1___133.014______0.000______50.0______200.000____150.000
+// CLK_OUT2___133.014_____90.000______50.0______300.000____150.000
+// CLK_OUT3___133.014____180.000______50.0______300.000____150.000
+// CLK_OUT4___133.014____270.000______50.0______300.000____150.000
+//
+//----------------------------------------------------------------------------
+// "Input Clock   Freq (MHz)    Input Jitter (UI)"
+//----------------------------------------------------------------------------
+// __primary_________133.014____________0.010
+
+`timescale 1ps/1ps
+
+(* CORE_GENERATION_INFO = "dcm_delay,clk_wiz_v3_6,{component_name=dcm_delay,use_phase_alignment=true,use_min_o_jitter=false,use_max_i_jitter=false,use_dyn_phase_shift=false,use_inclk_switchover=false,use_dyn_reconfig=false,feedback_source=FDBK_ONCHIP,primtype_sel=DCM_SP,num_out_clk=4,clkin1_period=7.518,clkin2_period=7.518,use_power_down=false,use_reset=true,use_locked=true,use_inclk_stopped=false,use_status=false,use_freeze=false,use_clk_valid=false,feedback_type=SINGLE,clock_mgr_type=A [...]
+module dcm_delay
+ (// Clock in ports
+  input         clk133,
+  input         CLKFB_IN,
+  // Clock out ports
+  output        clk133out,
+  output        clk133_p90,
+  output        clk133_p180,
+  output        clk133_p270,
+  output        CLKFB_OUT,
+  // Status and control signals
+  input         RESET,
+  output        LOCKED
+ );
+
+  // Input buffering
+  //------------------------------------
+  assign clkin1 = clk133;
+
+
+  // Clocking primitive
+  //------------------------------------
+
+  // Instantiation of the DCM primitive
+  //    * Unused inputs are tied off
+  //    * Unused outputs are labeled unused
+  wire        psdone_unused;
+  wire        locked_int;
+  wire [7:0]  status_int;
+  wire clk0;
+  wire clk90;
+  wire clk180;
+  wire clk270;
+
+  DCM_SP
+  #(.CLKDV_DIVIDE          (2.000),
+    .CLKFX_DIVIDE          (1),
+    .CLKFX_MULTIPLY        (4),
+    .CLKIN_DIVIDE_BY_2     ("FALSE"),
+    .CLKIN_PERIOD          (7.518),
+    .CLKOUT_PHASE_SHIFT    ("NONE"),
+    .CLK_FEEDBACK          ("1X"),
+    .DESKEW_ADJUST         ("SYSTEM_SYNCHRONOUS"),
+    .PHASE_SHIFT           (0),
+    .STARTUP_WAIT          ("FALSE"))
+  dcm_sp_inst
+    // Input clock
+   (.CLKIN                 (clkin1),
+    .CLKFB                 (CLKFB_IN),
+    // Output clocks
+    .CLK0                  (clk0),
+    .CLK90                 (clk90),
+    .CLK180                (clk180),
+    .CLK270                (clk270),
+    .CLK2X                 (),
+    .CLK2X180              (),
+    .CLKFX                 (),
+    .CLKFX180              (),
+    .CLKDV                 (),
+    // Ports for dynamic phase shift
+    .PSCLK                 (1'b0),
+    .PSEN                  (1'b0),
+    .PSINCDEC              (1'b0),
+    .PSDONE                (),
+    // Other control and status signals
+    .LOCKED                (locked_int),
+    .STATUS                (status_int),
+ 
+    .RST                   (RESET),
+    // Unused pin- tie low
+    .DSSEN                 (1'b0));
+
+    assign LOCKED = locked_int;
+
+  // Output buffering
+  //-----------------------------------
+  assign CLKFB_OUT = clk0;
+
+  BUFG clkout1_buf
+   (.O   (clk133out),
+    .I   (clk0));
+
+
+  BUFG clkout2_buf
+   (.O   (clk133_p90),
+    .I   (clk90));
+
+  BUFG clkout3_buf
+   (.O   (clk133_p180),
+    .I   (clk180));
+
+  BUFG clkout4_buf
+   (.O   (clk133_p270),
+    .I   (clk270));
+
+
+
+endmodule
diff --git a/toolruns/xilinx/novena/novena_eim.bmm b/toolruns/xilinx/novena/novena_eim.bmm
new file mode 100644
index 0000000..e69de29
diff --git a/toolruns/xilinx/novena/novena_eim.ucf b/toolruns/xilinx/novena/novena_eim.ucf
new file mode 100755
index 0000000..2246f61
--- /dev/null
+++ b/toolruns/xilinx/novena/novena_eim.ucf
@@ -0,0 +1,653 @@
+###  Autogenerated on 2013-May-10 01:38 by edifToUcf.py
+###  Extracting designator U800 from EDIF netlist novena-dvt.EDF
+
+### extended performance annotation
+CONFIG VCCAUX  = 3.3;
+# Valid values are 2.5 and 3.3
+CONFIG MCB_PERFORMANCE  = EXTENDED;
+
+### define setup/hold constraints for EIM
+OFFSET = IN 4125 ps VALID 4750 ps BEFORE "bclk";
+# this was also supposed to be IN 4125 ps VALID 625 ps BEFORE "bclk", but
+# the computation gives me -3.5ns slack on hold time, so...add 4750 ps to the hold again.
+#5200 ps is the true limit
+OFFSET = OUT 5100 ps AFTER "bclk";
+
+
+##################################################################################
+# Timing Ignore constraints for paths crossing the clock domain 
+##################################################################################
+NET "eim_d_t*" TIG;
+
+#NET "reg_wo_40010/state[*]" TIG;  # ignore fast timing on expansion-side GPIO
+#NET "reg_wo_40012/state[*]" TIG;
+#NET "reg_ro_41010/state[*]" TIG;
+#NET "reg_ro_41012/state[*]" TIG;
+
+##################################################################################
+# Force ODDR's to meet output timing constraint
+##################################################################################
+INST "oddr2_eim0" IOB =FORCE;
+INST "oddr2_eim1" IOB =FORCE;
+INST "oddr2_eim2" IOB =FORCE;
+INST "oddr2_eim3" IOB =FORCE;
+INST "oddr2_eim4" IOB =FORCE;
+INST "oddr2_eim5" IOB =FORCE;
+INST "oddr2_eim6" IOB =FORCE;
+INST "oddr2_eim7" IOB =FORCE;
+INST "oddr2_eim8" IOB =FORCE;
+INST "oddr2_eim9" IOB =FORCE;
+INST "oddr2_eimA" IOB =FORCE;
+INST "oddr2_eimB" IOB =FORCE;
+INST "oddr2_eimC" IOB =FORCE;
+INST "oddr2_eimD" IOB =FORCE;
+INST "oddr2_eimE" IOB =FORCE;
+INST "oddr2_eimF" IOB =FORCE;
+
+#INST "bclk_o_fbk" LOC="REG_T_X43Y140";
+#INST "bclk_o_fbk" LOC="BUFIO2_X2Y29";
+
+############################################################################
+## Clock constraints                                                        
+############################################################################
+NET "clk" TNM_NET = "clk_tnm";
+NET "bclk" TNM_NET = "bclk_tnm";
+TIMESPEC TS_clk = PERIOD "clk_tnm" 50 MHz;
+TIMESPEC TS_bclk = PERIOD "bclk_tnm" 133 MHz;
+
+
+NET "APOPTOSIS" LOC = K1;
+NET "APOPTOSIS" IOSTANDARD = LVCMOS33;
+
+# AUD6_TFS
+# NET "DEL_CONT" LOC = A4;
+# NET "DEL_CONT" IOSTANDARD = LVCMOS33;
+# AUD6_TXC
+# NET "DEL_RST_L" LOC = B4;
+# NET "DEL_RST_L" IOSTANDARD = LVCMOS33;
+# NET "AUD6_TXD" LOC = A6;
+# NET "AUD6_TXD" IOSTANDARD = LVCMOS33;
+# NET "AUD_MCLK" LOC = H6;
+# NET "AUD_MCLK" IOSTANDARD = LVCMOS33;
+# AUD_MIC_CLK
+#NET "ZEROVEN" LOC = G3;
+#NET "ZEROVEN" IOSTANDARD = LVCMOS33;
+# NET "AUD_MIC_DAT" LOC = C5;
+# NET "AUD_MIC_DAT" IOSTANDARD = LVCMOS33;
+
+# NET "BATT_NRST" LOC = N1;
+# NET "BATT_NRST" IOSTANDARD = LVCMOS33;
+# NET "BATT_REFLASH_ALRT" LOC = N2;
+# NET "BATT_REFLASH_ALRT" IOSTANDARD = LVCMOS33;
+
+NET "CLK2_N" LOC = H1;
+NET "CLK2_N" IOSTANDARD = LVDS_33;
+NET "CLK2_N" DIFF_TERM = "TRUE";
+NET "CLK2_P" LOC = H2;
+NET "CLK2_P" IOSTANDARD = LVDS_33;
+NET "CLK2_P" DIFF_TERM = "TRUE";
+
+# NET "DDC_SCL" LOC = J6;
+# NET "DDC_SCL" IOSTANDARD = LVCMOS33;
+# NET "DDC_SDA" LOC = F2;
+# NET "DDC_SDA" IOSTANDARD = LVCMOS33;
+
+# ECSPI3_MISO
+#NET "SPI1_MISO" LOC = A3;
+#NET "SPI1_MISO" IOSTANDARD = LVCMOS33;
+# # R13 
+# ECSPI3_MOSI
+#NET "SPI1_MOSI" LOC = A2;
+#NET "SPI1_MOSI" IOSTANDARD = LVCMOS33;
+# ECSPI3_RDY
+#NET "SPI1_DELI_SEL" LOC = A5;
+#NET "SPI1_DELI_SEL" IOSTANDARD = LVCMOS33;
+# # R15 
+# ECSPI3_SCLK
+#NET "SPI1_SCLK" LOC = D9;
+#NET "SPI1_SCLK" IOSTANDARD = LVCMOS33;
+# ECSPI3_SS2
+#NET "SAMPEN" LOC = B3;
+#NET "SAMPEN" IOSTANDARD = LVCMOS33;
+
+NET "EIM_BCLK" LOC = C9;
+NET "EIM_BCLK" IOSTANDARD = LVCMOS33;
+NET "EIM_CS[0]" LOC = B11;
+NET "EIM_CS[0]" IOSTANDARD = LVCMOS33;
+NET "EIM_CS[1]" LOC = A15;
+NET "EIM_CS[1]" IOSTANDARD = LVCMOS33;
+
+NET "EIM_DA[0]" LOC = G9;
+NET "EIM_DA[0]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[0]" SLEW = SLOW;
+NET "EIM_DA[1]" LOC = A10;
+NET "EIM_DA[1]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[1]" SLEW = SLOW;
+NET "EIM_DA[2]" LOC = F9;
+NET "EIM_DA[2]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[2]" SLEW = SLOW;
+NET "EIM_DA[3]" LOC = B9;
+NET "EIM_DA[3]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[3]" SLEW = SLOW;
+NET "EIM_DA[4]" LOC = E13;
+NET "EIM_DA[4]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[4]" SLEW = SLOW;
+NET "EIM_DA[5]" LOC = F13;
+NET "EIM_DA[5]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[5]" SLEW = SLOW;
+NET "EIM_DA[6]" LOC = A9;
+NET "EIM_DA[6]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[6]" SLEW = SLOW;
+NET "EIM_DA[7]" LOC = A8;
+NET "EIM_DA[7]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[7]" SLEW = SLOW;
+NET "EIM_DA[8]" LOC = B8;
+NET "EIM_DA[8]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[8]" SLEW = SLOW;
+NET "EIM_DA[9]" LOC = D8;
+NET "EIM_DA[9]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[9]" SLEW = SLOW;
+NET "EIM_DA[10]" LOC = D11;
+NET "EIM_DA[10]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[10]" SLEW = SLOW;
+NET "EIM_DA[11]" LOC = C8;
+NET "EIM_DA[11]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[11]" SLEW = SLOW;
+NET "EIM_DA[12]" LOC = C7;
+NET "EIM_DA[12]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[12]" SLEW = SLOW;
+
+NET "EIM_DA[13]" LOC = C11;
+NET "EIM_DA[13]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[13]" SLEW = SLOW;
+
+NET "EIM_DA[14]" LOC = C4;
+NET "EIM_DA[14]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[14]" SLEW = SLOW;
+NET "EIM_DA[15]" LOC = B6;
+NET "EIM_DA[15]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[15]" SLEW = SLOW;
+
+# EIM_A16
+NET "EIM_A[16]" LOC = A11;
+NET "EIM_A[16]" IOSTANDARD = LVCMOS33;
+NET "EIM_A[16]" SLEW = SLOW;
+
+# EIM_A17
+NET "EIM_A[17]" LOC = B12;
+NET "EIM_A[17]" IOSTANDARD = LVCMOS33;
+NET "EIM_A[17]" SLEW = SLOW;
+# EIM_A18
+NET "EIM_A[18]" LOC = D14;
+NET "EIM_A[18]" IOSTANDARD = LVCMOS33;
+NET "EIM_A[18]" SLEW = SLOW;
+
+# EIM_LBA
+NET "EIM_LBA" LOC = B14;
+NET "EIM_LBA" IOSTANDARD = LVCMOS33;
+NET "EIM_LBA" SLEW = SLOW;
+NET "EIM_OE" LOC = C10;
+NET "EIM_OE" IOSTANDARD = LVCMOS33;
+NET "EIM_OE" SLEW = SLOW;
+NET "EIM_RW" LOC = C14;
+NET "EIM_RW" SLEW = SLOW;
+NET "EIM_RW" IOSTANDARD = LVCMOS33;
+
+#NET "LED3" LOC = C14;
+#NET "LED3" IOSTANDARD = LVCMOS33;
+# NET "EIM_WAIT" LOC = A7;
+# NET "EIM_WAIT" IOSTANDARD = LVCMOS33;
+
+# #NET "FPGA_DONE" LOC = V17;
+# #NET "FPGA_DONE" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_HSWAPEN" LOC = D4;
+# #NET "FPGA_HSWAPEN" IOSTANDARD = LVCMOS33;
+# FPGA_INIT_N
+#NET "LED2" LOC = U3;
+#NET "LED2" IOSTANDARD = LVCMOS33;
+
+NET "FPGA_LED2" LOC = A16;
+NET "FPGA_LED2" IOSTANDARD = LVCMOS33;
+NET "FPGA_LED2" SLEW = SLOW;
+
+# NET "FPGA_LSPI_CLK" LOC = D3;
+# NET "FPGA_LSPI_CLK" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_CS" LOC = D1;
+# NET "FPGA_LSPI_CS" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_HOLD" LOC = E3;
+# NET "FPGA_LSPI_HOLD" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_MISO" LOC = D2;
+# NET "FPGA_LSPI_MISO" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_MOSI" LOC = C2;
+# NET "FPGA_LSPI_MOSI" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_WP" LOC = C1;
+# NET "FPGA_LSPI_WP" IOSTANDARD = LVCMOS33;
+
+# #NET "FPGA_M0" LOC = T15;
+# #NET "FPGA_M0" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_M1" LOC = N12;
+# #NET "FPGA_M1" IOSTANDARD = LVCMOS33;
+
+# #NET "FPGA_RESET_N" LOC = V2;
+# #NET "FPGA_RESET_N" IOSTANDARD = TMDS_33;
+# #NET "FPGA_SUSPEND" LOC = R16;
+# #NET "FPGA_SUSPEND" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TCK" LOC = A17;
+# #NET "FPGA_TCK" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TDI" LOC = D15;
+# #NET "FPGA_TDI" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TDO" LOC = D16;
+# #NET "FPGA_TDO" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TMS" LOC = B18;
+# #NET "FPGA_TMS" IOSTANDARD = LVCMOS33;
+
+# # NET "GND" LOC = A1;# A18 B7 B13 C3 C16 D5 D10 E15 G2 G5 G12 G17 H8 H10 J4 J9 J11 J15 K8 K10 L9 L11 M2 M6 M17 N13 R1 R4 R9 R14 R18 T16 U6 U12 V1 V18 
+# # NET "GND" IOSTANDARD = LVCMOS33;
+
+#NET "I2C3_SCL" LOC = P4;
+#NET "I2C3_SCL" IOSTANDARD = LVCMOS33;
+#NET "I2C3_SDA" LOC = P3;
+#NET "I2C3_SDA" IOSTANDARD = LVCMOS33;
+
+# # NET "P1_2V" LOC = G7;# H9 H11 J8 J10 K9 K11 L8 L10 M7 M12 
+# # NET "P1_2V" IOSTANDARD = LVCMOS33;
+# # NET "P3_3V_DELAYED" LOC = B1;# B5 B10 B15 B17 D7 D13 E2 E5 E9 E10 E14 E17 G4 G10 G15 J2 J5 J12 J14 J17 K7 M4 M9 M15 P5 P9 P10 P14 R2 R6 R12 R17 U4 U9 U14 
+# # NET "P3_3V_DELAYED" IOSTANDARD = LVCMOS33;
+
+NET "RESETBMCU" LOC = F1;
+NET "RESETBMCU" IOSTANDARD = LVCMOS33;
+
+# NET "SMB_SCL" LOC = N3;
+# NET "SMB_SCL" IOSTANDARD = LVCMOS33;
+# NET "SMB_SDA" LOC = N4;
+# NET "SMB_SDA" IOSTANDARD = LVCMOS33;
+
+# NET "UART4_CTS" LOC = U1;
+# NET "UART4_CTS" IOSTANDARD = LVCMOS33;
+# NET "UART4_RTS" LOC = U2;
+# NET "UART4_RTS" IOSTANDARD = LVCMOS33;
+# NET "UART4_RXD" LOC = T1;
+# NET "UART4_RXD" IOSTANDARD = LVCMOS33;
+# NET "UART4_TXD" LOC = P1;
+# NET "UART4_TXD" IOSTANDARD = LVCMOS33;
+
+# UIM_CLK
+#NET "MODES" LOC = B16;
+#NET "MODES" IOSTANDARD = LVCMOS33;
+# UIM_DATA
+#NET "ENC_SW" LOC = A12;
+#NET "ENC_SW" IOSTANDARD = LVCMOS33;
+# #NET "UIM_PWR" LOC = C18;
+# #NET "UIM_PWR" IOSTANDARD = SSTL15_II;
+# #NET "UIM_PWRON" LOC = A14;
+# #NET "UIM_PWRON" IOSTANDARD = LVCMOS33;
+# UIM_RESET
+#NET "SW_BACKUP" LOC = C15;
+#NET "SW_BACKUP" IOSTANDARD = LVCMOS33;
+
+##############
+# DDR3
+##############
+
+# NET "F_BA[2]" IOSTANDARD = SSTL15_II;
+# NET "F_BA[1]" IOSTANDARD = SSTL15_II;
+# NET "F_BA[0]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[13]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[12]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[11]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[10]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[9]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[8]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[7]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[6]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[5]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[4]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[3]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[2]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[1]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[0]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[15]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[14]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[13]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[12]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[11]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[10]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[9]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[8]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[7]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[6]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[5]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[4]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[3]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[2]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[1]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[0]" IOSTANDARD = SSTL15_II;
+# NET "F_CAS_N" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_CKE" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_ODT" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_RST_N" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_RZQ" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_ZIO" IOSTANDARD = SSTL15_II;
+# NET "F_LDM" IOSTANDARD = SSTL15_II;
+# NET "F_RAS_N" IOSTANDARD = SSTL15_II;
+# NET "F_UDM" IOSTANDARD = SSTL15_II;
+# NET "F_WE_N" IOSTANDARD = SSTL15_II;
+
+
+# NET "F_BA[0]" LOC = H13;
+# NET "F_BA[1]" LOC = H14;
+# NET "F_BA[2]" LOC = K13;
+# NET "F_BA[0]" OUT_TERM = UNTUNED_50;
+# NET "F_BA[1]" OUT_TERM = UNTUNED_50;
+# NET "F_BA[2]" OUT_TERM = UNTUNED_50;
+
+# NET "F_CAS_N" LOC = K16;
+# NET "F_CAS_N" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_A[0]" LOC = H15;
+# NET "F_DDR3_A[1]" LOC = H16;
+# NET "F_DDR3_A[10]" LOC = E16;
+# NET "F_DDR3_A[11]" LOC = G14;
+# NET "F_DDR3_A[12]" LOC = D18;
+# NET "F_DDR3_A[13]" LOC = C17;
+# NET "F_DDR3_A[2]" LOC = F18;
+# NET "F_DDR3_A[3]" LOC = J13;
+# NET "F_DDR3_A[4]" LOC = E18;
+# NET "F_DDR3_A[5]" LOC = L12;
+# NET "F_DDR3_A[6]" LOC = L13;
+# NET "F_DDR3_A[7]" LOC = F17;
+# NET "F_DDR3_A[8]" LOC = H12;
+# NET "F_DDR3_A[9]" LOC = G13;
+# NET "F_DDR3_A[0]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[10]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[11]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[12]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[13]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[1]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[2]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[3]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[4]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[5]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[6]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[7]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[8]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[9]" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_CKE" LOC = D17;
+# NET "F_DDR3_CKE" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_CK_N" LOC = G18;
+# NET "F_DDR3_CK_N" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_DDR3_CK_N" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_CK_P" LOC = G16;
+# NET "F_DDR3_CK_P" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_DDR3_CK_P" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_D[0]" LOC = M16;
+# NET "F_DDR3_D[1]" LOC = M18;
+# NET "F_DDR3_D[10]" LOC = P17;
+# NET "F_DDR3_D[11]" LOC = P18;
+# NET "F_DDR3_D[12]" LOC = T17;
+# NET "F_DDR3_D[13]" LOC = T18;
+# NET "F_DDR3_D[14]" LOC = U17;
+# NET "F_DDR3_D[15]" LOC = U18;
+# NET "F_DDR3_D[2]" LOC = L17;
+# NET "F_DDR3_D[3]" LOC = L18;
+# NET "F_DDR3_D[4]" LOC = H17;
+# NET "F_DDR3_D[5]" LOC = H18;
+# NET "F_DDR3_D[6]" LOC = J16;
+# NET "F_DDR3_D[7]" LOC = J18;
+# NET "F_DDR3_D[8]" LOC = N17;
+# NET "F_DDR3_D[9]" LOC = N18;
+# NET "F_DDR3_D[0]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[10]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[11]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[12]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[13]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[14]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[15]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[1]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[2]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[3]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[4]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[5]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[6]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[7]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[8]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[9]" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_RST_N" LOC = F14;
+# NET "F_DDR3_RST_N" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_ODT" LOC = K14;
+# NET "F_DDR3_ODT" OUT_TERM = UNTUNED_50;
+
+# NET "F_RAS_N" LOC = K15;
+# NET "F_RAS_N" OUT_TERM = UNTUNED_50;
+# NET "F_UDM" LOC = L15;
+# NET "F_UDM" OUT_TERM = UNTUNED_50;
+# NET "F_UDQS_N" LOC = N16;
+# NET "F_UDQS_N" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_UDQS_N" OUT_TERM = UNTUNED_50;
+# NET "F_UDQS_P" LOC = N15;
+# NET "F_UDQS_P" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_UDQS_P" OUT_TERM = UNTUNED_50;
+# NET "F_LDM" LOC = L16;
+# NET "F_LDM" OUT_TERM = UNTUNED_50;
+# NET "F_LDQS_N" LOC = K18;
+# NET "F_LDQS_N" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_LDQS_N" OUT_TERM = UNTUNED_50;
+# NET "F_LDQS_P" LOC = K17;
+# NET "F_LDQS_P" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_LDQS_P" OUT_TERM = UNTUNED_50;
+# NET "F_WE_N" LOC = K12;
+# NET "F_WE_N" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_RZQ" LOC = F15;
+# NET "F_DDR3_RZQ" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_ZIO" LOC = M14;
+# NET "F_DDR3_ZIO" OUT_TERM = UNTUNED_50;
+
+#NET "F_BA[*]" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_CAS_N" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_A[*]" SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_CKE" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+
+#NET "F_DDR3_D[*]" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_RST_N" IOSTANDARD = SSTL15_II | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_ODT" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_RAS_N" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_UDM" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_LDM" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_WE_N" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_RZQ" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_DDR3_ZIO" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+
+##############
+# I/O connector
+##############
+#NET "F_DX0" LOC = K6;
+#NET "F_DX0" IOSTANDARD = LVCMOS33;
+#NET "F_DX0" SLEW = SLOW;
+# NET "F_DX0" PULLUP;
+
+#NET "F_DX1" LOC = L7;
+#NET "F_DX1" IOSTANDARD = LVCMOS33;
+#NET "F_DX1" SLEW = SLOW;
+
+#NET "F_DX2" LOC = H3;
+#NET "F_DX2" IOSTANDARD = LVCMOS33;
+#NET "F_DX2" SLEW = SLOW;
+
+#NET "F_DX3" LOC = H4;
+#NET "F_DX3" IOSTANDARD = LVCMOS33;
+#NET "F_DX3" SLEW = SLOW;
+# NET "F_DX3" PULLUP;
+
+# NET "F_DX[4]" LOC = J1;
+# NET "F_DX[5]" LOC = J3;
+#NET "F_DX6" LOC = L3;
+#NET "F_DX6" IOSTANDARD = LVCMOS33;
+#NET "F_DX6" SLEW = SLOW;
+
+#NET "F_DX7" LOC = L4;
+#NET "F_DX7" IOSTANDARD = LVCMOS33;
+#NET "F_DX7" SLEW = SLOW;
+
+# NET "F_DX8" LOC = K2;
+# NET "F_DX8" IOSTANDARD = LVCMOS33;
+# NET "F_DX8" SLEW = SLOW;
+
+#NET "F_DX11" LOC = M1;
+#NET "F_DX11" IOSTANDARD = LVCMOS33;
+#NET "F_DX11" SLEW = SLOW;
+#NET "F_DX12" LOC = M3;
+#NET "F_DX12" IOSTANDARD = LVCMOS33;
+#NET "F_DX12" SLEW = SLOW;
+
+#NET "F_DX13" LOC = P2;
+#NET "F_DX13" IOSTANDARD = LVCMOS33;
+#NET "F_DX13" SLEW = SLOW;
+
+#NET "F_DX14" LOC = T2;
+#NET "F_DX14" IOSTANDARD = LVCMOS33;
+#NET "F_DX14" SLEW = SLOW;
+
+#NET "F_DX15" LOC = M5;
+#NET "F_DX15" IOSTANDARD = LVCMOS33;
+#NET "F_DX15" SLEW = SLOW;
+
+# NET "F_DX[16]" LOC = L6;
+#NET "F_DX17" LOC = G1;
+#NET "F_DX17" IOSTANDARD = LVCMOS33;
+#NET "F_DX17" SLEW = SLOW;
+#NET "F_DX18" LOC = H7;
+#NET "F_DX18" IOSTANDARD = LVCMOS33;
+#NET "F_DX18" SLEW = SLOW;
+
+# NET "F_DX[*]" IOSTANDARD = LVCMOS33;
+
+#NET "F_LVDS_N0" LOC = P6;
+#NET "F_LVDS_N0" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_N0" SLEW = SLOW;
+#NET "F_LVDS_P0" LOC = N5;
+#NET "F_LVDS_P0" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_P0" SLEW = SLOW;
+# NET "F_LVDS_N1" LOC = V4;
+# NET "F_LVDS_N1" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N1" SLEW = SLOW;
+# NET "F_LVDS_P1" LOC = T4;
+# NET "F_LVDS_P1" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_P1" SLEW = SLOW;
+# NET "F_LVDS_N2" LOC = T3;
+# NET "F_LVDS_N2" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N2" SLEW = SLOW;
+# NET "F_LVDS_P[2]" LOC = R3;
+
+# NET "F_LVDS_N3" LOC = V5;
+# NET "F_LVDS_N3" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N3" SLEW = SLOW;
+
+# NET "F_LVDS_P[3]" LOC = U5;
+# NET "F_LVDS_N4" LOC = T5;
+# NET "F_LVDS_N4" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N4" SLEW = SLOW;
+#NET "F_LVDS_P4" LOC = R5;
+#NET "F_LVDS_P4" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_P4" SLEW = SLOW;
+
+# NET "F_LVDS_N5" LOC = T7;
+# NET "F_LVDS_N5" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N5" SLEW = SLOW;
+
+# NET "F_LVDS_P5" LOC = R7;
+# NET "F_LVDS_P5" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_P5" SLEW = SLOW;
+
+# NET "F_LVDS_N[6]" LOC = V6;
+# NET "F_LVDS_P[6]" LOC = T6;
+#NET "F_LVDS_N7" LOC = V7;
+#NET "F_LVDS_N7" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_N7" SLEW = SLOW;
+
+#NET "F_LVDS_P7" LOC = U7;
+#NET "F_LVDS_P7" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_P7" SLEW = SLOW;
+
+# NET "F_LVDS_N8" LOC = V8;
+# NET "F_LVDS_N8" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N8" SLEW = SLOW;
+
+# NET "F_LVDS_P[8]" LOC = U8;
+# NET "F_LVDS_N[9]" LOC = V9;
+#NET "F_LVDS_P9" LOC = T9;
+#NET "F_LVDS_P9" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_P9" SLEW = SLOW;
+# NET "F_LVDS_N[10]" LOC = V11;
+# NET "F_LVDS_P[10]" LOC = U11;
+#NET "F_LVDS_N11" LOC = T11;
+#NET "F_LVDS_N11" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_N11" SLEW = SLOW;
+#NET "F_LVDS_P11" LOC = R11;
+#NET "F_LVDS_P11" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_P11" SLEW = SLOW;
+# NET "F_LVDS_N[12]" LOC = V13;
+# NET "F_LVDS_P[12]" LOC = U13;
+# NET "F_LVDS_N[13]" LOC = V14;
+# NET "F_LVDS_P[13]" LOC = T14;
+# NET "F_LVDS_N[14]" LOC = V16;
+# NET "F_LVDS_P[14]" LOC = U16;
+#NET "F_LVDS_N15" LOC = V10;
+#NET "F_LVDS_N15" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_N15" SLEW = SLOW;
+#NET "F_LVDS_P15" LOC = U10;
+#NET "F_LVDS_P15" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_P15" SLEW = SLOW;
+
+# NET "F_LVDS_P[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+# NET "F_LVDS_N[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+
+#NET "F_LVDS_NA" LOC = K3;
+#NET "F_LVDS_NA" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_NA" SLEW = SLOW;
+# NET "F_LVDS_PA" LOC = K4;
+# NET "F_LVDS_PA" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+#NET "F_LVDS_NB" LOC = K5;
+#NET "F_LVDS_NB" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_NB" SLEW = SLOW;
+#NET "F_LVDS_PB" LOC = L5;
+#NET "F_LVDS_PB" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_PB" SLEW = SLOW;
+#NET "F_LVDS_NC" LOC = L1;
+#NET "F_LVDS_NC" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_NC" SLEW = SLOW;
+#NET "F_LVDS_PC" LOC = L2;
+#NET "F_LVDS_PC" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_PC" SLEW = SLOW;
+
+#NET "F_LVDS_CK0_N" LOC = T8;
+#NET "F_LVDS_CK0_N" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_CK0_N" SLEW = SLOW;
+#NET "F_LVDS_CK0_P" LOC = R8;
+#NET "F_LVDS_CK0_P" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_CK0_P" SLEW = SLOW;
+
+#NET "F_LVDS_CK1_N" LOC = T10;
+#NET "F_LVDS_CK1_N" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_CK1_N" SLEW = SLOW;
+#NET "F_LVDS_CK1_P" LOC = R10;
+#NET "F_LVDS_CK1_P" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_CK1_P" SLEW = SLOW;
+
+# NET "F_LVDS_CK_N[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+# NET "F_LVDS_CK_P[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+NET "CLK2_P" PERIOD = 20 ns;
diff --git a/toolruns/xilinx/novena/novena_eim.v b/toolruns/xilinx/novena/novena_eim.v
new file mode 100755
index 0000000..4cf4dcf
--- /dev/null
+++ b/toolruns/xilinx/novena/novena_eim.v
@@ -0,0 +1,567 @@
+//////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2014, Andrew "bunnie" Huang
+//
+// See the NOTICE file distributed with this work for additional 
+// information regarding copyright ownership.  The copyright holder 
+// licenses this file to you under the Apache License, Version 2.0 
+// (the "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// code distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//////////////////////////////////////////////////////////////////////////////
+
+`timescale 1ns / 1ps
+
+module novena_fpga(
+		   // CPU EIM register interface mapping
+		   input wire EIM_BCLK,
+		   input wire [1:0] EIM_CS,
+		   input wire [18:16] EIM_A,
+		   input wire EIM_LBA,
+		   input wire EIM_OE,
+		   input wire EIM_RW,
+		   inout wire [15:0] EIM_DA,
+
+`ifdef UNDEFINED
+		   ////// expansion connector side mapping
+		   // CPU->DUT mappings bank A
+		   output wire F_LVDS_P15,
+		   output wire F_LVDS_N15,
+		   output wire F_LVDS_P0,
+		   output wire F_LVDS_N0,
+		   output wire F_LVDS_CK1_P,
+		   output wire F_LVDS_CK1_N,
+		   output wire F_DX14,
+		   output wire F_LVDS_P4,
+		   // CPU->DUT mappings bank B
+		   output wire F_LVDS_P11,
+		   output wire F_LVDS_N11,
+		   output wire F_DX1,
+		   output wire F_LVDS_NC,
+		   output wire F_LVDS_PC,
+		   output wire F_DX17,
+		   output wire F_LVDS_NB,
+		   output wire F_LVDS_PB,
+
+		   output wire F_LVDS_P7, // OE_N control for CPU->DUT bank A mappings
+		   output wire F_LVDS_N7, // OE_N control for CPU->DUT bank B mappings
+
+		   // DUT->CPU mappings
+		   input wire F_DX18,
+		   input wire F_LVDS_CK0_N,
+		   input wire F_LVDS_CK0_P,
+		   input wire F_LVDS_P9,
+		   input wire F_DX0,
+		   input wire F_DX3,
+		   input wire F_DX2,
+		   input wire F_DX11,
+
+		   /// board control
+		   output wire F_DX15,    // output voltage select, 1 = Low V, 0 = 5V
+		   input wire F_LVDS_NA,  // OC flag
+
+		   /// ADC
+		   output wire F_DX13, // CS
+		   output wire F_DX7,  // SCLK
+		   output wire F_DX6,  // DIN
+		   input wire F_DX12,  // DOUT
+		   
+		   input wire I2C3_SCL,
+		   inout wire I2C3_SDA,
+`endif // UNDEFINED
+
+		   //// clocks n stuff
+		   input wire CLK2_N, // free-runs at 50 MHz
+		   input wire CLK2_P,
+		   output wire FPGA_LED2,
+		   input wire RESETBMCU,
+		   output wire APOPTOSIS
+	 );
+
+   wire [15:0] 		      eim_dout;
+   wire [15:0] 		      eim_din;
+   wire 		      clk;   // free-runs at 50 MHz, unbuffered
+   wire 		      clk50; // zero-delay, DLL version of above. Use this.
+   wire 		      bclk;  // NOTE: doesn't run until first CPU access to EIM; then free-runs at 133 MHz
+   reg [23:0] 		      counter;
+   wire 		      reset;
+
+   wire 		      bclk_dll, bclk_div2_dll, bclk_div4_dll, bclk_locked;
+   wire 		      bclk_early;
+   wire 		      bclk_i, bclk_o;
+   wire 		      clk25;
+   wire 		      slowclk;
+
+`ifdef UNDEFINED   
+   ////////////
+   // This code section is specific to the GPBB
+   //
+   // The rest of the code is generic to creating EIM/I2C interfaces in the FPGA
+   ////////////
+   wire [7:0] 		      cpu_to_dutA; // this set of wires connected to EIM registers below
+   wire [7:0] 		      cpu_to_dutB;
+   wire 		      drive_dutA_N;
+   wire 		      drive_dutB_N;
+   wire [7:0] 		      dut_to_cpu;
+   wire [15:0] 		      gpbb_ctl;
+   wire [15:0] 		      gpbb_stat;
+
+   /// the following is a set of board/pin-name to logical mappings
+   // CPU->DUT mappings bank A
+   assign F_LVDS_P15 = cpu_to_dutA[0];
+   assign F_LVDS_N15 = cpu_to_dutA[1];
+   assign F_LVDS_P0 =  cpu_to_dutA[2];
+   assign F_LVDS_N0 =  cpu_to_dutA[3];
+   assign F_LVDS_CK1_P =  cpu_to_dutA[4];
+   assign F_LVDS_CK1_N =  cpu_to_dutA[5];
+   assign F_DX14 =  cpu_to_dutA[6];
+   assign F_LVDS_P4 =  cpu_to_dutA[7];
+   // CPU->DUT mappings bank B
+   assign F_LVDS_P11 = cpu_to_dutB[0];   // LED 0
+   assign F_LVDS_N11 = cpu_to_dutB[1];   // LED 1
+   assign F_DX1 = cpu_to_dutB[2];        // LED 2
+   assign F_LVDS_NC = cpu_to_dutB[3];    // LED 3
+   assign F_LVDS_PC = cpu_to_dutB[4];
+   assign F_DX17 = cpu_to_dutB[5];
+   assign F_LVDS_NB = cpu_to_dutB[6];
+   assign F_LVDS_PB = cpu_to_dutB[7];
+
+   assign F_LVDS_P7 = drive_dutA_N; // OE_N control for CPU->DUT mappings
+   assign F_LVDS_N7 = drive_dutB_N; // OE_N control for CPU->DUT mappings
+   
+   assign drive_dutA_N = !gpbb_ctl[0]; // invert so drive is true from programming model
+   assign drive_dutB_N = !gpbb_ctl[1]; // invert so drive is true from programming model
+
+   assign F_DX15 = !gpbb_ctl[15];  // bit 15 selects output voltage
+   // invert so from software, default 0 = low voltage; 1 = drive 5V
+      
+   // DUT->CPU mappings
+   assign dut_to_cpu[0] = F_DX18;
+   assign dut_to_cpu[1] = F_LVDS_CK0_N;
+   assign dut_to_cpu[2] = F_LVDS_CK0_P;
+   assign dut_to_cpu[3] = F_LVDS_P9;
+   assign dut_to_cpu[4] = F_DX0;
+   assign dut_to_cpu[5] = F_DX3;
+   assign dut_to_cpu[6] = F_DX2;
+   assign dut_to_cpu[7] = F_DX11;
+   
+
+   assign gpbb_stat[15:0] = {15'b0, F_LVDS_NA};  // tie unused lines to 0
+   // bit 0 is overcurrent flag
+    
+   /////////////
+   // right, so fwiw, we map the ADC to the I2C bus.
+   // you can also map this to EIM by making registers that
+   // map to ADC wires, but this is an instructive example of
+   // how to use I2C-to-FPGA mappings with something reasonably generic.
+   // the I2C interface block is called i2c_slave and is near the bottom of this file
+   
+   /// ADC
+   wire       adc_go;
+   wire [2:0] adc_chan;
+   
+   wire [9:0] adc_in;
+   wire       adc_valid;
+   
+   adc10cs022 adc10cs022 (
+			  .DIG_ADC_CS(F_DX13),
+			  .DIG_ADC_IN(F_DX6),
+			  .DIG_ADC_OUT(F_DX12),
+			  .DIG_ADC_SCLK(F_DX7),
+
+			  .adc_in(adc_in),
+			  .adc_chan(adc_chan),
+			  .adc_valid(adc_valid),
+			  .adc_go(adc_go),
+			  
+			  .clk_3p2MHz(slowclk),
+			  .reset(reset)
+			  );
+`endif // UNDEFINED
+   
+   //////////////////////////////
+   ///// The following code is used to create the EIM interface to the CPU
+   /////
+   ///// Copy-and-paste the memory-mapped register templates to make
+   ///// more control registers. If you're working on your own design,
+   ///// I recommend you start with all the code below this area as a baseline.
+   /////
+   ///// It's tricky to change code that relates to
+   ///// the details of talking to the i.MX6 EIM -- it's difficult to
+   ///// close timing, so you'll need to understand a bit about FPGA timing
+   ///// closure to make chages to that section
+   //////////////////////////////
+
+   ////////////////////////////////////
+   ///// EIM Register set
+   /////
+   ///// All registers split into write or read only blanks
+   /////
+   ///// bank 0 maps to CS0 in EIM space, and the CPU timings should
+   ///// be configured for "word mode" (e.g. 16-bit accesses) only
+   ///// This address space has the fundamental C type of (unsigned short *)
+   /////  0x40000 - 0x40FFF is reserved for write-only bank 0 
+   /////  0x41000 - 0x41FFF is reserved for read-only bank 0
+   /////
+   ///// bank 1 maps to CS1 in EIM space. The CPU timings should be
+   ///// configured for "burst mode". Accessing this space is most
+   ///// efficient using memcpy() with a block size of 8 bytes, as all
+   ///// registers in this space are 64-bit values. You can also try
+   ///// using a fundamental C type of (unsigned long long *)
+   /////
+   /////  0xC040000 - 0xC040FFF is reserved for w/o bank 1 
+   /////  0xC041000 - 0xC041FFF is reserved for r/o bank 1
+   ////////////////////////////////////
+   
+   ////////// VERSION LOG (major version 000B) /////////////
+   ////// by convention, I reserve 0x41FFC and 0x41FFE for
+   ////// minor and major version codes, respectively
+   ////// I try to give every FPGA design type a unique version
+   ////// code, and keep track of the rev of the design with the
+   ////// minor code.
+   //////
+   ////// This particular design gets Major version 0xB.
+   //////
+   // Minor version 0001, October 19 2014
+   //   Initial design to validate GPBB
+   //
+   
+   reg 		      cs0_r, rw_r;
+   reg [15:0] 	      din_r;
+   reg [18:0] 	      bus_addr_r;
+   reg 		      adv_r;
+
+   reg 		      cs0_in, rw_in, adv_in;
+   reg [15:0] 	      din_in;
+   reg [2:0] 	      a_in;
+
+   /// pipeline the inputs to allow for s/h closure
+   always @(posedge bclk_i) begin
+      cs0_in <= EIM_CS[0];
+      rw_in <= EIM_RW;
+      din_in <= eim_din;
+      adv_in <= !EIM_LBA; // latch address on LBA low
+      a_in <= EIM_A[18:16];
+
+      cs0_r <= cs0_in;
+      rw_r <= rw_in;
+      din_r <= din_in;
+      adv_r <= adv_in;
+   end
+   
+   always @(posedge bclk_i) begin 
+      if( adv_in ) begin
+	 bus_addr_r <= {a_in, din_in};
+      end else begin
+	 bus_addr_r <= bus_addr_r;
+      end
+   end
+
+   /// retime and mux between cs0 and cs1 banks on the output
+`ifdef BURST
+   wire [15:0]               ro_d_b;
+   reg [15:0] 		     ro_d_b_r;
+   always @(posedge bclk_i) begin
+      ro_d_b_r[15:0] <= ro_d_b[15:0];
+   end
+`endif
+   reg [15:0]		     ro_d_r;
+   reg [1:0] 		     eim_rdcs;
+   reg [15:0] 		     eim_dout_pipe;
+   reg [15:0] 		     eim_dout_pipe2;
+   
+   always @(posedge bclk_dll) begin
+      ro_d_r <= ro_d;
+      eim_rdcs[1:0] <= EIM_CS[1:0];
+`ifdef BURST
+      eim_dout_pipe <= (eim_rdcs[1:0] == 2'b10) ? ro_d_r : ro_d_b_r;
+`else
+      eim_dout_pipe <= (eim_rdcs[1:0] == 2'b10) ? ro_d_r : 16'b0;
+`endif
+   end
+
+   always @(posedge bclk_o) begin
+      eim_dout_pipe2 <= eim_dout_pipe; // retime near the source to allow max time for wire delay
+   end;
+   
+   wire [15:0] ro_d;
+
+`ifdef UNDEFINED
+   wire [15:0] r40000wo;
+   wire [15:0] r40002wo;
+
+   //////// write-only registers
+   reg_wo reg_wo_40000 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h40000),
+			 .bus_d(din_r), .we(!cs0_r && !rw_r), .re(!cs0_r && rw_r), .rbk_d(ro_d), 
+			 .reg_d( r40000wo[15:0] ) );
+   
+   reg_wo reg_wo_40002 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h40002),
+			 .bus_d(din_r), .we(!cs0_r && !rw_r), .re(1'b0), .rbk_d(ro_d), // unreadable for testing
+			 .reg_d( r40002wo[15:0] ) );
+
+
+   reg_wo reg_wo_40010 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h40010),
+			 .bus_d(din_r), .we(!cs0_r && !rw_r), .re(!cs0_r && rw_r), .rbk_d(ro_d), 
+			 .reg_d( {cpu_to_dutB[7:0], cpu_to_dutA[7:0]} ) );
+
+   reg_wo reg_wo_40012 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h40012),
+			 .bus_d(din_r), .we(!cs0_r && !rw_r), .re(!cs0_r && rw_r), .rbk_d(ro_d), 
+			 .reg_d( gpbb_ctl[15:0] ) );
+   
+   //////// read-only registers
+   // loopback readback
+   reg_ro reg_ro_41000 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h41000),
+			 .bus_d(ro_d), .re(!cs0_r && rw_r),
+			 .reg_d( r40000wo[15:0] ) );
+
+   reg_ro reg_ro_41002 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h41002),
+			 .bus_d(ro_d), .re(!cs0_r && rw_r),
+			 .reg_d( r40002wo[15:0] ) );
+
+   reg_ro reg_ro_41010 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h41010),
+			 .bus_d(ro_d), .re(!cs0_r && rw_r),
+			 .reg_d( {8'b0,dut_to_cpu[7:0]} ) );
+
+   reg_ro reg_ro_41012 ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h41012),
+			 .bus_d(ro_d), .re(!cs0_r && rw_r),
+			 .reg_d( gpbb_stat[15:0] ) );
+
+   
+   ///////////////////////
+   // CS1 bank registers: minimum size here is 64-bit, tuned for synchronous burst access only
+   ///////////////////////
+
+   wire [63:0] 	     rC04_0000wo;
+   wire [63:0] 	     rC04_0008wo;
+   
+   ///////// write registers
+   // loopback test
+   reg_wo_4burst reg_wo_4b_C04_0000( .clk(bclk_i), .bus_ad(eim_din), .my_a(19'h4_0000), 
+				     .bus_a(EIM_A[18:16]), .adv(!EIM_LBA), .rw(EIM_RW), .cs(!EIM_CS[1]), 
+				     .reg_d( rC04_0000wo[63:0] ), .rbk_d(ro_d_b) );
+
+   reg_wo_4burst reg_wo_4b_C04_0008( .clk(bclk_i), .bus_ad(eim_din), .my_a(19'h4_0008),
+				     .bus_a(EIM_A[18:16]), .adv(!EIM_LBA), .rw(EIM_RW), .cs(!EIM_CS[1]),
+				     .reg_d( rC04_0008wo[63:0] ), .rbk_d(ro_d_b) );
+
+   ///////// read registers
+   // loopback test
+   reg_ro_4burst reg_ro_4b_C04_1000( .clk(bclk_i), .bus_ad(eim_din), .my_a(19'h4_1000),
+				     .bus_a(EIM_A[18:16]), .adv(!EIM_LBA), .rw(EIM_RW), .cs(!EIM_CS[1]),
+				     .reg_d( rC04_0000wo[63:0] ), .rbk_d(ro_d_b) );
+
+   reg_ro_4burst reg_ro_4b_C04_1008( .clk(bclk_i), .bus_ad(eim_din), .my_a(19'h4_1008),
+				     .bus_a(EIM_A[18:16]), .adv(!EIM_LBA), .rw(EIM_RW), .cs(!EIM_CS[1]),
+				     .reg_d( rC04_0008wo[63:0] ), .rbk_d(ro_d_b) );
+
+   // FPGA minor version code
+   reg_ro reg_ro_41FFC ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h41FFC),
+			 .bus_d(ro_d), .re(!cs0_r && rw_r),
+			 .reg_d( 16'h0001 ) ); // minor version
+
+   // FPGA major version code
+   reg_ro reg_ro_41FFE ( .clk(bclk_dll), .bus_a(bus_addr_r), .my_a(19'h41FFE),
+			 .bus_d(ro_d), .re(!cs0_r && rw_r),
+			 .reg_d( 16'h000B ) ); // 000B is for GPBB release
+
+   ////////////////////////////////////
+   ///// I2C register set -- for those who don't want to use EIM
+   ////////////////////////////////////
+   wire [7:0] 	      reg_0_test;
+   wire       SDA_pd;
+   wire       SDA_int;
+   
+   IOBUF #(.DRIVE(8), .SLEW("SLOW")) IOBUF_sda (.IO(I2C3_SDA), .I(1'b0), .T(!SDA_pd), .O(SDA_int));
+   i2c_slave i2c_slave(
+		       .SCL(I2C3_SCL),
+		       .SDA(SDA_int),
+		       .SDA_pd(SDA_pd),
+		       .clk(clk25), // nominally 26 MHz, this is close enough
+		       .glbl_reset(reset),
+		       .i2c_device_addr(8'h3C),
+
+		       // outputs from I2C block (CPU->FPGA) 0-3F
+		       .reg_0(reg_0_test),
+
+		       .reg_2({adc_go,adc_chan[2:0]}),
+		       // bit 2-0:  ADC channel
+		       // bit 3:    initiate conversion
+
+		       // inputs to I2C block (FPGA->CPU) 40-7F
+		       .reg_40(adc_in[7:0]),
+		       .reg_41({6'b0,adc_in[9:8]}),
+
+		       .reg_42({7'b0, adc_valid}),
+
+		       // ID / version code
+		       // minor / major
+		       .reg_fc(8'h00), .reg_fd(8'h01), .reg_fe(8'h00), .reg_ff(8'h0B)
+		       );
+`endif // UNDEFINED
+      
+   ////////////////////////////////////
+   ///// MASTER RESET
+   ////////////////////////////////////
+   // synced to a 3.2MHz clock
+   sync_reset dll_res_sync( .glbl_reset(!RESETBMCU), .clk(slowclk), .reset(reset) );
+   
+
+   ////////////////////////////////////
+   ///// BCLK DLL -- generate zero-delay clock plus slower versions for internal use
+   ////////////////////////////////////
+   wire 	      bclk_int_in, bclk_io_in;
+   IBUFG   clkibufg (.I(EIM_BCLK), .O(bclk) );
+   BUFG    bclk_dll_bufg(.I(bclk), .O(bclk_int_in) );
+   
+   bclk_dll bclk_dll_mod( .clk133in(bclk_int_in), .clk133(bclk_dll),
+			  .RESET(reset), .LOCKED(bclk_locked));
+
+   wire 	      o_reset, o_locked, i_locked;
+   wire 	      i_fbk_out, i_fbk_in;
+   wire 	      o_fbk_out, o_fbk_in;
+   
+   dcm_delay bclk_i_dll( .clk133(bclk_int_in), .clk133out(bclk_i),
+			  .CLKFB_IN(i_fbk_in), .CLKFB_OUT(i_fbk_out),
+			  .RESET(reset), .LOCKED(i_locked));
+
+   dcm_delay bclk_o_dll( .clk133(bclk_int_in), .clk133out(bclk_o),
+			  .CLKFB_IN(o_fbk_in), .CLKFB_OUT(o_fbk_out),
+			  .RESET(reset), .LOCKED(o_locked));
+   
+   // lock it to the input path
+   BUFIO2FB bclk_o_fbk(.I(bclk_o), .O(o_fbk_in));  // was this
+//   assign o_fbk_in = bclk_o;
+//   BUFG bclk_io_fbk(.I(bclk_io), .O(io_fbk_in));
+   
+   assign i_fbk_in = bclk_i;
+
+   ////////////////////////////////////
+   ///// create 3.2MHz, 25MHz, and 50MHz buffered clocks from clk using DLL
+   ////////////////////////////////////
+   wire       dll_locked;
+
+   clk_dll clk_dll
+     (// Clock in ports
+      .clk50in(clk),      // IN
+      // Clock out ports
+      .clk50(clk50),     // OUT
+      .clk25(clk25),     // OUT
+      .clk3p2(slowclk),     // OUT
+      // Status and control signals
+      .RESET(!RESETBMCU),// IN
+      .LOCKED(dll_locked));      // OUT
+   
+
+   //////////////
+   // Output pipeline registers -- explicit instantiation as their LOCs are controlled in the UCF.
+   //////////////
+   FDSE oddr2_eim0(.D(eim_dout_pipe2[ 0]), .C(bclk_o), .Q(eim_dout[ 0]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eim1(.D(eim_dout_pipe2[ 1]), .C(bclk_o), .Q(eim_dout[ 1]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eim2(.D(eim_dout_pipe2[ 2]), .C(bclk_o), .Q(eim_dout[ 2]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eim3(.D(eim_dout_pipe2[ 3]), .C(bclk_o), .Q(eim_dout[ 3]), .CE(1'b1), .S(1'b0));
+
+   FDSE oddr2_eim4(.D(eim_dout_pipe2[ 4]), .C(bclk_o), .Q(eim_dout[ 4]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eim5(.D(eim_dout_pipe2[ 5]), .C(bclk_o), .Q(eim_dout[ 5]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eim6(.D(eim_dout_pipe2[ 6]), .C(bclk_o), .Q(eim_dout[ 6]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eim7(.D(eim_dout_pipe2[ 7]), .C(bclk_o), .Q(eim_dout[ 7]), .CE(1'b1), .S(1'b0));
+
+   FDSE oddr2_eim8(.D(eim_dout_pipe2[ 8]), .C(bclk_o), .Q(eim_dout[ 8]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eim9(.D(eim_dout_pipe2[ 9]), .C(bclk_o), .Q(eim_dout[ 9]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eimA(.D(eim_dout_pipe2[10]), .C(bclk_o), .Q(eim_dout[10]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eimB(.D(eim_dout_pipe2[11]), .C(bclk_o), .Q(eim_dout[11]), .CE(1'b1), .S(1'b0));
+
+   FDSE oddr2_eimC(.D(eim_dout_pipe2[12]), .C(bclk_o), .Q(eim_dout[12]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eimD(.D(eim_dout_pipe2[13]), .C(bclk_o), .Q(eim_dout[13]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eimE(.D(eim_dout_pipe2[14]), .C(bclk_o), .Q(eim_dout[14]), .CE(1'b1), .S(1'b0));
+   FDSE oddr2_eimF(.D(eim_dout_pipe2[15]), .C(bclk_o), .Q(eim_dout[15]), .CE(1'b1), .S(1'b0));
+   
+
+   //////////////
+   /// "heartbeat" counter
+   //////////////
+   always @(posedge clk50) begin
+      counter <= counter + 1;
+   end
+
+   assign FPGA_LED2 = counter[23];
+
+   //////////////
+   // IOBUFs as required by design
+   //////////////
+   IBUFGDS clkibufgds( .I(CLK2_P), .IB(CLK2_N), .O(clk) );
+
+   reg [15:0]	      eim_d_t;
+   reg 		      eim_lba_reg;
+   reg 		      eim_oe_reg;
+
+   always @(posedge bclk_i) begin
+      eim_lba_reg <= EIM_LBA;
+      eim_oe_reg <= EIM_OE;
+   end
+
+   always @(posedge bclk_o) begin
+      eim_d_t[ 0] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 1] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 2] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 3] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 4] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 5] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 6] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 7] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 8] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[ 9] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[10] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[11] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[12] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[13] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[14] = eim_oe_reg | !eim_lba_reg;
+      eim_d_t[15] = eim_oe_reg | !eim_lba_reg;
+   end
+   
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim0  (.IO(EIM_DA[ 0]), .I(eim_dout[ 0]), .T(eim_d_t[ 0]), .O(eim_din[ 0]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim1  (.IO(EIM_DA[ 1]), .I(eim_dout[ 1]), .T(eim_d_t[ 1]), .O(eim_din[ 1]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim2  (.IO(EIM_DA[ 2]), .I(eim_dout[ 2]), .T(eim_d_t[ 2]), .O(eim_din[ 2]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim3  (.IO(EIM_DA[ 3]), .I(eim_dout[ 3]), .T(eim_d_t[ 3]), .O(eim_din[ 3]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim4  (.IO(EIM_DA[ 4]), .I(eim_dout[ 4]), .T(eim_d_t[ 4]), .O(eim_din[ 4]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim5  (.IO(EIM_DA[ 5]), .I(eim_dout[ 5]), .T(eim_d_t[ 5]), .O(eim_din[ 5]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim6  (.IO(EIM_DA[ 6]), .I(eim_dout[ 6]), .T(eim_d_t[ 6]), .O(eim_din[ 6]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim7  (.IO(EIM_DA[ 7]), .I(eim_dout[ 7]), .T(eim_d_t[ 7]), .O(eim_din[ 7]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim8  (.IO(EIM_DA[ 8]), .I(eim_dout[ 8]), .T(eim_d_t[ 8]), .O(eim_din[ 8]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim9  (.IO(EIM_DA[ 9]), .I(eim_dout[ 9]), .T(eim_d_t[ 9]), .O(eim_din[ 9]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim10 (.IO(EIM_DA[10]), .I(eim_dout[10]), .T(eim_d_t[10]), .O(eim_din[10]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim11 (.IO(EIM_DA[11]), .I(eim_dout[11]), .T(eim_d_t[11]), .O(eim_din[11]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim12 (.IO(EIM_DA[12]), .I(eim_dout[12]), .T(eim_d_t[12]), .O(eim_din[12]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim13 (.IO(EIM_DA[13]), .I(eim_dout[13]), .T(eim_d_t[13]), .O(eim_din[13]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim14 (.IO(EIM_DA[14]), .I(eim_dout[14]), .T(eim_d_t[14]), .O(eim_din[14]));
+   IOBUF #(.DRIVE(12), .SLEW("FAST")) IOBUF_eim15 (.IO(EIM_DA[15]), .I(eim_dout[15]), .T(eim_d_t[15]), .O(eim_din[15]));
+
+   //////////////
+   // tie downs (unused signals as of this rev of design)
+   //////////////
+   assign APOPTOSIS = 1'b0; // make apoptosis inactive, tigh high to force reboot on config
+   
+   //////////////
+   // coretest hash cores
+   //////////////
+   coretest_hashes core(
+			//.clk(bclk_dll),
+			//.clk(clk50),
+			.clk(clk25),
+			.reset_n(!reset),
+
+			.cs(!cs0_r && !adv_r),
+			.we(!rw_r),
+
+			.address(bus_addr_r),
+			.write_data(din_r),
+			.read_data(ro_d)
+			);
+
+endmodule
diff --git a/toolruns/xilinx/novena/novena_i2c.bmm b/toolruns/xilinx/novena/novena_i2c.bmm
new file mode 100644
index 0000000..e69de29
diff --git a/toolruns/xilinx/novena/novena_i2c.ucf b/toolruns/xilinx/novena/novena_i2c.ucf
new file mode 100644
index 0000000..448c692
--- /dev/null
+++ b/toolruns/xilinx/novena/novena_i2c.ucf
@@ -0,0 +1,594 @@
+###  Autogenerated on 2013-May-10 01:38 by edifToUcf.py
+###  Extracting designator U800 from EDIF netlist novena-dvt.EDF
+
+### extended performance annotation
+CONFIG VCCAUX  = 3.3;
+# Valid values are 2.5 and 3.3
+CONFIG MCB_PERFORMANCE  = EXTENDED;
+
+NET "APOPTOSIS" LOC = K1;
+NET "APOPTOSIS" IOSTANDARD = LVCMOS33;
+
+# AUD6_TFS
+# NET "DEL_CONT" LOC = A4;
+# NET "DEL_CONT" IOSTANDARD = LVCMOS33;
+# AUD6_TXC
+# NET "DEL_RST_L" LOC = B4;
+# NET "DEL_RST_L" IOSTANDARD = LVCMOS33;
+# NET "AUD6_TXD" LOC = A6;
+# NET "AUD6_TXD" IOSTANDARD = LVCMOS33;
+# NET "AUD_MCLK" LOC = H6;
+# NET "AUD_MCLK" IOSTANDARD = LVCMOS33;
+# AUD_MIC_CLK
+#NET "ZEROVEN" LOC = G3;
+#NET "ZEROVEN" IOSTANDARD = LVCMOS33;
+# NET "AUD_MIC_DAT" LOC = C5;
+# NET "AUD_MIC_DAT" IOSTANDARD = LVCMOS33;
+
+# NET "BATT_NRST" LOC = N1;
+# NET "BATT_NRST" IOSTANDARD = LVCMOS33;
+# NET "BATT_REFLASH_ALRT" LOC = N2;
+# NET "BATT_REFLASH_ALRT" IOSTANDARD = LVCMOS33;
+
+NET "CLK2_N" LOC = H1;
+NET "CLK2_N" IOSTANDARD = LVDS_33;
+NET "CLK2_N" DIFF_TERM = "TRUE";
+NET "CLK2_P" LOC = H2;
+NET "CLK2_P" IOSTANDARD = LVDS_33;
+NET "CLK2_P" DIFF_TERM = "TRUE";
+
+# NET "DDC_SCL" LOC = J6;
+# NET "DDC_SCL" IOSTANDARD = LVCMOS33;
+# NET "DDC_SDA" LOC = F2;
+# NET "DDC_SDA" IOSTANDARD = LVCMOS33;
+
+# ECSPI3_MISO
+#NET "SPI1_MISO" LOC = A3;
+#NET "SPI1_MISO" IOSTANDARD = LVCMOS33;
+# # R13 
+# ECSPI3_MOSI
+#NET "SPI1_MOSI" LOC = A2;
+#NET "SPI1_MOSI" IOSTANDARD = LVCMOS33;
+# ECSPI3_RDY
+#NET "SPI1_DELI_SEL" LOC = A5;
+#NET "SPI1_DELI_SEL" IOSTANDARD = LVCMOS33;
+# # R15 
+# ECSPI3_SCLK
+#NET "SPI1_SCLK" LOC = D9;
+#NET "SPI1_SCLK" IOSTANDARD = LVCMOS33;
+# ECSPI3_SS2
+#NET "SAMPEN" LOC = B3;
+#NET "SAMPEN" IOSTANDARD = LVCMOS33;
+
+# NET "EIM_BCLK" LOC = C9;
+# NET "EIM_BCLK" IOSTANDARD = LVCMOS33;
+#NET "EIM_CS[0]" LOC = B11;
+#NET "EIM_CS[0]" IOSTANDARD = LVCMOS33;
+#NET "EIM_CS[1]" LOC = A15;
+#NET "EIM_CS[1]" IOSTANDARD = LVCMOS33;
+
+NET "EIM_DA[0]" LOC = G9;
+NET "EIM_DA[0]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[0]" SLEW = SLOW;
+NET "EIM_DA[1]" LOC = A10;
+NET "EIM_DA[1]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[1]" SLEW = SLOW;
+NET "EIM_DA[2]" LOC = F9;
+NET "EIM_DA[2]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[2]" SLEW = SLOW;
+NET "EIM_DA[3]" LOC = B9;
+NET "EIM_DA[3]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[3]" SLEW = SLOW;
+NET "EIM_DA[4]" LOC = E13;
+NET "EIM_DA[4]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[4]" SLEW = SLOW;
+NET "EIM_DA[5]" LOC = F13;
+NET "EIM_DA[5]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[5]" SLEW = SLOW;
+NET "EIM_DA[6]" LOC = A9;
+NET "EIM_DA[6]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[6]" SLEW = SLOW;
+NET "EIM_DA[7]" LOC = A8;
+NET "EIM_DA[7]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[7]" SLEW = SLOW;
+NET "EIM_DA[8]" LOC = B8;
+NET "EIM_DA[8]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[8]" SLEW = SLOW;
+NET "EIM_DA[9]" LOC = D8;
+NET "EIM_DA[9]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[9]" SLEW = SLOW;
+NET "EIM_DA[10]" LOC = D11;
+NET "EIM_DA[10]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[10]" SLEW = SLOW;
+NET "EIM_DA[11]" LOC = C8;
+NET "EIM_DA[11]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[11]" SLEW = SLOW;
+NET "EIM_DA[12]" LOC = C7;
+NET "EIM_DA[12]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[12]" SLEW = SLOW;
+
+NET "EIM_DA[13]" LOC = C11;
+NET "EIM_DA[13]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[13]" SLEW = SLOW;
+
+NET "EIM_DA[14]" LOC = C4;
+NET "EIM_DA[14]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[14]" SLEW = SLOW;
+NET "EIM_DA[15]" LOC = B6;
+NET "EIM_DA[15]" IOSTANDARD = LVCMOS33;
+NET "EIM_DA[15]" SLEW = SLOW;
+
+# EIM_A16
+NET "EIM_A16" LOC = A11;
+NET "EIM_A16" IOSTANDARD = LVCMOS33;
+NET "EIM_A16" SLEW = SLOW;
+
+# EIM_A17
+NET "EIM_A17" LOC = B12;
+NET "EIM_A17" IOSTANDARD = LVCMOS33;
+NET "EIM_A17" SLEW = SLOW;
+# EIM_A18
+#NET "ENC_PBIN" LOC = D14;
+#NET "ENC_PBIN" IOSTANDARD = LVCMOS33;
+
+# EIM_LBA
+#NET "EIM_LBA" LOC = B14;
+#NET "EIM_LBA" IOSTANDARD = LVCMOS33;
+# NET "EIM_OE" LOC = C10;
+# NET "EIM_OE" IOSTANDARD = LVCMOS33;
+# EIM_RW
+#NET "LED3" LOC = C14;
+#NET "LED3" IOSTANDARD = LVCMOS33;
+# NET "EIM_WAIT" LOC = A7;
+# NET "EIM_WAIT" IOSTANDARD = LVCMOS33;
+
+# #NET "FPGA_DONE" LOC = V17;
+# #NET "FPGA_DONE" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_HSWAPEN" LOC = D4;
+# #NET "FPGA_HSWAPEN" IOSTANDARD = LVCMOS33;
+# FPGA_INIT_N
+#NET "LED2" LOC = U3;
+#NET "LED2" IOSTANDARD = LVCMOS33;
+
+NET "FPGA_LED2" LOC = A16;
+NET "FPGA_LED2" IOSTANDARD = LVCMOS33;
+NET "FPGA_LED2" SLEW = SLOW;
+
+# NET "FPGA_LSPI_CLK" LOC = D3;
+# NET "FPGA_LSPI_CLK" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_CS" LOC = D1;
+# NET "FPGA_LSPI_CS" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_HOLD" LOC = E3;
+# NET "FPGA_LSPI_HOLD" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_MISO" LOC = D2;
+# NET "FPGA_LSPI_MISO" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_MOSI" LOC = C2;
+# NET "FPGA_LSPI_MOSI" IOSTANDARD = LVCMOS33;
+# NET "FPGA_LSPI_WP" LOC = C1;
+# NET "FPGA_LSPI_WP" IOSTANDARD = LVCMOS33;
+
+# #NET "FPGA_M0" LOC = T15;
+# #NET "FPGA_M0" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_M1" LOC = N12;
+# #NET "FPGA_M1" IOSTANDARD = LVCMOS33;
+
+# #NET "FPGA_RESET_N" LOC = V2;
+# #NET "FPGA_RESET_N" IOSTANDARD = TMDS_33;
+# #NET "FPGA_SUSPEND" LOC = R16;
+# #NET "FPGA_SUSPEND" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TCK" LOC = A17;
+# #NET "FPGA_TCK" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TDI" LOC = D15;
+# #NET "FPGA_TDI" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TDO" LOC = D16;
+# #NET "FPGA_TDO" IOSTANDARD = LVCMOS33;
+# #NET "FPGA_TMS" LOC = B18;
+# #NET "FPGA_TMS" IOSTANDARD = LVCMOS33;
+
+# # NET "GND" LOC = A1;# A18 B7 B13 C3 C16 D5 D10 E15 G2 G5 G12 G17 H8 H10 J4 J9 J11 J15 K8 K10 L9 L11 M2 M6 M17 N13 R1 R4 R9 R14 R18 T16 U6 U12 V1 V18 
+# # NET "GND" IOSTANDARD = LVCMOS33;
+
+NET "I2C3_SCL" LOC = P4;
+NET "I2C3_SCL" IOSTANDARD = LVCMOS33;
+NET "I2C3_SDA" LOC = P3;
+NET "I2C3_SDA" IOSTANDARD = LVCMOS33;
+
+# # NET "P1_2V" LOC = G7;# H9 H11 J8 J10 K9 K11 L8 L10 M7 M12 
+# # NET "P1_2V" IOSTANDARD = LVCMOS33;
+# # NET "P3_3V_DELAYED" LOC = B1;# B5 B10 B15 B17 D7 D13 E2 E5 E9 E10 E14 E17 G4 G10 G15 J2 J5 J12 J14 J17 K7 M4 M9 M15 P5 P9 P10 P14 R2 R6 R12 R17 U4 U9 U14 
+# # NET "P3_3V_DELAYED" IOSTANDARD = LVCMOS33;
+
+NET "RESETBMCU" LOC = F1;
+NET "RESETBMCU" IOSTANDARD = LVCMOS33;
+
+# NET "SMB_SCL" LOC = N3;
+# NET "SMB_SCL" IOSTANDARD = LVCMOS33;
+# NET "SMB_SDA" LOC = N4;
+# NET "SMB_SDA" IOSTANDARD = LVCMOS33;
+
+# NET "UART4_CTS" LOC = U1;
+# NET "UART4_CTS" IOSTANDARD = LVCMOS33;
+# NET "UART4_RTS" LOC = U2;
+# NET "UART4_RTS" IOSTANDARD = LVCMOS33;
+# NET "UART4_RXD" LOC = T1;
+# NET "UART4_RXD" IOSTANDARD = LVCMOS33;
+# NET "UART4_TXD" LOC = P1;
+# NET "UART4_TXD" IOSTANDARD = LVCMOS33;
+
+# UIM_CLK
+#NET "MODES" LOC = B16;
+#NET "MODES" IOSTANDARD = LVCMOS33;
+# UIM_DATA
+#NET "ENC_SW" LOC = A12;
+#NET "ENC_SW" IOSTANDARD = LVCMOS33;
+# #NET "UIM_PWR" LOC = C18;
+# #NET "UIM_PWR" IOSTANDARD = SSTL15_II;
+# #NET "UIM_PWRON" LOC = A14;
+# #NET "UIM_PWRON" IOSTANDARD = LVCMOS33;
+# UIM_RESET
+#NET "SW_BACKUP" LOC = C15;
+#NET "SW_BACKUP" IOSTANDARD = LVCMOS33;
+
+##############
+# DDR3
+##############
+
+# NET "F_BA[2]" IOSTANDARD = SSTL15_II;
+# NET "F_BA[1]" IOSTANDARD = SSTL15_II;
+# NET "F_BA[0]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[13]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[12]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[11]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[10]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[9]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[8]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[7]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[6]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[5]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[4]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[3]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[2]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[1]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_A[0]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[15]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[14]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[13]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[12]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[11]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[10]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[9]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[8]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[7]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[6]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[5]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[4]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[3]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[2]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[1]" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_D[0]" IOSTANDARD = SSTL15_II;
+# NET "F_CAS_N" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_CKE" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_ODT" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_RST_N" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_RZQ" IOSTANDARD = SSTL15_II;
+# NET "F_DDR3_ZIO" IOSTANDARD = SSTL15_II;
+# NET "F_LDM" IOSTANDARD = SSTL15_II;
+# NET "F_RAS_N" IOSTANDARD = SSTL15_II;
+# NET "F_UDM" IOSTANDARD = SSTL15_II;
+# NET "F_WE_N" IOSTANDARD = SSTL15_II;
+
+
+# NET "F_BA[0]" LOC = H13;
+# NET "F_BA[1]" LOC = H14;
+# NET "F_BA[2]" LOC = K13;
+# NET "F_BA[0]" OUT_TERM = UNTUNED_50;
+# NET "F_BA[1]" OUT_TERM = UNTUNED_50;
+# NET "F_BA[2]" OUT_TERM = UNTUNED_50;
+
+# NET "F_CAS_N" LOC = K16;
+# NET "F_CAS_N" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_A[0]" LOC = H15;
+# NET "F_DDR3_A[1]" LOC = H16;
+# NET "F_DDR3_A[10]" LOC = E16;
+# NET "F_DDR3_A[11]" LOC = G14;
+# NET "F_DDR3_A[12]" LOC = D18;
+# NET "F_DDR3_A[13]" LOC = C17;
+# NET "F_DDR3_A[2]" LOC = F18;
+# NET "F_DDR3_A[3]" LOC = J13;
+# NET "F_DDR3_A[4]" LOC = E18;
+# NET "F_DDR3_A[5]" LOC = L12;
+# NET "F_DDR3_A[6]" LOC = L13;
+# NET "F_DDR3_A[7]" LOC = F17;
+# NET "F_DDR3_A[8]" LOC = H12;
+# NET "F_DDR3_A[9]" LOC = G13;
+# NET "F_DDR3_A[0]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[10]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[11]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[12]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[13]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[1]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[2]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[3]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[4]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[5]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[6]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[7]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[8]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_A[9]" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_CKE" LOC = D17;
+# NET "F_DDR3_CKE" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_CK_N" LOC = G18;
+# NET "F_DDR3_CK_N" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_DDR3_CK_N" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_CK_P" LOC = G16;
+# NET "F_DDR3_CK_P" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_DDR3_CK_P" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_D[0]" LOC = M16;
+# NET "F_DDR3_D[1]" LOC = M18;
+# NET "F_DDR3_D[10]" LOC = P17;
+# NET "F_DDR3_D[11]" LOC = P18;
+# NET "F_DDR3_D[12]" LOC = T17;
+# NET "F_DDR3_D[13]" LOC = T18;
+# NET "F_DDR3_D[14]" LOC = U17;
+# NET "F_DDR3_D[15]" LOC = U18;
+# NET "F_DDR3_D[2]" LOC = L17;
+# NET "F_DDR3_D[3]" LOC = L18;
+# NET "F_DDR3_D[4]" LOC = H17;
+# NET "F_DDR3_D[5]" LOC = H18;
+# NET "F_DDR3_D[6]" LOC = J16;
+# NET "F_DDR3_D[7]" LOC = J18;
+# NET "F_DDR3_D[8]" LOC = N17;
+# NET "F_DDR3_D[9]" LOC = N18;
+# NET "F_DDR3_D[0]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[10]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[11]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[12]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[13]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[14]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[15]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[1]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[2]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[3]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[4]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[5]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[6]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[7]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[8]" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_D[9]" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_RST_N" LOC = F14;
+# NET "F_DDR3_RST_N" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_ODT" LOC = K14;
+# NET "F_DDR3_ODT" OUT_TERM = UNTUNED_50;
+
+# NET "F_RAS_N" LOC = K15;
+# NET "F_RAS_N" OUT_TERM = UNTUNED_50;
+# NET "F_UDM" LOC = L15;
+# NET "F_UDM" OUT_TERM = UNTUNED_50;
+# NET "F_UDQS_N" LOC = N16;
+# NET "F_UDQS_N" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_UDQS_N" OUT_TERM = UNTUNED_50;
+# NET "F_UDQS_P" LOC = N15;
+# NET "F_UDQS_P" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_UDQS_P" OUT_TERM = UNTUNED_50;
+# NET "F_LDM" LOC = L16;
+# NET "F_LDM" OUT_TERM = UNTUNED_50;
+# NET "F_LDQS_N" LOC = K18;
+# NET "F_LDQS_N" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_LDQS_N" OUT_TERM = UNTUNED_50;
+# NET "F_LDQS_P" LOC = K17;
+# NET "F_LDQS_P" IOSTANDARD = DIFF_SSTL15_II;
+# NET "F_LDQS_P" OUT_TERM = UNTUNED_50;
+# NET "F_WE_N" LOC = K12;
+# NET "F_WE_N" OUT_TERM = UNTUNED_50;
+
+# NET "F_DDR3_RZQ" LOC = F15;
+# NET "F_DDR3_RZQ" OUT_TERM = UNTUNED_50;
+# NET "F_DDR3_ZIO" LOC = M14;
+# NET "F_DDR3_ZIO" OUT_TERM = UNTUNED_50;
+
+#NET "F_BA[*]" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_CAS_N" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_A[*]" SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_CKE" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+
+#NET "F_DDR3_D[*]" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_RST_N" IOSTANDARD = SSTL15_II | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_ODT" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_RAS_N" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_UDM" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_LDM" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_WE_N" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+#NET "F_DDR3_RZQ" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+#NET "F_DDR3_ZIO" IOSTANDARD = SSTL15_II  | OUT_TERM = UNTUNED_50;
+
+
+##############
+# I/O connector
+##############
+#NET "F_DX0" LOC = K6;
+#NET "F_DX0" IOSTANDARD = LVCMOS33;
+#NET "F_DX0" SLEW = SLOW;
+# NET "F_DX0" PULLUP;
+
+NET "F_DX1" LOC = L7;
+NET "F_DX1" IOSTANDARD = LVCMOS33;
+NET "F_DX1" SLEW = SLOW;
+
+#NET "F_DX2" LOC = H3;
+#NET "F_DX2" IOSTANDARD = LVCMOS33;
+#NET "F_DX2" SLEW = SLOW;
+
+#NET "F_DX3" LOC = H4;
+#NET "F_DX3" IOSTANDARD = LVCMOS33;
+#NET "F_DX3" SLEW = SLOW;
+# NET "F_DX3" PULLUP;
+
+# NET "F_DX[4]" LOC = J1;
+# NET "F_DX[5]" LOC = J3;
+# NET "F_DX6" LOC = L3;
+# NET "F_DX6" IOSTANDARD = LVCMOS33;
+# NET "F_DX6" SLEW = SLOW;
+
+# NET "F_DX7" LOC = L4;
+# NET "F_DX7" IOSTANDARD = LVCMOS33;
+# NET "F_DX7" SLEW = SLOW;
+
+# NET "F_DX8" LOC = K2;
+# NET "F_DX8" IOSTANDARD = LVCMOS33;
+# NET "F_DX8" SLEW = SLOW;
+
+#NET "F_DX11" LOC = M1;
+#NET "F_DX11" IOSTANDARD = LVCMOS33;
+#NET "F_DX11" SLEW = SLOW;
+# NET "F_DX12" LOC = M3;
+# NET "F_DX12" IOSTANDARD = LVCMOS33;
+# NET "F_DX12" SLEW = SLOW;
+
+# NET "F_DX13" LOC = P2;
+# NET "F_DX13" IOSTANDARD = LVCMOS33;
+# NET "F_DX13" SLEW = SLOW;
+
+# NET "F_DX14" LOC = T2;
+# NET "F_DX14" IOSTANDARD = LVCMOS33;
+# NET "F_DX14" SLEW = SLOW;
+
+NET "F_DX15" LOC = M5;
+NET "F_DX15" IOSTANDARD = LVCMOS33;
+NET "F_DX15" SLEW = SLOW;
+
+# NET "F_DX[16]" LOC = L6;
+NET "F_DX17" LOC = G1;
+NET "F_DX17" IOSTANDARD = LVCMOS33;
+NET "F_DX17" SLEW = SLOW;
+#NET "F_DX18" LOC = H7;
+#NET "F_DX18" IOSTANDARD = LVCMOS33;
+#NET "F_DX18" SLEW = SLOW;
+
+# NET "F_DX[*]" IOSTANDARD = LVCMOS33;
+
+NET "F_LVDS_N0" LOC = P6;
+NET "F_LVDS_N0" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_N0" SLEW = SLOW;
+NET "F_LVDS_P0" LOC = N5;
+NET "F_LVDS_P0" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_P0" SLEW = SLOW;
+# NET "F_LVDS_N1" LOC = V4;
+# NET "F_LVDS_N1" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N1" SLEW = SLOW;
+# NET "F_LVDS_P1" LOC = T4;
+# NET "F_LVDS_P1" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_P1" SLEW = SLOW;
+# NET "F_LVDS_N2" LOC = T3;
+# NET "F_LVDS_N2" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N2" SLEW = SLOW;
+# NET "F_LVDS_P[2]" LOC = R3;
+
+# NET "F_LVDS_N3" LOC = V5;
+# NET "F_LVDS_N3" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N3" SLEW = SLOW;
+
+# NET "F_LVDS_P[3]" LOC = U5;
+# NET "F_LVDS_N4" LOC = T5;
+# NET "F_LVDS_N4" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N4" SLEW = SLOW;
+NET "F_LVDS_P4" LOC = R5;
+NET "F_LVDS_P4" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_P4" SLEW = SLOW;
+
+# NET "F_LVDS_N5" LOC = T7;
+# NET "F_LVDS_N5" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N5" SLEW = SLOW;
+
+# NET "F_LVDS_P5" LOC = R7;
+# NET "F_LVDS_P5" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_P5" SLEW = SLOW;
+
+# NET "F_LVDS_N[6]" LOC = V6;
+# NET "F_LVDS_P[6]" LOC = T6;
+NET "F_LVDS_N7" LOC = V7;
+NET "F_LVDS_N7" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_N7" SLEW = SLOW;
+
+NET "F_LVDS_P7" LOC = U7;
+NET "F_LVDS_P7" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_P7" SLEW = SLOW;
+
+# NET "F_LVDS_N8" LOC = V8;
+# NET "F_LVDS_N8" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_N8" SLEW = SLOW;
+
+# NET "F_LVDS_P[8]" LOC = U8;
+# NET "F_LVDS_N[9]" LOC = V9;
+#NET "F_LVDS_P9" LOC = T9;
+#NET "F_LVDS_P9" IOSTANDARD = LVCMOS33;
+
+# NET "F_LVDS_N[10]" LOC = V11;
+# NET "F_LVDS_P[10]" LOC = U11;
+NET "F_LVDS_N11" LOC = T11;
+NET "F_LVDS_N11" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_N11" SLEW = SLOW;
+NET "F_LVDS_P11" LOC = R11;
+NET "F_LVDS_P11" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_P11" SLEW = SLOW;
+# NET "F_LVDS_N[12]" LOC = V13;
+# NET "F_LVDS_P[12]" LOC = U13;
+# NET "F_LVDS_N[13]" LOC = V14;
+# NET "F_LVDS_P[13]" LOC = T14;
+# NET "F_LVDS_N[14]" LOC = V16;
+# NET "F_LVDS_P[14]" LOC = U16;
+NET "F_LVDS_N15" LOC = V10;
+NET "F_LVDS_N15" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_N15" SLEW = SLOW;
+NET "F_LVDS_P15" LOC = U10;
+NET "F_LVDS_P15" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_P15" SLEW = SLOW;
+
+# NET "F_LVDS_P[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+# NET "F_LVDS_N[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+
+# NET "F_LVDS_NA" LOC = K3;
+# NET "F_LVDS_NA" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+# NET "F_LVDS_PA" LOC = K4;
+# NET "F_LVDS_PA" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+# NET "F_LVDS_NB" LOC = K5;
+# NET "F_LVDS_NB" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_NB" SLEW = SLOW;
+# NET "F_LVDS_PB" LOC = L5;
+# NET "F_LVDS_PB" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_PB" SLEW = SLOW;
+NET "F_LVDS_NC" LOC = L1;
+NET "F_LVDS_NC" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_NC" SLEW = SLOW;
+# NET "F_LVDS_PC" LOC = L2;
+# NET "F_LVDS_PC" IOSTANDARD = LVCMOS33;
+# NET "F_LVDS_PC" SLEW = SLOW;
+
+#NET "F_LVDS_CK0_N" LOC = T8;
+#NET "F_LVDS_CK0_N" IOSTANDARD = LVCMOS33;
+#NET "F_LVDS_CK0_P" LOC = R8;
+#NET "F_LVDS_CK0_P" IOSTANDARD = LVCMOS33;
+
+NET "F_LVDS_CK1_N" LOC = T10;
+NET "F_LVDS_CK1_N" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_CK1_N" SLEW = SLOW;
+NET "F_LVDS_CK1_P" LOC = R10;
+NET "F_LVDS_CK1_P" IOSTANDARD = LVCMOS33;
+NET "F_LVDS_CK1_P" SLEW = SLOW;
+
+# NET "F_LVDS_CK_N[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+# NET "F_LVDS_CK_P[*]" IOSTANDARD = LVDS_33 | DIFF_TERM = TRUE;
+NET "CLK2_P" PERIOD = 20 ns;
diff --git a/toolruns/xilinx/novena/novena_i2c.v b/toolruns/xilinx/novena/novena_i2c.v
new file mode 100644
index 0000000..fd0f667
--- /dev/null
+++ b/toolruns/xilinx/novena/novena_i2c.v
@@ -0,0 +1,147 @@
+//////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013, Andrew "bunnie" Huang
+//
+// See the NOTICE file distributed with this work for additional 
+// information regarding copyright ownership.  The copyright holder 
+// licenses this file to you under the Apache License, Version 2.0 
+// (the "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// code distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//////////////////////////////////////////////////////////////////////////////
+
+/// note: must set "-g UnusedPin:Pullnone" to avoid conflicts with unused pins
+
+`timescale 1ns / 1ps
+
+module novena_fpga(
+	// CPU side mapping
+	input wire [15:0] EIM_DA,
+	output reg EIM_A16,  // relay of the trigger output
+	output reg EIM_A17,  // relay of the trigger data (read path)
+
+	// connector side mapping
+	//input wire F_LVDS_N3, // output of trigger
+	//input wire F_DX2,       // output of trigger
+	//output wire F_LVDS_N5, // trigger reset
+	output wire F_LVDS_P4,   // trigger reset
+	//inout wire F_LVDS_P5, // trigger data (bidir)
+	//input wire F_DX18,      // trigger data in from sticker (DUT->CPU)
+	output wire F_LVDS_P11, // trigger data out to sticker (CPU->DUT)
+	//output wire F_LVDS_N8, // trigger clock
+	//output wire F_DX14,      // trigger clock
+
+	output wire F_LVDS_N7, // drive TPI data line
+	output wire F_LVDS_P7, // drive TPI signal lines
+
+	output wire F_DX15,  // 1 = drive 5V, 0 = drive 3V to DUT
+
+	output wire F_LVDS_CK1_N,
+	output wire F_LVDS_CK1_P,
+	output wire F_LVDS_N11,
+
+	output wire F_LVDS_N0,
+	output wire F_LVDS_P0,
+	output wire F_DX1,
+
+	output wire F_LVDS_N15,
+	output wire F_LVDS_P15,
+	output wire F_LVDS_NC,
+
+	//input wire F_DX11,
+	//input wire F_DX3,
+	//input wire F_DX0,
+
+	//input wire F_LVDS_CK0_P,
+	//input wire F_LVDS_CK0_N,
+	//input wire F_LVDS_P9,
+
+	//input wire [1:0] EIM_CS,
+	//input wire EIM_LBA,
+
+	input wire CLK2_N,
+	input wire CLK2_P,
+	output wire FPGA_LED2,
+
+	input wire I2C3_SCL,
+	inout wire I2C3_SDA,
+
+	input wire RESETBMCU,
+	output wire F_DX17,  // dummy
+	output wire APOPTOSIS
+);
+	wire clk;
+
+	IBUFGDS clkibufgds(
+		.I(CLK2_P),
+		.IB(CLK2_N),
+		.O(clk)
+	);
+
+	assign FPGA_LED2 = 1'b1;
+
+	assign APOPTOSIS = 1'b0;
+	assign F_DX15 = 1'b1; //+5V P_DUT
+   
+	// OE on bank to drive signals; signal not inverted in software
+	assign F_LVDS_P7 = !EIM_DA[3];
+	// OE on bank to drive the data; signal not inverted in software
+	assign F_LVDS_N7 = !EIM_DA[4];
+	assign F_LVDS_P4 = 1'b0;
+	assign F_LVDS_P11 = 1'b0;
+	assign F_LVDS_CK1_N = 1'b0;
+	assign F_LVDS_CK1_P = 1'b0;
+	assign F_LVDS_N11 = 1'b0;
+	assign F_LVDS_N0 = 1'b0;
+	assign F_LVDS_P0 = 1'b0;
+	assign F_DX1 = 1'b0;
+	assign F_LVDS_N15 = 1'b0;
+	assign F_LVDS_P15 = 1'b0;
+	assign F_LVDS_NC = 1'b0;
+
+	// reduction and of EIM_DA, dummy-map to keep compiler quiet
+	assign F_DX17 = &EIM_DA | RESETBMCU;
+   
+	////////////////////////////////////
+	///// I2C register set
+	////////////////////////////////////
+	wire       SDA_pd;
+	wire       SDA_int;
+	reg        clk25;
+   
+	initial begin
+		clk25 <= 1'b0;
+	end
+	always @ (posedge clk) begin
+		clk25 <= ~clk25;
+		EIM_A16 <= 1'b0;
+		EIM_A17 <= 1'b0;
+	end
+   
+	IOBUF #(
+		.DRIVE(8),
+		.SLEW("SLOW")
+	) IOBUF_sda (
+		.IO(I2C3_SDA),
+		.I(1'b0),
+		.T(!SDA_pd),
+		.O(SDA_int)
+	);
+
+   coretest_hashes top(
+		       .clk(clk25),
+		       .reset_n(1'b1),
+      
+		       .SCL(I2C3_SCL),
+		       .SDA(SDA_int),
+		       .SDA_pd(SDA_pd)
+		       );
+
+endmodule
diff --git a/toolruns/xilinx/novena/novena_i2c_simple.bmm b/toolruns/xilinx/novena/novena_i2c_simple.bmm
new file mode 120000
index 0000000..44023c0
--- /dev/null
+++ b/toolruns/xilinx/novena/novena_i2c_simple.bmm
@@ -0,0 +1 @@
+novena_i2c.bmm
\ No newline at end of file
diff --git a/toolruns/xilinx/novena/novena_i2c_simple.ucf b/toolruns/xilinx/novena/novena_i2c_simple.ucf
new file mode 120000
index 0000000..d15676a
--- /dev/null
+++ b/toolruns/xilinx/novena/novena_i2c_simple.ucf
@@ -0,0 +1 @@
+novena_i2c.ucf
\ No newline at end of file
diff --git a/toolruns/xilinx/novena/xilinx.mk b/toolruns/xilinx/novena/xilinx.mk
new file mode 100644
index 0000000..5fb770e
--- /dev/null
+++ b/toolruns/xilinx/novena/xilinx.mk
@@ -0,0 +1,176 @@
+# The top level module should define the variables below then include
+# this file.  The files listed should be in the same directory as the
+# Makefile.  
+#
+#   variable	description
+#   ----------  -------------
+#   project	project name (top level module should match this name)
+#   top_module  top level module of the project
+#   libdir	path to library directory
+#   libs	library modules used
+#   vfiles	all local .v files
+#   xilinx_cores  all local .xco files
+#   vendor      vendor of FPGA (xilinx, altera, etc.)
+#   family      FPGA device family (spartan3e) 
+#   part        FPGA part name (xc4vfx12-10-sf363)
+#   flashsize   size of flash for mcs file (16384)
+#   optfile     (optional) xst extra opttions file to put in .scr
+#   map_opts    (optional) options to give to map
+#   par_opts    (optional) options to give to par
+#   intstyle    (optional) intstyle option to all tools
+#
+#   files 		description
+#   ----------  	------------
+#   $(project).ucf	ucf file
+#
+# Library modules should have a modules.mk in their root directory,
+# namely $(libdir)/<libname>/module.mk, that simply adds to the vfiles
+# and xilinx_cores variable.
+#
+# all the .xco files listed in xilinx_cores will be generated with core, with
+# the resulting .v and .ngc files placed back in the same directory as
+# the .xco file.
+#
+# TODO: .xco files are device dependant, should use a template based system
+
+coregen_work_dir ?= ./coregen-tmp
+map_opts ?= -timing -ol high -detail -pr b -register_duplication -w
+par_opts ?= -ol high
+isedir ?= /opt/Xilinx/13.3/ISE_DS
+xil_env ?= . $(isedir)/settings32.sh
+flashsize ?= 8192
+
+libmks = $(patsubst %,$(libdir)/%/module.mk,$(libs)) 
+mkfiles = Makefile* $(libmks) xilinx.mk
+include $(libmks)
+
+corengcs = $(foreach core,$(xilinx_cores),$(core:.xco=.ngc))
+local_corengcs = $(foreach ngc,$(corengcs),$(notdir $(ngc)))
+vfiles += $(foreach core,$(xilinx_cores),$(core:.xco=.v))
+junk += $(local_corengcs)
+
+.PHONY: default xilinx_cores clean twr etwr
+default: $(project).bit $(project).mcs
+xilinx_cores: $(corengcs)
+twr: $(project).twr
+etwr: $(project)_err.twr
+
+define cp_template
+$(2): $(1)
+	cp $(1) $(2)
+endef
+$(foreach ngc,$(corengcs),$(eval $(call cp_template,$(ngc),$(notdir $(ngc)))))
+
+%.ngc %.v: %.xco
+	@echo "=== rebuilding $@"
+	if [ -d $(coregen_work_dir) ]; then \
+		rm -rf $(coregen_work_dir)/*; \
+	else \
+		mkdir -p $(coregen_work_dir); \
+	fi
+	cd $(coregen_work_dir); \
+	$(xil_env); \
+	coregen -b $$OLDPWD/$<; \
+	cd -
+	xcodir=`dirname $<`; \
+	basename=`basename $< .xco`; \
+	if [ ! -r $(coregen_work_dir/$$basename.ngc) ]; then \
+		echo "'$@' wasn't created."; \
+		exit 1; \
+	else \
+		cp $(coregen_work_dir)/$$basename.v $(coregen_work_dir)/$$basename.ngc $$xcodir; \
+	fi
+junk += $(coregen_work_dir)
+
+date = $(shell date +%F-%H-%M)
+
+# some common junk
+junk += *.xrpt
+
+programming_files: $(project).bit $(project).mcs
+	mkdir -p $@/$(date)
+	mkdir -p $@/latest
+	for x in .bit .mcs .cfi _bd.bmm; do cp $(project)$$x $@/$(date)/$(project)$$x; cp $(project)$$x $@/latest/$(project)$$x; done
+	$(xil_env); xst -help | head -1 | sed 's/^/#/' | cat - $(project).scr > $@/$(date)/$(project).scr
+
+$(project).mcs: $(project).bit
+	$(xil_env); \
+	promgen -w -s $(flashsize) -p mcs -o $@ -u 0 $^
+junk += $(project).mcs $(project).cfi $(project).prm
+
+$(project).bit: $(project)_par.ncd
+	$(xil_env); \
+	bitgen $(intstyle) -g UnusedPin:Pullnone -g DriveDone:yes -g StartupClk:Cclk -w $(project)_par.ncd $(project).bit
+junk += $(project).bgn $(project).bit $(project).drc $(project)_bd.bmm
+
+
+$(project)_par.ncd: $(project).ncd
+	$(xil_env); \
+	if par $(intstyle) $(par_opts) -w $(project).ncd $(project)_par.ncd; then \
+		:; \
+	else \
+		$(MAKE) etwr; \
+	fi 
+junk += $(project)_par.ncd $(project)_par.par $(project)_par.pad 
+junk += $(project)_par_pad.csv $(project)_par_pad.txt 
+junk += $(project)_par.grf $(project)_par.ptwx
+junk += $(project)_par.unroutes $(project)_par.xpi
+
+$(project).ncd: $(project).ngd
+	if [ -r $(project)_par.ncd ]; then \
+		cp $(project)_par.ncd smartguide.ncd; \
+		smartguide="-smartguide smartguide.ncd"; \
+	else \
+		smartguide=""; \
+	fi; \
+	$(xil_env); \
+	map $(intstyle) $(map_opts) $$smartguide $<
+junk += $(project).ncd $(project).pcf $(project).ngm $(project).mrp $(project).map
+junk += smartguide.ncd $(project).psr 
+junk += $(project)_summary.xml $(project)_usage.xml
+
+$(project).ngd: $(project).ngc $(project).ucf $(project).bmm
+	$(xil_env); ngdbuild $(intstyle) $(project).ngc -bm $(project).bmm
+junk += $(project).ngd $(project).bld
+
+$(project).ngc: $(vfiles) $(local_corengcs) $(project).scr $(project).prj
+	$(xil_env); xst $(intstyle) -ifn $(project).scr
+junk += xlnx_auto* $(top_module).lso $(project).srp 
+junk += netlist.lst xst $(project).ngc
+
+$(project).prj: $(vfiles) $(mkfiles)
+	for src in $(vfiles); do echo "verilog work $$src" >> $(project).tmpprj; done
+	sort -u $(project).tmpprj > $(project).prj
+	rm -f $(project).tmpprj
+junk += $(project).prj
+
+optfile += $(wildcard $(project).opt)
+top_module ?= $(project)
+$(project).scr: $(optfile) $(mkfiles) ./xilinx.opt
+	echo "run" > $@
+	echo "-p $(part)" >> $@
+	echo "-top $(top_module)" >> $@
+	echo "-ifn $(project).prj" >> $@
+	echo "-ofn $(project).ngc" >> $@
+	cat ./xilinx.opt $(optfile) >> $@
+junk += $(project).scr
+
+$(project).post_map.twr: $(project).ncd
+	$(xil_env); trce -e 10 $< $(project).pcf -o $@
+junk += $(project).post_map.twr $(project).post_map.twx smartpreview.twr
+
+$(project).twr: $(project)_par.ncd
+	$(xil_env); trce $< $(project).pcf -o $(project).twr
+junk += $(project).twr $(project).twx smartpreview.twr
+
+$(project)_err.twr: $(project)_par.ncd
+	$(xil_env); trce -e 10 $< $(project).pcf -o $(project)_err.twr
+junk += $(project)_err.twr $(project)_err.twx
+junk += $(project).lso $(project)_bitgen.xwb $(project)_bitgen.xwbt
+junk += usage_statistics_webtalk.html par_usage_statistics.html webtalk.log _xmsgs default.xreport
+
+.gitignore: $(mkfiles)
+	echo programming_files $(junk) | sed 's, ,\n,g' > .gitignore
+
+clean::
+	rm -rf $(junk)
diff --git a/toolruns/xilinx/novena/xilinx.opt b/toolruns/xilinx/novena/xilinx.opt
new file mode 100644
index 0000000..7fe9d8b
--- /dev/null
+++ b/toolruns/xilinx/novena/xilinx.opt
@@ -0,0 +1,42 @@
+-ifmt mixed
+-ofmt NGC
+-opt_mode speed
+-opt_level 1
+-iuc NO
+-keep_hierarchy no
+-netlist_hierarchy as_optimized
+-rtlview no
+-glob_opt AllClockNets
+-read_cores yes
+-write_timing_constraints NO
+-cross_clock_analysis NO
+-hierarchy_separator /
+-bus_delimiter <>
+-case maintain
+-slice_utilization_ratio 100
+-bram_utilization_ratio 100
+#-dsp_utilization_ratio 100
+-safe_implementation No
+-fsm_extract YES
+-fsm_encoding Auto
+-fsm_style lut
+-ram_extract Yes
+-ram_style Auto
+-rom_extract Yes
+-rom_style Auto
+-shreg_extract YES
+-auto_bram_packing NO
+-resource_sharing YES
+-async_to_sync NO
+#-use_dsp48 auto
+-iobuf YES
+-max_fanout 500
+-register_duplication YES
+-register_balancing No
+-optimize_primitives NO
+-use_clock_enable Auto
+-use_sync_set Auto
+-use_sync_reset Auto
+-iob auto
+-equivalent_register_removal YES
+-slice_utilization_ratio_maxmargin 5



More information about the Commits mailing list