From 15c3e78408a2835003b9d7dee0b7fbae544d10ba Mon Sep 17 00:00:00 2001 From: Thales Fragoso Date: Sun, 7 Mar 2021 20:15:40 -0300 Subject: Move nRF's util into a separate crate --- Cargo.toml | 1 + embassy-extras/Cargo.toml | 19 ++++++ embassy-extras/src/fmt.rs | 119 +++++++++++++++++++++++++++++++++++ embassy-extras/src/lib.rs | 17 +++++ embassy-extras/src/peripheral.rs | 119 +++++++++++++++++++++++++++++++++++ embassy-extras/src/ring_buffer.rs | 80 ++++++++++++++++++++++++ embassy-nrf/Cargo.toml | 1 + embassy-nrf/src/buffered_uarte.rs | 8 +-- embassy-nrf/src/lib.rs | 1 - embassy-nrf/src/qspi.rs | 3 +- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/uarte.rs | 6 +- embassy-nrf/src/util/mod.rs | 12 ---- embassy-nrf/src/util/peripheral.rs | 120 ------------------------------------ embassy-nrf/src/util/ring_buffer.rs | 80 ------------------------ 15 files changed, 366 insertions(+), 222 deletions(-) create mode 100644 embassy-extras/Cargo.toml create mode 100644 embassy-extras/src/fmt.rs create mode 100644 embassy-extras/src/lib.rs create mode 100644 embassy-extras/src/peripheral.rs create mode 100644 embassy-extras/src/ring_buffer.rs delete mode 100644 embassy-nrf/src/util/mod.rs delete mode 100644 embassy-nrf/src/util/peripheral.rs delete mode 100644 embassy-nrf/src/util/ring_buffer.rs diff --git a/Cargo.toml b/Cargo.toml index 80ae93745..069437317 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "embassy-nrf-examples", "embassy-stm32f4-examples", "embassy-macros", + "embassy-extras", ] # embassy-std enables std-only features. Since Cargo resolves all features diff --git a/embassy-extras/Cargo.toml b/embassy-extras/Cargo.toml new file mode 100644 index 000000000..3c42b5c2f --- /dev/null +++ b/embassy-extras/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "embassy-extras" +version = "0.1.0" +authors = ["Dario Nieuwenhuis "] +edition = "2018" + +[features] +defmt-trace = [ ] +defmt-debug = [ ] +defmt-info = [ ] +defmt-warn = [ ] +defmt-error = [ ] + +[dependencies] +embassy = { version = "0.1.0", path = "../embassy" } + +defmt = { version = "0.2.0", optional = true } +log = { version = "0.4.11", optional = true } +cortex-m = "0.7.1" diff --git a/embassy-extras/src/fmt.rs b/embassy-extras/src/fmt.rs new file mode 100644 index 000000000..1be1057a7 --- /dev/null +++ b/embassy-extras/src/fmt.rs @@ -0,0 +1,119 @@ +#![macro_use] +#![allow(clippy::module_inception)] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +pub use fmt::*; + +#[cfg(feature = "defmt")] +mod fmt { + pub use defmt::{ + assert, assert_eq, assert_ne, debug, debug_assert, debug_assert_eq, debug_assert_ne, error, + info, panic, todo, trace, unreachable, unwrap, warn, + }; +} + +#[cfg(feature = "log")] +mod fmt { + pub use core::{ + assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo, + unreachable, + }; + pub use log::{debug, error, info, trace, warn}; +} + +#[cfg(not(any(feature = "defmt", feature = "log")))] +mod fmt { + #![macro_use] + + pub use core::{ + assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo, + unreachable, + }; + + #[macro_export] + macro_rules! trace { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! debug { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! info { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! warn { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! error { + ($($msg:expr),+ $(,)?) => { + () + }; + } +} + +#[cfg(not(feature = "defmt"))] +#[macro_export] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-extras/src/lib.rs b/embassy-extras/src/lib.rs new file mode 100644 index 000000000..4a95173cf --- /dev/null +++ b/embassy-extras/src/lib.rs @@ -0,0 +1,17 @@ +#![no_std] + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod peripheral; +pub mod ring_buffer; + +/// Low power blocking wait loop using WFE/SEV. +pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { + while !condition() { + // WFE might "eat" an event that would have otherwise woken the executor. + cortex_m::asm::wfe(); + } + // Retrigger an event to be transparent to the executor. + cortex_m::asm::sev(); +} diff --git a/embassy-extras/src/peripheral.rs b/embassy-extras/src/peripheral.rs new file mode 100644 index 000000000..e9357969d --- /dev/null +++ b/embassy-extras/src/peripheral.rs @@ -0,0 +1,119 @@ +use core::cell::UnsafeCell; +use core::marker::{PhantomData, PhantomPinned}; +use core::mem::MaybeUninit; +use core::pin::Pin; +use core::sync::atomic::{compiler_fence, Ordering}; + +use embassy::interrupt::{Interrupt, InterruptExt}; + +use crate::fmt::assert; + +pub trait PeripheralState { + type Interrupt: Interrupt; + fn on_interrupt(&mut self); +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum Life { + Ready, + Created, + Freed, +} + +pub struct PeripheralMutex { + life: Life, + + state: MaybeUninit>, // Init if life != Freed + irq: MaybeUninit, // Init if life != Freed + + _not_send: PhantomData<*mut ()>, + _pinned: PhantomPinned, +} + +impl PeripheralMutex { + pub fn new(state: S, irq: S::Interrupt) -> Self { + Self { + life: Life::Created, + state: MaybeUninit::new(UnsafeCell::new(state)), + irq: MaybeUninit::new(irq), + _not_send: PhantomData, + _pinned: PhantomPinned, + } + } + + /// safety: self must be pinned. + unsafe fn setup(&mut self) { + assert!(self.life == Life::Created); + + let irq = &mut *self.irq.as_mut_ptr(); + irq.disable(); + compiler_fence(Ordering::SeqCst); + + irq.set_handler(|p| { + // Safety: it's OK to get a &mut to the state, since + // - We're in the IRQ, no one else can't preempt us + // - We can't have preempted a with() call because the irq is disabled during it. + let state = &mut *(p as *mut S); + state.on_interrupt(); + }); + irq.set_handler_context(self.state.as_mut_ptr() as *mut ()); + + compiler_fence(Ordering::SeqCst); + irq.enable(); + + self.life = Life::Ready; + } + + pub fn with(self: Pin<&mut Self>, f: impl FnOnce(&mut S, &mut S::Interrupt) -> R) -> R { + let this = unsafe { self.get_unchecked_mut() }; + if this.life != Life::Ready { + unsafe { this.setup() } + } + + let irq = unsafe { &mut *this.irq.as_mut_ptr() }; + + irq.disable(); + compiler_fence(Ordering::SeqCst); + + // Safety: it's OK to get a &mut to the state, since the irq is disabled. + let state = unsafe { &mut *(*this.state.as_ptr()).get() }; + + let r = f(state, irq); + + compiler_fence(Ordering::SeqCst); + irq.enable(); + + r + } + + pub fn try_free(self: Pin<&mut Self>) -> Option<(S, S::Interrupt)> { + let this = unsafe { self.get_unchecked_mut() }; + + if this.life != Life::Freed { + return None; + } + + unsafe { &mut *this.irq.as_mut_ptr() }.disable(); + compiler_fence(Ordering::SeqCst); + + this.life = Life::Freed; + + let state = unsafe { this.state.as_ptr().read().into_inner() }; + let irq = unsafe { this.irq.as_ptr().read() }; + Some((state, irq)) + } + + pub fn free(self: Pin<&mut Self>) -> (S, S::Interrupt) { + unwrap!(self.try_free()) + } +} + +impl Drop for PeripheralMutex { + fn drop(&mut self) { + if self.life != Life::Freed { + let irq = unsafe { &mut *self.irq.as_mut_ptr() }; + irq.disable(); + irq.remove_handler(); + } + } +} diff --git a/embassy-extras/src/ring_buffer.rs b/embassy-extras/src/ring_buffer.rs new file mode 100644 index 000000000..f2b9f7359 --- /dev/null +++ b/embassy-extras/src/ring_buffer.rs @@ -0,0 +1,80 @@ +use crate::fmt::{assert, *}; + +pub struct RingBuffer<'a> { + buf: &'a mut [u8], + start: usize, + end: usize, + empty: bool, +} + +impl<'a> RingBuffer<'a> { + pub fn new(buf: &'a mut [u8]) -> Self { + Self { + buf, + start: 0, + end: 0, + empty: true, + } + } + + pub fn push_buf(&mut self) -> &mut [u8] { + if self.start == self.end && !self.empty { + trace!(" ringbuf: push_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.start <= self.end { + self.buf.len() - self.end + } else { + self.start - self.end + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); + &mut self.buf[self.end..self.end + n] + } + + pub fn push(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + if n == 0 { + return; + } + + self.end = self.wrap(self.end + n); + self.empty = false; + } + + pub fn pop_buf(&mut self) -> &mut [u8] { + if self.empty { + trace!(" ringbuf: pop_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.end <= self.start { + self.buf.len() - self.start + } else { + self.end - self.start + }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); + &mut self.buf[self.start..self.start + n] + } + + pub fn pop(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + if n == 0 { + return; + } + + self.start = self.wrap(self.start + n); + self.empty = self.start == self.end; + } + + fn wrap(&self, n: usize) -> usize { + assert!(n <= self.buf.len()); + if n == self.buf.len() { + 0 + } else { + n + } + } +} diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 419b7f33f..026e815f7 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -20,6 +20,7 @@ defmt-error = [ ] [dependencies] embassy = { version = "0.1.0", path = "../embassy" } +embassy-extras = {version = "0.1.0", path = "../embassy-extras" } defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 848687f7b..b1366cd9e 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -13,14 +13,15 @@ use core::task::{Context, Poll}; use embassy::interrupt::InterruptExt; use embassy::io::{AsyncBufRead, AsyncWrite, Result}; use embassy::util::WakerRegistration; +use embassy_extras::low_power_wait_until; +use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; +use embassy_extras::ring_buffer::RingBuffer; use embedded_hal::digital::v2::OutputPin; +use crate::fmt::*; use crate::hal::ppi::ConfigurablePpi; use crate::interrupt::{self, Interrupt}; use crate::pac; -use crate::util::peripheral::{PeripheralMutex, PeripheralState}; -use crate::util::ring_buffer::RingBuffer; -use crate::{fmt::*, util::low_power_wait_until}; // Re-export SVD variants to allow user to directly set values pub use crate::hal::uarte::Pins; @@ -116,7 +117,6 @@ impl<'a, U: Instance, T: TimerInstance, P1: ConfigurablePpi, P2: ConfigurablePpi } }); - // Enable UARTE instance uarte.enable.write(|w| w.enable().enabled()); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 3de6299e9..bb37ec367 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -90,7 +90,6 @@ pub(crate) fn slice_in_ram_or(slice: &[u8], err: T) -> Result<(), T> { // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub(crate) mod util; pub mod buffered_uarte; pub mod gpiote; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 1dc178f26..f6e8175fa 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -2,6 +2,8 @@ use core::future::Future; use core::pin::Pin; use core::task::Poll; +use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; + use crate::fmt::{assert, assert_eq, *}; use crate::hal::gpio::{Output, Pin as GpioPin, PushPull}; use crate::interrupt::{self}; @@ -11,7 +13,6 @@ pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; pub use crate::pac::qspi::ifconfig0::PPSIZE_A as WritePageSize; pub use crate::pac::qspi::ifconfig0::READOC_A as ReadOpcode; pub use crate::pac::qspi::ifconfig0::WRITEOC_A as WriteOpcode; -use crate::util::peripheral::{PeripheralMutex, PeripheralState}; // TODO // - config: diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index b72329631..4921bae3b 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -3,10 +3,10 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy::util::WakerRegistration; +use embassy_extras::peripheral::{PeripheralMutex, PeripheralState}; use futures::future::poll_fn; use crate::interrupt::{self, Interrupt}; -use crate::util::peripheral::{PeripheralMutex, PeripheralState}; use crate::{pac, slice_in_ram_or}; pub use crate::hal::spim::{ diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index aea01f95c..b5e4da862 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -16,8 +16,8 @@ use crate::fmt::{assert, *}; use crate::hal::pac; use crate::hal::prelude::*; use crate::hal::target_constants::EASY_DMA_SIZE; +use crate::interrupt; use crate::interrupt::Interrupt; -use crate::{interrupt, util}; pub use crate::hal::uarte::Pins; // Re-export SVD variants to allow user to directly set values. @@ -45,7 +45,7 @@ where /// Creates the interface to a UARTE instance. /// Sets the baud rate, parity and assigns the pins to the UARTE peripheral. /// - /// # Unsafe + /// # Safety /// /// The returned API is safe unless you use `mem::forget` (or similar safe mechanisms) /// on stack allocated buffers which which have been passed to [`send()`](Uarte::send) @@ -327,7 +327,7 @@ where .tasks_stoprx .write(|w| unsafe { w.bits(1) }); - util::low_power_wait_until(|| T::state().rx_done.signaled()) + embassy_extras::low_power_wait_until(|| T::state().rx_done.signaled()) } } } diff --git a/embassy-nrf/src/util/mod.rs b/embassy-nrf/src/util/mod.rs deleted file mode 100644 index cf3306545..000000000 --- a/embassy-nrf/src/util/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod peripheral; -pub mod ring_buffer; - -/// Low power blocking wait loop using WFE/SEV. -pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { - while !condition() { - // WFE might "eat" an event that would have otherwise woken the executor. - cortex_m::asm::wfe(); - } - // Retrigger an event to be transparent to the executor. - cortex_m::asm::sev(); -} diff --git a/embassy-nrf/src/util/peripheral.rs b/embassy-nrf/src/util/peripheral.rs deleted file mode 100644 index bb88f0820..000000000 --- a/embassy-nrf/src/util/peripheral.rs +++ /dev/null @@ -1,120 +0,0 @@ -use core::cell::UnsafeCell; -use core::marker::{PhantomData, PhantomPinned}; -use core::mem::MaybeUninit; -use core::pin::Pin; -use core::sync::atomic::{compiler_fence, Ordering}; - -use embassy::interrupt::InterruptExt; - -use crate::fmt::{assert, *}; -use crate::interrupt::Interrupt; - -pub trait PeripheralState { - type Interrupt: Interrupt; - fn on_interrupt(&mut self); -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum Life { - Ready, - Created, - Freed, -} - -pub struct PeripheralMutex { - life: Life, - - state: MaybeUninit>, // Init if life != Freed - irq: MaybeUninit, // Init if life != Freed - - _not_send: PhantomData<*mut ()>, - _pinned: PhantomPinned, -} - -impl PeripheralMutex { - pub fn new(state: S, irq: S::Interrupt) -> Self { - Self { - life: Life::Created, - state: MaybeUninit::new(UnsafeCell::new(state)), - irq: MaybeUninit::new(irq), - _not_send: PhantomData, - _pinned: PhantomPinned, - } - } - - /// safety: self must be pinned. - unsafe fn setup(&mut self) { - assert!(self.life == Life::Created); - - let irq = &mut *self.irq.as_mut_ptr(); - irq.disable(); - compiler_fence(Ordering::SeqCst); - - irq.set_handler(|p| { - // Safety: it's OK to get a &mut to the state, since - // - We're in the IRQ, no one else can't preempt us - // - We can't have preempted a with() call because the irq is disabled during it. - let state = &mut *(p as *mut S); - state.on_interrupt(); - }); - irq.set_handler_context(self.state.as_mut_ptr() as *mut ()); - - compiler_fence(Ordering::SeqCst); - irq.enable(); - - self.life = Life::Ready; - } - - pub fn with(self: Pin<&mut Self>, f: impl FnOnce(&mut S, &mut S::Interrupt) -> R) -> R { - let this = unsafe { self.get_unchecked_mut() }; - if this.life != Life::Ready { - unsafe { this.setup() } - } - - let irq = unsafe { &mut *this.irq.as_mut_ptr() }; - - irq.disable(); - compiler_fence(Ordering::SeqCst); - - // Safety: it's OK to get a &mut to the state, since the irq is disabled. - let state = unsafe { &mut *(*this.state.as_ptr()).get() }; - - let r = f(state, irq); - - compiler_fence(Ordering::SeqCst); - irq.enable(); - - r - } - - pub fn try_free(self: Pin<&mut Self>) -> Option<(S, S::Interrupt)> { - let this = unsafe { self.get_unchecked_mut() }; - - if this.life != Life::Freed { - return None; - } - - unsafe { &mut *this.irq.as_mut_ptr() }.disable(); - compiler_fence(Ordering::SeqCst); - - this.life = Life::Freed; - - let state = unsafe { this.state.as_ptr().read().into_inner() }; - let irq = unsafe { this.irq.as_ptr().read() }; - Some((state, irq)) - } - - pub fn free(self: Pin<&mut Self>) -> (S, S::Interrupt) { - unwrap!(self.try_free()) - } -} - -impl Drop for PeripheralMutex { - fn drop(&mut self) { - if self.life != Life::Freed { - let irq = unsafe { &mut *self.irq.as_mut_ptr() }; - irq.disable(); - irq.remove_handler(); - } - } -} diff --git a/embassy-nrf/src/util/ring_buffer.rs b/embassy-nrf/src/util/ring_buffer.rs deleted file mode 100644 index f2b9f7359..000000000 --- a/embassy-nrf/src/util/ring_buffer.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::fmt::{assert, *}; - -pub struct RingBuffer<'a> { - buf: &'a mut [u8], - start: usize, - end: usize, - empty: bool, -} - -impl<'a> RingBuffer<'a> { - pub fn new(buf: &'a mut [u8]) -> Self { - Self { - buf, - start: 0, - end: 0, - empty: true, - } - } - - pub fn push_buf(&mut self) -> &mut [u8] { - if self.start == self.end && !self.empty { - trace!(" ringbuf: push_buf empty"); - return &mut self.buf[..0]; - } - - let n = if self.start <= self.end { - self.buf.len() - self.end - } else { - self.start - self.end - }; - - trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); - &mut self.buf[self.end..self.end + n] - } - - pub fn push(&mut self, n: usize) { - trace!(" ringbuf: push {:?}", n); - if n == 0 { - return; - } - - self.end = self.wrap(self.end + n); - self.empty = false; - } - - pub fn pop_buf(&mut self) -> &mut [u8] { - if self.empty { - trace!(" ringbuf: pop_buf empty"); - return &mut self.buf[..0]; - } - - let n = if self.end <= self.start { - self.buf.len() - self.start - } else { - self.end - self.start - }; - - trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); - &mut self.buf[self.start..self.start + n] - } - - pub fn pop(&mut self, n: usize) { - trace!(" ringbuf: pop {:?}", n); - if n == 0 { - return; - } - - self.start = self.wrap(self.start + n); - self.empty = self.start == self.end; - } - - fn wrap(&self, n: usize) -> usize { - assert!(n <= self.buf.len()); - if n == self.buf.len() { - 0 - } else { - n - } - } -} -- cgit