aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf
diff options
context:
space:
mode:
authorLiam Murphy <[email protected]>2021-06-30 12:34:57 +1000
committerLiam Murphy <[email protected]>2021-06-30 12:34:57 +1000
commit89fdad3a6b8d2c1a702be91a15eaea7eeab5f6db (patch)
tree1135890f837a6b114b7a71fe7f826cdb2de85e97 /embassy-nrf
parentae0219de6f32222be5ef18be86376971a92f1751 (diff)
Don't wake the future for every byte in `fill_bytes`
Diffstat (limited to 'embassy-nrf')
-rw-r--r--embassy-nrf/src/rng.rs92
1 files changed, 78 insertions, 14 deletions
diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs
index e5ec02c67..0ec87effa 100644
--- a/embassy-nrf/src/rng.rs
+++ b/embassy-nrf/src/rng.rs
@@ -1,13 +1,18 @@
1use core::cell::RefCell;
1use core::convert::Infallible; 2use core::convert::Infallible;
3use core::future::Future;
2use core::marker::PhantomData; 4use core::marker::PhantomData;
5use core::ptr::NonNull;
6use core::task::Poll;
7use core::task::Waker;
3 8
4use embassy::interrupt::InterruptExt; 9use embassy::interrupt::InterruptExt;
5use embassy::traits; 10use embassy::traits;
11use embassy::util::CriticalSectionMutex;
6use embassy::util::OnDrop; 12use embassy::util::OnDrop;
7use embassy::util::Signal;
8use embassy::util::Unborrow; 13use embassy::util::Unborrow;
9use embassy_extras::unborrow; 14use embassy_extras::unborrow;
10use futures::Future; 15use futures::future::poll_fn;
11use rand_core::RngCore; 16use rand_core::RngCore;
12 17
13use crate::interrupt; 18use crate::interrupt;
@@ -20,18 +25,41 @@ impl RNG {
20 } 25 }
21} 26}
22 27
23static NEXT_BYTE: Signal<u8> = Signal::new(); 28static STATE: CriticalSectionMutex<RefCell<State>> =
29 CriticalSectionMutex::new(RefCell::new(State {
30 buffer: None,
31 waker: None,
32 index: 0,
33 }));
34
35struct State {
36 buffer: Option<NonNull<[u8]>>,
37 waker: Option<Waker>,
38 index: usize,
39}
40
41// SAFETY: `NonNull` is `!Send` because of the possibility of it being aliased.
42// However, `buffer` is only used within `on_interrupt`,
43// and the original `&mut` passed to `fill_bytes` cannot be used because the safety contract of `Rng::new`
44// means that it must still be borrowed by `RngFuture`, and so `rustc` will not let it be accessed.
45unsafe impl Send for State {}
24 46
25/// A wrapper around an nRF RNG peripheral. 47/// A wrapper around an nRF RNG peripheral.
26/// 48///
27/// It has a non-blocking API, through `embassy::traits::Rng`, and a blocking api through `rand`. 49/// It has a non-blocking API, through `embassy::traits::Rng`, and a blocking api through `rand`.
28pub struct Rng<'d> { 50pub struct Rng<'d> {
29 irq: interrupt::RNG, 51 irq: interrupt::RNG,
30 phantom: PhantomData<&'d mut RNG>, 52 phantom: PhantomData<(&'d mut RNG, &'d mut interrupt::RNG)>,
31} 53}
32 54
33impl<'d> Rng<'d> { 55impl<'d> Rng<'d> {
34 pub fn new( 56 /// Creates a new RNG driver from the `RNG` peripheral and interrupt.
57 ///
58 /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,
59 /// e.g. using `mem::forget`.
60 ///
61 /// The synchronous API is safe.
62 pub unsafe fn new(
35 _rng: impl Unborrow<Target = RNG> + 'd, 63 _rng: impl Unborrow<Target = RNG> + 'd,
36 irq: impl Unborrow<Target = interrupt::RNG> + 'd, 64 irq: impl Unborrow<Target = interrupt::RNG> + 'd,
37 ) -> Self { 65 ) -> Self {
@@ -42,7 +70,7 @@ impl<'d> Rng<'d> {
42 phantom: PhantomData, 70 phantom: PhantomData,
43 }; 71 };
44 72
45 this.stop(); 73 Self::stop();
46 this.disable_irq(); 74 this.disable_irq();
47 75
48 this.irq.set_handler(Self::on_interrupt); 76 this.irq.set_handler(Self::on_interrupt);
@@ -53,11 +81,25 @@ impl<'d> Rng<'d> {
53 } 81 }
54 82
55 fn on_interrupt(_: *mut ()) { 83 fn on_interrupt(_: *mut ()) {
56 NEXT_BYTE.signal(RNG::regs().value.read().value().bits()); 84 critical_section::with(|cs| {
57 RNG::regs().events_valrdy.reset(); 85 let mut state = STATE.borrow(cs).borrow_mut();
86 // SAFETY: the safety requirements on `Rng::new` make sure that the original `&mut`'s lifetime is still valid,
87 // meaning it can't be aliased and is a valid pointer.
88 let buffer = unsafe { state.buffer.unwrap().as_mut() };
89 buffer[state.index] = RNG::regs().value.read().value().bits();
90 state.index += 1;
91 if state.index == buffer.len() {
92 // Stop the RNG within the interrupt so that it doesn't get triggered again on the way to waking the future.
93 Self::stop();
94 if let Some(waker) = state.waker.take() {
95 waker.wake();
96 }
97 }
98 RNG::regs().events_valrdy.reset();
99 });
58 } 100 }
59 101
60 fn stop(&self) { 102 fn stop() {
61 RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) 103 RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) })
62 } 104 }
63 105
@@ -98,17 +140,39 @@ impl<'d> traits::rng::Rng for Rng<'d> {
98 140
99 fn fill_bytes<'a>(&'a mut self, dest: &'a mut [u8]) -> Self::RngFuture<'a> { 141 fn fill_bytes<'a>(&'a mut self, dest: &'a mut [u8]) -> Self::RngFuture<'a> {
100 async move { 142 async move {
143 critical_section::with(|cs| {
144 let mut state = STATE.borrow(cs).borrow_mut();
145 state.buffer = Some(dest.into());
146 });
147
101 self.enable_irq(); 148 self.enable_irq();
102 self.start(); 149 self.start();
103 150
104 let on_drop = OnDrop::new(|| { 151 let on_drop = OnDrop::new(|| {
105 self.stop(); 152 Self::stop();
106 self.disable_irq(); 153 self.disable_irq();
107 }); 154 });
108 155
109 for byte in dest.iter_mut() { 156 poll_fn(|cx| {
110 *byte = NEXT_BYTE.wait().await; 157 critical_section::with(|cs| {
111 } 158 let mut state = STATE.borrow(cs).borrow_mut();
159 state.waker = Some(cx.waker().clone());
160 // SAFETY: see safety message in interrupt handler.
161 // Also, both here and in the interrupt handler, we're in a critical section,
162 // so they can't interfere with each other.
163 let buffer = unsafe { state.buffer.unwrap().as_ref() };
164
165 if state.index == buffer.len() {
166 // Reset the state for next time
167 state.buffer = None;
168 state.index = 0;
169 Poll::Ready(())
170 } else {
171 Poll::Pending
172 }
173 })
174 })
175 .await;
112 176
113 // Trigger the teardown 177 // Trigger the teardown
114 drop(on_drop); 178 drop(on_drop);
@@ -129,7 +193,7 @@ impl<'d> RngCore for Rng<'d> {
129 *byte = regs.value.read().value().bits(); 193 *byte = regs.value.read().value().bits();
130 } 194 }
131 195
132 self.stop(); 196 Self::stop();
133 } 197 }
134 198
135 fn next_u32(&mut self) -> u32 { 199 fn next_u32(&mut self) -> u32 {