[Cryptech-Commits] [core/cipher/aes_speed] 01/01: Adding inital version of AES core optimized for performance.

git at cryptech.is git at cryptech.is
Mon May 21 13:36:33 UTC 2018


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

joachim at secworks.se pushed a commit to branch master
in repository core/cipher/aes_speed.

commit e389761d61a5616add66ce2dacd0f13feb68d1e1
Author: Joachim Strömbergson <joachim at secworks.se>
AuthorDate: Mon May 21 15:35:55 2018 +0200

    Adding inital version of AES core optimized for performance.
---
 LICENSE                        |  29 ++
 README.md                      |  48 +++
 src/rtl/aes.v                  | 278 ++++++++++++++++++
 src/rtl/aes_core.v             | 339 +++++++++++++++++++++
 src/rtl/aes_decipher_block.v   | 525 +++++++++++++++++++++++++++++++++
 src/rtl/aes_encipher_block.v   | 486 ++++++++++++++++++++++++++++++
 src/rtl/aes_inv_sbox.v         | 325 +++++++++++++++++++++
 src/rtl/aes_key_mem.v          | 454 ++++++++++++++++++++++++++++
 src/rtl/aes_sbox.v             | 325 +++++++++++++++++++++
 src/tb/tb_aes.v                | 542 ++++++++++++++++++++++++++++++++++
 src/tb/tb_aes_core.v           | 464 +++++++++++++++++++++++++++++
 src/tb/tb_aes_decipher_block.v | 406 +++++++++++++++++++++++++
 src/tb/tb_aes_encipher_block.v | 422 ++++++++++++++++++++++++++
 src/tb/tb_aes_key_mem.v        | 650 +++++++++++++++++++++++++++++++++++++++++
 toolruns/Makefile              | 133 +++++++++
 15 files changed, 5426 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..aa1732f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Author: Joachim Strömbergson
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6322084
--- /dev/null
+++ b/README.md
@@ -0,0 +1,48 @@
+aes_speed
+=========
+
+Speed optimized Verilog implementation of the symmetric block cipher AES
+(Advanced Encryption Standard) as specified in the NIST document [FIPS
+197](http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf).
+
+This core is modified version of the Cryptech AES core. Note that the
+name of the core modules are identical to that core. The purpose of this
+is to allow a drop-in replacement in Cryptech designs.
+
+
+## Status ##
+Just started, not done. Does not work.
+
+
+## Introduction ##
+
+This implementation supports 128 and 256 bit keys. The
+implementation is iterative and process one 128 block at a time. Blocks
+are processed on a word level with 4 S-boxes in the data path. The
+S-boxes for encryption are shared with the key expansion and the core
+can thus not do key update in parallel with block processing.
+
+The encipher and decipher block processing datapaths are separated and
+basically self contained given access to a set of round keys and a
+block. This makes it possible to hard wire either encipher or decipher
+and allow the build tools to optimize away the other functionality which
+will reduce the size to about 50%. For cipher modes such as CTR, GCM
+decryption in the AES core will never be used and thus the decipher
+block processing can be removed.
+
+This is a fairly compact implementation. Further reduction could be
+achived by just having a single S-box. Similarly the performane can be
+increased by having 8 or even 16 S-boxes which would reduce the number
+of cycles to two cycles for each round.
+
+
+## Performance and area comparison ##
+Number of cycles for the Cryptech AES core:
+- TBW
+
+
+Number of cycles for the Cryptech AES core:
+- TBW
+
+
+Resources used by the Crypteh AES core:
diff --git a/src/rtl/aes.v b/src/rtl/aes.v
new file mode 100644
index 0000000..0d719d2
--- /dev/null
+++ b/src/rtl/aes.v
@@ -0,0 +1,278 @@
+//======================================================================
+//
+// aes.v
+// --------
+// Top level wrapper for the AES block cipher core.
+//
+//
+// Author: Joachim Strombergson
+// 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 aes(
+           // Clock and reset.
+           input wire           clk,
+           input wire           reset_n,
+
+           // Control.
+           input wire           cs,
+           input wire           we,
+
+           // Data ports.
+           input wire  [7 : 0]  address,
+           input wire  [31 : 0] write_data,
+           output wire [31 : 0] read_data,
+           output wire          error
+          );
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  localparam ADDR_NAME0       = 8'h00;
+  localparam ADDR_NAME1       = 8'h01;
+  localparam ADDR_VERSION     = 8'h02;
+
+  localparam ADDR_CTRL        = 8'h08;
+  localparam CTRL_INIT_BIT    = 0;
+  localparam CTRL_NEXT_BIT    = 1;
+
+  localparam ADDR_STATUS      = 8'h09;
+  localparam STATUS_READY_BIT = 0;
+  localparam STATUS_VALID_BIT = 1;
+
+  localparam ADDR_CONFIG      = 8'h0a;
+  localparam CTRL_ENCDEC_BIT  = 0;
+  localparam CTRL_KEYLEN_BIT  = 1;
+
+  localparam ADDR_KEY0        = 8'h10;
+  localparam ADDR_KEY7        = 8'h17;
+
+  localparam ADDR_BLOCK0      = 8'h20;
+  localparam ADDR_BLOCK3      = 8'h23;
+
+  localparam ADDR_RESULT0     = 8'h30;
+  localparam ADDR_RESULT1     = 8'h31;
+  localparam ADDR_RESULT2     = 8'h32;
+  localparam ADDR_RESULT3     = 8'h33;
+
+  localparam CORE_NAME0       = 32'h61657320; // "aes "
+  localparam CORE_NAME1       = 32'h20202020; // "    "
+  localparam CORE_VERSION     = 32'h302e3630; // "0.60"
+
+
+  //----------------------------------------------------------------
+  // Registers including update variables and write enable.
+  //----------------------------------------------------------------
+  reg init_reg;
+  reg init_new;
+
+  reg next_reg;
+  reg next_new;
+
+  reg encdec_reg;
+  reg keylen_reg;
+  reg config_we;
+
+  reg [31 : 0] block_reg [0 : 3];
+  reg          block_we;
+
+  reg [31 : 0] key_reg [0 : 7];
+  reg          key_we;
+
+  reg [127 : 0] result_reg;
+  reg           valid_reg;
+  reg           ready_reg;
+
+
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  reg [31 : 0]   tmp_read_data;
+  reg            tmp_error;
+
+  wire           core_encdec;
+  wire           core_init;
+  wire           core_next;
+  wire           core_ready;
+  wire [255 : 0] core_key;
+  wire           core_keylen;
+  wire [127 : 0] core_block;
+  wire [127 : 0] core_result;
+  wire           core_valid;
+
+
+  //----------------------------------------------------------------
+  // Concurrent connectivity for ports etc.
+  //----------------------------------------------------------------
+  assign read_data = tmp_read_data;
+  assign error     = tmp_error;
+
+  assign core_key = {key_reg[0], key_reg[1], key_reg[2], key_reg[3],
+                     key_reg[4], key_reg[5], key_reg[6], key_reg[7]};
+
+  assign core_block  = {block_reg[0], block_reg[1],
+                        block_reg[2], block_reg[3]};
+  assign core_init   = init_reg;
+  assign core_next   = next_reg;
+  assign core_encdec = encdec_reg;
+  assign core_keylen = keylen_reg;
+
+
+  //----------------------------------------------------------------
+  // core instantiation.
+  //----------------------------------------------------------------
+  aes_core core(
+                .clk(clk),
+                .reset_n(reset_n),
+
+                .encdec(core_encdec),
+                .init(core_init),
+                .next(core_next),
+                .ready(core_ready),
+
+                .key(core_key),
+                .keylen(core_keylen),
+
+                .block(core_block),
+                .result(core_result),
+                .result_valid(core_valid)
+               );
+
+
+  //----------------------------------------------------------------
+  // reg_update
+  // Update functionality for all registers in the core.
+  // All registers are positive edge triggered with asynchronous
+  // active low reset.
+  //----------------------------------------------------------------
+  always @ (posedge clk or negedge reset_n)
+    begin : reg_update
+      integer i;
+
+      if (!reset_n)
+        begin
+          for (i = 0 ; i < 4 ; i = i + 1)
+            block_reg[i] <= 32'h0;
+
+          for (i = 0 ; i < 8 ; i = i + 1)
+            key_reg[i] <= 32'h0;
+
+          init_reg   <= 1'b0;
+          next_reg   <= 1'b0;
+          encdec_reg <= 1'b0;
+          keylen_reg <= 1'b0;
+
+          result_reg <= 128'h0;
+          valid_reg  <= 1'b0;
+          ready_reg  <= 1'b0;
+        end
+      else
+        begin
+          ready_reg  <= core_ready;
+          valid_reg  <= core_valid;
+          result_reg <= core_result;
+          init_reg   <= init_new;
+          next_reg   <= next_new;
+
+          if (config_we)
+            begin
+              encdec_reg <= write_data[CTRL_ENCDEC_BIT];
+              keylen_reg <= write_data[CTRL_KEYLEN_BIT];
+            end
+
+          if (key_we)
+            key_reg[address[2 : 0]] <= write_data;
+
+          if (block_we)
+            block_reg[address[1 : 0]] <= write_data;
+        end
+    end // reg_update
+
+
+  //----------------------------------------------------------------
+  // api
+  //
+  // The interface command decoding logic.
+  //----------------------------------------------------------------
+  always @*
+    begin : api
+      init_new      = 1'b0;
+      next_new      = 1'b0;
+      config_we     = 1'b0;
+      key_we        = 1'b0;
+      block_we      = 1'b0;
+      tmp_read_data = 32'h0;
+      tmp_error     = 1'b0;
+
+      if (cs)
+        begin
+          if (we)
+            begin
+              if (address == ADDR_CTRL)
+                begin
+                  init_new = write_data[CTRL_INIT_BIT];
+                  next_new = write_data[CTRL_NEXT_BIT];
+                end
+
+              if (address == ADDR_CONFIG)
+                config_we = 1'b1;
+
+              if ((address >= ADDR_KEY0) && (address <= ADDR_KEY7))
+                key_we = 1'b1;
+
+              if ((address >= ADDR_BLOCK0) && (address <= ADDR_BLOCK3))
+                block_we = 1'b1;
+            end // if (we)
+
+          else
+            begin
+              case (address)
+                ADDR_NAME0:   tmp_read_data = CORE_NAME0;
+                ADDR_NAME1:   tmp_read_data = CORE_NAME1;
+                ADDR_VERSION: tmp_read_data = CORE_VERSION;
+                ADDR_CTRL:    tmp_read_data = {28'h0, keylen_reg, encdec_reg, next_reg, init_reg};
+                ADDR_STATUS:  tmp_read_data = {30'h0, valid_reg, ready_reg};
+
+                default:
+                  begin
+                  end
+              endcase // case (address)
+
+              if ((address >= ADDR_RESULT0) && (address <= ADDR_RESULT3))
+                tmp_read_data = result_reg[(3 - (address - ADDR_RESULT0)) * 32 +: 32];
+            end
+        end
+    end // addr_decoder
+endmodule // aes
+
+//======================================================================
+// EOF aes.v
+//======================================================================
diff --git a/src/rtl/aes_core.v b/src/rtl/aes_core.v
new file mode 100644
index 0000000..5196a1f
--- /dev/null
+++ b/src/rtl/aes_core.v
@@ -0,0 +1,339 @@
+//======================================================================
+//
+// aes.core.v
+// ----------
+// The AES core. This core supports key size of 128, and 256 bits.
+// Most of the functionality is within the submodules.
+//
+//
+// Author: Joachim Strombergson
+// 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 aes_core(
+                input wire            clk,
+                input wire            reset_n,
+
+                input wire            encdec,
+                input wire            init,
+                input wire            next,
+                output wire           ready,
+
+                input wire [255 : 0]  key,
+                input wire            keylen,
+
+                input wire [127 : 0]  block,
+                output wire [127 : 0] result,
+                output wire           result_valid
+               );
+
+
+
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  localparam CTRL_IDLE  = 2'h0;
+  localparam CTRL_INIT  = 2'h1;
+  localparam CTRL_NEXT  = 2'h2;
+
+
+  //----------------------------------------------------------------
+  // Registers including update variables and write enable.
+  //----------------------------------------------------------------
+  reg [1 : 0] aes_core_ctrl_reg;
+  reg [1 : 0] aes_core_ctrl_new;
+  reg         aes_core_ctrl_we;
+
+  reg         result_valid_reg;
+  reg         result_valid_new;
+  reg         result_valid_we;
+
+  reg         ready_reg;
+  reg         ready_new;
+  reg         ready_we;
+
+
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  reg            init_state;
+
+  wire [127 : 0] round_key;
+  wire           key_ready;
+
+  reg            enc_next;
+  wire [3 : 0]   enc_round_nr;
+  wire [127 : 0] enc_new_block;
+  wire           enc_ready;
+  wire [31 : 0]  enc_sboxw;
+
+  reg            dec_next;
+  wire [3 : 0]   dec_round_nr;
+  wire [127 : 0] dec_new_block;
+  wire           dec_ready;
+
+  reg [127 : 0]  muxed_new_block;
+  reg [3 : 0]    muxed_round_nr;
+  reg            muxed_ready;
+
+  wire [31 : 0]  keymem_sboxw;
+
+  reg [31 : 0]   muxed_sboxw;
+  wire [31 : 0]  new_sboxw;
+
+
+  //----------------------------------------------------------------
+  // Instantiations.
+  //----------------------------------------------------------------
+  aes_encipher_block enc_block(
+                               .clk(clk),
+                               .reset_n(reset_n),
+
+                               .next(enc_next),
+
+                               .keylen(keylen),
+                               .round(enc_round_nr),
+                               .round_key(round_key),
+
+                               .sboxw(enc_sboxw),
+                               .new_sboxw(new_sboxw),
+
+                               .block(block),
+                               .new_block(enc_new_block),
+                               .ready(enc_ready)
+                              );
+
+
+  aes_decipher_block dec_block(
+                               .clk(clk),
+                               .reset_n(reset_n),
+
+                               .next(dec_next),
+
+                               .keylen(keylen),
+                               .round(dec_round_nr),
+                               .round_key(round_key),
+
+                               .block(block),
+                               .new_block(dec_new_block),
+                               .ready(dec_ready)
+                              );
+
+
+  aes_key_mem keymem(
+                     .clk(clk),
+                     .reset_n(reset_n),
+
+                     .key(key),
+                     .keylen(keylen),
+                     .init(init),
+
+                     .round(muxed_round_nr),
+                     .round_key(round_key),
+                     .ready(key_ready),
+
+                     .sboxw(keymem_sboxw),
+                     .new_sboxw(new_sboxw)
+                    );
+
+
+  aes_sbox sbox_inst(.sboxw(muxed_sboxw), .new_sboxw(new_sboxw));
+
+
+  //----------------------------------------------------------------
+  // Concurrent connectivity for ports etc.
+  //----------------------------------------------------------------
+  assign ready        = ready_reg;
+  assign result       = muxed_new_block;
+  assign result_valid = result_valid_reg;
+
+
+  //----------------------------------------------------------------
+  // reg_update
+  //
+  // Update functionality for all registers in the core.
+  // All registers are positive edge triggered with asynchronous
+  // active low reset. All registers have write enable.
+  //----------------------------------------------------------------
+  always @ (posedge clk or negedge reset_n)
+    begin: reg_update
+      if (!reset_n)
+        begin
+          result_valid_reg  <= 1'b0;
+          ready_reg         <= 1'b1;
+          aes_core_ctrl_reg <= CTRL_IDLE;
+        end
+      else
+        begin
+          if (result_valid_we)
+            result_valid_reg <= result_valid_new;
+
+          if (ready_we)
+            ready_reg <= ready_new;
+
+          if (aes_core_ctrl_we)
+            aes_core_ctrl_reg <= aes_core_ctrl_new;
+        end
+    end // reg_update
+
+
+  //----------------------------------------------------------------
+  // sbox_mux
+  //
+  // Controls which of the encipher datapath or the key memory
+  // that gets access to the sbox.
+  //----------------------------------------------------------------
+  always @*
+    begin : sbox_mux
+      if (init_state)
+        begin
+          muxed_sboxw = keymem_sboxw;
+        end
+      else
+        begin
+          muxed_sboxw = enc_sboxw;
+        end
+    end // sbox_mux
+
+
+  //----------------------------------------------------------------
+  // encdex_mux
+  //
+  // Controls which of the datapaths that get the next signal, have
+  // access to the memory as well as the block processing result.
+  //----------------------------------------------------------------
+  always @*
+    begin : encdec_mux
+      enc_next = 1'b0;
+      dec_next = 1'b0;
+
+      if (encdec)
+        begin
+          // Encipher operations
+          enc_next        = next;
+          muxed_round_nr  = enc_round_nr;
+          muxed_new_block = enc_new_block;
+          muxed_ready     = enc_ready;
+        end
+      else
+        begin
+          // Decipher operations
+          dec_next        = next;
+          muxed_round_nr  = dec_round_nr;
+          muxed_new_block = dec_new_block;
+          muxed_ready     = dec_ready;
+        end
+    end // encdec_mux
+
+
+  //----------------------------------------------------------------
+  // aes_core_ctrl
+  //
+  // Control FSM for aes core. Basically tracks if we are in
+  // key init, encipher or decipher modes and connects the
+  // different submodules to shared resources and interface ports.
+  //----------------------------------------------------------------
+  always @*
+    begin : aes_core_ctrl
+      init_state        = 1'b0;
+      ready_new         = 1'b0;
+      ready_we          = 1'b0;
+      result_valid_new  = 1'b0;
+      result_valid_we   = 1'b0;
+      aes_core_ctrl_new = CTRL_IDLE;
+      aes_core_ctrl_we  = 1'b0;
+
+      case (aes_core_ctrl_reg)
+        CTRL_IDLE:
+          begin
+            if (init)
+              begin
+                init_state        = 1'b1;
+                ready_new         = 1'b0;
+                ready_we          = 1'b1;
+                result_valid_new  = 1'b0;
+                result_valid_we   = 1'b1;
+                aes_core_ctrl_new = CTRL_INIT;
+                aes_core_ctrl_we  = 1'b1;
+              end
+            else if (next)
+              begin
+                init_state        = 1'b0;
+                ready_new         = 1'b0;
+                ready_we          = 1'b1;
+                result_valid_new  = 1'b0;
+                result_valid_we   = 1'b1;
+                aes_core_ctrl_new = CTRL_NEXT;
+                aes_core_ctrl_we  = 1'b1;
+              end
+          end
+
+        CTRL_INIT:
+          begin
+            init_state = 1'b1;
+
+            if (key_ready)
+              begin
+                ready_new         = 1'b1;
+                ready_we          = 1'b1;
+                aes_core_ctrl_new = CTRL_IDLE;
+                aes_core_ctrl_we  = 1'b1;
+              end
+          end
+
+        CTRL_NEXT:
+          begin
+            init_state = 1'b0;
+
+            if (muxed_ready)
+              begin
+                ready_new         = 1'b1;
+                ready_we          = 1'b1;
+                result_valid_new  = 1'b1;
+                result_valid_we   = 1'b1;
+                aes_core_ctrl_new = CTRL_IDLE;
+                aes_core_ctrl_we  = 1'b1;
+             end
+          end
+
+        default:
+          begin
+
+          end
+      endcase // case (aes_core_ctrl_reg)
+
+    end // aes_core_ctrl
+endmodule // aes_core
+
+//======================================================================
+// EOF aes_core.v
+//======================================================================
diff --git a/src/rtl/aes_decipher_block.v b/src/rtl/aes_decipher_block.v
new file mode 100644
index 0000000..82bdffb
--- /dev/null
+++ b/src/rtl/aes_decipher_block.v
@@ -0,0 +1,525 @@
+//======================================================================
+//
+// aes_decipher_block.v
+// --------------------
+// The AES decipher round. A pure combinational module that implements
+// the initial round, main round and final round logic for
+// decciper operations.
+//
+//
+// Author: Joachim Strombergson
+// 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 aes_decipher_block(
+                          input wire            clk,
+                          input wire            reset_n,
+
+                          input wire            next,
+
+                          input wire            keylen,
+                          output wire [3 : 0]   round,
+                          input wire [127 : 0]  round_key,
+
+                          input wire [127 : 0]  block,
+                          output wire [127 : 0] new_block,
+                          output wire           ready
+                         );
+
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  localparam AES_128_BIT_KEY = 1'h0;
+  localparam AES_256_BIT_KEY = 1'h1;
+
+  localparam AES128_ROUNDS = 4'ha;
+  localparam AES256_ROUNDS = 4'he;
+
+  localparam NO_UPDATE    = 3'h0;
+  localparam INIT_UPDATE  = 3'h1;
+  localparam SBOX_UPDATE  = 3'h2;
+  localparam MAIN_UPDATE  = 3'h3;
+  localparam FINAL_UPDATE = 3'h4;
+
+  localparam CTRL_IDLE  = 3'h0;
+  localparam CTRL_INIT  = 3'h1;
+  localparam CTRL_SBOX  = 3'h2;
+  localparam CTRL_MAIN  = 3'h3;
+  localparam CTRL_FINAL = 3'h4;
+
+
+  //----------------------------------------------------------------
+  // Gaolis multiplication functions for Inverse MixColumn.
+  //----------------------------------------------------------------
+  function [7 : 0] gm2(input [7 : 0] op);
+    begin
+      gm2 = {op[6 : 0], 1'b0} ^ (8'h1b & {8{op[7]}});
+    end
+  endfunction // gm2
+
+  function [7 : 0] gm3(input [7 : 0] op);
+    begin
+      gm3 = gm2(op) ^ op;
+    end
+  endfunction // gm3
+
+  function [7 : 0] gm4(input [7 : 0] op);
+    begin
+      gm4 = gm2(gm2(op));
+    end
+  endfunction // gm4
+
+  function [7 : 0] gm8(input [7 : 0] op);
+    begin
+      gm8 = gm2(gm4(op));
+    end
+  endfunction // gm8
+
+  function [7 : 0] gm09(input [7 : 0] op);
+    begin
+      gm09 = gm8(op) ^ op;
+    end
+  endfunction // gm09
+
+  function [7 : 0] gm11(input [7 : 0] op);
+    begin
+      gm11 = gm8(op) ^ gm2(op) ^ op;
+    end
+  endfunction // gm11
+
+  function [7 : 0] gm13(input [7 : 0] op);
+    begin
+      gm13 = gm8(op) ^ gm4(op) ^ op;
+    end
+  endfunction // gm13
+
+  function [7 : 0] gm14(input [7 : 0] op);
+    begin
+      gm14 = gm8(op) ^ gm4(op) ^ gm2(op);
+    end
+  endfunction // gm14
+
+  function [31 : 0] inv_mixw(input [31 : 0] w);
+    reg [7 : 0] b0, b1, b2, b3;
+    reg [7 : 0] mb0, mb1, mb2, mb3;
+    begin
+      b0 = w[31 : 24];
+      b1 = w[23 : 16];
+      b2 = w[15 : 08];
+      b3 = w[07 : 00];
+
+      mb0 = gm14(b0) ^ gm11(b1) ^ gm13(b2) ^ gm09(b3);
+      mb1 = gm09(b0) ^ gm14(b1) ^ gm11(b2) ^ gm13(b3);
+      mb2 = gm13(b0) ^ gm09(b1) ^ gm14(b2) ^ gm11(b3);
+      mb3 = gm11(b0) ^ gm13(b1) ^ gm09(b2) ^ gm14(b3);
+
+      inv_mixw = {mb0, mb1, mb2, mb3};
+    end
+  endfunction // mixw
+
+  function [127 : 0] inv_mixcolumns(input [127 : 0] data);
+    reg [31 : 0] w0, w1, w2, w3;
+    reg [31 : 0] ws0, ws1, ws2, ws3;
+    begin
+      w0 = data[127 : 096];
+      w1 = data[095 : 064];
+      w2 = data[063 : 032];
+      w3 = data[031 : 000];
+
+      ws0 = inv_mixw(w0);
+      ws1 = inv_mixw(w1);
+      ws2 = inv_mixw(w2);
+      ws3 = inv_mixw(w3);
+
+      inv_mixcolumns = {ws0, ws1, ws2, ws3};
+    end
+  endfunction // inv_mixcolumns
+
+  function [127 : 0] inv_shiftrows(input [127 : 0] data);
+    reg [31 : 0] w0, w1, w2, w3;
+    reg [31 : 0] ws0, ws1, ws2, ws3;
+    begin
+      w0 = data[127 : 096];
+      w1 = data[095 : 064];
+      w2 = data[063 : 032];
+      w3 = data[031 : 000];
+
+      ws0 = {w0[31 : 24], w3[23 : 16], w2[15 : 08], w1[07 : 00]};
+      ws1 = {w1[31 : 24], w0[23 : 16], w3[15 : 08], w2[07 : 00]};
+      ws2 = {w2[31 : 24], w1[23 : 16], w0[15 : 08], w3[07 : 00]};
+      ws3 = {w3[31 : 24], w2[23 : 16], w1[15 : 08], w0[07 : 00]};
+
+      inv_shiftrows = {ws0, ws1, ws2, ws3};
+    end
+  endfunction // inv_shiftrows
+
+  function [127 : 0] addroundkey(input [127 : 0] data, input [127 : 0] rkey);
+    begin
+      addroundkey = data ^ rkey;
+    end
+  endfunction // addroundkey
+
+
+  //----------------------------------------------------------------
+  // Registers including update variables and write enable.
+  //----------------------------------------------------------------
+  reg [1 : 0]   sword_ctr_reg;
+  reg [1 : 0]   sword_ctr_new;
+  reg           sword_ctr_we;
+  reg           sword_ctr_inc;
+  reg           sword_ctr_rst;
+
+  reg [3 : 0]   round_ctr_reg;
+  reg [3 : 0]   round_ctr_new;
+  reg           round_ctr_we;
+  reg           round_ctr_set;
+  reg           round_ctr_dec;
+
+  reg [127 : 0] block_new;
+  reg [31 : 0]  block_w0_reg;
+  reg [31 : 0]  block_w1_reg;
+  reg [31 : 0]  block_w2_reg;
+  reg [31 : 0]  block_w3_reg;
+  reg           block_w0_we;
+  reg           block_w1_we;
+  reg           block_w2_we;
+  reg           block_w3_we;
+
+  reg           ready_reg;
+  reg           ready_new;
+  reg           ready_we;
+
+  reg [2 : 0]   dec_ctrl_reg;
+  reg [2 : 0]   dec_ctrl_new;
+  reg           dec_ctrl_we;
+
+
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  reg [31 : 0]  tmp_sboxw;
+  wire [31 : 0] new_sboxw;
+  reg [2 : 0]   update_type;
+
+
+  //----------------------------------------------------------------
+  // Instantiations.
+  //----------------------------------------------------------------
+  aes_inv_sbox inv_sbox_inst(.sword(tmp_sboxw), .new_sword(new_sboxw));
+
+
+  //----------------------------------------------------------------
+  // Concurrent connectivity for ports etc.
+  //----------------------------------------------------------------
+  assign round     = round_ctr_reg;
+  assign new_block = {block_w0_reg, block_w1_reg, block_w2_reg, block_w3_reg};
+  assign ready     = ready_reg;
+
+
+  //----------------------------------------------------------------
+  // 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 or negedge reset_n)
+    begin: reg_update
+      if (!reset_n)
+        begin
+          block_w0_reg  <= 32'h0;
+          block_w1_reg  <= 32'h0;
+          block_w2_reg  <= 32'h0;
+          block_w3_reg  <= 32'h0;
+          sword_ctr_reg <= 2'h0;
+          round_ctr_reg <= 4'h0;
+          ready_reg     <= 1'b1;
+          dec_ctrl_reg  <= CTRL_IDLE;
+        end
+      else
+        begin
+          if (block_w0_we)
+            block_w0_reg <= block_new[127 : 096];
+
+          if (block_w1_we)
+            block_w1_reg <= block_new[095 : 064];
+
+          if (block_w2_we)
+            block_w2_reg <= block_new[063 : 032];
+
+          if (block_w3_we)
+            block_w3_reg <= block_new[031 : 000];
+
+          if (sword_ctr_we)
+            sword_ctr_reg <= sword_ctr_new;
+
+          if (round_ctr_we)
+            round_ctr_reg <= round_ctr_new;
+
+          if (ready_we)
+            ready_reg <= ready_new;
+
+          if (dec_ctrl_we)
+            dec_ctrl_reg <= dec_ctrl_new;
+        end
+    end // reg_update
+
+
+  //----------------------------------------------------------------
+  // round_logic
+  //
+  // The logic needed to implement init, main and final rounds.
+  //----------------------------------------------------------------
+  always @*
+    begin : round_logic
+      reg [127 : 0] old_block, inv_shiftrows_block, inv_mixcolumns_block;
+      reg [127 : 0] addkey_block;
+
+      inv_shiftrows_block  = 128'h0;
+      inv_mixcolumns_block = 128'h0;
+      addkey_block         = 128'h0;
+      block_new            = 128'h0;
+      tmp_sboxw            = 32'h0;
+      block_w0_we          = 1'b0;
+      block_w1_we          = 1'b0;
+      block_w2_we          = 1'b0;
+      block_w3_we          = 1'b0;
+
+      old_block            = {block_w0_reg, block_w1_reg, block_w2_reg, block_w3_reg};
+
+      // Update based on update type.
+      case (update_type)
+        // InitRound
+        INIT_UPDATE:
+          begin
+            old_block           = block;
+            addkey_block        = addroundkey(old_block, round_key);
+            inv_shiftrows_block = inv_shiftrows(addkey_block);
+            block_new           = inv_shiftrows_block;
+            block_w0_we         = 1'b1;
+            block_w1_we         = 1'b1;
+            block_w2_we         = 1'b1;
+            block_w3_we         = 1'b1;
+          end
+
+        SBOX_UPDATE:
+          begin
+            block_new = {new_sboxw, new_sboxw, new_sboxw, new_sboxw};
+
+            case (sword_ctr_reg)
+              2'h0:
+                begin
+                  tmp_sboxw   = block_w0_reg;
+                  block_w0_we = 1'b1;
+                end
+
+              2'h1:
+                begin
+                  tmp_sboxw   = block_w1_reg;
+                  block_w1_we = 1'b1;
+                end
+
+              2'h2:
+                begin
+                  tmp_sboxw   = block_w2_reg;
+                  block_w2_we = 1'b1;
+                end
+
+              2'h3:
+                begin
+                  tmp_sboxw   = block_w3_reg;
+                  block_w3_we = 1'b1;
+                end
+            endcase // case (sbox_mux_ctrl_reg)
+          end
+
+        MAIN_UPDATE:
+          begin
+            addkey_block         = addroundkey(old_block, round_key);
+            inv_mixcolumns_block = inv_mixcolumns(addkey_block);
+            inv_shiftrows_block  = inv_shiftrows(inv_mixcolumns_block);
+            block_new            = inv_shiftrows_block;
+            block_w0_we          = 1'b1;
+            block_w1_we          = 1'b1;
+            block_w2_we          = 1'b1;
+            block_w3_we          = 1'b1;
+          end
+
+        FINAL_UPDATE:
+          begin
+            block_new    = addroundkey(old_block, round_key);
+            block_w0_we  = 1'b1;
+            block_w1_we  = 1'b1;
+            block_w2_we  = 1'b1;
+            block_w3_we  = 1'b1;
+          end
+
+        default:
+          begin
+          end
+      endcase // case (update_type)
+    end // round_logic
+
+
+  //----------------------------------------------------------------
+  // sword_ctr
+  //
+  // The subbytes word counter with reset and increase logic.
+  //----------------------------------------------------------------
+  always @*
+    begin : sword_ctr
+      sword_ctr_new = 2'h0;
+      sword_ctr_we  = 1'b0;
+
+      if (sword_ctr_rst)
+        begin
+          sword_ctr_new = 2'h0;
+          sword_ctr_we  = 1'b1;
+        end
+      else if (sword_ctr_inc)
+        begin
+          sword_ctr_new = sword_ctr_reg + 1'b1;
+          sword_ctr_we  = 1'b1;
+        end
+    end // sword_ctr
+
+
+  //----------------------------------------------------------------
+  // round_ctr
+  //
+  // The round counter with reset and increase logic.
+  //----------------------------------------------------------------
+  always @*
+    begin : round_ctr
+      round_ctr_new = 4'h0;
+      round_ctr_we  = 1'b0;
+
+      if (round_ctr_set)
+        begin
+          if (keylen == AES_256_BIT_KEY)
+            begin
+              round_ctr_new = AES256_ROUNDS;
+            end
+          else
+            begin
+              round_ctr_new = AES128_ROUNDS;
+            end
+          round_ctr_we  = 1'b1;
+        end
+      else if (round_ctr_dec)
+        begin
+          round_ctr_new = round_ctr_reg - 1'b1;
+          round_ctr_we  = 1'b1;
+        end
+    end // round_ctr
+
+
+  //----------------------------------------------------------------
+  // decipher_ctrl
+  //
+  // The FSM that controls the decipher operations.
+  //----------------------------------------------------------------
+  always @*
+    begin: decipher_ctrl
+      sword_ctr_inc = 1'b0;
+      sword_ctr_rst = 1'b0;
+      round_ctr_dec = 1'b0;
+      round_ctr_set = 1'b0;
+      ready_new     = 1'b0;
+      ready_we      = 1'b0;
+      update_type   = NO_UPDATE;
+      dec_ctrl_new  = CTRL_IDLE;
+      dec_ctrl_we   = 1'b0;
+
+      case(dec_ctrl_reg)
+        CTRL_IDLE:
+          begin
+            if (next)
+              begin
+                round_ctr_set = 1'b1;
+                ready_new     = 1'b0;
+                ready_we      = 1'b1;
+                dec_ctrl_new  = CTRL_INIT;
+                dec_ctrl_we   = 1'b1;
+              end
+          end
+
+        CTRL_INIT:
+          begin
+            sword_ctr_rst = 1'b1;
+            update_type   = INIT_UPDATE;
+            dec_ctrl_new  = CTRL_SBOX;
+            dec_ctrl_we   = 1'b1;
+          end
+
+        CTRL_SBOX:
+          begin
+            sword_ctr_inc = 1'b1;
+            update_type   = SBOX_UPDATE;
+            if (sword_ctr_reg == 2'h3)
+              begin
+                round_ctr_dec = 1'b1;
+                dec_ctrl_new  = CTRL_MAIN;
+                dec_ctrl_we   = 1'b1;
+              end
+          end
+
+        CTRL_MAIN:
+          begin
+            sword_ctr_rst = 1'b1;
+            if (round_ctr_reg > 0)
+              begin
+                update_type   = MAIN_UPDATE;
+                dec_ctrl_new  = CTRL_SBOX;
+                dec_ctrl_we   = 1'b1;
+              end
+            else
+              begin
+                update_type  = FINAL_UPDATE;
+                ready_new    = 1'b1;
+                ready_we     = 1'b1;
+                dec_ctrl_new = CTRL_IDLE;
+                dec_ctrl_we  = 1'b1;
+              end
+          end
+
+        default:
+          begin
+            // Empty. Just here to make the synthesis tool happy.
+          end
+      endcase // case (dec_ctrl_reg)
+    end // decipher_ctrl
+endmodule // aes_decipher_block
+
+//======================================================================
+// EOF aes_decipher_block.v
+//======================================================================
diff --git a/src/rtl/aes_encipher_block.v b/src/rtl/aes_encipher_block.v
new file mode 100644
index 0000000..094653a
--- /dev/null
+++ b/src/rtl/aes_encipher_block.v
@@ -0,0 +1,486 @@
+//======================================================================
+//
+// aes_encipher_block.v
+// --------------------
+// The AES encipher round. A pure combinational module that implements
+// the initial round, main round and final round logic for
+// enciper operations.
+//
+//
+// Author: Joachim Strombergson
+// 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 aes_encipher_block(
+                          input wire            clk,
+                          input wire            reset_n,
+
+                          input wire            next,
+
+                          input wire            keylen,
+                          output wire [3 : 0]   round,
+                          input wire [127 : 0]  round_key,
+
+                          output wire [31 : 0]  sboxw,
+                          input wire  [31 : 0]  new_sboxw,
+
+                          input wire [127 : 0]  block,
+                          output wire [127 : 0] new_block,
+                          output wire           ready
+                         );
+
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  localparam AES_128_BIT_KEY = 1'h0;
+  localparam AES_256_BIT_KEY = 1'h1;
+
+  localparam AES128_ROUNDS = 4'ha;
+  localparam AES256_ROUNDS = 4'he;
+
+  localparam NO_UPDATE    = 3'h0;
+  localparam INIT_UPDATE  = 3'h1;
+  localparam SBOX_UPDATE  = 3'h2;
+  localparam MAIN_UPDATE  = 3'h3;
+  localparam FINAL_UPDATE = 3'h4;
+
+  localparam CTRL_IDLE  = 3'h0;
+  localparam CTRL_INIT  = 3'h1;
+  localparam CTRL_SBOX  = 3'h2;
+  localparam CTRL_MAIN  = 3'h3;
+  localparam CTRL_FINAL = 3'h4;
+
+
+  //----------------------------------------------------------------
+  // Round functions with sub functions.
+  //----------------------------------------------------------------
+  function [7 : 0] gm2(input [7 : 0] op);
+    begin
+      gm2 = {op[6 : 0], 1'b0} ^ (8'h1b & {8{op[7]}});
+    end
+  endfunction // gm2
+
+  function [7 : 0] gm3(input [7 : 0] op);
+    begin
+      gm3 = gm2(op) ^ op;
+    end
+  endfunction // gm3
+
+  function [31 : 0] mixw(input [31 : 0] w);
+    reg [7 : 0] b0, b1, b2, b3;
+    reg [7 : 0] mb0, mb1, mb2, mb3;
+    begin
+      b0 = w[31 : 24];
+      b1 = w[23 : 16];
+      b2 = w[15 : 08];
+      b3 = w[07 : 00];
+
+      mb0 = gm2(b0) ^ gm3(b1) ^ b2      ^ b3;
+      mb1 = b0      ^ gm2(b1) ^ gm3(b2) ^ b3;
+      mb2 = b0      ^ b1      ^ gm2(b2) ^ gm3(b3);
+      mb3 = gm3(b0) ^ b1      ^ b2      ^ gm2(b3);
+
+      mixw = {mb0, mb1, mb2, mb3};
+    end
+  endfunction // mixw
+
+  function [127 : 0] mixcolumns(input [127 : 0] data);
+    reg [31 : 0] w0, w1, w2, w3;
+    reg [31 : 0] ws0, ws1, ws2, ws3;
+    begin
+      w0 = data[127 : 096];
+      w1 = data[095 : 064];
+      w2 = data[063 : 032];
+      w3 = data[031 : 000];
+
+      ws0 = mixw(w0);
+      ws1 = mixw(w1);
+      ws2 = mixw(w2);
+      ws3 = mixw(w3);
+
+      mixcolumns = {ws0, ws1, ws2, ws3};
+    end
+  endfunction // mixcolumns
+
+  function [127 : 0] shiftrows(input [127 : 0] data);
+    reg [31 : 0] w0, w1, w2, w3;
+    reg [31 : 0] ws0, ws1, ws2, ws3;
+    begin
+      w0 = data[127 : 096];
+      w1 = data[095 : 064];
+      w2 = data[063 : 032];
+      w3 = data[031 : 000];
+
+      ws0 = {w0[31 : 24], w1[23 : 16], w2[15 : 08], w3[07 : 00]};
+      ws1 = {w1[31 : 24], w2[23 : 16], w3[15 : 08], w0[07 : 00]};
+      ws2 = {w2[31 : 24], w3[23 : 16], w0[15 : 08], w1[07 : 00]};
+      ws3 = {w3[31 : 24], w0[23 : 16], w1[15 : 08], w2[07 : 00]};
+
+      shiftrows = {ws0, ws1, ws2, ws3};
+    end
+  endfunction // shiftrows
+
+  function [127 : 0] addroundkey(input [127 : 0] data, input [127 : 0] rkey);
+    begin
+      addroundkey = data ^ rkey;
+    end
+  endfunction // addroundkey
+
+
+  //----------------------------------------------------------------
+  // Registers including update variables and write enable.
+  //----------------------------------------------------------------
+  reg [1 : 0]   sword_ctr_reg;
+  reg [1 : 0]   sword_ctr_new;
+  reg           sword_ctr_we;
+  reg           sword_ctr_inc;
+  reg           sword_ctr_rst;
+
+  reg [3 : 0]   round_ctr_reg;
+  reg [3 : 0]   round_ctr_new;
+  reg           round_ctr_we;
+  reg           round_ctr_rst;
+  reg           round_ctr_inc;
+
+  reg [127 : 0] block_new;
+  reg [31 : 0]  block_w0_reg;
+  reg [31 : 0]  block_w1_reg;
+  reg [31 : 0]  block_w2_reg;
+  reg [31 : 0]  block_w3_reg;
+  reg           block_w0_we;
+  reg           block_w1_we;
+  reg           block_w2_we;
+  reg           block_w3_we;
+
+  reg           ready_reg;
+  reg           ready_new;
+  reg           ready_we;
+
+  reg [2 : 0]   enc_ctrl_reg;
+  reg [2 : 0]   enc_ctrl_new;
+  reg           enc_ctrl_we;
+
+
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  reg [2 : 0]  update_type;
+  reg [31 : 0] muxed_sboxw;
+
+
+  //----------------------------------------------------------------
+  // Concurrent connectivity for ports etc.
+  //----------------------------------------------------------------
+  assign round     = round_ctr_reg;
+  assign sboxw     = muxed_sboxw;
+  assign new_block = {block_w0_reg, block_w1_reg, block_w2_reg, block_w3_reg};
+  assign ready     = ready_reg;
+
+
+  //----------------------------------------------------------------
+  // reg_update
+  //
+  // Update functionality for all registers in the core.
+  // All registers are positive edge triggered with asynchronous
+  // active low reset. All registers have write enable.
+  //----------------------------------------------------------------
+  always @ (posedge clk or negedge reset_n)
+    begin: reg_update
+      if (!reset_n)
+        begin
+          block_w0_reg  <= 32'h0;
+          block_w1_reg  <= 32'h0;
+          block_w2_reg  <= 32'h0;
+          block_w3_reg  <= 32'h0;
+          sword_ctr_reg <= 2'h0;
+          round_ctr_reg <= 4'h0;
+          ready_reg     <= 1'b1;
+          enc_ctrl_reg  <= CTRL_IDLE;
+        end
+      else
+        begin
+          if (block_w0_we)
+            block_w0_reg <= block_new[127 : 096];
+
+          if (block_w1_we)
+            block_w1_reg <= block_new[095 : 064];
+
+          if (block_w2_we)
+            block_w2_reg <= block_new[063 : 032];
+
+          if (block_w3_we)
+            block_w3_reg <= block_new[031 : 000];
+
+          if (sword_ctr_we)
+            sword_ctr_reg <= sword_ctr_new;
+
+          if (round_ctr_we)
+            round_ctr_reg <= round_ctr_new;
+
+          if (ready_we)
+            ready_reg <= ready_new;
+
+          if (enc_ctrl_we)
+            enc_ctrl_reg <= enc_ctrl_new;
+        end
+    end // reg_update
+
+
+  //----------------------------------------------------------------
+  // round_logic
+  //
+  // The logic needed to implement init, main and final rounds.
+  //----------------------------------------------------------------
+  always @*
+    begin : round_logic
+      reg [127 : 0] old_block, shiftrows_block, mixcolumns_block;
+      reg [127 : 0] addkey_init_block, addkey_main_block, addkey_final_block;
+
+      block_new   = 128'h0;
+      muxed_sboxw = 32'h0;
+      block_w0_we = 1'b0;
+      block_w1_we = 1'b0;
+      block_w2_we = 1'b0;
+      block_w3_we = 1'b0;
+
+      old_block          = {block_w0_reg, block_w1_reg, block_w2_reg, block_w3_reg};
+      shiftrows_block    = shiftrows(old_block);
+      mixcolumns_block   = mixcolumns(shiftrows_block);
+      addkey_init_block  = addroundkey(block, round_key);
+      addkey_main_block  = addroundkey(mixcolumns_block, round_key);
+      addkey_final_block = addroundkey(shiftrows_block, round_key);
+
+      case (update_type)
+        INIT_UPDATE:
+          begin
+            block_new    = addkey_init_block;
+            block_w0_we  = 1'b1;
+            block_w1_we  = 1'b1;
+            block_w2_we  = 1'b1;
+            block_w3_we  = 1'b1;
+          end
+
+        SBOX_UPDATE:
+          begin
+            block_new = {new_sboxw, new_sboxw, new_sboxw, new_sboxw};
+
+            case (sword_ctr_reg)
+              2'h0:
+                begin
+                  muxed_sboxw = block_w0_reg;
+                  block_w0_we = 1'b1;
+                end
+
+              2'h1:
+                begin
+                  muxed_sboxw = block_w1_reg;
+                  block_w1_we = 1'b1;
+                end
+
+              2'h2:
+                begin
+                  muxed_sboxw = block_w2_reg;
+                  block_w2_we = 1'b1;
+                end
+
+              2'h3:
+                begin
+                  muxed_sboxw = block_w3_reg;
+                  block_w3_we = 1'b1;
+                end
+            endcase // case (sbox_mux_ctrl_reg)
+          end
+
+        MAIN_UPDATE:
+          begin
+            block_new    = addkey_main_block;
+            block_w0_we  = 1'b1;
+            block_w1_we  = 1'b1;
+            block_w2_we  = 1'b1;
+            block_w3_we  = 1'b1;
+          end
+
+        FINAL_UPDATE:
+          begin
+            block_new    = addkey_final_block;
+            block_w0_we  = 1'b1;
+            block_w1_we  = 1'b1;
+            block_w2_we  = 1'b1;
+            block_w3_we  = 1'b1;
+          end
+
+        default:
+          begin
+          end
+      endcase // case (update_type)
+    end // round_logic
+
+
+  //----------------------------------------------------------------
+  // sword_ctr
+  //
+  // The subbytes word counter with reset and increase logic.
+  //----------------------------------------------------------------
+  always @*
+    begin : sword_ctr
+      sword_ctr_new = 2'h0;
+      sword_ctr_we  = 1'b0;
+
+      if (sword_ctr_rst)
+        begin
+          sword_ctr_new = 2'h0;
+          sword_ctr_we  = 1'b1;
+        end
+      else if (sword_ctr_inc)
+        begin
+          sword_ctr_new = sword_ctr_reg + 1'b1;
+          sword_ctr_we  = 1'b1;
+        end
+    end // sword_ctr
+
+
+  //----------------------------------------------------------------
+  // round_ctr
+  //
+  // The round counter with reset and increase logic.
+  //----------------------------------------------------------------
+  always @*
+    begin : round_ctr
+      round_ctr_new = 4'h0;
+      round_ctr_we  = 1'b0;
+
+      if (round_ctr_rst)
+        begin
+          round_ctr_new = 4'h0;
+          round_ctr_we  = 1'b1;
+        end
+      else if (round_ctr_inc)
+        begin
+          round_ctr_new = round_ctr_reg + 1'b1;
+          round_ctr_we  = 1'b1;
+        end
+    end // round_ctr
+
+
+  //----------------------------------------------------------------
+  // encipher_ctrl
+  //
+  // The FSM that controls the encipher operations.
+  //----------------------------------------------------------------
+  always @*
+    begin: encipher_ctrl
+      reg [3 : 0]  num_rounds;
+
+      if (keylen == AES_256_BIT_KEY)
+        begin
+          num_rounds = AES256_ROUNDS;
+        end
+      else
+        begin
+          num_rounds = AES128_ROUNDS;
+        end
+
+      sword_ctr_inc = 1'b0;
+      sword_ctr_rst = 1'b0;
+      round_ctr_inc = 1'b0;
+      round_ctr_rst = 1'b0;
+      ready_new     = 1'b0;
+      ready_we      = 1'b0;
+      update_type   = NO_UPDATE;
+      enc_ctrl_new  = CTRL_IDLE;
+      enc_ctrl_we   = 1'b0;
+
+      case(enc_ctrl_reg)
+        CTRL_IDLE:
+          begin
+            if (next)
+              begin
+                round_ctr_rst = 1'b1;
+                ready_new     = 1'b0;
+                ready_we      = 1'b1;
+                enc_ctrl_new  = CTRL_INIT;
+                enc_ctrl_we   = 1'b1;
+              end
+          end
+
+        CTRL_INIT:
+          begin
+            round_ctr_inc = 1'b1;
+            sword_ctr_rst = 1'b1;
+            update_type   = INIT_UPDATE;
+            enc_ctrl_new  = CTRL_SBOX;
+            enc_ctrl_we   = 1'b1;
+          end
+
+        CTRL_SBOX:
+          begin
+            sword_ctr_inc = 1'b1;
+            update_type   = SBOX_UPDATE;
+            if (sword_ctr_reg == 2'h3)
+              begin
+                enc_ctrl_new  = CTRL_MAIN;
+                enc_ctrl_we   = 1'b1;
+              end
+          end
+
+        CTRL_MAIN:
+          begin
+            sword_ctr_rst = 1'b1;
+            round_ctr_inc = 1'b1;
+            if (round_ctr_reg < num_rounds)
+              begin
+                update_type   = MAIN_UPDATE;
+                enc_ctrl_new  = CTRL_SBOX;
+                enc_ctrl_we   = 1'b1;
+              end
+            else
+              begin
+                update_type  = FINAL_UPDATE;
+                ready_new    = 1'b1;
+                ready_we     = 1'b1;
+                enc_ctrl_new = CTRL_IDLE;
+                enc_ctrl_we  = 1'b1;
+              end
+          end
+
+        default:
+          begin
+            // Empty. Just here to make the synthesis tool happy.
+          end
+      endcase // case (enc_ctrl_reg)
+    end // encipher_ctrl
+
+endmodule // aes_encipher_block
+
+//======================================================================
+// EOF aes_encipher_block.v
+//======================================================================
diff --git a/src/rtl/aes_inv_sbox.v b/src/rtl/aes_inv_sbox.v
new file mode 100644
index 0000000..807f397
--- /dev/null
+++ b/src/rtl/aes_inv_sbox.v
@@ -0,0 +1,325 @@
+//======================================================================
+//
+// aes_inv_sbox.v
+// --------------
+// The inverse AES S-box. Basically a 256 Byte ROM.
+//
+//
+// Author: Joachim Strombergson
+// 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 aes_inv_sbox(
+                    input wire  [31 : 0] sword,
+                    output wire [31 : 0] new_sword
+                   );
+
+
+  //----------------------------------------------------------------
+  // The inverse sbox array.
+  //----------------------------------------------------------------
+  wire [7 : 0] inv_sbox [0 : 255];
+
+
+  //----------------------------------------------------------------
+  // Four parallel muxes.
+  //----------------------------------------------------------------
+  assign new_sword[31 : 24] = inv_sbox[sword[31 : 24]];
+  assign new_sword[23 : 16] = inv_sbox[sword[23 : 16]];
+  assign new_sword[15 : 08] = inv_sbox[sword[15 : 08]];
+  assign new_sword[07 : 00] = inv_sbox[sword[07 : 00]];
+
+
+  //----------------------------------------------------------------
+  // Creating the contents of the array.
+  //----------------------------------------------------------------
+  assign inv_sbox[8'h00] = 8'h52;
+  assign inv_sbox[8'h01] = 8'h09;
+  assign inv_sbox[8'h02] = 8'h6a;
+  assign inv_sbox[8'h03] = 8'hd5;
+  assign inv_sbox[8'h04] = 8'h30;
+  assign inv_sbox[8'h05] = 8'h36;
+  assign inv_sbox[8'h06] = 8'ha5;
+  assign inv_sbox[8'h07] = 8'h38;
+  assign inv_sbox[8'h08] = 8'hbf;
+  assign inv_sbox[8'h09] = 8'h40;
+  assign inv_sbox[8'h0a] = 8'ha3;
+  assign inv_sbox[8'h0b] = 8'h9e;
+  assign inv_sbox[8'h0c] = 8'h81;
+  assign inv_sbox[8'h0d] = 8'hf3;
+  assign inv_sbox[8'h0e] = 8'hd7;
+  assign inv_sbox[8'h0f] = 8'hfb;
+  assign inv_sbox[8'h10] = 8'h7c;
+  assign inv_sbox[8'h11] = 8'he3;
+  assign inv_sbox[8'h12] = 8'h39;
+  assign inv_sbox[8'h13] = 8'h82;
+  assign inv_sbox[8'h14] = 8'h9b;
+  assign inv_sbox[8'h15] = 8'h2f;
+  assign inv_sbox[8'h16] = 8'hff;
+  assign inv_sbox[8'h17] = 8'h87;
+  assign inv_sbox[8'h18] = 8'h34;
+  assign inv_sbox[8'h19] = 8'h8e;
+  assign inv_sbox[8'h1a] = 8'h43;
+  assign inv_sbox[8'h1b] = 8'h44;
+  assign inv_sbox[8'h1c] = 8'hc4;
+  assign inv_sbox[8'h1d] = 8'hde;
+  assign inv_sbox[8'h1e] = 8'he9;
+  assign inv_sbox[8'h1f] = 8'hcb;
+  assign inv_sbox[8'h20] = 8'h54;
+  assign inv_sbox[8'h21] = 8'h7b;
+  assign inv_sbox[8'h22] = 8'h94;
+  assign inv_sbox[8'h23] = 8'h32;
+  assign inv_sbox[8'h24] = 8'ha6;
+  assign inv_sbox[8'h25] = 8'hc2;
+  assign inv_sbox[8'h26] = 8'h23;
+  assign inv_sbox[8'h27] = 8'h3d;
+  assign inv_sbox[8'h28] = 8'hee;
+  assign inv_sbox[8'h29] = 8'h4c;
+  assign inv_sbox[8'h2a] = 8'h95;
+  assign inv_sbox[8'h2b] = 8'h0b;
+  assign inv_sbox[8'h2c] = 8'h42;
+  assign inv_sbox[8'h2d] = 8'hfa;
+  assign inv_sbox[8'h2e] = 8'hc3;
+  assign inv_sbox[8'h2f] = 8'h4e;
+  assign inv_sbox[8'h30] = 8'h08;
+  assign inv_sbox[8'h31] = 8'h2e;
+  assign inv_sbox[8'h32] = 8'ha1;
+  assign inv_sbox[8'h33] = 8'h66;
+  assign inv_sbox[8'h34] = 8'h28;
+  assign inv_sbox[8'h35] = 8'hd9;
+  assign inv_sbox[8'h36] = 8'h24;
+  assign inv_sbox[8'h37] = 8'hb2;
+  assign inv_sbox[8'h38] = 8'h76;
+  assign inv_sbox[8'h39] = 8'h5b;
+  assign inv_sbox[8'h3a] = 8'ha2;
+  assign inv_sbox[8'h3b] = 8'h49;
+  assign inv_sbox[8'h3c] = 8'h6d;
+  assign inv_sbox[8'h3d] = 8'h8b;
+  assign inv_sbox[8'h3e] = 8'hd1;
+  assign inv_sbox[8'h3f] = 8'h25;
+  assign inv_sbox[8'h40] = 8'h72;
+  assign inv_sbox[8'h41] = 8'hf8;
+  assign inv_sbox[8'h42] = 8'hf6;
+  assign inv_sbox[8'h43] = 8'h64;
+  assign inv_sbox[8'h44] = 8'h86;
+  assign inv_sbox[8'h45] = 8'h68;
+  assign inv_sbox[8'h46] = 8'h98;
+  assign inv_sbox[8'h47] = 8'h16;
+  assign inv_sbox[8'h48] = 8'hd4;
+  assign inv_sbox[8'h49] = 8'ha4;
+  assign inv_sbox[8'h4a] = 8'h5c;
+  assign inv_sbox[8'h4b] = 8'hcc;
+  assign inv_sbox[8'h4c] = 8'h5d;
+  assign inv_sbox[8'h4d] = 8'h65;
+  assign inv_sbox[8'h4e] = 8'hb6;
+  assign inv_sbox[8'h4f] = 8'h92;
+  assign inv_sbox[8'h50] = 8'h6c;
+  assign inv_sbox[8'h51] = 8'h70;
+  assign inv_sbox[8'h52] = 8'h48;
+  assign inv_sbox[8'h53] = 8'h50;
+  assign inv_sbox[8'h54] = 8'hfd;
+  assign inv_sbox[8'h55] = 8'hed;
+  assign inv_sbox[8'h56] = 8'hb9;
+  assign inv_sbox[8'h57] = 8'hda;
+  assign inv_sbox[8'h58] = 8'h5e;
+  assign inv_sbox[8'h59] = 8'h15;
+  assign inv_sbox[8'h5a] = 8'h46;
+  assign inv_sbox[8'h5b] = 8'h57;
+  assign inv_sbox[8'h5c] = 8'ha7;
+  assign inv_sbox[8'h5d] = 8'h8d;
+  assign inv_sbox[8'h5e] = 8'h9d;
+  assign inv_sbox[8'h5f] = 8'h84;
+  assign inv_sbox[8'h60] = 8'h90;
+  assign inv_sbox[8'h61] = 8'hd8;
+  assign inv_sbox[8'h62] = 8'hab;
+  assign inv_sbox[8'h63] = 8'h00;
+  assign inv_sbox[8'h64] = 8'h8c;
+  assign inv_sbox[8'h65] = 8'hbc;
+  assign inv_sbox[8'h66] = 8'hd3;
+  assign inv_sbox[8'h67] = 8'h0a;
+  assign inv_sbox[8'h68] = 8'hf7;
+  assign inv_sbox[8'h69] = 8'he4;
+  assign inv_sbox[8'h6a] = 8'h58;
+  assign inv_sbox[8'h6b] = 8'h05;
+  assign inv_sbox[8'h6c] = 8'hb8;
+  assign inv_sbox[8'h6d] = 8'hb3;
+  assign inv_sbox[8'h6e] = 8'h45;
+  assign inv_sbox[8'h6f] = 8'h06;
+  assign inv_sbox[8'h70] = 8'hd0;
+  assign inv_sbox[8'h71] = 8'h2c;
+  assign inv_sbox[8'h72] = 8'h1e;
+  assign inv_sbox[8'h73] = 8'h8f;
+  assign inv_sbox[8'h74] = 8'hca;
+  assign inv_sbox[8'h75] = 8'h3f;
+  assign inv_sbox[8'h76] = 8'h0f;
+  assign inv_sbox[8'h77] = 8'h02;
+  assign inv_sbox[8'h78] = 8'hc1;
+  assign inv_sbox[8'h79] = 8'haf;
+  assign inv_sbox[8'h7a] = 8'hbd;
+  assign inv_sbox[8'h7b] = 8'h03;
+  assign inv_sbox[8'h7c] = 8'h01;
+  assign inv_sbox[8'h7d] = 8'h13;
+  assign inv_sbox[8'h7e] = 8'h8a;
+  assign inv_sbox[8'h7f] = 8'h6b;
+  assign inv_sbox[8'h80] = 8'h3a;
+  assign inv_sbox[8'h81] = 8'h91;
+  assign inv_sbox[8'h82] = 8'h11;
+  assign inv_sbox[8'h83] = 8'h41;
+  assign inv_sbox[8'h84] = 8'h4f;
+  assign inv_sbox[8'h85] = 8'h67;
+  assign inv_sbox[8'h86] = 8'hdc;
+  assign inv_sbox[8'h87] = 8'hea;
+  assign inv_sbox[8'h88] = 8'h97;
+  assign inv_sbox[8'h89] = 8'hf2;
+  assign inv_sbox[8'h8a] = 8'hcf;
+  assign inv_sbox[8'h8b] = 8'hce;
+  assign inv_sbox[8'h8c] = 8'hf0;
+  assign inv_sbox[8'h8d] = 8'hb4;
+  assign inv_sbox[8'h8e] = 8'he6;
+  assign inv_sbox[8'h8f] = 8'h73;
+  assign inv_sbox[8'h90] = 8'h96;
+  assign inv_sbox[8'h91] = 8'hac;
+  assign inv_sbox[8'h92] = 8'h74;
+  assign inv_sbox[8'h93] = 8'h22;
+  assign inv_sbox[8'h94] = 8'he7;
+  assign inv_sbox[8'h95] = 8'had;
+  assign inv_sbox[8'h96] = 8'h35;
+  assign inv_sbox[8'h97] = 8'h85;
+  assign inv_sbox[8'h98] = 8'he2;
+  assign inv_sbox[8'h99] = 8'hf9;
+  assign inv_sbox[8'h9a] = 8'h37;
+  assign inv_sbox[8'h9b] = 8'he8;
+  assign inv_sbox[8'h9c] = 8'h1c;
+  assign inv_sbox[8'h9d] = 8'h75;
+  assign inv_sbox[8'h9e] = 8'hdf;
+  assign inv_sbox[8'h9f] = 8'h6e;
+  assign inv_sbox[8'ha0] = 8'h47;
+  assign inv_sbox[8'ha1] = 8'hf1;
+  assign inv_sbox[8'ha2] = 8'h1a;
+  assign inv_sbox[8'ha3] = 8'h71;
+  assign inv_sbox[8'ha4] = 8'h1d;
+  assign inv_sbox[8'ha5] = 8'h29;
+  assign inv_sbox[8'ha6] = 8'hc5;
+  assign inv_sbox[8'ha7] = 8'h89;
+  assign inv_sbox[8'ha8] = 8'h6f;
+  assign inv_sbox[8'ha9] = 8'hb7;
+  assign inv_sbox[8'haa] = 8'h62;
+  assign inv_sbox[8'hab] = 8'h0e;
+  assign inv_sbox[8'hac] = 8'haa;
+  assign inv_sbox[8'had] = 8'h18;
+  assign inv_sbox[8'hae] = 8'hbe;
+  assign inv_sbox[8'haf] = 8'h1b;
+  assign inv_sbox[8'hb0] = 8'hfc;
+  assign inv_sbox[8'hb1] = 8'h56;
+  assign inv_sbox[8'hb2] = 8'h3e;
+  assign inv_sbox[8'hb3] = 8'h4b;
+  assign inv_sbox[8'hb4] = 8'hc6;
+  assign inv_sbox[8'hb5] = 8'hd2;
+  assign inv_sbox[8'hb6] = 8'h79;
+  assign inv_sbox[8'hb7] = 8'h20;
+  assign inv_sbox[8'hb8] = 8'h9a;
+  assign inv_sbox[8'hb9] = 8'hdb;
+  assign inv_sbox[8'hba] = 8'hc0;
+  assign inv_sbox[8'hbb] = 8'hfe;
+  assign inv_sbox[8'hbc] = 8'h78;
+  assign inv_sbox[8'hbd] = 8'hcd;
+  assign inv_sbox[8'hbe] = 8'h5a;
+  assign inv_sbox[8'hbf] = 8'hf4;
+  assign inv_sbox[8'hc0] = 8'h1f;
+  assign inv_sbox[8'hc1] = 8'hdd;
+  assign inv_sbox[8'hc2] = 8'ha8;
+  assign inv_sbox[8'hc3] = 8'h33;
+  assign inv_sbox[8'hc4] = 8'h88;
+  assign inv_sbox[8'hc5] = 8'h07;
+  assign inv_sbox[8'hc6] = 8'hc7;
+  assign inv_sbox[8'hc7] = 8'h31;
+  assign inv_sbox[8'hc8] = 8'hb1;
+  assign inv_sbox[8'hc9] = 8'h12;
+  assign inv_sbox[8'hca] = 8'h10;
+  assign inv_sbox[8'hcb] = 8'h59;
+  assign inv_sbox[8'hcc] = 8'h27;
+  assign inv_sbox[8'hcd] = 8'h80;
+  assign inv_sbox[8'hce] = 8'hec;
+  assign inv_sbox[8'hcf] = 8'h5f;
+  assign inv_sbox[8'hd0] = 8'h60;
+  assign inv_sbox[8'hd1] = 8'h51;
+  assign inv_sbox[8'hd2] = 8'h7f;
+  assign inv_sbox[8'hd3] = 8'ha9;
+  assign inv_sbox[8'hd4] = 8'h19;
+  assign inv_sbox[8'hd5] = 8'hb5;
+  assign inv_sbox[8'hd6] = 8'h4a;
+  assign inv_sbox[8'hd7] = 8'h0d;
+  assign inv_sbox[8'hd8] = 8'h2d;
+  assign inv_sbox[8'hd9] = 8'he5;
+  assign inv_sbox[8'hda] = 8'h7a;
+  assign inv_sbox[8'hdb] = 8'h9f;
+  assign inv_sbox[8'hdc] = 8'h93;
+  assign inv_sbox[8'hdd] = 8'hc9;
+  assign inv_sbox[8'hde] = 8'h9c;
+  assign inv_sbox[8'hdf] = 8'hef;
+  assign inv_sbox[8'he0] = 8'ha0;
+  assign inv_sbox[8'he1] = 8'he0;
+  assign inv_sbox[8'he2] = 8'h3b;
+  assign inv_sbox[8'he3] = 8'h4d;
+  assign inv_sbox[8'he4] = 8'hae;
+  assign inv_sbox[8'he5] = 8'h2a;
+  assign inv_sbox[8'he6] = 8'hf5;
+  assign inv_sbox[8'he7] = 8'hb0;
+  assign inv_sbox[8'he8] = 8'hc8;
+  assign inv_sbox[8'he9] = 8'heb;
+  assign inv_sbox[8'hea] = 8'hbb;
+  assign inv_sbox[8'heb] = 8'h3c;
+  assign inv_sbox[8'hec] = 8'h83;
+  assign inv_sbox[8'hed] = 8'h53;
+  assign inv_sbox[8'hee] = 8'h99;
+  assign inv_sbox[8'hef] = 8'h61;
+  assign inv_sbox[8'hf0] = 8'h17;
+  assign inv_sbox[8'hf1] = 8'h2b;
+  assign inv_sbox[8'hf2] = 8'h04;
+  assign inv_sbox[8'hf3] = 8'h7e;
+  assign inv_sbox[8'hf4] = 8'hba;
+  assign inv_sbox[8'hf5] = 8'h77;
+  assign inv_sbox[8'hf6] = 8'hd6;
+  assign inv_sbox[8'hf7] = 8'h26;
+  assign inv_sbox[8'hf8] = 8'he1;
+  assign inv_sbox[8'hf9] = 8'h69;
+  assign inv_sbox[8'hfa] = 8'h14;
+  assign inv_sbox[8'hfb] = 8'h63;
+  assign inv_sbox[8'hfc] = 8'h55;
+  assign inv_sbox[8'hfd] = 8'h21;
+  assign inv_sbox[8'hfe] = 8'h0c;
+  assign inv_sbox[8'hff] = 8'h7d;
+
+endmodule // aes_inv_sbox
+
+//======================================================================
+// EOF aes_inv_sbox.v
+//======================================================================
diff --git a/src/rtl/aes_key_mem.v b/src/rtl/aes_key_mem.v
new file mode 100644
index 0000000..496fc08
--- /dev/null
+++ b/src/rtl/aes_key_mem.v
@@ -0,0 +1,454 @@
+//======================================================================
+//
+// aes_key_mem.v
+// -------------
+// The AES key memort including round key generator.
+//
+//
+// Author: Joachim Strombergson
+// 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 aes_key_mem(
+                   input wire            clk,
+                   input wire            reset_n,
+
+                   input wire [255 : 0]  key,
+                   input wire            keylen,
+                   input wire            init,
+
+                   input wire    [3 : 0] round,
+                   output wire [127 : 0] round_key,
+                   output wire           ready,
+
+
+                   output wire [31 : 0]  sboxw,
+                   input wire  [31 : 0]  new_sboxw
+                  );
+
+
+  //----------------------------------------------------------------
+  // Parameters.
+  //----------------------------------------------------------------
+  localparam AES_128_BIT_KEY = 1'h0;
+  localparam AES_256_BIT_KEY = 1'h1;
+
+  localparam AES_128_NUM_ROUNDS = 4'ha;
+  localparam AES_256_NUM_ROUNDS = 4'he;
+
+  localparam CTRL_IDLE     = 3'h0;
+  localparam CTRL_INIT     = 3'h1;
+  localparam CTRL_GENERATE = 3'h2;
+  localparam CTRL_DONE     = 3'h3;
+
+
+  //----------------------------------------------------------------
+  // Registers.
+  //----------------------------------------------------------------
+  reg [127 : 0] key_mem [0 : 14];
+  reg [127 : 0] key_mem_new;
+  reg           key_mem_we;
+
+  reg [127 : 0] prev_key0_reg;
+  reg [127 : 0] prev_key0_new;
+  reg           prev_key0_we;
+
+  reg [127 : 0] prev_key1_reg;
+  reg [127 : 0] prev_key1_new;
+  reg           prev_key1_we;
+
+  reg [3 : 0] round_ctr_reg;
+  reg [3 : 0] round_ctr_new;
+  reg         round_ctr_rst;
+  reg         round_ctr_inc;
+  reg         round_ctr_we;
+
+  reg [2 : 0] key_mem_ctrl_reg;
+  reg [2 : 0] key_mem_ctrl_new;
+  reg         key_mem_ctrl_we;
+
+  reg         ready_reg;
+  reg         ready_new;
+  reg         ready_we;
+
+  reg [7 : 0] rcon_reg;
+  reg [7 : 0] rcon_new;
+  reg         rcon_we;
+  reg         rcon_set;
+  reg         rcon_next;
+
+
+  //----------------------------------------------------------------
+  // Wires.
+  //----------------------------------------------------------------
+  reg [31 : 0] tmp_sboxw;
+
+  reg           round_key_update;
+  reg [3 : 0]   num_rounds;
+
+  reg [127 : 0] tmp_round_key;
+
+
+  //----------------------------------------------------------------
+  // Concurrent assignments for ports.
+  //----------------------------------------------------------------
+  assign round_key  = tmp_round_key;
+  assign ready      = ready_reg;
+  assign sboxw      = tmp_sboxw;
+
+
+  //----------------------------------------------------------------
+  // reg_update
+  //
+  // Update functionality for all registers in the core.
+  // All registers are positive edge triggered with asynchronous
+  // active low reset. All registers have write enable.
+  //----------------------------------------------------------------
+  always @ (posedge clk or negedge reset_n)
+    begin: reg_update
+      integer i;
+
+      if (!reset_n)
+        begin
+          for (i = 0 ; i < 4 ; i = i + 1)
+            key_mem [i] <= 128'h0;
+
+          rcon_reg         <= 8'h0;
+          ready_reg        <= 1'b0;
+          round_ctr_reg    <= 4'h0;
+          key_mem_ctrl_reg <= CTRL_IDLE;
+        end
+      else
+        begin
+          if (round_ctr_we)
+            round_ctr_reg <= round_ctr_new;
+
+          if (ready_we)
+            ready_reg <= ready_new;
+
+          if (rcon_we)
+            rcon_reg <= rcon_new;
+
+          if (key_mem_we)
+            key_mem[round_ctr_reg] <= key_mem_new;
+
+          if (prev_key0_we)
+            prev_key0_reg <= prev_key0_new;
+
+          if (prev_key1_we)
+            prev_key1_reg <= prev_key1_new;
+
+          if (key_mem_ctrl_we)
+            key_mem_ctrl_reg <= key_mem_ctrl_new;
+        end
+    end // reg_update
+
+
+  //----------------------------------------------------------------
+  // key_mem_read
+  //
+  // Combinational read port for the key memory.
+  //----------------------------------------------------------------
+  always @*
+    begin : key_mem_read
+      tmp_round_key = key_mem[round];
+    end // key_mem_read
+
+
+  //----------------------------------------------------------------
+  // round_key_gen
+  //
+  // The round key generator logic for AES-128 and AES-256.
+  //----------------------------------------------------------------
+  always @*
+    begin: round_key_gen
+      reg [31 : 0] w0, w1, w2, w3, w4, w5, w6, w7;
+      reg [31 : 0] k0, k1, k2, k3;
+      reg [31 : 0] rconw, rotstw, tw, trw;
+
+      // Default assignments.
+      key_mem_new   = 128'h0;
+      key_mem_we    = 1'b0;
+      prev_key0_new = 128'h0;
+      prev_key0_we  = 1'b0;
+      prev_key1_new = 128'h0;
+      prev_key1_we  = 1'b0;
+
+      k0 = 32'h0;
+      k1 = 32'h0;
+      k2 = 32'h0;
+      k3 = 32'h0;
+
+      rcon_set   = 1'b1;
+      rcon_next  = 1'b0;
+
+      // Extract words and calculate intermediate values.
+      // Perform rotation of sbox word etc.
+      w0 = prev_key0_reg[127 : 096];
+      w1 = prev_key0_reg[095 : 064];
+      w2 = prev_key0_reg[063 : 032];
+      w3 = prev_key0_reg[031 : 000];
+
+      w4 = prev_key1_reg[127 : 096];
+      w5 = prev_key1_reg[095 : 064];
+      w6 = prev_key1_reg[063 : 032];
+      w7 = prev_key1_reg[031 : 000];
+
+      rconw = {rcon_reg, 24'h0};
+      tmp_sboxw = w7;
+      rotstw = {new_sboxw[23 : 00], new_sboxw[31 : 24]};
+      trw = rotstw ^ rconw;
+      tw = new_sboxw;
+
+      // Generate the specific round keys.
+      if (round_key_update)
+        begin
+          rcon_set   = 1'b0;
+          key_mem_we = 1'b1;
+          case (keylen)
+            AES_128_BIT_KEY:
+              begin
+                if (round_ctr_reg == 0)
+                  begin
+                    key_mem_new   = key[255 : 128];
+                    prev_key1_new = key[255 : 128];
+                    prev_key1_we  = 1'b1;
+                    rcon_next     = 1'b1;
+                  end
+                else
+                  begin
+                    k0 = w4 ^ trw;
+                    k1 = w5 ^ w4 ^ trw;
+                    k2 = w6 ^ w5 ^ w4 ^ trw;
+                    k3 = w7 ^ w6 ^ w5 ^ w4 ^ trw;
+
+                    key_mem_new   = {k0, k1, k2, k3};
+                    prev_key1_new = {k0, k1, k2, k3};
+                    prev_key1_we  = 1'b1;
+                    rcon_next     = 1'b1;
+                  end
+              end
+
+            AES_256_BIT_KEY:
+              begin
+                if (round_ctr_reg == 0)
+                  begin
+                    key_mem_new   = key[255 : 128];
+                    prev_key0_new = key[255 : 128];
+                    prev_key0_we  = 1'b1;
+                  end
+                else if (round_ctr_reg == 1)
+                  begin
+                    key_mem_new   = key[127 : 0];
+                    prev_key1_new = key[127 : 0];
+                    prev_key1_we  = 1'b1;
+                    rcon_next     = 1'b1;
+                  end
+                else
+                  begin
+                    if (round_ctr_reg[0] == 0)
+                      begin
+                        k0 = w0 ^ trw;
+                        k1 = w1 ^ w0 ^ trw;
+                        k2 = w2 ^ w1 ^ w0 ^ trw;
+                        k3 = w3 ^ w2 ^ w1 ^ w0 ^ trw;
+                      end
+                    else
+                      begin
+                        k0 = w0 ^ tw;
+                        k1 = w1 ^ w0 ^ tw;
+                        k2 = w2 ^ w1 ^ w0 ^ tw;
+                        k3 = w3 ^ w2 ^ w1 ^ w0 ^ tw;
+                        rcon_next = 1'b1;
+                      end
+
+                    // Store the generated round keys.
+                    key_mem_new   = {k0, k1, k2, k3};
+                    prev_key1_new = {k0, k1, k2, k3};
+                    prev_key1_we  = 1'b1;
+                    prev_key0_new = prev_key1_reg;
+                    prev_key0_we  = 1'b1;
+                  end
+              end
+
+            default:
+              begin
+              end
+          endcase // case (keylen)
+        end
+    end // round_key_gen
+
+
+  //----------------------------------------------------------------
+  // rcon_logic
+  //
+  // Caclulates the rcon value for the different key expansion
+  // iterations.
+  //----------------------------------------------------------------
+  always @*
+    begin : rcon_logic
+      reg [7 : 0] tmp_rcon;
+      rcon_new = 8'h00;
+      rcon_we  = 1'b0;
+
+      tmp_rcon = {rcon_reg[6 : 0], 1'b0} ^ (8'h1b & {8{rcon_reg[7]}});
+
+      if (rcon_set)
+        begin
+          rcon_new = 8'h8d;
+          rcon_we  = 1'b1;
+        end
+
+      if (rcon_next)
+        begin
+          rcon_new = tmp_rcon[7 : 0];
+          rcon_we  = 1'b1;
+        end
+    end
+
+
+  //----------------------------------------------------------------
+  // round_ctr
+  //
+  // The round counter logic with increase and reset.
+  //----------------------------------------------------------------
+  always @*
+    begin : round_ctr
+      round_ctr_new = 4'h0;
+      round_ctr_we  = 1'b0;
+
+      if (round_ctr_rst)
+        begin
+          round_ctr_new = 4'h0;
+          round_ctr_we  = 1'b1;
+        end
+
+      else if (round_ctr_inc)
+        begin
+          round_ctr_new = round_ctr_reg + 1'b1;
+          round_ctr_we  = 1'b1;
+        end
+    end
+
+
+  //----------------------------------------------------------------
+  // num_rounds_logic
+  //
+  // Logic to select the number of rounds to generate keys for
+  //----------------------------------------------------------------
+  always @*
+    begin : num_rounds_logic
+      num_rounds = 4'h0;
+
+      case (keylen)
+        AES_128_BIT_KEY:
+          begin
+            num_rounds = AES_128_NUM_ROUNDS;
+          end
+
+        AES_256_BIT_KEY:
+          begin
+            num_rounds = AES_256_NUM_ROUNDS;
+          end
+
+        default:
+          begin
+          end
+      endcase // case (keylen)
+    end
+
+
+  //----------------------------------------------------------------
+  // key_mem_ctrl
+  //
+  //
+  // The FSM that controls the round key generation.
+  //----------------------------------------------------------------
+  always @*
+    begin: key_mem_ctrl
+      // Default assignments.
+      ready_new        = 1'b0;
+      ready_we         = 1'b0;
+      round_key_update = 1'b0;
+      round_ctr_rst    = 1'b0;
+      round_ctr_inc    = 1'b0;
+      key_mem_ctrl_new = CTRL_IDLE;
+      key_mem_ctrl_we  = 1'b0;
+
+      case(key_mem_ctrl_reg)
+        CTRL_IDLE:
+          begin
+            if (init)
+              begin
+                ready_new        = 1'b0;
+                ready_we         = 1'b1;
+                key_mem_ctrl_new = CTRL_INIT;
+                key_mem_ctrl_we  = 1'b1;
+              end
+          end
+
+        CTRL_INIT:
+          begin
+            round_ctr_rst    = 1'b1;
+            key_mem_ctrl_new = CTRL_GENERATE;
+            key_mem_ctrl_we  = 1'b1;
+          end
+
+        CTRL_GENERATE:
+          begin
+            round_ctr_inc    = 1'b1;
+            round_key_update = 1'b1;
+            if (round_ctr_reg == num_rounds)
+              begin
+                key_mem_ctrl_new = CTRL_DONE;
+                key_mem_ctrl_we  = 1'b1;
+              end
+          end
+
+        CTRL_DONE:
+          begin
+            ready_new        = 1'b1;
+            ready_we         = 1'b1;
+            key_mem_ctrl_new = CTRL_IDLE;
+            key_mem_ctrl_we  = 1'b1;
+          end
+
+        default:
+          begin
+          end
+      endcase // case (key_mem_ctrl_reg)
+
+    end // key_mem_ctrl
+endmodule // aes_key_mem
+
+//======================================================================
+// EOF aes_key_mem.v
+//======================================================================
diff --git a/src/rtl/aes_sbox.v b/src/rtl/aes_sbox.v
new file mode 100644
index 0000000..09ba129
--- /dev/null
+++ b/src/rtl/aes_sbox.v
@@ -0,0 +1,325 @@
+//======================================================================
+//
+// aes_sbox.v
+// ----------
+// The AES S-box. Basically a 256 Byte ROM. This implementation
+// contains four parallel S-boxes to handle a 32 bit word.
+//
+//
+// Copyright (c) 2013 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 aes_sbox(
+                input wire [31 : 0]  sboxw,
+                output wire [31 : 0] new_sboxw
+               );
+
+
+  //----------------------------------------------------------------
+  // The sbox array.
+  //----------------------------------------------------------------
+  wire [7 : 0] sbox [0 : 255];
+
+
+  //----------------------------------------------------------------
+  // Four parallel muxes.
+  //----------------------------------------------------------------
+  assign new_sboxw[31 : 24] = sbox[sboxw[31 : 24]];
+  assign new_sboxw[23 : 16] = sbox[sboxw[23 : 16]];
+  assign new_sboxw[15 : 08] = sbox[sboxw[15 : 08]];
+  assign new_sboxw[07 : 00] = sbox[sboxw[07 : 00]];
+
+
+  //----------------------------------------------------------------
+  // Creating the sbox array contents.
+  //----------------------------------------------------------------
+  assign sbox[8'h00] = 8'h63;
+  assign sbox[8'h01] = 8'h7c;
+  assign sbox[8'h02] = 8'h77;
+  assign sbox[8'h03] = 8'h7b;
+  assign sbox[8'h04] = 8'hf2;
+  assign sbox[8'h05] = 8'h6b;
+  assign sbox[8'h06] = 8'h6f;
+  assign sbox[8'h07] = 8'hc5;
+  assign sbox[8'h08] = 8'h30;
+  assign sbox[8'h09] = 8'h01;
+  assign sbox[8'h0a] = 8'h67;
+  assign sbox[8'h0b] = 8'h2b;
+  assign sbox[8'h0c] = 8'hfe;
+  assign sbox[8'h0d] = 8'hd7;
+  assign sbox[8'h0e] = 8'hab;
+  assign sbox[8'h0f] = 8'h76;
+  assign sbox[8'h10] = 8'hca;
+  assign sbox[8'h11] = 8'h82;
+  assign sbox[8'h12] = 8'hc9;
+  assign sbox[8'h13] = 8'h7d;
+  assign sbox[8'h14] = 8'hfa;
+  assign sbox[8'h15] = 8'h59;
+  assign sbox[8'h16] = 8'h47;
+  assign sbox[8'h17] = 8'hf0;
+  assign sbox[8'h18] = 8'had;
+  assign sbox[8'h19] = 8'hd4;
+  assign sbox[8'h1a] = 8'ha2;
+  assign sbox[8'h1b] = 8'haf;
+  assign sbox[8'h1c] = 8'h9c;
+  assign sbox[8'h1d] = 8'ha4;
+  assign sbox[8'h1e] = 8'h72;
+  assign sbox[8'h1f] = 8'hc0;
+  assign sbox[8'h20] = 8'hb7;
+  assign sbox[8'h21] = 8'hfd;
+  assign sbox[8'h22] = 8'h93;
+  assign sbox[8'h23] = 8'h26;
+  assign sbox[8'h24] = 8'h36;
+  assign sbox[8'h25] = 8'h3f;
+  assign sbox[8'h26] = 8'hf7;
+  assign sbox[8'h27] = 8'hcc;
+  assign sbox[8'h28] = 8'h34;
+  assign sbox[8'h29] = 8'ha5;
+  assign sbox[8'h2a] = 8'he5;
+  assign sbox[8'h2b] = 8'hf1;
+  assign sbox[8'h2c] = 8'h71;
+  assign sbox[8'h2d] = 8'hd8;
+  assign sbox[8'h2e] = 8'h31;
+  assign sbox[8'h2f] = 8'h15;
+  assign sbox[8'h30] = 8'h04;
+  assign sbox[8'h31] = 8'hc7;
+  assign sbox[8'h32] = 8'h23;
+  assign sbox[8'h33] = 8'hc3;
+  assign sbox[8'h34] = 8'h18;
+  assign sbox[8'h35] = 8'h96;
+  assign sbox[8'h36] = 8'h05;
+  assign sbox[8'h37] = 8'h9a;
+  assign sbox[8'h38] = 8'h07;
+  assign sbox[8'h39] = 8'h12;
+  assign sbox[8'h3a] = 8'h80;
+  assign sbox[8'h3b] = 8'he2;
+  assign sbox[8'h3c] = 8'heb;
+  assign sbox[8'h3d] = 8'h27;
+  assign sbox[8'h3e] = 8'hb2;
+  assign sbox[8'h3f] = 8'h75;
+  assign sbox[8'h40] = 8'h09;
+  assign sbox[8'h41] = 8'h83;
+  assign sbox[8'h42] = 8'h2c;
+  assign sbox[8'h43] = 8'h1a;
+  assign sbox[8'h44] = 8'h1b;
+  assign sbox[8'h45] = 8'h6e;
+  assign sbox[8'h46] = 8'h5a;
+  assign sbox[8'h47] = 8'ha0;
+  assign sbox[8'h48] = 8'h52;
+  assign sbox[8'h49] = 8'h3b;
+  assign sbox[8'h4a] = 8'hd6;
+  assign sbox[8'h4b] = 8'hb3;
+  assign sbox[8'h4c] = 8'h29;
+  assign sbox[8'h4d] = 8'he3;
+  assign sbox[8'h4e] = 8'h2f;
+  assign sbox[8'h4f] = 8'h84;
+  assign sbox[8'h50] = 8'h53;
+  assign sbox[8'h51] = 8'hd1;
+  assign sbox[8'h52] = 8'h00;
+  assign sbox[8'h53] = 8'hed;
+  assign sbox[8'h54] = 8'h20;
+  assign sbox[8'h55] = 8'hfc;
+  assign sbox[8'h56] = 8'hb1;
+  assign sbox[8'h57] = 8'h5b;
+  assign sbox[8'h58] = 8'h6a;
+  assign sbox[8'h59] = 8'hcb;
+  assign sbox[8'h5a] = 8'hbe;
+  assign sbox[8'h5b] = 8'h39;
+  assign sbox[8'h5c] = 8'h4a;
+  assign sbox[8'h5d] = 8'h4c;
+  assign sbox[8'h5e] = 8'h58;
+  assign sbox[8'h5f] = 8'hcf;
+  assign sbox[8'h60] = 8'hd0;
+  assign sbox[8'h61] = 8'hef;
+  assign sbox[8'h62] = 8'haa;
+  assign sbox[8'h63] = 8'hfb;
+  assign sbox[8'h64] = 8'h43;
+  assign sbox[8'h65] = 8'h4d;
+  assign sbox[8'h66] = 8'h33;
+  assign sbox[8'h67] = 8'h85;
+  assign sbox[8'h68] = 8'h45;
+  assign sbox[8'h69] = 8'hf9;
+  assign sbox[8'h6a] = 8'h02;
+  assign sbox[8'h6b] = 8'h7f;
+  assign sbox[8'h6c] = 8'h50;
+  assign sbox[8'h6d] = 8'h3c;
+  assign sbox[8'h6e] = 8'h9f;
+  assign sbox[8'h6f] = 8'ha8;
+  assign sbox[8'h70] = 8'h51;
+  assign sbox[8'h71] = 8'ha3;
+  assign sbox[8'h72] = 8'h40;
+  assign sbox[8'h73] = 8'h8f;
+  assign sbox[8'h74] = 8'h92;
+  assign sbox[8'h75] = 8'h9d;
+  assign sbox[8'h76] = 8'h38;
+  assign sbox[8'h77] = 8'hf5;
+  assign sbox[8'h78] = 8'hbc;
+  assign sbox[8'h79] = 8'hb6;
+  assign sbox[8'h7a] = 8'hda;
+  assign sbox[8'h7b] = 8'h21;
+  assign sbox[8'h7c] = 8'h10;
+  assign sbox[8'h7d] = 8'hff;
+  assign sbox[8'h7e] = 8'hf3;
+  assign sbox[8'h7f] = 8'hd2;
+  assign sbox[8'h80] = 8'hcd;
+  assign sbox[8'h81] = 8'h0c;
+  assign sbox[8'h82] = 8'h13;
+  assign sbox[8'h83] = 8'hec;
+  assign sbox[8'h84] = 8'h5f;
+  assign sbox[8'h85] = 8'h97;
+  assign sbox[8'h86] = 8'h44;
+  assign sbox[8'h87] = 8'h17;
+  assign sbox[8'h88] = 8'hc4;
+  assign sbox[8'h89] = 8'ha7;
+  assign sbox[8'h8a] = 8'h7e;
+  assign sbox[8'h8b] = 8'h3d;
+  assign sbox[8'h8c] = 8'h64;
+  assign sbox[8'h8d] = 8'h5d;
+  assign sbox[8'h8e] = 8'h19;
+  assign sbox[8'h8f] = 8'h73;
+  assign sbox[8'h90] = 8'h60;
+  assign sbox[8'h91] = 8'h81;
+  assign sbox[8'h92] = 8'h4f;
+  assign sbox[8'h93] = 8'hdc;
+  assign sbox[8'h94] = 8'h22;
+  assign sbox[8'h95] = 8'h2a;
+  assign sbox[8'h96] = 8'h90;
+  assign sbox[8'h97] = 8'h88;
+  assign sbox[8'h98] = 8'h46;
+  assign sbox[8'h99] = 8'hee;
+  assign sbox[8'h9a] = 8'hb8;
+  assign sbox[8'h9b] = 8'h14;
+  assign sbox[8'h9c] = 8'hde;
+  assign sbox[8'h9d] = 8'h5e;
+  assign sbox[8'h9e] = 8'h0b;
+  assign sbox[8'h9f] = 8'hdb;
+  assign sbox[8'ha0] = 8'he0;
+  assign sbox[8'ha1] = 8'h32;
+  assign sbox[8'ha2] = 8'h3a;
+  assign sbox[8'ha3] = 8'h0a;
+  assign sbox[8'ha4] = 8'h49;
+  assign sbox[8'ha5] = 8'h06;
+  assign sbox[8'ha6] = 8'h24;
+  assign sbox[8'ha7] = 8'h5c;
+  assign sbox[8'ha8] = 8'hc2;
+  assign sbox[8'ha9] = 8'hd3;
+  assign sbox[8'haa] = 8'hac;
+  assign sbox[8'hab] = 8'h62;
+  assign sbox[8'hac] = 8'h91;
+  assign sbox[8'had] = 8'h95;
+  assign sbox[8'hae] = 8'he4;
+  assign sbox[8'haf] = 8'h79;
+  assign sbox[8'hb0] = 8'he7;
+  assign sbox[8'hb1] = 8'hc8;
+  assign sbox[8'hb2] = 8'h37;
+  assign sbox[8'hb3] = 8'h6d;
+  assign sbox[8'hb4] = 8'h8d;
+  assign sbox[8'hb5] = 8'hd5;
+  assign sbox[8'hb6] = 8'h4e;
+  assign sbox[8'hb7] = 8'ha9;
+  assign sbox[8'hb8] = 8'h6c;
+  assign sbox[8'hb9] = 8'h56;
+  assign sbox[8'hba] = 8'hf4;
+  assign sbox[8'hbb] = 8'hea;
+  assign sbox[8'hbc] = 8'h65;
+  assign sbox[8'hbd] = 8'h7a;
+  assign sbox[8'hbe] = 8'hae;
+  assign sbox[8'hbf] = 8'h08;
+  assign sbox[8'hc0] = 8'hba;
+  assign sbox[8'hc1] = 8'h78;
+  assign sbox[8'hc2] = 8'h25;
+  assign sbox[8'hc3] = 8'h2e;
+  assign sbox[8'hc4] = 8'h1c;
+  assign sbox[8'hc5] = 8'ha6;
+  assign sbox[8'hc6] = 8'hb4;
+  assign sbox[8'hc7] = 8'hc6;
+  assign sbox[8'hc8] = 8'he8;
+  assign sbox[8'hc9] = 8'hdd;
+  assign sbox[8'hca] = 8'h74;
+  assign sbox[8'hcb] = 8'h1f;
+  assign sbox[8'hcc] = 8'h4b;
+  assign sbox[8'hcd] = 8'hbd;
+  assign sbox[8'hce] = 8'h8b;
+  assign sbox[8'hcf] = 8'h8a;
+  assign sbox[8'hd0] = 8'h70;
+  assign sbox[8'hd1] = 8'h3e;
+  assign sbox[8'hd2] = 8'hb5;
+  assign sbox[8'hd3] = 8'h66;
+  assign sbox[8'hd4] = 8'h48;
+  assign sbox[8'hd5] = 8'h03;
+  assign sbox[8'hd6] = 8'hf6;
+  assign sbox[8'hd7] = 8'h0e;
+  assign sbox[8'hd8] = 8'h61;
+  assign sbox[8'hd9] = 8'h35;
+  assign sbox[8'hda] = 8'h57;
+  assign sbox[8'hdb] = 8'hb9;
+  assign sbox[8'hdc] = 8'h86;
+  assign sbox[8'hdd] = 8'hc1;
+  assign sbox[8'hde] = 8'h1d;
+  assign sbox[8'hdf] = 8'h9e;
+  assign sbox[8'he0] = 8'he1;
+  assign sbox[8'he1] = 8'hf8;
+  assign sbox[8'he2] = 8'h98;
+  assign sbox[8'he3] = 8'h11;
+  assign sbox[8'he4] = 8'h69;
+  assign sbox[8'he5] = 8'hd9;
+  assign sbox[8'he6] = 8'h8e;
+  assign sbox[8'he7] = 8'h94;
+  assign sbox[8'he8] = 8'h9b;
+  assign sbox[8'he9] = 8'h1e;
+  assign sbox[8'hea] = 8'h87;
+  assign sbox[8'heb] = 8'he9;
+  assign sbox[8'hec] = 8'hce;
+  assign sbox[8'hed] = 8'h55;
+  assign sbox[8'hee] = 8'h28;
+  assign sbox[8'hef] = 8'hdf;
+  assign sbox[8'hf0] = 8'h8c;
+  assign sbox[8'hf1] = 8'ha1;
+  assign sbox[8'hf2] = 8'h89;
+  assign sbox[8'hf3] = 8'h0d;
+  assign sbox[8'hf4] = 8'hbf;
+  assign sbox[8'hf5] = 8'he6;
+  assign sbox[8'hf6] = 8'h42;
+  assign sbox[8'hf7] = 8'h68;
+  assign sbox[8'hf8] = 8'h41;
+  assign sbox[8'hf9] = 8'h99;
+  assign sbox[8'hfa] = 8'h2d;
+  assign sbox[8'hfb] = 8'h0f;
+  assign sbox[8'hfc] = 8'hb0;
+  assign sbox[8'hfd] = 8'h54;
+  assign sbox[8'hfe] = 8'hbb;
+  assign sbox[8'hff] = 8'h16;
+
+endmodule // aes_sbox
+
+//======================================================================
+// EOF aes_sbox.v
+//======================================================================
diff --git a/src/tb/tb_aes.v b/src/tb/tb_aes.v
new file mode 100644
index 0000000..188a21a
--- /dev/null
+++ b/src/tb/tb_aes.v
@@ -0,0 +1,542 @@
+//======================================================================
+//
+// tb_aes.v
+// --------
+// Testbench for the aes top level wrapper.
+//
+//
+// Author: Joachim Strombergson
+// 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.
+//
+//======================================================================
+
+//------------------------------------------------------------------
+// Test module.
+//------------------------------------------------------------------
+module tb_aes();
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter DEBUG     = 0;
+
+  parameter CLK_HALF_PERIOD = 1;
+  parameter CLK_PERIOD      = 2 * CLK_HALF_PERIOD;
+
+  // The DUT address map.
+  parameter ADDR_NAME0       = 8'h00;
+  parameter ADDR_NAME1       = 8'h01;
+  parameter ADDR_VERSION     = 8'h02;
+
+  parameter ADDR_CTRL        = 8'h08;
+  parameter CTRL_INIT_BIT    = 0;
+  parameter CTRL_NEXT_BIT    = 1;
+  parameter CTRL_ENCDEC_BIT  = 2;
+  parameter CTRL_KEYLEN_BIT  = 3;
+
+  parameter ADDR_STATUS      = 8'h09;
+  parameter STATUS_READY_BIT = 0;
+  parameter STATUS_VALID_BIT = 1;
+
+  parameter ADDR_CONFIG      = 8'h0a;
+
+  parameter ADDR_KEY0        = 8'h10;
+  parameter ADDR_KEY1        = 8'h11;
+  parameter ADDR_KEY2        = 8'h12;
+  parameter ADDR_KEY3        = 8'h13;
+  parameter ADDR_KEY4        = 8'h14;
+  parameter ADDR_KEY5        = 8'h15;
+  parameter ADDR_KEY6        = 8'h16;
+  parameter ADDR_KEY7        = 8'h17;
+
+  parameter ADDR_BLOCK0      = 8'h20;
+  parameter ADDR_BLOCK1      = 8'h21;
+  parameter ADDR_BLOCK2      = 8'h22;
+  parameter ADDR_BLOCK3      = 8'h23;
+
+  parameter ADDR_RESULT0     = 8'h30;
+  parameter ADDR_RESULT1     = 8'h31;
+  parameter ADDR_RESULT2     = 8'h32;
+  parameter ADDR_RESULT3     = 8'h33;
+
+  parameter AES_128_BIT_KEY = 0;
+  parameter AES_256_BIT_KEY = 1;
+
+  parameter AES_DECIPHER = 1'b0;
+  parameter AES_ENCIPHER = 1'b1;
+
+
+  //----------------------------------------------------------------
+  // Register and Wire declarations.
+  //----------------------------------------------------------------
+  reg [31 : 0]  cycle_ctr;
+  reg [31 : 0]  error_ctr;
+  reg [31 : 0]  tc_ctr;
+
+  reg [31 : 0]  read_data;
+  reg [127 : 0] result_data;
+
+  reg           tb_clk;
+  reg           tb_reset_n;
+  reg           tb_cs;
+  reg           tb_we;
+  reg [7  : 0]  tb_address;
+  reg [31 : 0]  tb_write_data;
+  wire [31 : 0] tb_read_data;
+  wire          tb_error;
+
+
+  //----------------------------------------------------------------
+  // Device Under Test.
+  //----------------------------------------------------------------
+  aes dut(
+           .clk(tb_clk),
+           .reset_n(tb_reset_n),
+           .cs(tb_cs),
+           .we(tb_we),
+           .address(tb_address),
+           .write_data(tb_write_data),
+           .read_data(tb_read_data),
+           .error(tb_error)
+          );
+
+
+  //----------------------------------------------------------------
+  // clk_gen
+  //
+  // Always running clock generator process.
+  //----------------------------------------------------------------
+  always
+    begin : clk_gen
+      #CLK_HALF_PERIOD;
+      tb_clk = !tb_clk;
+    end // clk_gen
+
+
+  //----------------------------------------------------------------
+  // sys_monitor()
+  //
+  // An always running process that creates a cycle counter and
+  // conditionally displays information about the DUT.
+  //----------------------------------------------------------------
+  always
+    begin : sys_monitor
+      cycle_ctr = cycle_ctr + 1;
+
+      #(CLK_PERIOD);
+
+      if (DEBUG)
+        begin
+          dump_dut_state();
+        end
+    end
+
+
+  //----------------------------------------------------------------
+  // dump_dut_state()
+  //
+  // Dump the state of the dump when needed.
+  //----------------------------------------------------------------
+  task dump_dut_state;
+    begin
+      $display("cycle: 0x%016x", cycle_ctr);
+      $display("State of DUT");
+      $display("------------");
+      $display("ctrl_reg:   init   = 0x%01x, next   = 0x%01x", dut.init_reg, dut.next_reg);
+      $display("config_reg: encdec = 0x%01x, length = 0x%01x ", dut.encdec_reg, dut.keylen_reg);
+      $display("");
+
+      $display("block: 0x%08x, 0x%08x, 0x%08x, 0x%08x",
+               dut.block_reg[0], dut.block_reg[1], dut.block_reg[2], dut.block_reg[3]);
+      $display("");
+
+    end
+  endtask // dump_dut_state
+
+
+  //----------------------------------------------------------------
+  // reset_dut()
+  //
+  // Toggle reset to put the DUT into a well known state.
+  //----------------------------------------------------------------
+  task reset_dut;
+    begin
+      $display("*** Toggle reset.");
+      tb_reset_n = 0;
+
+      #(2 * CLK_PERIOD);
+      tb_reset_n = 1;
+      $display("");
+    end
+  endtask // reset_dut
+
+
+  //----------------------------------------------------------------
+  // display_test_results()
+  //
+  // Display the accumulated test results.
+  //----------------------------------------------------------------
+  task display_test_results;
+    begin
+      if (error_ctr == 0)
+        begin
+          $display("*** All %02d test cases completed successfully", tc_ctr);
+        end
+      else
+        begin
+          $display("*** %02d tests completed - %02d test cases did not complete successfully.",
+                   tc_ctr, error_ctr);
+        end
+    end
+  endtask // display_test_results
+
+
+  //----------------------------------------------------------------
+  // init_sim()
+  //
+  // Initialize all counters and testbed functionality as well
+  // as setting the DUT inputs to defined values.
+  //----------------------------------------------------------------
+  task init_sim;
+    begin
+      cycle_ctr     = 0;
+      error_ctr     = 0;
+      tc_ctr        = 0;
+
+      tb_clk        = 0;
+      tb_reset_n    = 1;
+
+      tb_cs         = 0;
+      tb_we         = 0;
+      tb_address    = 8'h0;
+      tb_write_data = 32'h0;
+    end
+  endtask // init_sim
+
+
+  //----------------------------------------------------------------
+  // write_word()
+  //
+  // Write the given word to the DUT using the DUT interface.
+  //----------------------------------------------------------------
+  task write_word(input [11 : 0] address,
+                  input [31 : 0] word);
+    begin
+      if (DEBUG)
+        begin
+          $display("*** Writing 0x%08x to 0x%02x.", word, address);
+          $display("");
+        end
+
+      tb_address = address;
+      tb_write_data = word;
+      tb_cs = 1;
+      tb_we = 1;
+      #(2 * CLK_PERIOD);
+      tb_cs = 0;
+      tb_we = 0;
+    end
+  endtask // write_word
+
+
+  //----------------------------------------------------------------
+  // write_block()
+  //
+  // Write the given block to the dut.
+  //----------------------------------------------------------------
+  task write_block(input [127 : 0] block);
+    begin
+      write_word(ADDR_BLOCK0, block[127  :  96]);
+      write_word(ADDR_BLOCK1, block[95   :  64]);
+      write_word(ADDR_BLOCK2, block[63   :  32]);
+      write_word(ADDR_BLOCK3, block[31   :   0]);
+    end
+  endtask // write_block
+
+
+  //----------------------------------------------------------------
+  // read_word()
+  //
+  // Read a data word from the given address in the DUT.
+  // the word read will be available in the global variable
+  // read_data.
+  //----------------------------------------------------------------
+  task read_word(input [11 : 0]  address);
+    begin
+      tb_address = address;
+      tb_cs = 1;
+      tb_we = 0;
+      #(CLK_PERIOD);
+      read_data = tb_read_data;
+      tb_cs = 0;
+
+      if (DEBUG)
+        begin
+          $display("*** Reading 0x%08x from 0x%02x.", read_data, address);
+          $display("");
+        end
+    end
+  endtask // read_word
+
+
+  //----------------------------------------------------------------
+  // read_result()
+  //
+  // Read the result block in the dut.
+  //----------------------------------------------------------------
+  task read_result;
+    begin
+      read_word(ADDR_RESULT0);
+      result_data[127 : 096] = read_data;
+      read_word(ADDR_RESULT1);
+      result_data[095 : 064] = read_data;
+      read_word(ADDR_RESULT2);
+      result_data[063 : 032] = read_data;
+      read_word(ADDR_RESULT3);
+      result_data[031 : 000] = read_data;
+    end
+  endtask // read_result
+
+
+  //----------------------------------------------------------------
+  // init_key()
+  //
+  // init the key in the dut by writing the given key and
+  // key length and then trigger init processing.
+  //----------------------------------------------------------------
+  task init_key(input [255 : 0] key, input key_length);
+    begin
+      if (DEBUG)
+        begin
+          $display("key length: 0x%01x", key_length);
+          $display("Initializing key expansion for key: 0x%016x", key);
+        end
+
+      write_word(ADDR_KEY0, key[255  : 224]);
+      write_word(ADDR_KEY1, key[223  : 192]);
+      write_word(ADDR_KEY2, key[191  : 160]);
+      write_word(ADDR_KEY3, key[159  : 128]);
+      write_word(ADDR_KEY4, key[127  :  96]);
+      write_word(ADDR_KEY5, key[95   :  64]);
+      write_word(ADDR_KEY6, key[63   :  32]);
+      write_word(ADDR_KEY7, key[31   :   0]);
+
+      if (key_length)
+        begin
+          write_word(ADDR_CONFIG, 8'h02);
+        end
+      else
+        begin
+          write_word(ADDR_CONFIG, 8'h00);
+        end
+
+      write_word(ADDR_CTRL, 8'h01);
+
+      #(100 * CLK_PERIOD);
+    end
+  endtask // init_key
+
+
+  //----------------------------------------------------------------
+  // ecb_mode_single_block_test()
+  //
+  // Perform ECB mode encryption or decryption single block test.
+  //----------------------------------------------------------------
+  task ecb_mode_single_block_test(input [7 : 0]   tc_number,
+                                  input           encdec,
+                                  input [255 : 0] key,
+                                  input           key_length,
+                                  input [127 : 0] block,
+                                  input [127 : 0] expected);
+    begin
+      $display("*** TC %0d ECB mode test started.", tc_number);
+      tc_ctr = tc_ctr + 1;
+
+      init_key(key, key_length);
+      write_block(block);
+      dump_dut_state();
+
+      write_word(ADDR_CONFIG, (8'h00 + (key_length << 1)+ encdec));
+      write_word(ADDR_CTRL, 8'h02);
+
+      #(100 * CLK_PERIOD);
+
+      read_result();
+
+      if (result_data == expected)
+        begin
+          $display("*** TC %0d successful.", tc_number);
+          $display("");
+        end
+      else
+        begin
+          $display("*** ERROR: TC %0d NOT successful.", tc_number);
+          $display("Expected: 0x%032x", expected);
+          $display("Got:      0x%032x", result_data);
+          $display("");
+
+          error_ctr = error_ctr + 1;
+        end
+    end
+  endtask // ecb_mode_single_block_test
+
+
+  //----------------------------------------------------------------
+  // aes_test()
+  //
+  // Main test task will perform complete NIST test of AES.
+  //----------------------------------------------------------------
+  task aes_test;
+    reg [255 : 0] nist_aes128_key;
+    reg [255 : 0] nist_aes256_key;
+
+    reg [127 : 0] nist_plaintext0;
+    reg [127 : 0] nist_plaintext1;
+    reg [127 : 0] nist_plaintext2;
+    reg [127 : 0] nist_plaintext3;
+
+    reg [127 : 0] nist_ecb_128_enc_expected0;
+    reg [127 : 0] nist_ecb_128_enc_expected1;
+    reg [127 : 0] nist_ecb_128_enc_expected2;
+    reg [127 : 0] nist_ecb_128_enc_expected3;
+
+    reg [127 : 0] nist_ecb_256_enc_expected0;
+    reg [127 : 0] nist_ecb_256_enc_expected1;
+    reg [127 : 0] nist_ecb_256_enc_expected2;
+    reg [127 : 0] nist_ecb_256_enc_expected3;
+
+    begin
+      nist_aes128_key = 256'h2b7e151628aed2a6abf7158809cf4f3c00000000000000000000000000000000;
+      nist_aes256_key = 256'h603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4;
+
+      nist_plaintext0 = 128'h6bc1bee22e409f96e93d7e117393172a;
+      nist_plaintext1 = 128'hae2d8a571e03ac9c9eb76fac45af8e51;
+      nist_plaintext2 = 128'h30c81c46a35ce411e5fbc1191a0a52ef;
+      nist_plaintext3 = 128'hf69f2445df4f9b17ad2b417be66c3710;
+
+      nist_ecb_128_enc_expected0 = 128'h3ad77bb40d7a3660a89ecaf32466ef97;
+      nist_ecb_128_enc_expected1 = 128'hf5d3d58503b9699de785895a96fdbaaf;
+      nist_ecb_128_enc_expected2 = 128'h43b1cd7f598ece23881b00e3ed030688;
+      nist_ecb_128_enc_expected3 = 128'h7b0c785e27e8ad3f8223207104725dd4;
+
+      nist_ecb_256_enc_expected0 = 128'hf3eed1bdb5d2a03c064b5a7e3db181f8;
+      nist_ecb_256_enc_expected1 = 128'h591ccb10d410ed26dc5ba74a31362870;
+      nist_ecb_256_enc_expected2 = 128'hb6ed21b99ca6f4f9f153e7b1beafed1d;
+      nist_ecb_256_enc_expected3 = 128'h23304b7a39f9f3ff067d8d8f9e24ecc7;
+
+
+      $display("ECB 128 bit key tests");
+      $display("---------------------");
+      ecb_mode_single_block_test(8'h01, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_plaintext0, nist_ecb_128_enc_expected0);
+
+      ecb_mode_single_block_test(8'h02, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                nist_plaintext1, nist_ecb_128_enc_expected1);
+
+      ecb_mode_single_block_test(8'h03, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_plaintext2, nist_ecb_128_enc_expected2);
+
+      ecb_mode_single_block_test(8'h04, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_plaintext3, nist_ecb_128_enc_expected3);
+
+
+      ecb_mode_single_block_test(8'h05, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected0, nist_plaintext0);
+
+      ecb_mode_single_block_test(8'h06, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected1, nist_plaintext1);
+
+      ecb_mode_single_block_test(8'h07, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected2, nist_plaintext2);
+
+      ecb_mode_single_block_test(8'h08, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected3, nist_plaintext3);
+
+
+      $display("");
+      $display("ECB 256 bit key tests");
+      $display("---------------------");
+      ecb_mode_single_block_test(8'h10, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext0, nist_ecb_256_enc_expected0);
+
+      ecb_mode_single_block_test(8'h11, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext1, nist_ecb_256_enc_expected1);
+
+      ecb_mode_single_block_test(8'h12, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext2, nist_ecb_256_enc_expected2);
+
+      ecb_mode_single_block_test(8'h13, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext3, nist_ecb_256_enc_expected3);
+
+
+      ecb_mode_single_block_test(8'h14, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected0, nist_plaintext0);
+
+      ecb_mode_single_block_test(8'h15, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected1, nist_plaintext1);
+
+      ecb_mode_single_block_test(8'h16, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected2, nist_plaintext2);
+
+      ecb_mode_single_block_test(8'h17, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected3, nist_plaintext3);
+    end
+  endtask // aes_test
+
+
+  //----------------------------------------------------------------
+  // main
+  //
+  // The main test functionality.
+  //----------------------------------------------------------------
+  initial
+    begin : main
+      $display("   -= Testbench for AES started =-");
+      $display("    ==============================");
+      $display("");
+
+      init_sim();
+      dump_dut_state();
+      reset_dut();
+      dump_dut_state();
+
+      aes_test();
+
+      display_test_results();
+
+      $display("");
+      $display("*** AES simulation done. ***");
+      $finish;
+    end // main
+endmodule // tb_aes
+
+//======================================================================
+// EOF tb_aes.v
+//======================================================================
diff --git a/src/tb/tb_aes_core.v b/src/tb/tb_aes_core.v
new file mode 100644
index 0000000..d36d0bc
--- /dev/null
+++ b/src/tb/tb_aes_core.v
@@ -0,0 +1,464 @@
+//======================================================================
+//
+// tb_aes_core.v
+// -------------
+// Testbench for the AES block cipher core.
+//
+//
+// Author: Joachim Strombergson
+// 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.
+//
+//======================================================================
+
+//------------------------------------------------------------------
+// Test module.
+//------------------------------------------------------------------
+module tb_aes_core();
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter DEBUG     = 0;
+  parameter DUMP_WAIT = 0;
+
+  parameter CLK_HALF_PERIOD = 1;
+  parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
+
+  parameter AES_128_BIT_KEY = 0;
+  parameter AES_256_BIT_KEY = 1;
+
+  parameter AES_DECIPHER = 1'b0;
+  parameter AES_ENCIPHER = 1'b1;
+
+
+  //----------------------------------------------------------------
+  // Register and Wire declarations.
+  //----------------------------------------------------------------
+  reg [31 : 0] cycle_ctr;
+  reg [31 : 0] error_ctr;
+  reg [31 : 0] tc_ctr;
+
+  reg            tb_clk;
+  reg            tb_reset_n;
+  reg            tb_encdec;
+  reg            tb_init;
+  reg            tb_next;
+  wire           tb_ready;
+  reg [255 : 0]  tb_key;
+  reg            tb_keylen;
+  reg [127 : 0]  tb_block;
+  wire [127 : 0] tb_result;
+  wire           tb_result_valid;
+
+
+  //----------------------------------------------------------------
+  // Device Under Test.
+  //----------------------------------------------------------------
+  aes_core dut(
+               .clk(tb_clk),
+               .reset_n(tb_reset_n),
+
+               .encdec(tb_encdec),
+               .init(tb_init),
+               .next(tb_next),
+               .ready(tb_ready),
+
+               .key(tb_key),
+               .keylen(tb_keylen),
+
+               .block(tb_block),
+               .result(tb_result)
+              );
+
+
+  //----------------------------------------------------------------
+  // clk_gen
+  //
+  // Always running clock generator process.
+  //----------------------------------------------------------------
+  always
+    begin : clk_gen
+      #CLK_HALF_PERIOD;
+      tb_clk = !tb_clk;
+    end // clk_gen
+
+
+  //----------------------------------------------------------------
+  // sys_monitor()
+  //
+  // An always running process that creates a cycle counter and
+  // conditionally displays information about the DUT.
+  //----------------------------------------------------------------
+  always
+    begin : sys_monitor
+      cycle_ctr = cycle_ctr + 1;
+      #(CLK_PERIOD);
+      if (DEBUG)
+        begin
+          dump_dut_state();
+        end
+    end
+
+
+  //----------------------------------------------------------------
+  // dump_dut_state()
+  //
+  // Dump the state of the dump when needed.
+  //----------------------------------------------------------------
+  task dump_dut_state;
+    begin
+      $display("State of DUT");
+      $display("------------");
+      $display("Inputs and outputs:");
+      $display("encdec = 0x%01x, init = 0x%01x, next = 0x%01x",
+               dut.encdec, dut.init, dut.next);
+      $display("keylen = 0x%01x, key  = 0x%032x ", dut.keylen, dut.key);
+      $display("block  = 0x%032x", dut.block);
+      $display("");
+      $display("ready        = 0x%01x", dut.ready);
+      $display("result_valid = 0x%01x, result = 0x%032x",
+               dut.result_valid, dut.result);
+      $display("");
+      $display("Encipher state::");
+      $display("enc_ctrl = 0x%01x, round_ctr = 0x%01x",
+               dut.enc_block.enc_ctrl_reg, dut.enc_block.round_ctr_reg);
+      $display("");
+    end
+  endtask // dump_dut_state
+
+
+  //----------------------------------------------------------------
+  // dump_keys()
+  //
+  // Dump the keys in the key memory of the dut.
+  //----------------------------------------------------------------
+  task dump_keys;
+    begin
+      $display("State of key memory in DUT:");
+      $display("key[00] = 0x%016x", dut.keymem.key_mem[00]);
+      $display("key[01] = 0x%016x", dut.keymem.key_mem[01]);
+      $display("key[02] = 0x%016x", dut.keymem.key_mem[02]);
+      $display("key[03] = 0x%016x", dut.keymem.key_mem[03]);
+      $display("key[04] = 0x%016x", dut.keymem.key_mem[04]);
+      $display("key[05] = 0x%016x", dut.keymem.key_mem[05]);
+      $display("key[06] = 0x%016x", dut.keymem.key_mem[06]);
+      $display("key[07] = 0x%016x", dut.keymem.key_mem[07]);
+      $display("key[08] = 0x%016x", dut.keymem.key_mem[08]);
+      $display("key[09] = 0x%016x", dut.keymem.key_mem[09]);
+      $display("key[10] = 0x%016x", dut.keymem.key_mem[10]);
+      $display("key[11] = 0x%016x", dut.keymem.key_mem[11]);
+      $display("key[12] = 0x%016x", dut.keymem.key_mem[12]);
+      $display("key[13] = 0x%016x", dut.keymem.key_mem[13]);
+      $display("key[14] = 0x%016x", dut.keymem.key_mem[14]);
+      $display("");
+    end
+  endtask // dump_keys
+
+
+  //----------------------------------------------------------------
+  // reset_dut()
+  //
+  // Toggle reset to put the DUT into a well known state.
+  //----------------------------------------------------------------
+  task reset_dut;
+    begin
+      $display("*** Toggle reset.");
+      tb_reset_n = 0;
+      #(2 * CLK_PERIOD);
+      tb_reset_n = 1;
+    end
+  endtask // reset_dut
+
+
+  //----------------------------------------------------------------
+  // init_sim()
+  //
+  // Initialize all counters and testbed functionality as well
+  // as setting the DUT inputs to defined values.
+  //----------------------------------------------------------------
+  task init_sim;
+    begin
+      cycle_ctr = 0;
+      error_ctr = 0;
+      tc_ctr    = 0;
+
+      tb_clk     = 0;
+      tb_reset_n = 1;
+      tb_encdec  = 0;
+      tb_init    = 0;
+      tb_next    = 0;
+      tb_key     = {8{32'h00000000}};
+      tb_keylen  = 0;
+
+      tb_block  = {4{32'h00000000}};
+    end
+  endtask // init_sim
+
+
+  //----------------------------------------------------------------
+  // display_test_result()
+  //
+  // Display the accumulated test results.
+  //----------------------------------------------------------------
+  task display_test_result;
+    begin
+      if (error_ctr == 0)
+        begin
+          $display("*** All %02d test cases completed successfully", tc_ctr);
+        end
+      else
+        begin
+          $display("*** %02d tests completed - %02d test cases did not complete successfully.",
+                   tc_ctr, error_ctr);
+        end
+    end
+  endtask // display_test_result
+
+
+  //----------------------------------------------------------------
+  // wait_ready()
+  //
+  // Wait for the ready flag in the dut to be set.
+  //
+  // Note: It is the callers responsibility to call the function
+  // when the dut is actively processing and will in fact at some
+  // point set the flag.
+  //----------------------------------------------------------------
+  task wait_ready;
+    begin
+      while (!tb_ready)
+        begin
+          #(CLK_PERIOD);
+          if (DUMP_WAIT)
+            begin
+              dump_dut_state();
+            end
+        end
+    end
+  endtask // wait_ready
+
+
+  //----------------------------------------------------------------
+  // wait_valid()
+  //
+  // Wait for the result_valid flag in the dut to be set.
+  //
+  // Note: It is the callers responsibility to call the function
+  // when the dut is actively processing a block and will in fact
+  // at some point set the flag.
+  //----------------------------------------------------------------
+  task wait_valid;
+    begin
+      while (!tb_result_valid)
+        begin
+          #(CLK_PERIOD);
+        end
+    end
+  endtask // wait_valid
+
+
+  //----------------------------------------------------------------
+  // ecb_mode_single_block_test()
+  //
+  // Perform ECB mode encryption or decryption single block test.
+  //----------------------------------------------------------------
+  task ecb_mode_single_block_test(input [7 : 0]   tc_number,
+                                  input           encdec,
+                                  input [255 : 0] key,
+                                  input           key_length,
+                                  input [127 : 0] block,
+                                  input [127 : 0] expected);
+   begin
+     $display("*** TC %0d ECB mode test started.", tc_number);
+     tc_ctr = tc_ctr + 1;
+
+     // Init the cipher with the given key and length.
+     tb_key = key;
+     tb_keylen = key_length;
+     tb_init = 1;
+     #(2 * CLK_PERIOD);
+     tb_init = 0;
+     wait_ready();
+
+     $display("Key expansion done");
+     $display("");
+
+     dump_keys();
+
+
+     // Perform encipher och decipher operation on the block.
+     tb_encdec = encdec;
+     tb_block = block;
+     tb_next = 1;
+     #(2 * CLK_PERIOD);
+     tb_next = 0;
+     wait_ready();
+
+     if (tb_result == expected)
+       begin
+         $display("*** TC %0d successful.", tc_number);
+         $display("");
+       end
+     else
+       begin
+         $display("*** ERROR: TC %0d NOT successful.", tc_number);
+         $display("Expected: 0x%032x", expected);
+         $display("Got:      0x%032x", tb_result);
+         $display("");
+
+         error_ctr = error_ctr + 1;
+       end
+   end
+  endtask // ecb_mode_single_block_test
+
+
+  //----------------------------------------------------------------
+  // aes_core_test
+  // The main test functionality.
+  //
+  // Test cases taken from NIST SP 800-38A:
+  // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
+  //----------------------------------------------------------------
+  initial
+    begin : aes_core_test
+      reg [255 : 0] nist_aes128_key;
+      reg [255 : 0] nist_aes256_key;
+
+      reg [127 : 0] nist_plaintext0;
+      reg [127 : 0] nist_plaintext1;
+      reg [127 : 0] nist_plaintext2;
+      reg [127 : 0] nist_plaintext3;
+
+      reg [127 : 0] nist_ecb_128_enc_expected0;
+      reg [127 : 0] nist_ecb_128_enc_expected1;
+      reg [127 : 0] nist_ecb_128_enc_expected2;
+      reg [127 : 0] nist_ecb_128_enc_expected3;
+
+      reg [127 : 0] nist_ecb_256_enc_expected0;
+      reg [127 : 0] nist_ecb_256_enc_expected1;
+      reg [127 : 0] nist_ecb_256_enc_expected2;
+      reg [127 : 0] nist_ecb_256_enc_expected3;
+
+      nist_aes128_key = 256'h2b7e151628aed2a6abf7158809cf4f3c00000000000000000000000000000000;
+      nist_aes256_key = 256'h603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4;
+
+      nist_plaintext0 = 128'h6bc1bee22e409f96e93d7e117393172a;
+      nist_plaintext1 = 128'hae2d8a571e03ac9c9eb76fac45af8e51;
+      nist_plaintext2 = 128'h30c81c46a35ce411e5fbc1191a0a52ef;
+      nist_plaintext3 = 128'hf69f2445df4f9b17ad2b417be66c3710;
+
+      nist_ecb_128_enc_expected0 = 128'h3ad77bb40d7a3660a89ecaf32466ef97;
+      nist_ecb_128_enc_expected1 = 128'hf5d3d58503b9699de785895a96fdbaaf;
+      nist_ecb_128_enc_expected2 = 128'h43b1cd7f598ece23881b00e3ed030688;
+      nist_ecb_128_enc_expected3 = 128'h7b0c785e27e8ad3f8223207104725dd4;
+
+      nist_ecb_256_enc_expected0 = 128'hf3eed1bdb5d2a03c064b5a7e3db181f8;
+      nist_ecb_256_enc_expected1 = 128'h591ccb10d410ed26dc5ba74a31362870;
+      nist_ecb_256_enc_expected2 = 128'hb6ed21b99ca6f4f9f153e7b1beafed1d;
+      nist_ecb_256_enc_expected3 = 128'h23304b7a39f9f3ff067d8d8f9e24ecc7;
+
+
+      $display("   -= Testbench for aes core started =-");
+      $display("     ================================");
+      $display("");
+
+      init_sim();
+      dump_dut_state();
+      reset_dut();
+      dump_dut_state();
+
+
+      $display("ECB 128 bit key tests");
+      $display("---------------------");
+      ecb_mode_single_block_test(8'h01, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_plaintext0, nist_ecb_128_enc_expected0);
+
+     ecb_mode_single_block_test(8'h02, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                nist_plaintext1, nist_ecb_128_enc_expected1);
+
+     ecb_mode_single_block_test(8'h03, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                nist_plaintext2, nist_ecb_128_enc_expected2);
+
+     ecb_mode_single_block_test(8'h04, AES_ENCIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                nist_plaintext3, nist_ecb_128_enc_expected3);
+
+
+      ecb_mode_single_block_test(8'h05, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected0, nist_plaintext0);
+
+      ecb_mode_single_block_test(8'h06, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected1, nist_plaintext1);
+
+      ecb_mode_single_block_test(8'h07, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected2, nist_plaintext2);
+
+      ecb_mode_single_block_test(8'h08, AES_DECIPHER, nist_aes128_key, AES_128_BIT_KEY,
+                                 nist_ecb_128_enc_expected3, nist_plaintext3);
+
+
+      $display("");
+      $display("ECB 256 bit key tests");
+      $display("---------------------");
+      ecb_mode_single_block_test(8'h10, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext0, nist_ecb_256_enc_expected0);
+
+      ecb_mode_single_block_test(8'h11, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext1, nist_ecb_256_enc_expected1);
+
+      ecb_mode_single_block_test(8'h12, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext2, nist_ecb_256_enc_expected2);
+
+      ecb_mode_single_block_test(8'h13, AES_ENCIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_plaintext3, nist_ecb_256_enc_expected3);
+
+
+      ecb_mode_single_block_test(8'h14, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected0, nist_plaintext0);
+
+      ecb_mode_single_block_test(8'h15, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected1, nist_plaintext1);
+
+      ecb_mode_single_block_test(8'h16, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected2, nist_plaintext2);
+
+      ecb_mode_single_block_test(8'h17, AES_DECIPHER, nist_aes256_key, AES_256_BIT_KEY,
+                                 nist_ecb_256_enc_expected3, nist_plaintext3);
+
+
+      display_test_result();
+      $display("");
+      $display("*** AES core simulation done. ***");
+      $finish;
+    end // aes_core_test
+endmodule // tb_aes_core
+
+//======================================================================
+// EOF tb_aes_core.v
+//======================================================================
diff --git a/src/tb/tb_aes_decipher_block.v b/src/tb/tb_aes_decipher_block.v
new file mode 100644
index 0000000..ec228c0
--- /dev/null
+++ b/src/tb/tb_aes_decipher_block.v
@@ -0,0 +1,406 @@
+//======================================================================
+//
+// tb_aes_decipher_block.v
+// -----------------------
+// Testbench for the AES decipher block module.
+//
+//
+// Author: Joachim Strombergson
+// 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.
+//
+//======================================================================
+
+//------------------------------------------------------------------
+// Test module.
+//------------------------------------------------------------------
+module tb_aes_decipher_block();
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter DEBUG     = 1;
+  parameter DUMP_WAIT = 0;
+
+  parameter CLK_HALF_PERIOD = 1;
+  parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
+
+  parameter AES_128_BIT_KEY = 0;
+  parameter AES_256_BIT_KEY = 1;
+
+  parameter AES_DECIPHER = 1'b0;
+  parameter AES_ENCIPHER = 1'b1;
+
+
+  //----------------------------------------------------------------
+  // Register and Wire declarations.
+  //----------------------------------------------------------------
+  reg [31 : 0]   cycle_ctr;
+  reg [31 : 0]   error_ctr;
+  reg [31 : 0]   tc_ctr;
+
+  reg            tb_clk;
+  reg            tb_reset_n;
+
+  reg            tb_next;
+  reg            tb_keylen;
+  wire           tb_ready;
+  wire [3 : 0]   tb_round;
+  wire [127 : 0] tb_round_key;
+
+  reg [127 : 0]  tb_block;
+  wire [127 : 0] tb_new_block;
+
+  reg [127 : 0] key_mem [0 : 14];
+
+
+  //----------------------------------------------------------------
+  // Assignments.
+  //----------------------------------------------------------------
+  assign tb_round_key = key_mem[tb_round];
+
+
+  //----------------------------------------------------------------
+  // Device Under Test.
+  //----------------------------------------------------------------
+  aes_decipher_block dut(
+                         .clk(tb_clk),
+                         .reset_n(tb_reset_n),
+
+                         .next(tb_next),
+
+                         .keylen(tb_keylen),
+                         .round(tb_round),
+                         .round_key(tb_round_key),
+
+                         .block(tb_block),
+                         .new_block(tb_new_block),
+                         .ready(tb_ready)
+                        );
+
+
+  //----------------------------------------------------------------
+  // clk_gen
+  //
+  // Always running clock generator process.
+  //----------------------------------------------------------------
+  always
+    begin : clk_gen
+      #CLK_HALF_PERIOD;
+      tb_clk = !tb_clk;
+    end // clk_gen
+
+
+  //----------------------------------------------------------------
+  // sys_monitor()
+  //
+  // An always running process that creates a cycle counter and
+  // conditionally displays information about the DUT.
+  //----------------------------------------------------------------
+  always
+    begin : sys_monitor
+      cycle_ctr = cycle_ctr + 1;
+      #(CLK_PERIOD);
+      if (DEBUG)
+        begin
+          dump_dut_state();
+        end
+    end
+
+
+  //----------------------------------------------------------------
+  // dump_dut_state()
+  //
+  // Dump the state of the dump when needed.
+  //----------------------------------------------------------------
+  task dump_dut_state;
+    begin
+      $display("State of DUT");
+      $display("------------");
+      $display("Interfaces");
+      $display("ready = 0x%01x, next = 0x%01x, keylen = 0x%01x",
+               dut.ready, dut.next, dut.keylen);
+      $display("block     = 0x%032x", dut.block);
+      $display("new_block = 0x%032x", dut.new_block);
+      $display("");
+
+      $display("Control states");
+      $display("round = 0x%01x", dut.round);
+      $display("dec_ctrl = 0x%01x, update_type = 0x%01x, sword_ctr = 0x%01x, round_ctr = 0x%01x",
+               dut.dec_ctrl_reg, dut.update_type, dut.sword_ctr_reg, dut.round_ctr_reg);
+      $display("");
+
+      $display("Internal data values");
+      $display("round_key = 0x%016x", dut.round_key);
+      $display("sboxw = 0x%08x, new_sboxw = 0x%08x", dut.tmp_sboxw, dut.new_sboxw);
+      $display("block_w0_reg = 0x%08x, block_w1_reg = 0x%08x, block_w2_reg = 0x%08x, block_w3_reg = 0x%08x",
+               dut.block_w0_reg, dut.block_w1_reg, dut.block_w2_reg, dut.block_w3_reg);
+      $display("");
+      $display("old_block            = 0x%08x", dut.round_logic.old_block);
+      $display("inv_shiftrows_block  = 0x%08x", dut.round_logic.inv_shiftrows_block);
+      $display("inv_mixcolumns_block = 0x%08x", dut.round_logic.inv_mixcolumns_block);
+      $display("addkey_block         = 0x%08x", dut.round_logic.addkey_block);
+      $display("block_w0_new = 0x%08x, block_w1_new = 0x%08x, block_w2_new = 0x%08x, block_w3_new = 0x%08x",
+               dut.block_new[127 : 096], dut.block_new[095 : 064],
+               dut.block_new[063 : 032], dut.block_new[031 : 000]);
+      $display("");
+    end
+  endtask // dump_dut_state
+
+
+  //----------------------------------------------------------------
+  // reset_dut()
+  //
+  // Toggle reset to put the DUT into a well known state.
+  //----------------------------------------------------------------
+  task reset_dut;
+    begin
+      $display("*** Toggle reset.");
+      tb_reset_n = 0;
+      #(2 * CLK_PERIOD);
+      tb_reset_n = 1;
+    end
+  endtask // reset_dut
+
+
+  //----------------------------------------------------------------
+  // init_sim()
+  //
+  // Initialize all counters and testbed functionality as well
+  // as setting the DUT inputs to defined values.
+  //----------------------------------------------------------------
+  task init_sim;
+    begin
+      cycle_ctr    = 0;
+      error_ctr    = 0;
+      tc_ctr       = 0;
+
+      tb_clk       = 0;
+      tb_reset_n   = 1;
+
+      tb_next      = 0;
+      tb_keylen    = 0;
+
+      tb_block     = {4{32'h00000000}};
+    end
+  endtask // init_sim
+
+
+  //----------------------------------------------------------------
+  // display_test_result()
+  //
+  // Display the accumulated test results.
+  //----------------------------------------------------------------
+  task display_test_result;
+    begin
+      if (error_ctr == 0)
+        begin
+          $display("*** All %02d test cases completed successfully", tc_ctr);
+        end
+      else
+        begin
+          $display("*** %02d tests completed - %02d test cases did not complete successfully.",
+                   tc_ctr, error_ctr);
+        end
+    end
+  endtask // display_test_result
+
+
+  //----------------------------------------------------------------
+  // wait_ready()
+  //
+  // Wait for the ready flag in the dut to be set.
+  //
+  // Note: It is the callers responsibility to call the function
+  // when the dut is actively processing and will in fact at some
+  // point set the flag.
+  //----------------------------------------------------------------
+  task wait_ready;
+    begin
+      while (!tb_ready)
+        begin
+          #(CLK_PERIOD);
+          if (DUMP_WAIT)
+            begin
+              dump_dut_state();
+            end
+        end
+    end
+  endtask // wait_ready
+
+
+  //----------------------------------------------------------------
+  // test_ecb_de()
+  //
+  // Perform ECB mode encryption test.
+  //----------------------------------------------------------------
+  task test_ecb_dec(
+                    input           key_length,
+                    input [127 : 0] block,
+                    input [127 : 0] expected);
+   begin
+     $display("*** TC %0d ECB mode test started.", tc_ctr);
+
+     // Init the cipher with the given key and length.
+     tb_keylen = key_length;
+
+     // Perform decipher operation on the block.
+     tb_block = block;
+     tb_next = 1;
+     #(2 * CLK_PERIOD);
+     tb_next = 0;
+     #(2 * CLK_PERIOD);
+
+     wait_ready();
+
+     if (tb_new_block == expected)
+       begin
+         $display("*** TC %0d successful.", tc_ctr);
+         $display("");
+       end
+     else
+       begin
+         $display("*** ERROR: TC %0d NOT successful.", tc_ctr);
+         $display("Expected: 0x%032x", expected);
+         $display("Got:      0x%032x", tb_new_block);
+         $display("");
+
+         error_ctr = error_ctr + 1;
+       end
+     tc_ctr = tc_ctr + 1;
+   end
+  endtask // ecb_mode_single_block_test
+
+
+  //----------------------------------------------------------------
+  // tb_aes_decipher_block
+  // The main test functionality.
+  //
+  // Test cases taken from NIST SP 800-38A:
+  // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
+  //----------------------------------------------------------------
+  initial
+    begin : tb_aes_decipher_block
+      reg [127 : 0] nist_plaintext0;
+      reg [127 : 0] nist_plaintext1;
+      reg [127 : 0] nist_plaintext2;
+      reg [127 : 0] nist_plaintext3;
+
+      reg [127 : 0] nist_ecb_128_dec_ciphertext0;
+      reg [127 : 0] nist_ecb_128_dec_ciphertext1;
+      reg [127 : 0] nist_ecb_128_dec_ciphertext2;
+      reg [127 : 0] nist_ecb_128_dec_ciphertext3;
+
+      reg [127 : 0] nist_ecb_256_dec_ciphertext0;
+      reg [127 : 0] nist_ecb_256_dec_ciphertext1;
+      reg [127 : 0] nist_ecb_256_dec_ciphertext2;
+      reg [127 : 0] nist_ecb_256_dec_ciphertext3;
+
+      nist_plaintext0 = 128'h6bc1bee22e409f96e93d7e117393172a;
+      nist_plaintext1 = 128'hae2d8a571e03ac9c9eb76fac45af8e51;
+      nist_plaintext2 = 128'h30c81c46a35ce411e5fbc1191a0a52ef;
+      nist_plaintext3 = 128'hf69f2445df4f9b17ad2b417be66c3710;
+
+      nist_ecb_128_dec_ciphertext0 = 128'h3ad77bb40d7a3660a89ecaf32466ef97;
+      nist_ecb_128_dec_ciphertext1 = 128'hf5d3d58503b9699de785895a96fdbaaf;
+      nist_ecb_128_dec_ciphertext2 = 128'h43b1cd7f598ece23881b00e3ed030688;
+      nist_ecb_128_dec_ciphertext3 = 128'h7b0c785e27e8ad3f8223207104725dd4;
+
+      nist_ecb_256_dec_ciphertext0 = 255'hf3eed1bdb5d2a03c064b5a7e3db181f8;
+      nist_ecb_256_dec_ciphertext1 = 255'h591ccb10d410ed26dc5ba74a31362870;
+      nist_ecb_256_dec_ciphertext2 = 255'hb6ed21b99ca6f4f9f153e7b1beafed1d;
+      nist_ecb_256_dec_ciphertext3 = 255'h23304b7a39f9f3ff067d8d8f9e24ecc7;
+
+
+      $display("   -= Testbench for aes decipher block started =-");
+      $display("     ============================================");
+      $display("");
+
+      init_sim();
+      dump_dut_state();
+      reset_dut();
+      dump_dut_state();
+
+
+      // NIST 128 bit ECB tests.
+      key_mem[00] = 128'h2b7e151628aed2a6abf7158809cf4f3c;
+      key_mem[01] = 128'ha0fafe1788542cb123a339392a6c7605;
+      key_mem[02] = 128'hf2c295f27a96b9435935807a7359f67f;
+      key_mem[03] = 128'h3d80477d4716fe3e1e237e446d7a883b;
+      key_mem[04] = 128'hef44a541a8525b7fb671253bdb0bad00;
+      key_mem[05] = 128'hd4d1c6f87c839d87caf2b8bc11f915bc;
+      key_mem[06] = 128'h6d88a37a110b3efddbf98641ca0093fd;
+      key_mem[07] = 128'h4e54f70e5f5fc9f384a64fb24ea6dc4f;
+      key_mem[08] = 128'head27321b58dbad2312bf5607f8d292f;
+      key_mem[09] = 128'hac7766f319fadc2128d12941575c006e;
+      key_mem[10] = 128'hd014f9a8c9ee2589e13f0cc8b6630ca6;
+      key_mem[11] = 128'h00000000000000000000000000000000;
+      key_mem[12] = 128'h00000000000000000000000000000000;
+      key_mem[13] = 128'h00000000000000000000000000000000;
+      key_mem[14] = 128'h00000000000000000000000000000000;
+
+      test_ecb_dec(AES_128_BIT_KEY, nist_ecb_128_dec_ciphertext0, nist_plaintext0);
+      test_ecb_dec(AES_128_BIT_KEY, nist_ecb_128_dec_ciphertext1, nist_plaintext1);
+      test_ecb_dec(AES_128_BIT_KEY, nist_ecb_128_dec_ciphertext2, nist_plaintext2);
+      test_ecb_dec(AES_128_BIT_KEY, nist_ecb_128_dec_ciphertext3, nist_plaintext3);
+
+
+      // NIST 256 bit ECB tests.
+      key_mem[00] = 128'h603deb1015ca71be2b73aef0857d7781;
+      key_mem[01] = 128'h1f352c073b6108d72d9810a30914dff4;
+      key_mem[02] = 128'h9ba354118e6925afa51a8b5f2067fcde;
+      key_mem[03] = 128'ha8b09c1a93d194cdbe49846eb75d5b9a;
+      key_mem[04] = 128'hd59aecb85bf3c917fee94248de8ebe96;
+      key_mem[05] = 128'hb5a9328a2678a647983122292f6c79b3;
+      key_mem[06] = 128'h812c81addadf48ba24360af2fab8b464;
+      key_mem[07] = 128'h98c5bfc9bebd198e268c3ba709e04214;
+      key_mem[08] = 128'h68007bacb2df331696e939e46c518d80;
+      key_mem[09] = 128'hc814e20476a9fb8a5025c02d59c58239;
+      key_mem[10] = 128'hde1369676ccc5a71fa2563959674ee15;
+      key_mem[11] = 128'h5886ca5d2e2f31d77e0af1fa27cf73c3;
+      key_mem[12] = 128'h749c47ab18501ddae2757e4f7401905a;
+      key_mem[13] = 128'hcafaaae3e4d59b349adf6acebd10190d;
+      key_mem[14] = 128'hfe4890d1e6188d0b046df344706c631e;
+
+      test_ecb_dec(AES_256_BIT_KEY, nist_ecb_256_dec_ciphertext0, nist_plaintext0);
+      test_ecb_dec(AES_256_BIT_KEY, nist_ecb_256_dec_ciphertext1, nist_plaintext1);
+      test_ecb_dec(AES_256_BIT_KEY, nist_ecb_256_dec_ciphertext2, nist_plaintext2);
+      test_ecb_dec(AES_256_BIT_KEY, nist_ecb_256_dec_ciphertext3, nist_plaintext3);
+
+
+      display_test_result();
+      $display("");
+      $display("*** AES decipher block module simulation done. ***");
+      $finish;
+    end // aes_core_test
+endmodule // tb_aes_decipher_block
+
+//======================================================================
+// EOF tb_aes_decipher_block.v
+//======================================================================
diff --git a/src/tb/tb_aes_encipher_block.v b/src/tb/tb_aes_encipher_block.v
new file mode 100644
index 0000000..68e88dd
--- /dev/null
+++ b/src/tb/tb_aes_encipher_block.v
@@ -0,0 +1,422 @@
+//======================================================================
+//
+// tb_aes_encipher_block.v
+// -----------------------
+// Testbench for the AES encipher block module.
+//
+//
+// Author: Joachim Strombergson
+// 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.
+//
+//======================================================================
+
+//------------------------------------------------------------------
+// Test module.
+//------------------------------------------------------------------
+module tb_aes_encipher_block();
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter DEBUG     = 1;
+  parameter DUMP_WAIT = 0;
+
+  parameter CLK_HALF_PERIOD = 1;
+  parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
+
+  parameter AES_128_BIT_KEY = 0;
+  parameter AES_256_BIT_KEY = 1;
+
+  parameter AES_DECIPHER = 1'b0;
+  parameter AES_ENCIPHER = 1'b1;
+
+
+  //----------------------------------------------------------------
+  // Register and Wire declarations.
+  //----------------------------------------------------------------
+  reg [31 : 0]   cycle_ctr;
+  reg [31 : 0]   error_ctr;
+  reg [31 : 0]   tc_ctr;
+
+  reg            tb_clk;
+  reg            tb_reset_n;
+
+  reg            tb_next;
+  reg            tb_keylen;
+  wire           tb_ready;
+  wire [3 : 0]   tb_round;
+  wire [127 : 0] tb_round_key;
+
+  wire [31 : 0]  tb_sboxw;
+  wire [31 : 0]  tb_new_sboxw;
+
+  reg [127 : 0]  tb_block;
+  wire [127 : 0] tb_new_block;
+
+  reg [127 : 0] key_mem [0 : 14];
+
+
+  //----------------------------------------------------------------
+  // Assignments.
+  //----------------------------------------------------------------
+  assign tb_round_key = key_mem[tb_round];
+
+
+  //----------------------------------------------------------------
+  // Device Under Test.
+  //----------------------------------------------------------------
+  // We need an sbox for the tests.
+  aes_sbox sbox(
+                .sboxw(tb_sboxw),
+                .new_sboxw(tb_new_sboxw)
+               );
+
+
+  // The device under test.
+  aes_encipher_block dut(
+                         .clk(tb_clk),
+                         .reset_n(tb_reset_n),
+
+                         .next(tb_next),
+
+                         .keylen(tb_keylen),
+                         .round(tb_round),
+                         .round_key(tb_round_key),
+
+                         .sboxw(tb_sboxw),
+                         .new_sboxw(tb_new_sboxw),
+
+                         .block(tb_block),
+                         .new_block(tb_new_block),
+                         .ready(tb_ready)
+                        );
+
+
+  //----------------------------------------------------------------
+  // clk_gen
+  //
+  // Always running clock generator process.
+  //----------------------------------------------------------------
+  always
+    begin : clk_gen
+      #CLK_HALF_PERIOD;
+      tb_clk = !tb_clk;
+    end // clk_gen
+
+
+  //----------------------------------------------------------------
+  // sys_monitor()
+  //
+  // An always running process that creates a cycle counter and
+  // conditionally displays information about the DUT.
+  //----------------------------------------------------------------
+  always
+    begin : sys_monitor
+      cycle_ctr = cycle_ctr + 1;
+      #(CLK_PERIOD);
+      if (DEBUG)
+        begin
+          dump_dut_state();
+        end
+    end
+
+
+  //----------------------------------------------------------------
+  // dump_dut_state()
+  //
+  // Dump the state of the dump when needed.
+  //----------------------------------------------------------------
+  task dump_dut_state;
+    begin
+      $display("State of DUT");
+      $display("------------");
+      $display("Interfaces");
+      $display("ready = 0x%01x, next = 0x%01x, keylen = 0x%01x",
+               dut.ready, dut.next, dut.keylen);
+      $display("block     = 0x%032x", dut.block);
+      $display("new_block = 0x%032x", dut.new_block);
+      $display("");
+
+      $display("Control states");
+      $display("round = 0x%01x", dut.round);
+      $display("enc_ctrl = 0x%01x, update_type = 0x%01x, sword_ctr = 0x%01x, round_ctr = 0x%01x",
+               dut.enc_ctrl_reg, dut.update_type, dut.sword_ctr_reg, dut.round_ctr_reg);
+      $display("");
+
+      $display("Internal data values");
+      $display("round_key = 0x%016x", dut.round_key);
+      $display("sboxw = 0x%08x, new_sboxw = 0x%08x", dut.sboxw, dut.new_sboxw);
+      $display("block_w0_reg = 0x%08x, block_w1_reg = 0x%08x, block_w2_reg = 0x%08x, block_w3_reg = 0x%08x",
+               dut.block_w0_reg, dut.block_w1_reg, dut.block_w2_reg, dut.block_w3_reg);
+      $display("");
+      $display("old_block          = 0x%08x", dut.round_logic.old_block);
+      $display("shiftrows_block    = 0x%08x", dut.round_logic.shiftrows_block);
+      $display("mixcolumns_block   = 0x%08x", dut.round_logic.mixcolumns_block);
+      $display("addkey_init_block  = 0x%08x", dut.round_logic.addkey_init_block);
+      $display("addkey_main_block  = 0x%08x", dut.round_logic.addkey_main_block);
+      $display("addkey_final_block = 0x%08x", dut.round_logic.addkey_final_block);
+      $display("block_w0_new = 0x%08x, block_w1_new = 0x%08x, block_w2_new = 0x%08x, block_w3_new = 0x%08x",
+               dut.block_new[127 : 096], dut.block_new[095 : 064],
+               dut.block_new[063 : 032], dut.block_new[031 : 000]);
+      $display("");
+    end
+  endtask // dump_dut_state
+
+
+  //----------------------------------------------------------------
+  // reset_dut()
+  //
+  // Toggle reset to put the DUT into a well known state.
+  //----------------------------------------------------------------
+  task reset_dut;
+    begin
+      $display("*** Toggle reset.");
+      tb_reset_n = 0;
+      #(2 * CLK_PERIOD);
+      tb_reset_n = 1;
+    end
+  endtask // reset_dut
+
+
+  //----------------------------------------------------------------
+  // init_sim()
+  //
+  // Initialize all counters and testbed functionality as well
+  // as setting the DUT inputs to defined values.
+  //----------------------------------------------------------------
+  task init_sim;
+    begin
+      cycle_ctr    = 0;
+      error_ctr    = 0;
+      tc_ctr       = 0;
+
+      tb_clk       = 0;
+      tb_reset_n   = 1;
+
+      tb_next      = 0;
+      tb_keylen    = 0;
+
+      tb_block     = {4{32'h00000000}};
+    end
+  endtask // init_sim
+
+
+  //----------------------------------------------------------------
+  // display_test_result()
+  //
+  // Display the accumulated test results.
+  //----------------------------------------------------------------
+  task display_test_result;
+    begin
+      if (error_ctr == 0)
+        begin
+          $display("*** All %02d test cases completed successfully", tc_ctr);
+        end
+      else
+        begin
+          $display("*** %02d tests completed - %02d test cases did not complete successfully.",
+                   tc_ctr, error_ctr);
+        end
+    end
+  endtask // display_test_result
+
+
+  //----------------------------------------------------------------
+  // wait_ready()
+  //
+  // Wait for the ready flag in the dut to be set.
+  //
+  // Note: It is the callers responsibility to call the function
+  // when the dut is actively processing and will in fact at some
+  // point set the flag.
+  //----------------------------------------------------------------
+  task wait_ready;
+    begin
+      while (!tb_ready)
+        begin
+          #(CLK_PERIOD);
+          if (DUMP_WAIT)
+            begin
+              dump_dut_state();
+            end
+        end
+    end
+  endtask // wait_ready
+
+
+  //----------------------------------------------------------------
+  // test_ecb_enc()
+  //
+  // Perform ECB mode encryption test.
+  //----------------------------------------------------------------
+  task test_ecb_enc(
+                    input           key_length,
+                    input [127 : 0] block,
+                    input [127 : 0] expected);
+   begin
+     $display("*** TC %0d ECB mode test started.", tc_ctr);
+
+     // Init the cipher with the given key and length.
+     tb_keylen = key_length;
+
+     // Perform encipher operation on the block.
+     tb_block = block;
+     tb_next = 1;
+     #(2 * CLK_PERIOD);
+     tb_next = 0;
+     #(2 * CLK_PERIOD);
+
+     wait_ready();
+
+     if (tb_new_block == expected)
+       begin
+         $display("*** TC %0d successful.", tc_ctr);
+         $display("");
+       end
+     else
+       begin
+         $display("*** ERROR: TC %0d NOT successful.", tc_ctr);
+         $display("Expected: 0x%032x", expected);
+         $display("Got:      0x%032x", tb_new_block);
+         $display("");
+
+         error_ctr = error_ctr + 1;
+       end
+     tc_ctr = tc_ctr + 1;
+   end
+  endtask // ecb_mode_single_block_test
+
+
+  //----------------------------------------------------------------
+  // tb_aes_encipher_block
+  // The main test functionality.
+  //
+  // Test cases taken from NIST SP 800-38A:
+  // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
+  //----------------------------------------------------------------
+  initial
+    begin : tb_aes_encipher_block
+      reg [127 : 0] nist_plaintext0;
+      reg [127 : 0] nist_plaintext1;
+      reg [127 : 0] nist_plaintext2;
+      reg [127 : 0] nist_plaintext3;
+
+      reg [127 : 0] nist_ecb_128_enc_expected0;
+      reg [127 : 0] nist_ecb_128_enc_expected1;
+      reg [127 : 0] nist_ecb_128_enc_expected2;
+      reg [127 : 0] nist_ecb_128_enc_expected3;
+
+      reg [127 : 0] nist_ecb_256_enc_expected0;
+      reg [127 : 0] nist_ecb_256_enc_expected1;
+      reg [127 : 0] nist_ecb_256_enc_expected2;
+      reg [127 : 0] nist_ecb_256_enc_expected3;
+
+      nist_plaintext0 = 128'h6bc1bee22e409f96e93d7e117393172a;
+      nist_plaintext1 = 128'hae2d8a571e03ac9c9eb76fac45af8e51;
+      nist_plaintext2 = 128'h30c81c46a35ce411e5fbc1191a0a52ef;
+      nist_plaintext3 = 128'hf69f2445df4f9b17ad2b417be66c3710;
+
+      nist_ecb_128_enc_expected0 = 128'h3ad77bb40d7a3660a89ecaf32466ef97;
+      nist_ecb_128_enc_expected1 = 128'hf5d3d58503b9699de785895a96fdbaaf;
+      nist_ecb_128_enc_expected2 = 128'h43b1cd7f598ece23881b00e3ed030688;
+      nist_ecb_128_enc_expected3 = 128'h7b0c785e27e8ad3f8223207104725dd4;
+
+      nist_ecb_256_enc_expected0 = 255'hf3eed1bdb5d2a03c064b5a7e3db181f8;
+      nist_ecb_256_enc_expected1 = 255'h591ccb10d410ed26dc5ba74a31362870;
+      nist_ecb_256_enc_expected2 = 255'hb6ed21b99ca6f4f9f153e7b1beafed1d;
+      nist_ecb_256_enc_expected3 = 255'h23304b7a39f9f3ff067d8d8f9e24ecc7;
+
+
+      $display("   -= Testbench for aes encipher block started =-");
+      $display("     ============================================");
+      $display("");
+
+      init_sim();
+      dump_dut_state();
+      reset_dut();
+      dump_dut_state();
+
+
+      // NIST 128 bit ECB tests.
+      key_mem[00] = 128'h2b7e151628aed2a6abf7158809cf4f3c;
+      key_mem[01] = 128'ha0fafe1788542cb123a339392a6c7605;
+      key_mem[02] = 128'hf2c295f27a96b9435935807a7359f67f;
+      key_mem[03] = 128'h3d80477d4716fe3e1e237e446d7a883b;
+      key_mem[04] = 128'hef44a541a8525b7fb671253bdb0bad00;
+      key_mem[05] = 128'hd4d1c6f87c839d87caf2b8bc11f915bc;
+      key_mem[06] = 128'h6d88a37a110b3efddbf98641ca0093fd;
+      key_mem[07] = 128'h4e54f70e5f5fc9f384a64fb24ea6dc4f;
+      key_mem[08] = 128'head27321b58dbad2312bf5607f8d292f;
+      key_mem[09] = 128'hac7766f319fadc2128d12941575c006e;
+      key_mem[10] = 128'hd014f9a8c9ee2589e13f0cc8b6630ca6;
+      key_mem[11] = 128'h00000000000000000000000000000000;
+      key_mem[12] = 128'h00000000000000000000000000000000;
+      key_mem[13] = 128'h00000000000000000000000000000000;
+      key_mem[14] = 128'h00000000000000000000000000000000;
+
+      test_ecb_enc(AES_128_BIT_KEY, nist_plaintext0, nist_ecb_128_enc_expected0);
+      test_ecb_enc(AES_128_BIT_KEY, nist_plaintext1, nist_ecb_128_enc_expected1);
+      test_ecb_enc(AES_128_BIT_KEY, nist_plaintext2, nist_ecb_128_enc_expected2);
+      test_ecb_enc(AES_128_BIT_KEY, nist_plaintext3, nist_ecb_128_enc_expected3);
+
+
+      // NIST 256 bit ECB tests.
+      key_mem[00] = 128'h603deb1015ca71be2b73aef0857d7781;
+      key_mem[01] = 128'h1f352c073b6108d72d9810a30914dff4;
+      key_mem[02] = 128'h9ba354118e6925afa51a8b5f2067fcde;
+      key_mem[03] = 128'ha8b09c1a93d194cdbe49846eb75d5b9a;
+      key_mem[04] = 128'hd59aecb85bf3c917fee94248de8ebe96;
+      key_mem[05] = 128'hb5a9328a2678a647983122292f6c79b3;
+      key_mem[06] = 128'h812c81addadf48ba24360af2fab8b464;
+      key_mem[07] = 128'h98c5bfc9bebd198e268c3ba709e04214;
+      key_mem[08] = 128'h68007bacb2df331696e939e46c518d80;
+      key_mem[09] = 128'hc814e20476a9fb8a5025c02d59c58239;
+      key_mem[10] = 128'hde1369676ccc5a71fa2563959674ee15;
+      key_mem[11] = 128'h5886ca5d2e2f31d77e0af1fa27cf73c3;
+      key_mem[12] = 128'h749c47ab18501ddae2757e4f7401905a;
+      key_mem[13] = 128'hcafaaae3e4d59b349adf6acebd10190d;
+      key_mem[14] = 128'hfe4890d1e6188d0b046df344706c631e;
+
+      test_ecb_enc(AES_256_BIT_KEY, nist_plaintext0, nist_ecb_256_enc_expected0);
+      test_ecb_enc(AES_256_BIT_KEY, nist_plaintext1, nist_ecb_256_enc_expected1);
+      test_ecb_enc(AES_256_BIT_KEY, nist_plaintext2, nist_ecb_256_enc_expected2);
+      test_ecb_enc(AES_256_BIT_KEY, nist_plaintext3, nist_ecb_256_enc_expected3);
+
+
+      display_test_result();
+      $display("");
+      $display("*** AES encipher block module simulation done. ***");
+      $finish;
+    end // aes_core_test
+endmodule // tb_aes_encipher_block
+
+//======================================================================
+// EOF tb_aes_encipher_block.v
+//======================================================================
diff --git a/src/tb/tb_aes_key_mem.v b/src/tb/tb_aes_key_mem.v
new file mode 100644
index 0000000..cac216a
--- /dev/null
+++ b/src/tb/tb_aes_key_mem.v
@@ -0,0 +1,650 @@
+//======================================================================
+//
+// tb_aes_key_mem.v
+// ----------------
+// Testbench for the AES key memory module.
+//
+//
+// Author: Joachim Strombergson
+// 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.
+//
+//======================================================================
+
+//------------------------------------------------------------------
+// Test module.
+//------------------------------------------------------------------
+module tb_aes_key_mem();
+
+  //----------------------------------------------------------------
+  // Internal constant and parameter definitions.
+  //----------------------------------------------------------------
+  parameter DEBUG = 1;
+  parameter SHOW_SBOX = 0;
+
+  parameter CLK_HALF_PERIOD = 1;
+  parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
+
+  parameter AES_128_BIT_KEY = 0;
+  parameter AES_256_BIT_KEY = 1;
+
+  parameter AES_128_NUM_ROUNDS = 10;
+  parameter AES_192_NUM_ROUNDS = 12;
+  parameter AES_256_NUM_ROUNDS = 14;
+
+  parameter AES_DECIPHER = 1'b0;
+  parameter AES_ENCIPHER = 1'b1;
+
+
+  //----------------------------------------------------------------
+  // Register and Wire declarations.
+  //----------------------------------------------------------------
+  reg [31 : 0] cycle_ctr;
+  reg [31 : 0] error_ctr;
+  reg [31 : 0] tc_ctr;
+
+  reg            tb_clk;
+  reg            tb_reset_n;
+  reg [255 : 0]  tb_key;
+  reg            tb_keylen;
+  reg            tb_init;
+  reg [3 : 0]    tb_round;
+  wire [127 : 0] tb_round_key;
+  wire           tb_ready;
+
+  wire [31 : 0]  tb_sboxw;
+  wire [31 : 0]  tb_new_sboxw;
+
+
+  //----------------------------------------------------------------
+  // Device Under Test.
+  //----------------------------------------------------------------
+  aes_key_mem dut(
+                  .clk(tb_clk),
+                  .reset_n(tb_reset_n),
+
+                  .key(tb_key),
+                  .keylen(tb_keylen),
+                  .init(tb_init),
+
+                  .round(tb_round),
+                  .round_key(tb_round_key),
+                  .ready(tb_ready),
+
+                  .sboxw(tb_sboxw),
+                  .new_sboxw(tb_new_sboxw)
+                 );
+
+  // The DUT requirees Sboxes.
+  aes_sbox sbox(.sboxw(tb_sboxw), .new_sboxw(tb_new_sboxw));
+
+
+  //----------------------------------------------------------------
+  // clk_gen
+  //
+  // Always running clock generator process.
+  //----------------------------------------------------------------
+  always
+    begin : clk_gen
+      #CLK_HALF_PERIOD;
+      tb_clk = !tb_clk;
+    end // clk_gen
+
+
+  //----------------------------------------------------------------
+  // sys_monitor()
+  //
+  // An always running process that creates a cycle counter and
+  // conditionally displays information about the DUT.
+  //----------------------------------------------------------------
+  always
+    begin : sys_monitor
+      cycle_ctr = cycle_ctr + 1;
+      #(CLK_PERIOD);
+      if (DEBUG)
+        begin
+          dump_dut_state();
+        end
+    end
+
+
+  //----------------------------------------------------------------
+  // dump_dut_state()
+  //
+  // Dump the state of the dump when needed.
+  //----------------------------------------------------------------
+  task dump_dut_state;
+    begin
+      $display("State of DUT");
+      $display("------------");
+      $display("Inputs and outputs:");
+      $display("key       = 0x%032x", dut.key);
+      $display("keylen    = 0x%01x, init = 0x%01x, ready = 0x%01x",
+               dut.keylen, dut.init, dut.ready);
+      $display("round     = 0x%02x", dut.round);
+      $display("round_key = 0x%016x", dut.round_key);
+      $display("");
+
+      $display("Internal states:");
+      $display("key_mem_ctrl = 0x%01x, round_key_update = 0x%01x, round_ctr_reg = 0x%01x, rcon_reg = 0x%01x",
+               dut.key_mem_ctrl_reg, dut.round_key_update, dut.round_ctr_reg, dut.rcon_reg);
+
+      $display("prev_key0_reg = 0x%016x, prev_key0_new = 0x%016x, prev_key0_we = 0x%01x",
+               dut.prev_key0_reg, dut.prev_key0_new, dut.prev_key0_we);
+      $display("prev_key1_reg = 0x%016x, prev_key1_new = 0x%016x, prev_key1_we = 0x%01x",
+               dut.prev_key1_reg, dut.prev_key1_new, dut.prev_key1_we);
+
+      $display("w0 = 0x%04x, w1 = 0x%04x, w2 = 0x%04x, w3 = 0x%04x",
+               dut.round_key_gen.w0, dut.round_key_gen.w1,
+               dut.round_key_gen.w2, dut.round_key_gen.w3);
+      $display("w4 = 0x%04x, w5 = 0x%04x, w6 = 0x%04x, w7 = 0x%04x",
+               dut.round_key_gen.w4, dut.round_key_gen.w5,
+               dut.round_key_gen.w6, dut.round_key_gen.w7);
+      $display("sboxw = 0x%04x, new_sboxw = 0x%04x, rconw = 0x%04x",
+               dut.sboxw, dut.new_sboxw, dut.round_key_gen.rconw);
+      $display("tw = 0x%04x, trw = 0x%04x", dut.round_key_gen.tw, dut.round_key_gen.trw);
+      $display("key_mem_new = 0x%016x, key_mem_we = 0x%01x",
+               dut.key_mem_new, dut.key_mem_we);
+      $display("");
+
+      if (SHOW_SBOX)
+        begin
+          $display("Sbox functionality:");
+          $display("sboxw = 0x%08x", sbox.sboxw);
+          $display("tmp_new_sbox0 = 0x%02x, tmp_new_sbox1 = 0x%02x, tmp_new_sbox2 = 0x%02x, tmp_new_sbox3",
+                   sbox.tmp_new_sbox0, sbox.tmp_new_sbox1, sbox.tmp_new_sbox2, sbox.tmp_new_sbox3);
+          $display("new_sboxw = 0x%08x", sbox.new_sboxw);
+          $display("");
+        end
+    end
+  endtask // dump_dut_state
+
+
+  //----------------------------------------------------------------
+  // reset_dut()
+  //
+  // Toggle reset to put the DUT into a well known state.
+  //----------------------------------------------------------------
+  task reset_dut;
+    begin
+      $display("*** Toggle reset.");
+      tb_reset_n = 0;
+      #(2 * CLK_PERIOD);
+      tb_reset_n = 1;
+    end
+  endtask // reset_dut
+
+
+  //----------------------------------------------------------------
+  // init_sim()
+  //
+  // Initialize all counters and testbed functionality as well
+  // as setting the DUT inputs to defined values.
+  //----------------------------------------------------------------
+  task init_sim;
+    begin
+      cycle_ctr = 0;
+      error_ctr = 0;
+      tc_ctr    = 0;
+
+      tb_clk     = 0;
+      tb_reset_n = 1;
+      tb_key     = {8{32'h00000000}};
+      tb_keylen  = 0;
+      tb_init    = 0;
+      tb_round   = 4'h0;
+    end
+  endtask // init_sim
+
+
+  //----------------------------------------------------------------
+  // wait_ready()
+  //
+  // Wait for the ready flag in the dut to be set.
+  //
+  // Note: It is the callers responsibility to call the function
+  // when the dut is actively processing and will in fact at some
+  // point set the flag.
+  //----------------------------------------------------------------
+  task wait_ready;
+    begin
+      while (!tb_ready)
+        begin
+          #(CLK_PERIOD);
+        end
+    end
+  endtask // wait_ready
+
+
+  //----------------------------------------------------------------
+  // check_key()
+  //
+  // Check a given key in the dut key memory against a given
+  // expected key.
+  //----------------------------------------------------------------
+  task check_key(input [3 : 0] key_nr, input [127 : 0] expected);
+    begin
+      tb_round = key_nr;
+      #(CLK_PERIOD);
+      if (tb_round_key == expected)
+        begin
+          $display("** key 0x%01x matched expected round key.", key_nr);
+          $display("** Got:      0x%016x **", tb_round_key);
+        end
+      else
+        begin
+          $display("** Error: key 0x%01x did not match expected round key. **", key_nr);
+          $display("** Expected: 0x%016x **", expected);
+          $display("** Got:      0x%016x **", tb_round_key);
+          error_ctr = error_ctr + 1;
+        end
+      $display("");
+    end
+  endtask // check_key
+
+
+  //----------------------------------------------------------------
+  // test_key_128()
+  //
+  // Test 128 bit keys. Due to array problems, the result check
+  // is fairly ugly.
+  //----------------------------------------------------------------
+  task test_key_128(input [255 : 0] key,
+                    input [127 : 0] expected00,
+                    input [127 : 0] expected01,
+                    input [127 : 0] expected02,
+                    input [127 : 0] expected03,
+                    input [127 : 0] expected04,
+                    input [127 : 0] expected05,
+                    input [127 : 0] expected06,
+                    input [127 : 0] expected07,
+                    input [127 : 0] expected08,
+                    input [127 : 0] expected09,
+                    input [127 : 0] expected10
+                   );
+    begin
+      $display("** Testing with 128-bit key 0x%16x", key[255 : 128]);
+      $display("");
+
+      tb_key = key;
+      tb_keylen = AES_128_BIT_KEY;
+      tb_init = 1;
+      #(2 * CLK_PERIOD);
+      tb_init = 0;
+      wait_ready();
+
+      check_key(4'h0, expected00);
+      check_key(4'h1, expected01);
+      check_key(4'h2, expected02);
+      check_key(4'h3, expected03);
+      check_key(4'h4, expected04);
+      check_key(4'h5, expected05);
+      check_key(4'h6, expected06);
+      check_key(4'h7, expected07);
+      check_key(4'h8, expected08);
+      check_key(4'h9, expected09);
+      check_key(4'ha, expected10);
+
+      tc_ctr = tc_ctr + 1;
+    end
+  endtask // test_key_128
+
+
+  //----------------------------------------------------------------
+  // test_key_256()
+  //
+  // Test 256 bit keys. Due to array problems, the result check
+  // is fairly ugly.
+  //----------------------------------------------------------------
+  task test_key_256(input [255 : 0] key,
+                    input [127 : 0] expected00,
+                    input [127 : 0] expected01,
+                    input [127 : 0] expected02,
+                    input [127 : 0] expected03,
+                    input [127 : 0] expected04,
+                    input [127 : 0] expected05,
+                    input [127 : 0] expected06,
+                    input [127 : 0] expected07,
+                    input [127 : 0] expected08,
+                    input [127 : 0] expected09,
+                    input [127 : 0] expected10,
+                    input [127 : 0] expected11,
+                    input [127 : 0] expected12,
+                    input [127 : 0] expected13,
+                    input [127 : 0] expected14
+                   );
+    begin
+      $display("** Testing with 256-bit key 0x%32x", key[255 : 000]);
+      $display("");
+
+      tb_key = key;
+      tb_keylen = AES_256_BIT_KEY;
+      tb_init = 1;
+      #(2 * CLK_PERIOD);
+      tb_init = 0;
+
+      wait_ready();
+
+      check_key(4'h0, expected00);
+      check_key(4'h1, expected01);
+      check_key(4'h2, expected02);
+      check_key(4'h3, expected03);
+      check_key(4'h4, expected04);
+      check_key(4'h5, expected05);
+      check_key(4'h6, expected06);
+      check_key(4'h7, expected07);
+      check_key(4'h8, expected08);
+      check_key(4'h9, expected09);
+      check_key(4'ha, expected10);
+      check_key(4'hb, expected11);
+      check_key(4'hc, expected12);
+      check_key(4'hd, expected13);
+      check_key(4'he, expected14);
+
+      tc_ctr = tc_ctr + 1;
+    end
+  endtask // test_key_256
+
+
+  //----------------------------------------------------------------
+  // display_test_result()
+  //
+  // Display the accumulated test results.
+  //----------------------------------------------------------------
+  task display_test_result;
+    begin
+      if (error_ctr == 0)
+        begin
+          $display("*** All %02d test cases completed successfully", tc_ctr);
+        end
+      else
+        begin
+          $display("*** %02d tests completed - %02d test cases did not complete successfully.",
+                   tc_ctr, error_ctr);
+        end
+    end
+  endtask // display_test_result
+
+
+  //----------------------------------------------------------------
+  // aes_key_mem_test
+  // The main test functionality.
+  //----------------------------------------------------------------
+  initial
+    begin : aes_key_mem_test
+      reg [255 : 0] key128_0;
+      reg [255 : 0] key128_1;
+      reg [255 : 0] key128_2;
+      reg [255 : 0] key128_3;
+      reg [255 : 0] nist_key128;
+      reg [255 : 0] key256_0;
+      reg [255 : 0] key256_1;
+      reg [255 : 0] key256_2;
+      reg [255 : 0] nist_key256;
+
+      reg [127 : 0] expected_00;
+      reg [127 : 0] expected_01;
+      reg [127 : 0] expected_02;
+      reg [127 : 0] expected_03;
+      reg [127 : 0] expected_04;
+      reg [127 : 0] expected_05;
+      reg [127 : 0] expected_06;
+      reg [127 : 0] expected_07;
+      reg [127 : 0] expected_08;
+      reg [127 : 0] expected_09;
+      reg [127 : 0] expected_10;
+      reg [127 : 0] expected_11;
+      reg [127 : 0] expected_12;
+      reg [127 : 0] expected_13;
+      reg [127 : 0] expected_14;
+
+      $display("   -= Testbench for aes key mem started =-");
+      $display("    =====================================");
+      $display("");
+
+      init_sim();
+      dump_dut_state();
+      reset_dut();
+
+      $display("State after reset:");
+      dump_dut_state();
+      $display("");
+
+      #(100 *CLK_PERIOD);
+
+      // AES-128 test case 1 key and expected values.
+      key128_0    = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+      expected_00 = 128'h00000000000000000000000000000000;
+      expected_01 = 128'h62636363626363636263636362636363;
+      expected_02 = 128'h9b9898c9f9fbfbaa9b9898c9f9fbfbaa;
+      expected_03 = 128'h90973450696ccffaf2f457330b0fac99;
+      expected_04 = 128'hee06da7b876a1581759e42b27e91ee2b;
+      expected_05 = 128'h7f2e2b88f8443e098dda7cbbf34b9290;
+      expected_06 = 128'hec614b851425758c99ff09376ab49ba7;
+      expected_07 = 128'h217517873550620bacaf6b3cc61bf09b;
+      expected_08 = 128'h0ef903333ba9613897060a04511dfa9f;
+      expected_09 = 128'hb1d4d8e28a7db9da1d7bb3de4c664941;
+      expected_10 = 128'hb4ef5bcb3e92e21123e951cf6f8f188e;
+
+      test_key_128(key128_0,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10);
+
+
+      // AES-128 test case 2 key and expected values.
+      key128_1    = 256'hffffffffffffffffffffffffffffffff00000000000000000000000000000000;
+      expected_00 = 128'hffffffffffffffffffffffffffffffff;
+      expected_01 = 128'he8e9e9e917161616e8e9e9e917161616;
+      expected_02 = 128'hadaeae19bab8b80f525151e6454747f0;
+      expected_03 = 128'h090e2277b3b69a78e1e7cb9ea4a08c6e;
+      expected_04 = 128'he16abd3e52dc2746b33becd8179b60b6;
+      expected_05 = 128'he5baf3ceb766d488045d385013c658e6;
+      expected_06 = 128'h71d07db3c6b6a93bc2eb916bd12dc98d;
+      expected_07 = 128'he90d208d2fbb89b6ed5018dd3c7dd150;
+      expected_08 = 128'h96337366b988fad054d8e20d68a5335d;
+      expected_09 = 128'h8bf03f233278c5f366a027fe0e0514a3;
+      expected_10 = 128'hd60a3588e472f07b82d2d7858cd7c326;
+
+      test_key_128(key128_1,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10);
+
+
+      // AES-128 test case 3 key and expected values.
+      key128_2    = 256'h000102030405060708090a0b0c0d0e0f00000000000000000000000000000000;
+      expected_00 = 128'h000102030405060708090a0b0c0d0e0f;
+      expected_01 = 128'hd6aa74fdd2af72fadaa678f1d6ab76fe;
+      expected_02 = 128'hb692cf0b643dbdf1be9bc5006830b3fe;
+      expected_03 = 128'hb6ff744ed2c2c9bf6c590cbf0469bf41;
+      expected_04 = 128'h47f7f7bc95353e03f96c32bcfd058dfd;
+      expected_05 = 128'h3caaa3e8a99f9deb50f3af57adf622aa;
+      expected_06 = 128'h5e390f7df7a69296a7553dc10aa31f6b;
+      expected_07 = 128'h14f9701ae35fe28c440adf4d4ea9c026;
+      expected_08 = 128'h47438735a41c65b9e016baf4aebf7ad2;
+      expected_09 = 128'h549932d1f08557681093ed9cbe2c974e;
+      expected_10 = 128'h13111d7fe3944a17f307a78b4d2b30c5;
+
+      test_key_128(key128_2,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10);
+
+
+      // AES-128 test case 4 key and expected values.
+      key128_3    = 256'h6920e299a5202a6d656e636869746f2a00000000000000000000000000000000;
+      expected_00 = 128'h6920e299a5202a6d656e636869746f2a;
+      expected_01 = 128'hfa8807605fa82d0d3ac64e6553b2214f;
+      expected_02 = 128'hcf75838d90ddae80aa1be0e5f9a9c1aa;
+      expected_03 = 128'h180d2f1488d0819422cb6171db62a0db;
+      expected_04 = 128'hbaed96ad323d173910f67648cb94d693;
+      expected_05 = 128'h881b4ab2ba265d8baad02bc36144fd50;
+      expected_06 = 128'hb34f195d096944d6a3b96f15c2fd9245;
+      expected_07 = 128'ha7007778ae6933ae0dd05cbbcf2dcefe;
+      expected_08 = 128'hff8bccf251e2ff5c5c32a3e7931f6d19;
+      expected_09 = 128'h24b7182e7555e77229674495ba78298c;
+      expected_10 = 128'hae127cdadb479ba8f220df3d4858f6b1;
+
+      test_key_128(key128_3,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10);
+
+
+      // NIST AES-128 test case.
+      nist_key128 = 256'h2b7e151628aed2a6abf7158809cf4f3c00000000000000000000000000000000;
+      expected_00 = 128'h2b7e151628aed2a6abf7158809cf4f3c;
+      expected_01 = 128'ha0fafe1788542cb123a339392a6c7605;
+      expected_02 = 128'hf2c295f27a96b9435935807a7359f67f;
+      expected_03 = 128'h3d80477d4716fe3e1e237e446d7a883b;
+      expected_04 = 128'hef44a541a8525b7fb671253bdb0bad00;
+      expected_05 = 128'hd4d1c6f87c839d87caf2b8bc11f915bc;
+      expected_06 = 128'h6d88a37a110b3efddbf98641ca0093fd;
+      expected_07 = 128'h4e54f70e5f5fc9f384a64fb24ea6dc4f;
+      expected_08 = 128'head27321b58dbad2312bf5607f8d292f;
+      expected_09 = 128'hac7766f319fadc2128d12941575c006e;
+      expected_10 = 128'hd014f9a8c9ee2589e13f0cc8b6630ca6;
+
+      $display("Testing the NIST AES-128 key.");
+      test_key_128(nist_key128,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10);
+
+
+      // AES-256 test case 1 key and expected values.
+      key256_0    = 256'h000000000000000000000000000000000000000000000000000000000000000;
+      expected_00 = 128'h00000000000000000000000000000000;
+      expected_01 = 128'h00000000000000000000000000000000;
+      expected_02 = 128'h62636363626363636263636362636363;
+      expected_03 = 128'haafbfbfbaafbfbfbaafbfbfbaafbfbfb;
+      expected_04 = 128'h6f6c6ccf0d0f0fac6f6c6ccf0d0f0fac;
+      expected_05 = 128'h7d8d8d6ad77676917d8d8d6ad7767691;
+      expected_06 = 128'h5354edc15e5be26d31378ea23c38810e;
+      expected_07 = 128'h968a81c141fcf7503c717a3aeb070cab;
+      expected_08 = 128'h9eaa8f28c0f16d45f1c6e3e7cdfe62e9;
+      expected_09 = 128'h2b312bdf6acddc8f56bca6b5bdbbaa1e;
+      expected_10 = 128'h6406fd52a4f79017553173f098cf1119;
+      expected_11 = 128'h6dbba90b0776758451cad331ec71792f;
+      expected_12 = 128'he7b0e89c4347788b16760b7b8eb91a62;
+      expected_13 = 128'h74ed0ba1739b7e252251ad14ce20d43b;
+      expected_14 = 128'h10f80a1753bf729c45c979e7cb706385;
+
+      test_key_256(key256_0,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10, expected_11,
+                   expected_12, expected_13, expected_14);
+
+
+      // AES-256 test case 2 key and expected values.
+      key256_1    = 256'hffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
+      expected_00 = 128'hffffffffffffffffffffffffffffffff;
+      expected_01 = 128'hffffffffffffffffffffffffffffffff;
+      expected_02 = 128'he8e9e9e917161616e8e9e9e917161616;
+      expected_03 = 128'h0fb8b8b8f04747470fb8b8b8f0474747;
+      expected_04 = 128'h4a4949655d5f5f73b5b6b69aa2a0a08c;
+      expected_05 = 128'h355858dcc51f1f9bcaa7a7233ae0e064;
+      expected_06 = 128'hafa80ae5f2f755964741e30ce5e14380;
+      expected_07 = 128'heca0421129bf5d8ae318faa9d9f81acd;
+      expected_08 = 128'he60ab7d014fde24653bc014ab65d42ca;
+      expected_09 = 128'ha2ec6e658b5333ef684bc946b1b3d38b;
+      expected_10 = 128'h9b6c8a188f91685edc2d69146a702bde;
+      expected_11 = 128'ha0bd9f782beeac9743a565d1f216b65a;
+      expected_12 = 128'hfc22349173b35ccfaf9e35dbc5ee1e05;
+      expected_13 = 128'h0695ed132d7b41846ede24559cc8920f;
+      expected_14 = 128'h546d424f27de1e8088402b5b4dae355e;
+
+      test_key_256(key256_1,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10, expected_11,
+                   expected_12, expected_13, expected_14);
+
+
+      // AES-256 test case 3 key and expected values.
+      key256_2    = 256'h000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f;
+      expected_00 = 128'h000102030405060708090a0b0c0d0e0f;
+      expected_01 = 128'h101112131415161718191a1b1c1d1e1f;
+      expected_02 = 128'ha573c29fa176c498a97fce93a572c09c;
+      expected_03 = 128'h1651a8cd0244beda1a5da4c10640bade;
+      expected_04 = 128'hae87dff00ff11b68a68ed5fb03fc1567;
+      expected_05 = 128'h6de1f1486fa54f9275f8eb5373b8518d;
+      expected_06 = 128'hc656827fc9a799176f294cec6cd5598b;
+      expected_07 = 128'h3de23a75524775e727bf9eb45407cf39;
+      expected_08 = 128'h0bdc905fc27b0948ad5245a4c1871c2f;
+      expected_09 = 128'h45f5a66017b2d387300d4d33640a820a;
+      expected_10 = 128'h7ccff71cbeb4fe5413e6bbf0d261a7df;
+      expected_11 = 128'hf01afafee7a82979d7a5644ab3afe640;
+      expected_12 = 128'h2541fe719bf500258813bbd55a721c0a;
+      expected_13 = 128'h4e5a6699a9f24fe07e572baacdf8cdea;
+      expected_14 = 128'h24fc79ccbf0979e9371ac23c6d68de36;
+
+      test_key_256(key256_2,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10, expected_11,
+                   expected_12, expected_13, expected_14);
+
+
+      nist_key256 = 256'h603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4;
+      expected_00 = 128'h603deb1015ca71be2b73aef0857d7781;
+      expected_01 = 128'h1f352c073b6108d72d9810a30914dff4;
+      expected_02 = 128'h9ba354118e6925afa51a8b5f2067fcde;
+      expected_03 = 128'ha8b09c1a93d194cdbe49846eb75d5b9a;
+      expected_04 = 128'hd59aecb85bf3c917fee94248de8ebe96;
+      expected_05 = 128'hb5a9328a2678a647983122292f6c79b3;
+      expected_06 = 128'h812c81addadf48ba24360af2fab8b464;
+      expected_07 = 128'h98c5bfc9bebd198e268c3ba709e04214;
+      expected_08 = 128'h68007bacb2df331696e939e46c518d80;
+      expected_09 = 128'hc814e20476a9fb8a5025c02d59c58239;
+      expected_10 = 128'hde1369676ccc5a71fa2563959674ee15;
+      expected_11 = 128'h5886ca5d2e2f31d77e0af1fa27cf73c3;
+      expected_12 = 128'h749c47ab18501ddae2757e4f7401905a;
+      expected_13 = 128'hcafaaae3e4d59b349adf6acebd10190d;
+      expected_14 = 128'hfe4890d1e6188d0b046df344706c631e;
+
+      test_key_256(nist_key256,
+                   expected_00, expected_01, expected_02, expected_03,
+                   expected_04, expected_05, expected_06, expected_07,
+                   expected_08, expected_09, expected_10, expected_11,
+                   expected_12, expected_13, expected_14);
+
+
+      display_test_result();
+      $display("");
+      $display("*** AES core simulation done. ***");
+      $finish;
+    end // aes_key_mem_test
+endmodule // tb_aes_key_mem
+
+//======================================================================
+// EOF tb_aes_key_mem.v
+//======================================================================
diff --git a/toolruns/Makefile b/toolruns/Makefile
new file mode 100755
index 0000000..835a92f
--- /dev/null
+++ b/toolruns/Makefile
@@ -0,0 +1,133 @@
+#===================================================================
+#
+# Makefile
+# --------
+# Makefile for building the aes keygen, core and top simulations.
+#
+#
+# Author: Joachim Strombergson
+# 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.
+#
+#===================================================================
+
+SBOX_SRC=../src/rtl/aes_sbox.v
+INV_SBOX_SRC=../src/rtl/aes_inv_sbox.v
+KEYMEM_SRC=../src/rtl/aes_key_mem.v
+ENCIPHER_SRC=../src/rtl/aes_encipher_block.v
+DECIPHER_SRC=../src/rtl/aes_decipher_block.v
+CORE_SRC=../src/rtl/aes_core.v $(KEYMEM_SRC) $(SBOX_SRC) $(INV_SBOX_SRC) $(ENCIPHER_SRC) $(DECIPHER_SRC)
+TOP_SRC=../src/rtl/aes.v $(CORE_SRC)
+
+TB_TOP_SRC =../src/tb/tb_aes.v
+TB_CORE_SRC =../src/tb/tb_aes_core.v
+TB_KEYMEM_SRC =../src/tb/tb_aes_key_mem.v
+TB_ENCIPHER_SRC =../src/tb/tb_aes_encipher_block.v
+TB_DECIPHER_SRC =../src/tb/tb_aes_decipher_block.v
+
+CC=iverilog
+LINT=verilator
+
+
+all: top.sim core.sim keymem.sim encipher.sim decipher.sim
+
+top.sim: $(TB_TOP_SRC) $(TOP_SRC)
+	$(CC) -o top.sim $(TB_TOP_SRC) $(TOP_SRC)
+
+
+core.sim: $(TB_CORE_SRC) $(CORE_SRC)
+	$(CC) -o core.sim $(TB_CORE_SRC) $(CORE_SRC)
+
+
+keymem.sim:  $(TB_KEYMEM_SRC) $(KEYGEN_SRC) $(SBOX_SRC)
+	$(CC) -o keymem.sim $(TB_KEYMEM_SRC) $(KEYMEM_SRC) $(SBOX_SRC)
+
+
+encipher.sim:  $(TB_ENCIPHER_SRC) $(ENCIPHER_SRC) $(SBOX_SRC)
+	$(CC) -o encipher.sim $(TB_ENCIPHER_SRC) $(ENCIPHER_SRC) $(SBOX_SRC)
+
+
+decipher.sim:  $(TB_DECIPHER_SRC) $(DECIPHER_SRC) $(INV_SBOX_SRC)
+	$(CC) -o decipher.sim $(TB_DECIPHER_SRC) $(DECIPHER_SRC) $(INV_SBOX_SRC)
+
+
+sim-keymem: keymem.sim
+	./keymem.sim
+
+
+sim-encipher: encipher.sim
+	./encipher.sim
+
+
+sim-decipher: decipher.sim
+	./decipher.sim
+
+
+sim-core: core.sim
+	./core.sim
+
+
+sim-top: top.sim
+	./top.sim
+
+
+lint:
+	verilator +1364-2001ext+ --lint-only -Wall $(TOP_SRC)
+
+
+clean:
+	rm -f decipher.sim
+	rm -f encipher.sim
+	rm -f keymem.sim
+	rm -f core.sim
+	rm -f top.sim
+
+
+help:
+	@echo "Build system for simulation of AES Verilog core"
+	@echo ""
+	@echo "Supported targets:"
+	@echo "------------------"
+	@echo "all:          Build all simulation targets."
+	@echo "lint:         Lint all rtl source files."
+	@echo "top.sim:      Build top level simulation target."
+	@echo "core.sim:     Build core level simulation target."
+	@echo "keymem.sim:   Build key memory simulation target."
+	@echo "encipher.sim: Build encipher block simulation target."
+	@echo "decipher.sim: Build decipher block simulation target."
+	@echo "sim-top:      Run top level simulation."
+	@echo "sim-core:     Run core level simulation."
+	@echo "sim-keymem    Run keymem simulation."
+	@echo "sim-encipher  Run encipher block simulation."
+	@echo "sim-decipher  Run decipher block simulation."
+	@echo "clean:        Delete all built files."
+
+#===================================================================
+# EOF Makefile
+#===================================================================



More information about the Commits mailing list