diff options
| author | Liam Murphy <[email protected]> | 2021-06-30 12:34:57 +1000 |
|---|---|---|
| committer | Liam Murphy <[email protected]> | 2021-06-30 12:34:57 +1000 |
| commit | 89fdad3a6b8d2c1a702be91a15eaea7eeab5f6db (patch) | |
| tree | 1135890f837a6b114b7a71fe7f826cdb2de85e97 /embassy-nrf/src/rng.rs | |
| parent | ae0219de6f32222be5ef18be86376971a92f1751 (diff) | |
Don't wake the future for every byte in `fill_bytes`
Diffstat (limited to 'embassy-nrf/src/rng.rs')
| -rw-r--r-- | embassy-nrf/src/rng.rs | 92 |
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 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 1 | use core::convert::Infallible; | 2 | use core::convert::Infallible; |
| 3 | use core::future::Future; | ||
| 2 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | use core::ptr::NonNull; | ||
| 6 | use core::task::Poll; | ||
| 7 | use core::task::Waker; | ||
| 3 | 8 | ||
| 4 | use embassy::interrupt::InterruptExt; | 9 | use embassy::interrupt::InterruptExt; |
| 5 | use embassy::traits; | 10 | use embassy::traits; |
| 11 | use embassy::util::CriticalSectionMutex; | ||
| 6 | use embassy::util::OnDrop; | 12 | use embassy::util::OnDrop; |
| 7 | use embassy::util::Signal; | ||
| 8 | use embassy::util::Unborrow; | 13 | use embassy::util::Unborrow; |
| 9 | use embassy_extras::unborrow; | 14 | use embassy_extras::unborrow; |
| 10 | use futures::Future; | 15 | use futures::future::poll_fn; |
| 11 | use rand_core::RngCore; | 16 | use rand_core::RngCore; |
| 12 | 17 | ||
| 13 | use crate::interrupt; | 18 | use crate::interrupt; |
| @@ -20,18 +25,41 @@ impl RNG { | |||
| 20 | } | 25 | } |
| 21 | } | 26 | } |
| 22 | 27 | ||
| 23 | static NEXT_BYTE: Signal<u8> = Signal::new(); | 28 | static STATE: CriticalSectionMutex<RefCell<State>> = |
| 29 | CriticalSectionMutex::new(RefCell::new(State { | ||
| 30 | buffer: None, | ||
| 31 | waker: None, | ||
| 32 | index: 0, | ||
| 33 | })); | ||
| 34 | |||
| 35 | struct 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. | ||
| 45 | unsafe 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`. |
| 28 | pub struct Rng<'d> { | 50 | pub 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 | ||
| 33 | impl<'d> Rng<'d> { | 55 | impl<'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 { |
