├── .gitignore ├── img ├── neotrng_cell_map.png ├── neotrng_debiasing.png ├── neotrng_architecture.png ├── neotrng_sampling_unit.png └── neotrng_ring_oscillator.png ├── .github ├── dependabot.yml └── workflows │ └── main.yml ├── sim ├── ghdl.sh └── neoTRNG_tb.vhd ├── CITATION.cff ├── LICENSE ├── rtl └── neoTRNG.vhd └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.ghw 2 | *.cf 3 | ~* 4 | -------------------------------------------------------------------------------- /img/neotrng_cell_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stnolting/neoTRNG/HEAD/img/neotrng_cell_map.png -------------------------------------------------------------------------------- /img/neotrng_debiasing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stnolting/neoTRNG/HEAD/img/neotrng_debiasing.png -------------------------------------------------------------------------------- /img/neotrng_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stnolting/neoTRNG/HEAD/img/neotrng_architecture.png -------------------------------------------------------------------------------- /img/neotrng_sampling_unit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stnolting/neoTRNG/HEAD/img/neotrng_sampling_unit.png -------------------------------------------------------------------------------- /img/neotrng_ring_oscillator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stnolting/neoTRNG/HEAD/img/neotrng_ring_oscillator.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | -------------------------------------------------------------------------------- /sim/ghdl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd $(dirname "$0") 6 | 7 | ghdl -a ../rtl/neoTRNG.vhd neoTRNG_tb.vhd 8 | ghdl -e neoTRNG_tb 9 | ghdl -r neoTRNG_tb --stop-time=100us --wave=neoTRNG_tb.ghw 10 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this project, please cite it as below." 3 | authors: 4 | - family-names: "Nolting" 5 | given-names: "Stephan" 6 | title: "The neoTRNG V3 - A Tiny and Platform-Independent True Random Number Generator." 7 | version: 3.0.0 8 | doi: 10.5281/zenodo.10071201 9 | date-released: 2023-11-04 10 | url: "https://github.com/stnolting/neoTRNG" 11 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: neoTRNG simulation 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'rtl/**' 7 | - 'sim/**' 8 | - '.github/workflows/main.yml' 9 | pull_request: 10 | paths: 11 | - 'rtl/**' 12 | - 'sim/**' 13 | - '.github/workflows/main.yml' 14 | workflow_dispatch: 15 | 16 | jobs: 17 | 18 | neoTRNG-sim: 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | 23 | - name: '📂 Repository checkout' 24 | uses: actions/checkout@v6 25 | with: 26 | fetch-depth: 0 27 | submodules: recursive 28 | 29 | - name: '📦 Install GHDL' 30 | uses: ghdl/setup-ghdl@v1 31 | with: 32 | version: nightly 33 | backend: mcode 34 | 35 | - name: '🔍 Check tools' 36 | run: ghdl -v 37 | 38 | - name: '⚙️ Run neoTRNG simulation' 39 | run: /bin/bash -c "chmod u+x $GITHUB_WORKSPACE/sim/ghdl.sh && $GITHUB_WORKSPACE/sim/ghdl.sh" 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Stephan Nolting 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /sim/neoTRNG_tb.vhd: -------------------------------------------------------------------------------- 1 | -- simple neoTRNG testbench -- 2 | 3 | library ieee; 4 | use ieee.std_logic_1164.all; 5 | use ieee.numeric_std.all; 6 | use std.textio.all; 7 | 8 | entity neoTRNG_tb is 9 | generic ( 10 | NUM_CELLS : natural range 1 to 99 := 3; -- number of ring-oscillator cells 11 | NUM_INV_START : natural range 3 to 99 := 5; -- number of inverters in first ring-oscillator cell, has to be odd 12 | NUM_RAW_BITS : natural range 1 to 4096 := 64 -- number of XOR-ed raw bits per random sample byte (has to be a power of 2) 13 | ); 14 | end neoTRNG_tb; 15 | 16 | architecture neoTRNG_tb_rtl of neoTRNG_tb is 17 | 18 | -- neoTRNG -- 19 | component neoTRNG 20 | generic ( 21 | NUM_CELLS : natural range 1 to 99 := 3; 22 | NUM_INV_START : natural range 3 to 99 := 5; 23 | NUM_RAW_BITS : natural range 1 to 4096 := 64; 24 | SIM_MODE : boolean := false 25 | ); 26 | port ( 27 | clk_i : in std_ulogic; 28 | rstn_i : in std_ulogic; 29 | enable_i : in std_ulogic; 30 | valid_o : out std_ulogic; 31 | data_o : out std_ulogic_vector(7 downto 0) 32 | ); 33 | end component; 34 | 35 | -- generators -- 36 | signal clk_gen, rstn_gen, en_gen : std_ulogic := '0'; 37 | 38 | -- interface -- 39 | signal rnd_valid : std_ulogic; 40 | signal rnd_data : std_ulogic_vector(7 downto 0); 41 | 42 | begin 43 | 44 | -- generators -- 45 | clk_gen <= not clk_gen after 10 ns; 46 | rstn_gen <= '0', '1' after 25 ns; 47 | en_gen <= '0', '1' after 100 ns; 48 | 49 | -- dut -- 50 | neoTRNG_inst: neoTRNG 51 | generic map ( 52 | NUM_CELLS => NUM_CELLS, 53 | NUM_INV_START => NUM_INV_START, 54 | NUM_RAW_BITS => NUM_RAW_BITS, 55 | SIM_MODE => true -- this is a simulation 56 | ) 57 | port map ( 58 | clk_i => clk_gen, 59 | rstn_i => rstn_gen, 60 | enable_i => en_gen, 61 | valid_o => rnd_valid, 62 | data_o => rnd_data 63 | ); 64 | 65 | -- console output -- 66 | console_output : process(clk_gen) 67 | file output : text open write_mode is "STD_OUTPUT"; 68 | variable line_v : line; 69 | begin 70 | if rising_edge(clk_gen) then 71 | if (rnd_valid = '1') then 72 | write(line_v, integer'image(to_integer(unsigned(rnd_data)))); 73 | writeline(output, line_v); 74 | end if; 75 | end if; 76 | end process console_output; 77 | 78 | end neoTRNG_tb_rtl; 79 | -------------------------------------------------------------------------------- /rtl/neoTRNG.vhd: -------------------------------------------------------------------------------- 1 | -- ============================================================================================= -- 2 | -- neoTRNG - A Tiny and Platform-Independent True Random Number Generator (Version 3.3) -- 3 | -- https://github.com/stnolting/neoTRNG -- 4 | -- ============================================================================================= -- 5 | -- The neoTNG true-random number generator samples free-running ring-oscillators (combinatorial -- 6 | -- loops) to obtain *phase noise* hat is used as entropy source. The individual ring-oscillators -- 7 | -- are based on plain inverter chains that are decoupled using individually-enabled latches in -- 8 | -- order to prevent the synthesis tool from trimming parts of the logic. -- 9 | -- -- 10 | -- Hence, the TRNG provides a platform- agnostic architecture that can be implemented for any -- 11 | -- FPGA/ASIC without requiring primitive instantiation or technology-specific attributes or -- 12 | -- platform-specific synthesis options. -- 13 | -- -- 14 | -- The random output from each entropy cells is synchronized and XOR-ed with the other cell's -- 15 | -- outputs before it is and fed into a simple 2-bit "John von Neumann randomness extractor" -- 16 | -- (extracting edges). de-biased bits are combined using an CRC-style shift -- 17 | -- register (entropy compression) to generate one final random data byte. -- 18 | -- ============================================================================================= -- 19 | -- BSD 3-Clause License -- 20 | -- -- 21 | -- Copyright (c) 2025, Stephan Nolting. All rights reserved. -- 22 | -- -- 23 | -- Redistribution and use in source and binary forms, with or without modification, are -- 24 | -- permitted provided that the following conditions are met: -- 25 | -- -- 26 | -- 1. Redistributions of source code must retain the above copyright notice, this list of -- 27 | -- conditions and the following disclaimer. -- 28 | -- -- 29 | -- 2. Redistributions in binary form must reproduce the above copyright notice, this list of -- 30 | -- conditions and the following disclaimer in the documentation and/or other materials -- 31 | -- provided with the distribution. -- 32 | -- -- 33 | -- 3. Neither the name of the copyright holder nor the names of its contributors may be used to -- 34 | -- endorse or promote products derived from this software without specific prior written -- 35 | -- permission. -- 36 | -- -- 37 | -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS -- 38 | -- OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -- 39 | -- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -- 40 | -- COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -- 41 | -- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -- 42 | -- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -- 43 | -- AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -- 44 | -- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -- 45 | -- OF THE POSSIBILITY OF SUCH DAMAGE. -- 46 | -- ============================================================================================= -- 47 | 48 | library ieee; 49 | use ieee.std_logic_1164.all; 50 | use ieee.numeric_std.all; 51 | 52 | entity neoTRNG is 53 | generic ( 54 | NUM_CELLS : natural range 1 to 99 := 3; -- number of ring-oscillator cells 55 | NUM_INV_START : natural range 3 to 99 := 5; -- number of inverters in first ring-oscillator cell, has to be odd 56 | NUM_RAW_BITS : natural range 1 to 4096 := 64; -- number of XOR-ed raw bits per random sample byte (has to be a power of 2) 57 | SIM_MODE : boolean := false -- enable simulation mode (no physical random if enabled!) 58 | ); 59 | port ( 60 | clk_i : in std_ulogic; -- module clock 61 | rstn_i : in std_ulogic; -- module reset, low-active, async, optional 62 | enable_i : in std_ulogic; -- module enable (high-active) 63 | valid_o : out std_ulogic; -- data_o is valid when set (high for one cycle) 64 | data_o : out std_ulogic_vector(7 downto 0) -- random data byte output 65 | ); 66 | end neoTRNG; 67 | 68 | architecture neoTRNG_rtl of neoTRNG is 69 | 70 | -- round_up[log2(x)] -- 71 | function clog2_f(x : natural) return natural is 72 | begin 73 | for i in 0 to natural'high loop 74 | if (2**i >= x) then 75 | return i; 76 | end if; 77 | end loop; 78 | return 0; 79 | end function clog2_f; 80 | 81 | -- entropy source cell -- 82 | component neoTRNG_cell 83 | generic ( 84 | NUM_INV : natural range 3 to 999 := 3; -- number of inverters, has to be odd, min 3 85 | SIM_MODE : boolean := false -- enable simulation mode (no physical random if enabled!) 86 | ); 87 | port ( 88 | clk_i : in std_ulogic; -- clock 89 | rstn_i : in std_ulogic; -- reset, low-active, async, optional 90 | en_i : in std_ulogic; -- enable-chain input 91 | en_o : out std_ulogic; -- enable-chain output 92 | rnd_o : out std_ulogic -- random data (sync) 93 | ); 94 | end component; 95 | 96 | -- entropy cell interconnect -- 97 | signal cell_en_in : std_ulogic_vector(NUM_CELLS-1 downto 0); -- enable-sreg input 98 | signal cell_en_out : std_ulogic_vector(NUM_CELLS-1 downto 0); -- enable-sreg output 99 | signal cell_rnd : std_ulogic_vector(NUM_CELLS-1 downto 0); -- cell random output 100 | signal cell_sum : std_ulogic; -- combined random data 101 | 102 | -- de-biasing -- 103 | signal debias_sreg : std_ulogic_vector(1 downto 0); -- sample buffer 104 | signal debias_state : std_ulogic; -- process de-biasing every second cycle 105 | signal debias_valid : std_ulogic; -- result bit valid 106 | signal debias_data : std_ulogic; -- result bit 107 | 108 | -- sampling control -- 109 | signal sample_en : std_ulogic; -- global enable 110 | signal sample_sreg : std_ulogic_vector(7 downto 0); -- shift-register / de-serializer 111 | signal sample_cnt : std_ulogic_vector(clog2_f(NUM_RAW_BITS) downto 0); -- bits-per-sample counter 112 | 113 | -- CRC polynomial (tap mask) -- 114 | constant poly_c : std_ulogic_vector(7 downto 0) := "00000111"; -- CRC-8: x^8 + x^2 + x^1 + x^0 115 | 116 | begin 117 | 118 | -- Configuration Checks ------------------------------------------------------------------- 119 | -- ------------------------------------------------------------------------------------------- 120 | assert false report 121 | "[neoTRNG] The neoTRNG (v3.3) - A Tiny and Platform-Independent True Random Number Generator, " & 122 | "https://github.com/stnolting/neoTRNG" severity note; 123 | 124 | assert (NUM_INV_START mod 2) /= 0 report 125 | "[neoTRNG] Number of inverters in first cell [NUM_INV_START] has to be odd!" severity error; 126 | 127 | assert 2**clog2_f(NUM_RAW_BITS) = NUM_RAW_BITS report 128 | "[neoTRNG] Number of pre-processed raw bits [NUM_RAW_BITS] has to be a power of 2!" severity error; 129 | 130 | assert not SIM_MODE report 131 | "[neoTRNG] Simulation-mode enabled (NO TRUE/PHYSICAL RANDOM)!" severity warning; 132 | 133 | 134 | -- Entropy Source ------------------------------------------------------------------------- 135 | -- ------------------------------------------------------------------------------------------- 136 | entropy_cell_gen: 137 | for i in 0 to NUM_CELLS-1 generate 138 | neoTRNG_cell_inst: neoTRNG_cell 139 | generic map ( 140 | NUM_INV => NUM_INV_START + (2 * i), -- increasing (odd) ring-oscillator length 141 | SIM_MODE => SIM_MODE 142 | ) 143 | port map ( 144 | clk_i => clk_i, 145 | rstn_i => rstn_i, 146 | en_i => cell_en_in(i), 147 | en_o => cell_en_out(i), 148 | rnd_o => cell_rnd(i) 149 | ); 150 | end generate; 151 | 152 | -- enable-shift-register chain -- 153 | cell_en_in(0) <= sample_en; 154 | cell_en_in(NUM_CELLS-1 downto 1) <= cell_en_out(NUM_CELLS-2 downto 0); 155 | 156 | -- combine cell outputs -- 157 | combine: process(cell_rnd) 158 | variable tmp_v : std_ulogic; 159 | begin 160 | tmp_v := '0'; 161 | for i in 0 to NUM_CELLS-1 loop 162 | tmp_v := tmp_v xor cell_rnd(i); 163 | end loop; 164 | cell_sum <= tmp_v; 165 | end process combine; 166 | 167 | 168 | -- John von Neumann Randomness Extractor (De-Biasing) ------------------------------------- 169 | -- ------------------------------------------------------------------------------------------- 170 | debiasing: process(rstn_i, clk_i) 171 | begin 172 | if (rstn_i = '0') then 173 | debias_sreg <= (others => '0'); 174 | debias_state <= '0'; 175 | elsif rising_edge(clk_i) then 176 | debias_sreg <= debias_sreg(0) & cell_sum; 177 | -- start operation when last cell is enabled and process in every second cycle -- 178 | debias_state <= (not debias_state) and cell_en_out(cell_en_out'left); 179 | end if; 180 | end process debiasing; 181 | 182 | -- check groups of two non-overlapping bits from the random stream for edges -- 183 | debias_valid <= debias_state and (debias_sreg(1) xor debias_sreg(0)); 184 | debias_data <= debias_sreg(0); 185 | 186 | 187 | -- Sampling Control ----------------------------------------------------------------------- 188 | -- ------------------------------------------------------------------------------------------- 189 | sampling_control: process(rstn_i, clk_i) 190 | begin 191 | if (rstn_i = '0') then 192 | sample_en <= '0'; 193 | sample_cnt <= (others => '0'); 194 | sample_sreg <= (others => '0'); 195 | elsif rising_edge(clk_i) then 196 | sample_en <= enable_i; 197 | if (sample_en = '0') or (sample_cnt(sample_cnt'left) = '1') then -- start new iteration 198 | sample_cnt <= (others => '0'); 199 | sample_sreg <= (others => '0'); 200 | elsif (debias_valid = '1') then -- valid raw random bit 201 | sample_cnt <= std_ulogic_vector(unsigned(sample_cnt) + 1); 202 | -- CRC-style sampling shift-register to mix random stream -- 203 | if ((sample_sreg(sample_sreg'left) xor debias_data) = '1') then -- feedback bit 204 | sample_sreg <= (sample_sreg(sample_sreg'left-1 downto 0) & '0') xor poly_c; 205 | else 206 | sample_sreg <= (sample_sreg(sample_sreg'left-1 downto 0) & '0'); 207 | end if; 208 | end if; 209 | end if; 210 | end process sampling_control; 211 | 212 | -- TRNG output stream -- 213 | data_o <= sample_sreg; 214 | valid_o <= sample_cnt(sample_cnt'left); 215 | 216 | end neoTRNG_rtl; 217 | 218 | 219 | -- ================================================================================================ -- 220 | -- neoTRNG entropy source cell, based on a simple ring-oscillator constructed from an odd number -- 221 | -- of inverter. The inverters are decoupled using individually-enabled latches to prevent synthesis -- 222 | -- from "optimizing" (=removing) parts of the oscillator chain. -- 223 | -- ================================================================================================ -- 224 | 225 | library ieee; 226 | use ieee.std_logic_1164.all; 227 | 228 | entity neoTRNG_cell is 229 | generic ( 230 | NUM_INV : natural range 3 to 999 := 3; -- number of inverters, has to be odd, min 3 231 | SIM_MODE : boolean := false -- enable simulation mode (no physical random if enabled!) 232 | ); 233 | port ( 234 | clk_i : in std_ulogic; -- clock 235 | rstn_i : in std_ulogic; -- reset, low-active, async, optional 236 | en_i : in std_ulogic; -- enable-chain input 237 | en_o : out std_ulogic; -- enable-chain output 238 | rnd_o : out std_ulogic -- random data (sync) 239 | ); 240 | end neoTRNG_cell; 241 | 242 | architecture neoTRNG_cell_rtl of neoTRNG_cell is 243 | 244 | signal sreg : std_ulogic_vector(NUM_INV-1 downto 0); -- enable-shift-register 245 | signal latch : std_ulogic_vector(NUM_INV-1 downto 0); -- ring oscillator: latches 246 | signal inv_in : std_ulogic_vector(NUM_INV-1 downto 0); -- ring oscillator: inverter inputs 247 | signal inv_out : std_ulogic_vector(NUM_INV-1 downto 0); -- ring oscillator: inverter outputs 248 | signal sync : std_ulogic_vector(1 downto 0); -- output synchronizer 249 | 250 | begin 251 | 252 | -- Enable Shift-Register ------------------------------------------------------------------ 253 | -- ------------------------------------------------------------------------------------------- 254 | -- Using individual enable signals from a shift register for each inverter in order to prevent 255 | -- the synthesis tool from removing all but one inverter (since they implement "logical 256 | -- identical functions"). This makes the TRNG platform independent as we do not require tool-/ 257 | -- technology-specific primitives, attributes or other options. 258 | 259 | en_shift_reg: process(rstn_i, clk_i) 260 | begin 261 | if (rstn_i = '0') then 262 | sreg <= (others => '0'); 263 | elsif rising_edge(clk_i) then 264 | sreg <= sreg(sreg'left-1 downto 0) & en_i; 265 | end if; 266 | end process en_shift_reg; 267 | 268 | -- output for global enable chain -- 269 | en_o <= sreg(sreg'left); 270 | 271 | 272 | -- Physical Entropy Source: Ring Oscillator ----------------------------------------------- 273 | -- ------------------------------------------------------------------------------------------- 274 | -- Each cell is based on a simple ring oscillator with an odd number of inverters. Each 275 | -- inverter is followed by a latch that provides a reset (to start in a defined state) and 276 | -- a latch-enable to make the latch transparent. Switching to transparent mode is done one by 277 | -- one by the enable shift register. 278 | 279 | ring_osc_gen: 280 | for i in 0 to NUM_INV-1 generate 281 | 282 | -- latch with global reset and individual enable -- 283 | latch(i) <= '0' when (en_i = '0') else latch(i) when (sreg(i) = '0') else inv_out(i); 284 | 285 | -- inverter for actual synthesis -- 286 | inverter_phy: 287 | if not SIM_MODE generate 288 | inv_out(i) <= not inv_in(i); -- this is one part of the ring oscillator's physical propagation delay 289 | end generate; 290 | 291 | -- inverter with "propagation delay" (implemented as a simple FF) -- 292 | inverter_sim: 293 | if SIM_MODE generate -- for SIMULATION ONLY 294 | inverter_sim_ff: process(clk_i) -- this will NOT generate true random numbers 295 | begin 296 | if rising_edge(clk_i) then 297 | inv_out(i) <= not inv_in(i); 298 | end if; 299 | end process inverter_sim_ff; 300 | end generate; 301 | 302 | end generate; 303 | 304 | -- chaining -- 305 | inv_in(0) <= latch(NUM_INV-1); -- beginning/end of chain 306 | inv_in(NUM_INV-1 downto 1) <= latch(NUM_INV-2 downto 0); -- inside chain 307 | 308 | 309 | -- Output Synchronizer -------------------------------------------------------------------- 310 | -- ------------------------------------------------------------------------------------------- 311 | -- Sample the actual entropy source (= phase noise) and move it to the system's clock domain. 312 | 313 | synchronizer: process(rstn_i, clk_i) 314 | begin 315 | if (rstn_i = '0') then 316 | sync <= (others => '0'); 317 | elsif rising_edge(clk_i) then 318 | sync <= sync(0) & latch(latch'left); 319 | end if; 320 | end process synchronizer; 321 | 322 | -- cell output -- 323 | rnd_o <= sync(1); 324 | 325 | end neoTRNG_cell_rtl; 326 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The neoTRNG True Random Number Generator 2 | 3 | **A Tiny and Platform-Independent True Random Number Generator for _any_ FPGA (and even ASICs).** 4 | 5 | [![neoTRNG simulation](https://github.com/stnolting/neoTRNG/actions/workflows/main.yml/badge.svg)](https://github.com/stnolting/neoTRNG/actions/workflows/main.yml) 6 | [![Release](https://img.shields.io/github/v/release/stnolting/neoTRNG)](https://github.com/stnolting/neoTRNG/releases) 7 | [![License](https://img.shields.io/github/license/stnolting/neoTRNG)](https://github.com/stnolting/neoTRNG/blob/main/LICENSE) 8 | [![DOI](https://zenodo.org/badge/430418414.svg)](https://zenodo.org/badge/latestdoi/430418414) 9 | 10 | * [Introduction](#introduction) 11 | * [Top Entity, Integration and Interface](#top-entity) 12 | * [Theory of Operation / Architecture](#architecture) 13 | * [Evaluation](#evaluation) 14 | * [Hardware Utilization](#hardware-utilization) 15 | * [Simulation](#simulation) 16 | * [Acknowledgments](#acknowledgments) 17 | * [References](#references) 18 | 19 | 20 | ## Introduction 21 | 22 | The neoTRNG aims to be a small and **platform-agnostic** TRUE random number generator (TRNG) that 23 | can be synthesized for _any_ target technology (FPGAs and even ASICs). It is based on simple free-running 24 | ring-oscillators, which are enhanced by a _special technique_ in order to allow synthesis for any platform. 25 | The _phase noise_ that occurs when sampling free-running ring-oscillators is used as physical entropy source. 26 | 27 | This project is a "spin-off" from the [NEORV32 RISC-V Processor](https://github.com/stnolting/neorv32) where 28 | the neoTRNG is implemented as default SoC module. 29 | 30 | **Key Features** 31 | 32 | * [x] technology, vendor and platform/technology independent - can be synthesized for **any** platform 33 | * [x] tiny hardware footprint (less than 100 LUT4s/FFs for the standard configuration) 34 | * [x] high throughput (for a physical TRNG) 35 | * [x] fully open source with a [permissive license](https://github.com/stnolting/neoTRNG/blob/main/LICENSE) 36 | * [x] full-digital design; single-file VHDL module without any dependencies 37 | * [x] very high operating frequency to ease timing closure 38 | * [x] easy to use / simple integration 39 | * [x] full documentation down to rtl level + evaluation 40 | 41 | > [!CAUTION] 42 | > It is possible that there might be at least _some_ cross correlations between internal/external 43 | signals/events and the generated random numbers. Hence, there is **no guarantee at all** the neoTRNG provides 44 | _perfect or even cryptographically secure_ random numbers! See the provided evaluation results or (even better) 45 | test it by yourself. Furthermore, there is no tampering detection mechanism or online health monitoring available 46 | yet to check for integrity/quality of the generated random data. 47 | 48 | > [!WARNING] 49 | > Keeping the neoTRNG _permanently enabled_ will increase dynamic power consumption and might also cause 50 | local heating of the chip (when using very large configurations). Furthermore, additional electromagnetic 51 | interference (EMI) might be emitted by the design. 52 | 53 | 54 | ## Top Entity 55 | 56 | The whole design is implemented as a single VHDL file 57 | [`rtl/neoTRNG.vhd`](https://github.com/stnolting/neoTRNG/blob/main/rtl/neoTRNG.vhd) that 58 | has no dependencies at all (like special libraries, packages or submodules). 59 | 60 | ```vhdl 61 | entity neoTRNG is 62 | generic ( 63 | NUM_CELLS : natural range 1 to 99 := 3; -- number of ring-oscillator cells 64 | NUM_INV_START : natural range 3 to 99 := 5; -- number of inverters in first ring-oscillator cell, has to be odd 65 | NUM_RAW_BITS : natural range 1 to 4096 := 64; -- number of XOR-ed raw bits per random sample byte (has to be a power of 2) 66 | SIM_MODE : boolean := false -- enable simulation mode (no physical random if enabled!) 67 | ); 68 | port ( 69 | clk_i : in std_ulogic; -- module clock 70 | rstn_i : in std_ulogic; -- module reset, low-active, async, optional 71 | enable_i : in std_ulogic; -- module enable (high-active) 72 | valid_o : out std_ulogic; -- data_o is valid when set (high for one cycle) 73 | data_o : out std_ulogic_vector(7 downto 0) -- random data byte output 74 | ); 75 | end neoTRNG; 76 | ``` 77 | 78 | ### Interface and Configuration 79 | 80 | The neoTRNG uses a single clock domain driven by the `clk_i` signal. The module's reset signal `rstn_i` 81 | is _optional_ (tie to `'1'` if not used). Random data is obtained by using a simple data/valid interface: 82 | whenever a new valid random byte is available the `valid_o` output will be high for exactly one cycle so 83 | the `data_o` output can be sampled by the user logic. 84 | 85 | The `enable_i` signal is used to initialize and start the TRNG. Before the TRNG can be used this signal 86 | should be kept low for at least several 100 clock cycles (depending on the configuration) to ensure that 87 | all bits of the internal shift registers are cleared again. When `enable_i` is set and `valid_o` becomes 88 | set for the first time the TRNG is operational. Disabling the TRNG also requires `enable_i` being low for 89 | the same amount of clock cycles. When `enable_i` gets low all ring-oscillators will be stopped reducing 90 | dynamic switching activity and power consumption. 91 | 92 | Three generics are provided to configure the neoTRNG. `NUM_CELLS` defines the total number of entropy 93 | cells. `NUM_INV_START` defines the number of inverters (= the length of the ring-oscillator) in the very 94 | first cell. These two generics are further described in the [Architecture](#architecture) section below. 95 | `NUM_RAW_BITS` defines the number of raw entropy bits that get XOR-ed into the final random sample byte. 96 | The last generic `SIM_MODE` can be set to allow [simulating](#simulation) of the TRNG within a plain RTL 97 | simulation. 98 | 99 | 100 | ## Architecture 101 | 102 | ![neoTRNG architecture](https://raw.githubusercontent.com/stnolting/neoTRNG/main/img/neotrng_architecture.png) 103 | 104 | The neoTRNG is based on a configurable number (`NUM_CELLS`) of [entropy cells](#entropy-cells). Each cell 105 | provides a simple a ring-oscillator ("RO") that is built using an odd number of inverters. The oscillation 106 | frequency of the RO is defined by the propagation delay of the elements within the ring. This frequency is 107 | not static as it is subject to minimal fluctuations caused by thermal noise electronic shot noise. The 108 | state of the RO's last inverter is sampled into a flip flop by using a static clock (`clk_i`). As the RO's 109 | frequency chaotically varies over time the inherent **phase noise** of the sampled data is used as actual 110 | entropy source. 111 | 112 | Each entropy cell generates a 1-bit stream of random data. The outputs of all cells are mixed using a wide 113 | XOR gate before the stream is [de-biased](#de-biasing) by a simple randomness extractor. Several de-biased 114 | bits are sampled / de-serialized by the [sampling unit](#sampling-unit) to provide byte-wide random number. 115 | The sampling unit also applies a simple post-processing in order to improve the spectral distribution of 116 | the random numbers. 117 | 118 | ### Entropy Cells 119 | 120 | Each entropy cell consists of a ring-oscillator that is build from an odd number of **inverting latches**. 121 | The length of ring in the very first entropy cell is defined by the `NUM_INV_START` generic. Every 122 | additional entropy cell adds another 2 inverters to this initial chain length. Hence, each additional 123 | entropy cell oscillates at a lower frequency then the one before. 124 | 125 | Asynchronous elements like ring-oscillators are hard to implement in a platform-independent way as they 126 | usually require the use of platform-/technology-specific primitives, attributes or synthesis settings. In 127 | order to provide a real target-agnostic architecture, which can be synthesized for any target technology, 128 | a special technique is applied: each inverter inside the RO is followed by a **latch** that provides a 129 | global reset and also an individual latch-enable to switch the latch to transparent mode. 130 | 131 | The individual latch-enables are controlled by a long shift register that features a distinct FF for every 132 | single latch in the RO chain. When the TRNG is enabled, this shift register starts to fill with ones. Thus, 133 | the latches are individually enabled one-by-one making it impossible for the synthesis tool to trim any 134 | logic/elements from the RO chain as the start-up states of each latch can (theoretically) be monitored by 135 | external logic. The enable shift register of all entropy cells are daisy-chained to continue this start-up 136 | procedure across the entire entropy array. 137 | 138 | The following image shows the simplified schematic of the very first entropy cell consisting of 5 139 | inverter-latch elements for the rings oscillator, 5 flip flops for the enable shift register and another 2 140 | flip flops for the synchronizer. 141 | 142 | ![neoTRNG entropy cell](https://raw.githubusercontent.com/stnolting/neoTRNG/main/img/neotrng_ring_oscillator.png) 143 | 144 | An image showing the FPGA the mapping result (generated by Intel Quartus Prime) of the very first entropy 145 | cell can be seen below. It shows that all latch+inverter elements of the ring-oscillator chain were 146 | successfully mapped to individual LUT4s. 147 | 148 | ![neoTRNG Quartus Prime mapping](https://raw.githubusercontent.com/stnolting/neoTRNG/main/img/neotrng_cell_map.png) 149 | 150 | 151 | ### De-Biasing 152 | 153 | As soon as the last bit of the entropy cell's daisy-chained enable shift register is set the de-biasing 154 | unit gets started. This unit implements a simple "John von Neumann Randomness Extractor" to de-bias the 155 | obtained random data stream. The extractor implements a 2-bit shift register that samples the XOR-ed 156 | random bit from the entropy cell array. In every second cycle the extractor evaluates the two sampled bits 157 | to check a non-overlapping pair of bits for _edges_. 158 | 159 | ![neoTRNG de-biasing](https://raw.githubusercontent.com/stnolting/neoTRNG/main/img/neotrng_debiasing.png) 160 | 161 | Whenever an edge has been detected a "valid" signal is send to the following sampling unit. A rising-edge 162 | (`01`) emits a `1` data bit and a falling-edge (`10`) emits a `0` data bit. Hence, the de-biasing unit 163 | requires at least two clock cycles to generate a single random bit. If no edge is detected (`00` or `11`) 164 | the valid signal remains low and the sampling unit halts. 165 | 166 | ### Sampling Unit 167 | 168 | The sampling unit implements a 8-bit shift register to convert the serial de-biased bitstream into byte-wide 169 | random numbers. Additionally, the sample unit provides a simple post processing to improve the spectral 170 | distribution of the obtained random samples. 171 | 172 | ![neoTRNG sampling unit](https://raw.githubusercontent.com/stnolting/neoTRNG/main/img/neotrng_sampling_unit.png) 173 | 174 | In order to generate one byte of random data the sampling unit reset its internal shift register to all-zero 175 | and starts consuming single bits from the de-biased random stream. By default, 64 raw entropy bits are used, 176 | but this number can be adjusted by the `NUM_RAW_BITS` generic (using more random bits might improve random 177 | quality at the extend of the final generation rate). The shift register implements a simple 8-bit 178 | [CRC](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) for entropy compression. The following 179 | polynomial is used: `x^8 + x^2 + x^1 + 1` 180 | 181 | 182 | ## Evaluation 183 | 184 | The neoTRNG is evaluated as part of the [NEORV32](https://github.com/stnolting/neorv32) processor, where the 185 | neoTRNG is available as standard SoC module. The system was implemented on an AMD Artix-7 (`xc7a35ticsg324-1L`) 186 | FPGA running at 150MHz. For the evaluation the tiny **default configuration** has been used: 187 | 188 | ``` 189 | NUM_CELLS = 3 190 | NUM_INV_START = 5 191 | NUM_RAW_BITS = 64 192 | SIM_MODE = false 193 | ``` 194 | 195 | > [!NOTE] 196 | > A total amount of **32MB** of random data has been obtained for the evaluations. This data set is 197 | available as `data.bin` binary file in the [release](https://github.com/stnolting/neoTRNG/releases) assets. 198 | 199 | ### Histogram Analysis 200 | 201 | For the simple histogram analysis 32MB of random bytes were sampled from the neoTRNG. The obtained bytes 202 | were accumulated according to their occurrence and sorted into bins where each bin represents one specific 203 | byte pattern (1 byte = 8 bits = 256 different patterns). The resulting was then analyzed with regard to 204 | its statistical properties: 205 | 206 | * arithmetic mean of all sampled random bytes 207 | * average occurrence across all bit patterns 208 | * min and max occurrences and deviation from the average occurrence 209 | 210 | ``` 211 | [NOTE] integer numbers only 212 | Number of samples: 33554432 213 | Arithmetic mean: 127 (optimum would be 127) 214 | 215 | Histogram occurrence 216 | Average: 131072 (optimum would be 33554432/256 = 131072) 217 | Min: 130036 = average - 1036 (deviation) at bin 210 (optimum deviation would be 0) 218 | Max: 132035 = average + 963 (deviation) at bin 163 (optimum deviation would be 0) 219 | Average dev.: +/- 282 (optimum would be 0) 220 | ``` 221 | 222 | ### Entropy per Byte 223 | 224 | ``` 225 | Entropy = 7.999995 bits per byte. 226 | 227 | Optimum compression would reduce the size 228 | of this 33226752 byte file by 0 percent. 229 | 230 | Chi square distribution for 33226752 samples is 243.56, and randomly 231 | would exceed this value 68.61 percent of the times. 232 | 233 | Arithmetic mean value of data bytes is 127.4802 (127.5 = random). 234 | Monte Carlo value for Pi is 3.141387759 (error 0.01 percent). 235 | Serial correlation coefficient is 0.000155 (totally uncorrelated = 0.0). 236 | ``` 237 | 238 | ### FIPS 140-2 RNG Tests 239 | 240 | ``` 241 | $ rngtest < entropy.bin 242 | rngtest 5 243 | Copyright (c) 2004 by Henrique de Moraes Holschuh 244 | This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 245 | 246 | rngtest: starting FIPS tests... 247 | rngtest: entropy source drained 248 | rngtest: bits received from input: 265814016 249 | rngtest: FIPS 140-2 successes: 13279 250 | rngtest: FIPS 140-2 failures: 11 251 | rngtest: FIPS 140-2(2001-10-10) Monobit: 1 252 | rngtest: FIPS 140-2(2001-10-10) Poker: 1 253 | rngtest: FIPS 140-2(2001-10-10) Runs: 4 254 | rngtest: FIPS 140-2(2001-10-10) Long run: 5 255 | rngtest: FIPS 140-2(2001-10-10) Continuous run: 0 256 | ``` 257 | 258 | ### Hardware Utilization 259 | 260 | Mapping results for the neoTRNG implemented within the NEORV32 RISC-V Processor using the default 261 | configuration. Results generated for an Intel Cyclone `EP4CE22F17C6N` FPGA running at 100MHz using Intel 262 | Quartus Prime. 263 | 264 | ``` 265 | Module Hierarchy Logic Cells Logic Registers 266 | ------------------------------------------------------------------------------------ 267 | neoTRNG:neoTRNG_inst 57 (27) 46 (19) 268 | neoTRNG_cell:\entropy_source:0:neoTRNG_cell_inst 8 (8) 7 (7) 269 | neoTRNG_cell:\entropy_source:1:neoTRNG_cell_inst 10 (10) 9 (9) 270 | neoTRNG_cell:\entropy_source:2:neoTRNG_cell_inst 15 (15) 11 (11) 271 | ``` 272 | 273 | > [!NOTE] 274 | > Synthesis tools might emit a warning that latches and combinatorial loops 275 | have been detected. However, this is no design flaw as this is exactly what we want. :wink: 276 | 277 | ### Throughput 278 | 279 | The neoTRNG's maximum generation rate is defined by two factors: 280 | 281 | * A = 2: cycles required by the de-biasing logic to output one raw random bit 282 | * B = NUM_RAW_BITS (default = 64): number of raw random bits required by the sampling unit to generate one random byte 283 | 284 | Hence, the neoTRNG requires _at least_ `A * B = 2 * 64 = 128` clock cycles to emit one random byte. 285 | FPGA evaluation has shown that the actual sampling time is around 300 clock cycles. Thus, an 286 | implementation running at 100 MHz can generate approximately 330kB of random data per second. 287 | Higher generation rates can be achieved by running several neoTRNG instances in parallel. 288 | 289 | 290 | ## Simulation 291 | 292 | Since the asynchronous ring-oscillators cannot be rtl-simulated (due to the combinatorial loops), the 293 | neoTRNG provides a dedicated simulation mode that is enabled by the `SIM_MODE` generic. When enabled, 294 | a "propagation delay" implemented as simple flip flop is added to the ring-oscillator's inverters. 295 | 296 | > [!IMPORTANT] 297 | > The simulation mode is intended for simulation/debugging only! 298 | > Designs with `SIM_MODE` enabled can be synthesized but will **not provide any true/physical random** numbers at all! 299 | 300 | The [`sim`](https://github.com/stnolting/neoTRNG/sim) folder provides a simple testbench for the neoTRNG 301 | using the default configuration. The testbench will output the obtained random data bytes as decimal 302 | values to the simulator console. The testbench can be simulated with GHDL by using the provided script: 303 | 304 | ``` 305 | neoTRNG/sim$ sh ghdl.sh 306 | ../rtl/neoTRNG.vhd:120:3:@0ms:(assertion note): [neoTRNG] The neoTRNG (v3.3) - A Tiny and Platform-Independent True Random Number Generator, https://github.com/stnolting/neoTRNG 307 | ../rtl/neoTRNG.vhd:130:3:@0ms:(assertion warning): [neoTRNG] Simulation-mode enabled (NO TRUE/PHYSICAL RANDOM)! 308 | 89 309 | 147 310 | 99 311 | 116 312 | 11 313 | 55 314 | 203 315 | 84 316 | 97 317 | 204 318 | 117 319 | 196 320 | ghdl:info: simulation stopped by --stop-time @100u 321 | ``` 322 | 323 | The GHDL waveform data is stored to `sim/neoTRNG_tb.ghw` and can be viewed using `gtkwave`: 324 | 325 | ``` 326 | neoTRNG/sim$ gtkwave neoTRNG_tb.ghw 327 | ``` 328 | 329 | A simple simulation run is executed by the [`neoTRNG-sim`](https://github.com/stnolting/neoTRNG/actions) 330 | GitHub actions workflow. 331 | 332 | 333 | ## Acknowledgments 334 | 335 | A big thank you to Maarten Baert ([@MaartenBaert](https://github.com/MaartenBaert)) who did a great 336 | [evaluation of the neoTRNG (v3.2)](https://github.com/stnolting/neoTRNG/issues/6) 337 | and came up with excellent ideas to improve it. 338 | 339 | 340 | ## References 341 | 342 | * Kumar, Sandeep S., et al. "The butterfly PUF protecting IP on every FPGA." 2008 IEEE International Workshop 343 | on Hardware-Oriented Security and Trust. IEEE, 2008. 344 | * Tuncer, Taner, et al. "Implementation of non-periodic sampling true random number generator on FPGA." 345 | Informacije Midem 44.4 (2014): 296-302. 346 | --------------------------------------------------------------------------------