aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/rng.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-03-05 21:50:15 +0100
committerDario Nieuwenhuis <[email protected]>2023-03-06 00:17:51 +0100
commitd113fcfe326bd338df2db7733fcf0ae9f230c594 (patch)
tree3f7ee9082c6185a4e14adeeeffaecc98ef3c86fc /embassy-nrf/src/rng.rs
parent96788ac93a1e98ef8d9d5e8d80d5102aef34d45d (diff)
nrf/rng: make available on all chips, use Instance trait, switch to new interrupt binding.
Diffstat (limited to 'embassy-nrf/src/rng.rs')
-rw-r--r--embassy-nrf/src/rng.rs193
1 files changed, 122 insertions, 71 deletions
diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs
index b0b3a8eb8..a5602248d 100644
--- a/embassy-nrf/src/rng.rs
+++ b/embassy-nrf/src/rng.rs
@@ -1,83 +1,48 @@
1//! Random Number Generator (RNG) driver. 1//! Random Number Generator (RNG) driver.
2 2
3#![macro_use]
4
3use core::future::poll_fn; 5use core::future::poll_fn;
6use core::marker::PhantomData;
4use core::ptr; 7use core::ptr;
5use core::sync::atomic::{AtomicPtr, Ordering}; 8use core::sync::atomic::{AtomicPtr, Ordering};
6use core::task::Poll; 9use core::task::Poll;
7 10
11use embassy_cortex_m::interrupt::Interrupt;
8use embassy_hal_common::drop::OnDrop; 12use embassy_hal_common::drop::OnDrop;
9use embassy_hal_common::{into_ref, PeripheralRef}; 13use embassy_hal_common::{into_ref, PeripheralRef};
10use embassy_sync::waitqueue::AtomicWaker; 14use embassy_sync::waitqueue::AtomicWaker;
11 15
12use crate::interrupt::InterruptExt; 16use crate::interrupt::InterruptExt;
13use crate::peripherals::RNG; 17use crate::{interrupt, Peripheral};
14use crate::{interrupt, pac, Peripheral};
15
16impl RNG {
17 fn regs() -> &'static pac::rng::RegisterBlock {
18 unsafe { &*pac::RNG::ptr() }
19 }
20}
21
22static STATE: State = State {
23 ptr: AtomicPtr::new(ptr::null_mut()),
24 end: AtomicPtr::new(ptr::null_mut()),
25 waker: AtomicWaker::new(),
26};
27 18
28struct State { 19/// Interrupt handler.
29 ptr: AtomicPtr<u8>, 20pub struct InterruptHandler<T: Instance> {
30 end: AtomicPtr<u8>, 21 _phantom: PhantomData<T>,
31 waker: AtomicWaker,
32} 22}
33 23
34/// A wrapper around an nRF RNG peripheral. 24impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> {
35/// 25 unsafe fn on_interrupt() {
36/// It has a non-blocking API, and a blocking api through `rand`. 26 let s = T::state();
37pub struct Rng<'d> { 27 let r = T::regs();
38 irq: PeripheralRef<'d, interrupt::RNG>,
39}
40
41impl<'d> Rng<'d> {
42 /// Creates a new RNG driver from the `RNG` peripheral and interrupt.
43 ///
44 /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,
45 /// e.g. using `mem::forget`.
46 ///
47 /// The synchronous API is safe.
48 pub fn new(_rng: impl Peripheral<P = RNG> + 'd, irq: impl Peripheral<P = interrupt::RNG> + 'd) -> Self {
49 into_ref!(irq);
50
51 let this = Self { irq };
52 28
53 this.stop();
54 this.disable_irq();
55
56 this.irq.set_handler(Self::on_interrupt);
57 this.irq.unpend();
58 this.irq.enable();
59
60 this
61 }
62
63 fn on_interrupt(_: *mut ()) {
64 // Clear the event. 29 // Clear the event.
65 RNG::regs().events_valrdy.reset(); 30 r.events_valrdy.reset();
66 31
67 // Mutate the slice within a critical section, 32 // Mutate the slice within a critical section,
68 // so that the future isn't dropped in between us loading the pointer and actually dereferencing it. 33 // so that the future isn't dropped in between us loading the pointer and actually dereferencing it.
69 let (ptr, end) = critical_section::with(|_| { 34 let (ptr, end) = critical_section::with(|_| {
70 let ptr = STATE.ptr.load(Ordering::Relaxed); 35 let ptr = s.ptr.load(Ordering::Relaxed);
71 // We need to make sure we haven't already filled the whole slice, 36 // We need to make sure we haven't already filled the whole slice,
72 // in case the interrupt fired again before the executor got back to the future. 37 // in case the interrupt fired again before the executor got back to the future.
73 let end = STATE.end.load(Ordering::Relaxed); 38 let end = s.end.load(Ordering::Relaxed);
74 if !ptr.is_null() && ptr != end { 39 if !ptr.is_null() && ptr != end {
75 // If the future was dropped, the pointer would have been set to null, 40 // If the future was dropped, the pointer would have been set to null,
76 // so we're still good to mutate the slice. 41 // so we're still good to mutate the slice.
77 // The safety contract of `Rng::new` means that the future can't have been dropped 42 // The safety contract of `Rng::new` means that the future can't have been dropped
78 // without calling its destructor. 43 // without calling its destructor.
79 unsafe { 44 unsafe {
80 *ptr = RNG::regs().value.read().value().bits(); 45 *ptr = r.value.read().value().bits();
81 } 46 }
82 } 47 }
83 (ptr, end) 48 (ptr, end)
@@ -90,15 +55,15 @@ impl<'d> Rng<'d> {
90 } 55 }
91 56
92 let new_ptr = unsafe { ptr.add(1) }; 57 let new_ptr = unsafe { ptr.add(1) };
93 match STATE 58 match s
94 .ptr 59 .ptr
95 .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed) 60 .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed)
96 { 61 {
97 Ok(_) => { 62 Ok(_) => {
98 let end = STATE.end.load(Ordering::Relaxed); 63 let end = s.end.load(Ordering::Relaxed);
99 // It doesn't matter if `end` was changed under our feet, because then this will just be false. 64 // It doesn't matter if `end` was changed under our feet, because then this will just be false.
100 if new_ptr == end { 65 if new_ptr == end {
101 STATE.waker.wake(); 66 s.waker.wake();
102 } 67 }
103 } 68 }
104 Err(_) => { 69 Err(_) => {
@@ -107,21 +72,53 @@ impl<'d> Rng<'d> {
107 } 72 }
108 } 73 }
109 } 74 }
75}
76
77/// A wrapper around an nRF RNG peripheral.
78///
79/// It has a non-blocking API, and a blocking api through `rand`.
80pub struct Rng<'d, T: Instance> {
81 _peri: PeripheralRef<'d, T>,
82}
83
84impl<'d, T: Instance> Rng<'d, T> {
85 /// Creates a new RNG driver from the `RNG` peripheral and interrupt.
86 ///
87 /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,
88 /// e.g. using `mem::forget`.
89 ///
90 /// The synchronous API is safe.
91 pub fn new(
92 rng: impl Peripheral<P = T> + 'd,
93 _irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
94 ) -> Self {
95 into_ref!(rng);
96
97 let this = Self { _peri: rng };
98
99 this.stop();
100 this.disable_irq();
101
102 unsafe { T::Interrupt::steal() }.unpend();
103 unsafe { T::Interrupt::steal() }.enable();
104
105 this
106 }
110 107
111 fn stop(&self) { 108 fn stop(&self) {
112 RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) 109 T::regs().tasks_stop.write(|w| unsafe { w.bits(1) })
113 } 110 }
114 111
115 fn start(&self) { 112 fn start(&self) {
116 RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) }) 113 T::regs().tasks_start.write(|w| unsafe { w.bits(1) })
117 } 114 }
118 115
119 fn enable_irq(&self) { 116 fn enable_irq(&self) {
120 RNG::regs().intenset.write(|w| w.valrdy().set()); 117 T::regs().intenset.write(|w| w.valrdy().set());
121 } 118 }
122 119
123 fn disable_irq(&self) { 120 fn disable_irq(&self) {
124 RNG::regs().intenclr.write(|w| w.valrdy().clear()); 121 T::regs().intenclr.write(|w| w.valrdy().clear());
125 } 122 }
126 123
127 /// Enable or disable the RNG's bias correction. 124 /// Enable or disable the RNG's bias correction.
@@ -131,7 +128,7 @@ impl<'d> Rng<'d> {
131 /// 128 ///
132 /// Defaults to disabled. 129 /// Defaults to disabled.
133 pub fn set_bias_correction(&self, enable: bool) { 130 pub fn set_bias_correction(&self, enable: bool) {
134 RNG::regs().config.write(|w| w.dercen().bit(enable)) 131 T::regs().config.write(|w| w.dercen().bit(enable))
135 } 132 }
136 133
137 /// Fill the buffer with random bytes. 134 /// Fill the buffer with random bytes.
@@ -140,11 +137,13 @@ impl<'d> Rng<'d> {
140 return; // Nothing to fill 137 return; // Nothing to fill
141 } 138 }
142 139
140 let s = T::state();
141
143 let range = dest.as_mut_ptr_range(); 142 let range = dest.as_mut_ptr_range();
144 // Even if we've preempted the interrupt, it can't preempt us again, 143 // Even if we've preempted the interrupt, it can't preempt us again,
145 // so we don't need to worry about the order we write these in. 144 // so we don't need to worry about the order we write these in.
146 STATE.ptr.store(range.start, Ordering::Relaxed); 145 s.ptr.store(range.start, Ordering::Relaxed);
147 STATE.end.store(range.end, Ordering::Relaxed); 146 s.end.store(range.end, Ordering::Relaxed);
148 147
149 self.enable_irq(); 148 self.enable_irq();
150 self.start(); 149 self.start();
@@ -154,16 +153,16 @@ impl<'d> Rng<'d> {
154 self.disable_irq(); 153 self.disable_irq();
155 154
156 // The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here. 155 // The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here.
157 STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed); 156 s.ptr.store(ptr::null_mut(), Ordering::Relaxed);
158 STATE.end.store(ptr::null_mut(), Ordering::Relaxed); 157 s.end.store(ptr::null_mut(), Ordering::Relaxed);
159 }); 158 });
160 159
161 poll_fn(|cx| { 160 poll_fn(|cx| {
162 STATE.waker.register(cx.waker()); 161 s.waker.register(cx.waker());
163 162
164 // The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`. 163 // The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`.
165 let end = STATE.end.load(Ordering::Relaxed); 164 let end = s.end.load(Ordering::Relaxed);
166 let ptr = STATE.ptr.load(Ordering::Relaxed); 165 let ptr = s.ptr.load(Ordering::Relaxed);
167 166
168 if ptr == end { 167 if ptr == end {
169 // We're done. 168 // We're done.
@@ -183,7 +182,7 @@ impl<'d> Rng<'d> {
183 self.start(); 182 self.start();
184 183
185 for byte in dest.iter_mut() { 184 for byte in dest.iter_mut() {
186 let regs = RNG::regs(); 185 let regs = T::regs();
187 while regs.events_valrdy.read().bits() == 0 {} 186 while regs.events_valrdy.read().bits() == 0 {}
188 regs.events_valrdy.reset(); 187 regs.events_valrdy.reset();
189 *byte = regs.value.read().value().bits(); 188 *byte = regs.value.read().value().bits();
@@ -193,13 +192,16 @@ impl<'d> Rng<'d> {
193 } 192 }
194} 193}
195 194
196impl<'d> Drop for Rng<'d> { 195impl<'d, T: Instance> Drop for Rng<'d, T> {
197 fn drop(&mut self) { 196 fn drop(&mut self) {
198 self.irq.disable() 197 self.stop();
198 let s = T::state();
199 s.ptr.store(ptr::null_mut(), Ordering::Relaxed);
200 s.end.store(ptr::null_mut(), Ordering::Relaxed);
199 } 201 }
200} 202}
201 203
202impl<'d> rand_core::RngCore for Rng<'d> { 204impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> {
203 fn fill_bytes(&mut self, dest: &mut [u8]) { 205 fn fill_bytes(&mut self, dest: &mut [u8]) {
204 self.blocking_fill_bytes(dest); 206 self.blocking_fill_bytes(dest);
205 } 207 }
@@ -223,4 +225,53 @@ impl<'d> rand_core::RngCore for Rng<'d> {
223 } 225 }
224} 226}
225 227
226impl<'d> rand_core::CryptoRng for Rng<'d> {} 228impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {}
229
230pub(crate) mod sealed {
231 use super::*;
232
233 /// Peripheral static state
234 pub struct State {
235 pub ptr: AtomicPtr<u8>,
236 pub end: AtomicPtr<u8>,
237 pub waker: AtomicWaker,
238 }
239
240 impl State {
241 pub const fn new() -> Self {
242 Self {
243 ptr: AtomicPtr::new(ptr::null_mut()),
244 end: AtomicPtr::new(ptr::null_mut()),
245 waker: AtomicWaker::new(),
246 }
247 }
248 }
249
250 pub trait Instance {
251 fn regs() -> &'static crate::pac::rng::RegisterBlock;
252 fn state() -> &'static State;
253 }
254}
255
256/// RNG peripheral instance.
257pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
258 /// Interrupt for this peripheral.
259 type Interrupt: Interrupt;
260}
261
262macro_rules! impl_rng {
263 ($type:ident, $pac_type:ident, $irq:ident) => {
264 impl crate::rng::sealed::Instance for peripherals::$type {
265 fn regs() -> &'static crate::pac::rng::RegisterBlock {
266 unsafe { &*pac::$pac_type::ptr() }
267 }
268 fn state() -> &'static crate::rng::sealed::State {
269 static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new();
270 &STATE
271 }
272 }
273 impl crate::rng::Instance for peripherals::$type {
274 type Interrupt = crate::interrupt::$irq;
275 }
276 };
277}