blob: 1a5b763cb452bc2c8b23195d41e9f0ae7cc57855 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "prim_assert.sv"
/**
* OTBN random number coordination
*
* This module implements the RND, RND_PREFETCH and URND CSRs/WSRs. The EDN (entropy distribution
* network) provides the bits for random numbers. RND gives direct access to EDN bits. URND provides
* bits from an LFSR that is seeded with bits from the EDN.
*/
////////////////////////////////////////////////////////////////////////////////////////////////////
// IMPORTANT NOTE: //
// DO NOT USE THIS BLINDLY! //
// //
// This is an initial prototype of the random number functionality in OTBN. Details are still //
// under discussion and subject to change. It has not yet been verified this provides the //
// necessary guarantees required for the various uses of random numbers in OTBN software. //
////////////////////////////////////////////////////////////////////////////////////////////////////
module otbn_rnd import otbn_pkg::*;
#(
parameter urnd_lfsr_seed_t RndCnstUrndLfsrSeed = RndCnstUrndLfsrSeedDefault,
parameter urnd_chunk_lfsr_perm_t RndCnstUrndChunkLfsrPerm = RndCnstUrndChunkLfsrPermDefault
) (
input logic clk_i,
input logic rst_ni,
input logic rnd_req_i,
input logic rnd_prefetch_req_i,
output logic rnd_valid_o,
output logic [WLEN-1:0] rnd_data_o,
// Request URND LFSR reseed from the EDN
input logic urnd_reseed_req_i,
// Remains asserted whilst reseed is in progress
output logic urnd_reseed_busy_o,
// When asserted URND LFSR state advances. It is permissible to advance the state whilst
// reseeding.
input logic urnd_advance_i,
// URND data from LFSR
output logic [WLEN-1:0] urnd_data_o,
// Entropy distribution network (EDN)
output logic edn_rnd_req_o,
input logic edn_rnd_ack_i,
input logic [EdnDataWidth-1:0] edn_rnd_data_i,
output logic edn_urnd_req_o,
input logic edn_urnd_ack_i,
input logic [EdnDataWidth-1:0] edn_urnd_data_i
);
// Determine how many LFSR chunks are required to fill a full WLEN register
localparam int LfsrChunksPerWLEN = WLEN / UrndChunkLfsrWidth;
localparam int BytesPerLfsrChunk = UrndChunkLfsrWidth / 8;
logic rnd_valid_q, rnd_valid_d;
logic [WLEN-1:0] rnd_data_q, rnd_data_d;
logic rnd_data_en;
logic rnd_req_complete;
logic edn_rnd_req_complete;
logic edn_rnd_req_start;
logic edn_rnd_req_q, edn_rnd_req_d;
////////////////////////
// RND Implementation //
////////////////////////
assign rnd_req_complete = rnd_req_i & rnd_valid_o;
assign edn_rnd_req_complete = edn_rnd_req_o & edn_rnd_ack_i;
assign rnd_data_en = edn_rnd_req_complete;
// RND becomes valid when EDN request completes and provides new bits. Valid is cleared when OTBN
// reads RND
assign rnd_valid_d = (rnd_valid_q & ~rnd_req_complete) | edn_rnd_req_complete;
assign rnd_data_d = edn_rnd_data_i;
// Start an EDN request when there is a prefetch or an attempt at reading RND when RND data is not
// available. Signalling `edn_rnd_req_start` whilst there is an outstanding request has no effect
// and is harmless.
assign edn_rnd_req_start = rnd_prefetch_req_i | (rnd_req_i & ~rnd_valid_q);
// Assert `edn_rnd_req_o` when a request is started and keep it asserted until the request is done
assign edn_rnd_req_d = (edn_rnd_req_q | edn_rnd_req_start) & ~edn_rnd_req_complete;
assign edn_rnd_req_o = edn_rnd_req_q;
always_ff @(posedge clk_i) begin
if (rnd_data_en) begin
rnd_data_q <= rnd_data_d;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rnd_valid_q <= 1'b0;
edn_rnd_req_q <= 1'b0;
end else begin
rnd_valid_q <= rnd_valid_d;
edn_rnd_req_q <= edn_rnd_req_d;
end
end
assign rnd_valid_o = rnd_valid_q;
assign rnd_data_o = rnd_data_q;
/////////////////////////
// URND Implementation //
/////////////////////////
logic edn_urnd_req_complete;
logic edn_urnd_req_q, edn_urnd_req_d;
assign edn_urnd_req_complete = edn_urnd_req_o & edn_urnd_ack_i;
assign edn_urnd_req_d = (edn_urnd_req_q | urnd_reseed_req_i) & ~edn_urnd_req_complete;
assign edn_urnd_req_o = edn_urnd_req_q;
assign urnd_reseed_busy_o = edn_urnd_req_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
edn_urnd_req_q <= 1'b0;
end else begin
edn_urnd_req_q <= edn_urnd_req_d;
end
end
logic lfsr_seed_en;
logic [UrndChunkLfsrWidth-1:0] lfsr_seed [LfsrChunksPerWLEN];
logic [UrndChunkLfsrWidth-1:0] lfsr_state [LfsrChunksPerWLEN];
assign lfsr_seed_en = edn_urnd_req_complete;
// We use multiple LFSR instances each having a width of ChunkSize.
// This is a functional prototype of the final URND functionality and is subject to change
// https://github.com/lowRISC/opentitan/issues/6083
for (genvar c = 0; c < LfsrChunksPerWLEN; c++) begin : gen_lfsr_chunks
localparam logic [UrndChunkLfsrWidth-1:0] LfsrChunkSeed =
RndCnstUrndLfsrSeed[c * UrndChunkLfsrWidth +: UrndChunkLfsrWidth];
assign lfsr_seed[c] = edn_urnd_data_i[c * UrndChunkLfsrWidth+: UrndChunkLfsrWidth];
prim_lfsr #(
.LfsrType ( "GAL_XOR" ),
.LfsrDw ( UrndChunkLfsrWidth ),
.StateOutDw ( UrndChunkLfsrWidth ),
.DefaultSeed ( LfsrChunkSeed ),
.StatePermEn ( 1'b1 ),
.StatePerm ( RndCnstUrndChunkLfsrPerm )
) u_lfsr_chunk (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.seed_en_i ( lfsr_seed_en ),
.seed_i ( lfsr_seed[c] ),
.lfsr_en_i ( urnd_advance_i ),
.entropy_i ( '0 ),
.state_o ( lfsr_state[c] )
);
end
// Further "scramble" the LFSR state at the byte level to break linear shift patterns.
for (genvar c = 0; c < LfsrChunksPerWLEN; c++) begin : gen_lfsr_state_scramble_outer
for (genvar b = 0;b < BytesPerLfsrChunk; b++) begin : gen_lfsr_start_scramble_inner
assign urnd_data_o[b * 8 + c * UrndChunkLfsrWidth +: 8] =
prim_cipher_pkg::sbox4_8bit(lfsr_state[c][b*8 +: 8], prim_cipher_pkg::PRINCE_SBOX4);
end
end
`ASSERT(rnd_req_stable, rnd_req_i & ~rnd_valid_o |=> rnd_req_i)
`ASSERT(rnd_clear_on_req_complete, rnd_req_complete |=> ~rnd_valid_q)
endmodule