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