aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/src/lib.rs4
-rw-r--r--embassy-rp/src/trng.rs405
-rw-r--r--examples/rp23/src/bin/trng.rs64
3 files changed, 473 insertions, 0 deletions
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index c357c14c2..d402cf793 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -42,6 +42,8 @@ pub mod rtc;
42pub mod spi; 42pub mod spi;
43#[cfg(feature = "time-driver")] 43#[cfg(feature = "time-driver")]
44pub mod time_driver; 44pub mod time_driver;
45#[cfg(feature = "_rp235x")]
46pub mod trng;
45pub mod uart; 47pub mod uart;
46pub mod usb; 48pub mod usb;
47pub mod watchdog; 49pub mod watchdog;
@@ -402,6 +404,8 @@ embassy_hal_internal::peripherals! {
402 404
403 WATCHDOG, 405 WATCHDOG,
404 BOOTSEL, 406 BOOTSEL,
407
408 TRNG
405} 409}
406 410
407#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))] 411#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
diff --git a/embassy-rp/src/trng.rs b/embassy-rp/src/trng.rs
new file mode 100644
index 000000000..9f2f33c4b
--- /dev/null
+++ b/embassy-rp/src/trng.rs
@@ -0,0 +1,405 @@
1//! True Random Number Generator (TRNG) driver.
2
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::ops::Not;
6use core::task::Poll;
7
8use embassy_hal_internal::Peripheral;
9use embassy_sync::waitqueue::AtomicWaker;
10use rand_core::Error;
11
12use crate::interrupt::typelevel::{Binding, Interrupt};
13use crate::peripherals::TRNG;
14use crate::{interrupt, pac};
15
16trait SealedInstance {
17 fn regs() -> pac::trng::Trng;
18 fn waker() -> &'static AtomicWaker;
19}
20
21/// TRNG peripheral instance.
22#[allow(private_bounds)]
23pub trait Instance: SealedInstance {
24 /// Interrupt for this peripheral.
25 type Interrupt: Interrupt;
26}
27
28impl SealedInstance for TRNG {
29 fn regs() -> rp_pac::trng::Trng {
30 pac::TRNG
31 }
32
33 fn waker() -> &'static AtomicWaker {
34 static WAKER: AtomicWaker = AtomicWaker::new();
35 &WAKER
36 }
37}
38
39impl Instance for TRNG {
40 type Interrupt = interrupt::typelevel::TRNG_IRQ;
41}
42
43#[derive(Copy, Clone, Debug)]
44#[allow(missing_docs)]
45/// TRNG ROSC Inverter chain length options.
46pub enum InverterChainLength {
47 None = 0,
48 One,
49 Two,
50 Three,
51 Four,
52}
53
54impl From<InverterChainLength> for u8 {
55 fn from(value: InverterChainLength) -> Self {
56 value as u8
57 }
58}
59
60/// Configuration for the TRNG.
61///
62/// - Three built in entropy checks
63/// - ROSC frequency controlled by selecting one of ROSC chain lengths
64/// - Sample period in terms of system clock ticks
65///
66///
67/// Default configuration is based on the following from documentation:
68///
69/// ----
70///
71/// RP2350 Datasheet 12.12.2
72///
73/// ...
74///
75/// When configuring the TRNG block, consider the following principles:
76/// • As average generation time increases, result quality increases and failed entropy checks decrease.
77/// • A low sample count decreases average generation time, but increases the chance of NIST test-failing results and
78/// failed entropy checks.
79/// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or
80/// 1 and sample count settings of 20-25.
81///
82/// ---
83///
84/// Note, Pico SDK and Bootrom don't use any of the entropy checks and sample the ROSC directly
85/// by setting the sample period to 0. Random data collected this way is then passed through
86/// either hardware accelerated SHA256 (Bootrom) or xoroshiro128** (version 1.0!).
87#[non_exhaustive]
88#[derive(Copy, Clone, Debug)]
89pub struct Config {
90 /// Bypass TRNG autocorrelation test
91 pub disable_autocorrelation_test: bool,
92 /// Bypass CRNGT test
93 pub disable_crngt_test: bool,
94 /// When set, the Von-Neuman balancer is bypassed (including the
95 /// 32 consecutive bits test)
96 pub disable_von_neumann_balancer: bool,
97 /// Sets the number of rng_clk cycles between two consecutive
98 /// ring oscillator samples.
99 /// Note: If the von Neumann decorrelator is bypassed, the minimum value for
100 /// sample counter must not be less than seventeen
101 pub sample_count: u32,
102 /// Selects the number of inverters (out of four possible
103 /// selections) in the ring oscillator (the entropy source). Higher values select
104 /// longer inverter chain lengths.
105 pub inverter_chain_length: InverterChainLength,
106}
107
108impl Default for Config {
109 fn default() -> Self {
110 Config {
111 disable_autocorrelation_test: true,
112 disable_crngt_test: true,
113 disable_von_neumann_balancer: true,
114 sample_count: 25,
115 inverter_chain_length: InverterChainLength::One,
116 }
117 }
118}
119
120/// True Random Number Generator Driver for RP2350
121///
122/// This driver provides async and blocking options.
123///
124/// See [Config] for configuration details.
125///
126/// Usage example:
127/// ```no_run
128/// use embassy_executor::Spawner;
129/// use embassy_rp::trng::Trng;
130/// use embassy_rp::peripherals::TRNG;
131/// use embassy_rp::bind_interrupts;
132///
133/// bind_interrupts!(struct Irqs {
134/// TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;
135/// });
136///
137/// #[embassy_executor::main]
138/// async fn main(spawner: Spawner) {
139/// let peripherals = embassy_rp::init(Default::default());
140/// let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default());
141///
142/// let mut randomness = [0u8; 58];
143/// loop {
144/// trng.fill_bytes(&mut randomness).await;
145/// assert_ne!(randomness, [0u8; 58]);
146/// }
147///}
148/// ```
149pub struct Trng<'d, T: Instance> {
150 phantom: PhantomData<&'d mut T>,
151}
152
153/// 12.12.1. Overview
154/// On request, the TRNG block generates a block of 192 entropy bits generated by automatically processing a series of
155/// periodic samples from the TRNG block’s internal Ring Oscillator (ROSC).
156const TRNG_BLOCK_SIZE_BITS: usize = 192;
157const TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8;
158
159impl<'d, T: Instance> Trng<'d, T> {
160 /// Create a new TRNG driver.
161 pub fn new(
162 _trng: impl Peripheral<P = T> + 'd,
163 _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd,
164 config: Config,
165 ) -> Self {
166 let regs = T::regs();
167
168 regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false));
169
170 let trng_config_register = regs.trng_config();
171 trng_config_register.write(|w| {
172 w.set_rnd_src_sel(config.inverter_chain_length.clone().into());
173 });
174
175 let sample_count_register = regs.sample_cnt1();
176 sample_count_register.write(|w| {
177 *w = config.sample_count;
178 });
179
180 let debug_control_register = regs.trng_debug_control();
181 debug_control_register.write(|w| {
182 w.set_auto_correlate_bypass(config.disable_autocorrelation_test);
183 w.set_trng_crngt_bypass(config.disable_crngt_test);
184 w.set_vnc_bypass(config.disable_von_neumann_balancer)
185 });
186
187 Trng { phantom: PhantomData }
188 }
189
190 fn start_rng(&self) {
191 let regs = T::regs();
192 let source_enable_register = regs.rnd_source_enable();
193 // Enable TRNG ROSC
194 source_enable_register.write(|w| w.set_rnd_src_en(true));
195 }
196
197 fn stop_rng(&self) {
198 let regs = T::regs();
199 let source_enable_register = regs.rnd_source_enable();
200 source_enable_register.write(|w| w.set_rnd_src_en(false));
201 let reset_bits_counter_register = regs.rst_bits_counter();
202 reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true));
203 }
204
205 fn enable_irq(&self) {
206 unsafe { T::Interrupt::enable() }
207 }
208
209 fn disable_irq(&self) {
210 T::Interrupt::disable();
211 }
212
213 fn blocking_wait_for_successful_generation(&self) {
214 let regs = T::regs();
215
216 let trng_busy_register = regs.trng_busy();
217 let trng_valid_register = regs.trng_valid();
218
219 let mut success = false;
220 while success.not() {
221 while trng_busy_register.read().trng_busy() {}
222 if trng_valid_register.read().ehr_valid().not() {
223 if regs.rng_isr().read().autocorr_err() {
224 regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true));
225 } else {
226 panic!("RNG not busy, but ehr is not valid!")
227 }
228 } else {
229 success = true
230 }
231 }
232 }
233
234 fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) {
235 let regs = T::regs();
236 let ehr_data_regs = [
237 regs.ehr_data0(),
238 regs.ehr_data1(),
239 regs.ehr_data2(),
240 regs.ehr_data3(),
241 regs.ehr_data4(),
242 regs.ehr_data5(),
243 ];
244
245 for (i, reg) in ehr_data_regs.iter().enumerate() {
246 buffer[i * 4..i * 4 + 4].copy_from_slice(&reg.read().to_ne_bytes());
247 }
248 }
249
250 fn blocking_read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) {
251 self.blocking_wait_for_successful_generation();
252 self.read_ehr_registers_into_array(buffer);
253 }
254
255 /// Fill the buffer with random bytes, async version.
256 pub async fn fill_bytes(&mut self, destination: &mut [u8]) {
257 if destination.is_empty() {
258 return; // Nothing to fill
259 }
260
261 self.start_rng();
262 self.enable_irq();
263
264 let mut bytes_transferred = 0usize;
265 let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES];
266
267 let regs = T::regs();
268
269 let trng_busy_register = regs.trng_busy();
270 let trng_valid_register = regs.trng_valid();
271
272 let waker = T::waker();
273
274 let destination_length = destination.len();
275
276 poll_fn(|context| {
277 waker.register(context.waker());
278 if bytes_transferred == destination_length {
279 self.stop_rng();
280 self.disable_irq();
281 Poll::Ready(())
282 } else {
283 if trng_busy_register.read().trng_busy() {
284 Poll::Pending
285 } else {
286 if trng_valid_register.read().ehr_valid().not() {
287 panic!("RNG not busy, but ehr is not valid!")
288 }
289 self.read_ehr_registers_into_array(&mut buffer);
290 let remaining = destination_length - bytes_transferred;
291 if remaining > TRNG_BLOCK_SIZE_BYTES {
292 destination[bytes_transferred..bytes_transferred + TRNG_BLOCK_SIZE_BYTES]
293 .copy_from_slice(&buffer);
294 bytes_transferred += TRNG_BLOCK_SIZE_BYTES
295 } else {
296 destination[bytes_transferred..bytes_transferred + remaining]
297 .copy_from_slice(&buffer[0..remaining]);
298 bytes_transferred += remaining
299 }
300 if bytes_transferred == destination_length {
301 self.stop_rng();
302 self.disable_irq();
303 Poll::Ready(())
304 } else {
305 Poll::Pending
306 }
307 }
308 }
309 })
310 .await
311 }
312
313 /// Fill the buffer with random bytes, blocking version.
314 pub fn blocking_fill_bytes(&mut self, destination: &mut [u8]) {
315 if destination.is_empty() {
316 return; // Nothing to fill
317 }
318 self.start_rng();
319
320 let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES];
321
322 for chunk in destination.chunks_mut(TRNG_BLOCK_SIZE_BYTES) {
323 self.blocking_wait_for_successful_generation();
324 self.blocking_read_ehr_registers_into_array(&mut buffer);
325 chunk.copy_from_slice(&buffer[..chunk.len()])
326 }
327 self.stop_rng()
328 }
329
330 /// Return a random u32, blocking.
331 pub fn blocking_next_u32(&mut self) -> u32 {
332 let regs = T::regs();
333 self.start_rng();
334 self.blocking_wait_for_successful_generation();
335 // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to
336 // clear all of the result registers.
337 let result = regs.ehr_data5().read();
338 self.stop_rng();
339 result
340 }
341
342 /// Return a random u64, blocking.
343 pub fn blocking_next_u64(&mut self) -> u64 {
344 let regs = T::regs();
345 self.start_rng();
346 self.blocking_wait_for_successful_generation();
347
348 let low = regs.ehr_data4().read() as u64;
349 // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to
350 // clear all of the result registers.
351 let result = (regs.ehr_data5().read() as u64) << 32 | low;
352 self.stop_rng();
353 result
354 }
355}
356
357impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> {
358 fn next_u32(&mut self) -> u32 {
359 self.blocking_next_u32()
360 }
361
362 fn next_u64(&mut self) -> u64 {
363 self.blocking_next_u64()
364 }
365
366 fn fill_bytes(&mut self, dest: &mut [u8]) {
367 self.blocking_fill_bytes(dest)
368 }
369
370 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
371 self.blocking_fill_bytes(dest);
372 Ok(())
373 }
374}
375/// TRNG interrupt handler.
376pub struct InterruptHandler<T: Instance> {
377 _trng: PhantomData<T>,
378}
379
380impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
381 unsafe fn on_interrupt() {
382 let regs = T::regs();
383 let isr = regs.rng_isr().read();
384 // Clear ehr bit
385 regs.rng_icr().write(|w| {
386 w.set_ehr_valid(true);
387 });
388 if isr.ehr_valid() {
389 T::waker().wake();
390 } else {
391 // 12.12.5. List of Registers
392 // ...
393 // TRNG: RNG_ISR Register
394 // ...
395 // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row.
396 // When set, RNG ceases functioning until next reset
397 if isr.autocorr_err() {
398 warn!("TRNG Autocorrect error! Resetting TRNG");
399 regs.trng_sw_reset().write(|w| {
400 w.set_trng_sw_reset(true);
401 });
402 }
403 }
404 }
405}
diff --git a/examples/rp23/src/bin/trng.rs b/examples/rp23/src/bin/trng.rs
new file mode 100644
index 000000000..e146baa2e
--- /dev/null
+++ b/examples/rp23/src/bin/trng.rs
@@ -0,0 +1,64 @@
1//! This example shows TRNG usage
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
9use embassy_rp::block::ImageDef;
10use embassy_rp::gpio::{Level, Output};
11use embassy_rp::peripherals::TRNG;
12use embassy_rp::trng::Trng;
13use embassy_time::Timer;
14use rand::RngCore;
15use {defmt_rtt as _, panic_probe as _};
16
17#[link_section = ".start_block"]
18#[used]
19pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
20
21// Program metadata for `picotool info`
22#[link_section = ".bi_entries"]
23#[used]
24pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
25 embassy_rp::binary_info::rp_program_name!(c"example"),
26 embassy_rp::binary_info::rp_cargo_version!(),
27 embassy_rp::binary_info::rp_program_description!(c"Blinky"),
28 embassy_rp::binary_info::rp_program_build_attribute!(),
29];
30
31bind_interrupts!(struct Irqs {
32 TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;
33});
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 let peripherals = embassy_rp::init(Default::default());
38
39 // Initialize the TRNG with default configuration
40 let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default());
41 // A buffer to collect random bytes in.
42 let mut randomness = [0u8; 58];
43
44 let mut led = Output::new(peripherals.PIN_25, Level::Low);
45
46 loop {
47 trng.fill_bytes(&mut randomness).await;
48 info!("Random bytes async {}", &randomness);
49 trng.blocking_fill_bytes(&mut randomness);
50 info!("Random bytes blocking {}", &randomness);
51 let random_u32 = trng.next_u32();
52 let random_u64 = trng.next_u64();
53 info!("Random u32 {} u64 {}", random_u32, random_u64);
54 // Random number of blinks between 0 and 31
55 let blinks = random_u32 % 32;
56 for _ in 0..blinks {
57 led.set_high();
58 Timer::after_millis(20).await;
59 led.set_low();
60 Timer::after_millis(20).await;
61 }
62 Timer::after_millis(1000).await;
63 }
64}