From 79269570674ae0778fe4593037ec46bf46a3f06e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 23 Dec 2021 13:34:15 +0100 Subject: Use cortex-m only on cortex-m archs. Without this, build fails for iOS. --- embassy/Cargo.toml | 17 ++++- embassy/src/blocking_mutex/kind.rs | 13 ++-- embassy/src/blocking_mutex/mod.rs | 121 ++++++++++++++++-------------- embassy/src/executor/arch/arm.rs | 137 ---------------------------------- embassy/src/executor/arch/cortex_m.rs | 137 ++++++++++++++++++++++++++++++++++ embassy/src/executor/mod.rs | 25 +++++-- embassy/src/lib.rs | 1 + 7 files changed, 244 insertions(+), 207 deletions(-) delete mode 100644 embassy/src/executor/arch/arm.rs create mode 100644 embassy/src/executor/arch/cortex_m.rs diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index bbe657f40..611c35eed 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml @@ -29,7 +29,6 @@ executor-agnostic = [] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -cortex-m = "0.7.3" futures = { version = "0.3.17", default-features = false, features = [ "cfg-target-has-atomic", "unstable" ] } pin-project = { version = "1.0.8", default-features = false } embassy-macros = { version = "0.1.0", path = "../embassy-macros"} @@ -38,12 +37,28 @@ atomic-polyfill = "0.1.5" critical-section = "0.2.5" embedded-hal = "0.2.6" heapless = "0.7.5" +cfg-if = "1.0.0" # WASM dependencies wasm-bindgen = { version = "0.2.76", features = ["nightly"], optional = true } js-sys = { version = "0.3", optional = true } wasm-timer = { version = "0.2.5", optional = true } +[target."thumbv6m-none-eabi".dependencies] +cortex-m = "0.7.3" +[target."thumbv7m-none-eabi".dependencies] +cortex-m = "0.7.3" +[target."thumbv7em-none-eabi".dependencies] +cortex-m = "0.7.3" +[target."thumbv7em-none-eabihf".dependencies] +cortex-m = "0.7.3" +[target."thumbv8m.base-none-eabi".dependencies] +cortex-m = "0.7.3" +[target."thumbv8m.main-none-eabi".dependencies] +cortex-m = "0.7.3" +[target."thumbv8m.main-none-eabihf".dependencies] +cortex-m = "0.7.3" + [dev-dependencies] embassy = { path = ".", features = ["executor-agnostic"] } futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy/src/blocking_mutex/kind.rs b/embassy/src/blocking_mutex/kind.rs index 30fc90497..a4a45605f 100644 --- a/embassy/src/blocking_mutex/kind.rs +++ b/embassy/src/blocking_mutex/kind.rs @@ -1,19 +1,20 @@ -use super::{CriticalSectionMutex, Mutex, NoopMutex, ThreadModeMutex}; - pub trait MutexKind { - type Mutex: Mutex; + type Mutex: super::Mutex; } pub enum CriticalSection {} impl MutexKind for CriticalSection { - type Mutex = CriticalSectionMutex; + type Mutex = super::CriticalSectionMutex; } +#[cfg(any(cortex_m, feature = "std"))] pub enum ThreadMode {} +#[cfg(any(cortex_m, feature = "std"))] impl MutexKind for ThreadMode { - type Mutex = ThreadModeMutex; + type Mutex = super::ThreadModeMutex; } + pub enum Noop {} impl MutexKind for Noop { - type Mutex = NoopMutex; + type Mutex = super::NoopMutex; } diff --git a/embassy/src/blocking_mutex/mod.rs b/embassy/src/blocking_mutex/mod.rs index 8ada27cb3..949531392 100644 --- a/embassy/src/blocking_mutex/mod.rs +++ b/embassy/src/blocking_mutex/mod.rs @@ -62,75 +62,82 @@ impl Mutex for CriticalSectionMutex { } } -/// A "mutex" that only allows borrowing from thread mode. -/// -/// # Safety -/// -/// **This Mutex is only safe on single-core systems.** -/// -/// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access. -pub struct ThreadModeMutex { - inner: UnsafeCell, -} - -// NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode. -// Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can -// be Send+Sync even if T is not Send (unlike CriticalSectionMutex) -unsafe impl Sync for ThreadModeMutex {} -unsafe impl Send for ThreadModeMutex {} +#[cfg(any(cortex_m, feature = "std"))] +pub use thread_mode_mutex::*; +#[cfg(any(cortex_m, feature = "std"))] +mod thread_mode_mutex { + use super::*; + + /// A "mutex" that only allows borrowing from thread mode. + /// + /// # Safety + /// + /// **This Mutex is only safe on single-core systems.** + /// + /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access. + pub struct ThreadModeMutex { + inner: UnsafeCell, + } -impl ThreadModeMutex { - /// Creates a new mutex - pub const fn new(value: T) -> Self { - ThreadModeMutex { - inner: UnsafeCell::new(value), + // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode. + // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can + // be Send+Sync even if T is not Send (unlike CriticalSectionMutex) + unsafe impl Sync for ThreadModeMutex {} + unsafe impl Send for ThreadModeMutex {} + + impl ThreadModeMutex { + /// Creates a new mutex + pub const fn new(value: T) -> Self { + ThreadModeMutex { + inner: UnsafeCell::new(value), + } } - } - /// Borrows the data - pub fn borrow(&self) -> &T { - assert!( - in_thread_mode(), - "ThreadModeMutex can only be borrowed from thread mode." - ); - unsafe { &*self.inner.get() } + /// Borrows the data + pub fn borrow(&self) -> &T { + assert!( + in_thread_mode(), + "ThreadModeMutex can only be borrowed from thread mode." + ); + unsafe { &*self.inner.get() } + } } -} -impl Mutex for ThreadModeMutex { - type Data = T; + impl Mutex for ThreadModeMutex { + type Data = T; - fn new(data: T) -> Self { - Self::new(data) - } + fn new(data: T) -> Self { + Self::new(data) + } - fn lock(&self, f: impl FnOnce(&Self::Data) -> R) -> R { - f(self.borrow()) + fn lock(&self, f: impl FnOnce(&Self::Data) -> R) -> R { + f(self.borrow()) + } } -} -impl Drop for ThreadModeMutex { - fn drop(&mut self) { - // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so - // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if - // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, - // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. - assert!( - in_thread_mode(), - "ThreadModeMutex can only be dropped from thread mode." - ); - - // Drop of the inner `T` happens after this. + impl Drop for ThreadModeMutex { + fn drop(&mut self) { + // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so + // `drop` needs the same guarantees as `lock`. `ThreadModeMutex` is Send even if + // T isn't, so without this check a user could create a ThreadModeMutex in thread mode, + // send it to interrupt context and drop it there, which would "send" a T even if T is not Send. + assert!( + in_thread_mode(), + "ThreadModeMutex can only be dropped from thread mode." + ); + + // Drop of the inner `T` happens after this. + } } -} -pub fn in_thread_mode() -> bool { - #[cfg(feature = "std")] - return Some("main") == std::thread::current().name(); + pub fn in_thread_mode() -> bool { + #[cfg(feature = "std")] + return Some("main") == std::thread::current().name(); - #[cfg(not(feature = "std"))] - return cortex_m::peripheral::SCB::vect_active() - == cortex_m::peripheral::scb::VectActive::ThreadMode; + #[cfg(not(feature = "std"))] + return cortex_m::peripheral::SCB::vect_active() + == cortex_m::peripheral::scb::VectActive::ThreadMode; + } } /// A "mutex" that does nothing and cannot be shared between threads. diff --git a/embassy/src/executor/arch/arm.rs b/embassy/src/executor/arch/arm.rs deleted file mode 100644 index d23a595ff..000000000 --- a/embassy/src/executor/arch/arm.rs +++ /dev/null @@ -1,137 +0,0 @@ -use core::marker::PhantomData; -use core::ptr; - -use super::{raw, Spawner}; -use crate::interrupt::{Interrupt, InterruptExt}; - -/// Thread mode executor, using WFE/SEV. -/// -/// This is the simplest and most common kind of executor. It runs on -/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction -/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction -/// is executed, to make the `WFE` exit from sleep and poll the task. -/// -/// This executor allows for ultra low power consumption for chips where `WFE` -/// triggers low-power sleep without extra steps. If your chip requires extra steps, -/// you may use [`raw::Executor`] directly to program custom behavior. -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()), - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [Forever](crate::util::Forever) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { self.inner.poll() }; - cortex_m::asm::wfe(); - } - } -} - -fn pend_by_number(n: u16) { - #[derive(Clone, Copy)] - struct N(u16); - unsafe impl cortex_m::interrupt::InterruptNumber for N { - fn number(self) -> u16 { - self.0 - } - } - cortex_m::peripheral::NVIC::pend(N(n)) -} - -/// Interrupt mode executor. -/// -/// This executor runs tasks in interrupt mode. The interrupt handler is set up -/// to poll tasks, and when a task is woken the interrupt is pended from software. -/// -/// This allows running async tasks at a priority higher than thread mode. One -/// use case is to leave thread mode free for non-async tasks. Another use case is -/// to run multiple executors: one in thread mode for low priority tasks and another in -/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower -/// priority ones. -/// -/// It is even possible to run multiple interrupt mode executors at different priorities, -/// by assigning different priorities to the interrupts. For an example on how to do this, -/// See the 'multiprio' example for 'embassy-nrf'. -/// -/// To use it, you have to pick an interrupt that won't be used by the hardware. -/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). -/// If this is not the case, you may use an interrupt from any unused peripheral. -/// -/// It is somewhat more complex to use, it's recommended to use the thread-mode -/// [`Executor`] instead, if it works for your use case. -pub struct InterruptExecutor { - irq: I, - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl InterruptExecutor { - /// Create a new Executor. - pub fn new(irq: I) -> Self { - let ctx = irq.number() as *mut (); - Self { - irq, - inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), - not_send: PhantomData, - } - } - - /// Start the executor. - /// - /// The `init` closure is called from interrupt mode, with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the interrupt is configured so that the executor starts running the tasks. - /// Once the executor is started, `start` returns. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [Forever](crate::util::Forever) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { - self.irq.disable(); - - init(self.inner.spawner()); - - self.irq.set_handler(|ctx| unsafe { - let executor = &*(ctx as *const raw::Executor); - executor.poll(); - }); - self.irq.set_handler_context(&self.inner as *const _ as _); - self.irq.enable(); - } -} diff --git a/embassy/src/executor/arch/cortex_m.rs b/embassy/src/executor/arch/cortex_m.rs new file mode 100644 index 000000000..d23a595ff --- /dev/null +++ b/embassy/src/executor/arch/cortex_m.rs @@ -0,0 +1,137 @@ +use core::marker::PhantomData; +use core::ptr; + +use super::{raw, Spawner}; +use crate::interrupt::{Interrupt, InterruptExt}; + +/// Thread mode executor, using WFE/SEV. +/// +/// This is the simplest and most common kind of executor. It runs on +/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction +/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction +/// is executed, to make the `WFE` exit from sleep and poll the task. +/// +/// This executor allows for ultra low power consumption for chips where `WFE` +/// triggers low-power sleep without extra steps. If your chip requires extra steps, +/// you may use [`raw::Executor`] directly to program custom behavior. +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [Forever](crate::util::Forever) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + cortex_m::asm::wfe(); + } + } +} + +fn pend_by_number(n: u16) { + #[derive(Clone, Copy)] + struct N(u16); + unsafe impl cortex_m::interrupt::InterruptNumber for N { + fn number(self) -> u16 { + self.0 + } + } + cortex_m::peripheral::NVIC::pend(N(n)) +} + +/// Interrupt mode executor. +/// +/// This executor runs tasks in interrupt mode. The interrupt handler is set up +/// to poll tasks, and when a task is woken the interrupt is pended from software. +/// +/// This allows running async tasks at a priority higher than thread mode. One +/// use case is to leave thread mode free for non-async tasks. Another use case is +/// to run multiple executors: one in thread mode for low priority tasks and another in +/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower +/// priority ones. +/// +/// It is even possible to run multiple interrupt mode executors at different priorities, +/// by assigning different priorities to the interrupts. For an example on how to do this, +/// See the 'multiprio' example for 'embassy-nrf'. +/// +/// To use it, you have to pick an interrupt that won't be used by the hardware. +/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). +/// If this is not the case, you may use an interrupt from any unused peripheral. +/// +/// It is somewhat more complex to use, it's recommended to use the thread-mode +/// [`Executor`] instead, if it works for your use case. +pub struct InterruptExecutor { + irq: I, + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl InterruptExecutor { + /// Create a new Executor. + pub fn new(irq: I) -> Self { + let ctx = irq.number() as *mut (); + Self { + irq, + inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), + not_send: PhantomData, + } + } + + /// Start the executor. + /// + /// The `init` closure is called from interrupt mode, with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the interrupt is configured so that the executor starts running the tasks. + /// Once the executor is started, `start` returns. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [Forever](crate::util::Forever) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { + self.irq.disable(); + + init(self.inner.spawner()); + + self.irq.set_handler(|ctx| unsafe { + let executor = &*(ctx as *const raw::Executor); + executor.poll(); + }); + self.irq.set_handler_context(&self.inner as *const _ as _); + self.irq.enable(); + } +} diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs index 3ec24c13c..5b53b8d0b 100644 --- a/embassy/src/executor/mod.rs +++ b/embassy/src/executor/mod.rs @@ -2,12 +2,25 @@ #![deny(missing_docs)] -#[cfg_attr(feature = "std", path = "arch/std.rs")] -#[cfg_attr(feature = "wasm", path = "arch/wasm.rs")] -#[cfg_attr(not(any(feature = "std", feature = "wasm")), path = "arch/arm.rs")] -mod arch; +cfg_if::cfg_if! { + if #[cfg(cortex_m)] { + #[path="arch/cortex_m.rs"] + mod arch; + pub use arch::*; + } + else if #[cfg(feature="wasm")] { + #[path="arch/wasm.rs"] + mod arch; + pub use arch::*; + } + else if #[cfg(feature="std")] { + #[path="arch/std.rs"] + mod arch; + pub use arch::*; + } +} + pub mod raw; -mod spawner; -pub use arch::*; +mod spawner; pub use spawner::*; diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index a74d1a514..9d8ef3888 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs @@ -13,6 +13,7 @@ pub mod channel; pub mod waitqueue; pub mod executor; +#[cfg(cortex_m)] pub mod interrupt; pub mod io; #[cfg(feature = "time")] -- cgit