diff --git a/embassy-rp/src/trng.rs b/embassy-rp/src/trng.rs index 611fee83b..a8a0172be 100644 --- a/embassy-rp/src/trng.rs +++ b/embassy-rp/src/trng.rs @@ -78,6 +78,9 @@ impl From for u8 { /// 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. +/// Larger sample count settings (e.g. 100) provide proportionately slower average generation times. These settings +/// significantly reduce, but do not eliminate NIST test failures and entropy check failures. Results occasionally take an +/// especially long time to generate. /// /// --- /// @@ -108,9 +111,10 @@ pub struct Config { impl Default for Config { fn default() -> Self { Config { - disable_autocorrelation_test: true, - disable_crngt_test: true, - disable_von_neumann_balancer: true, + // WARNING: Disabling these tests increases likelihood of poor rng results. + disable_autocorrelation_test: false, + disable_crngt_test: false, + disable_von_neumann_balancer: false, sample_count: 25, inverter_chain_length: InverterChainLength::One, } @@ -148,6 +152,7 @@ impl Default for Config { /// ``` pub struct Trng<'d, T: Instance> { phantom: PhantomData<&'d mut T>, + config: Config, } /// 12.12.1. Overview @@ -159,28 +164,12 @@ 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: Peri<'d, T>, _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 } + let trng = Trng { + phantom: PhantomData, + config: config, + }; + trng.initialize_rng(); + trng } fn start_rng(&self) { @@ -198,6 +187,29 @@ impl<'d, T: Instance> Trng<'d, T> { reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true)); } + fn initialize_rng(&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(self.config.inverter_chain_length.clone().into()); + }); + + let sample_count_register = regs.sample_cnt1(); + sample_count_register.write(|w| { + *w = self.config.sample_count; + }); + + let debug_control_register = regs.trng_debug_control(); + debug_control_register.write(|w| { + w.set_auto_correlate_bypass(self.config.disable_autocorrelation_test); + w.set_trng_crngt_bypass(self.config.disable_crngt_test); + w.set_vnc_bypass(self.config.disable_von_neumann_balancer); + }); + } + fn enable_irq(&self) { unsafe { T::Interrupt::enable() } } @@ -218,6 +230,10 @@ impl<'d, T: Instance> Trng<'d, T> { 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)); + // Fixed delay is required after TRNG soft reset. This read is sufficient. + regs.trng_sw_reset().read(); + self.initialize_rng(); + self.start_rng(); } else { panic!("RNG not busy, but ehr is not valid!") } @@ -279,8 +295,11 @@ impl<'d, T: Instance> Trng<'d, T> { if trng_busy_register.read().trng_busy() { Poll::Pending } else { + // If woken up and EHR is *not* valid, assume the trng has been reset and reinitialize, restart. if trng_valid_register.read().ehr_valid().not() { - panic!("RNG not busy, but ehr is not valid!") + self.initialize_rng(); + self.start_rng(); + return Poll::Pending; } self.read_ehr_registers_into_array(&mut buffer); let remaining = destination_length - bytes_transferred; @@ -380,25 +399,36 @@ impl interrupt::typelevel::Handler for InterruptHandl 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() { + regs.rng_icr().write(|w| { + w.set_ehr_valid(true); + }); T::waker().wake(); - } else { + } else if isr.crngt_err() { + warn!("TRNG CRNGT error! Increase sample count to reduce likelihood"); + regs.rng_icr().write(|w| { + w.set_crngt_err(true); + }); + } else if isr.vn_err() { + warn!("TRNG Von-Neumann balancer error! Increase sample count to reduce likelihood"); + regs.rng_icr().write(|w| { + w.set_vn_err(true); + }); + } else if isr.autocorr_err() { // 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); - }); - } + warn!("TRNG Autocorrect error! Resetting TRNG. Increase sample count to reduce likelihood"); + regs.trng_sw_reset().write(|w| { + w.set_trng_sw_reset(true); + }); + // Fixed delay is required after TRNG soft reset, this read is sufficient. + regs.trng_sw_reset().read(); + // Wake up to reinitialize and restart the TRNG. + T::waker().wake(); } } }