blob: 9bdfef6079abda674f4b32b3ab7d9bc8de053393 [file] [log] [blame]
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2021 Tamas Hubai
`default_nettype none
/*
Pseudorandom number generator using a Fibonacci-style XNOR linear feedback shift register
*/
module prng (
input clk,
input rst_n,
input [`PRNG_STATE_BITS-1:0] polynomial, // bit mask used for feedback, should be chosen so that
// the prng repeats ifself after 2^(`PRNG_STATE_BITS-1) cycles
input [`PRNG_STATE_BITS-1:0] state_init, // used to seed the prng on reset
input entropy, // optional external entropy for more randomness
output [`DATA_WIDTH-1:0] random
);
localparam SCRAMBLE_CYCLES = `PRNG_STATE_BITS;
reg [`PRNG_STATE_BITS-1:0] state;
generate genvar shift;
// shift register for generating next `DATA_WIDTH states
for (shift=0; shift<`DATA_WIDTH; shift=shift+1) begin:g_shift
wire [`PRNG_STATE_BITS-1:0] prev_state;
wire feedback;
if (shift == 0) begin:i_first
assign prev_state = state;
assign feedback = ^(prev_state & polynomial) ^ entropy;
end else begin:i_nfirst
assign prev_state = g_shift[shift-1].new_state;
assign feedback = ^(prev_state & polynomial);
end
wire [`PRNG_STATE_BITS-1:0] new_state = {prev_state[`PRNG_STATE_BITS-2:0], ~feedback};
assign random[`DATA_WIDTH-shift-1] = prev_state[`PRNG_STATE_BITS-1];
end
wire [`PRNG_STATE_BITS-1:0] final_state = g_shift[`DATA_WIDTH-1].new_state;
// reuse the same shift register to shift out a couple of bits in the beginning so that
// we can use a very simple seed without affecting the quality of the first few cycles
// (for constant seeds this happens at synth time, so it's practically free)
for (shift=0; shift<SCRAMBLE_CYCLES; shift=shift+1) begin:g_scramble
wire [`PRNG_STATE_BITS-1:0] prev_state;
if (shift == 0) begin:i_first
assign prev_state = state_init;
end else begin:i_nfirst
assign prev_state = g_scramble[shift-1].new_state;
end
wire feedback = ^(prev_state & polynomial);
wire [`PRNG_STATE_BITS-1:0] new_state = {prev_state[`PRNG_STATE_BITS-2:0], ~feedback};
end
wire [`PRNG_STATE_BITS-1:0] scrambled_init = g_scramble[SCRAMBLE_CYCLES-1].new_state;
endgenerate
always @(posedge clk) begin
if (!rst_n) begin
state <= scrambled_init;
end else begin
state <= final_state;
end
end
endmodule
`default_nettype wire