embassy_stm32/
rng.rs

1//! Random Number Generator (RNG)
2#![macro_use]
3
4use core::future::poll_fn;
5use core::marker::PhantomData;
6use core::task::Poll;
7
8use embassy_hal_internal::{into_ref, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker;
10use rand_core::{CryptoRng, RngCore};
11
12use crate::interrupt::typelevel::Interrupt;
13use crate::{interrupt, pac, peripherals, rcc, Peripheral};
14
15static RNG_WAKER: AtomicWaker = AtomicWaker::new();
16
17/// RNG error
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub enum Error {
21    /// Seed error.
22    SeedError,
23    /// Clock error. Double-check the RCC configuration,
24    /// see the Reference Manual for details on restrictions
25    /// on RNG clocks.
26    ClockError,
27}
28
29/// RNG interrupt handler.
30pub struct InterruptHandler<T: Instance> {
31    _phantom: PhantomData<T>,
32}
33
34impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
35    unsafe fn on_interrupt() {
36        let bits = T::regs().sr().read();
37        if bits.drdy() || bits.seis() || bits.ceis() {
38            T::regs().cr().modify(|reg| reg.set_ie(false));
39            RNG_WAKER.wake();
40        }
41    }
42}
43
44/// RNG driver.
45pub struct Rng<'d, T: Instance> {
46    _inner: PeripheralRef<'d, T>,
47}
48
49impl<'d, T: Instance> Rng<'d, T> {
50    /// Create a new RNG driver.
51    pub fn new(
52        inner: impl Peripheral<P = T> + 'd,
53        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
54    ) -> Self {
55        rcc::enable_and_reset::<T>();
56        into_ref!(inner);
57        let mut random = Self { _inner: inner };
58        random.reset();
59
60        T::Interrupt::unpend();
61        unsafe { T::Interrupt::enable() };
62
63        random
64    }
65
66    /// Reset the RNG.
67    #[cfg(rng_v1)]
68    pub fn reset(&mut self) {
69        T::regs().cr().write(|reg| {
70            reg.set_rngen(false);
71        });
72        T::regs().sr().modify(|reg| {
73            reg.set_seis(false);
74            reg.set_ceis(false);
75        });
76        T::regs().cr().modify(|reg| {
77            reg.set_rngen(true);
78        });
79        // Reference manual says to discard the first.
80        let _ = self.next_u32();
81    }
82
83    /// Reset the RNG.
84    #[cfg(not(rng_v1))]
85    pub fn reset(&mut self) {
86        T::regs().cr().write(|reg| {
87            reg.set_condrst(true);
88            reg.set_nistc(pac::rng::vals::Nistc::CUSTOM);
89            // set RNG config "A" according to reference manual
90            // this has to be written within the same write access as setting the CONDRST bit
91            reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIG_A);
92            reg.set_clkdiv(pac::rng::vals::Clkdiv::NO_DIV);
93            reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIG_A_B);
94            reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIG_A);
95            reg.set_ced(true);
96            reg.set_ie(false);
97            reg.set_rngen(true);
98        });
99        T::regs().cr().modify(|reg| {
100            reg.set_ced(false);
101        });
102        // wait for CONDRST to be set
103        while !T::regs().cr().read().condrst() {}
104        // magic number must be written immediately before every read or write access to HTCR
105        T::regs().htcr().write(|w| w.set_htcfg(pac::rng::vals::Htcfg::MAGIC));
106        // write recommended value according to reference manual
107        // note: HTCR can only be written during conditioning
108        T::regs()
109            .htcr()
110            .write(|w| w.set_htcfg(pac::rng::vals::Htcfg::RECOMMENDED));
111        // finish conditioning
112        T::regs().cr().modify(|reg| {
113            reg.set_rngen(true);
114            reg.set_condrst(false);
115        });
116        // wait for CONDRST to be reset
117        while T::regs().cr().read().condrst() {}
118    }
119
120    /// Try to recover from a seed error.
121    pub fn recover_seed_error(&mut self) {
122        self.reset();
123        // reset should also clear the SEIS flag
124        if T::regs().sr().read().seis() {
125            warn!("recovering from seed error failed");
126            return;
127        }
128        // wait for SECS to be cleared by RNG
129        while T::regs().sr().read().secs() {}
130    }
131
132    /// Fill the given slice with random values.
133    pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
134        for chunk in dest.chunks_mut(4) {
135            let mut bits = T::regs().sr().read();
136            if !bits.seis() && !bits.ceis() && !bits.drdy() {
137                // wait for interrupt
138                poll_fn(|cx| {
139                    // quick check to avoid registration if already done.
140                    let bits = T::regs().sr().read();
141                    if bits.drdy() || bits.seis() || bits.ceis() {
142                        return Poll::Ready(());
143                    }
144                    RNG_WAKER.register(cx.waker());
145                    T::regs().cr().modify(|reg| reg.set_ie(true));
146                    // Need to check condition **after** `register` to avoid a race
147                    // condition that would result in lost notifications.
148                    let bits = T::regs().sr().read();
149                    if bits.drdy() || bits.seis() || bits.ceis() {
150                        Poll::Ready(())
151                    } else {
152                        Poll::Pending
153                    }
154                })
155                .await;
156
157                // Re-read the status register after wait.
158                bits = T::regs().sr().read()
159            }
160            if bits.seis() {
161                // in case of noise-source or seed error we try to recover here
162                // but we must not use the data in DR and we return an error
163                // to leave retry-logic to the application
164                self.recover_seed_error();
165                return Err(Error::SeedError);
166            } else if bits.ceis() {
167                // clock error detected, DR could still be used but keep it safe,
168                // clear the error and abort
169                T::regs().sr().modify(|sr| sr.set_ceis(false));
170                return Err(Error::ClockError);
171            } else if bits.drdy() {
172                // DR can be read up to four times until the output buffer is empty
173                // DRDY is cleared automatically when that happens
174                let random_word = T::regs().dr().read();
175                // reference manual: always check if DR is zero
176                if random_word == 0 {
177                    return Err(Error::SeedError);
178                }
179                // write bytes to chunk
180                for (dest, src) in chunk.iter_mut().zip(random_word.to_ne_bytes().iter()) {
181                    *dest = *src
182                }
183            }
184        }
185
186        Ok(())
187    }
188}
189
190impl<'d, T: Instance> RngCore for Rng<'d, T> {
191    fn next_u32(&mut self) -> u32 {
192        loop {
193            let sr = T::regs().sr().read();
194            if sr.seis() | sr.ceis() {
195                self.reset();
196            } else if sr.drdy() {
197                return T::regs().dr().read();
198            }
199        }
200    }
201
202    fn next_u64(&mut self) -> u64 {
203        let mut rand = self.next_u32() as u64;
204        rand |= (self.next_u32() as u64) << 32;
205        rand
206    }
207
208    fn fill_bytes(&mut self, dest: &mut [u8]) {
209        for chunk in dest.chunks_mut(4) {
210            let rand = self.next_u32();
211            for (slot, num) in chunk.iter_mut().zip(rand.to_ne_bytes().iter()) {
212                *slot = *num
213            }
214        }
215    }
216
217    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
218        self.fill_bytes(dest);
219        Ok(())
220    }
221}
222
223impl<'d, T: Instance> CryptoRng for Rng<'d, T> {}
224
225trait SealedInstance {
226    fn regs() -> pac::rng::Rng;
227}
228
229/// RNG instance trait.
230#[allow(private_bounds)]
231pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
232    /// Interrupt for this RNG instance.
233    type Interrupt: interrupt::typelevel::Interrupt;
234}
235
236foreach_interrupt!(
237    ($inst:ident, rng, RNG, GLOBAL, $irq:ident) => {
238        impl Instance for peripherals::$inst {
239            type Interrupt = crate::interrupt::typelevel::$irq;
240        }
241
242        impl SealedInstance for peripherals::$inst {
243            fn regs() -> crate::pac::rng::Rng {
244                crate::pac::$inst
245            }
246        }
247    };
248);