//! True Random Number Generator (TRNG) driver. use core::future::poll_fn; use core::marker::PhantomData; use core::ops::Not; use core::task::Poll; use embassy_hal_internal::Peripheral; use embassy_sync::waitqueue::AtomicWaker; use rand_core::Error; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::peripherals::TRNG; use crate::{interrupt, pac}; trait SealedInstance { fn regs() -> pac::trng::Trng; fn waker() -> &'static AtomicWaker; } /// TRNG peripheral instance. #[allow(private_bounds)] pub trait Instance: SealedInstance { /// Interrupt for this peripheral. type Interrupt: Interrupt; } impl SealedInstance for TRNG { fn regs() -> rp_pac::trng::Trng { pac::TRNG } fn waker() -> &'static AtomicWaker { static WAKER: AtomicWaker = AtomicWaker::new(); &WAKER } } impl Instance for TRNG { type Interrupt = interrupt::typelevel::TRNG_IRQ; } #[derive(Copy, Clone, Debug)] #[allow(missing_docs)] /// TRNG ROSC Inverter chain length options. pub enum InverterChainLength { None = 0, One, Two, Three, Four, } impl From for u8 { fn from(value: InverterChainLength) -> Self { value as u8 } } /// Configuration for the TRNG. /// /// - Three built in entropy checks /// - ROSC frequency controlled by selecting one of ROSC chain lengths /// - Sample period in terms of system clock ticks /// /// /// Default configuration is based on the following from documentation: /// /// ---- /// /// RP2350 Datasheet 12.12.2 /// /// ... /// /// When configuring the TRNG block, consider the following principles: /// • As average generation time increases, result quality increases and failed entropy checks decrease. /// • A low sample count decreases average generation time, but increases the chance of NIST test-failing results and /// failed entropy checks. /// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or /// 1 and sample count settings of 20-25. /// /// --- /// /// Note, Pico SDK and Bootrom don't use any of the entropy checks and sample the ROSC directly /// by setting the sample period to 0. Random data collected this way is then passed through /// either hardware accelerated SHA256 (Bootrom) or xoroshiro128** (version 1.0!). #[non_exhaustive] #[derive(Copy, Clone, Debug)] pub struct Config { /// Bypass TRNG autocorrelation test pub disable_autocorrelation_test: bool, /// Bypass CRNGT test pub disable_crngt_test: bool, /// When set, the Von-Neuman balancer is bypassed (including the /// 32 consecutive bits test) pub disable_von_neumann_balancer: bool, /// Sets the number of rng_clk cycles between two consecutive /// ring oscillator samples. /// Note: If the von Neumann decorrelator is bypassed, the minimum value for /// sample counter must not be less than seventeen pub sample_count: u32, /// Selects the number of inverters (out of four possible /// selections) in the ring oscillator (the entropy source). Higher values select /// longer inverter chain lengths. pub inverter_chain_length: InverterChainLength, } impl Default for Config { fn default() -> Self { Config { disable_autocorrelation_test: true, disable_crngt_test: true, disable_von_neumann_balancer: true, sample_count: 25, inverter_chain_length: InverterChainLength::One, } } } /// True Random Number Generator Driver for RP2350 /// /// This driver provides async and blocking options. /// /// See [Config] for configuration details. /// /// Usage example: /// ```no_run /// use embassy_executor::Spawner; /// use embassy_rp::trng::Trng; /// use embassy_rp::peripherals::TRNG; /// use embassy_rp::bind_interrupts; /// /// bind_interrupts!(struct Irqs { /// TRNG_IRQ => embassy_rp::trng::InterruptHandler; /// }); /// /// #[embassy_executor::main] /// async fn main(spawner: Spawner) { /// let peripherals = embassy_rp::init(Default::default()); /// let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); /// /// let mut randomness = [0u8; 58]; /// loop { /// trng.fill_bytes(&mut randomness).await; /// assert_ne!(randomness, [0u8; 58]); /// } ///} /// ``` pub struct Trng<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } /// 12.12.1. Overview /// On request, the TRNG block generates a block of 192 entropy bits generated by automatically processing a series of /// periodic samples from the TRNG block’s internal Ring Oscillator (ROSC). const TRNG_BLOCK_SIZE_BITS: usize = 192; const TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8; impl<'d, T: Instance> Trng<'d, T> { /// Create a new TRNG driver. pub fn new( _trng: impl Peripheral

+ 'd, _irq: impl Binding> + 'd, config: Config, ) -> Self { let regs = T::regs(); regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false)); let trng_config_register = regs.trng_config(); trng_config_register.write(|w| { w.set_rnd_src_sel(config.inverter_chain_length.clone().into()); }); let sample_count_register = regs.sample_cnt1(); sample_count_register.write(|w| { *w = config.sample_count; }); let debug_control_register = regs.trng_debug_control(); debug_control_register.write(|w| { w.set_auto_correlate_bypass(config.disable_autocorrelation_test); w.set_trng_crngt_bypass(config.disable_crngt_test); w.set_vnc_bypass(config.disable_von_neumann_balancer) }); Trng { phantom: PhantomData } } fn start_rng(&self) { let regs = T::regs(); let source_enable_register = regs.rnd_source_enable(); // Enable TRNG ROSC source_enable_register.write(|w| w.set_rnd_src_en(true)); } fn stop_rng(&self) { let regs = T::regs(); let source_enable_register = regs.rnd_source_enable(); source_enable_register.write(|w| w.set_rnd_src_en(false)); let reset_bits_counter_register = regs.rst_bits_counter(); reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true)); } fn enable_irq(&self) { unsafe { T::Interrupt::enable() } } fn disable_irq(&self) { T::Interrupt::disable(); } fn blocking_wait_for_successful_generation(&self) { let regs = T::regs(); let trng_busy_register = regs.trng_busy(); let trng_valid_register = regs.trng_valid(); let mut success = false; while success.not() { while trng_busy_register.read().trng_busy() {} if trng_valid_register.read().ehr_valid().not() { if regs.rng_isr().read().autocorr_err() { regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true)); } else { panic!("RNG not busy, but ehr is not valid!") } } else { success = true } } } fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { let regs = T::regs(); let ehr_data_regs = [ regs.ehr_data0(), regs.ehr_data1(), regs.ehr_data2(), regs.ehr_data3(), regs.ehr_data4(), regs.ehr_data5(), ]; for (i, reg) in ehr_data_regs.iter().enumerate() { buffer[i * 4..i * 4 + 4].copy_from_slice(®.read().to_ne_bytes()); } } fn blocking_read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { self.blocking_wait_for_successful_generation(); self.read_ehr_registers_into_array(buffer); } /// Fill the buffer with random bytes, async version. pub async fn fill_bytes(&mut self, destination: &mut [u8]) { if destination.is_empty() { return; // Nothing to fill } self.start_rng(); self.enable_irq(); let mut bytes_transferred = 0usize; let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; let regs = T::regs(); let trng_busy_register = regs.trng_busy(); let trng_valid_register = regs.trng_valid(); let waker = T::waker(); let destination_length = destination.len(); poll_fn(|context| { waker.register(context.waker()); if bytes_transferred == destination_length { self.stop_rng(); self.disable_irq(); Poll::Ready(()) } else { if trng_busy_register.read().trng_busy() { Poll::Pending } else { if trng_valid_register.read().ehr_valid().not() { panic!("RNG not busy, but ehr is not valid!") } self.read_ehr_registers_into_array(&mut buffer); let remaining = destination_length - bytes_transferred; if remaining > TRNG_BLOCK_SIZE_BYTES { destination[bytes_transferred..bytes_transferred + TRNG_BLOCK_SIZE_BYTES] .copy_from_slice(&buffer); bytes_transferred += TRNG_BLOCK_SIZE_BYTES } else { destination[bytes_transferred..bytes_transferred + remaining] .copy_from_slice(&buffer[0..remaining]); bytes_transferred += remaining } if bytes_transferred == destination_length { self.stop_rng(); self.disable_irq(); Poll::Ready(()) } else { Poll::Pending } } } }) .await } /// Fill the buffer with random bytes, blocking version. pub fn blocking_fill_bytes(&mut self, destination: &mut [u8]) { if destination.is_empty() { return; // Nothing to fill } self.start_rng(); let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; for chunk in destination.chunks_mut(TRNG_BLOCK_SIZE_BYTES) { self.blocking_wait_for_successful_generation(); self.blocking_read_ehr_registers_into_array(&mut buffer); chunk.copy_from_slice(&buffer[..chunk.len()]) } self.stop_rng() } /// Return a random u32, blocking. pub fn blocking_next_u32(&mut self) -> u32 { let regs = T::regs(); self.start_rng(); self.blocking_wait_for_successful_generation(); // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to // clear all of the result registers. let result = regs.ehr_data5().read(); self.stop_rng(); result } /// Return a random u64, blocking. pub fn blocking_next_u64(&mut self) -> u64 { let regs = T::regs(); self.start_rng(); self.blocking_wait_for_successful_generation(); let low = regs.ehr_data4().read() as u64; // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to // clear all of the result registers. let result = (regs.ehr_data5().read() as u64) << 32 | low; self.stop_rng(); result } } impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> { fn next_u32(&mut self) -> u32 { self.blocking_next_u32() } fn next_u64(&mut self) -> u64 { self.blocking_next_u64() } fn fill_bytes(&mut self, dest: &mut [u8]) { self.blocking_fill_bytes(dest) } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.blocking_fill_bytes(dest); Ok(()) } } /// TRNG interrupt handler. pub struct InterruptHandler { _trng: PhantomData, } impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let regs = T::regs(); let isr = regs.rng_isr().read(); // Clear ehr bit regs.rng_icr().write(|w| { w.set_ehr_valid(true); }); if isr.ehr_valid() { T::waker().wake(); } else { // 12.12.5. List of Registers // ... // TRNG: RNG_ISR Register // ... // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row. // When set, RNG ceases functioning until next reset if isr.autocorr_err() { warn!("TRNG Autocorrect error! Resetting TRNG"); regs.trng_sw_reset().write(|w| { w.set_trng_sw_reset(true); }); } } } }