diff options
| author | Liam Murphy <[email protected]> | 2021-06-29 17:26:16 +1000 |
|---|---|---|
| committer | Liam Murphy <[email protected]> | 2021-06-29 17:26:16 +1000 |
| commit | 8a4ab298192c388bed09fc198ee1786561cd50f9 (patch) | |
| tree | ecaed07d38549f0f453c8003cf45c2eaa692b81a /embassy-nrf/src/rng.rs | |
| parent | e6d6e82e54bca88ab1144802d2b716b867934225 (diff) | |
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.
Diffstat (limited to 'embassy-nrf/src/rng.rs')
| -rw-r--r-- | embassy-nrf/src/rng.rs | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs new file mode 100644 index 000000000..40778c64c --- /dev/null +++ b/embassy-nrf/src/rng.rs | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | use core::convert::Infallible; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | |||
| 4 | use embassy::interrupt::InterruptExt; | ||
| 5 | use embassy::traits; | ||
| 6 | use embassy::util::OnDrop; | ||
| 7 | use embassy::util::Signal; | ||
| 8 | use embassy::util::Unborrow; | ||
| 9 | use embassy_extras::unborrow; | ||
| 10 | use futures::Future; | ||
| 11 | use rand_core::RngCore; | ||
| 12 | |||
| 13 | use crate::interrupt; | ||
| 14 | use crate::pac; | ||
| 15 | use crate::peripherals::RNG; | ||
| 16 | |||
| 17 | impl RNG { | ||
| 18 | fn regs() -> &'static pac::rng::RegisterBlock { | ||
| 19 | unsafe { &*pac::RNG::ptr() } | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | static NEXT_BYTE: Signal<u8> = Signal::new(); | ||
| 24 | |||
| 25 | /// A wrapper around an nRF RNG peripheral. | ||
| 26 | /// | ||
| 27 | /// It has a non-blocking API, through `embassy::traits::Rng`, and a blocking api through `rand`. | ||
| 28 | pub struct Rng<'d> { | ||
| 29 | irq: interrupt::RNG, | ||
| 30 | phantom: PhantomData<&'d mut RNG>, | ||
| 31 | } | ||
| 32 | |||
| 33 | impl<'d> Rng<'d> { | ||
| 34 | pub fn new( | ||
| 35 | _rng: impl Unborrow<Target = RNG> + 'd, | ||
| 36 | irq: impl Unborrow<Target = interrupt::RNG> + 'd, | ||
| 37 | ) -> Self { | ||
| 38 | unborrow!(irq); | ||
| 39 | |||
| 40 | let this = Self { | ||
| 41 | irq, | ||
| 42 | phantom: PhantomData, | ||
| 43 | }; | ||
| 44 | |||
| 45 | this.stop(); | ||
| 46 | this.disable_irq(); | ||
| 47 | |||
| 48 | this.irq.set_handler(Self::on_interrupt); | ||
| 49 | this.irq.unpend(); | ||
| 50 | this.irq.enable(); | ||
| 51 | |||
| 52 | this | ||
| 53 | } | ||
| 54 | |||
| 55 | fn on_interrupt(_: *mut ()) { | ||
| 56 | NEXT_BYTE.signal(RNG::regs().value.read().value().bits()); | ||
| 57 | RNG::regs().events_valrdy.reset(); | ||
| 58 | } | ||
| 59 | |||
| 60 | fn stop(&self) { | ||
| 61 | RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) | ||
| 62 | } | ||
| 63 | |||
| 64 | fn start(&self) { | ||
| 65 | RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) }) | ||
| 66 | } | ||
| 67 | |||
| 68 | fn enable_irq(&self) { | ||
| 69 | RNG::regs().intenset.write(|w| w.valrdy().set()); | ||
| 70 | } | ||
| 71 | |||
| 72 | fn disable_irq(&self) { | ||
| 73 | RNG::regs().intenclr.write(|w| w.valrdy().clear()); | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Enable or disable the RNG's bias correction. | ||
| 77 | /// | ||
| 78 | /// Bias correction removes any bias towards a '1' or a '0' in the bits generated. | ||
| 79 | /// However, this makes the generation of numbers slower. | ||
| 80 | /// | ||
| 81 | /// Defaults to disabled. | ||
| 82 | pub fn bias_correction(&self, enable: bool) { | ||
| 83 | RNG::regs().config.write(|w| w.dercen().bit(enable)) | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | impl<'d> Drop for Rng<'d> { | ||
| 88 | fn drop(&mut self) { | ||
| 89 | self.irq.disable() | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | impl<'d> traits::rng::Rng for Rng<'d> { | ||
| 94 | type Error = Infallible; | ||
| 95 | |||
| 96 | #[rustfmt::skip] // For some reason rustfmt removes the where clause | ||
| 97 | type RngFuture<'a> where 'd: 'a = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 98 | |||
| 99 | fn fill_bytes<'a>(&'a mut self, dest: &'a mut [u8]) -> Self::RngFuture<'a> { | ||
| 100 | self.enable_irq(); | ||
| 101 | self.start(); | ||
| 102 | |||
| 103 | async move { | ||
| 104 | let on_drop = OnDrop::new(|| { | ||
| 105 | self.stop(); | ||
| 106 | self.disable_irq(); | ||
| 107 | }); | ||
| 108 | |||
| 109 | for byte in dest.iter_mut() { | ||
| 110 | *byte = NEXT_BYTE.wait().await; | ||
| 111 | } | ||
| 112 | |||
| 113 | // Trigger the teardown | ||
| 114 | drop(on_drop); | ||
| 115 | |||
| 116 | Ok(()) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | impl<'d> RngCore for Rng<'d> { | ||
| 122 | fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
| 123 | self.start(); | ||
| 124 | |||
| 125 | for byte in dest.iter_mut() { | ||
| 126 | let regs = RNG::regs(); | ||
| 127 | while regs.events_valrdy.read().bits() == 0 {} | ||
| 128 | regs.events_valrdy.reset(); | ||
| 129 | *byte = regs.value.read().value().bits(); | ||
| 130 | } | ||
| 131 | |||
| 132 | self.stop(); | ||
| 133 | } | ||
| 134 | |||
| 135 | fn next_u32(&mut self) -> u32 { | ||
| 136 | let mut bytes = [0; 4]; | ||
| 137 | self.fill_bytes(&mut bytes); | ||
| 138 | // We don't care about the endianness, so just use the native one. | ||
| 139 | u32::from_ne_bytes(bytes) | ||
| 140 | } | ||
| 141 | |||
| 142 | fn next_u64(&mut self) -> u64 { | ||
| 143 | let mut bytes = [0; 8]; | ||
| 144 | self.fill_bytes(&mut bytes); | ||
| 145 | u64::from_ne_bytes(bytes) | ||
| 146 | } | ||
| 147 | |||
| 148 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { | ||
| 149 | self.fill_bytes(dest); | ||
| 150 | Ok(()) | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | // TODO: Should `Rng` implement `CryptoRng`? It's 'suitable for cryptographic purposes' according to the specification. | ||
