//======================================================================
//
// trng_mixer.v
// ------------
// Mixer for the TRNG.
//
//
// 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 trng_mixer(
input wire clk,
input wire reset_n,
input wire cs,
input wire we,
input wire [7 : 0] address,
input wire [31 : 0] write_data,
output wire [31 : 0] read_data,
output wire error,
input wire discard,
input wire test_mode,
output wire security_error,
input wire more_seed,
input wire entropy0_enabled,
input wire entropy0_syn,
input wire [31 : 0] entropy0_data,
output wire entropy0_ack,
input wire entropy1_enabled,
input wire entropy1_syn,
input wire [31 : 0] entropy1_data,
output wire entropy1_ack,
input wire entropy2_enabled,
input wire entropy2_syn,
input wire [31 : 0] entropy2_data,
output wire entropy2_ack,
output wire [511 : 0] seed_data,
output wire seed_syn,
input wire seed_ack,
output wire [7 : 0] debug,
input wire debug_update
);
//----------------------------------------------------------------
// Internal constant and parameter definitions.
//----------------------------------------------------------------
localparam MODE_SHA_512 = 2'h3;
localparam ENTROPY_IDLE = 4'h0;
localparam ENTROPY_SRC0 = 4'h1;
localparam ENTROPY_SRC0_ACK = 4'h2;
localparam ENTROPY_SRC1 = 4'h3;
localparam ENTROPY_SRC1_ACK = 4'h4;
localparam ENTROPY_SRC2 = 4'h5;
localparam ENTROPY_SRC2_ACK = 4'h6;
localparam TEST_ENTROPY = 4'he;
localparam TEST_ENTROPY_ACK = 4'hf;
localparam CTRL_IDLE = 4'h0;
localparam CTRL_COLLECT = 4'h1;
localparam CTRL_MIX = 4'h2;
localparam CTRL_SYN = 4'h3;
localparam CTRL_ACK = 4'h4;
localparam CTRL_NEXT = 4'h5;
localparam ADDR_NAME0 = 8'h00;
localparam ADDR_NAME1 = 8'h01;
localparam ADDR_VERSION = 8'h02;
localparam ADDR_MIXER_CTRL = 8'h08;
localparam MIXER_CTRL_ENABLE_BIT = 0;
localparam MIXER_CTRL_RESTART_BIT = 1;
localparam ADDR_MIXER_STATUS = 8'h09;
localparam ADDR_MIXER_TIMEOUT = 8'h20;
localparam DEFAULT_ENTROPY_TIMEOUT = 24'h100000;
parameter CORE_NAME0 = 32'h726e676d; // "rngm"
parameter CORE_NAME1 = 32'h69786572; // "ixer"
parameter CORE_VERSION = 32'h302e3530; // "0.50"
//----------------------------------------------------------------
// Registers including update variables and write enable.
//----------------------------------------------------------------
reg [31 : 0] block_mem [0 : 31];
reg [4 : 0] word_ctr_reg;
reg [4 : 0] word_ctr_new;
reg word_ctr_inc;
reg word_ctr_rst;
reg word_ctr_we;
reg [3 : 0] entropy_collect_ctrl_reg;
reg [3 : 0] entropy_collect_ctrl_new;
reg entropy_collect_ctrl_we;
reg [23 : 0] entropy_timeout_ctr_reg;
reg [23 : 0] entropy_timeout_ctr_new;
reg entropy_timeout_ctr_inc;
reg entropy_timeout_ctr_rst;
reg entropy_timeout_ctr_we;
reg entropy_timeout;
reg [23 : 0] entropy_timeout_reg;
reg [23 : 0] entropy_timeout_new;
reg entropy_timeout_we;
reg [3 : 0] mixer_ctrl_reg;
reg [3 : 0] mixer_ctrl_new;
reg mixer_ctrl_we;
reg seed_syn_reg;
reg seed_syn_new;
reg seed_syn_we;
reg init_done_reg;
reg init_done_new;
reg init_done_we;
reg enable_reg;
reg enable_new;
reg enable_we;
reg restart_reg;
reg restart_new;
reg [31 : 0] tmp_read_data;
//----------------------------------------------------------------
// Wires.
//----------------------------------------------------------------
reg [31 : 0] muxed_entropy;
reg collect_block;
reg update_block;
reg block_done;
reg hash_init;
reg hash_next;
wire hash_work_factor;
wire [31 : 0] hash_work_factor_num;
wire [1023 : 0] hash_block;
wire hash_ready;
wire [511 : 0] hash_digest;
wire hash_digest_valid;
reg tmp_entropy0_ack;
reg tmp_entropy1_ack;
reg tmp_entropy2_ack;
reg tmp_error;
//----------------------------------------------------------------
// Concurrent connectivity for ports etc.
//----------------------------------------------------------------
assign read_data = tmp_read_data;
assign error = tmp_error;
assign security_error = 0;
assign seed_syn = seed_syn_reg;
assign seed_data = hash_digest;
assign hash_block = {block_mem[00], block_mem[01], block_mem[02], block_mem[03],
block_mem[04], block_mem[05], block_mem[06], block_mem[07],
block_mem[08], block_mem[09], block_mem[10], block_mem[11],
block_mem[12], block_mem[13], block_mem[14], block_mem[15],
block_mem[16], block_mem[17], block_mem[18], block_mem[19],
block_mem[20], block_mem[21], block_mem[22], block_mem[23],
block_mem[24], block_mem[25], block_mem[26], block_mem[27],
block_mem[28], block_mem[29], block_mem[30], block_mem[31]};
assign hash_work_factor = 0;
assign hash_work_factor_num = 32'h0;
assign entropy0_ack = tmp_entropy0_ack;
assign entropy1_ack = tmp_entropy1_ack;
assign entropy2_ack = tmp_entropy2_ack;
assign debug = 8'h55;
//----------------------------------------------------------------
// core instantiation.
//----------------------------------------------------------------
sha512_core hash_inst(
.clk(clk),
.reset_n(reset_n),
.init(hash_init),
.next(hash_next),
.mode(MODE_SHA_512),
.work_factor(hash_work_factor),
.work_factor_num(hash_work_factor_num),
.block(hash_block),
.ready(hash_ready),
.digest(hash_digest),
.digest_valid(hash_digest_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 < 32 ; i = i + 1)
block_mem[i] <= 32'h0;
init_done_reg <= 0;
word_ctr_reg <= 5'h00;
seed_syn_reg <= 0;
enable_reg <= 1;
restart_reg <= 0;
entropy_timeout_reg <= DEFAULT_ENTROPY_TIMEOUT;
entropy_timeout_ctr_reg <= 24'h000000;
entropy_collect_ctrl_reg <= CTRL_IDLE;
mixer_ctrl_reg <= CTRL_IDLE;
end
else
begin
restart_reg <= restart_new;
if (update_block)
block_mem[word_ctr_reg] <= muxed_entropy;
if (init_done_we)
init_done_reg <= init_done_new;
if (word_ctr_we)
word_ctr_reg <= word_ctr_new;
if (seed_syn_we)
seed_syn_reg <= seed_syn_new;
if (entropy_collect_ctrl_we)
entropy_collect_ctrl_reg <= entropy_collect_ctrl_new;
if (enable_we)
enable_reg <= enable_new;
if (mixer_ctrl_we)
mixer_ctrl_reg <= mixer_ctrl_new;
if (entropy_timeout_we)
entropy_timeout_reg <= entropy_timeout_new;
if (entropy_timeout_ctr_we)
entropy_timeout_ctr_reg <= entropy_timeout_ctr_new;
end
end // reg_update
//----------------------------------------------------------------
// mixer_api_logic
//----------------------------------------------------------------
always @*
begin : mixer_api_logic
enable_new = 0;
enable_we = 0;
restart_new = 0;
entropy_timeout_new = 24'h000000;
entropy_timeout_we = 0;
tmp_read_data = 32'h00000000;
tmp_error = 0;
if (cs)
begin
if (we)
begin
// Write operations.
case (address)
// Write operations.
ADDR_MIXER_CTRL:
begin
enable_new = write_data[MIXER_CTRL_ENABLE_BIT];
enable_we = 1;
restart_new = write_data[MIXER_CTRL_RESTART_BIT];
end
ADDR_MIXER_TIMEOUT:
begin
entropy_timeout_new = write_data[23 : 0];
entropy_timeout_we = 1;
end
default:
begin
tmp_error = 1;
end
endcase // case (address)
end // if (we)
else
begin
// Read operations.
case (address)
// Read operations.
ADDR_NAME0:
tmp_read_data = CORE_NAME0;
ADDR_NAME1:
tmp_read_data = CORE_NAME1;
ADDR_VERSION:
tmp_read_data = CORE_VERSION;
ADDR_MIXER_CTRL:
begin
tmp_read_data = {30'h00000000, restart_reg, enable_reg};
end
ADDR_MIXER_STATUS:
begin
tmp_read_data = 32'h00000000;
end
ADDR_MIXER_TIMEOUT:
begin
tmp_read_data = {8'h00, entropy_timeout_reg};
end
default:
begin
tmp_error = 1;
end
endcase // case (address)
end
end
end // mixer_api_logic
//----------------------------------------------------------------
// entropy_collect_ctrl
//
// This FSM implements a round-robin mux for signals from the
// entropy sources and updates the block until a block has
// been filled.
//----------------------------------------------------------------
always @*
begin : entropy_mux
tmp_entropy0_ack = 0;
tmp_entropy1_ack = 0;
tmp_entropy2_ack = 0;
word_ctr_inc = 0;
word_ctr_rst = 0;
update_block = 0;
block_done = 0;
muxed_entropy = 32'h0;
entropy_timeout_ctr_inc = 0;
entropy_timeout_ctr_rst = 0;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 0;
case (entropy_collect_ctrl_reg)
ENTROPY_IDLE:
begin
if (collect_block)
begin
word_ctr_rst = 1;
entropy_timeout_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_SRC0;
entropy_collect_ctrl_we = 1;
end
end
ENTROPY_SRC0:
begin
if (!enable_reg)
begin
word_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
if (entropy0_enabled)
begin
if (entropy0_syn)
begin
muxed_entropy = entropy0_data;
update_block = 1;
entropy_collect_ctrl_new = ENTROPY_SRC0_ACK;
entropy_collect_ctrl_we = 1;
end
else
if (entropy_timeout)
begin
entropy_timeout_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_SRC1;
entropy_collect_ctrl_we = 1;
end
else
begin
entropy_timeout_ctr_inc = 1;
end
end
else
begin
entropy_timeout_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_SRC1;
entropy_collect_ctrl_we = 1;
end
end
end
ENTROPY_SRC0_ACK:
begin
tmp_entropy0_ack = 1;
if (!enable_reg)
begin
word_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
if (word_ctr_reg == 5'h1f)
begin
block_done = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
word_ctr_inc = 1;
entropy_collect_ctrl_new = ENTROPY_SRC1;
entropy_collect_ctrl_we = 1;
end
end
end
ENTROPY_SRC1:
begin
if (!enable_reg)
begin
word_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
if (entropy1_enabled)
begin
if (entropy1_syn)
begin
muxed_entropy = entropy1_data;
update_block = 1;
entropy_collect_ctrl_new = ENTROPY_SRC1_ACK;
entropy_collect_ctrl_we = 1;
end
else
if (entropy_timeout)
begin
entropy_timeout_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_SRC2;
entropy_collect_ctrl_we = 1;
end
else
begin
entropy_timeout_ctr_inc = 1;
end
end
else
begin
entropy_collect_ctrl_new = ENTROPY_SRC2;
entropy_collect_ctrl_we = 1;
end
end
end
ENTROPY_SRC1_ACK:
begin
tmp_entropy1_ack = 1;
if (!enable_reg)
begin
word_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
if (word_ctr_reg == 5'h1f)
begin
block_done = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
word_ctr_inc = 1;
entropy_collect_ctrl_new = ENTROPY_SRC2;
entropy_collect_ctrl_we = 1;
end
end
end
ENTROPY_SRC2:
begin
if (!enable_reg)
begin
word_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
if (entropy2_enabled)
begin
if (entropy2_syn)
begin
muxed_entropy = entropy2_data;
update_block = 1;
entropy_collect_ctrl_new = ENTROPY_SRC2_ACK;
entropy_collect_ctrl_we = 1;
end
else
if (entropy_timeout)
begin
entropy_timeout_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_SRC0;
entropy_collect_ctrl_we = 1;
end
else
begin
entropy_timeout_ctr_inc = 1;
end
end
else
begin
entropy_collect_ctrl_new = ENTROPY_SRC0;
entropy_collect_ctrl_we = 1;
end
end
end
ENTROPY_SRC2_ACK:
begin
tmp_entropy2_ack = 1;
if (!enable_reg)
begin
word_ctr_rst = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
if (word_ctr_reg == 5'h1f)
begin
block_done = 1;
entropy_collect_ctrl_new = ENTROPY_IDLE;
entropy_collect_ctrl_we = 1;
end
else
begin
word_ctr_inc = 1;
entropy_collect_ctrl_new = ENTROPY_SRC0;
entropy_collect_ctrl_we = 1;
end
end
end
endcase // case (entropy_collect_ctrl_reg)
end // entropy_mux
//----------------------------------------------------------------
// entropy_timeout_logic
//
// Logic that updates the entropy timeout counter and signals
// when the wait for antropy from a provider has exceeded
// acceptable time.
//----------------------------------------------------------------
always @*
begin : entropy_timeout_logic
entropy_timeout_ctr_new = 24'h000000;
entropy_timeout_ctr_we = 0;
entropy_timeout = 0;
if (entropy_timeout_ctr_reg == entropy_timeout_reg)
begin
entropy_timeout = 1;
entropy_timeout_ctr_new = 24'h000000;
entropy_timeout_ctr_we = 1;
end
if (entropy_timeout_ctr_rst)
begin
entropy_timeout_ctr_new = 24'h000000;
entropy_timeout_ctr_we = 1;
end
if (entropy_timeout_ctr_inc)
begin
entropy_timeout_ctr_new = entropy_timeout_ctr_reg + 1'b1;
entropy_timeout_ctr_we = 1;
end
end
//----------------------------------------------------------------
// word_ctr
//----------------------------------------------------------------
always @*
begin : word_ctr
word_ctr_new = 5'h00;
word_ctr_we = 0;
if (word_ctr_rst)
begin
word_ctr_new = 5'h00;
word_ctr_we = 1;
end
if (word_ctr_inc)
begin
word_ctr_new = word_ctr_reg + 1'b1;
word_ctr_we = 1;
end
end // word_ctr
//----------------------------------------------------------------
// mixer_ctrl_fsm
//
// Control FSM for the mixer.
//----------------------------------------------------------------
always @*
begin : mixer_ctrl_fsm
seed_syn_new = 0;
seed_syn_we = 0;
init_done_new = 0;
init_done_we = 0;
hash_init = 0;
hash_next = 0;
collect_block = 0;
mixer_ctrl_new = CTRL_IDLE;
mixer_ctrl_we = 0;
case (mixer_ctrl_reg)
CTRL_IDLE:
begin
if (more_seed)
begin
collect_block = 1;
init_done_new = 0;
init_done_we = 1;
mixer_ctrl_new = CTRL_COLLECT;
mixer_ctrl_we = 1;
end
end
CTRL_COLLECT:
begin
if ((discard))
begin
mixer_ctrl_new = CTRL_IDLE;
mixer_ctrl_we = 1;
end
else
begin
if (block_done)
begin
mixer_ctrl_new = CTRL_MIX;
mixer_ctrl_we = 1;
end
end
end
CTRL_MIX:
begin
if ((discard))
begin
mixer_ctrl_new = CTRL_IDLE;
mixer_ctrl_we = 1;
end
else
begin
if (init_done_reg)
begin
hash_next = 1;
end
else
begin
hash_init = 1;
end
mixer_ctrl_new = CTRL_SYN;
mixer_ctrl_we = 1;
end
end
CTRL_SYN:
begin
if ((discard))
begin
mixer_ctrl_new = CTRL_IDLE;
mixer_ctrl_we = 1;
end
else if (hash_ready)
begin
seed_syn_new = 1;
seed_syn_we = 1;
mixer_ctrl_new = CTRL_ACK;
mixer_ctrl_we = 1;
end
end
CTRL_ACK:
begin
if ((discard))
begin
mixer_ctrl_new = CTRL_IDLE;
mixer_ctrl_we = 1;
end
else if (seed_ack)
begin
seed_syn_new = 0;
seed_syn_we = 1;
mixer_ctrl_new = CTRL_NEXT;
mixer_ctrl_we = 1;
end
end
CTRL_NEXT:
begin
if ((discard))
begin
mixer_ctrl_new = CTRL_IDLE;
mixer_ctrl_we = 1;
end
else if (more_seed)
begin
collect_block = 1;
init_done_new = 1;
init_done_we = 1;
mixer_ctrl_new = CTRL_COLLECT;
mixer_ctrl_we = 1;
end
end
endcase // case (cspng_ctrl_reg)
end // mixer_ctrl_fsm
endmodule // trng_mixer
//======================================================================
// EOF trng_mixer.v
//======================================================================