nrf/rng: make available on all chips, use Instance trait, switch to new interrupt binding.

This commit is contained in:
Dario Nieuwenhuis 2023-03-05 21:50:15 +01:00
parent 96788ac93a
commit d113fcfe32
12 changed files with 154 additions and 76 deletions

View file

@ -142,6 +142,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
impl_qdec!(QDEC, QDEC, QDEC); impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View file

@ -152,6 +152,8 @@ impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC); impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View file

@ -154,6 +154,8 @@ impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC); impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View file

@ -155,6 +155,8 @@ impl_timer!(TIMER3, TIMER3, TIMER3, extended);
impl_qdec!(QDEC, QDEC, QDEC); impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0); impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View file

@ -175,6 +175,8 @@ impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC); impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View file

@ -201,6 +201,8 @@ impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC); impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);

View file

@ -212,6 +212,8 @@ impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC); impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0); impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View file

@ -127,6 +127,9 @@ embassy_hal_common::peripherals! {
// SAADC // SAADC
SAADC, SAADC,
// RNG
RNG,
// PWM // PWM
PWM0, PWM0,
PWM1, PWM1,
@ -252,6 +255,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);
impl_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0); impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View file

@ -61,7 +61,7 @@ pub mod pwm;
pub mod qdec; pub mod qdec;
#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))]
pub mod qspi; pub mod qspi;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))]
pub mod rng; pub mod rng;
#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] #[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod saadc; pub mod saadc;

View file

@ -1,83 +1,48 @@
//! Random Number Generator (RNG) driver. //! Random Number Generator (RNG) driver.
#![macro_use]
use core::future::poll_fn; use core::future::poll_fn;
use core::marker::PhantomData;
use core::ptr; use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering}; use core::sync::atomic::{AtomicPtr, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::drop::OnDrop; use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt::InterruptExt; use crate::interrupt::InterruptExt;
use crate::peripherals::RNG; use crate::{interrupt, Peripheral};
use crate::{interrupt, pac, Peripheral};
impl RNG { /// Interrupt handler.
fn regs() -> &'static pac::rng::RegisterBlock { pub struct InterruptHandler<T: Instance> {
unsafe { &*pac::RNG::ptr() } _phantom: PhantomData<T>,
}
} }
static STATE: State = State { impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> {
ptr: AtomicPtr::new(ptr::null_mut()), unsafe fn on_interrupt() {
end: AtomicPtr::new(ptr::null_mut()), let s = T::state();
waker: AtomicWaker::new(), let r = T::regs();
};
struct State {
ptr: AtomicPtr<u8>,
end: AtomicPtr<u8>,
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<P = RNG> + 'd, irq: impl Peripheral<P = interrupt::RNG> + '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. // Clear the event.
RNG::regs().events_valrdy.reset(); r.events_valrdy.reset();
// Mutate the slice within a critical section, // Mutate the slice within a critical section,
// so that the future isn't dropped in between us loading the pointer and actually dereferencing it. // 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, 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, // 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. // 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 !ptr.is_null() && ptr != end {
// If the future was dropped, the pointer would have been set to null, // If the future was dropped, the pointer would have been set to null,
// so we're still good to mutate the slice. // so we're still good to mutate the slice.
// The safety contract of `Rng::new` means that the future can't have been dropped // The safety contract of `Rng::new` means that the future can't have been dropped
// without calling its destructor. // without calling its destructor.
unsafe { unsafe {
*ptr = RNG::regs().value.read().value().bits(); *ptr = r.value.read().value().bits();
} }
} }
(ptr, end) (ptr, end)
@ -90,15 +55,15 @@ impl<'d> Rng<'d> {
} }
let new_ptr = unsafe { ptr.add(1) }; let new_ptr = unsafe { ptr.add(1) };
match STATE match s
.ptr .ptr
.compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed) .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed)
{ {
Ok(_) => { 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. // It doesn't matter if `end` was changed under our feet, because then this will just be false.
if new_ptr == end { if new_ptr == end {
STATE.waker.wake(); s.waker.wake();
} }
} }
Err(_) => { 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<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + '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) { 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) { 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) { fn enable_irq(&self) {
RNG::regs().intenset.write(|w| w.valrdy().set()); T::regs().intenset.write(|w| w.valrdy().set());
} }
fn disable_irq(&self) { 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. /// Enable or disable the RNG's bias correction.
@ -131,7 +128,7 @@ impl<'d> Rng<'d> {
/// ///
/// Defaults to disabled. /// Defaults to disabled.
pub fn set_bias_correction(&self, enable: bool) { 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. /// Fill the buffer with random bytes.
@ -140,11 +137,13 @@ impl<'d> Rng<'d> {
return; // Nothing to fill return; // Nothing to fill
} }
let s = T::state();
let range = dest.as_mut_ptr_range(); let range = dest.as_mut_ptr_range();
// Even if we've preempted the interrupt, it can't preempt us again, // 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. // so we don't need to worry about the order we write these in.
STATE.ptr.store(range.start, Ordering::Relaxed); s.ptr.store(range.start, Ordering::Relaxed);
STATE.end.store(range.end, Ordering::Relaxed); s.end.store(range.end, Ordering::Relaxed);
self.enable_irq(); self.enable_irq();
self.start(); self.start();
@ -154,16 +153,16 @@ impl<'d> Rng<'d> {
self.disable_irq(); self.disable_irq();
// The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here. // 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); s.ptr.store(ptr::null_mut(), Ordering::Relaxed);
STATE.end.store(ptr::null_mut(), Ordering::Relaxed); s.end.store(ptr::null_mut(), Ordering::Relaxed);
}); });
poll_fn(|cx| { 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`. // 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 end = s.end.load(Ordering::Relaxed);
let ptr = STATE.ptr.load(Ordering::Relaxed); let ptr = s.ptr.load(Ordering::Relaxed);
if ptr == end { if ptr == end {
// We're done. // We're done.
@ -183,7 +182,7 @@ impl<'d> Rng<'d> {
self.start(); self.start();
for byte in dest.iter_mut() { for byte in dest.iter_mut() {
let regs = RNG::regs(); let regs = T::regs();
while regs.events_valrdy.read().bits() == 0 {} while regs.events_valrdy.read().bits() == 0 {}
regs.events_valrdy.reset(); regs.events_valrdy.reset();
*byte = regs.value.read().value().bits(); *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) { 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]) { fn fill_bytes(&mut self, dest: &mut [u8]) {
self.blocking_fill_bytes(dest); 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<u8>,
pub end: AtomicPtr<u8>,
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<P = Self> + 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;
}
};
}

View file

@ -3,15 +3,19 @@
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_nrf::interrupt;
use embassy_nrf::rng::Rng; use embassy_nrf::rng::Rng;
use embassy_nrf::{bind_interrupts, peripherals, rng};
use rand::Rng as _; use rand::Rng as _;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
RNG => rng::InterruptHandler<peripherals::RNG>;
});
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default()); 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 // Async API
let mut bytes = [0; 4]; let mut bytes = [0; 4];

View file

@ -10,7 +10,7 @@ use embassy_net::tcp::TcpSocket;
use embassy_net::{Stack, StackResources}; use embassy_net::{Stack, StackResources};
use embassy_nrf::rng::Rng; use embassy_nrf::rng::Rng;
use embassy_nrf::usb::{Driver, HardwareVbusDetect}; 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::embassy_net::{Device, Runner, State as NetState};
use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State};
use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb::{Builder, Config, UsbDevice};
@ -18,6 +18,10 @@ use embedded_io::asynch::Write;
use static_cell::StaticCell; use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
RNG => rng::InterruptHandler<peripherals::RNG>;
});
type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>;
macro_rules! singleton { macro_rules! singleton {
@ -108,7 +112,7 @@ async fn main(spawner: Spawner) {
//}); //});
// Generate random seed // 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]; let mut seed = [0; 8];
rng.blocking_fill_bytes(&mut seed); rng.blocking_fill_bytes(&mut seed);
let seed = u64::from_le_bytes(seed); let seed = u64::from_le_bytes(seed);