diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 185cda43..e406c081 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -142,6 +142,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 1e3c054a..153795e5 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -152,6 +152,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 3bb44171..a7a7cf58 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -154,6 +154,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index b28778f3..14a1b8cc 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -155,6 +155,8 @@ impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 00dc9fd8..83ecd0de 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -175,6 +175,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 345608c9..5e5db04d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -201,6 +201,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 630f01aa..f6d33f85 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -212,6 +212,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index d7ba6c16..1e59528c 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -127,6 +127,9 @@ embassy_hal_common::peripherals! { // SAADC SAADC, + // RNG + RNG, + // PWM PWM0, PWM1, @@ -252,6 +255,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index feefa248..ee29ce20 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -61,7 +61,7 @@ pub mod pwm; pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; -#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] +#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))] pub mod rng; #[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index b0b3a8eb..a5602248 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,83 +1,48 @@ //! Random Number Generator (RNG) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; -use crate::peripherals::RNG; -use crate::{interrupt, pac, Peripheral}; +use crate::{interrupt, Peripheral}; -impl RNG { - fn regs() -> &'static pac::rng::RegisterBlock { - unsafe { &*pac::RNG::ptr() } - } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -static STATE: State = State { - ptr: AtomicPtr::new(ptr::null_mut()), - end: AtomicPtr::new(ptr::null_mut()), - waker: AtomicWaker::new(), -}; +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let s = T::state(); + let r = T::regs(); -struct State { - ptr: AtomicPtr, - end: AtomicPtr, - waker: AtomicWaker, -} - -/// A wrapper around an nRF RNG peripheral. -/// -/// It has a non-blocking API, and a blocking api through `rand`. -pub struct Rng<'d> { - irq: PeripheralRef<'d, interrupt::RNG>, -} - -impl<'d> Rng<'d> { - /// Creates a new RNG driver from the `RNG` peripheral and interrupt. - /// - /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, - /// e.g. using `mem::forget`. - /// - /// The synchronous API is safe. - pub fn new(_rng: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - - let this = Self { irq }; - - this.stop(); - this.disable_irq(); - - this.irq.set_handler(Self::on_interrupt); - this.irq.unpend(); - this.irq.enable(); - - this - } - - fn on_interrupt(_: *mut ()) { // Clear the event. - RNG::regs().events_valrdy.reset(); + r.events_valrdy.reset(); // Mutate the slice within a critical section, // so that the future isn't dropped in between us loading the pointer and actually dereferencing it. let (ptr, end) = critical_section::with(|_| { - let ptr = STATE.ptr.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); // We need to make sure we haven't already filled the whole slice, // in case the interrupt fired again before the executor got back to the future. - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); if !ptr.is_null() && ptr != end { // If the future was dropped, the pointer would have been set to null, // so we're still good to mutate the slice. // The safety contract of `Rng::new` means that the future can't have been dropped // without calling its destructor. unsafe { - *ptr = RNG::regs().value.read().value().bits(); + *ptr = r.value.read().value().bits(); } } (ptr, end) @@ -90,15 +55,15 @@ impl<'d> Rng<'d> { } let new_ptr = unsafe { ptr.add(1) }; - match STATE + match s .ptr .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed) { Ok(_) => { - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); // It doesn't matter if `end` was changed under our feet, because then this will just be false. if new_ptr == end { - STATE.waker.wake(); + s.waker.wake(); } } Err(_) => { @@ -107,21 +72,53 @@ impl<'d> Rng<'d> { } } } +} + +/// A wrapper around an nRF RNG peripheral. +/// +/// It has a non-blocking API, and a blocking api through `rand`. +pub struct Rng<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Rng<'d, T> { + /// Creates a new RNG driver from the `RNG` peripheral and interrupt. + /// + /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, + /// e.g. using `mem::forget`. + /// + /// The synchronous API is safe. + pub fn new( + rng: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + ) -> Self { + into_ref!(rng); + + let this = Self { _peri: rng }; + + this.stop(); + this.disable_irq(); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + this + } fn stop(&self) { - RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) } fn start(&self) { - RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_start.write(|w| unsafe { w.bits(1) }) } fn enable_irq(&self) { - RNG::regs().intenset.write(|w| w.valrdy().set()); + T::regs().intenset.write(|w| w.valrdy().set()); } fn disable_irq(&self) { - RNG::regs().intenclr.write(|w| w.valrdy().clear()); + T::regs().intenclr.write(|w| w.valrdy().clear()); } /// Enable or disable the RNG's bias correction. @@ -131,7 +128,7 @@ impl<'d> Rng<'d> { /// /// Defaults to disabled. pub fn set_bias_correction(&self, enable: bool) { - RNG::regs().config.write(|w| w.dercen().bit(enable)) + T::regs().config.write(|w| w.dercen().bit(enable)) } /// Fill the buffer with random bytes. @@ -140,11 +137,13 @@ impl<'d> Rng<'d> { return; // Nothing to fill } + let s = T::state(); + let range = dest.as_mut_ptr_range(); // Even if we've preempted the interrupt, it can't preempt us again, // so we don't need to worry about the order we write these in. - STATE.ptr.store(range.start, Ordering::Relaxed); - STATE.end.store(range.end, Ordering::Relaxed); + s.ptr.store(range.start, Ordering::Relaxed); + s.end.store(range.end, Ordering::Relaxed); self.enable_irq(); self.start(); @@ -154,16 +153,16 @@ impl<'d> Rng<'d> { self.disable_irq(); // The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here. - STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed); - STATE.end.store(ptr::null_mut(), Ordering::Relaxed); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); }); poll_fn(|cx| { - STATE.waker.register(cx.waker()); + s.waker.register(cx.waker()); // The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`. - let end = STATE.end.load(Ordering::Relaxed); - let ptr = STATE.ptr.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); if ptr == end { // We're done. @@ -183,7 +182,7 @@ impl<'d> Rng<'d> { self.start(); for byte in dest.iter_mut() { - let regs = RNG::regs(); + let regs = T::regs(); while regs.events_valrdy.read().bits() == 0 {} regs.events_valrdy.reset(); *byte = regs.value.read().value().bits(); @@ -193,13 +192,16 @@ impl<'d> Rng<'d> { } } -impl<'d> Drop for Rng<'d> { +impl<'d, T: Instance> Drop for Rng<'d, T> { fn drop(&mut self) { - self.irq.disable() + self.stop(); + let s = T::state(); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); } } -impl<'d> rand_core::RngCore for Rng<'d> { +impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> { fn fill_bytes(&mut self, dest: &mut [u8]) { self.blocking_fill_bytes(dest); } @@ -223,4 +225,53 @@ impl<'d> rand_core::RngCore for Rng<'d> { } } -impl<'d> rand_core::CryptoRng for Rng<'d> {} +impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {} + +pub(crate) mod sealed { + use super::*; + + /// Peripheral static state + pub struct State { + pub ptr: AtomicPtr, + pub end: AtomicPtr, + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + ptr: AtomicPtr::new(ptr::null_mut()), + end: AtomicPtr::new(ptr::null_mut()), + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::rng::RegisterBlock; + fn state() -> &'static State; + } +} + +/// RNG peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_rng { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::rng::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::rng::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::rng::sealed::State { + static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new(); + &STATE + } + } + impl crate::rng::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs index 64707394..855743f5 100644 --- a/examples/nrf52840/src/bin/rng.rs +++ b/examples/nrf52840/src/bin/rng.rs @@ -3,15 +3,19 @@ #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::rng::Rng; +use embassy_nrf::{bind_interrupts, peripherals, rng}; use rand::Rng as _; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); // Async API let mut bytes = [0; 4]; diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 97978089..083a1cbb 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -10,7 +10,7 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -18,6 +18,10 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; macro_rules! singleton { @@ -108,7 +112,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.blocking_fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed);