commit
						e55c89f890
					
				| @ -33,6 +33,7 @@ embedded-hal    = { version = "0.2.4" } | ||||
| embedded-dma    = { version = "0.1.2" } | ||||
| futures     = { version = "0.3.5", default-features = false } | ||||
| critical-section = "0.2.1" | ||||
| rand_core = "0.6.3" | ||||
| 
 | ||||
| nrf52805-pac  = { version = "0.1.0", optional = true, features = [ "rt" ]} | ||||
| nrf52810-pac  = { version = "0.9.0", optional = true, features = [ "rt" ]} | ||||
|  | ||||
| @ -8,6 +8,9 @@ embassy_extras::peripherals! { | ||||
|     RTC0, | ||||
|     RTC1, | ||||
| 
 | ||||
|     // RNG
 | ||||
|     RNG, | ||||
| 
 | ||||
|     // UARTE
 | ||||
|     UARTE0, | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,9 @@ embassy_extras::peripherals! { | ||||
|     RTC0, | ||||
|     RTC1, | ||||
| 
 | ||||
|     // RNG
 | ||||
|     RNG, | ||||
| 
 | ||||
|     // UARTE
 | ||||
|     UARTE0, | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,9 @@ embassy_extras::peripherals! { | ||||
|     RTC0, | ||||
|     RTC1, | ||||
| 
 | ||||
|     // RNG
 | ||||
|     RNG, | ||||
| 
 | ||||
|     // UARTE
 | ||||
|     UARTE0, | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,9 @@ embassy_extras::peripherals! { | ||||
|     RTC0, | ||||
|     RTC1, | ||||
| 
 | ||||
|     // RNG
 | ||||
|     RNG, | ||||
| 
 | ||||
|     // UARTE
 | ||||
|     UARTE0, | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,9 @@ embassy_extras::peripherals! { | ||||
|     RTC1, | ||||
|     RTC2, | ||||
| 
 | ||||
|     // RNG
 | ||||
|     RNG, | ||||
| 
 | ||||
|     // UARTE
 | ||||
|     UARTE0, | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,9 @@ embassy_extras::peripherals! { | ||||
|     RTC1, | ||||
|     RTC2, | ||||
| 
 | ||||
|     // RNG
 | ||||
|     RNG, | ||||
| 
 | ||||
|     // UARTE
 | ||||
|     UARTE0, | ||||
|     UARTE1, | ||||
|  | ||||
| @ -9,6 +9,9 @@ embassy_extras::peripherals! { | ||||
|     RTC1, | ||||
|     RTC2, | ||||
| 
 | ||||
|     // RNG
 | ||||
|     RNG, | ||||
| 
 | ||||
|     // QSPI
 | ||||
|     QSPI, | ||||
| 
 | ||||
|  | ||||
| @ -33,6 +33,7 @@ pub mod ppi; | ||||
| pub mod pwm; | ||||
| #[cfg(feature = "nrf52840")] | ||||
| pub mod qspi; | ||||
| pub mod rng; | ||||
| pub mod rtc; | ||||
| #[cfg(not(feature = "nrf52820"))] | ||||
| pub mod saadc; | ||||
|  | ||||
							
								
								
									
										244
									
								
								embassy-nrf/src/rng.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								embassy-nrf/src/rng.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,244 @@ | ||||
| use core::convert::Infallible; | ||||
| use core::future::Future; | ||||
| use core::marker::PhantomData; | ||||
| use core::ptr; | ||||
| use core::sync::atomic::AtomicPtr; | ||||
| use core::sync::atomic::Ordering; | ||||
| use core::task::Poll; | ||||
| 
 | ||||
| use embassy::interrupt::InterruptExt; | ||||
| use embassy::traits; | ||||
| use embassy::util::AtomicWaker; | ||||
| use embassy::util::OnDrop; | ||||
| use embassy::util::Unborrow; | ||||
| use embassy_extras::unborrow; | ||||
| use futures::future::poll_fn; | ||||
| 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 STATE: State = State { | ||||
|     ptr: AtomicPtr::new(ptr::null_mut()), | ||||
|     end: AtomicPtr::new(ptr::null_mut()), | ||||
|     waker: AtomicWaker::new(), | ||||
| }; | ||||
| 
 | ||||
| struct State { | ||||
|     ptr: AtomicPtr<u8>, | ||||
|     end: AtomicPtr<u8>, | ||||
|     waker: AtomicWaker, | ||||
| } | ||||
| 
 | ||||
| /// 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, &'d mut 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 unsafe 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 ()) { | ||||
|         // Clear the event.
 | ||||
|         RNG::regs().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); | ||||
|             // 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); | ||||
|             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, end) | ||||
|         }); | ||||
| 
 | ||||
|         if ptr.is_null() || ptr == end { | ||||
|             // If the future was dropped, there's nothing to do.
 | ||||
|             // If `ptr == end`, we were called by mistake, so return.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         let new_ptr = unsafe { ptr.add(1) }; | ||||
|         match STATE | ||||
|             .ptr | ||||
|             .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed) | ||||
|         { | ||||
|             Ok(_) => { | ||||
|                 let end = STATE.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(); | ||||
|                 } | ||||
|             } | ||||
|             Err(_) => { | ||||
|                 // If the future was dropped or finished, there's no point trying to wake it.
 | ||||
|                 // It will have already stopped the RNG, so there's no need to do that either.
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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> { | ||||
|         async move { | ||||
|             if dest.len() == 0 { | ||||
|                 return Ok(()); // Nothing to fill
 | ||||
|             } | ||||
| 
 | ||||
|             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); | ||||
| 
 | ||||
|             self.enable_irq(); | ||||
|             self.start(); | ||||
| 
 | ||||
|             let on_drop = OnDrop::new(|| { | ||||
|                 self.stop(); | ||||
|                 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); | ||||
|             }); | ||||
| 
 | ||||
|             poll_fn(|cx| { | ||||
|                 STATE.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); | ||||
| 
 | ||||
|                 if ptr == end { | ||||
|                     // We're done.
 | ||||
|                     Poll::Ready(()) | ||||
|                 } else { | ||||
|                     Poll::Pending | ||||
|                 } | ||||
|             }) | ||||
|             .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" } | ||||
| panic-probe = { version = "0.2.0", features = ["print-defmt"] } | ||||
| futures = { version = "0.3.8", default-features = false, features = ["async-await"] } | ||||
| rand = { version = "0.8.4", default-features = false } | ||||
|  | ||||
							
								
								
									
										43
									
								
								examples/nrf/src/bin/rng.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								examples/nrf/src/bin/rng.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| #![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::traits::rng::Rng as _; | ||||
| use embassy_nrf::interrupt; | ||||
| use embassy_nrf::rng::Rng; | ||||
| use embassy_nrf::Peripherals; | ||||
| use rand::Rng as _; | ||||
| 
 | ||||
| #[embassy::main] | ||||
| async fn main(_spawner: Spawner, p: Peripherals) { | ||||
|     let mut rng = unsafe { 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)); | ||||
| 
 | ||||
|     let mut bytes = [0; 1024]; | ||||
|     rng.fill_bytes(&mut bytes).await.unwrap(); | ||||
|     let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros()); | ||||
|     let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones()); | ||||
|     defmt::info!( | ||||
|         "Chance of zero: {}%", | ||||
|         zero_count * 100 / (bytes.len() as u32 * 8) | ||||
|     ); | ||||
|     defmt::info!( | ||||
|         "Chance of one: {}%", | ||||
|         one_count * 100 / (bytes.len() as u32 * 8) | ||||
|     ); | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user