From befaf275e69b91edc202773148e08443356d21c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Thu, 23 Jan 2025 11:55:29 +0100 Subject: [PATCH 01/18] Added RNG --- Cargo.toml | 3 + src/lib.rs | 3 + src/rng.rs | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 src/rng.rs diff --git a/Cargo.toml b/Cargo.toml index 24c3e0c..011caa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,8 @@ ethernet = [] # Only STM32H563/73 have ethernet otfdec = [] # Only STM32H573/33 have OTFDEC sdmmc2 = [] # Only STM32H563/73 have SDMMC2 +rand = ["dep:rand_core"] + rt = ["stm32h5/rt"] stm32h503 = ["stm32h5/stm32h503", "device-selected", "rm0492"] stm32h523 = ["stm32h5/stm32h523", "device-selected", "rm0481", "h523_h533"] @@ -64,6 +66,7 @@ embedded-hal = "1.0.0" defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} +rand_core = { version = "0.6", default-features = false, optional = true } [dev-dependencies] log = { version = "0.4.20"} diff --git a/src/lib.rs b/src/lib.rs index ece8ce4..a086c91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,9 @@ pub mod icache; #[cfg(feature = "device-selected")] pub mod delay; +#[cfg(feature = "device-selected")] +pub mod rng; + #[cfg(feature = "device-selected")] mod sealed { pub trait Sealed {} diff --git a/src/rng.rs b/src/rng.rs new file mode 100644 index 0000000..a3850a5 --- /dev/null +++ b/src/rng.rs @@ -0,0 +1,262 @@ +// Note: Code taken from stm32h7xx-hal +//! Random Number Generator +//! +//! # Examples +//! +//! - [Random Blinky](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/blinky_random.rs) + +use core::cmp; +use core::mem; + +use crate::rcc::{rec, rec::RngClkSel}; +use crate::rcc::{CoreClocks, ResetEnable}; +use crate::stm32::RNG; +use crate::time::Hertz; + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ErrorKind { + ClockError = 0, + SeedError = 1, +} + +pub trait KerClk { + /// Return the kernel clock for the Random Number Generator + /// + /// # Panics + /// + /// Panics if the kernel clock is not running + fn kernel_clk_unwrap(prec: rec::Rng, clocks: &CoreClocks) -> Hertz; +} + +impl KerClk for RNG { + fn kernel_clk_unwrap(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { + match prec.get_kernel_clk_mux() { + //RngClkSel::Hsi48 => { + RngClkSel::Hsi48Ker => { + clocks.hsi48_ck().expect("RNG: HSI48 must be enabled") + } + RngClkSel::Pll1Q => { + clocks.pll1_q_ck().expect("RNG: PLL1_Q must be enabled") + } + RngClkSel::Lse => unimplemented!(), + RngClkSel::Lsi => { + clocks.lsi_ck().expect("RNG: LSI must be enabled") + } + } + } +} + +pub trait RngExt { + fn constrain(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; +} + +impl RngExt for RNG { + fn constrain(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { + let prec = prec.enable().reset(); + + let hclk = clocks.hclk(); + let rng_clk = Self::kernel_clk_unwrap(prec, clocks); + + // Otherwise clock checker will always flag an error + // See RM0433 Rev 6 Section 33.3.6 + assert!(rng_clk > hclk / 32, "RNG: Clock too slow"); + + self.cr().modify(|_, w| w.ced().clear_bit().rngen().set_bit()); + + Rng { rb: self } + } +} + +pub trait RngCore { + fn gen(&mut self) -> Result; + fn fill(&mut self, dest: &mut [W]) -> Result<(), ErrorKind>; +} + +pub struct Rng { + rb: RNG, +} + +impl Rng { + /// Returns 32 bits of randomness, or error + pub fn value(&mut self) -> Result { + loop { + let status = self.rb.sr().read(); + if status.cecs().bit() { + return Err(ErrorKind::ClockError); + } + if status.secs().bit() { + return Err(ErrorKind::SeedError); + } + if status.drdy().bit() { + return Ok(self.rb.dr().read().rndata().bits()); + } + } + } + + pub fn release(self) -> RNG { + self.rb + } + + /// Returns a reference to the inner peripheral + pub fn inner(&self) -> &RNG { + &self.rb + } + + /// Returns a mutable reference to the inner peripheral + pub fn inner_mut(&mut self) -> &mut RNG { + &mut self.rb + } +} + +impl core::iter::Iterator for Rng { + type Item = u32; + + fn next(&mut self) -> Option { + self.value().ok() + } +} + +macro_rules! rng_core { + ($($type:ty),+) => { + $( + impl RngCore<$type> for Rng { + /// Returns a single element with random value, or error + fn gen(&mut self) -> Result<$type, ErrorKind> { + let val = self.value()?; + Ok(val as $type) + } + + /// Fills buffer with random values, or return error + fn fill(&mut self, buffer: &mut [$type]) -> Result<(), ErrorKind> { + const BATCH_SIZE: usize = 4 / mem::size_of::<$type>(); + let mut i = 0_usize; + while i < buffer.len() { + let random_word = self.value()?; + + // using to_ne_bytes does not work for u8 and would make the macro + // implementation more complicated + #[allow(clippy::transmute_num_to_bytes)] + let bytes: [$type; BATCH_SIZE] = unsafe { mem::transmute(random_word) }; + let n = cmp::min(BATCH_SIZE, buffer.len() - i); + buffer[i..i + n].copy_from_slice(&bytes[..n]); + i += n; + } + Ok(()) + } + } + )+ + }; +} + +// Only for types larger than 32 bits +macro_rules! rng_core_large { + ($($type:ty),+) => { + $( + impl RngCore<$type> for Rng { + fn gen(&mut self) -> Result<$type, ErrorKind> { + const WORDS: usize = mem::size_of::<$type>() / mem::size_of::(); + let mut res: $type = 0; + + for i in 0..WORDS { + res |= (self.value()? as $type) << (i * (mem::size_of::() * 8)) + } + + Ok(res) + } + + fn fill(&mut self, dest: &mut [$type]) -> Result<(), ErrorKind> { + let len = dest.len() * (mem::size_of::<$type>() / mem::size_of::()); + let ptr = dest.as_mut_ptr() as *mut u32; + let slice_u32 = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; + self.fill(slice_u32) + } + } + )+ + }; +} + +macro_rules! rng_core_transmute { + ($($type:ty = $from:ty),+) => { + $( + impl RngCore<$type> for Rng { + fn gen(&mut self) -> Result<$type, ErrorKind> { + let num = >::gen(self)?; + Ok(unsafe { mem::transmute::<$from, $type>(num) }) + } + + fn fill(&mut self, dest: &mut [$type]) -> Result<(), ErrorKind> { + let unsigned_slice = unsafe { mem::transmute::<&mut [$type], &mut [$from]>(dest) }; + >::fill(self, unsigned_slice) + } + } + )+ + }; +} + +rng_core!(u8, u16, u32); + +// Alignment of these types must be a multiple of mem::align_of::<32>() +rng_core_large!(u64, u128); + +// A and B must have the same alignment +// rng_core_transmute!(A = B) +// assert!(mem::align_of::() == mem::align_of::()) +rng_core_transmute!( + i8 = u8, + i16 = u16, + i32 = u32, + i64 = u64, + i128 = u128, + isize = usize +); + +// If usize is 32 bits, use the rng_core! impl +#[cfg(target_pointer_width = "32")] +rng_core!(usize); + +// If usize is 64 bits, use the rng_core_large! impl +#[cfg(target_pointer_width = "64")] +rng_core_large!(usize); + +// rand_core +#[cfg(feature = "rand")] +#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] +impl rand_core::RngCore for Rng { + /// Generate a random u32 + /// Panics if RNG fails. + fn next_u32(&mut self) -> u32 { + self.gen().unwrap() + } + + /// Generate a random u64 + /// Panics if RNG fails. + fn next_u64(&mut self) -> u64 { + self.gen().unwrap() + } + + /// Fill a slice with random data. + /// Panics if RNG fails. + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.fill(dest).unwrap() + } + + /// Try to fill a slice with random data. Return an error if RNG fails. + fn try_fill_bytes( + &mut self, + dest: &mut [u8], + ) -> Result<(), rand_core::Error> { + self.fill(dest).map_err(|e| { + core::num::NonZeroU32::new( + rand_core::Error::CUSTOM_START + e as u32, + ) + // This should never fail as long as no enum variant is equal to 0 + .expect("Internal hal error") + .into() + }) + } +} + +#[cfg(feature = "rand")] +#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] +impl rand_core::CryptoRng for Rng {} \ No newline at end of file From f3427deacb8d1c45ec2fd86c074b81b650c544d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Thu, 23 Jan 2025 14:23:13 +0100 Subject: [PATCH 02/18] Added example --- examples/blinky_random.rs | 77 +++++++++++++++++++++++++++++++++++++++ src/rng.rs | 5 ++- 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 examples/blinky_random.rs diff --git a/examples/blinky_random.rs b/examples/blinky_random.rs new file mode 100644 index 0000000..704f765 --- /dev/null +++ b/examples/blinky_random.rs @@ -0,0 +1,77 @@ +// Note: Code taken from stm32h7xx-hal +//! This example toggles the Pin PA5 with a randomly generated period between 0 +//! and 200 milliseconds. The random period is generated by the internal +//! hardware random number generator. + +#![deny(warnings)] +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use embedded_hal::delay::DelayNs; +use stm32h5xx_hal::{ + delay::Delay, + pac, + prelude::*, + rng::{RngCore, RngExt}, +}; +#[macro_use] +mod utilities; + +use log::info; + +#[entry] +fn main() -> ! { + utilities::logger::init(); + let cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().expect("cannot take peripherals"); + + // Constrain and Freeze power + info!("Setup PWR... "); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.vos0().freeze(); + + // Constrain and Freeze clock + info!("Setup RCC... "); + let rcc = dp.RCC.constrain(); + //let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SYSCFG); + let ccdr = rcc.sys_ck(100.MHz()).freeze(pwrcfg, &dp.SBS); + + info!(""); + info!("stm32h5xx-hal example - Random Blinky"); + info!(""); + + let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); + + // Configure PA5 as output. + let mut led = gpioa.pa5.into_push_pull_output(); + + // Get the delay provider. + let mut delay = Delay::new(cp.SYST, &ccdr.clocks); + + // Get true random number generator + let mut rng = dp.RNG.constrain(ccdr.peripheral.RNG, &ccdr.clocks); + let mut random_bytes = [0u16; 3]; + match rng.fill(&mut random_bytes) { + Ok(()) => info!("random bytes: {:?}", random_bytes), + Err(err) => info!("RNG error: {:?}", err), + } + + loop { + let random_element: Result = rng.gen(); + + match random_element { + Ok(random) => { + // NOTE: the result of the expression `random % 200` + // is biased. This bias is called "modulo-bias". It is + // acceptable here for simplicity, but may not be + // acceptable for your application. + let period = random % 200_u32; + + led.toggle(); + delay.delay_ms(period); + } + Err(err) => info!("RNG error: {:?}", err), + } + } +} diff --git a/src/rng.rs b/src/rng.rs index a3850a5..eaafc51 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -62,7 +62,8 @@ impl RngExt for RNG { // See RM0433 Rev 6 Section 33.3.6 assert!(rng_clk > hclk / 32, "RNG: Clock too slow"); - self.cr().modify(|_, w| w.ced().clear_bit().rngen().set_bit()); + self.cr() + .modify(|_, w| w.ced().clear_bit().rngen().set_bit()); Rng { rb: self } } @@ -259,4 +260,4 @@ impl rand_core::RngCore for Rng { #[cfg(feature = "rand")] #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] -impl rand_core::CryptoRng for Rng {} \ No newline at end of file +impl rand_core::CryptoRng for Rng {} From 9c9e5e88cad5c7fc71b8520db70c13ab76be6e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Wed, 29 Jan 2025 08:49:40 +0100 Subject: [PATCH 03/18] Fixed some requested changes --- examples/blinky_random.rs | 1 - src/prelude.rs | 1 + src/rng.rs | 71 ++++++++++++++++++++++----------------- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/examples/blinky_random.rs b/examples/blinky_random.rs index 704f765..1e94475 100644 --- a/examples/blinky_random.rs +++ b/examples/blinky_random.rs @@ -13,7 +13,6 @@ use stm32h5xx_hal::{ delay::Delay, pac, prelude::*, - rng::{RngCore, RngExt}, }; #[macro_use] mod utilities; diff --git a/src/prelude.rs b/src/prelude.rs index d0d2d40..3f91032 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -9,3 +9,4 @@ pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt; pub use crate::time::U32Ext as _; pub use fugit::{ExtU32 as _, RateExtU32 as _}; +pub use crate::rng::{RngCore as _, RngExt as _}; \ No newline at end of file diff --git a/src/rng.rs b/src/rng.rs index eaafc51..c4a2c42 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -3,7 +3,7 @@ //! //! # Examples //! -//! - [Random Blinky](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/blinky_random.rs) +//! - [Random Blinky](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/blinky_random.rs) use core::cmp; use core::mem; @@ -20,33 +20,28 @@ pub enum ErrorKind { SeedError = 1, } -pub trait KerClk { - /// Return the kernel clock for the Random Number Generator - /// - /// # Panics - /// - /// Panics if the kernel clock is not running - fn kernel_clk_unwrap(prec: rec::Rng, clocks: &CoreClocks) -> Hertz; -} - -impl KerClk for RNG { - fn kernel_clk_unwrap(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { - match prec.get_kernel_clk_mux() { - //RngClkSel::Hsi48 => { - RngClkSel::Hsi48Ker => { - clocks.hsi48_ck().expect("RNG: HSI48 must be enabled") - } - RngClkSel::Pll1Q => { - clocks.pll1_q_ck().expect("RNG: PLL1_Q must be enabled") - } - RngClkSel::Lse => unimplemented!(), - RngClkSel::Lsi => { - clocks.lsi_ck().expect("RNG: LSI must be enabled") - } +/// Return the kernel clock for the Random Number Generator +/// +/// # Panics +/// +/// Panics if the kernel clock is not running +fn kernel_clk_unwrap(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { + match prec.get_kernel_clk_mux() { + //RngClkSel::Hsi48 => { + RngClkSel::Hsi48Ker => { + clocks.hsi48_ck().expect("RNG: HSI48 must be enabled") + } + RngClkSel::Pll1Q => { + clocks.pll1_q_ck().expect("RNG: PLL1_Q must be enabled") + } + RngClkSel::Lse => unimplemented!(), + RngClkSel::Lsi => { + clocks.lsi_ck().expect("RNG: LSI must be enabled") } } } + pub trait RngExt { fn constrain(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; } @@ -56,7 +51,7 @@ impl RngExt for RNG { let prec = prec.enable().reset(); let hclk = clocks.hclk(); - let rng_clk = Self::kernel_clk_unwrap(prec, clocks); + let rng_clk = kernel_clk_unwrap(prec, clocks); // Otherwise clock checker will always flag an error // See RM0433 Rev 6 Section 33.3.6 @@ -94,6 +89,25 @@ impl Rng { } } } + + /// Returns 32 bits of randomness, or error + pub fn nb_value(&mut self) -> nb::Result { + loop { + let status = self.rb.sr().read(); + if status.cecs().bit() { + return Err(ErrorKind::ClockError); + } + if status.secs().bit() { + return Err(ErrorKind::SeedError); + } + if status.drdy().bit() { + return Ok(self.rb.dr().read().rndata().bits()); + } + else { + return Err(nb::Error::WouldBlock); + } + } + } pub fn release(self) -> RNG { self.rb @@ -130,7 +144,7 @@ macro_rules! rng_core { /// Fills buffer with random values, or return error fn fill(&mut self, buffer: &mut [$type]) -> Result<(), ErrorKind> { - const BATCH_SIZE: usize = 4 / mem::size_of::<$type>(); + const BATCH_SIZE: usize = mem::size_of::() / mem::size_of::<$type>(); let mut i = 0_usize; while i < buffer.len() { let random_word = self.value()?; @@ -160,7 +174,7 @@ macro_rules! rng_core_large { let mut res: $type = 0; for i in 0..WORDS { - res |= (self.value()? as $type) << (i * (mem::size_of::() * 8)) + res |= (self.value()? as $type) << (i * (u32::BITS as usize)) } Ok(res) @@ -258,6 +272,3 @@ impl rand_core::RngCore for Rng { } } -#[cfg(feature = "rand")] -#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] -impl rand_core::CryptoRng for Rng {} From 890cb788f1a457f2d1e3174c317bf1b59d493990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Thu, 30 Jan 2025 15:57:19 +0100 Subject: [PATCH 04/18] Follow example configurations in RM0481 and RM0492 --- Cargo.toml | 1 + examples/blinky_random.rs | 8 +- src/rng.rs | 199 ++++++++++++++++++++++++++++++-------- 3 files changed, 160 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 011caa8..171bdba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ embedded-hal = "1.0.0" defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} +nb = "1.1.0" rand_core = { version = "0.6", default-features = false, optional = true } [dev-dependencies] diff --git a/examples/blinky_random.rs b/examples/blinky_random.rs index 1e94475..706582d 100644 --- a/examples/blinky_random.rs +++ b/examples/blinky_random.rs @@ -9,11 +9,7 @@ use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; -use stm32h5xx_hal::{ - delay::Delay, - pac, - prelude::*, -}; +use stm32h5xx_hal::{delay::Delay, pac, prelude::*}; #[macro_use] mod utilities; @@ -49,7 +45,7 @@ fn main() -> ! { let mut delay = Delay::new(cp.SYST, &ccdr.clocks); // Get true random number generator - let mut rng = dp.RNG.constrain(ccdr.peripheral.RNG, &ccdr.clocks); + let mut rng = dp.RNG.rng(ccdr.peripheral.RNG, &ccdr.clocks); let mut random_bytes = [0u16; 3]; match rng.fill(&mut random_bytes) { Ok(()) => info!("random bytes: {:?}", random_bytes), diff --git a/src/rng.rs b/src/rng.rs index c4a2c42..418ba9b 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -16,6 +16,7 @@ use crate::time::Hertz; #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ErrorKind { + ///Note: The clock error has no impact on generated random numbers that is the application can still read the RNG_DR register ClockError = 0, SeedError = 1, } @@ -35,30 +36,161 @@ fn kernel_clk_unwrap(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { clocks.pll1_q_ck().expect("RNG: PLL1_Q must be enabled") } RngClkSel::Lse => unimplemented!(), - RngClkSel::Lsi => { - clocks.lsi_ck().expect("RNG: LSI must be enabled") - } + RngClkSel::Lsi => clocks.lsi_ck().expect("RNG: LSI must be enabled"), } } +fn setup_clocks(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { + let prec = prec.enable().reset(); + + let hclk = clocks.hclk(); + let rng_clk = kernel_clk_unwrap(prec, clocks); + + // Otherwise clock checker will always flag an error + // See RM0481 Rev 2 Section 32.3.6 + assert!(rng_clk > (hclk / 32), "RNG: Clock too slow"); + + rng_clk +} + +#[cfg(any( + feature = "stm32h562", + feature = "stm32h563", + feature = "stm32h573", +))] + +/// This uses the register values specified in AN4230 but have not +/// performed the verification (buyer beware, users can/should do their own verification) +/// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 +pub trait RngNist { + fn rng_nist_st_an4230(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; +} + +#[cfg(any( + feature = "stm32h562", + feature = "stm32h563", + feature = "stm32h573" +))] +impl RngNist for RNG { + /// This uses the register values specified in AN4230 but have not + /// performed the verification (buyer beware, users can/should do their own verification) + /// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 + fn rng_nist_st_an4230(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { + let rng_clk = setup_clocks(prec, clocks); + + // ST has tested this configuration only with a RNG clock of 48MHz + assert_eq!(rng_clk, Hertz::MHz(48), "RNG: Clock not 48 MHz"); + + // Set control register values, also need to write 1 to CONDRST to be able to set the other values + self.cr() + .write(|w| unsafe { w.bits(0x00F00E00).condrst().set_bit() }); + + // Set health test control register values + self.htcr().write(|w| unsafe { w.bits(0x6A91) }); + + // Set noise source control register + self.nscr().write(|w| unsafe { w.bits(0x3AF66) }); + + // Configuration done, reset CONDRST, its value goes to 0 when the reset process is + // done. It takes about 2 AHB clock cycles + 2 RNG clock cycles. + self.cr().write(|w| w.condrst().clear_bit()); + + // It should take about 2 AHB clock cycles + 2 RNG clock cycles + while self.cr().read().condrst().bit_is_set() {} + + // Enable RNG + self.cr().modify(|_, w| w.rngen().set_bit()); + + Rng { rb: self } + } +} pub trait RngExt { - fn constrain(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; + fn rng(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; + fn rng_fast(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; } impl RngExt for RNG { - fn constrain(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { - let prec = prec.enable().reset(); + /// This uses the register values specified in RM0481 Rev 2 section 32.6.2 RNG configuration C + fn rng(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { + setup_clocks(prec, clocks); + + // Set control register values, also need to write 1 to CONDRST to be able to set the other values + self.cr().write(|w| unsafe { + w.nistc() + .clear_bit() + .rng_config1() + .bits(0x0F) + .clkdiv() + .bits(0x0) + .rng_config2() + .bits(0x0) + .rng_config3() + .bits(0xD) + .ced() + .clear_bit() + .condrst() + .set_bit() + }); + + // Set health test control register values + self.htcr().write(|w| unsafe { w.bits(0xAAC7) }); + + // Set noise source control register + #[cfg(not(feature = "stm32h503"))] // Not available on H503 + self.nscr().write(|w| unsafe { w.bits(0x0003FFFF) }); + + // Configuration done, reset CONDRST, its value goes to 0 when the reset process is + // done. It takes about 2 AHB clock cycles + 2 RNG clock cycles. + self.cr().write(|w| w.condrst().clear_bit()); + + // It should take about 2 AHB clock cycles + 2 RNG clock cycles + while self.cr().read().condrst().bit_is_set() {} + + // Enable RNG + self.cr().modify(|_, w| w.rngen().set_bit()); - let hclk = clocks.hclk(); - let rng_clk = kernel_clk_unwrap(prec, clocks); - - // Otherwise clock checker will always flag an error - // See RM0433 Rev 6 Section 33.3.6 - assert!(rng_clk > hclk / 32, "RNG: Clock too slow"); + Rng { rb: self } + } - self.cr() - .modify(|_, w| w.ced().clear_bit().rngen().set_bit()); + /// This uses the register values specified in RM0481 Rev 2 section 32.6.2 RNG configuration B + fn rng_fast(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { + setup_clocks(prec, clocks); + + // Set control register values, also need to write 1 to CONDRST to be able to set the other values + self.cr().write(|w| unsafe { + w.nistc() + .set_bit() + .rng_config1() + .bits(0x18) + .clkdiv() + .bits(0x0) + .rng_config2() + .bits(0x0) + .rng_config3() + .bits(0x0) + .ced() + .clear_bit() + .condrst() + .set_bit() + }); + + // Set health test control register values + self.htcr().write(|w| unsafe { w.bits(0xAAC7) }); + + // Set noise source control register + #[cfg(not(feature = "stm32h503"))] // Not available on H503 + self.nscr().write(|w| unsafe { w.bits(0x0003FFFF) }); + + // Configuration done, reset CONDRST, its value goes to 0 when the reset process is + // done. It takes about 2 AHB clock cycles + 2 RNG clock cycles. + self.cr().write(|w| w.condrst().clear_bit()); + + // It should take about 2 AHB clock cycles + 2 RNG clock cycles + while self.cr().read().condrst().bit_is_set() {} + + // Enable RNG + self.cr().modify(|_, w| w.rngen().set_bit()); Rng { rb: self } } @@ -76,36 +208,20 @@ pub struct Rng { impl Rng { /// Returns 32 bits of randomness, or error pub fn value(&mut self) -> Result { - loop { - let status = self.rb.sr().read(); - if status.cecs().bit() { - return Err(ErrorKind::ClockError); - } - if status.secs().bit() { - return Err(ErrorKind::SeedError); - } - if status.drdy().bit() { - return Ok(self.rb.dr().read().rndata().bits()); - } - } + nb::block!(self.nb_value()) } - + /// Returns 32 bits of randomness, or error pub fn nb_value(&mut self) -> nb::Result { - loop { - let status = self.rb.sr().read(); - if status.cecs().bit() { - return Err(ErrorKind::ClockError); - } - if status.secs().bit() { - return Err(ErrorKind::SeedError); - } - if status.drdy().bit() { - return Ok(self.rb.dr().read().rndata().bits()); - } - else { - return Err(nb::Error::WouldBlock); - } + let status = self.rb.sr().read(); + if status.cecs().bit() { + Err(nb::Error::Other(ErrorKind::ClockError)) + } else if status.secs().bit() { + Err(nb::Error::Other(ErrorKind::SeedError)) + } else if status.drdy().bit() { + Ok(self.rb.dr().read().rndata().bits()) + } else { + Err(nb::Error::WouldBlock) } } @@ -271,4 +387,3 @@ impl rand_core::RngCore for Rng { }) } } - From a4bedf5a8e6f179c2fee31323c876a7c8eed1f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Thu, 30 Jan 2025 16:05:48 +0100 Subject: [PATCH 05/18] fmt --- src/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prelude.rs b/src/prelude.rs index 3f91032..962878c 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -7,6 +7,6 @@ pub use crate::icache::ICacheExt as _stm32h5xx_hal_icache_ICacheExt; pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt; pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt; +pub use crate::rng::{RngCore as _, RngExt as _}; pub use crate::time::U32Ext as _; pub use fugit::{ExtU32 as _, RateExtU32 as _}; -pub use crate::rng::{RngCore as _, RngExt as _}; \ No newline at end of file From 18692649c64f739ba9d5f209a17f4a107633e37f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Thu, 22 May 2025 10:52:07 +0200 Subject: [PATCH 06/18] Updated to latest and fixed some desired changes --- src/rng.rs | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index 418ba9b..e372597 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -15,7 +15,7 @@ use crate::time::Hertz; #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ErrorKind { +pub enum Error { ///Note: The clock error has no impact on generated random numbers that is the application can still read the RNG_DR register ClockError = 0, SeedError = 1, @@ -33,7 +33,7 @@ fn kernel_clk_unwrap(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { clocks.hsi48_ck().expect("RNG: HSI48 must be enabled") } RngClkSel::Pll1Q => { - clocks.pll1_q_ck().expect("RNG: PLL1_Q must be enabled") + clocks.pll1().q_ck().expect("RNG: PLL1_Q must be enabled") } RngClkSel::Lse => unimplemented!(), RngClkSel::Lsi => clocks.lsi_ck().expect("RNG: LSI must be enabled"), @@ -59,8 +59,10 @@ fn setup_clocks(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { feature = "stm32h573", ))] -/// This uses the register values specified in AN4230 but have not -/// performed the verification (buyer beware, users can/should do their own verification) +/// Note: +/// This uses the register values specified in AN4230 but verification +/// using this HAL has not been performed. Users can/should do their +/// own verification or request documentation from ST directly. /// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 pub trait RngNist { fn rng_nist_st_an4230(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; @@ -72,8 +74,10 @@ pub trait RngNist { feature = "stm32h573" ))] impl RngNist for RNG { - /// This uses the register values specified in AN4230 but have not - /// performed the verification (buyer beware, users can/should do their own verification) + /// Note: + /// This uses the register values specified in AN4230 but verification + /// using this HAL has not been performed. Users can/should do their + /// own verification or request documentation from ST directly. /// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 fn rng_nist_st_an4230(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { let rng_clk = setup_clocks(prec, clocks); @@ -137,7 +141,9 @@ impl RngExt for RNG { self.htcr().write(|w| unsafe { w.bits(0xAAC7) }); // Set noise source control register - #[cfg(not(feature = "stm32h503"))] // Not available on H503 + // Note: + // This is currently not available in the PAC or SVD for H503 but is planned to be added + #[cfg(not(feature = "stm32h503"))] self.nscr().write(|w| unsafe { w.bits(0x0003FFFF) }); // Configuration done, reset CONDRST, its value goes to 0 when the reset process is @@ -197,8 +203,8 @@ impl RngExt for RNG { } pub trait RngCore { - fn gen(&mut self) -> Result; - fn fill(&mut self, dest: &mut [W]) -> Result<(), ErrorKind>; + fn gen(&mut self) -> Result; + fn fill(&mut self, dest: &mut [W]) -> Result<(), Error>; } pub struct Rng { @@ -207,17 +213,17 @@ pub struct Rng { impl Rng { /// Returns 32 bits of randomness, or error - pub fn value(&mut self) -> Result { + pub fn value(&mut self) -> Result { nb::block!(self.nb_value()) } /// Returns 32 bits of randomness, or error - pub fn nb_value(&mut self) -> nb::Result { + pub fn nb_value(&mut self) -> nb::Result { let status = self.rb.sr().read(); if status.cecs().bit() { - Err(nb::Error::Other(ErrorKind::ClockError)) + Err(nb::Error::Other(Error::ClockError)) } else if status.secs().bit() { - Err(nb::Error::Other(ErrorKind::SeedError)) + Err(nb::Error::Other(Error::SeedError)) } else if status.drdy().bit() { Ok(self.rb.dr().read().rndata().bits()) } else { @@ -253,13 +259,13 @@ macro_rules! rng_core { $( impl RngCore<$type> for Rng { /// Returns a single element with random value, or error - fn gen(&mut self) -> Result<$type, ErrorKind> { + fn gen(&mut self) -> Result<$type, Error> { let val = self.value()?; Ok(val as $type) } /// Fills buffer with random values, or return error - fn fill(&mut self, buffer: &mut [$type]) -> Result<(), ErrorKind> { + fn fill(&mut self, buffer: &mut [$type]) -> Result<(), Error> { const BATCH_SIZE: usize = mem::size_of::() / mem::size_of::<$type>(); let mut i = 0_usize; while i < buffer.len() { @@ -285,7 +291,7 @@ macro_rules! rng_core_large { ($($type:ty),+) => { $( impl RngCore<$type> for Rng { - fn gen(&mut self) -> Result<$type, ErrorKind> { + fn gen(&mut self) -> Result<$type, Error> { const WORDS: usize = mem::size_of::<$type>() / mem::size_of::(); let mut res: $type = 0; @@ -296,7 +302,7 @@ macro_rules! rng_core_large { Ok(res) } - fn fill(&mut self, dest: &mut [$type]) -> Result<(), ErrorKind> { + fn fill(&mut self, dest: &mut [$type]) -> Result<(), Error> { let len = dest.len() * (mem::size_of::<$type>() / mem::size_of::()); let ptr = dest.as_mut_ptr() as *mut u32; let slice_u32 = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; @@ -311,12 +317,12 @@ macro_rules! rng_core_transmute { ($($type:ty = $from:ty),+) => { $( impl RngCore<$type> for Rng { - fn gen(&mut self) -> Result<$type, ErrorKind> { + fn gen(&mut self) -> Result<$type, Error> { let num = >::gen(self)?; Ok(unsafe { mem::transmute::<$from, $type>(num) }) } - fn fill(&mut self, dest: &mut [$type]) -> Result<(), ErrorKind> { + fn fill(&mut self, dest: &mut [$type]) -> Result<(), Error> { let unsigned_slice = unsafe { mem::transmute::<&mut [$type], &mut [$from]>(dest) }; >::fill(self, unsigned_slice) } From 91d90f0bb69f4f46839d7e7c0bc5ed1ae73a47d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Thu, 22 May 2025 11:09:44 +0200 Subject: [PATCH 07/18] Formatting --- src/rng.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index e372597..8c33994 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -60,7 +60,7 @@ fn setup_clocks(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { ))] /// Note: -/// This uses the register values specified in AN4230 but verification +/// This uses the register values specified in AN4230 but verification /// using this HAL has not been performed. Users can/should do their /// own verification or request documentation from ST directly. /// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 @@ -75,7 +75,7 @@ pub trait RngNist { ))] impl RngNist for RNG { /// Note: - /// This uses the register values specified in AN4230 but verification + /// This uses the register values specified in AN4230 but verification /// using this HAL has not been performed. Users can/should do their /// own verification or request documentation from ST directly. /// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 @@ -141,7 +141,7 @@ impl RngExt for RNG { self.htcr().write(|w| unsafe { w.bits(0xAAC7) }); // Set noise source control register - // Note: + // Note: // This is currently not available in the PAC or SVD for H503 but is planned to be added #[cfg(not(feature = "stm32h503"))] self.nscr().write(|w| unsafe { w.bits(0x0003FFFF) }); From 60eee0bb8685dbde03eddbe8a1d1321a1a28039d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Tue, 27 May 2025 10:33:47 +0200 Subject: [PATCH 08/18] Added CryptRng and modes --- src/rng.rs | 96 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index 8c33994..eaa926c 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -65,7 +65,11 @@ fn setup_clocks(prec: rec::Rng, clocks: &CoreClocks) -> Hertz { /// own verification or request documentation from ST directly. /// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 pub trait RngNist { - fn rng_nist_st_an4230(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; + fn rng_nist_st_an4230( + self, + prec: rec::Rng, + clocks: &CoreClocks, + ) -> Rng; } #[cfg(any( @@ -79,7 +83,11 @@ impl RngNist for RNG { /// using this HAL has not been performed. Users can/should do their /// own verification or request documentation from ST directly. /// Requires RNG to be disabled since some register values can only be written when RNGEN = 0 - fn rng_nist_st_an4230(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { + fn rng_nist_st_an4230( + self, + prec: rec::Rng, + clocks: &CoreClocks, + ) -> Rng { let rng_clk = setup_clocks(prec, clocks); // ST has tested this configuration only with a RNG clock of 48MHz @@ -105,18 +113,21 @@ impl RngNist for RNG { // Enable RNG self.cr().modify(|_, w| w.rngen().set_bit()); - Rng { rb: self } + Rng { + rb: self, + mode: NIST, + } } } pub trait RngExt { - fn rng(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; - fn rng_fast(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; + fn rng(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; + fn rng_fast(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng; } impl RngExt for RNG { /// This uses the register values specified in RM0481 Rev 2 section 32.6.2 RNG configuration C - fn rng(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { + fn rng(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { setup_clocks(prec, clocks); // Set control register values, also need to write 1 to CONDRST to be able to set the other values @@ -156,11 +167,14 @@ impl RngExt for RNG { // Enable RNG self.cr().modify(|_, w| w.rngen().set_bit()); - Rng { rb: self } + Rng { + rb: self, + _mode: NORMAL, + } } /// This uses the register values specified in RM0481 Rev 2 section 32.6.2 RNG configuration B - fn rng_fast(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { + fn rng_fast(self, prec: rec::Rng, clocks: &CoreClocks) -> Rng { setup_clocks(prec, clocks); // Set control register values, also need to write 1 to CONDRST to be able to set the other values @@ -198,7 +212,10 @@ impl RngExt for RNG { // Enable RNG self.cr().modify(|_, w| w.rngen().set_bit()); - Rng { rb: self } + Rng { + rb: self, + _mode: FAST, + } } } @@ -207,27 +224,39 @@ pub trait RngCore { fn fill(&mut self, dest: &mut [W]) -> Result<(), Error>; } -pub struct Rng { +#[cfg(any( + feature = "stm32h562", + feature = "stm32h563", + feature = "stm32h573" +))] +/// NIST mode (type state) +/// Use [RngNist::rng_nist_st_an4230] to generate [Rng] in this mode +pub struct NIST; +/// FAST mode (type state) +/// Use [RngExt::rng_fast] to generate [Rng] in this mode +pub struct FAST; +/// NORMAL mode (type state) +/// Use [RngExt::rng] to generate [Rng] in this mode +pub struct NORMAL; + +pub struct Rng { rb: RNG, + _mode: MODE, } -impl Rng { +impl Rng { /// Returns 32 bits of randomness, or error pub fn value(&mut self) -> Result { - nb::block!(self.nb_value()) - } - - /// Returns 32 bits of randomness, or error - pub fn nb_value(&mut self) -> nb::Result { - let status = self.rb.sr().read(); - if status.cecs().bit() { - Err(nb::Error::Other(Error::ClockError)) - } else if status.secs().bit() { - Err(nb::Error::Other(Error::SeedError)) - } else if status.drdy().bit() { - Ok(self.rb.dr().read().rndata().bits()) - } else { - Err(nb::Error::WouldBlock) + loop { + let status = self.rb.sr().read(); + + if status.cecs().bit() { + return Err(Error::ClockError); + } else if status.secs().bit() { + return Err(Error::SeedError); + } else if status.drdy().bit() { + return Ok(self.rb.dr().read().rndata().bits()); + } } } @@ -246,7 +275,7 @@ impl Rng { } } -impl core::iter::Iterator for Rng { +impl core::iter::Iterator for Rng { type Item = u32; fn next(&mut self) -> Option { @@ -257,7 +286,7 @@ impl core::iter::Iterator for Rng { macro_rules! rng_core { ($($type:ty),+) => { $( - impl RngCore<$type> for Rng { + impl RngCore<$type> for Rng { /// Returns a single element with random value, or error fn gen(&mut self) -> Result<$type, Error> { let val = self.value()?; @@ -290,7 +319,7 @@ macro_rules! rng_core { macro_rules! rng_core_large { ($($type:ty),+) => { $( - impl RngCore<$type> for Rng { + impl RngCore<$type> for Rng { fn gen(&mut self) -> Result<$type, Error> { const WORDS: usize = mem::size_of::<$type>() / mem::size_of::(); let mut res: $type = 0; @@ -316,7 +345,7 @@ macro_rules! rng_core_large { macro_rules! rng_core_transmute { ($($type:ty = $from:ty),+) => { $( - impl RngCore<$type> for Rng { + impl RngCore<$type> for Rng { fn gen(&mut self) -> Result<$type, Error> { let num = >::gen(self)?; Ok(unsafe { mem::transmute::<$from, $type>(num) }) @@ -359,7 +388,7 @@ rng_core_large!(usize); // rand_core #[cfg(feature = "rand")] #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] -impl rand_core::RngCore for Rng { +impl rand_core::RngCore for Rng { /// Generate a random u32 /// Panics if RNG fails. fn next_u32(&mut self) -> u32 { @@ -393,3 +422,10 @@ impl rand_core::RngCore for Rng { }) } } + +#[cfg(all( + feature = "rand", + any(feature = "stm32h562", feature = "stm32h563", feature = "stm32h573") +))] +#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] +impl rand_core::CryptoRng for Rng {} From 92a05d3b3434ff1794d3466ef516adbc697b7f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Tue, 27 May 2025 10:41:52 +0200 Subject: [PATCH 09/18] Fixed missing field --- src/rng.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rng.rs b/src/rng.rs index eaa926c..8b011d6 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -115,7 +115,7 @@ impl RngNist for RNG { Rng { rb: self, - mode: NIST, + _mode: NIST, } } } From 032cb53364f45454ddeb20d2527f5382f988741a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Wed, 28 May 2025 11:18:18 +0200 Subject: [PATCH 10/18] Fixed requested changes --- Cargo.toml | 1 - src/rng.rs | 28 +++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 171bdba..011caa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,6 @@ embedded-hal = "1.0.0" defmt = { version = "1.0.0", optional = true } paste = "1.0.15" log = { version = "0.4.20", optional = true} -nb = "1.1.0" rand_core = { version = "0.6", default-features = false, optional = true } [dev-dependencies] diff --git a/src/rng.rs b/src/rng.rs index 8b011d6..4202b6b 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -6,6 +6,7 @@ //! - [Random Blinky](https://github.com/stm32-rs/stm32h5xx-hal/blob/master/examples/blinky_random.rs) use core::cmp; +use core::marker::PhantomData; use core::mem; use crate::rcc::{rec, rec::RngClkSel}; @@ -113,10 +114,7 @@ impl RngNist for RNG { // Enable RNG self.cr().modify(|_, w| w.rngen().set_bit()); - Rng { - rb: self, - _mode: NIST, - } + Rng::new(self) } } @@ -167,10 +165,7 @@ impl RngExt for RNG { // Enable RNG self.cr().modify(|_, w| w.rngen().set_bit()); - Rng { - rb: self, - _mode: NORMAL, - } + Rng::new(self) } /// This uses the register values specified in RM0481 Rev 2 section 32.6.2 RNG configuration B @@ -212,10 +207,7 @@ impl RngExt for RNG { // Enable RNG self.cr().modify(|_, w| w.rngen().set_bit()); - Rng { - rb: self, - _mode: FAST, - } + Rng::new(self) } } @@ -241,11 +233,19 @@ pub struct NORMAL; pub struct Rng { rb: RNG, - _mode: MODE, + _mode: PhantomData, } impl Rng { + fn new(rb: RNG) -> Self { + Self { + rb, + _mode: PhantomData, + } + } /// Returns 32 bits of randomness, or error + /// Automatically resets the seed error flag upon SeedError but will still return SeedError + /// Upon receiving SeedError the user is expected to keep polling this function until a valid value is returned pub fn value(&mut self) -> Result { loop { let status = self.rb.sr().read(); @@ -253,6 +253,8 @@ impl Rng { if status.cecs().bit() { return Err(Error::ClockError); } else if status.secs().bit() { + // Reset seed error flag so as to leave the peripheral in a valid state ready for use + self.rb.sr().modify(|_, w| w.seis().clear_bit()); return Err(Error::SeedError); } else if status.drdy().bit() { return Ok(self.rb.dr().read().rndata().bits()); From 166cfe2cbeb1301db2cd7e08386cf76b7aa27ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Wed, 28 May 2025 11:38:27 +0200 Subject: [PATCH 11/18] Changed iterator to work with result instead --- src/rng.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index 4202b6b..0115fa6 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -278,10 +278,10 @@ impl Rng { } impl core::iter::Iterator for Rng { - type Item = u32; + type Item = Result; - fn next(&mut self) -> Option { - self.value().ok() + fn next(&mut self) -> Option { + Some(self.value()) } } From e590474d68bb234f6f599e2589f1b021c8968a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Mon, 2 Jun 2025 11:48:53 +0200 Subject: [PATCH 12/18] Changed clock error handling --- src/rng.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index 0115fa6..f9de473 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -247,11 +247,18 @@ impl Rng { /// Automatically resets the seed error flag upon SeedError but will still return SeedError /// Upon receiving SeedError the user is expected to keep polling this function until a valid value is returned pub fn value(&mut self) -> Result { - loop { + 'outer: loop { let status = self.rb.sr().read(); if status.cecs().bit() { - return Err(Error::ClockError); + let sr = self.rb.sr(); + // Give rng some time to recover from clock disturbance, this time seems to be about a handful of milliseconds + for _ in 0..100_000 { + if sr.read().cecs().bit_is_clear() { + continue 'outer; + } + } + panic!("Failed to automatically recover from Rng Clock Error"); } else if status.secs().bit() { // Reset seed error flag so as to leave the peripheral in a valid state ready for use self.rb.sr().modify(|_, w| w.seis().clear_bit()); From b5c023d6f6e8a74d0d263ebe2fbfd9b980b0885f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Mon, 2 Jun 2025 11:54:02 +0200 Subject: [PATCH 13/18] Removed clock error type --- src/rng.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index f9de473..46ed7a8 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -16,11 +16,7 @@ use crate::time::Hertz; #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - ///Note: The clock error has no impact on generated random numbers that is the application can still read the RNG_DR register - ClockError = 0, - SeedError = 1, -} +pub struct SeedError; /// Return the kernel clock for the Random Number Generator /// @@ -212,8 +208,8 @@ impl RngExt for RNG { } pub trait RngCore { - fn gen(&mut self) -> Result; - fn fill(&mut self, dest: &mut [W]) -> Result<(), Error>; + fn gen(&mut self) -> Result; + fn fill(&mut self, dest: &mut [W]) -> Result<(), SeedError>; } #[cfg(any( @@ -246,7 +242,7 @@ impl Rng { /// Returns 32 bits of randomness, or error /// Automatically resets the seed error flag upon SeedError but will still return SeedError /// Upon receiving SeedError the user is expected to keep polling this function until a valid value is returned - pub fn value(&mut self) -> Result { + pub fn value(&mut self) -> Result { 'outer: loop { let status = self.rb.sr().read(); @@ -262,7 +258,7 @@ impl Rng { } else if status.secs().bit() { // Reset seed error flag so as to leave the peripheral in a valid state ready for use self.rb.sr().modify(|_, w| w.seis().clear_bit()); - return Err(Error::SeedError); + return Err(SeedError); } else if status.drdy().bit() { return Ok(self.rb.dr().read().rndata().bits()); } @@ -285,7 +281,7 @@ impl Rng { } impl core::iter::Iterator for Rng { - type Item = Result; + type Item = Result; fn next(&mut self) -> Option { Some(self.value()) @@ -297,13 +293,13 @@ macro_rules! rng_core { $( impl RngCore<$type> for Rng { /// Returns a single element with random value, or error - fn gen(&mut self) -> Result<$type, Error> { + fn gen(&mut self) -> Result<$type, SeedError> { let val = self.value()?; Ok(val as $type) } /// Fills buffer with random values, or return error - fn fill(&mut self, buffer: &mut [$type]) -> Result<(), Error> { + fn fill(&mut self, buffer: &mut [$type]) -> Result<(), SeedError> { const BATCH_SIZE: usize = mem::size_of::() / mem::size_of::<$type>(); let mut i = 0_usize; while i < buffer.len() { @@ -329,7 +325,7 @@ macro_rules! rng_core_large { ($($type:ty),+) => { $( impl RngCore<$type> for Rng { - fn gen(&mut self) -> Result<$type, Error> { + fn gen(&mut self) -> Result<$type, SeedError> { const WORDS: usize = mem::size_of::<$type>() / mem::size_of::(); let mut res: $type = 0; @@ -340,7 +336,7 @@ macro_rules! rng_core_large { Ok(res) } - fn fill(&mut self, dest: &mut [$type]) -> Result<(), Error> { + fn fill(&mut self, dest: &mut [$type]) -> Result<(), SeedError> { let len = dest.len() * (mem::size_of::<$type>() / mem::size_of::()); let ptr = dest.as_mut_ptr() as *mut u32; let slice_u32 = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; @@ -355,12 +351,12 @@ macro_rules! rng_core_transmute { ($($type:ty = $from:ty),+) => { $( impl RngCore<$type> for Rng { - fn gen(&mut self) -> Result<$type, Error> { + fn gen(&mut self) -> Result<$type, SeedError> { let num = >::gen(self)?; Ok(unsafe { mem::transmute::<$from, $type>(num) }) } - fn fill(&mut self, dest: &mut [$type]) -> Result<(), Error> { + fn fill(&mut self, dest: &mut [$type]) -> Result<(), SeedError> { let unsigned_slice = unsafe { mem::transmute::<&mut [$type], &mut [$from]>(dest) }; >::fill(self, unsigned_slice) } From 27ea3ebec06e7726c62916e672af1a26e31d67b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Mon, 2 Jun 2025 13:23:35 +0200 Subject: [PATCH 14/18] Added logging on clock error --- src/rng.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index 46ed7a8..c1d3ca5 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -247,6 +247,12 @@ impl Rng { let status = self.rb.sr().read(); if status.cecs().bit() { + #[cfg(feature = "log")] + log::warn!("RNG Clock error detected, retrying"); + + #[cfg(feature = "defmt")] + defmt::warn!("RNG Clock error detected, retrying"); + let sr = self.rb.sr(); // Give rng some time to recover from clock disturbance, this time seems to be about a handful of milliseconds for _ in 0..100_000 { @@ -281,10 +287,16 @@ impl Rng { } impl core::iter::Iterator for Rng { - type Item = Result; + type Item = u32; fn next(&mut self) -> Option { - Some(self.value()) + loop { + match self.value() { + Ok(x) => return Some(x), + // We recover automatically from a seed error, so try again + Err(SeedError) => (), + } + } } } From 628598c24f546aec14c784a55d1d22770453a025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Mon, 2 Jun 2025 13:25:49 +0200 Subject: [PATCH 15/18] Added logging for seed error --- src/rng.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rng.rs b/src/rng.rs index c1d3ca5..edd2eca 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -262,6 +262,11 @@ impl Rng { } panic!("Failed to automatically recover from Rng Clock Error"); } else if status.secs().bit() { + #[cfg(feature = "log")] + log::warn!("RNG Seed error detected, retrying"); + + #[cfg(feature = "defmt")] + defmt::warn!("RNG Seed error detected, retrying"); // Reset seed error flag so as to leave the peripheral in a valid state ready for use self.rb.sr().modify(|_, w| w.seis().clear_bit()); return Err(SeedError); From e225af182b848d1c708e3cf9449058f5e4cfd491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Mon, 2 Jun 2025 13:29:02 +0200 Subject: [PATCH 16/18] Fix for clippy --- src/rng.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index edd2eca..14c75be 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -296,10 +296,9 @@ impl core::iter::Iterator for Rng { fn next(&mut self) -> Option { loop { - match self.value() { - Ok(x) => return Some(x), - // We recover automatically from a seed error, so try again - Err(SeedError) => (), + // We recover automatically from a seed error, so try again + if let Ok(x) = self.value() { + return Some(x); } } } From fc207d127a9434517e68a0768a454e07900f5963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Mon, 2 Jun 2025 14:23:10 +0200 Subject: [PATCH 17/18] Fixed missed error --- .github/workflows/ci.yml | 6 ++++-- src/rng.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a615547..6c67a5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,8 @@ jobs: logger: - log - defmt + features: + - rand env: # Peripheral Feature flags FLAGS: rt @@ -45,6 +47,6 @@ jobs: - name: Install thumbv8m rust target run: rustup target add thumbv8m.main-none-eabihf - name: Build - run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }} + run: cargo build --verbose --release --examples --target thumbv8m.main-none-eabihf --features ${{ matrix.mcu }},${{ matrix.features }},${{ matrix.logger }},${{ env.FLAGS }} - name: Test - run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.logger }},${{ env.FLAGS }} + run: cargo test --lib --target x86_64-unknown-linux-gnu --features ${{ matrix.mcu }},${{ matrix.features }},${{ matrix.logger }},${{ env.FLAGS }} diff --git a/src/rng.rs b/src/rng.rs index 14c75be..a330d71 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -433,9 +433,9 @@ impl rand_core::RngCore for Rng { &mut self, dest: &mut [u8], ) -> Result<(), rand_core::Error> { - self.fill(dest).map_err(|e| { + self.fill(dest).map_err(|_e| { core::num::NonZeroU32::new( - rand_core::Error::CUSTOM_START + e as u32, + rand_core::Error::CUSTOM_START, ) // This should never fail as long as no enum variant is equal to 0 .expect("Internal hal error") From 41445187a05301d6c552b20925085639670bda4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sn=C3=B6man?= Date: Tue, 3 Jun 2025 09:09:53 +0200 Subject: [PATCH 18/18] Fixed formatting --- src/rng.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rng.rs b/src/rng.rs index a330d71..d30cc9f 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -434,12 +434,10 @@ impl rand_core::RngCore for Rng { dest: &mut [u8], ) -> Result<(), rand_core::Error> { self.fill(dest).map_err(|_e| { - core::num::NonZeroU32::new( - rand_core::Error::CUSTOM_START, - ) - // This should never fail as long as no enum variant is equal to 0 - .expect("Internal hal error") - .into() + core::num::NonZeroU32::new(rand_core::Error::CUSTOM_START) + // This should never fail as long as no enum variant is equal to 0 + .expect("Internal hal error") + .into() }) } }