blob: cb6d9538db6db6523bdcbcf07ff3612d92b0f086 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// This module implements XoShiRo256++ PRNG
//
// Additional Entropy input to XOR fresh entropy into the state.
// Lockup protection that reseeds the generator if it falls into the all-zero state.
//
// Refs: [1] D. Blackman and S. Vigna, Scrambled Linear Pseudorndom Number Generators
// https://arxiv.org/pdf/1805.01407.pdf
// [2] https://prng.di.unimi.it/
// [3] https://en.wikipedia.org/wiki/Xorshift#xoshiro_and_xoroshiro
`include "prim_assert.sv"
module prim_xoshiro256pp #(
// Output width, must be a multiple of 64
parameter int unsigned OutputDw = 64,
// PRNG reset state, must be nonzero!
parameter logic [255:0] DefaultSeed = 256'(1'b1),
parameter int unsigned NumStages = OutputDw / 64 // derived parameter
) (
input logic clk_i,
input logic rst_ni,
input logic seed_en_i, // load external seed into the state
input logic [255:0] seed_i, // external seed input
input logic xoshiro_en_i, // enables the PRNG
input logic [255:0] entropy_i, // additional entropy to be XOR'ed into the state
output logic [OutputDw-1:0] data_o, // PRNG output
output logic all_zero_o // alert signal indicates the all-zero state
);
logic [255:0] unrolled_state [NumStages+1];
logic [63:0] mid [NumStages];
logic lockup;
logic [255:0] xoshiro_d, xoshiro_q, next_xoshiro_state;
function automatic logic [255:0] state_update (input logic [255:0] data_in);
logic [63:0] a_in, b_in, c_in, d_in;
logic [63:0] a_out, b_out, c_out, d_out;
a_in = data_in[255:192];
b_in = data_in[191:128];
c_in = data_in[127:64];
d_in = data_in[63:0];
a_out = a_in ^ b_in ^ d_in;
b_out = a_in ^ b_in ^ c_in;
c_out = a_in ^ (b_in << 17) ^ c_in;
d_out = {d_in[18:0], d_in[63:19]} ^ {b_in[18:0], b_in[63:19]};
return {a_out, b_out, c_out, d_out};
endfunction: state_update
assign unrolled_state[0] = xoshiro_q;
for (genvar k = 0; k < NumStages; k++) begin : gen_state_functions
// State update function
assign unrolled_state[k+1] = state_update(unrolled_state[k]);
// State output function
assign mid[k] = unrolled_state[k][255:192] + unrolled_state[k][63:0];
assign data_o[(k+1)*64-1:k*64] = {mid[k][40:0], mid[k][63:41]} + unrolled_state[k][255:192];
end
assign next_xoshiro_state = entropy_i ^ unrolled_state[NumStages];
assign xoshiro_d = (seed_en_i) ? seed_i :
(xoshiro_en_i && lockup) ? DefaultSeed :
(xoshiro_en_i) ? next_xoshiro_state : xoshiro_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg_state
if (!rst_ni) begin
xoshiro_q <= DefaultSeed;
end else begin
xoshiro_q <= xoshiro_d;
end
end
// lockup condition is all-zero
assign lockup = ~(|xoshiro_q);
// Indicate that the state is all zeros.
assign all_zero_o = lockup;
// check that seed is not all-zero
`ASSERT_INIT(DefaultSeedNzCheck_A, |DefaultSeed)
endmodule