Skip to content

Commit 14a0889

Browse files
boondocklabsusbalbin
authored andcommitted
Add support for true random number generator (RNG) peripheral
- Adds `RngExt` trait to constrain and enable RNG peripheral with HSI48 clock - Implements state machine with `Rng<Stopped>` and `Rng<Running>` types - Provides blocking and non-blocking read methods for 32-bit random values - Implements `TryRngCore` from `rand` for interoperability with rand traits - Includes error handling for seed and clock error conditions
1 parent 62272cb commit 14a0889

File tree

4 files changed

+260
-0
lines changed

4 files changed

+260
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 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 = { version = "0.9", default-features = false }
2324

2425
[dependencies.cortex-m]
2526
version = "0.7.7"

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: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//! True Random Number Generator (TRNG)
2+
3+
use rand::TryRngCore;
4+
5+
use crate::{rcc::Rcc, stm32::RNG};
6+
use core::{fmt::Formatter, marker::PhantomData};
7+
8+
pub enum RngError {
9+
NotReady,
10+
SeedError,
11+
ClockError,
12+
}
13+
14+
impl core::fmt::Debug for RngError {
15+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
16+
match self {
17+
RngError::NotReady => write!(f, "RNG Not ready"),
18+
RngError::SeedError => write!(f, "RNG Seed error"),
19+
RngError::ClockError => write!(f, "RNG Clock error"),
20+
}
21+
}
22+
}
23+
24+
impl core::fmt::Display for RngError {
25+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
26+
core::fmt::Debug::fmt(self, f)
27+
}
28+
}
29+
30+
/// Extension trait for the RNG register block
31+
pub trait RngExt {
32+
fn constrain(self, rcc: &mut Rcc) -> Rng<Stopped>;
33+
}
34+
35+
impl RngExt for RNG {
36+
/// Constrain the RNG register block and return an Rng<Stopped>.
37+
/// Enables the RNG peripheral in the AHB2ENR register.
38+
/// Enables the HSI48 clock used by RNG
39+
fn constrain(self, rcc: &mut Rcc) -> Rng<Stopped> {
40+
// Enable RNG in AHB2ENR
41+
rcc.ahb2enr().modify(|_, w| w.rngen().set_bit());
42+
43+
// Enable HSI48 clock used by the RNG
44+
rcc.enable_hsi48();
45+
46+
Rng {
47+
_state: PhantomData,
48+
}
49+
}
50+
}
51+
52+
/// Type states for the RNG peripheral
53+
pub struct Stopped;
54+
pub struct Running;
55+
56+
/// True Random Number Generator (TRNG)
57+
pub struct Rng<State> {
58+
_state: core::marker::PhantomData<State>,
59+
}
60+
61+
impl Rng<Stopped> {
62+
/// Start the RNG peripheral
63+
///
64+
/// Enables clock error detection and starts random number generation.
65+
///
66+
/// Retrurns an [`Rng`] in the `Running` state
67+
pub fn start(self) -> Rng<Running> {
68+
unsafe {
69+
(*RNG::ptr())
70+
.cr()
71+
.modify(|_, w| w.rngen().set_bit().ced().clear_bit())
72+
};
73+
74+
Rng {
75+
_state: PhantomData,
76+
}
77+
}
78+
}
79+
80+
impl Rng<Running> {
81+
/// Stop the RNG peripheral
82+
///
83+
/// Returns an [`Rng`] in the `Stopped` state
84+
pub fn stop(self) -> Rng<Stopped> {
85+
unsafe { (*RNG::ptr()).cr().modify(|_, w| w.rngen().clear_bit()) };
86+
87+
Rng {
88+
_state: PhantomData,
89+
}
90+
}
91+
92+
/// Check if the RNG is ready
93+
#[inline(always)]
94+
pub fn is_ready(&self) -> bool {
95+
unsafe { (*RNG::ptr()).sr().read().drdy().bit_is_set() }
96+
}
97+
98+
/// Check if the seed error flag is set
99+
pub fn is_seed_error(&self) -> bool {
100+
unsafe { (*RNG::ptr()).sr().read().seis().bit_is_set() }
101+
}
102+
103+
/// Check if the clock error flag is set
104+
pub fn is_clock_error(&self) -> bool {
105+
unsafe { (*RNG::ptr()).sr().read().ceis().bit_is_set() }
106+
}
107+
108+
/// Blocking read of a random u32 from the RNG in polling mode.
109+
///
110+
/// Returns an [`RngError`] if the RNG reports an error condition.
111+
/// Polls the data ready flag until a random word is ready to be read.
112+
///
113+
/// For non-blocking operation use [`read_non_blocking()`]
114+
pub fn read_blocking(&self) -> Result<u32, RngError> {
115+
loop {
116+
match self.read_non_blocking() {
117+
Ok(value) => return Ok(value),
118+
Err(RngError::NotReady) => continue,
119+
Err(e) => return Err(e),
120+
}
121+
}
122+
}
123+
124+
/// Non blocking read of a random u32 from the RNG in polling mode.
125+
///
126+
/// Returns an [`RngError`] if the RNG is not ready or reports an error condition.
127+
///
128+
/// For blocking reads use [`read_blocking()`]
129+
pub fn read_non_blocking(&self) -> Result<u32, RngError> {
130+
// Read the SR register to check if there is an error condition,
131+
// and if the DRDY bit is set to indicate a valid random number is available.
132+
let status = unsafe { (*RNG::ptr()).sr().read() };
133+
134+
// Check if the seed or clock error bits are set
135+
if status.seis().bit_is_set() {
136+
return Err(RngError::SeedError);
137+
}
138+
139+
if status.ceis().bit_is_set() {
140+
return Err(RngError::ClockError);
141+
}
142+
143+
if status.drdy().bit_is_set() {
144+
// Data is ready. Read the DR register and return the value.
145+
Ok(unsafe { (*RNG::ptr()).dr().read().bits() })
146+
} else {
147+
Err(RngError::NotReady)
148+
}
149+
}
150+
}
151+
152+
/// Implement [`rand::TryRngCore`] for the RNG peripheral.
153+
///
154+
/// Since this is a fallible trait, to use this as a [`rand::RngCore`] with [`rand::Rng`],
155+
/// the [`unwrap_err`] method can be used for compatibility with [`rand::Rng`] but will panic on error.
156+
///
157+
/// See https://docs.rs/rand/latest/rand/trait.TryRngCore.html
158+
impl TryRngCore for &Rng<Running> {
159+
type Error = RngError;
160+
161+
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
162+
self.read_blocking()
163+
}
164+
165+
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
166+
let mut result = 0u64;
167+
for _ in 0..2 {
168+
result |= self.try_next_u32()? as u64;
169+
result <<= 32;
170+
}
171+
Ok(result)
172+
}
173+
174+
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
175+
for chunk in dst.chunks_mut(size_of::<u32>()) {
176+
let value = self.try_next_u32()?;
177+
chunk.copy_from_slice(&value.to_ne_bytes());
178+
}
179+
Ok(())
180+
}
181+
}

0 commit comments

Comments
 (0)