Skip to content

Commit a6369ca

Browse files
committed
2 parents 15f7fab + b9220ed commit a6369ca

File tree

4 files changed

+312
-0
lines changed

4 files changed

+312
-0
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ stm32-usbd = { version = "0.7.0", optional = true }
2020
fixed = { version = "1.28.0", optional = true }
2121
embedded-io = "0.6"
2222
stm32-hrtim = { version = "0.1.0", optional = true }
23+
rand_core = { version = "0.9", default-features = false }
2324

2425
[dependencies.cortex-m]
2526
version = "0.7.7"
@@ -72,6 +73,7 @@ bme680 = "0.6.0"
7273
embedded-sdmmc = "0.3.0"
7374
usb-device = { version = "0.3.2", features = ["defmt"] }
7475
usbd-serial = "0.2.2"
76+
rand = { version = "0.9", default-features = false }
7577

7678
#TODO: Separate feature sets
7779
[features]

examples/rand.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//! Example of using the [`Rng`] peripheral.
2+
//!
3+
//! This example demonstrates common use cases of the [`rand::Rng`] trait using the G4 TRNG.
4+
//!
5+
//! ```DEFMT_LOG=debug cargo run --release --example rand --features stm32g431,defmt -- --chip STM32G431KBTx```
6+
7+
#![deny(warnings)]
8+
#![deny(unsafe_code)]
9+
#![no_main]
10+
#![no_std]
11+
12+
use hal::prelude::*;
13+
use hal::rng::RngExt;
14+
use hal::stm32;
15+
use rand::distr::Bernoulli;
16+
use rand::distr::Distribution;
17+
use rand::distr::Uniform;
18+
use rand::seq::IndexedRandom;
19+
use rand::{Rng, TryRngCore};
20+
use stm32g4xx_hal as hal;
21+
22+
use cortex_m_rt::entry;
23+
24+
#[macro_use]
25+
mod utils;
26+
27+
use utils::logger::info;
28+
29+
#[entry]
30+
fn main() -> ! {
31+
utils::logger::init();
32+
33+
info!("start");
34+
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
35+
let cp = cortex_m::Peripherals::take().expect("cannot take core peripherals");
36+
37+
let mut rcc = dp.RCC.constrain();
38+
39+
let mut delay = cp.SYST.delay(&rcc.clocks);
40+
41+
// Constrain and start the random number generator peripheral
42+
let rng = dp.RNG.constrain(&mut rcc).start();
43+
44+
// Create a Uniform distribution sampler between 100 and 1000
45+
let between = Uniform::try_from(100..1000).unwrap();
46+
47+
// Create a Bernoulli distribution sampler with a 20% probability of returning true
48+
let bernoulli = Bernoulli::new(0.2).unwrap();
49+
50+
// A slice of values for IndexedRandom::choose
51+
let slice = ["foo", "bar", "baz"];
52+
53+
loop {
54+
let random_float = rng.unwrap_err().random::<f32>();
55+
info!("Random float: {}", random_float);
56+
57+
let random_u8 = rng.unwrap_err().random::<u8>();
58+
info!("Random u8: {}", random_u8);
59+
60+
let random_array: [f32; 8] = rng.unwrap_err().random();
61+
info!("Random array {:?}", &random_array);
62+
63+
let random_dist = between.sample(&mut rng.unwrap_err());
64+
info!("Random dist: {}", random_dist);
65+
66+
let random_range = rng.unwrap_err().random_range(-10..10);
67+
info!("Random range: {}", random_range);
68+
69+
let random_choice = slice.choose(&mut rng.unwrap_err());
70+
info!("Random choice: {:?}", random_choice);
71+
72+
let random_bernoulli = bernoulli.sample(&mut rng.unwrap_err());
73+
info!("Random bernoulli: {}", random_bernoulli);
74+
75+
delay.delay_ms(1000);
76+
}
77+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub mod syscfg;
9393
pub mod time;
9494
pub mod timer;
9595
// pub mod watchdog;
96+
pub mod rng;
9697

9798
#[cfg(all(
9899
feature = "hrtim",

src/rng.rs

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
//! True Random Number Generator (TRNG)
2+
3+
use rand_core::TryRngCore;
4+
5+
use crate::{rcc::Rcc, stm32::RNG};
6+
use core::{fmt::Formatter, marker::PhantomData};
7+
8+
/// Error type for the RNG peripheral
9+
pub enum RngError {
10+
/// The DRDY bit is not set in the status register.
11+
NotReady,
12+
/// A noise source seed error was detected. [`seed_error_recovery()`]
13+
/// should be called to recover from the error.
14+
SeedError,
15+
/// A clock error was detected. fRNGCLOCK is less than fHCLK/32
16+
/// The clock error condition is automatically cleared when the clock condition returns to normal.
17+
ClockError,
18+
}
19+
20+
impl core::fmt::Debug for RngError {
21+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
22+
match self {
23+
RngError::NotReady => write!(f, "RNG Not ready"),
24+
RngError::SeedError => write!(f, "RNG Seed error"),
25+
RngError::ClockError => write!(f, "RNG Clock error"),
26+
}
27+
}
28+
}
29+
30+
impl core::fmt::Display for RngError {
31+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
32+
core::fmt::Debug::fmt(self, f)
33+
}
34+
}
35+
36+
#[cfg(feature = "defmt")]
37+
impl defmt::Format for RngError {
38+
fn format(&self, fmt: defmt::Formatter) {
39+
match self {
40+
RngError::NotReady => defmt::write!(fmt, "RNG Not ready"),
41+
RngError::SeedError => defmt::write!(fmt, "RNG Seed error"),
42+
RngError::ClockError => defmt::write!(fmt, "RNG Clock error"),
43+
}
44+
}
45+
}
46+
47+
/// Extension trait for the RNG register block
48+
pub trait RngExt {
49+
fn constrain(self, rcc: &mut Rcc) -> Rng<Stopped>;
50+
}
51+
52+
impl RngExt for RNG {
53+
/// Constrain the RNG register block and return an Rng<Stopped>.
54+
/// Enables the RNG peripheral in the AHB2ENR register.
55+
/// Enables the HSI48 clock used by RNG
56+
fn constrain(self, rcc: &mut Rcc) -> Rng<Stopped> {
57+
// Enable RNG in AHB2ENR
58+
rcc.ahb2enr().modify(|_, w| w.rngen().set_bit());
59+
60+
// Enable HSI48 clock used by the RNG
61+
rcc.enable_hsi48();
62+
63+
Rng {
64+
_state: PhantomData,
65+
}
66+
}
67+
}
68+
69+
/// Type states for the RNG peripheral
70+
pub struct Stopped;
71+
pub struct Running;
72+
73+
/// True Random Number Generator (TRNG)
74+
pub struct Rng<State> {
75+
_state: core::marker::PhantomData<State>,
76+
}
77+
78+
impl Rng<Stopped> {
79+
/// Start the RNG peripheral
80+
///
81+
/// Enables clock error detection and starts random number generation.
82+
///
83+
/// Retrurns an [`Rng`] in the `Running` state
84+
pub fn start(self) -> Rng<Running> {
85+
unsafe {
86+
(*RNG::ptr())
87+
.cr()
88+
.modify(|_, w| w.rngen().set_bit().ced().clear_bit())
89+
};
90+
91+
Rng {
92+
_state: PhantomData,
93+
}
94+
}
95+
}
96+
97+
impl Rng<Running> {
98+
/// Stop the RNG peripheral
99+
///
100+
/// Returns an [`Rng`] in the `Stopped` state
101+
pub fn stop(self) -> Rng<Stopped> {
102+
unsafe { (*RNG::ptr()).cr().modify(|_, w| w.rngen().clear_bit()) };
103+
104+
Rng {
105+
_state: PhantomData,
106+
}
107+
}
108+
109+
/// Check if the RNG is ready
110+
#[inline(always)]
111+
pub fn is_ready(&self) -> bool {
112+
unsafe { (*RNG::ptr()).sr().read().drdy().bit_is_set() }
113+
}
114+
115+
/// Check if the seed error flag is set
116+
#[inline(always)]
117+
pub fn is_seed_error(&self) -> bool {
118+
unsafe { (*RNG::ptr()).sr().read().seis().bit_is_set() }
119+
}
120+
121+
/// Check if the clock error flag is set
122+
#[inline(always)]
123+
pub fn is_clock_error(&self) -> bool {
124+
unsafe { (*RNG::ptr()).sr().read().ceis().bit_is_set() }
125+
}
126+
127+
/// Perform recovery sequence of a seed error from RM0440 26.3.7
128+
///
129+
/// The SEIS bit is cleared, and 12 words and read and discarded from the DR register.
130+
///
131+
/// If the recovery sequence was successful, the function returns `Ok(())`.
132+
/// If the SEIS bit is still set after the recovery sequence, [`RngError::SeedError`] is returned.
133+
pub fn seed_error_recovery(&mut self) -> Result<(), RngError> {
134+
// Clear SEIS bit
135+
unsafe { (*RNG::ptr()).sr().clear_bits(|w| w.seis().clear_bit()) };
136+
// Read and discard 12 words from DR register
137+
for _ in 0..12 {
138+
unsafe { (*RNG::ptr()).dr().read() };
139+
}
140+
// Confirm SEIS is still clear
141+
if unsafe { (*RNG::ptr()).sr().read().seis().bit_is_clear() } {
142+
Ok(())
143+
} else {
144+
Err(RngError::SeedError)
145+
}
146+
}
147+
148+
/// Blocking read of a random u32 from the RNG in polling mode.
149+
///
150+
/// Returns an [`RngError`] if the RNG reports an error condition.
151+
/// Polls the data ready flag until a random word is ready to be read.
152+
///
153+
/// For non-blocking operation use [`read_non_blocking()`]
154+
pub fn read_blocking(&self) -> Result<u32, RngError> {
155+
loop {
156+
match self.read_non_blocking() {
157+
Ok(value) => return Ok(value),
158+
Err(RngError::NotReady) => continue,
159+
Err(e) => return Err(e),
160+
}
161+
}
162+
}
163+
164+
/// Non blocking read of a random u32 from the RNG in polling mode.
165+
///
166+
/// Returns an [`RngError`] if the RNG is not ready or reports an error condition.
167+
///
168+
/// Once the RNG is ready, 4 consecutive 32 bit reads can be performed without blocking,
169+
/// at which point the internal FIFO will be refilled after 216 periods of the AHB clock
170+
/// (RM0440 26.7.3)
171+
///
172+
/// While the RNG is filling the FIFO, the function will return [`RngError::NotReady`].
173+
///
174+
/// For blocking reads use [`read_blocking()`]
175+
pub fn read_non_blocking(&self) -> Result<u32, RngError> {
176+
// Read the SR register to check if there is an error condition,
177+
// and if the DRDY bit is set to indicate a valid random number is available.
178+
let status = unsafe { (*RNG::ptr()).sr().read() };
179+
180+
// Check if the seed or clock error bits are set
181+
if status.seis().bit_is_set() {
182+
return Err(RngError::SeedError);
183+
}
184+
185+
if status.ceis().bit_is_set() {
186+
return Err(RngError::ClockError);
187+
}
188+
189+
if status.drdy().bit_is_set() {
190+
// The data ready bit is set. Read the DR register and check if it is zero.
191+
// A zero read indicates a seed error between reading SR and DR registers
192+
// see RM0440 26.7.3 RNDATA description.
193+
match unsafe { (*RNG::ptr()).dr().read().bits() } {
194+
0 => Err(RngError::SeedError),
195+
data => Ok(data),
196+
}
197+
} else {
198+
Err(RngError::NotReady)
199+
}
200+
}
201+
}
202+
203+
/// Implement [`rand::TryRngCore`] for the RNG peripheral.
204+
///
205+
/// Since this is a fallible trait, to use this as a [`rand::RngCore`] with [`rand::Rng`],
206+
/// the [`unwrap_err`] method can be used for compatibility with [`rand::Rng`] but will panic on error.
207+
///
208+
/// See https://docs.rs/rand/latest/rand/trait.TryRngCore.html
209+
impl TryRngCore for &Rng<Running> {
210+
type Error = RngError;
211+
212+
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
213+
self.read_blocking()
214+
}
215+
216+
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
217+
let mut result = 0u64;
218+
for _ in 0..2 {
219+
result |= self.try_next_u32()? as u64;
220+
result <<= 32;
221+
}
222+
Ok(result)
223+
}
224+
225+
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
226+
for chunk in dst.chunks_mut(size_of::<u32>()) {
227+
let value = self.try_next_u32()?;
228+
chunk.copy_from_slice(&value.to_ne_bytes());
229+
}
230+
Ok(())
231+
}
232+
}

0 commit comments

Comments
 (0)