mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 16:23:10 +00:00
Add an nRF RNG driver
Resolves #187 Like the stm32 driver, this has both a non-blocking and blocking API, and implements `rand_core::RngCore` for the blocking API.
This commit is contained in:
parent
e6d6e82e54
commit
8a4ab29819
@ -33,6 +33,7 @@ embedded-hal = { version = "0.2.4" }
|
|||||||
embedded-dma = { version = "0.1.2" }
|
embedded-dma = { version = "0.1.2" }
|
||||||
futures = { version = "0.3.5", default-features = false }
|
futures = { version = "0.3.5", default-features = false }
|
||||||
critical-section = "0.2.1"
|
critical-section = "0.2.1"
|
||||||
|
rand_core = "0.6.3"
|
||||||
|
|
||||||
nrf52805-pac = { version = "0.1.0", optional = true, features = [ "rt" ]}
|
nrf52805-pac = { version = "0.1.0", optional = true, features = [ "rt" ]}
|
||||||
nrf52810-pac = { version = "0.9.0", optional = true, features = [ "rt" ]}
|
nrf52810-pac = { version = "0.9.0", optional = true, features = [ "rt" ]}
|
||||||
|
@ -8,6 +8,9 @@ embassy_extras::peripherals! {
|
|||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
|
// RNG
|
||||||
|
RNG,
|
||||||
|
|
||||||
// UARTE
|
// UARTE
|
||||||
UARTE0,
|
UARTE0,
|
||||||
|
|
||||||
|
@ -8,6 +8,9 @@ embassy_extras::peripherals! {
|
|||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
|
// RNG
|
||||||
|
RNG,
|
||||||
|
|
||||||
// UARTE
|
// UARTE
|
||||||
UARTE0,
|
UARTE0,
|
||||||
|
|
||||||
|
@ -8,6 +8,9 @@ embassy_extras::peripherals! {
|
|||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
|
// RNG
|
||||||
|
RNG,
|
||||||
|
|
||||||
// UARTE
|
// UARTE
|
||||||
UARTE0,
|
UARTE0,
|
||||||
|
|
||||||
|
@ -8,6 +8,9 @@ embassy_extras::peripherals! {
|
|||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
|
// RNG
|
||||||
|
RNG,
|
||||||
|
|
||||||
// UARTE
|
// UARTE
|
||||||
UARTE0,
|
UARTE0,
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ embassy_extras::peripherals! {
|
|||||||
RTC1,
|
RTC1,
|
||||||
RTC2,
|
RTC2,
|
||||||
|
|
||||||
|
// RNG
|
||||||
|
RNG,
|
||||||
|
|
||||||
// UARTE
|
// UARTE
|
||||||
UARTE0,
|
UARTE0,
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ embassy_extras::peripherals! {
|
|||||||
RTC1,
|
RTC1,
|
||||||
RTC2,
|
RTC2,
|
||||||
|
|
||||||
|
// RNG
|
||||||
|
RNG,
|
||||||
|
|
||||||
// UARTE
|
// UARTE
|
||||||
UARTE0,
|
UARTE0,
|
||||||
UARTE1,
|
UARTE1,
|
||||||
|
@ -9,6 +9,9 @@ embassy_extras::peripherals! {
|
|||||||
RTC1,
|
RTC1,
|
||||||
RTC2,
|
RTC2,
|
||||||
|
|
||||||
|
// RNG
|
||||||
|
RNG,
|
||||||
|
|
||||||
// QSPI
|
// QSPI
|
||||||
QSPI,
|
QSPI,
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ pub mod ppi;
|
|||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
#[cfg(feature = "nrf52840")]
|
#[cfg(feature = "nrf52840")]
|
||||||
pub mod qspi;
|
pub mod qspi;
|
||||||
|
pub mod rng;
|
||||||
pub mod rtc;
|
pub mod rtc;
|
||||||
#[cfg(not(feature = "nrf52820"))]
|
#[cfg(not(feature = "nrf52820"))]
|
||||||
pub mod saadc;
|
pub mod saadc;
|
||||||
|
154
embassy-nrf/src/rng.rs
Normal file
154
embassy-nrf/src/rng.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use embassy::interrupt::InterruptExt;
|
||||||
|
use embassy::traits;
|
||||||
|
use embassy::util::OnDrop;
|
||||||
|
use embassy::util::Signal;
|
||||||
|
use embassy::util::Unborrow;
|
||||||
|
use embassy_extras::unborrow;
|
||||||
|
use futures::Future;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use crate::interrupt;
|
||||||
|
use crate::pac;
|
||||||
|
use crate::peripherals::RNG;
|
||||||
|
|
||||||
|
impl RNG {
|
||||||
|
fn regs() -> &'static pac::rng::RegisterBlock {
|
||||||
|
unsafe { &*pac::RNG::ptr() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NEXT_BYTE: Signal<u8> = Signal::new();
|
||||||
|
|
||||||
|
/// A wrapper around an nRF RNG peripheral.
|
||||||
|
///
|
||||||
|
/// It has a non-blocking API, through `embassy::traits::Rng`, and a blocking api through `rand`.
|
||||||
|
pub struct Rng<'d> {
|
||||||
|
irq: interrupt::RNG,
|
||||||
|
phantom: PhantomData<&'d mut RNG>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Rng<'d> {
|
||||||
|
pub fn new(
|
||||||
|
_rng: impl Unborrow<Target = RNG> + 'd,
|
||||||
|
irq: impl Unborrow<Target = interrupt::RNG> + 'd,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(irq);
|
||||||
|
|
||||||
|
let this = Self {
|
||||||
|
irq,
|
||||||
|
phantom: PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stop();
|
||||||
|
this.disable_irq();
|
||||||
|
|
||||||
|
this.irq.set_handler(Self::on_interrupt);
|
||||||
|
this.irq.unpend();
|
||||||
|
this.irq.enable();
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt(_: *mut ()) {
|
||||||
|
NEXT_BYTE.signal(RNG::regs().value.read().value().bits());
|
||||||
|
RNG::regs().events_valrdy.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&self) {
|
||||||
|
RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(&self) {
|
||||||
|
RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_irq(&self) {
|
||||||
|
RNG::regs().intenset.write(|w| w.valrdy().set());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable_irq(&self) {
|
||||||
|
RNG::regs().intenclr.write(|w| w.valrdy().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable or disable the RNG's bias correction.
|
||||||
|
///
|
||||||
|
/// Bias correction removes any bias towards a '1' or a '0' in the bits generated.
|
||||||
|
/// However, this makes the generation of numbers slower.
|
||||||
|
///
|
||||||
|
/// Defaults to disabled.
|
||||||
|
pub fn bias_correction(&self, enable: bool) {
|
||||||
|
RNG::regs().config.write(|w| w.dercen().bit(enable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Drop for Rng<'d> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.irq.disable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> traits::rng::Rng for Rng<'d> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[rustfmt::skip] // For some reason rustfmt removes the where clause
|
||||||
|
type RngFuture<'a> where 'd: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||||
|
|
||||||
|
fn fill_bytes<'a>(&'a mut self, dest: &'a mut [u8]) -> Self::RngFuture<'a> {
|
||||||
|
self.enable_irq();
|
||||||
|
self.start();
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let on_drop = OnDrop::new(|| {
|
||||||
|
self.stop();
|
||||||
|
self.disable_irq();
|
||||||
|
});
|
||||||
|
|
||||||
|
for byte in dest.iter_mut() {
|
||||||
|
*byte = NEXT_BYTE.wait().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the teardown
|
||||||
|
drop(on_drop);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> RngCore for Rng<'d> {
|
||||||
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||||
|
self.start();
|
||||||
|
|
||||||
|
for byte in dest.iter_mut() {
|
||||||
|
let regs = RNG::regs();
|
||||||
|
while regs.events_valrdy.read().bits() == 0 {}
|
||||||
|
regs.events_valrdy.reset();
|
||||||
|
*byte = regs.value.read().value().bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_u32(&mut self) -> u32 {
|
||||||
|
let mut bytes = [0; 4];
|
||||||
|
self.fill_bytes(&mut bytes);
|
||||||
|
// We don't care about the endianness, so just use the native one.
|
||||||
|
u32::from_ne_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_u64(&mut self) -> u64 {
|
||||||
|
let mut bytes = [0; 8];
|
||||||
|
self.fill_bytes(&mut bytes);
|
||||||
|
u64::from_ne_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||||
|
self.fill_bytes(dest);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Should `Rng` implement `CryptoRng`? It's 'suitable for cryptographic purposes' according to the specification.
|
@ -29,3 +29,4 @@ cortex-m-rt = "0.6.13"
|
|||||||
embedded-hal = { version = "0.2.4" }
|
embedded-hal = { version = "0.2.4" }
|
||||||
panic-probe = { version = "0.2.0", features = ["print-defmt"] }
|
panic-probe = { version = "0.2.0", features = ["print-defmt"] }
|
||||||
futures = { version = "0.3.8", default-features = false, features = ["async-await"] }
|
futures = { version = "0.3.8", default-features = false, features = ["async-await"] }
|
||||||
|
rand = { version = "0.8.4", default-features = false }
|
||||||
|
30
examples/nrf/src/bin/rng.rs
Normal file
30
examples/nrf/src/bin/rng.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(min_type_alias_impl_trait)]
|
||||||
|
#![feature(impl_trait_in_bindings)]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use defmt::panic;
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_nrf::Peripherals;
|
||||||
|
use embassy_nrf::rng::Rng;
|
||||||
|
use embassy_nrf::interrupt;
|
||||||
|
use embassy::traits::rng::Rng as _;
|
||||||
|
use rand::Rng as _;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
let mut rng = Rng::new(p.RNG, interrupt::take!(RNG));
|
||||||
|
|
||||||
|
// Async API
|
||||||
|
let mut bytes = [0; 4];
|
||||||
|
rng.fill_bytes(&mut bytes).await.unwrap(); // nRF RNG is infallible
|
||||||
|
defmt::info!("Some random bytes: {:?}", bytes);
|
||||||
|
|
||||||
|
// Sync API with `rand`
|
||||||
|
defmt::info!("A random number from 1 to 10: {:?}", rng.gen_range(1..=10));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user