Skip to content

Commit

Permalink
⚠️ rework TRNG (#1120)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting authored Dec 14, 2024
2 parents 79e9df2 + a1ed9af commit e56a8fd
Show file tree
Hide file tree
Showing 18 changed files with 134 additions and 190 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 14.12.2024 | 1.10.7.3 | :warning: rework TRNG (change HAL; remove interrupt) | [#1120](https://github.com/stnolting/neorv32/pull/1120) |
| 12.12.2024 | 1.10.7.2 | add external memory configuration/initialization options to testbench | [#1119](https://github.com/stnolting/neorv32/pull/1119) |
| 11.12.2024 | 1.10.7.1 | :test_tube: shrink bootloader's minimal ISA (`rv32e`) and RAM (256 bytes) requirements | [#1118](https://github.com/stnolting/neorv32/pull/1118) |
| 10.12.2024 | [**:rocket:1.10.7**](https://github.com/stnolting/neorv32/releases/tag/v1.10.7) | **New release** | |
Expand Down
2 changes: 1 addition & 1 deletion docs/datasheet/soc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ table (the channel number also corresponds to the according FIRQ priority: 0 = h
[options="header",grid="rows"]
|=======================
| Channel | Source | Description
| 0 | <<_true_random_number_generator_trng,TRNG>> | TRNG data available interrupt
| 0 | _reserved_ | _hardwired to zero_
| 1 | <<_custom_functions_subsystem_cfs,CFS>> | Custom functions subsystem (CFS) interrupt (user-defined)
| 2 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 RX FIFO level interrupt
| 3 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 TX FIFO level interrupt
Expand Down
55 changes: 23 additions & 32 deletions docs/datasheet/soc_trng.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
| Top entity ports: | none |
| Configuration generics: | `IO_TRNG_EN` | implement TRNG when `true`
| | `IO_TRNG_FIFO` | data FIFO depth, min 1, has to be a power of two
| CPU interrupts: | fast IRQ channel 0 | TRNG data available interrupt (see <<_processor_interrupts>>)
| CPU interrupts: | none
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
|=======================

Expand All @@ -33,34 +33,27 @@ neoTRNG repository: https://github.com/stnolting/neoTRNG
The synthesis tool might emit warnings regarding **inferred latches** or **combinatorial loops**. However, this
is not design flaw as this is exactly what we want. ;)

.Simulation
[IMPORTANT]
When simulating the processor the TRNG is automatically set to "simulation mode". In this mode the physical entropy
sources (the ring oscillators) are replaced by a simple **pseudo RNG** based on a LFSR providing only
**deterministic pseudo-random** data. The `TRNG_CTRL_SIM_MODE` flag of the control register is set if simulation
mode is active.


**Theory of Operation**

The TRNG features a single control register `CTRL` for control, status check and data access. When the `TRNG_CTRL_EN`
bit is set, the TRNG is enabled and starts operation. As soon as the `TRNG_CTRL_VALID` bit is set a new random data byte
is available and can be obtained from the lowest 8 bits of the `CTRL` register. If this bit is cleared, there is no
valid data available and the lowest 8 bit of the `CTRL` register are set to all-zero.
The TRNG provides two memory mapped interface register. One control register (`CTRL`) for configuration and
status check and one data register (`DATA`) for obtaining the random data. The TRNG is enabled by setting the
control register's `TRNG_CTRL_EN`. As soon as the `TRNG_CTRL_AVAIL` bit is set a new random data byte is
available and can be obtained from the lowest 8 bits of the `DATA` register. If this bit is cleared, there
is no valid data available and the reading `DATA` will return all-zero.

An internal entropy FIFO can be configured using the `IO_TRNG_FIFO` generic. This FIFO automatically samples
new random data from the TRNG to provide some kind of _random data pool_ for applications, which require a large number
of random data in a short time. The random data FIFO can be cleared at any time either by disabling the TRNG or by
setting the `TRNG_CTRL_FIFO_CLR` flag. The FIFO depth can be retrieved by software via the `TRNG_CTRL_FIFO_*` bits.

new random data from the TRNG to provide some kind of _random data pool_ for applications which require a
larger number of random data in a short time. The random data FIFO can be cleared at any time either by
disabling the TRNG or by setting the `TRNG_CTRL_FIFO_CLR` flag. The FIFO depth can be retrieved by software
via the `TRNG_CTRL_FIFO_*` bits.

**TRNG Interrupt**

As the neoTRNG is a rather slow entropy source, a "data available" interrupt is provided to inform the application
software that new random data is available. This interrupt can be trigger by either of two conditions: trigger the
interrupt if _any_ random data is available (i.e. the data FIFO is not empty; `TRNG_CTRL_IRQ_SEL = 0`) or trigger
the interrupt if the random pool is full (i.e. the data FIFO is full; `TRNG_CTRL_IRQ_SEL = 1`).
Once the TRNG interrupt has fired it remains pending until the actual cause of the interrupt is resolved.
.Simulation
[IMPORTANT]
When simulating the processor the TRNG is automatically set to "simulation mode". In this mode the physical
entropy sources (the ring oscillators) are replaced by a simple **pseudo RNG** based on a LFSR providing only
**deterministic pseudo-random** data. The `TRNG_CTRL_SIM_MODE` flag of the control register is set if simulation
mode is active.


**Register Map**
Expand All @@ -70,13 +63,11 @@ Once the TRNG interrupt has fired it remains pending until the actual cause of t
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.9+<| `0xfffffa00` .9+<| `CTRL` <|`7:0` `TRNG_CTRL_DATA_MSB : TRNG_CTRL_DATA_MSB` ^| r/- <| 8-bit random data
<|`15:8` - ^| r/- <| reserved, read as zero
<|`19:16` `TRNG_CTRL_FIFO_MSB : TRNG_CTRL_FIFO_MSB` ^| r/- <| FIFO depth, log2(`IO_TRNG_FIFO`)
<|`27:20` - ^| r/- <| reserved, read as zero
<|`27` `TRNG_CTRL_IRQ_SEL` ^| r/w <| interrupt trigger select (0 = data available, 1 = FIFO full)
<|`28` `TRNG_CTRL_FIFO_CLR` ^| -/w <| flush random data FIFO when set; flag auto-clears
<|`29` `TRNG_CTRL_SIM_MODE` ^| r/- <| simulation mode (PRNG!)
<|`30` `TRNG_CTRL_EN` ^| r/w <| TRNG enable
<|`31` `TRNG_CTRL_VALID` ^| r/- <| random data is valid when set
.5+<| `0xfffffa00` .5+<| `CTRL` <|`0` `TRNG_CTRL_EN` ^| r/w <| TRNG enable
<|`1` `TRNG_CTRL_FIFO_CLR` ^| -/w <| flush random data FIFO when set; flag auto-clears
<|`5:2` `TRNG_CTRL_FIFO_MSB : TRNG_CTRL_FIFO_LSB` ^| r/- <| FIFO depth, log2(`IO_TRNG_FIFO`)
<|`6` `TRNG_CTRL_SIM_MODE` ^| r/- <| simulation mode (PRNG!)
<|`7` `TRNG_CTRL_AVAIL` ^| r/- <| random data available when set
.2+<| `0xfffffa04` .2+<| `DATA` <|`7:0` `TRNG_DATA_MSB : TRNG_DATA_LSB` ^| r/- <| random data byte
<|`31:8` _reserved_ ^| r/- <| reserved, read as zero
|=======================
Binary file modified docs/figures/neorv32_processor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion rtl/core/neorv32_cpu_cp_muldiv.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ begin
elsif rising_edge(clk_i) then
-- defaults --
ctrl.out_en <= '0';
ctrl.cnt <= std_ulogic_vector(to_unsigned(XLEN-2, index_size_f(XLEN))); -- cycle counter initialization
ctrl.cnt <= std_ulogic_vector(to_unsigned(XLEN-2, ctrl.cnt'length)); -- cycle counter initialization

-- fsm --
case ctrl.state is
Expand Down
6 changes: 3 additions & 3 deletions rtl/core/neorv32_fifo.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use neorv32.neorv32_package.all;

entity neorv32_fifo is
generic (
FIFO_DEPTH : natural := 4; -- number of fifo entries; has to be a power of two; min 1
FIFO_WIDTH : natural := 32; -- size of data elements in fifo
FIFO_DEPTH : natural := 4; -- number of FIFO entries; has to be a power of two; min 1
FIFO_WIDTH : natural := 32; -- size of data elements in FIFO
FIFO_RSYNC : boolean := false; -- false = async read; true = sync read
FIFO_SAFE : boolean := false; -- true = allow read/write only if data available
FULL_RESET : boolean := false -- true = reset all memory cells (cannot be mapped to BRAM)
Expand Down Expand Up @@ -50,7 +50,7 @@ architecture neorv32_fifo_rtl of neorv32_fifo is
signal fifo_mem : fifo_mem_t; -- for fifo_depth_c > 1
signal fifo_reg : std_ulogic_vector(FIFO_WIDTH-1 downto 0); -- for fifo_depth_c = 1

-- Fifo control and status --
-- FIFO control and status --
signal we, re, match, empty, full, half, free, avail : std_ulogic;

-- write/read pointer --
Expand Down
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ package neorv32_package is

-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100702"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100703"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

Expand Down
10 changes: 4 additions & 6 deletions rtl/core/neorv32_top.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ architecture neorv32_top_rtl of neorv32_top is

-- IRQs --
type firq_enum_t is (
FIRQ_TRNG, FIRQ_UART0_RX, FIRQ_UART0_TX, FIRQ_UART1_RX, FIRQ_UART1_TX, FIRQ_SPI, FIRQ_SDI, FIRQ_TWI,
FIRQ_reserved, FIRQ_UART0_RX, FIRQ_UART0_TX, FIRQ_UART1_RX, FIRQ_UART1_TX, FIRQ_SPI, FIRQ_SDI, FIRQ_TWI,
FIRQ_CFS, FIRQ_NEOLED, FIRQ_XIRQ, FIRQ_GPTMR, FIRQ_ONEWIRE, FIRQ_DMA, FIRQ_SLINK_RX, FIRQ_SLINK_TX
);
type firq_t is array (firq_enum_t) of std_ulogic;
Expand Down Expand Up @@ -544,7 +544,7 @@ begin
);

-- fast interrupt requests (FIRQs) --
cpu_firq(0) <= firq(FIRQ_TRNG);
cpu_firq(0) <= '0'; -- reserved
cpu_firq(1) <= firq(FIRQ_CFS);
cpu_firq(2) <= firq(FIRQ_UART0_RX);
cpu_firq(3) <= firq(FIRQ_UART0_TX);
Expand Down Expand Up @@ -1388,21 +1388,19 @@ begin
if IO_TRNG_EN generate
neorv32_trng_inst: entity neorv32.neorv32_trng
generic map (
IO_TRNG_FIFO => IO_TRNG_FIFO
TRNG_FIFO => IO_TRNG_FIFO
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
bus_req_i => iodev_req(IODEV_TRNG),
bus_rsp_o => iodev_rsp(IODEV_TRNG),
irq_o => firq(FIRQ_TRNG)
bus_rsp_o => iodev_rsp(IODEV_TRNG)
);
end generate;

neorv32_trng_inst_false:
if not IO_TRNG_EN generate
iodev_rsp(IODEV_TRNG) <= rsp_terminate_c;
firq(FIRQ_TRNG) <= '0';
end generate;


Expand Down
88 changes: 35 additions & 53 deletions rtl/core/neorv32_trng.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@ use neorv32.neorv32_package.all;

entity neorv32_trng is
generic (
IO_TRNG_FIFO : natural range 1 to 2**15 -- RND fifo depth, has to be a power of two, min 1
TRNG_FIFO : natural range 1 to 2**15 -- FIFO depth, has to be a power of two, min 1
);
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
irq_o : out std_ulogic -- data-available interrupt
bus_rsp_o : out bus_rsp_t -- bus response
);
end neorv32_trng;

Expand All @@ -38,17 +37,16 @@ architecture neorv32_trng_rtl of neorv32_trng is
-- ------------------------------------------------------------------------------------------------

-- control register bits --
constant ctrl_data_lsb_c : natural := 0; -- r/-: Random data byte LSB
constant ctrl_data_msb_c : natural := 7; -- r/-: Random data byte MSB
--
constant ctrl_fifo_size0_c : natural := 16; -- r/-: log2(FIFO size) bit 0
constant ctrl_fifo_size3_c : natural := 19; -- r/-: log2(FIFO size) bit 3
--
constant ctrl_irq_sel_c : natural := 27; -- r/w: interrupt select (0 = data available, 1 = FIFO full)
constant ctrl_fifo_clr_c : natural := 28; -- -/w: Clear data FIFO (auto clears)
constant ctrl_sim_mode_c : natural := 29; -- r/-: TRNG implemented in pseudo-RNG simulation mode
constant ctrl_en_c : natural := 30; -- r/w: TRNG enable
constant ctrl_valid_c : natural := 31; -- r/-: Output data valid
constant ctrl_en_c : natural := 0; -- r/w: TRNG enable
constant ctrl_fifo_clr_c : natural := 1; -- -/w: Clear data FIFO (auto clears)
constant ctrl_fifo_size0_c : natural := 2; -- r/-: log2(FIFO size) bit 0, LSB
constant ctrl_fifo_size3_c : natural := 5; -- r/-: log2(FIFO size) bit 3, MSB
constant ctrl_sim_mode_c : natural := 6; -- r/-: TRNG implemented in pseudo-RNG simulation mode
constant ctrl_avail_c : natural := 7; -- r/-: Random data available

-- data register bits --
constant ctrl_data_lsb_c : natural := 0; -- r/-: random data bit 0, LSB
constant ctrl_data_msb_c : natural := 7; -- r/-: random data bit 7, MSB

-- neoTRNG true random number generator --
component neoTRNG
Expand All @@ -66,10 +64,10 @@ architecture neorv32_trng_rtl of neorv32_trng is
);
end component;

-- control --
signal enable, irq_sel, fifo_clr : std_ulogic;
-- control register --
signal enable, fifo_clr : std_ulogic;

-- data FIFO --
-- data FIFO interface --
type fifo_t is record
we : std_ulogic; -- write enable
re : std_ulogic; -- read enable
Expand All @@ -91,7 +89,6 @@ begin
if (rstn_i = '0') then
bus_rsp_o <= rsp_terminate_c;
fifo_clr <= '0';
irq_sel <= '0';
enable <= '0';
elsif rising_edge(clk_i) then
-- defaults --
Expand All @@ -101,19 +98,22 @@ begin
fifo_clr <= '0'; -- auto-clear
-- host access --
if (bus_req_i.stb = '1') then
if (bus_req_i.rw = '1') then -- write access
irq_sel <= bus_req_i.data(ctrl_irq_sel_c);
fifo_clr <= bus_req_i.data(ctrl_fifo_clr_c);
if (bus_req_i.rw = '1') then -- write access (control register)
enable <= bus_req_i.data(ctrl_en_c);
fifo_clr <= bus_req_i.data(ctrl_fifo_clr_c);
else -- read access
bus_rsp_o.data(ctrl_data_msb_c downto ctrl_data_lsb_c) <= fifo.rdata;
--
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(IO_TRNG_FIFO), 4));
--
bus_rsp_o.data(ctrl_irq_sel_c) <= irq_sel;
bus_rsp_o.data(ctrl_sim_mode_c) <= bool_to_ulogic_f(is_simulation_c);
bus_rsp_o.data(ctrl_en_c) <= enable;
bus_rsp_o.data(ctrl_valid_c) <= fifo.avail;
if (bus_req_i.addr(2) = '0') then -- control register
bus_rsp_o.data(ctrl_en_c) <= enable;
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(TRNG_FIFO), 4));
bus_rsp_o.data(ctrl_sim_mode_c) <= bool_to_ulogic_f(is_simulation_c);
bus_rsp_o.data(ctrl_avail_c) <= fifo.avail;
else -- data register
if (fifo.avail = '0') then -- output zero if no data available
bus_rsp_o.data(ctrl_data_msb_c downto ctrl_data_lsb_c) <= (others => '0');
else
bus_rsp_o.data(ctrl_data_msb_c downto ctrl_data_lsb_c) <= fifo.rdata;
end if;
end if;
end if;
end if;
end if;
Expand Down Expand Up @@ -141,11 +141,11 @@ begin
-- -------------------------------------------------------------------------------------------
rnd_pool_fifo_inst: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => IO_TRNG_FIFO, -- number of fifo entries; has to be a power of two; min 1
FIFO_WIDTH => 8, -- size of data elements in fifo
FIFO_RSYNC => true, -- sync read
FIFO_SAFE => true, -- safe access
FULL_RESET => false -- no HW reset, try to infer BRAM
FIFO_DEPTH => TRNG_FIFO, -- number of FIFO entries; has to be a power of two; min 1
FIFO_WIDTH => 8, -- size of data elements in FIFO
FIFO_RSYNC => true, -- sync read
FIFO_SAFE => true, -- safe access
FULL_RESET => false -- no HW reset, try to infer BRAM
)
port map (
-- control --
Expand All @@ -164,25 +164,7 @@ begin
);

fifo.clear <= '1' when (enable = '0') or (fifo_clr = '1') else '0';
fifo.re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') else '0';

-- IRQ generator --
irq_generator: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_o <= '0';
elsif rising_edge(clk_i) then
if (enable = '1') then
if (irq_sel = '0') then -- fire IRQ if any data is available
irq_o <= fifo.avail;
else -- fire IRQ if data FIFO is full
irq_o <= not fifo.free;
end if;
else
irq_o <= '0';
end if;
end if;
end process irq_generator;
fifo.re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') and (bus_req_i.addr(2) = '1') else '0';


end neorv32_trng_rtl;
Expand Down
10 changes: 5 additions & 5 deletions rtl/core/neorv32_twi.vhd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- ================================================================================ --
-- NEORV32 SoC - Two-Wire Interface Controller (TWI) --
-- NEORV32 SoC - Two-Wire Interface Host Controller (TWI) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
Expand Down Expand Up @@ -38,14 +38,14 @@ architecture neorv32_twi_rtl of neorv32_twi is

-- control register --
constant ctrl_en_c : natural := 0; -- r/w: module enable (reset when zero)
constant ctrl_prsc0_c : natural := 1; -- r/w: CLK prsc bit 0
constant ctrl_prsc2_c : natural := 3; -- r/w: CLK prsc bit 2
constant ctrl_prsc0_c : natural := 1; -- r/w: clock prescaler bit 0
constant ctrl_prsc2_c : natural := 3; -- r/w: clock prescaler bit 2
constant ctrl_cdiv0_c : natural := 4; -- r/w: clock divider bit 0
constant ctrl_cdiv3_c : natural := 7; -- r/w: clock divider bit 3
constant ctrl_clkstr_en_c : natural := 8; -- r/w: enable clock stretching
--
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(fifo size), bit 0 (lsb)
constant ctrl_fifo_size3_c : natural := 18; -- r/-: log2(fifo size), bit 3 (msb)
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(FIFO size), bit 0 (LSB)
constant ctrl_fifo_size3_c : natural := 18; -- r/-: log2(FIFO size), bit 3 (MSB)
--
constant ctrl_sense_scl_c : natural := 27; -- r/-: current state of the SCL bus line
constant ctrl_sense_sda_c : natural := 28; -- r/-: current state of the SDA bus line
Expand Down
2 changes: 1 addition & 1 deletion rtl/core/neorv32_xbus.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ begin
bus_rw <= '0';
elsif rising_edge(clk_i) then
if (pending = '0') then -- idle, waiting for request
timeout_cnt <= std_ulogic_vector(to_unsigned(TIMEOUT_VAL, index_size_f(TIMEOUT_VAL)+1));
timeout_cnt <= std_ulogic_vector(to_unsigned(TIMEOUT_VAL, timeout_cnt'length));
pending <= bus_req.stb;
else -- busy, transfer in progress
timeout_cnt <= std_ulogic_vector(unsigned(timeout_cnt) - 1);
Expand Down
8 changes: 4 additions & 4 deletions sw/example/demo_trng/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ int main(void) {
// enable TRNG
neorv32_uart0_printf("\nTRNG FIFO depth: %i\n", neorv32_trng_get_fifo_depth());
neorv32_uart0_printf("Starting TRNG...\n");
neorv32_trng_enable(0);
neorv32_trng_enable();
neorv32_cpu_delay_ms(100); // TRNG "warm up"
neorv32_trng_fifo_clear(); // discard "warm-up" data

while(1) {

Expand Down Expand Up @@ -346,14 +347,13 @@ void compute_rate(void) {

const uint32_t n_samples = 16*1024;
uint32_t i;
uint32_t tmp;
uint8_t data;

uint32_t cycles = neorv32_cpu_csr_read(CSR_CYCLE);

i = 0;
while (i<n_samples) {
tmp = NEORV32_TRNG->CTRL;
if (tmp & (1<<TRNG_CTRL_VALID)) { // valid sample?
if (neorv32_trng_get(&data)) { // data available?
i++;
}
}
Expand Down
Loading

0 comments on commit e56a8fd

Please sign in to comment.