aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/arch
diff options
context:
space:
mode:
authorjrmoulton <[email protected]>2025-06-10 15:47:54 -0600
committerjrmoulton <[email protected]>2025-06-10 15:48:36 -0600
commitcfad9798ff99d4de0571a512d156b5fe1ef1d427 (patch)
treefc3bf670f82d139de19466cddad1e909db7f3d2e /embassy-executor/src/arch
parentfc342915e6155dec7bafa3e135da7f37a9a07f5c (diff)
parent6186d111a5c150946ee5b7e9e68d987a38c1a463 (diff)
merge new embassy changes
Diffstat (limited to 'embassy-executor/src/arch')
-rw-r--r--embassy-executor/src/arch/cortex_ar.rs84
-rw-r--r--embassy-executor/src/arch/cortex_m.rs9
-rw-r--r--embassy-executor/src/arch/spin.rs58
3 files changed, 147 insertions, 4 deletions
diff --git a/embassy-executor/src/arch/cortex_ar.rs b/embassy-executor/src/arch/cortex_ar.rs
new file mode 100644
index 000000000..f9e2f3f7c
--- /dev/null
+++ b/embassy-executor/src/arch/cortex_ar.rs
@@ -0,0 +1,84 @@
1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-cortex-ar`.");
3
4#[export_name = "__pender"]
5#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))]
6fn __pender(context: *mut ()) {
7 // `context` is always `usize::MAX` created by `Executor::run`.
8 let context = context as usize;
9
10 #[cfg(feature = "executor-thread")]
11 // Try to make Rust optimize the branching away if we only use thread mode.
12 if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER {
13 cortex_ar::asm::sev();
14 return;
15 }
16}
17
18#[cfg(feature = "executor-thread")]
19pub use thread::*;
20#[cfg(feature = "executor-thread")]
21mod thread {
22 pub(super) const THREAD_PENDER: usize = usize::MAX;
23
24 use core::marker::PhantomData;
25
26 use cortex_ar::asm::wfe;
27 pub use embassy_executor_macros::main_cortex_ar as main;
28
29 use crate::{raw, Spawner};
30
31 /// Thread mode executor, using WFE/SEV.
32 ///
33 /// This is the simplest and most common kind of executor. It runs on
34 /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
35 /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
36 /// is executed, to make the `WFE` exit from sleep and poll the task.
37 ///
38 /// This executor allows for ultra low power consumption for chips where `WFE`
39 /// triggers low-power sleep without extra steps. If your chip requires extra steps,
40 /// you may use [`raw::Executor`] directly to program custom behavior.
41 pub struct Executor {
42 inner: raw::Executor,
43 not_send: PhantomData<*mut ()>,
44 }
45
46 impl Executor {
47 /// Create a new Executor.
48 pub fn new() -> Self {
49 Self {
50 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
51 not_send: PhantomData,
52 }
53 }
54
55 /// Run the executor.
56 ///
57 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
58 /// this executor. Use it to spawn the initial task(s). After `init` returns,
59 /// the executor starts running the tasks.
60 ///
61 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
62 /// for example by passing it as an argument to the initial tasks.
63 ///
64 /// This function requires `&'static mut self`. This means you have to store the
65 /// Executor instance in a place where it'll live forever and grants you mutable
66 /// access. There's a few ways to do this:
67 ///
68 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
69 /// - a `static mut` (unsafe)
70 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
71 ///
72 /// This function never returns.
73 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
74 init(self.inner.spawner());
75
76 loop {
77 unsafe {
78 self.inner.poll();
79 }
80 wfe();
81 }
82 }
83 }
84}
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 5c517e0a2..1c9ddd8a0 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -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/spin.rs b/embassy-executor/src/arch/spin.rs
new file mode 100644
index 000000000..340023620
--- /dev/null
+++ b/embassy-executor/src/arch/spin.rs
@@ -0,0 +1,58 @@
1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-spin`.");
3
4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9
10 pub use embassy_executor_macros::main_spin as main;
11
12 use crate::{raw, Spawner};
13
14 #[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}