Skip to content

Commit 82945be

Browse files
committed
added TRNG module
1 parent bde9397 commit 82945be

2 files changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) 2025 Pedro Correia
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
`default_nettype none
7+
8+
module tqvp_TRNG_20RO7FF_PC #(
9+
parameter SIZE_RO = 6 //size of the RO will be SIZE_RO + 1 inverter gates (7 in this case)
10+
parameter N_RO = 20 //number of ROs in parallel
11+
)(
12+
input clk, // Clock - the TinyQV project clock is normally set to 64MHz.
13+
input rst_n, // Reset_n - low to reset.
14+
15+
input [7:0] ui_in, // The input PMOD, always available. Note that ui_in[7] is normally used for UART RX.
16+
// The inputs are synchronized to the clock, note this will introduce 2 cycles of delay on the inputs.
17+
18+
output [7:0] uo_out, // The output PMOD. Each wire is only connected if this peripheral is selected.
19+
// Note that uo_out[0] is normally used for UART TX.
20+
21+
input [3:0] address, // Address within this peripheral's address space
22+
23+
input data_write, // Data write request from the TinyQV core.
24+
input [7:0] data_in, // Data in to the peripheral, valid when data_write is high.
25+
26+
output [7:0] data_out // Data out from the peripheral, set this in accordance with the supplied address
27+
);
28+
29+
reg [SIZE_RO:0] oscillator_ring [N_RO] = 0;
30+
reg [N_RO:0] oscillator_ring_Q = 0;
31+
32+
reg [7:0] ro_data = 0;
33+
reg [7:0] shift_reg = 0;
34+
reg xorA;
35+
reg counter = 4'b0;
36+
integer i;
37+
integer j;
38+
39+
always @(*) begin //Ring oscilators Construction logic
40+
41+
for (j = 0; j < N_RO; j = j + 1) begin
42+
for (i = 1; i <= SIZE_RO; i = i + 1) begin
43+
[i]oscillator_ring[j] = ~[i-1]oscillator_ring[j];
44+
end
45+
[0]oscillator_ring[j] = ~[SIZE_RO]oscillator_ring[j];
46+
end
47+
end
48+
49+
always @(posedge clk) begin //sampling FF logic
50+
for (i = 0; i < N_RO; i = i + 1) begin
51+
oscillator_ring_Q[i] = [SIZE_RO]oscillator_ring[i]
52+
end
53+
end
54+
55+
always @(*) begin // xor logic
56+
xorA = oscillator_ring_Q[0] ^ oscillator_ring_Q[1]
57+
for (i = 2; i < N_RO; i = i + 1) begin
58+
xorA = xorA ^ oscillator_ring_Q[i]
59+
end
60+
end
61+
62+
always @(posedge clk or posedge !rst_n) begin //shift register / 8bit data packager
63+
if (!rst_n) begin
64+
counter <= 4'b0;
65+
ro_data <= 8'b0;
66+
shift_reg <= 8'b0;
67+
tx_start <= 1'b0;
68+
end
69+
else begin
70+
if (rst_n) begin
71+
shift_reg[7] <= xorA;
72+
shift_reg[6] <= shift_reg[7];
73+
shift_reg[5] <= shift_reg[6];
74+
shift_reg[4] <= shift_reg[5];
75+
shift_reg[3] <= shift_reg[4];
76+
shift_reg[2] <= shift_reg[3];
77+
shift_reg[1] <= shift_reg[2];
78+
shift_reg[0] <= shift_reg[1];
79+
80+
// Counting logic
81+
if (counter >= 7) begin
82+
ro_data <= shift_reg;
83+
tx_start <= 1'b1;
84+
shift_reg <= 8'b0;
85+
data_out <= ro_data;
86+
end
87+
else begin
88+
tx_start <= 1'b0;
89+
end
90+
end
91+
end
92+
end
93+
94+
endmodule

test/user_peripherals/trng/trng.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# SPDX-FileCopyrightText: © 2024 Tiny Tapeout
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import cocotb
5+
from cocotb.clock import Clock
6+
from cocotb.triggers import ClockCycles
7+
8+
from tqv import TinyQV
9+
10+
PERIPHERAL_NUM = 16 + 36
11+
12+
@cocotb.test()
13+
async def test_project(dut):
14+
dut._log.info("Start")
15+
16+
# Set the clock period to 100 ns (10 MHz)
17+
clock = Clock(dut.clk, 100, units="ns")
18+
cocotb.start_soon(clock.start())
19+
20+
tqv = TinyQV(dut, PERIPHERAL_NUM)
21+
22+
# Reset
23+
await tqv.reset()
24+
25+
dut._log.info("Test project behavior")
26+
27+
# Test register write and read back
28+
await tqv.write_reg(0, 20)
29+
assert await tqv.read_reg(0) == 20
30+
print(tqv.read_reg(0))
31+
32+
# Set an input value, in the example this will be added to the register value
33+
dut.ui_in.value = 30
34+
35+
# Wait for two clock cycles to see the output values, because ui_in is synchronized over two clocks,
36+
# and a further clock is required for the output to propagate.
37+
await ClockCycles(dut.clk, 3)
38+
39+
# The following assersion is just an example of how to check the output values.
40+
# Change it to match the actual expected output of your module:
41+
assert dut.uo_out.value == 50
42+
43+
# Input value should be read back from register 1
44+
assert await tqv.read_reg(1) == 30
45+
46+
# Zero should be read back from register 2
47+
assert await tqv.read_reg(2) == 0
48+
49+
# A second write should work
50+
await tqv.write_reg(0, 40)
51+
assert dut.uo_out.value == 70

0 commit comments

Comments
 (0)