| // 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 |