From 4294bc5e4bb191adcdd2daffd77180bb602da720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Wed, 28 Feb 2024 22:36:31 +0100 Subject: [PATCH 01/12] Added IEEE 802.15.4 radio --- embassy-nrf/src/chips/nrf52833.rs | 5 + embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/radio/ble.rs | 15 +- embassy-nrf/src/radio/event.rs | 310 +++++++++++++++ embassy-nrf/src/radio/ieee802154.rs | 573 ++++++++++++++++++++++++++++ embassy-nrf/src/radio/mod.rs | 34 +- 6 files changed, 917 insertions(+), 22 deletions(-) create mode 100644 embassy-nrf/src/radio/event.rs create mode 100644 embassy-nrf/src/radio/ieee802154.rs diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 7c9b66d69..20f14e2d6 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -170,6 +170,9 @@ embassy_hal_internal::peripherals! { // I2S I2S, + + // Radio + RADIO, } impl_usb!(USBD, USBD, USBD); @@ -306,6 +309,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 04a6293a4..132bffa8b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,7 +47,7 @@ pub mod gpio; pub mod gpiote; // TODO: tested on other chips -#[cfg(any(feature = "nrf52840"))] +#[cfg(any(feature = "nrf52833", feature = "nrf52840"))] pub mod radio; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 24dba582f..ecf8cc5eb 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -15,19 +15,6 @@ use crate::interrupt::typelevel::Interrupt; use crate::radio::*; use crate::util::slice_in_ram_or; -/// RADIO error. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - /// Buffer was too long. - BufferTooLong, - /// Buffer was to short. - BufferTooShort, - /// The buffer is not in data RAM. It is most likely in flash, and nRF's DMA cannot access flash. - BufferNotInRAM, -} - /// Radio driver. pub struct Radio<'d, T: Instance> { _p: PeripheralRef<'d, T>, @@ -393,7 +380,7 @@ impl<'d, T: Instance> Radio<'d, T> { // On poll check if interrupt happen poll_fn(|cx| { - s.end_waker.register(cx.waker()); + s.event_waker.register(cx.waker()); if r.events_end.read().events_end().bit_is_set() { // trace!("radio:end"); return core::task::Poll::Ready(()); diff --git a/embassy-nrf/src/radio/event.rs b/embassy-nrf/src/radio/event.rs new file mode 100644 index 000000000..11056b4d8 --- /dev/null +++ b/embassy-nrf/src/radio/event.rs @@ -0,0 +1,310 @@ +use crate::pac; +use bitflags; + +bitflags::bitflags! { + /// Event as bit flags + pub struct Event : u32 { + /// Radio ready + const READY = 1u32 << 0; + /// Address operation done + const ADDRESS = 1u32 << 1; + /// Payload operation done + const PAYLOAD = 1u32 << 2; + /// Packet operation done + const END = 1u32 << 3; + /// Radio has been disabled + const DISABLED = 1u32 << 4; + /// Device address match in last received packet + const DEV_MATCH = 1u32 << 5; + /// No device address match in last received packet + const DEV_MISS = 1u32 << 6; + /// RSSI sampling complete + const RSSI_END = 1u32 << 7; + /// Bit counter reached target + const BC_MATCH = 1u32 << 10; + /// CRC ok in last received packet + const CRC_OK = 1u32 << 12; + /// CRC error in last received packet + const CRC_ERROR = 1u32 << 13; + /// IEEE 802.15.4 length field received + const FRAME_START = 1u32 << 14; + /// Sampling of energy detect complete + const ED_END = 1u32 << 15; + /// Sampling of energy detect stopped + const ED_STOPPED = 1u32 << 16; + /// Wireless medium in idle, ready to sent + const CCA_IDLE = 1u32 << 17; + /// Wireless medium busy, do not send + const CCA_BUSY = 1u32 << 18; + /// Clear channel assessment stopped + const CCA_STOPPED = 1u32 << 19; + /// BLE LR rate boost received + const RATE_BOOST = 1u32 << 20; + /// Radio has ramped up transmitter + const TX_READY = 1u32 << 21; + /// Radio has ramped up receiver + const RX_READY = 1u32 << 22; + /// MAC header match found + const MHR_MATCH = 1u32 << 23; + /// Preamble received, possible false triggering + const SYNC = 1u32 << 26; + /// Last bit sent / received + const PHY_END = 1u32 << 27; + /// Continuous tone extension is present + const CTE_PRESENT = 1u32 << 28; + } +} + +impl Event { + /// Read events from radio + #[cfg(not(feature = "nrf52832"))] + pub fn from_radio(radio: &pac::radio::RegisterBlock) -> Self { + let mut value = Self::empty(); + if radio.events_ready.read().events_ready().bit_is_set() { + value |= Self::READY; + } + if radio.events_address.read().events_address().bit_is_set() { + value |= Self::ADDRESS; + } + if radio.events_payload.read().events_payload().bit_is_set() { + value |= Self::PAYLOAD; + } + if radio.events_end.read().events_end().bit_is_set() { + value |= Self::END; + } + if radio.events_disabled.read().events_disabled().bit_is_set() { + value |= Self::DISABLED; + } + if radio.events_devmatch.read().events_devmatch().bit_is_set() { + value |= Self::DEV_MATCH; + } + if radio.events_devmiss.read().events_devmiss().bit_is_set() { + value |= Self::DEV_MISS; + } + if radio.events_rssiend.read().events_rssiend().bit_is_set() { + value |= Self::RSSI_END; + } + if radio.events_bcmatch.read().events_bcmatch().bit_is_set() { + value |= Self::BC_MATCH; + } + if radio.events_crcok.read().events_crcok().bit_is_set() { + value |= Self::CRC_OK; + } + if radio.events_crcerror.read().events_crcerror().bit_is_set() { + value |= Self::CRC_ERROR; + } + #[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_framestart.read().events_framestart().bit_is_set() { + value |= Self::FRAME_START; + } + #[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_edend.read().events_edend().bit_is_set() { + value |= Self::ED_END; + } + #[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_edstopped.read().events_edstopped().bit_is_set() { + value |= Self::ED_STOPPED; + } + #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] + if radio.events_ccaidle.read().events_ccaidle().bit_is_set() { + value |= Self::CCA_IDLE; + } + #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] + if radio.events_ccabusy.read().events_ccabusy().bit_is_set() { + value |= Self::CCA_BUSY; + } + #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] + if radio.events_ccastopped.read().events_ccastopped().bit_is_set() { + value |= Self::CCA_STOPPED; + } + #[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_rateboost.read().events_rateboost().bit_is_set() { + value |= Self::RATE_BOOST; + } + #[cfg(any( + feature = "nrf52805", + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_txready.read().events_txready().bit_is_set() { + value |= Self::TX_READY; + } + #[cfg(any( + feature = "nrf52805", + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_rxready.read().events_rxready().bit_is_set() { + value |= Self::RX_READY; + } + #[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_mhrmatch.read().events_mhrmatch().bit_is_set() { + value |= Self::MHR_MATCH; + } + #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] + if radio.events_sync.read().events_sync().bit_is_set() { + value |= Self::SYNC; + } + #[cfg(any( + feature = "nrf52805", + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_phyend.read().events_phyend().bit_is_set() { + value |= Self::PHY_END; + } + #[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "_nrf5340-net" + ))] + if radio.events_ctepresent.read().events_ctepresent().bit_is_set() { + value |= Self::CTE_PRESENT; + } + value + } + // The nRF52832 SVD probably is a bit broken + /// Read events from radio + #[cfg(feature = "nrf52832")] + pub fn from_radio(radio: &pac::radio::RegisterBlock) -> Self { + let mut value = Self::empty(); + if radio.events_ready.read().bits() == 1 { + value |= Self::READY; + } + if radio.events_address.read().bits() == 1 { + value |= Self::ADDRESS; + } + if radio.events_payload.read().bits() == 1 { + value |= Self::PAYLOAD; + } + if radio.events_end.read().bits() == 1 { + value |= Self::END; + } + if radio.events_disabled.read().bits() == 1 { + value |= Self::DISABLED; + } + if radio.events_devmatch.read().bits() == 1 { + value |= Self::DEV_MATCH; + } + if radio.events_devmiss.read().bits() == 1 { + value |= Self::DEV_MISS; + } + if radio.events_rssiend.read().bits() == 1 { + value |= Self::RSSI_END; + } + if radio.events_bcmatch.read().bits() == 1 { + value |= Self::BC_MATCH; + } + if radio.events_crcok.read().bits() == 1 { + value |= Self::CRC_OK; + } + if radio.events_crcerror.read().bits() == 1 { + value |= Self::CRC_ERROR; + } + value + } + + /// Read events from radio, mask with set interrupts + pub fn from_radio_masked(radio: &pac::radio::RegisterBlock) -> Self { + Self::from_radio(radio) & Self::from_bits_truncate(radio.intenset.read().bits()) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for Event { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", + if self.contains(Self::READY) { "RD" } else { "__" }, + if self.contains(Self::ADDRESS) { "AD" } else { "__" }, + if self.contains(Self::PAYLOAD) { "PL" } else { "__" }, + if self.contains(Self::END) { " E" } else { "__" }, + if self.contains(Self::DISABLED) { "DI" } else { "__" }, + if self.contains(Self::DEV_MATCH) { "D+" } else { "__" }, + if self.contains(Self::DEV_MISS) { "D-" } else { "__" }, + if self.contains(Self::RSSI_END) { "RE" } else { "__" }, + if self.contains(Self::BC_MATCH) { "CM" } else { "__" }, + if self.contains(Self::CRC_OK) { "CO" } else { "__" }, + if self.contains(Self::CRC_ERROR) { "CE" } else { "__" }, + if self.contains(Self::FRAME_START) { "FS" } else { "__" }, + if self.contains(Self::ED_END) { "EE" } else { "__" }, + if self.contains(Self::ED_STOPPED) { "ES" } else { "__" }, + if self.contains(Self::CCA_IDLE) { "CI" } else { "__" }, + if self.contains(Self::CCA_BUSY) { "CB" } else { "__" }, + if self.contains(Self::CCA_STOPPED) { "CS" } else { "__" }, + if self.contains(Self::RATE_BOOST) { "RB" } else { "__" }, + if self.contains(Self::TX_READY) { "TX" } else { "__" }, + if self.contains(Self::RX_READY) { "RX" } else { "__" }, + if self.contains(Self::MHR_MATCH) { "MM" } else { "__" }, + if self.contains(Self::SYNC) { "SY" } else { "__" }, + if self.contains(Self::PHY_END) { "PE" } else { "__" }, + if self.contains(Self::CTE_PRESENT) { "CP" } else { "__" }, + ) + } +} + +impl core::fmt::Display for Event { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + fmt, + "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", + if self.contains(Self::READY) { "RD" } else { "__" }, + if self.contains(Self::ADDRESS) { "AD" } else { "__" }, + if self.contains(Self::PAYLOAD) { "PL" } else { "__" }, + if self.contains(Self::END) { " E" } else { "__" }, + if self.contains(Self::DISABLED) { "DI" } else { "__" }, + if self.contains(Self::DEV_MATCH) { "D+" } else { "__" }, + if self.contains(Self::DEV_MISS) { "D-" } else { "__" }, + if self.contains(Self::RSSI_END) { "RE" } else { "__" }, + if self.contains(Self::BC_MATCH) { "CM" } else { "__" }, + if self.contains(Self::CRC_OK) { "CO" } else { "__" }, + if self.contains(Self::CRC_ERROR) { "CE" } else { "__" }, + if self.contains(Self::FRAME_START) { "FS" } else { "__" }, + if self.contains(Self::ED_END) { "EE" } else { "__" }, + if self.contains(Self::ED_STOPPED) { "ES" } else { "__" }, + if self.contains(Self::CCA_IDLE) { "CI" } else { "__" }, + if self.contains(Self::CCA_BUSY) { "CB" } else { "__" }, + if self.contains(Self::CCA_STOPPED) { "CS" } else { "__" }, + if self.contains(Self::RATE_BOOST) { "RB" } else { "__" }, + if self.contains(Self::TX_READY) { "TX" } else { "__" }, + if self.contains(Self::RX_READY) { "RX" } else { "__" }, + if self.contains(Self::MHR_MATCH) { "MM" } else { "__" }, + if self.contains(Self::SYNC) { "SY" } else { "__" }, + if self.contains(Self::PHY_END) { "PE" } else { "__" }, + if self.contains(Self::CTE_PRESENT) { "CP" } else { "__" }, + ) + } +} diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs new file mode 100644 index 000000000..4d7949cb3 --- /dev/null +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -0,0 +1,573 @@ +//! IEEE 802.15.4 radio + +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use super::{Error, Event, Instance, InterruptHandler}; +use crate::{ + interrupt::{self, typelevel::Interrupt}, + pac, Peripheral, +}; +use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower}; + +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; + +/// Default Start of Frame Delimiter = `0xA7` (IEEE compliant) +pub const DEFAULT_SFD: u8 = 0xA7; + +// TODO expose the other variants in `pac::CCAMODE_A` +/// Clear Channel Assessment method +pub enum Cca { + /// Carrier sense + CarrierSense, + /// Energy Detection / Energy Above Threshold + EnergyDetection { + /// Energy measurements above this value mean that the channel is assumed to be busy. + /// Note the measurement range is 0..0xFF - where 0 means that the received power was + /// less than 10 dB above the selected receiver sensitivity. This value is not given in dBm, + /// but can be converted. See the nrf52840 Product Specification Section 6.20.12.4 + /// for details. + ed_threshold: u8, + }, +} + +fn get_state(radio: &pac::radio::RegisterBlock) -> RadioState { + match radio.state.read().state().variant() { + Some(state) => state, + None => unreachable!(), + } +} + +fn trace_state(state: RadioState) { + match state { + RadioState::DISABLED => trace!("radio:state:DISABLED"), + RadioState::RX_RU => trace!("radio:state:RX_RU"), + RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), + RadioState::RX => trace!("radio:state:RX"), + RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), + RadioState::TX_RU => trace!("radio:state:TX_RU"), + RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), + RadioState::TX => trace!("radio:state:TX"), + RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), + } +} + +/// Radio driver. +pub struct Radio<'d, T: Instance> { + _p: PeripheralRef<'d, T>, + needs_enable: bool, +} + +impl<'d, T: Instance> Radio<'d, T> { + /// Create a new radio driver. + pub fn new( + radio: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + into_ref!(radio); + + let r = T::regs(); + + // Disable and enable to reset peripheral + r.power.write(|w| w.power().disabled()); + r.power.write(|w| w.power().enabled()); + + // Enable 802.15.4 mode + r.mode.write(|w| w.mode().ieee802154_250kbit()); + // Configure CRC skip address + r.crccnf.write(|w| w.len().two().skipaddr().ieee802154()); + unsafe { + // Configure CRC polynomial and init + r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021)); + r.crcinit.write(|w| w.crcinit().bits(0)); + // Configure packet layout + // 8-bit on air length + // S0 length, zero bytes + // S1 length, zero bytes + // S1 included in RAM if S1 length > 0, No. + // Code Indicator length, 0 + // Preamble length 32-bit zero + // Exclude CRC + // No TERM field + r.pcnf0.write(|w| { + w.lflen() + .bits(8) + .s0len() + .clear_bit() + .s1len() + .bits(0) + .s1incl() + .clear_bit() + .cilen() + .bits(0) + .plen() + ._32bit_zero() + .crcinc() + .include() + }); + r.pcnf1.write(|w| { + w.maxlen() + .bits(Packet::MAX_PSDU_LEN) + .statlen() + .bits(0) + .balen() + .bits(0) + .endian() + .clear_bit() + .whiteen() + .clear_bit() + }); + } + + // Enable NVIC interrupt + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + let mut radio = Self { + _p: radio, + needs_enable: false, + }; + + radio.set_sfd(DEFAULT_SFD); + radio.set_transmission_power(0); + radio.set_channel(11); + radio.set_cca(Cca::CarrierSense); + + radio + } + + /// Changes the radio channel + pub fn set_channel(&mut self, channel: u8) { + let r = T::regs(); + if channel < 11 || channel > 26 { + panic!("Bad 802.15.4 channel"); + } + let frequency_offset = (channel - 10) * 5; + self.needs_enable = true; + r.frequency + .write(|w| unsafe { w.frequency().bits(frequency_offset).map().default() }); + } + + /// Changes the Clear Channel Assessment method + pub fn set_cca(&mut self, cca: Cca) { + let r = T::regs(); + self.needs_enable = true; + match cca { + Cca::CarrierSense => r.ccactrl.write(|w| w.ccamode().carrier_mode()), + Cca::EnergyDetection { ed_threshold } => { + // "[ED] is enabled by first configuring the field CCAMODE=EdMode in CCACTRL + // and writing the CCAEDTHRES field to a chosen value." + r.ccactrl + .write(|w| unsafe { w.ccamode().ed_mode().ccaedthres().bits(ed_threshold) }); + } + } + } + + /// Changes the Start of Frame Delimiter + pub fn set_sfd(&mut self, sfd: u8) { + let r = T::regs(); + r.sfd.write(|w| unsafe { w.sfd().bits(sfd) }); + } + + /// Clear interrupts + pub fn clear_all_interrupts(&mut self) { + let r = T::regs(); + r.intenclr.write(|w| unsafe { w.bits(0xffff_ffff) }); + } + + /// Changes the radio transmission power + pub fn set_transmission_power(&mut self, power: i8) { + let r = T::regs(); + self.needs_enable = true; + + let tx_power: TxPower = match power { + 8 => TxPower::POS8D_BM, + 7 => TxPower::POS7D_BM, + 6 => TxPower::POS6D_BM, + 5 => TxPower::POS5D_BM, + 4 => TxPower::POS4D_BM, + 3 => TxPower::POS3D_BM, + 2 => TxPower::POS2D_BM, + 0 => TxPower::_0D_BM, + -4 => TxPower::NEG4D_BM, + -8 => TxPower::NEG8D_BM, + -12 => TxPower::NEG12D_BM, + -16 => TxPower::NEG16D_BM, + -20 => TxPower::NEG20D_BM, + -30 => TxPower::NEG30D_BM, + -40 => TxPower::NEG40D_BM, + _ => panic!("Invalid transmission power value"), + }; + + r.txpower.write(|w| w.txpower().variant(tx_power)); + } + + /// Waits until the radio state matches the given `state` + fn wait_for_radio_state(&self, state: RadioState) { + while self.state() != state {} + } + + fn state(&self) -> RadioState { + let r = T::regs(); + match r.state.read().state().variant() { + Some(state) => state, + None => unreachable!(), + } + } + + fn trace_state(&self) { + trace_state(self.state()); + } + + /// Moves the radio from any state to the DISABLED state + fn disable(&mut self) { + let r = T::regs(); + // See figure 110 in nRF52840-PS + loop { + match self.state() { + RadioState::DISABLED => return, + + RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => { + r.tasks_disable.write(|w| w.tasks_disable().set_bit()); + + self.wait_for_radio_state(RadioState::DISABLED); + return; + } + + // ramping down + RadioState::RX_DISABLE | RadioState::TX_DISABLE => { + self.wait_for_radio_state(RadioState::DISABLED); + return; + } + + // cancel ongoing transfer or ongoing CCA + RadioState::RX => { + r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit()); + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + self.wait_for_radio_state(RadioState::RX_IDLE); + } + RadioState::TX => { + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + self.wait_for_radio_state(RadioState::TX_IDLE); + } + } + } + } + + fn set_buffer(&mut self, buffer: &[u8]) { + let r = T::regs(); + r.packetptr.write(|w| unsafe { w.bits(buffer.as_ptr() as u32) }); + } + + /// Moves the radio to the RXIDLE state + fn receive_prepare(&mut self) { + let state = self.state(); + + let disable = match state { + RadioState::DISABLED => false, + RadioState::RX_DISABLE => true, + RadioState::TX_DISABLE => true, + RadioState::RX_IDLE => self.needs_enable, + // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE + RadioState::TX_IDLE => true, + _ => unreachable!(), + }; + if disable { + trace!("Receive Setup"); + self.trace_state(); + self.disable(); + } + self.needs_enable = false; + } + + fn receive_start(&mut self, packet: &mut Packet) { + // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's + // allocated in RAM + let r = T::regs(); + + // clear related events + r.events_framestart.reset(); + r.events_ccabusy.reset(); + r.events_phyend.reset(); + + self.receive_prepare(); + + // Configure shortcuts + // + // The radio goes through following states when receiving a 802.15.4 packet + // + // enable RX → ramp up RX → RX idle → Receive → end (PHYEND) + r.shorts.write(|w| w.rxready_start().enabled()); + + // set up RX buffer + self.set_buffer(packet.buffer.as_mut()); + + // start transfer + dma_start_fence(); + + match self.state() { + // Re-start receiver + RadioState::RX_IDLE => r.tasks_start.write(|w| w.tasks_start().set_bit()), + // Enable receiver + _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), + } + } + + fn receive_cancel() { + let r = T::regs(); + r.shorts.reset(); + if r.events_framestart.read().events_framestart().bit_is_set() { + // TODO: Is there a way to finish receiving this frame + trace!("EVENTS {}", Event::from_radio(r)); + } + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + loop { + match get_state(r) { + RadioState::DISABLED | RadioState::RX_IDLE => break, + _ => (), + } + } + // DMA transfer may have been in progress so synchronize with its memory operations + dma_end_fence(); + } + + /// Receives one radio packet and copies its contents into the given `packet` buffer + /// + /// This methods returns the `Ok` variant if the CRC included the packet was successfully + /// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet` + /// will be updated with the received packet's data + pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), u16> { + let s = T::state(); + let r = T::regs(); + + // Start the read + self.receive_start(packet); + + let dropper = OnDrop::new(|| Self::receive_cancel()); + + self.clear_all_interrupts(); + // wait until we have received something + core::future::poll_fn(|cx| { + s.event_waker.register(cx.waker()); + + if r.events_phyend.read().events_phyend().bit_is_set() { + r.events_phyend.reset(); + trace!("RX done poll"); + return Poll::Ready(()); + } else { + r.intenset.write(|w| w.phyend().set()); + }; + + Poll::Pending + }) + .await; + + dma_end_fence(); + dropper.defuse(); + + let crc = r.rxcrc.read().rxcrc().bits() as u16; + if r.crcstatus.read().crcstatus().bit_is_set() { + Ok(()) + } else { + Err(crc) + } + } + + /// Tries to send the given `packet` + /// + /// This method performs Clear Channel Assessment (CCA) first and sends the `packet` only if the + /// channel is observed to be *clear* (no transmission is currently ongoing), otherwise no + /// packet is transmitted and the `Err` variant is returned + /// + /// NOTE this method will *not* modify the `packet` argument. The mutable reference is used to + /// ensure the `packet` buffer is allocated in RAM, which is required by the RADIO peripheral + // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's + // allocated in RAM + pub async fn try_send(&mut self, packet: &mut Packet) -> Result<(), Error> { + let s = T::state(); + let r = T::regs(); + + // clear related events + r.events_framestart.reset(); + r.events_ccabusy.reset(); + r.events_phyend.reset(); + + // enable radio to perform cca + self.receive_prepare(); + + /// transmit result + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum TransmitResult { + /// Success + Success, + /// Clear channel assessment reported channel in use + ChannelInUse, + } + + // Configure shortcuts + // + // The radio goes through following states when sending a 802.15.4 packet + // + // enable RX → ramp up RX → clear channel assessment (CCA) → CCA result + // CCA idle → enable TX → start TX → TX → end (PHYEND) → disabled + // + // CCA might end up in the event CCABUSY in which there will be no transmission + r.shorts.write(|w| { + w.rxready_ccastart() + .enabled() + .ccaidle_txen() + .enabled() + .txready_start() + .enabled() + .ccabusy_disable() + .enabled() + .phyend_disable() + .enabled() + }); + + // Set transmission buffer + self.set_buffer(packet.buffer.as_mut()); + + // the DMA transfer will start at some point after the following write operation so + // we place the compiler fence here + dma_start_fence(); + // start CCA. In case the channel is clear, the data at packetptr will be sent automatically + + match self.state() { + // Re-start receiver + RadioState::RX_IDLE => r.tasks_ccastart.write(|w| w.tasks_ccastart().set_bit()), + // Enable receiver + _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), + } + + self.clear_all_interrupts(); + let result = core::future::poll_fn(|cx| { + s.event_waker.register(cx.waker()); + + if r.events_phyend.read().events_phyend().bit_is_set() { + r.events_phyend.reset(); + r.events_ccabusy.reset(); + trace!("TX done poll"); + return Poll::Ready(TransmitResult::Success); + } else if r.events_ccabusy.read().events_ccabusy().bit_is_set() { + r.events_ccabusy.reset(); + trace!("TX no CCA"); + return Poll::Ready(TransmitResult::ChannelInUse); + } + + r.intenset.write(|w| w.phyend().set().ccabusy().set()); + + Poll::Pending + }) + .await; + + match result { + TransmitResult::Success => Ok(()), + TransmitResult::ChannelInUse => Err(Error::ChannelInUse), + } + } +} + +/// An IEEE 802.15.4 packet +/// +/// This `Packet` is a PHY layer packet. It's made up of the physical header (PHR) and the PSDU +/// (PHY service data unit). The PSDU of this `Packet` will always include the MAC level CRC, AKA +/// the FCS (Frame Control Sequence) -- the CRC is fully computed in hardware and automatically +/// appended on transmission and verified on reception. +/// +/// The API lets users modify the usable part (not the CRC) of the PSDU via the `deref` and +/// `copy_from_slice` methods. These methods will automatically update the PHR. +/// +/// See figure 119 in the Product Specification of the nRF52840 for more details +pub struct Packet { + buffer: [u8; Self::SIZE], +} + +// See figure 124 in nRF52840-PS +impl Packet { + // for indexing purposes + const PHY_HDR: usize = 0; + const DATA: core::ops::RangeFrom = 1..; + + /// Maximum amount of usable payload (CRC excluded) a single packet can contain, in bytes + pub const CAPACITY: u8 = 125; + const CRC: u8 = 2; // size of the CRC, which is *never* copied to / from RAM + const MAX_PSDU_LEN: u8 = Self::CAPACITY + Self::CRC; + const SIZE: usize = 1 /* PHR */ + Self::MAX_PSDU_LEN as usize; + + /// Returns an empty packet (length = 0) + pub fn new() -> Self { + let mut packet = Self { + buffer: [0; Self::SIZE], + }; + packet.set_len(0); + packet + } + + /// Fills the packet payload with given `src` data + /// + /// # Panics + /// + /// This function panics if `src` is larger than `Self::CAPACITY` + pub fn copy_from_slice(&mut self, src: &[u8]) { + assert!(src.len() <= Self::CAPACITY as usize); + let len = src.len() as u8; + self.buffer[Self::DATA][..len as usize].copy_from_slice(&src[..len.into()]); + self.set_len(len); + } + + /// Returns the size of this packet's payload + pub fn len(&self) -> u8 { + self.buffer[Self::PHY_HDR] - Self::CRC + } + + /// Changes the size of the packet's payload + /// + /// # Panics + /// + /// This function panics if `len` is larger than `Self::CAPACITY` + pub fn set_len(&mut self, len: u8) { + assert!(len <= Self::CAPACITY); + self.buffer[Self::PHY_HDR] = len + Self::CRC; + } + + /// Returns the LQI (Link Quality Indicator) of the received packet + /// + /// Note that the LQI is stored in the `Packet`'s internal buffer by the hardware so the value + /// returned by this method is only valid after a `Radio.recv` operation. Operations that + /// modify the `Packet`, like `copy_from_slice` or `set_len`+`deref_mut`, will overwrite the + /// stored LQI value. + /// + /// Also note that the hardware will *not* compute a LQI for packets smaller than 3 bytes so + /// this method will return an invalid value for those packets. + pub fn lqi(&self) -> u8 { + self.buffer[1 /* PHY_HDR */ + self.len() as usize /* data */] + } +} + +impl core::ops::Deref for Packet { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + &self.buffer[Self::DATA][..self.len() as usize] + } +} + +impl core::ops::DerefMut for Packet { + fn deref_mut(&mut self) -> &mut [u8] { + let len = self.len(); + &mut self.buffer[Self::DATA][..len as usize] + } +} + +/// NOTE must be followed by a volatile write operation +fn dma_start_fence() { + compiler_fence(Ordering::Release); +} + +/// NOTE must be preceded by a volatile read operation +fn dma_end_fence() { + compiler_fence(Ordering::Acquire); +} diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 03f967f87..430078e8a 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -7,11 +7,32 @@ /// Bluetooth Low Energy Radio driver. pub mod ble; +mod event; +#[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "_nrf5340-net"))] +/// IEEE 802.15.4 +pub mod ieee802154; + +pub use event::Event; use core::marker::PhantomData; use crate::{interrupt, pac, Peripheral}; +/// RADIO error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Buffer was too long. + BufferTooLong, + /// Buffer was too short. + BufferTooShort, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, + /// Clear channel assessment reported channel in use + ChannelInUse, +} + /// Interrupt handler pub struct InterruptHandler { _phantom: PhantomData, @@ -21,11 +42,10 @@ impl interrupt::typelevel::Handler for InterruptHandl unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); - - if r.events_end.read().events_end().bit_is_set() { - s.end_waker.wake(); - r.intenclr.write(|w| w.end().clear()); - } + let events = Event::from_radio_masked(r); + // clear active interrupts + r.intenclr.write(|w| w.bits(events.bits())); + s.event_waker.wake(); } } @@ -34,12 +54,12 @@ pub(crate) mod sealed { pub struct State { /// end packet transmission or reception - pub end_waker: AtomicWaker, + pub event_waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { - end_waker: AtomicWaker::new(), + event_waker: AtomicWaker::new(), } } } From 35febae570ef6b89eb52948ee13684754b23da4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Thu, 29 Feb 2024 01:31:07 +0100 Subject: [PATCH 02/12] Fixed missing nrf52840 in Event --- embassy-nrf/src/radio/event.rs | 29 ++++++++++++++++++++++++++--- embassy-nrf/src/radio/ieee802154.rs | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/radio/event.rs b/embassy-nrf/src/radio/event.rs index 11056b4d8..8fb0958d5 100644 --- a/embassy-nrf/src/radio/event.rs +++ b/embassy-nrf/src/radio/event.rs @@ -97,6 +97,7 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_framestart.read().events_framestart().bit_is_set() { @@ -106,6 +107,7 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_edend.read().events_edend().bit_is_set() { @@ -115,20 +117,36 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_edstopped.read().events_edstopped().bit_is_set() { value |= Self::ED_STOPPED; } - #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] + #[cfg(any( + feature = "nrf52820", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-net" + ))] if radio.events_ccaidle.read().events_ccaidle().bit_is_set() { value |= Self::CCA_IDLE; } - #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] + #[cfg(any( + feature = "nrf52820", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-net" + ))] if radio.events_ccabusy.read().events_ccabusy().bit_is_set() { value |= Self::CCA_BUSY; } - #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] + #[cfg(any( + feature = "nrf52820", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-net" + ))] if radio.events_ccastopped.read().events_ccastopped().bit_is_set() { value |= Self::CCA_STOPPED; } @@ -136,6 +154,7 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_rateboost.read().events_rateboost().bit_is_set() { @@ -146,6 +165,7 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_txready.read().events_txready().bit_is_set() { @@ -156,6 +176,7 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_rxready.read().events_rxready().bit_is_set() { @@ -165,6 +186,7 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_mhrmatch.read().events_mhrmatch().bit_is_set() { @@ -179,6 +201,7 @@ impl Event { feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", + feature = "nrf52840", feature = "_nrf5340-net" ))] if radio.events_phyend.read().events_phyend().bit_is_set() { diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 4d7949cb3..adc6f8aaa 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -164,7 +164,7 @@ impl<'d, T: Instance> Radio<'d, T> { } } - /// Changes the Start of Frame Delimiter + /// Changes the Start of Frame Delimiter (SFD) pub fn set_sfd(&mut self, sfd: u8) { let r = T::regs(); r.sfd.write(|w| unsafe { w.sfd().bits(sfd) }); From f0753998bc9379bdb19ef4fcddf96ff19487a52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Thu, 29 Feb 2024 02:02:01 +0100 Subject: [PATCH 03/12] Clear all interrupts --- embassy-nrf/src/radio/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 430078e8a..914e6c438 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -42,9 +42,8 @@ impl interrupt::typelevel::Handler for InterruptHandl unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); - let events = Event::from_radio_masked(r); - // clear active interrupts - r.intenclr.write(|w| w.bits(events.bits())); + // clear all interrupts + r.intenclr.write(|w| w.bits(0xffff_ffff)); s.event_waker.wake(); } } From 8eac5d07f65e642ca4ad763ffe90bad97803089c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Thu, 29 Feb 2024 09:50:28 +0100 Subject: [PATCH 04/12] Remove Event, which was mostly used for debugging --- embassy-nrf/src/radio/event.rs | 333 ---------------------------- embassy-nrf/src/radio/ieee802154.rs | 3 +- embassy-nrf/src/radio/mod.rs | 3 - 3 files changed, 1 insertion(+), 338 deletions(-) delete mode 100644 embassy-nrf/src/radio/event.rs diff --git a/embassy-nrf/src/radio/event.rs b/embassy-nrf/src/radio/event.rs deleted file mode 100644 index 8fb0958d5..000000000 --- a/embassy-nrf/src/radio/event.rs +++ /dev/null @@ -1,333 +0,0 @@ -use crate::pac; -use bitflags; - -bitflags::bitflags! { - /// Event as bit flags - pub struct Event : u32 { - /// Radio ready - const READY = 1u32 << 0; - /// Address operation done - const ADDRESS = 1u32 << 1; - /// Payload operation done - const PAYLOAD = 1u32 << 2; - /// Packet operation done - const END = 1u32 << 3; - /// Radio has been disabled - const DISABLED = 1u32 << 4; - /// Device address match in last received packet - const DEV_MATCH = 1u32 << 5; - /// No device address match in last received packet - const DEV_MISS = 1u32 << 6; - /// RSSI sampling complete - const RSSI_END = 1u32 << 7; - /// Bit counter reached target - const BC_MATCH = 1u32 << 10; - /// CRC ok in last received packet - const CRC_OK = 1u32 << 12; - /// CRC error in last received packet - const CRC_ERROR = 1u32 << 13; - /// IEEE 802.15.4 length field received - const FRAME_START = 1u32 << 14; - /// Sampling of energy detect complete - const ED_END = 1u32 << 15; - /// Sampling of energy detect stopped - const ED_STOPPED = 1u32 << 16; - /// Wireless medium in idle, ready to sent - const CCA_IDLE = 1u32 << 17; - /// Wireless medium busy, do not send - const CCA_BUSY = 1u32 << 18; - /// Clear channel assessment stopped - const CCA_STOPPED = 1u32 << 19; - /// BLE LR rate boost received - const RATE_BOOST = 1u32 << 20; - /// Radio has ramped up transmitter - const TX_READY = 1u32 << 21; - /// Radio has ramped up receiver - const RX_READY = 1u32 << 22; - /// MAC header match found - const MHR_MATCH = 1u32 << 23; - /// Preamble received, possible false triggering - const SYNC = 1u32 << 26; - /// Last bit sent / received - const PHY_END = 1u32 << 27; - /// Continuous tone extension is present - const CTE_PRESENT = 1u32 << 28; - } -} - -impl Event { - /// Read events from radio - #[cfg(not(feature = "nrf52832"))] - pub fn from_radio(radio: &pac::radio::RegisterBlock) -> Self { - let mut value = Self::empty(); - if radio.events_ready.read().events_ready().bit_is_set() { - value |= Self::READY; - } - if radio.events_address.read().events_address().bit_is_set() { - value |= Self::ADDRESS; - } - if radio.events_payload.read().events_payload().bit_is_set() { - value |= Self::PAYLOAD; - } - if radio.events_end.read().events_end().bit_is_set() { - value |= Self::END; - } - if radio.events_disabled.read().events_disabled().bit_is_set() { - value |= Self::DISABLED; - } - if radio.events_devmatch.read().events_devmatch().bit_is_set() { - value |= Self::DEV_MATCH; - } - if radio.events_devmiss.read().events_devmiss().bit_is_set() { - value |= Self::DEV_MISS; - } - if radio.events_rssiend.read().events_rssiend().bit_is_set() { - value |= Self::RSSI_END; - } - if radio.events_bcmatch.read().events_bcmatch().bit_is_set() { - value |= Self::BC_MATCH; - } - if radio.events_crcok.read().events_crcok().bit_is_set() { - value |= Self::CRC_OK; - } - if radio.events_crcerror.read().events_crcerror().bit_is_set() { - value |= Self::CRC_ERROR; - } - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_framestart.read().events_framestart().bit_is_set() { - value |= Self::FRAME_START; - } - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_edend.read().events_edend().bit_is_set() { - value |= Self::ED_END; - } - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_edstopped.read().events_edstopped().bit_is_set() { - value |= Self::ED_STOPPED; - } - #[cfg(any( - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_ccaidle.read().events_ccaidle().bit_is_set() { - value |= Self::CCA_IDLE; - } - #[cfg(any( - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_ccabusy.read().events_ccabusy().bit_is_set() { - value |= Self::CCA_BUSY; - } - #[cfg(any( - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_ccastopped.read().events_ccastopped().bit_is_set() { - value |= Self::CCA_STOPPED; - } - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_rateboost.read().events_rateboost().bit_is_set() { - value |= Self::RATE_BOOST; - } - #[cfg(any( - feature = "nrf52805", - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_txready.read().events_txready().bit_is_set() { - value |= Self::TX_READY; - } - #[cfg(any( - feature = "nrf52805", - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_rxready.read().events_rxready().bit_is_set() { - value |= Self::RX_READY; - } - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_mhrmatch.read().events_mhrmatch().bit_is_set() { - value |= Self::MHR_MATCH; - } - #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] - if radio.events_sync.read().events_sync().bit_is_set() { - value |= Self::SYNC; - } - #[cfg(any( - feature = "nrf52805", - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - if radio.events_phyend.read().events_phyend().bit_is_set() { - value |= Self::PHY_END; - } - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "_nrf5340-net" - ))] - if radio.events_ctepresent.read().events_ctepresent().bit_is_set() { - value |= Self::CTE_PRESENT; - } - value - } - // The nRF52832 SVD probably is a bit broken - /// Read events from radio - #[cfg(feature = "nrf52832")] - pub fn from_radio(radio: &pac::radio::RegisterBlock) -> Self { - let mut value = Self::empty(); - if radio.events_ready.read().bits() == 1 { - value |= Self::READY; - } - if radio.events_address.read().bits() == 1 { - value |= Self::ADDRESS; - } - if radio.events_payload.read().bits() == 1 { - value |= Self::PAYLOAD; - } - if radio.events_end.read().bits() == 1 { - value |= Self::END; - } - if radio.events_disabled.read().bits() == 1 { - value |= Self::DISABLED; - } - if radio.events_devmatch.read().bits() == 1 { - value |= Self::DEV_MATCH; - } - if radio.events_devmiss.read().bits() == 1 { - value |= Self::DEV_MISS; - } - if radio.events_rssiend.read().bits() == 1 { - value |= Self::RSSI_END; - } - if radio.events_bcmatch.read().bits() == 1 { - value |= Self::BC_MATCH; - } - if radio.events_crcok.read().bits() == 1 { - value |= Self::CRC_OK; - } - if radio.events_crcerror.read().bits() == 1 { - value |= Self::CRC_ERROR; - } - value - } - - /// Read events from radio, mask with set interrupts - pub fn from_radio_masked(radio: &pac::radio::RegisterBlock) -> Self { - Self::from_radio(radio) & Self::from_bits_truncate(radio.intenset.read().bits()) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for Event { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!( - fmt, - "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", - if self.contains(Self::READY) { "RD" } else { "__" }, - if self.contains(Self::ADDRESS) { "AD" } else { "__" }, - if self.contains(Self::PAYLOAD) { "PL" } else { "__" }, - if self.contains(Self::END) { " E" } else { "__" }, - if self.contains(Self::DISABLED) { "DI" } else { "__" }, - if self.contains(Self::DEV_MATCH) { "D+" } else { "__" }, - if self.contains(Self::DEV_MISS) { "D-" } else { "__" }, - if self.contains(Self::RSSI_END) { "RE" } else { "__" }, - if self.contains(Self::BC_MATCH) { "CM" } else { "__" }, - if self.contains(Self::CRC_OK) { "CO" } else { "__" }, - if self.contains(Self::CRC_ERROR) { "CE" } else { "__" }, - if self.contains(Self::FRAME_START) { "FS" } else { "__" }, - if self.contains(Self::ED_END) { "EE" } else { "__" }, - if self.contains(Self::ED_STOPPED) { "ES" } else { "__" }, - if self.contains(Self::CCA_IDLE) { "CI" } else { "__" }, - if self.contains(Self::CCA_BUSY) { "CB" } else { "__" }, - if self.contains(Self::CCA_STOPPED) { "CS" } else { "__" }, - if self.contains(Self::RATE_BOOST) { "RB" } else { "__" }, - if self.contains(Self::TX_READY) { "TX" } else { "__" }, - if self.contains(Self::RX_READY) { "RX" } else { "__" }, - if self.contains(Self::MHR_MATCH) { "MM" } else { "__" }, - if self.contains(Self::SYNC) { "SY" } else { "__" }, - if self.contains(Self::PHY_END) { "PE" } else { "__" }, - if self.contains(Self::CTE_PRESENT) { "CP" } else { "__" }, - ) - } -} - -impl core::fmt::Display for Event { - fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - fmt, - "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", - if self.contains(Self::READY) { "RD" } else { "__" }, - if self.contains(Self::ADDRESS) { "AD" } else { "__" }, - if self.contains(Self::PAYLOAD) { "PL" } else { "__" }, - if self.contains(Self::END) { " E" } else { "__" }, - if self.contains(Self::DISABLED) { "DI" } else { "__" }, - if self.contains(Self::DEV_MATCH) { "D+" } else { "__" }, - if self.contains(Self::DEV_MISS) { "D-" } else { "__" }, - if self.contains(Self::RSSI_END) { "RE" } else { "__" }, - if self.contains(Self::BC_MATCH) { "CM" } else { "__" }, - if self.contains(Self::CRC_OK) { "CO" } else { "__" }, - if self.contains(Self::CRC_ERROR) { "CE" } else { "__" }, - if self.contains(Self::FRAME_START) { "FS" } else { "__" }, - if self.contains(Self::ED_END) { "EE" } else { "__" }, - if self.contains(Self::ED_STOPPED) { "ES" } else { "__" }, - if self.contains(Self::CCA_IDLE) { "CI" } else { "__" }, - if self.contains(Self::CCA_BUSY) { "CB" } else { "__" }, - if self.contains(Self::CCA_STOPPED) { "CS" } else { "__" }, - if self.contains(Self::RATE_BOOST) { "RB" } else { "__" }, - if self.contains(Self::TX_READY) { "TX" } else { "__" }, - if self.contains(Self::RX_READY) { "RX" } else { "__" }, - if self.contains(Self::MHR_MATCH) { "MM" } else { "__" }, - if self.contains(Self::SYNC) { "SY" } else { "__" }, - if self.contains(Self::PHY_END) { "PE" } else { "__" }, - if self.contains(Self::CTE_PRESENT) { "CP" } else { "__" }, - ) - } -} diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index adc6f8aaa..32951421b 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -3,7 +3,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use super::{Error, Event, Instance, InterruptHandler}; +use super::{Error, Instance, InterruptHandler}; use crate::{ interrupt::{self, typelevel::Interrupt}, pac, Peripheral, @@ -319,7 +319,6 @@ impl<'d, T: Instance> Radio<'d, T> { r.shorts.reset(); if r.events_framestart.read().events_framestart().bit_is_set() { // TODO: Is there a way to finish receiving this frame - trace!("EVENTS {}", Event::from_radio(r)); } r.tasks_stop.write(|w| w.tasks_stop().set_bit()); loop { diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 914e6c438..9b3a6cf49 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -7,13 +7,10 @@ /// Bluetooth Low Energy Radio driver. pub mod ble; -mod event; #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "_nrf5340-net"))] /// IEEE 802.15.4 pub mod ieee802154; -pub use event::Event; - use core::marker::PhantomData; use crate::{interrupt, pac, Peripheral}; From 047b20cee42e4438bcf054c4f94812c4895508e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Sun, 3 Mar 2024 01:12:08 +0100 Subject: [PATCH 05/12] Export Error in BLE module --- embassy-nrf/src/radio/ble.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index ecf8cc5eb..93c701de0 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -12,6 +12,7 @@ use pac::radio::state::STATE_A as RadioState; pub use pac::radio::txpower::TXPOWER_A as TxPower; use crate::interrupt::typelevel::Interrupt; +pub use crate::radio::Error; use crate::radio::*; use crate::util::slice_in_ram_or; From 0c4c996339ea7e70f40b0ad77a8850728ef9dcf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Sun, 3 Mar 2024 02:15:16 +0100 Subject: [PATCH 06/12] Fixed formatting --- embassy-nrf/src/radio/ieee802154.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 32951421b..91c6dbd3b 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -3,15 +3,15 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use super::{Error, Instance, InterruptHandler}; -use crate::{ - interrupt::{self, typelevel::Interrupt}, - pac, Peripheral, -}; -use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower}; - use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; +use pac::radio::state::STATE_A as RadioState; +use pac::radio::txpower::TXPOWER_A as TxPower; + +use super::{Error, Instance, InterruptHandler}; +use crate::interrupt::typelevel::Interrupt; +use crate::interrupt::{self}; +use crate::{pac, Peripheral}; /// Default Start of Frame Delimiter = `0xA7` (IEEE compliant) pub const DEFAULT_SFD: u8 = 0xA7; From c00f014f18e7ade176ed29cc06070466079d8268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Tue, 5 Mar 2024 01:03:10 +0100 Subject: [PATCH 07/12] Some more unifying, documentation --- embassy-nrf/src/radio/ble.rs | 14 +--- embassy-nrf/src/radio/ieee802154.rs | 109 ++++++++-------------------- embassy-nrf/src/radio/mod.rs | 28 +++++++ 3 files changed, 61 insertions(+), 90 deletions(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 93c701de0..846ac98af 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -8,8 +8,6 @@ use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; pub use pac::radio::mode::MODE_A as Mode; use pac::radio::pcnf0::PLEN_A as PreambleLength; -use pac::radio::state::STATE_A as RadioState; -pub use pac::radio::txpower::TXPOWER_A as TxPower; use crate::interrupt::typelevel::Interrupt; pub use crate::radio::Error; @@ -111,17 +109,7 @@ impl<'d, T: Instance> Radio<'d, T> { #[allow(dead_code)] fn trace_state(&self) { - match self.state() { - RadioState::DISABLED => trace!("radio:state:DISABLED"), - RadioState::RX_RU => trace!("radio:state:RX_RU"), - RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), - RadioState::RX => trace!("radio:state:RX"), - RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), - RadioState::TX_RU => trace!("radio:state:TX_RU"), - RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), - RadioState::TX => trace!("radio:state:TX"), - RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), - } + super::trace_state(T::regs()) } /// Set the radio mode diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 91c6dbd3b..2de53b392 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -1,19 +1,17 @@ -//! IEEE 802.15.4 radio +//! IEEE 802.15.4 radio driver use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; -use pac::radio::state::STATE_A as RadioState; -use pac::radio::txpower::TXPOWER_A as TxPower; -use super::{Error, Instance, InterruptHandler}; +use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower}; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; -use crate::{pac, Peripheral}; +use crate::Peripheral; -/// Default Start of Frame Delimiter = `0xA7` (IEEE compliant) +/// Default (IEEE compliant) Start of Frame Delimiter pub const DEFAULT_SFD: u8 = 0xA7; // TODO expose the other variants in `pac::CCAMODE_A` @@ -32,35 +30,14 @@ pub enum Cca { }, } -fn get_state(radio: &pac::radio::RegisterBlock) -> RadioState { - match radio.state.read().state().variant() { - Some(state) => state, - None => unreachable!(), - } -} - -fn trace_state(state: RadioState) { - match state { - RadioState::DISABLED => trace!("radio:state:DISABLED"), - RadioState::RX_RU => trace!("radio:state:RX_RU"), - RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), - RadioState::RX => trace!("radio:state:RX"), - RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), - RadioState::TX_RU => trace!("radio:state:TX_RU"), - RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), - RadioState::TX => trace!("radio:state:TX"), - RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), - } -} - -/// Radio driver. +/// IEEE 802.15.4 radio driver. pub struct Radio<'d, T: Instance> { _p: PeripheralRef<'d, T>, needs_enable: bool, } impl<'d, T: Instance> Radio<'d, T> { - /// Create a new radio driver. + /// Create a new IEEE 802.15.4 radio driver. pub fn new( radio: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, @@ -81,40 +58,43 @@ impl<'d, T: Instance> Radio<'d, T> { // Configure CRC polynomial and init r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021)); r.crcinit.write(|w| w.crcinit().bits(0)); - // Configure packet layout - // 8-bit on air length - // S0 length, zero bytes - // S1 length, zero bytes - // S1 included in RAM if S1 length > 0, No. - // Code Indicator length, 0 - // Preamble length 32-bit zero - // Exclude CRC - // No TERM field r.pcnf0.write(|w| { + // 8-bit on air length w.lflen() .bits(8) + // Zero bytes S0 field length .s0len() .clear_bit() + // Zero bytes S1 field length .s1len() .bits(0) + // Do not include S1 field in RAM if S1 length > 0 .s1incl() .clear_bit() + // Zero code Indicator length .cilen() .bits(0) + // 32-bit zero preamble .plen() ._32bit_zero() + // Include CRC in length .crcinc() .include() }); r.pcnf1.write(|w| { + // Maximum packet length w.maxlen() .bits(Packet::MAX_PSDU_LEN) + // Zero static length .statlen() .bits(0) + // Zero base address length .balen() .bits(0) + // Little-endian .endian() .clear_bit() + // Disable packet whitening .whiteen() .clear_bit() }); @@ -208,16 +188,9 @@ impl<'d, T: Instance> Radio<'d, T> { while self.state() != state {} } + /// Get the current radio state fn state(&self) -> RadioState { - let r = T::regs(); - match r.state.read().state().variant() { - Some(state) => state, - None => unreachable!(), - } - } - - fn trace_state(&self) { - trace_state(self.state()); + state(T::regs()) } /// Moves the radio from any state to the DISABLED state @@ -227,20 +200,17 @@ impl<'d, T: Instance> Radio<'d, T> { loop { match self.state() { RadioState::DISABLED => return, - + // idle or ramping up RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => { r.tasks_disable.write(|w| w.tasks_disable().set_bit()); - self.wait_for_radio_state(RadioState::DISABLED); return; } - // ramping down RadioState::RX_DISABLE | RadioState::TX_DISABLE => { self.wait_for_radio_state(RadioState::DISABLED); return; } - // cancel ongoing transfer or ongoing CCA RadioState::RX => { r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit()); @@ -262,35 +232,27 @@ impl<'d, T: Instance> Radio<'d, T> { /// Moves the radio to the RXIDLE state fn receive_prepare(&mut self) { - let state = self.state(); - - let disable = match state { + // clear related events + T::regs().events_ccabusy.reset(); + T::regs().events_phyend.reset(); + // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE + let disable = match self.state() { RadioState::DISABLED => false, - RadioState::RX_DISABLE => true, - RadioState::TX_DISABLE => true, RadioState::RX_IDLE => self.needs_enable, - // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE - RadioState::TX_IDLE => true, - _ => unreachable!(), + _ => true, }; if disable { - trace!("Receive Setup"); - self.trace_state(); self.disable(); } self.needs_enable = false; } + /// Prepare radio for receiving a packet fn receive_start(&mut self, packet: &mut Packet) { // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's // allocated in RAM let r = T::regs(); - // clear related events - r.events_framestart.reset(); - r.events_ccabusy.reset(); - r.events_phyend.reset(); - self.receive_prepare(); // Configure shortcuts @@ -314,15 +276,13 @@ impl<'d, T: Instance> Radio<'d, T> { } } + /// Cancel receiving packet fn receive_cancel() { let r = T::regs(); r.shorts.reset(); - if r.events_framestart.read().events_framestart().bit_is_set() { - // TODO: Is there a way to finish receiving this frame - } r.tasks_stop.write(|w| w.tasks_stop().set_bit()); loop { - match get_state(r) { + match state(r) { RadioState::DISABLED | RadioState::RX_IDLE => break, _ => (), } @@ -336,7 +296,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// This methods returns the `Ok` variant if the CRC included the packet was successfully /// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet` /// will be updated with the received packet's data - pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), u16> { + pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), Error> { let s = T::state(); let r = T::regs(); @@ -369,7 +329,7 @@ impl<'d, T: Instance> Radio<'d, T> { if r.crcstatus.read().crcstatus().bit_is_set() { Ok(()) } else { - Err(crc) + Err(Error::CrcFailed(crc)) } } @@ -387,11 +347,6 @@ impl<'d, T: Instance> Radio<'d, T> { let s = T::state(); let r = T::regs(); - // clear related events - r.events_framestart.reset(); - r.events_ccabusy.reset(); - r.events_phyend.reset(); - // enable radio to perform cca self.receive_prepare(); diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 9b3a6cf49..333dfb33d 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -15,6 +15,9 @@ use core::marker::PhantomData; use crate::{interrupt, pac, Peripheral}; +use pac::radio::state::STATE_A as RadioState; +use pac::radio::txpower::TXPOWER_A as TxPower; + /// RADIO error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -28,6 +31,8 @@ pub enum Error { BufferNotInRAM, /// Clear channel assessment reported channel in use ChannelInUse, + /// CRC check failed + CrcFailed(u16), } /// Interrupt handler @@ -89,3 +94,26 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } + +/// Get the state of the radio +pub(crate) fn state(radio: &pac::radio::RegisterBlock) -> RadioState { + match radio.state.read().state().variant() { + Some(state) => state, + None => unreachable!(), + } +} + +#[allow(dead_code)] +pub(crate) fn trace_state(radio: &pac::radio::RegisterBlock) { + match state(radio) { + RadioState::DISABLED => trace!("radio:state:DISABLED"), + RadioState::RX_RU => trace!("radio:state:RX_RU"), + RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), + RadioState::RX => trace!("radio:state:RX"), + RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), + RadioState::TX_RU => trace!("radio:state:TX_RU"), + RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), + RadioState::TX => trace!("radio:state:TX"), + RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), + } +} From 84935fbfab6a053113c135110ec4a1f4373ccfb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Tue, 5 Mar 2024 01:06:04 +0100 Subject: [PATCH 08/12] More formatting --- embassy-nrf/src/radio/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 333dfb33d..487e52d79 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -13,11 +13,11 @@ pub mod ieee802154; use core::marker::PhantomData; -use crate::{interrupt, pac, Peripheral}; - use pac::radio::state::STATE_A as RadioState; use pac::radio::txpower::TXPOWER_A as TxPower; +use crate::{interrupt, pac, Peripheral}; + /// RADIO error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From bc258b322b3828b5e52cba3e51c7de4ec014268e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Tue, 5 Mar 2024 11:39:15 +0100 Subject: [PATCH 09/12] Support nearly all nRF5 RADIOs --- embassy-nrf/src/chips/nrf51.rs | 5 ++++ embassy-nrf/src/chips/nrf52805.rs | 5 ++++ embassy-nrf/src/chips/nrf52810.rs | 5 ++++ embassy-nrf/src/chips/nrf52811.rs | 5 ++++ embassy-nrf/src/chips/nrf52820.rs | 5 ++++ embassy-nrf/src/chips/nrf52832.rs | 5 ++++ embassy-nrf/src/chips/nrf5340_net.rs | 5 ++++ embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/radio/ble.rs | 35 +++++++++++++++++++++++++++- embassy-nrf/src/radio/ieee802154.rs | 19 +++++++++++++++ embassy-nrf/src/radio/mod.rs | 7 +++++- 11 files changed, 95 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index 016352fb8..cc1cbc8a0 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs @@ -99,6 +99,9 @@ embassy_hal_internal::peripherals! { // TEMP TEMP, + + // Radio + RADIO, } impl_timer!(TIMER0, TIMER0, TIMER0); @@ -140,6 +143,8 @@ impl_pin!(P0_29, 0, 29); impl_pin!(P0_30, 0, 30); impl_pin!(P0_31, 0, 31); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 624d6613d..14c3f9b1a 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -129,6 +129,9 @@ embassy_hal_internal::peripherals! { // QDEC QDEC, + + // Radio + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -209,6 +212,8 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 002feab3b..c607586db 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! { // PDM PDM, + + // Radio + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -235,6 +238,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 5952907f8..5f70365b4 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! { // PDM PDM, + + // Radio + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -237,6 +240,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index c2f792cb9..82d097407 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -130,6 +130,9 @@ embassy_hal_internal::peripherals! { // QDEC QDEC, + + // Radio + RADIO, } impl_usb!(USBD, USBD, USBD); @@ -224,6 +227,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 65d52364d..67b32fe5f 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -150,6 +150,9 @@ embassy_hal_internal::peripherals! { // PDM PDM, + + // Radio + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -264,6 +267,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index a7cf82872..65e8f9653 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -248,6 +248,9 @@ embassy_hal_internal::peripherals! { P1_13, P1_14, P1_15, + + // Radio + RADIO, } impl_uarte!(SERIAL0, UARTE0, SERIAL0); @@ -345,6 +348,8 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( CLOCK_POWER, RADIO, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 132bffa8b..06a25a36d 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,7 +47,7 @@ pub mod gpio; pub mod gpiote; // TODO: tested on other chips -#[cfg(any(feature = "nrf52833", feature = "nrf52840"))] +#[cfg(not(any(feature = "nrf51", feature = "_nrf9160")))] pub mod radio; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 846ac98af..a306971b0 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -7,6 +7,7 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; pub use pac::radio::mode::MODE_A as Mode; +#[cfg(not(feature = "nrf51"))] use pac::radio::pcnf0::PLEN_A as PreambleLength; use crate::interrupt::typelevel::Interrupt; @@ -84,6 +85,7 @@ impl<'d, T: Instance> Radio<'d, T> { // Ch map between 2400 MHZ .. 2500 MHz // All modes use this range + #[cfg(not(feature = "nrf51"))] r.frequency.write(|w| w.map().default()); // Configure shortcuts to simplify and speed up sending and receiving packets. @@ -121,10 +123,18 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); r.mode.write(|w| w.mode().variant(mode)); + #[cfg(not(feature = "nrf51"))] r.pcnf0.write(|w| { w.plen().variant(match mode { Mode::BLE_1MBIT => PreambleLength::_8BIT, Mode::BLE_2MBIT => PreambleLength::_16BIT, + #[cfg(any( + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-net" + ))] Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE, _ => unimplemented!(), }) @@ -307,7 +317,11 @@ impl<'d, T: Instance> Radio<'d, T> { self.trigger_and_wait_end(move || { // Initialize the transmission // trace!("txen"); + + #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] r.tasks_txen.write(|w| w.tasks_txen().set_bit()); + #[cfg(any(feature = "nrf51", feature = "nrf52832"))] + r.tasks_txen.write(|w| unsafe { w.bits(1) }); }) .await; @@ -324,7 +338,10 @@ impl<'d, T: Instance> Radio<'d, T> { self.trigger_and_wait_end(move || { // Initialize the transmission // trace!("rxen"); + #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()); + #[cfg(any(feature = "nrf51", feature = "nrf52832"))] + r.tasks_rxen.write(|w| unsafe { w.bits(1) }); }) .await; @@ -346,10 +363,16 @@ impl<'d, T: Instance> Radio<'d, T> { r.intenclr.write(|w| w.end().clear()); r.events_end.reset(); + #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + #[cfg(any(feature = "nrf51", feature = "nrf52832"))] + r.tasks_stop.write(|w| unsafe { w.bits(1) }); // The docs don't explicitly mention any event to acknowledge the stop task + #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] while r.events_end.read().events_end().bit_is_clear() {} + #[cfg(any(feature = "nrf51", feature = "nrf52832"))] + while r.events_end.read().bits() == 0 {} trace!("radio drop: stopped"); }); @@ -370,7 +393,11 @@ impl<'d, T: Instance> Radio<'d, T> { // On poll check if interrupt happen poll_fn(|cx| { s.event_waker.register(cx.waker()); - if r.events_end.read().events_end().bit_is_set() { + #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] + let end_event = r.events_end.read().events_end().bit_is_set(); + #[cfg(any(feature = "nrf51", feature = "nrf52832"))] + let end_event = r.events_end.read().bits() == 1; + if end_event { // trace!("radio:end"); return core::task::Poll::Ready(()); } @@ -394,10 +421,16 @@ impl<'d, T: Instance> Radio<'d, T> { if self.state() != RadioState::DISABLED { trace!("radio:disable"); // Trigger the disable task + #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] r.tasks_disable.write(|w| w.tasks_disable().set_bit()); + #[cfg(any(feature = "nrf51", feature = "nrf52832"))] + r.tasks_disable.write(|w| unsafe { w.bits(1) }); // Wait until the radio is disabled + #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] while r.events_disabled.read().events_disabled().bit_is_clear() {} + #[cfg(any(feature = "nrf51", feature = "nrf52832"))] + while r.events_disabled.read().bits() == 0 {} compiler_fence(Ordering::SeqCst); diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 2de53b392..7bec4cb8c 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -162,15 +162,34 @@ impl<'d, T: Instance> Radio<'d, T> { self.needs_enable = true; let tx_power: TxPower = match power { + #[cfg(not(feature = "_nrf5340-net"))] 8 => TxPower::POS8D_BM, + #[cfg(not(feature = "_nrf5340-net"))] 7 => TxPower::POS7D_BM, + #[cfg(not(feature = "_nrf5340-net"))] 6 => TxPower::POS6D_BM, + #[cfg(not(feature = "_nrf5340-net"))] 5 => TxPower::POS5D_BM, + #[cfg(not(feature = "_nrf5340-net"))] 4 => TxPower::POS4D_BM, + #[cfg(not(feature = "_nrf5340-net"))] 3 => TxPower::POS3D_BM, + #[cfg(not(feature = "_nrf5340-net"))] 2 => TxPower::POS2D_BM, 0 => TxPower::_0D_BM, + #[cfg(feature = "_nrf5340-net")] + -1 => TxPower::NEG1D_BM, + #[cfg(feature = "_nrf5340-net")] + -2 => TxPower::NEG2D_BM, + #[cfg(feature = "_nrf5340-net")] + -3 => TxPower::NEG3D_BM, -4 => TxPower::NEG4D_BM, + #[cfg(feature = "_nrf5340-net")] + -5 => TxPower::NEG5D_BM, + #[cfg(feature = "_nrf5340-net")] + -6 => TxPower::NEG6D_BM, + #[cfg(feature = "_nrf5340-net")] + -7 => TxPower::NEG7D_BM, -8 => TxPower::NEG8D_BM, -12 => TxPower::NEG12D_BM, -16 => TxPower::NEG16D_BM, diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 487e52d79..adb8e1206 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -7,7 +7,12 @@ /// Bluetooth Low Energy Radio driver. pub mod ble; -#[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "_nrf5340-net"))] +#[cfg(any( + feature = "nrf52820", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-net" +))] /// IEEE 802.15.4 pub mod ieee802154; From 5408f21e993c0f94f04ed0c891b050d9a762289a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Tue, 5 Mar 2024 11:47:06 +0100 Subject: [PATCH 10/12] Fixed nrf51 radio build --- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/radio/mod.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 06a25a36d..e59a7bd5d 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,7 +47,7 @@ pub mod gpio; pub mod gpiote; // TODO: tested on other chips -#[cfg(not(any(feature = "nrf51", feature = "_nrf9160")))] +#[cfg(not(any(feature = "_nrf9160")))] pub mod radio; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index adb8e1206..5a2982d89 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -6,6 +6,7 @@ #![macro_use] /// Bluetooth Low Energy Radio driver. +#[cfg(not(feature = "nrf51"))] pub mod ble; #[cfg(any( feature = "nrf52820", @@ -19,6 +20,7 @@ pub mod ieee802154; use core::marker::PhantomData; use pac::radio::state::STATE_A as RadioState; +#[cfg(not(feature = "nrf51"))] use pac::radio::txpower::TXPOWER_A as TxPower; use crate::{interrupt, pac, Peripheral}; From 5b5d54c0c75e8a7fbe5370af4932c9845f4ce123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Tue, 5 Mar 2024 11:58:32 +0100 Subject: [PATCH 11/12] Do not build radio for nrf5340-app --- embassy-nrf/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index e59a7bd5d..718f229a3 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,7 +47,7 @@ pub mod gpio; pub mod gpiote; // TODO: tested on other chips -#[cfg(not(any(feature = "_nrf9160")))] +#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340-app")))] pub mod radio; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] From bb73d6b3fe81f26cb527d45bd700d2aef9b07090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Tue, 5 Mar 2024 15:01:05 +0100 Subject: [PATCH 12/12] Fixed suggestions, added nRF51 to BLE --- embassy-nrf/src/radio/ble.rs | 36 +++-------------------------- embassy-nrf/src/radio/ieee802154.rs | 10 ++++---- embassy-nrf/src/radio/mod.rs | 20 ++-------------- embassy-nrf/src/util.rs | 1 - 4 files changed, 10 insertions(+), 57 deletions(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index a306971b0..93003fb19 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -11,8 +11,8 @@ pub use pac::radio::mode::MODE_A as Mode; use pac::radio::pcnf0::PLEN_A as PreambleLength; use crate::interrupt::typelevel::Interrupt; -pub use crate::radio::Error; use crate::radio::*; +pub use crate::radio::{Error, TxPower}; use crate::util::slice_in_ram_or; /// Radio driver. @@ -103,15 +103,7 @@ impl<'d, T: Instance> Radio<'d, T> { } fn state(&self) -> RadioState { - match T::regs().state.read().state().variant() { - Some(s) => s, - None => unreachable!(), - } - } - - #[allow(dead_code)] - fn trace_state(&self) { - super::trace_state(T::regs()) + super::state(T::regs()) } /// Set the radio mode @@ -318,9 +310,6 @@ impl<'d, T: Instance> Radio<'d, T> { // Initialize the transmission // trace!("txen"); - #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] - r.tasks_txen.write(|w| w.tasks_txen().set_bit()); - #[cfg(any(feature = "nrf51", feature = "nrf52832"))] r.tasks_txen.write(|w| unsafe { w.bits(1) }); }) .await; @@ -338,9 +327,6 @@ impl<'d, T: Instance> Radio<'d, T> { self.trigger_and_wait_end(move || { // Initialize the transmission // trace!("rxen"); - #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] - r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()); - #[cfg(any(feature = "nrf51", feature = "nrf52832"))] r.tasks_rxen.write(|w| unsafe { w.bits(1) }); }) .await; @@ -363,15 +349,9 @@ impl<'d, T: Instance> Radio<'d, T> { r.intenclr.write(|w| w.end().clear()); r.events_end.reset(); - #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); - #[cfg(any(feature = "nrf51", feature = "nrf52832"))] r.tasks_stop.write(|w| unsafe { w.bits(1) }); // The docs don't explicitly mention any event to acknowledge the stop task - #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] - while r.events_end.read().events_end().bit_is_clear() {} - #[cfg(any(feature = "nrf51", feature = "nrf52832"))] while r.events_end.read().bits() == 0 {} trace!("radio drop: stopped"); @@ -393,11 +373,7 @@ impl<'d, T: Instance> Radio<'d, T> { // On poll check if interrupt happen poll_fn(|cx| { s.event_waker.register(cx.waker()); - #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] - let end_event = r.events_end.read().events_end().bit_is_set(); - #[cfg(any(feature = "nrf51", feature = "nrf52832"))] - let end_event = r.events_end.read().bits() == 1; - if end_event { + if r.events_end.read().bits() == 1 { // trace!("radio:end"); return core::task::Poll::Ready(()); } @@ -421,15 +397,9 @@ impl<'d, T: Instance> Radio<'d, T> { if self.state() != RadioState::DISABLED { trace!("radio:disable"); // Trigger the disable task - #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] - r.tasks_disable.write(|w| w.tasks_disable().set_bit()); - #[cfg(any(feature = "nrf51", feature = "nrf52832"))] r.tasks_disable.write(|w| unsafe { w.bits(1) }); // Wait until the radio is disabled - #[cfg(not(any(feature = "nrf51", feature = "nrf52832")))] - while r.events_disabled.read().events_disabled().bit_is_clear() {} - #[cfg(any(feature = "nrf51", feature = "nrf52832"))] while r.events_disabled.read().bits() == 0 {} compiler_fence(Ordering::SeqCst); diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 7bec4cb8c..298f8a574 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -162,19 +162,19 @@ impl<'d, T: Instance> Radio<'d, T> { self.needs_enable = true; let tx_power: TxPower = match power { - #[cfg(not(feature = "_nrf5340-net"))] + #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] 8 => TxPower::POS8D_BM, - #[cfg(not(feature = "_nrf5340-net"))] + #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] 7 => TxPower::POS7D_BM, - #[cfg(not(feature = "_nrf5340-net"))] + #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] 6 => TxPower::POS6D_BM, - #[cfg(not(feature = "_nrf5340-net"))] + #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] 5 => TxPower::POS5D_BM, #[cfg(not(feature = "_nrf5340-net"))] 4 => TxPower::POS4D_BM, #[cfg(not(feature = "_nrf5340-net"))] 3 => TxPower::POS3D_BM, - #[cfg(not(feature = "_nrf5340-net"))] + #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] 2 => TxPower::POS2D_BM, 0 => TxPower::_0D_BM, #[cfg(feature = "_nrf5340-net")] diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 5a2982d89..4c0cc3280 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -6,9 +6,9 @@ #![macro_use] /// Bluetooth Low Energy Radio driver. -#[cfg(not(feature = "nrf51"))] pub mod ble; #[cfg(any( + feature = "nrf52811", feature = "nrf52820", feature = "nrf52833", feature = "nrf52840", @@ -20,8 +20,7 @@ pub mod ieee802154; use core::marker::PhantomData; use pac::radio::state::STATE_A as RadioState; -#[cfg(not(feature = "nrf51"))] -use pac::radio::txpower::TXPOWER_A as TxPower; +pub use pac::radio::txpower::TXPOWER_A as TxPower; use crate::{interrupt, pac, Peripheral}; @@ -109,18 +108,3 @@ pub(crate) fn state(radio: &pac::radio::RegisterBlock) -> RadioState { None => unreachable!(), } } - -#[allow(dead_code)] -pub(crate) fn trace_state(radio: &pac::radio::RegisterBlock) { - match state(radio) { - RadioState::DISABLED => trace!("radio:state:DISABLED"), - RadioState::RX_RU => trace!("radio:state:RX_RU"), - RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), - RadioState::RX => trace!("radio:state:RX"), - RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), - RadioState::TX_RU => trace!("radio:state:TX_RU"), - RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), - RadioState::TX => trace!("radio:state:TX"), - RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), - } -} diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs index 6cdb97f08..13aba7dec 100644 --- a/embassy-nrf/src/util.rs +++ b/embassy-nrf/src/util.rs @@ -34,7 +34,6 @@ pub(crate) fn slice_in_ram(slice: *const [T]) -> bool { } /// Return an error if slice is not in RAM. Skips check if slice is zero-length. -#[cfg(not(feature = "nrf51"))] pub(crate) fn slice_in_ram_or(slice: *const [T], err: E) -> Result<(), E> { let (_, len) = slice_ptr_parts(slice); if len == 0 || slice_in_ram(slice) {