Skip to content
Closed
39 changes: 39 additions & 0 deletions docs/user_peripherals/36_trng.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!---

This file is used to generate your project datasheet. Please fill in the information below and delete any unused
sections.

You can also include images in this folder and reference them in the markdown. Each image must be less than
512 kb in size, and the combined size of all images must be less than 1 MB.
-->

# True Random Number Generator RO Based - 20RO7_FF

Author: Pedro Correia

Peripheral index: 36

## What it does

This Peripheral implements a True Random Number Generator (TRNG) based on jitter noise from Ring Oscilators (ROs).
The choosen architecture was sampling flip-flop with 20 ROs in parallel, each with 7 inverters.
TRNG based on best performer from https://ieeexplore.ieee.org/document/10209326 , for more info regarding the article contact me.


## Register map

The Peripheral Constantly streams data from the generator, to retrieve it just access the output register for the pheripheral and extract the data.

| Address | Name | Access | Description |
|---------|-------|--------|---------------------------------------------------------------------|
| 0x00 | out | R | 8bit True random number |


## How to test

consequent reads to the register, should yield diferent results.
Use NIST SP800-22R, to test data.

## External hardware

No external hardware required.
1 change: 1 addition & 0 deletions info.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ project:
- "tinyQV/peri/uart/uart_tx.v"
- "user_peripherals/affinex/mul.sv"
- "user_peripherals/affinex/tqvp_affinex.sv"
- "user_peripherals/trng/tqvp_TRNG_20RO7FF_PC.sv"


# The pinout of your project. Leave unused pins blank. DO NOT delete or add any pins.
Expand Down
15 changes: 15 additions & 0 deletions src/peripherals.v
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,21 @@ module tinyQV_peripherals #(parameter CLOCK_MHZ=64) (
.data_out(data_from_simple_peri[15])
);

tqvp_TRNG_20RO7FF_PC TRNG_036 (
.clk(clk),
.rst_n(rst_n),

.ui_in(ui_in),
.uo_out(uo_out_from_simple_peri[36]),

.address(addr_in[3:0]),

.data_write((data_write_n != 2'b11) & peri_simple[36]),
.data_in(data_in[7:0]),

.data_out(data_from_simple_peri[36])
);

// --------------------------------------------------------------------- //
// Additional full interface peripherals with no interrupt

Expand Down
95 changes: 95 additions & 0 deletions src/user_peripherals/trng/tqvp_TRNG_20RO7FF_PC.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2025 Pedro Correia
* SPDX-License-Identifier: Apache-2.0
*/

`default_nettype none

module tqvp_TRNG_20RO7FF_PC #(
parameter SIZE_RO = 6, //size of the RO will be SIZE_RO + 1 inverter gates (7 in this case)
N_RO = 20 //number of ROs in parallel
)(
input clk, // Clock - the TinyQV project clock is normally set to 64MHz.
input rst_n, // Reset_n - low to reset.

input [7:0] ui_in, // The input PMOD, always available. Note that ui_in[7] is normally used for UART RX.
// The inputs are synchronized to the clock, note this will introduce 2 cycles of delay on the inputs.

output [7:0] uo_out, // The output PMOD. Each wire is only connected if this peripheral is selected.
// Note that uo_out[0] is normally used for UART TX.

input [3:0] address, // Address within this peripheral's address space

input data_write, // Data write request from the TinyQV core.
input [7:0] data_in, // Data in to the peripheral, valid when data_write is high.

output [7:0] data_out // Data out from the peripheral, set this in accordance with the supplied address
);

reg [N_RO:0][SIZE_RO:0] oscillator_ring = 0;
reg [N_RO:0] oscillator_ring_Q = 0;

reg [7:0] ro_data = 0;
reg [7:0] shift_reg = 0;
reg xorA;
reg counter = 4'b0;
reg tx_start = 1'b0;
integer i;
integer j;

always @(*) begin //Ring oscilators Construction logic

for (j = 0; j < N_RO; j = j + 1) begin
for (i = 1; i <= SIZE_RO; i = i + 1) begin
oscillator_ring[j][i] = ~oscillator_ring[j][i-1];
end
oscillator_ring[j][0] = ~oscillator_ring[j][SIZE_RO];
end
end

always_ff @(posedge clk) begin //sampling FF logic
for (i = 0; i < N_RO; i = i + 1) begin
oscillator_ring_Q[i] = oscillator_ring[i][SIZE_RO];
end
end

always @(*) begin // xor logic
xorA = oscillator_ring_Q[0] ^ oscillator_ring_Q[1];
for (i = 2; i < N_RO; i = i + 1) begin
xorA = xorA ^ oscillator_ring_Q[i];
end
end

always_ff @(posedge clk or posedge !rst_n) begin //shift register / 8bit data packager
if (!rst_n) begin
counter <= 4'b0;
ro_data <= 8'b0;
shift_reg <= 8'b0;
tx_start <= 1'b0;
end
else begin
if (rst_n) begin
shift_reg[7] <= xorA;
shift_reg[6] <= shift_reg[7];
shift_reg[5] <= shift_reg[6];
shift_reg[4] <= shift_reg[5];
shift_reg[3] <= shift_reg[4];
shift_reg[2] <= shift_reg[3];
shift_reg[1] <= shift_reg[2];
shift_reg[0] <= shift_reg[1];

// Counting logic
if (counter >= 7) begin
ro_data <= shift_reg;
tx_start <= 1'b1;
shift_reg <= 8'b0;
end
else begin
tx_start <= 1'b0;
end
end
end
end
assign data_out = ro_data;

endmodule
2 changes: 1 addition & 1 deletion test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ USER_PERIPHERAL_32 = fpu.test
USER_PERIPHERAL_33 = pwl_synth.test
USER_PERIPHERAL_34 = tqvp_laurie_dwarf_line_table_accelerator.test
USER_PERIPHERAL_35 = prng.test
USER_PERIPHERAL_36 =
USER_PERIPHERAL_36 = trng.test
USER_PERIPHERAL_37 = intercal_alu.test
USER_PERIPHERAL_38 = CORDIC.test_trigonometric_simple \
CORDIC.test_linear_simple \
Expand Down
51 changes: 51 additions & 0 deletions test/user_peripherals/trng/trng.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SPDX-FileCopyrightText: © 2024 Tiny Tapeout
# SPDX-License-Identifier: Apache-2.0

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import ClockCycles

from tqv import TinyQV

PERIPHERAL_NUM = 16 + 36

@cocotb.test()
async def test_project(dut):
dut._log.info("Start")

# Set the clock period to 100 ns (10 MHz)
clock = Clock(dut.clk, 100, units="ns")
cocotb.start_soon(clock.start())

tqv = TinyQV(dut, PERIPHERAL_NUM)

# Reset
await tqv.reset()

dut._log.info("Test project behavior")

# Test register write and read back
await tqv.write_reg(0, 20)
assert await tqv.read_reg(0) == 20
print(tqv.read_reg(0))

# Set an input value, in the example this will be added to the register value
dut.ui_in.value = 30

# Wait for two clock cycles to see the output values, because ui_in is synchronized over two clocks,
# and a further clock is required for the output to propagate.
await ClockCycles(dut.clk, 3)

# The following assersion is just an example of how to check the output values.
# Change it to match the actual expected output of your module:
assert dut.uo_out.value == 50

# Input value should be read back from register 1
assert await tqv.read_reg(1) == 30

# Zero should be read back from register 2
assert await tqv.read_reg(2) == 0

# A second write should work
await tqv.write_reg(0, 40)
assert dut.uo_out.value == 70
Loading