diff options
| author | Dion Dokter <[email protected]> | 2025-11-20 13:22:38 +0100 |
|---|---|---|
| committer | Dion Dokter <[email protected]> | 2025-11-20 13:22:38 +0100 |
| commit | 4f2c36e447455e8d33607d586859d3d075cabf1d (patch) | |
| tree | 003cd822d688acd7c074dd229663b4648d100f71 /embassy-executor/src/arch | |
| parent | 663732d85abbae400f2dbab2c411802a5b60e9b1 (diff) | |
| parent | 661874d11de7d93ed52e08e020a9d4c7ee11122d (diff) | |
Merge branch 'main' into u0-lcd
Diffstat (limited to 'embassy-executor/src/arch')
| -rw-r--r-- | embassy-executor/src/arch/avr.rs | 4 | ||||
| -rw-r--r-- | embassy-executor/src/arch/cortex_ar.rs | 87 | ||||
| -rw-r--r-- | embassy-executor/src/arch/cortex_m.rs | 13 | ||||
| -rw-r--r-- | embassy-executor/src/arch/riscv32.rs | 4 | ||||
| -rw-r--r-- | embassy-executor/src/arch/spin.rs | 58 | ||||
| -rw-r--r-- | embassy-executor/src/arch/std.rs | 4 | ||||
| -rw-r--r-- | embassy-executor/src/arch/wasm.rs | 4 |
7 files changed, 160 insertions, 14 deletions
diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs index 70085d04d..a841afe15 100644 --- a/embassy-executor/src/arch/avr.rs +++ b/embassy-executor/src/arch/avr.rs | |||
| @@ -10,11 +10,11 @@ mod thread { | |||
| 10 | pub use embassy_executor_macros::main_avr as main; | 10 | pub use embassy_executor_macros::main_avr as main; |
| 11 | use portable_atomic::{AtomicBool, Ordering}; | 11 | use portable_atomic::{AtomicBool, Ordering}; |
| 12 | 12 | ||
| 13 | use crate::{raw, Spawner}; | 13 | use crate::{Spawner, raw}; |
| 14 | 14 | ||
| 15 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); | 15 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); |
| 16 | 16 | ||
| 17 | #[export_name = "__pender"] | 17 | #[unsafe(export_name = "__pender")] |
| 18 | fn __pender(_context: *mut ()) { | 18 | fn __pender(_context: *mut ()) { |
| 19 | SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); | 19 | SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); |
| 20 | } | 20 | } |
diff --git a/embassy-executor/src/arch/cortex_ar.rs b/embassy-executor/src/arch/cortex_ar.rs new file mode 100644 index 000000000..ce572738a --- /dev/null +++ b/embassy-executor/src/arch/cortex_ar.rs | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | #[cfg(arm_profile = "legacy")] | ||
| 2 | compile_error!("`arch-cortex-ar` does not support the legacy ARM profile, WFE/SEV are not available."); | ||
| 3 | |||
| 4 | #[cfg(feature = "executor-interrupt")] | ||
| 5 | compile_error!("`executor-interrupt` is not supported with `arch-cortex-ar`."); | ||
| 6 | |||
| 7 | #[unsafe(export_name = "__pender")] | ||
| 8 | #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] | ||
| 9 | fn __pender(context: *mut ()) { | ||
| 10 | // `context` is always `usize::MAX` created by `Executor::run`. | ||
| 11 | let context = context as usize; | ||
| 12 | |||
| 13 | #[cfg(feature = "executor-thread")] | ||
| 14 | // Try to make Rust optimize the branching away if we only use thread mode. | ||
| 15 | if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER { | ||
| 16 | aarch32_cpu::asm::sev(); | ||
| 17 | return; | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | #[cfg(feature = "executor-thread")] | ||
| 22 | pub use thread::*; | ||
| 23 | #[cfg(feature = "executor-thread")] | ||
| 24 | mod thread { | ||
| 25 | pub(super) const THREAD_PENDER: usize = usize::MAX; | ||
| 26 | |||
| 27 | use core::marker::PhantomData; | ||
| 28 | |||
| 29 | use aarch32_cpu::asm::wfe; | ||
| 30 | pub use embassy_executor_macros::main_cortex_ar as main; | ||
| 31 | |||
| 32 | use crate::{Spawner, raw}; | ||
| 33 | |||
| 34 | /// Thread mode executor, using WFE/SEV. | ||
| 35 | /// | ||
| 36 | /// This is the simplest and most common kind of executor. It runs on | ||
| 37 | /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction | ||
| 38 | /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction | ||
| 39 | /// is executed, to make the `WFE` exit from sleep and poll the task. | ||
| 40 | /// | ||
| 41 | /// This executor allows for ultra low power consumption for chips where `WFE` | ||
| 42 | /// triggers low-power sleep without extra steps. If your chip requires extra steps, | ||
| 43 | /// you may use [`raw::Executor`] directly to program custom behavior. | ||
| 44 | pub struct Executor { | ||
| 45 | inner: raw::Executor, | ||
| 46 | not_send: PhantomData<*mut ()>, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl Executor { | ||
| 50 | /// Create a new Executor. | ||
| 51 | pub fn new() -> Self { | ||
| 52 | Self { | ||
| 53 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), | ||
| 54 | not_send: PhantomData, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Run the executor. | ||
| 59 | /// | ||
| 60 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on | ||
| 61 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | ||
| 62 | /// the executor starts running the tasks. | ||
| 63 | /// | ||
| 64 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | ||
| 65 | /// for example by passing it as an argument to the initial tasks. | ||
| 66 | /// | ||
| 67 | /// This function requires `&'static mut self`. This means you have to store the | ||
| 68 | /// Executor instance in a place where it'll live forever and grants you mutable | ||
| 69 | /// access. There's a few ways to do this: | ||
| 70 | /// | ||
| 71 | /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) | ||
| 72 | /// - a `static mut` (unsafe) | ||
| 73 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | ||
| 74 | /// | ||
| 75 | /// This function never returns. | ||
| 76 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 77 | init(self.inner.spawner()); | ||
| 78 | |||
| 79 | loop { | ||
| 80 | unsafe { | ||
| 81 | self.inner.poll(); | ||
| 82 | } | ||
| 83 | wfe(); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 5c517e0a2..1ce96d1d5 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | #[export_name = "__pender"] | 1 | #[unsafe(export_name = "__pender")] |
| 2 | #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] | 2 | #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] |
| 3 | fn __pender(context: *mut ()) { | 3 | fn __pender(context: *mut ()) { |
| 4 | unsafe { | 4 | unsafe { |
| @@ -53,7 +53,7 @@ mod thread { | |||
| 53 | 53 | ||
| 54 | pub use embassy_executor_macros::main_cortex_m as main; | 54 | pub use embassy_executor_macros::main_cortex_m as main; |
| 55 | 55 | ||
| 56 | use crate::{raw, Spawner}; | 56 | use crate::{Spawner, raw}; |
| 57 | 57 | ||
| 58 | /// Thread mode executor, using WFE/SEV. | 58 | /// Thread mode executor, using WFE/SEV. |
| 59 | /// | 59 | /// |
| @@ -143,7 +143,7 @@ mod interrupt { | |||
| 143 | /// If this is not the case, you may use an interrupt from any unused peripheral. | 143 | /// If this is not the case, you may use an interrupt from any unused peripheral. |
| 144 | /// | 144 | /// |
| 145 | /// It is somewhat more complex to use, it's recommended to use the thread-mode | 145 | /// It is somewhat more complex to use, it's recommended to use the thread-mode |
| 146 | /// [`Executor`] instead, if it works for your use case. | 146 | /// [`Executor`](crate::Executor) instead, if it works for your use case. |
| 147 | pub struct InterruptExecutor { | 147 | pub struct InterruptExecutor { |
| 148 | started: Mutex<Cell<bool>>, | 148 | started: Mutex<Cell<bool>>, |
| 149 | executor: UnsafeCell<MaybeUninit<raw::Executor>>, | 149 | executor: UnsafeCell<MaybeUninit<raw::Executor>>, |
| @@ -179,11 +179,11 @@ mod interrupt { | |||
| 179 | /// The executor keeps running in the background through the interrupt. | 179 | /// The executor keeps running in the background through the interrupt. |
| 180 | /// | 180 | /// |
| 181 | /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] | 181 | /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] |
| 182 | /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a | 182 | /// is returned instead of a [`Spawner`](crate::Spawner) because the executor effectively runs in a |
| 183 | /// different "thread" (the interrupt), so spawning tasks on it is effectively | 183 | /// different "thread" (the interrupt), so spawning tasks on it is effectively |
| 184 | /// sending them. | 184 | /// sending them. |
| 185 | /// | 185 | /// |
| 186 | /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from | 186 | /// To obtain a [`Spawner`](crate::Spawner) for this executor, use [`Spawner::for_current_executor()`](crate::Spawner::for_current_executor()) from |
| 187 | /// a task running in it. | 187 | /// a task running in it. |
| 188 | /// | 188 | /// |
| 189 | /// # Interrupt requirements | 189 | /// # Interrupt requirements |
| @@ -195,6 +195,7 @@ mod interrupt { | |||
| 195 | /// You must set the interrupt priority before calling this method. You MUST NOT | 195 | /// You must set the interrupt priority before calling this method. You MUST NOT |
| 196 | /// do it after. | 196 | /// do it after. |
| 197 | /// | 197 | /// |
| 198 | /// [`SendSpawner`]: crate::SendSpawner | ||
| 198 | pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { | 199 | pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { |
| 199 | if critical_section::with(|cs| self.started.borrow(cs).replace(true)) { | 200 | if critical_section::with(|cs| self.started.borrow(cs).replace(true)) { |
| 200 | panic!("InterruptExecutor::start() called multiple times on the same executor."); | 201 | panic!("InterruptExecutor::start() called multiple times on the same executor."); |
| @@ -215,7 +216,7 @@ mod interrupt { | |||
| 215 | 216 | ||
| 216 | /// Get a SendSpawner for this executor | 217 | /// Get a SendSpawner for this executor |
| 217 | /// | 218 | /// |
| 218 | /// This returns a [`SendSpawner`] you can use to spawn tasks on this | 219 | /// This returns a [`SendSpawner`](crate::SendSpawner) you can use to spawn tasks on this |
| 219 | /// executor. | 220 | /// executor. |
| 220 | /// | 221 | /// |
| 221 | /// This MUST only be called on an executor that has already been started. | 222 | /// This MUST only be called on an executor that has already been started. |
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 01e63a9fd..c70c1344a 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs | |||
| @@ -10,12 +10,12 @@ mod thread { | |||
| 10 | 10 | ||
| 11 | pub use embassy_executor_macros::main_riscv as main; | 11 | pub use embassy_executor_macros::main_riscv as main; |
| 12 | 12 | ||
| 13 | use crate::{raw, Spawner}; | 13 | use crate::{Spawner, raw}; |
| 14 | 14 | ||
| 15 | /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV | 15 | /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV |
| 16 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); | 16 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); |
| 17 | 17 | ||
| 18 | #[export_name = "__pender"] | 18 | #[unsafe(export_name = "__pender")] |
| 19 | fn __pender(_context: *mut ()) { | 19 | fn __pender(_context: *mut ()) { |
| 20 | SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); | 20 | SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); |
| 21 | } | 21 | } |
diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs new file mode 100644 index 000000000..49f3356a6 --- /dev/null +++ b/embassy-executor/src/arch/spin.rs | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | #[cfg(feature = "executor-interrupt")] | ||
| 2 | compile_error!("`executor-interrupt` is not supported with `arch-spin`."); | ||
| 3 | |||
| 4 | #[cfg(feature = "executor-thread")] | ||
| 5 | pub use thread::*; | ||
| 6 | #[cfg(feature = "executor-thread")] | ||
| 7 | mod thread { | ||
| 8 | use core::marker::PhantomData; | ||
| 9 | |||
| 10 | pub use embassy_executor_macros::main_spin as main; | ||
| 11 | |||
| 12 | use crate::{Spawner, raw}; | ||
| 13 | |||
| 14 | #[unsafe(export_name = "__pender")] | ||
| 15 | fn __pender(_context: *mut ()) {} | ||
| 16 | |||
| 17 | /// Spin Executor | ||
| 18 | pub struct Executor { | ||
| 19 | inner: raw::Executor, | ||
| 20 | not_send: PhantomData<*mut ()>, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl Executor { | ||
| 24 | /// Create a new Executor. | ||
| 25 | pub fn new() -> Self { | ||
| 26 | Self { | ||
| 27 | inner: raw::Executor::new(core::ptr::null_mut()), | ||
| 28 | not_send: PhantomData, | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | /// Run the executor. | ||
| 33 | /// | ||
| 34 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on | ||
| 35 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | ||
| 36 | /// the executor starts running the tasks. | ||
| 37 | /// | ||
| 38 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | ||
| 39 | /// for example by passing it as an argument to the initial tasks. | ||
| 40 | /// | ||
| 41 | /// This function requires `&'static mut self`. This means you have to store the | ||
| 42 | /// Executor instance in a place where it'll live forever and grants you mutable | ||
| 43 | /// access. There's a few ways to do this: | ||
| 44 | /// | ||
| 45 | /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) | ||
| 46 | /// - a `static mut` (unsafe) | ||
| 47 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | ||
| 48 | /// | ||
| 49 | /// This function never returns. | ||
| 50 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 51 | init(self.inner.spawner()); | ||
| 52 | |||
| 53 | loop { | ||
| 54 | unsafe { self.inner.poll() }; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index b02b15988..c62ab723b 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs | |||
| @@ -10,9 +10,9 @@ mod thread { | |||
| 10 | 10 | ||
| 11 | pub use embassy_executor_macros::main_std as main; | 11 | pub use embassy_executor_macros::main_std as main; |
| 12 | 12 | ||
| 13 | use crate::{raw, Spawner}; | 13 | use crate::{Spawner, raw}; |
| 14 | 14 | ||
| 15 | #[export_name = "__pender"] | 15 | #[unsafe(export_name = "__pender")] |
| 16 | fn __pender(context: *mut ()) { | 16 | fn __pender(context: *mut ()) { |
| 17 | let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; | 17 | let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; |
| 18 | signaler.signal() | 18 | signaler.signal() |
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index f9d0f935c..d2ff2fe51 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs | |||
| @@ -13,9 +13,9 @@ mod thread { | |||
| 13 | use wasm_bindgen::prelude::*; | 13 | use wasm_bindgen::prelude::*; |
| 14 | 14 | ||
| 15 | use crate::raw::util::UninitCell; | 15 | use crate::raw::util::UninitCell; |
| 16 | use crate::{raw, Spawner}; | 16 | use crate::{Spawner, raw}; |
| 17 | 17 | ||
| 18 | #[export_name = "__pender"] | 18 | #[unsafe(export_name = "__pender")] |
| 19 | fn __pender(context: *mut ()) { | 19 | fn __pender(context: *mut ()) { |
| 20 | let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; | 20 | let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; |
| 21 | let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); | 21 | let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); |
