Initial commit:
* Xoroshiro128+ (works, tested) * MT19337 (work in progress) * Test benches * Reference C code.
This commit is contained in:
commit
5303c34431
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Reference implementation of Mersenne Twister MT19937 in C++11.
|
||||
*
|
||||
* Test bench by Joris van Rantwijk.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <random>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Reference implementation of Mersenne Twister MT19937\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Usage: ref_mt19937 SEED NUMVALUE\n");
|
||||
fprintf(stderr, " SEED seed value in range 0 .. (2**31-1)\n");
|
||||
fprintf(stderr, " NUMVALUE number of values to get from generator\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Example: ref_mt19937 0x31415926 100\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *p;
|
||||
unsigned long seed = strtoul(argv[1], &p, 0);
|
||||
if (p == argv[1] || *p != '\0') {
|
||||
fprintf(stderr, "ERROR: Invalid value for SEED0\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
unsigned long numval = strtoul(argv[2], &p, 0);
|
||||
if (p == argv[3] || *p != '\0') {
|
||||
fprintf(stderr, "ERROR: Invalid value for NUMVALUE\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mt19937 rng(seed);
|
||||
|
||||
for (unsigned long k = 0; k < numval; k++) {
|
||||
printf("0x%08lx\n", (unsigned long) rng());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Reference implementation of "xoroshiro128+" in C.
|
||||
*
|
||||
* Algorithm by David Blackman and Sebastiano Vigna.
|
||||
* Test bench by Joris van Rantwijk.
|
||||
*
|
||||
* To the extent possible under law, the author has dedicated all copyright
|
||||
* and related and neighboring rights to this software to the public domain
|
||||
* worldwide. This software is distributed without any warranty.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* ========== BEGIN of reference implementation of xoroshiro128+ ==========
|
||||
* See also http://xoroshiro.di.unimi.it/
|
||||
*/
|
||||
|
||||
/* Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org)
|
||||
|
||||
To the extent possible under law, the author has dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
See <http://creativecommons.org/publicdomain/zero/1.0/>. */
|
||||
|
||||
int64_t s[2];
|
||||
|
||||
static inline uint64_t rotl(const uint64_t x, int k) {
|
||||
return (x << k) | (x >> (64 - k));
|
||||
}
|
||||
|
||||
uint64_t next(void) {
|
||||
const uint64_t s0 = s[0];
|
||||
uint64_t s1 = s[1];
|
||||
const uint64_t result = s0 + s1;
|
||||
|
||||
s1 ^= s0;
|
||||
s[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b
|
||||
s[1] = rotl(s1, 36); // c
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ========== END of reference implementation of xoroshiro128+ ========== */
|
||||
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
char *p;
|
||||
unsigned long numval;
|
||||
unsigned long k;
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Reference implementation of RNG xoroshiro128+\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Usage: ref_xoroshiro SEED0 SEED1 NUMVALUE\n");
|
||||
fprintf(stderr, " SEED0 seed value in range 0 .. (2**64-1)\n");
|
||||
fprintf(stderr, " SEED1 seed value in range 0 .. (2**64-1)\n");
|
||||
fprintf(stderr, " NUMVALUE number of values to get from generator\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Example: ref_xoroshiro 0x3141592653589793 "
|
||||
"0x0123456789abcdef 100\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s[0] = strtoull(argv[1], &p, 0);
|
||||
if (p == argv[1] || *p != '\0') {
|
||||
fprintf(stderr, "ERROR: Invalid value for SEED0\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s[1] = strtoull(argv[2], &p, 0);
|
||||
if (p == argv[2] || *p != '\0') {
|
||||
fprintf(stderr, "ERROR: Invalid value for SEED1\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
numval = strtoul(argv[3], &p, 0);
|
||||
if (p == argv[3] || *p != '\0') {
|
||||
fprintf(stderr, "ERROR: Invalid value for NUMVALUE\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (k = 0; k < numval; k++) {
|
||||
printf("0x%016llx\n", (unsigned long long) next());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
--
|
||||
-- Pseudo Random Number Generator based on Mersenne Twister MT19937.
|
||||
--
|
||||
-- Author: Joris van Rantwijk <joris@jorisvr.nl>
|
||||
--
|
||||
-- This is a 32-bit random number generator in synthesizable VHDL.
|
||||
-- The generator produces 32 new random bits on every (enabled) clock cycle.
|
||||
--
|
||||
-- See also M. Matsumoto, T. Nishimura, "Mersenne Twister:
|
||||
-- a 623-dimensionally equidistributed uniform pseudorandom number generator",
|
||||
-- ACM TOMACS, vol. 8, no. 1, 1998.
|
||||
--
|
||||
-- The generator requires a 32-bit seed value.
|
||||
-- A default seed must be supplied at compile time and will be used
|
||||
-- to initialize the generator at reset. The generator also supports
|
||||
-- re-seeded at run time.
|
||||
--
|
||||
-- After reset, and after re-seeding, the generator needs 625 clock
|
||||
-- cycles to initialize its internal state. During this time, the generator
|
||||
-- is unable to provide correct output.
|
||||
--
|
||||
-- NOTE: This is not a cryptographic random number generator.
|
||||
--
|
||||
|
||||
-- TODO : Multiplication in reseeding severely limits the maximum frequency
|
||||
-- for this design.
|
||||
-- Add pipelining and increase the number of clock cycles for reseeding.
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
|
||||
entity rng_mt19937 is
|
||||
|
||||
generic (
|
||||
-- Default seed value.
|
||||
init_seed: std_logic_vector(31 downto 0) );
|
||||
|
||||
port (
|
||||
|
||||
-- Clock, rising edge active.
|
||||
clk: in std_logic;
|
||||
|
||||
-- Synchronous reset, active high.
|
||||
rst: in std_logic;
|
||||
|
||||
-- High to generate new output value.
|
||||
enable: in std_logic;
|
||||
|
||||
-- High to re-seed the generator (works regardless of enable signal).
|
||||
reseed: in std_logic;
|
||||
|
||||
-- New seed value (must be valid when reseed = '1').
|
||||
newseed: in std_logic_vector(31 downto 0);
|
||||
|
||||
-- Output value.
|
||||
-- A new value appears on every rising clock edge where enable = '1'.
|
||||
output: out std_logic_vector(31 downto 0);
|
||||
|
||||
-- High while re-seeding (normal function not available).
|
||||
busy: out std_logic );
|
||||
|
||||
end entity;
|
||||
|
||||
|
||||
architecture rng_mt19937_arch of rng_mt19937 is
|
||||
|
||||
-- Constants.
|
||||
constant const_a: std_logic_vector(31 downto 0) := x"9908b0df";
|
||||
constant const_b: std_logic_vector(31 downto 0) := x"9d2c5680";
|
||||
constant const_c: std_logic_vector(31 downto 0) := x"efc60000";
|
||||
constant const_f: natural := 1812433253;
|
||||
|
||||
-- Block RAM for generator state.
|
||||
type mem_t is array(0 to 620) of std_logic_vector(31 downto 0);
|
||||
signal mem: mem_t;
|
||||
|
||||
-- RAM access registers.
|
||||
signal reg_a_addr: std_logic_vector(9 downto 0);
|
||||
signal reg_b_addr: std_logic_vector(9 downto 0);
|
||||
signal reg_a_wdata: std_logic_vector(31 downto 0);
|
||||
signal reg_a_rdata: std_logic_vector(31 downto 0);
|
||||
signal reg_b_rdata: std_logic_vector(31 downto 0);
|
||||
|
||||
-- Internal registers.
|
||||
signal reg_enable: std_logic;
|
||||
signal reg_reseeding1: std_logic;
|
||||
signal reg_reseeding2: std_logic;
|
||||
signal reg_a_wdata_p: std_logic_vector(31 downto 0);
|
||||
signal reg_a_rdata_p: std_logic_vector(31 downto 0);
|
||||
signal reg_reseed_cnt: std_logic_vector(9 downto 0);
|
||||
|
||||
-- Output register.
|
||||
signal reg_output: std_logic_vector(31 downto 0) := (others => '0');
|
||||
signal reg_busy: std_logic;
|
||||
|
||||
-- -- Multiply unsigned number with constant and discard overflowing bits.
|
||||
-- function mulconst(x: unsigned)
|
||||
-- return unsigned
|
||||
-- is
|
||||
-- variable t: unsigned(2*x'length-1 downto 0);
|
||||
-- begin
|
||||
-- t := x * const_f;
|
||||
-- return t(x'length-1 downto 0);
|
||||
-- end function;
|
||||
|
||||
-- Multiply unsigned number with constant and discard overflowing bits.
|
||||
function mulconst(x: unsigned)
|
||||
return unsigned
|
||||
is
|
||||
begin
|
||||
return x
|
||||
+ shift_left(x, 2)
|
||||
+ shift_left(x, 5)
|
||||
+ shift_left(x, 6)
|
||||
+ shift_left(x, 8)
|
||||
+ shift_left(x, 11)
|
||||
- shift_left(x, 15)
|
||||
+ shift_left(x, 19)
|
||||
- shift_left(x, 26)
|
||||
- shift_left(x, 28)
|
||||
+ shift_left(x, 31);
|
||||
end function;
|
||||
|
||||
begin
|
||||
|
||||
-- Drive output signal.
|
||||
output <= reg_output;
|
||||
busy <= reg_busy;
|
||||
|
||||
-- Main synchronous process.
|
||||
process (clk) is
|
||||
variable y: std_logic_vector(31 downto 0);
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
|
||||
-- Update memory pointers.
|
||||
if reg_enable = '1' then
|
||||
|
||||
if unsigned(reg_a_addr) = 620 then
|
||||
reg_a_addr <= (others => '0');
|
||||
else
|
||||
reg_a_addr <= std_logic_vector(unsigned(reg_a_addr) + 1);
|
||||
end if;
|
||||
|
||||
if unsigned(reg_b_addr) = 620 then
|
||||
reg_b_addr <= (others => '0');
|
||||
else
|
||||
reg_b_addr <= std_logic_vector(unsigned(reg_b_addr) + 1);
|
||||
end if;
|
||||
|
||||
end if;
|
||||
|
||||
-- Keep previous values of registers.
|
||||
if reg_enable = '1' then
|
||||
reg_a_rdata_p <= reg_a_rdata;
|
||||
reg_a_wdata_p <= reg_a_wdata;
|
||||
end if;
|
||||
|
||||
-- Update reseeding counter.
|
||||
reg_reseed_cnt <= std_logic_vector(unsigned(reg_reseed_cnt) + 1);
|
||||
|
||||
-- Determine end of reseeding.
|
||||
reg_busy <= reg_reseeding2;
|
||||
reg_reseeding2 <= reg_reseeding1;
|
||||
if unsigned(reg_reseed_cnt) = 623 then
|
||||
reg_reseeding1 <= '0';
|
||||
end if;
|
||||
|
||||
-- Enable state machine on next cycle
|
||||
-- a) during initialization, and
|
||||
-- b) on-demand for new output.
|
||||
reg_enable <= reg_reseeding2 or enable;
|
||||
|
||||
-- Update internal RNG state.
|
||||
if reg_enable = '1' then
|
||||
|
||||
if reg_reseeding1 = '1' then
|
||||
|
||||
-- Continue re-seeding loop.
|
||||
y := reg_a_wdata;
|
||||
y(1 downto 0) := y(1 downto 0) xor y(31 downto 30);
|
||||
reg_a_wdata <= std_logic_vector(
|
||||
mulconst(unsigned(y)) +
|
||||
unsigned(reg_reseed_cnt) );
|
||||
|
||||
else
|
||||
|
||||
-- Normal operation.
|
||||
-- Perform one step of the "twist" function.
|
||||
|
||||
y := reg_a_rdata_p(31 downto 31) &
|
||||
reg_a_rdata(30 downto 0);
|
||||
|
||||
if y(0) = '1' then
|
||||
y := "0" & y(31 downto 1);
|
||||
y := y xor const_a;
|
||||
else
|
||||
y := "0" & y(31 downto 1);
|
||||
end if;
|
||||
|
||||
reg_a_wdata_p <= reg_a_wdata;
|
||||
reg_a_wdata <= reg_b_rdata xor y;
|
||||
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- Produce output value (when enabled).
|
||||
if enable = '1' then
|
||||
|
||||
if reg_enable = '1' then
|
||||
y := reg_a_wdata;
|
||||
else
|
||||
y := reg_a_wdata_p;
|
||||
end if;
|
||||
|
||||
y(20 downto 0) := y(20 downto 0) xor y(31 downto 11);
|
||||
y(31 downto 7) := y(31 downto 7) xor
|
||||
(y(24 downto 0) and const_b(31 downto 7));
|
||||
y(31 downto 15) := y(31 downto 15) xor
|
||||
(y(16 downto 0) and const_c(31 downto 15));
|
||||
y(13 downto 0) := y(13 downto 0) xor y(31 downto 18);
|
||||
|
||||
reg_output <= y;
|
||||
|
||||
end if;
|
||||
|
||||
-- Start re-seeding.
|
||||
if reseed = '1' then
|
||||
reg_reseeding1 <= '1';
|
||||
reg_reseeding2 <= '1';
|
||||
reg_reseed_cnt <= std_logic_vector(to_unsigned(1, 10));
|
||||
reg_enable <= '1';
|
||||
reg_a_wdata <= newseed;
|
||||
reg_busy <= '1';
|
||||
end if;
|
||||
|
||||
-- Synchronous reset.
|
||||
if rst = '1' then
|
||||
reg_a_addr <= std_logic_vector(to_unsigned(0, 10));
|
||||
reg_b_addr <= std_logic_vector(to_unsigned(396, 10));
|
||||
reg_reseeding1 <= '1';
|
||||
reg_reseeding2 <= '1';
|
||||
reg_reseed_cnt <= std_logic_vector(to_unsigned(1, 10));
|
||||
reg_enable <= '1';
|
||||
reg_a_wdata <= init_seed;
|
||||
reg_output <= (others => '0');
|
||||
reg_busy <= '1';
|
||||
end if;
|
||||
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- Synchronous process for block RAM.
|
||||
process (clk) is
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if reg_enable = '1' then
|
||||
|
||||
-- Read from port A.
|
||||
reg_a_rdata <= mem(to_integer(unsigned(reg_a_addr)));
|
||||
|
||||
-- Read from port B.
|
||||
reg_b_rdata <= mem(to_integer(unsigned(reg_b_addr)));
|
||||
|
||||
-- Write to port A.
|
||||
mem(to_integer(unsigned(reg_a_addr))) <= reg_a_wdata;
|
||||
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
end architecture;
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
--
|
||||
-- Pseudo Random Number Generator "xoroshiro128+".
|
||||
--
|
||||
-- Author: Joris van Rantwijk <joris@jorisvr.nl>
|
||||
--
|
||||
-- This is a 64-bit random number generator in synthesizable VHDL.
|
||||
-- The generator produces 64 new random bits on every (enabled) clock cycle.
|
||||
--
|
||||
-- The algorithm "xoroshiro128+" is by David Blackman and Sebastiano Vigna.
|
||||
-- See also http://xoroshiro.di.unimi.it/
|
||||
--
|
||||
-- The generator requires a 128-bit seed value, not equal to zero.
|
||||
-- A default seed must be supplied at compile time and will be used
|
||||
-- to initialize the generator at reset. The generator also supports
|
||||
-- re-seeding at run time.
|
||||
--
|
||||
-- After reset, at least one enabled clock cycle is needed before
|
||||
-- a random number appears on the output.
|
||||
--
|
||||
-- NOTE: This is not a cryptographic random number generator.
|
||||
--
|
||||
-- NOTE: The least significant output bit is less random than
|
||||
-- all other output bits.
|
||||
--
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
|
||||
entity xoroshiro128plus is
|
||||
|
||||
generic (
|
||||
-- Default seed value.
|
||||
init_seed: std_logic_vector(127 downto 0) );
|
||||
|
||||
port (
|
||||
|
||||
-- Clock, rising edge active.
|
||||
clk: in std_logic;
|
||||
|
||||
-- Synchronous reset, active high.
|
||||
rst: in std_logic;
|
||||
|
||||
-- Clock enable, active high.
|
||||
enable: in std_logic;
|
||||
|
||||
-- High to re-seed the generator (requires enable = '1').
|
||||
reseed: in std_logic;
|
||||
|
||||
-- New seed value (must be valid when reseed = '1').
|
||||
newseed: in std_logic_vector(127 downto 0);
|
||||
|
||||
-- Output value.
|
||||
-- A new value appears on every rising clock edge where enable = '1'.
|
||||
output: out std_logic_vector(63 downto 0) );
|
||||
|
||||
end entity;
|
||||
|
||||
|
||||
architecture xoroshiro128plus_arch of xoroshiro128plus is
|
||||
|
||||
-- Internal state of RNG.
|
||||
signal reg_state_s0: std_logic_vector(63 downto 0) := init_seed(63 downto 0);
|
||||
signal reg_state_s1: std_logic_vector(63 downto 0) := init_seed(127 downto 64);
|
||||
|
||||
-- Output register.
|
||||
signal reg_output: std_logic_vector(63 downto 0) := (others => '0');
|
||||
|
||||
-- Shift left.
|
||||
function shiftl(x: std_logic_vector; b: integer)
|
||||
return std_logic_vector
|
||||
is
|
||||
constant n: integer := x'length;
|
||||
variable y: std_logic_vector(n-1 downto 0);
|
||||
begin
|
||||
y(n-1 downto b) := x(x'high-b downto x'low);
|
||||
y(b-1 downto 0) := (others => '0');
|
||||
return y;
|
||||
end function;
|
||||
|
||||
-- Rotate left.
|
||||
function rotl(x: std_logic_vector; b: integer)
|
||||
return std_logic_vector
|
||||
is
|
||||
constant n: integer := x'length;
|
||||
variable y: std_logic_vector(n-1 downto 0);
|
||||
begin
|
||||
y(n-1 downto b) := x(x'high-b downto x'low);
|
||||
y(b-1 downto 0) := x(x'high downto x'high-b+1);
|
||||
return y;
|
||||
end function;
|
||||
|
||||
begin
|
||||
|
||||
-- Drive output signal.
|
||||
output <= reg_output;
|
||||
|
||||
-- Synchronous process.
|
||||
process (clk) is
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
|
||||
if enable = '1' then
|
||||
|
||||
-- Prepare output word.
|
||||
reg_output <= std_logic_vector(unsigned(reg_state_s0) +
|
||||
unsigned(reg_state_s1));
|
||||
|
||||
-- Update internal state.
|
||||
reg_state_s0 <= reg_state_s0 xor
|
||||
reg_state_s1 xor
|
||||
shiftl(reg_state_s0, 14) xor
|
||||
shiftl(reg_state_s1, 14) xor
|
||||
rotl(reg_state_s0, 55);
|
||||
|
||||
reg_state_s1 <= rotl(reg_state_s0, 36) xor
|
||||
rotl(reg_state_s1, 36);
|
||||
|
||||
-- Re-seed function.
|
||||
if reseed = '1' then
|
||||
reg_state_s0 <= newseed(63 downto 0);
|
||||
reg_state_s1 <= newseed(127 downto 64);
|
||||
end if;
|
||||
|
||||
end if;
|
||||
|
||||
-- Synchronous reset.
|
||||
if rst = '1' then
|
||||
reg_state_s0 <= init_seed(63 downto 0);
|
||||
reg_state_s1 <= init_seed(127 downto 64);
|
||||
reg_output <= (others => '0');
|
||||
end if;
|
||||
|
||||
end if;
|
||||
end process;
|
||||
|
||||
end architecture;
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
--
|
||||
-- Test bench for PRNG MT19937.
|
||||
--
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
entity tb_mt19937 is
|
||||
end entity;
|
||||
|
||||
architecture arch of tb_mt19937 is
|
||||
|
||||
signal clk: std_logic;
|
||||
signal clock_active: boolean := false;
|
||||
|
||||
signal s_rst: std_logic;
|
||||
signal s_enable: std_logic;
|
||||
signal s_reseed: std_logic;
|
||||
signal s_newseed: std_logic_vector(31 downto 0);
|
||||
signal s_output: std_logic_vector(31 downto 0);
|
||||
signal s_busy: std_logic;
|
||||
|
||||
function to_hex_string(s: std_logic_vector)
|
||||
return string
|
||||
is
|
||||
constant alphabet: string(1 to 16) := "0123456789abcdef";
|
||||
variable y: string(1 to s'length/4);
|
||||
begin
|
||||
for i in y'range loop
|
||||
y(i) := alphabet(to_integer(unsigned(s(s'high+4-4*i downto s'high+1-4*i))) + 1);
|
||||
end loop;
|
||||
return y;
|
||||
end function;
|
||||
|
||||
begin
|
||||
|
||||
-- Instantiate PRNG.
|
||||
inst_prng: entity work.rng_mt19937
|
||||
generic map (
|
||||
init_seed => x"31415926" )
|
||||
port map (
|
||||
clk => clk,
|
||||
rst => s_rst,
|
||||
enable => s_enable,
|
||||
reseed => s_reseed,
|
||||
newseed => s_newseed,
|
||||
output => s_output,
|
||||
busy => s_busy );
|
||||
|
||||
-- Generate clock.
|
||||
clk <= (not clk) after 10 ns when clock_active else '0';
|
||||
|
||||
-- Main simulation process.
|
||||
process is
|
||||
begin
|
||||
|
||||
report "Start test bench";
|
||||
|
||||
-- Reset.
|
||||
s_rst <= '1';
|
||||
s_enable <= '0';
|
||||
s_reseed <= '0';
|
||||
s_newseed <= (others => '0');
|
||||
|
||||
-- Start clock.
|
||||
clock_active <= true;
|
||||
|
||||
-- Wait 2 clock cycles, then end reset.
|
||||
wait for 30 ns;
|
||||
wait until falling_edge(clk);
|
||||
s_rst <= '0';
|
||||
|
||||
-- Check that generator is initializing.
|
||||
assert s_busy = '1' report "Generator fails to indicate BUSY";
|
||||
wait until falling_edge(clk);
|
||||
assert s_busy = '1' report "Generator fails to indicate BUSY";
|
||||
|
||||
-- Give generator time to complete initialization.
|
||||
for i in 0 to 623 loop
|
||||
wait until falling_edge(clk);
|
||||
end loop;
|
||||
assert s_busy = '0' report "Generator should be ready but still indicates BUSY";
|
||||
|
||||
-- Produce numbers
|
||||
for i in 0 to 1500 loop
|
||||
|
||||
if i mod 5 = 0 or i mod 7 = 0 then
|
||||
s_enable <= '0';
|
||||
wait until falling_edge(clk);
|
||||
else
|
||||
s_enable <= '1';
|
||||
wait until falling_edge(clk);
|
||||
report "Got 0x" & to_hex_string(s_output);
|
||||
end if;
|
||||
|
||||
end loop;
|
||||
|
||||
-- Re-seed generator.
|
||||
report "Re-seed generator";
|
||||
s_enable <= '0';
|
||||
s_reseed <= '1';
|
||||
s_newseed <= x"fedcba98";
|
||||
wait until falling_edge(clk);
|
||||
s_reseed <= '0';
|
||||
s_newseed <= (others => '0');
|
||||
|
||||
-- Check that generator is initializing.
|
||||
assert s_busy = '1' report "Generator fails to indicate BUSY";
|
||||
wait until falling_edge(clk);
|
||||
assert s_busy = '1' report "Generator fails to indicate BUSY";
|
||||
|
||||
-- Give generator time to complete initialization.
|
||||
for i in 0 to 623 loop
|
||||
wait until falling_edge(clk);
|
||||
end loop;
|
||||
assert s_busy = '0' report "Generator should be ready but still indicates BUSY";
|
||||
|
||||
-- Produce numbers
|
||||
for i in 0 to 1500 loop
|
||||
|
||||
if i mod 5 = 1 or i mod 7 = 1 then
|
||||
s_enable <= '0';
|
||||
wait until falling_edge(clk);
|
||||
else
|
||||
s_enable <= '1';
|
||||
wait until falling_edge(clk);
|
||||
report "Got 0x" & to_hex_string(s_output);
|
||||
end if;
|
||||
|
||||
end loop;
|
||||
|
||||
-- End simulation.
|
||||
report "End testbench";
|
||||
|
||||
clock_active <= false;
|
||||
wait;
|
||||
|
||||
end process;
|
||||
|
||||
end architecture;
|
|
@ -0,0 +1,118 @@
|
|||
--
|
||||
-- Test bench for PRNG "xoroshiro128+".
|
||||
--
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
entity tb_xoroshiro128plus is
|
||||
end entity;
|
||||
|
||||
architecture arch of tb_xoroshiro128plus is
|
||||
|
||||
signal clk: std_logic;
|
||||
signal clock_active: boolean := false;
|
||||
|
||||
signal s_rst: std_logic;
|
||||
signal s_enable: std_logic;
|
||||
signal s_reseed: std_logic;
|
||||
signal s_newseed: std_logic_vector(127 downto 0);
|
||||
signal s_output: std_logic_vector(63 downto 0);
|
||||
|
||||
function to_hex_string(s: std_logic_vector)
|
||||
return string
|
||||
is
|
||||
constant alphabet: string(1 to 16) := "0123456789abcdef";
|
||||
variable y: string(1 to s'length/4);
|
||||
begin
|
||||
for i in y'range loop
|
||||
y(i) := alphabet(to_integer(unsigned(s(s'high+4-4*i downto s'high+1-4*i))) + 1);
|
||||
end loop;
|
||||
return y;
|
||||
end function;
|
||||
|
||||
begin
|
||||
|
||||
-- Instantiate PRNG.
|
||||
inst_prng: entity work.xoroshiro128plus
|
||||
generic map (
|
||||
init_seed => x"0123456789abcdef3141592653589793" )
|
||||
port map (
|
||||
clk => clk,
|
||||
rst => s_rst,
|
||||
enable => s_enable,
|
||||
reseed => s_reseed,
|
||||
newseed => s_newseed,
|
||||
output => s_output );
|
||||
|
||||
-- Generate clock.
|
||||
clk <= (not clk) after 10 ns when clock_active else '0';
|
||||
|
||||
-- Main simulation process.
|
||||
process is
|
||||
begin
|
||||
|
||||
report "Start test bench";
|
||||
|
||||
-- Reset.
|
||||
s_rst <= '1';
|
||||
s_enable <= '0';
|
||||
s_reseed <= '0';
|
||||
s_newseed <= (others => '0');
|
||||
|
||||
-- Start clock.
|
||||
clock_active <= true;
|
||||
|
||||
-- Wait 2 clock cycles, then end reset.
|
||||
wait for 30 ns;
|
||||
wait until falling_edge(clk);
|
||||
s_rst <= '0';
|
||||
|
||||
-- Produce numbers
|
||||
for i in 0 to 150 loop
|
||||
|
||||
if i mod 5 = 0 or i mod 7 = 0 then
|
||||
s_enable <= '0';
|
||||
wait until falling_edge(clk);
|
||||
else
|
||||
s_enable <= '1';
|
||||
wait until falling_edge(clk);
|
||||
report "Got 0x" & to_hex_string(s_output);
|
||||
end if;
|
||||
|
||||
end loop;
|
||||
|
||||
-- Re-seed generator.
|
||||
report "Re-seed generator";
|
||||
s_enable <= '1';
|
||||
s_reseed <= '1';
|
||||
s_newseed <= x"3141592653589793fedcba9876543210";
|
||||
wait until falling_edge(clk);
|
||||
|
||||
s_reseed <= '0';
|
||||
s_newseed <= (others => '0');
|
||||
|
||||
-- Produce numbers
|
||||
for i in 0 to 150 loop
|
||||
|
||||
if i mod 5 = 0 or i mod 7 = 0 then
|
||||
s_enable <= '0';
|
||||
wait until falling_edge(clk);
|
||||
else
|
||||
s_enable <= '1';
|
||||
wait until falling_edge(clk);
|
||||
report "Got 0x" & to_hex_string(s_output);
|
||||
end if;
|
||||
|
||||
end loop;
|
||||
|
||||
-- End simulation.
|
||||
report "End testbench";
|
||||
|
||||
clock_active <= false;
|
||||
wait;
|
||||
|
||||
end process;
|
||||
|
||||
end architecture;
|
Loading…
Reference in New Issue