From a38dcc614c3dfc6a8738233e5fa14665711b6638 Mon Sep 17 00:00:00 2001 From: Eivind Alexander Bergem Date: Fri, 16 Aug 2024 10:17:39 +0200 Subject: [PATCH 1/2] Added non-blocking radio receive --- nrf-hal-common/src/ieee802154.rs | 114 +++++++++++++++++++------------ 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/nrf-hal-common/src/ieee802154.rs b/nrf-hal-common/src/ieee802154.rs index dc3d0cc2..69e99c23 100644 --- a/nrf-hal-common/src/ieee802154.rs +++ b/nrf-hal-common/src/ieee802154.rs @@ -6,6 +6,8 @@ use core::{ sync::atomic::{self, Ordering}, }; +use nb::block; + use crate::{ clocks::{Clocks, ExternalOscillator}, pac::{ @@ -15,6 +17,47 @@ use crate::{ timer::{self, Timer}, }; +/// Non-blocking receive +pub struct Recv<'a, 'c> { + radio: &'a mut Radio<'c>, +} + +impl<'a, 'c> Recv<'a, 'c> { + fn new(radio: &'a mut Radio<'c>) -> Self { + Self { radio } + } + + /// Check if receive is done + /// + /// This methods returns the `Ok` variant if the CRC included the + /// packet was successfully validated by the hardware. It returns + /// `Err(nb::Error::WouldBlock)` if a packet hasn't been received + /// yet, and `Err(nb::Error::Other)` if the CRC check failed. + pub fn is_done(&mut self) -> nb::Result { + if self.radio.radio.events_end.read().events_end().bit_is_set() { + self.radio.radio.events_end.reset(); + + dma_end_fence(); + + let crc = self.radio.radio.rxcrc.read().rxcrc().bits() as u16; + + if self.radio.radio.crcstatus.read().crcstatus().bit_is_set() { + Ok(crc) + } else { + Err(nb::Error::Other(crc)) + } + } else { + Err(nb::Error::WouldBlock) + } + } +} + +impl<'a, 'c> Drop for Recv<'a, 'c> { + fn drop(&mut self) { + self.radio.cancel_recv(); + } +} + /// IEEE 802.15.4 radio pub struct Radio<'c> { radio: RADIO, @@ -305,22 +348,26 @@ impl<'c> Radio<'c> { /// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet` /// will be updated with the received packet's data pub fn recv(&mut self, packet: &mut Packet) -> Result { + // Start non-blocking receive + let mut recv = self.recv_non_blocking(packet); + + // Block untill receive is done + block!(recv.is_done()) + } + + /// Receives one radio packet and copies its contents into the given `packet` buffer + /// + /// This method is non-blocking + pub fn recv_non_blocking<'a>(&'a mut self, packet: &'a mut Packet) -> Recv<'a, 'c> { // Start the read - // NOTE(unsafe) We block until reception completes or errors + // NOTE(unsafe) + // The packet must live until the transfer is done. Recv takes + // a mutable reference to the packet to enforce this. unsafe { self.start_recv(packet); } - // wait until we have received something - self.wait_for_event(Event::End); - dma_end_fence(); - - let crc = self.radio.rxcrc.read().rxcrc().bits() as u16; - if self.radio.crcstatus.read().crcstatus().bit_is_set() { - Ok(crc) - } else { - Err(crc) - } + Recv::new(self) } /// Listens for a packet for no longer than the specified amount of microseconds @@ -347,39 +394,23 @@ impl<'c> Radio<'c> { // Start the timeout timer timer.start(microseconds); - // Start the read - // NOTE(unsafe) We block until reception completes or errors - unsafe { - self.start_recv(packet); - } - - // Wait for transmission to end - let mut recv_completed = false; + // Start non-blocking receive + let mut recv = self.recv_non_blocking(packet); + // Check if either receive is done or timeout occured loop { - if self.radio.events_end.read().bits() != 0 { - // transfer complete - dma_end_fence(); - recv_completed = true; - break; + match recv.is_done() { + Ok(crc) => break Ok(crc), + Err(err) => match err { + nb::Error::Other(crc) => break Err(Error::Crc(crc)), + nb::Error::WouldBlock => (), + }, } if timer.reset_if_finished() { - // timeout - break; - } - } - - if !recv_completed { - // Cancel the reception if it did not complete until now - self.cancel_recv(); - Err(Error::Timeout) - } else { - let crc = self.radio.rxcrc.read().rxcrc().bits() as u16; - if self.radio.crcstatus.read().crcstatus().bit_is_set() { - Ok(crc) - } else { - Err(Error::Crc(crc)) + // Break loop in case of timeout. Receive is + // cancelled when `recv` is dropped. + break Err(Error::Timeout); } } } @@ -674,10 +705,6 @@ impl<'c> Radio<'c> { fn wait_for_event(&self, event: Event) { match event { - Event::End => { - while self.radio.events_end.read().events_end().bit_is_clear() {} - self.radio.events_end.reset(); - } Event::PhyEnd => { while self .radio @@ -728,7 +755,6 @@ fn dma_end_fence() { } enum Event { - End, PhyEnd, } From 9f280d27e09550f3c7b6f7ae2ddc08c04081d566 Mon Sep 17 00:00:00 2001 From: Eivind Alexander Bergem Date: Tue, 20 Aug 2024 13:42:37 +0200 Subject: [PATCH 2/2] `recv_non_blocking` takes closure --- nrf-hal-common/src/ieee802154.rs | 55 +++++++++++++++++--------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/nrf-hal-common/src/ieee802154.rs b/nrf-hal-common/src/ieee802154.rs index 69e99c23..4f04b09f 100644 --- a/nrf-hal-common/src/ieee802154.rs +++ b/nrf-hal-common/src/ieee802154.rs @@ -33,7 +33,7 @@ impl<'a, 'c> Recv<'a, 'c> { /// packet was successfully validated by the hardware. It returns /// `Err(nb::Error::WouldBlock)` if a packet hasn't been received /// yet, and `Err(nb::Error::Other)` if the CRC check failed. - pub fn is_done(&mut self) -> nb::Result { + pub fn is_done(&self) -> nb::Result { if self.radio.radio.events_end.read().events_end().bit_is_set() { self.radio.radio.events_end.reset(); @@ -349,25 +349,30 @@ impl<'c> Radio<'c> { /// will be updated with the received packet's data pub fn recv(&mut self, packet: &mut Packet) -> Result { // Start non-blocking receive - let mut recv = self.recv_non_blocking(packet); - - // Block untill receive is done - block!(recv.is_done()) + self.recv_non_blocking(packet, |recv| { + // Block untill receive is done + block!(recv.is_done()) + }) } /// Receives one radio packet and copies its contents into the given `packet` buffer /// /// This method is non-blocking - pub fn recv_non_blocking<'a>(&'a mut self, packet: &'a mut Packet) -> Recv<'a, 'c> { + pub fn recv_non_blocking<'a, T>( + &'a mut self, + packet: &'a mut Packet, + f: impl FnOnce(&Recv<'a, 'c>) -> T, + ) -> T { // Start the read // NOTE(unsafe) - // The packet must live until the transfer is done. Recv takes - // a mutable reference to the packet to enforce this. + // The packet must live until the transfer is done. Receive is handled inside + // a closure to ensure this unsafe { self.start_recv(packet); } - Recv::new(self) + let recv = Recv::new(self); + f(&recv) } /// Listens for a packet for no longer than the specified amount of microseconds @@ -395,24 +400,24 @@ impl<'c> Radio<'c> { timer.start(microseconds); // Start non-blocking receive - let mut recv = self.recv_non_blocking(packet); - - // Check if either receive is done or timeout occured - loop { - match recv.is_done() { - Ok(crc) => break Ok(crc), - Err(err) => match err { - nb::Error::Other(crc) => break Err(Error::Crc(crc)), - nb::Error::WouldBlock => (), - }, - } + self.recv_non_blocking(packet, |recv| { + // Check if either receive is done or timeout occured + loop { + match recv.is_done() { + Ok(crc) => break Ok(crc), + Err(err) => match err { + nb::Error::Other(crc) => break Err(Error::Crc(crc)), + nb::Error::WouldBlock => (), + }, + } - if timer.reset_if_finished() { - // Break loop in case of timeout. Receive is - // cancelled when `recv` is dropped. - break Err(Error::Timeout); + if timer.reset_if_finished() { + // Break loop in case of timeout. Receive is + // cancelled when `recv` is dropped. + break Err(Error::Timeout); + } } - } + }) } unsafe fn start_recv(&mut self, packet: &mut Packet) {