aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/arch/cortex_m.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-04-03 01:18:27 +0200
committerDario Nieuwenhuis <[email protected]>2023-04-03 03:09:11 +0200
commitd3c4e4a20a05085eae8d568c7efdbe09bada9cf5 (patch)
treeef92420c5a4574c9db5cb614d0ecc49d6462badc /embassy-executor/src/arch/cortex_m.rs
parentb41ee47115509ba5ed302c684c0c36b6a3e3f76f (diff)
executor: add Pender, rework Cargo features.
This introduces a `Pender` struct with enum cases for thread-mode, interrupt-mode and custom callback executors. This avoids calls through function pointers when using only the thread or interrupt executors. Faster, and friendlier to `cargo-call-stack`. `embassy-executor` now has `arch-xxx` Cargo features to select the arch and to enable the builtin executors (thread and interrupt).
Diffstat (limited to 'embassy-executor/src/arch/cortex_m.rs')
-rw-r--r--embassy-executor/src/arch/cortex_m.rs238
1 files changed, 194 insertions, 44 deletions
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 4b27a264e..d6a55c4c7 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -1,59 +1,209 @@
1use core::arch::asm; 1#[cfg(feature = "executor-thread")]
2use core::marker::PhantomData; 2pub use thread::*;
3use core::ptr; 3#[cfg(feature = "executor-thread")]
4 4mod thread {
5use super::{raw, Spawner}; 5 use core::arch::asm;
6 6 use core::marker::PhantomData;
7/// Thread mode executor, using WFE/SEV. 7
8/// 8 #[cfg(feature = "nightly")]
9/// This is the simplest and most common kind of executor. It runs on 9 pub use embassy_macros::main_cortex_m as main;
10/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction 10
11/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction 11 use crate::raw::{Pender, PenderInner};
12/// is executed, to make the `WFE` exit from sleep and poll the task. 12 use crate::{raw, Spawner};
13/// 13
14/// This executor allows for ultra low power consumption for chips where `WFE` 14 #[derive(Copy, Clone)]
15/// triggers low-power sleep without extra steps. If your chip requires extra steps, 15 pub(crate) struct ThreadPender;
16/// you may use [`raw::Executor`] directly to program custom behavior. 16
17pub struct Executor { 17 impl ThreadPender {
18 inner: raw::Executor, 18 pub(crate) fn pend(self) {
19 not_send: PhantomData<*mut ()>, 19 unsafe { core::arch::asm!("sev") }
20 }
21 }
22
23 /// Thread mode executor, using WFE/SEV.
24 ///
25 /// This is the simplest and most common kind of executor. It runs on
26 /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
27 /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
28 /// is executed, to make the `WFE` exit from sleep and poll the task.
29 ///
30 /// This executor allows for ultra low power consumption for chips where `WFE`
31 /// triggers low-power sleep without extra steps. If your chip requires extra steps,
32 /// you may use [`raw::Executor`] directly to program custom behavior.
33 pub struct Executor {
34 inner: raw::Executor,
35 not_send: PhantomData<*mut ()>,
36 }
37
38 impl Executor {
39 /// Create a new Executor.
40 pub fn new() -> Self {
41 Self {
42 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
43 not_send: PhantomData,
44 }
45 }
46
47 /// Run the executor.
48 ///
49 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
50 /// this executor. Use it to spawn the initial task(s). After `init` returns,
51 /// the executor starts running the tasks.
52 ///
53 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
54 /// for example by passing it as an argument to the initial tasks.
55 ///
56 /// This function requires `&'static mut self`. This means you have to store the
57 /// Executor instance in a place where it'll live forever and grants you mutable
58 /// access. There's a few ways to do this:
59 ///
60 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
61 /// - a `static mut` (unsafe)
62 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
63 ///
64 /// This function never returns.
65 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
66 init(self.inner.spawner());
67
68 loop {
69 unsafe {
70 self.inner.poll();
71 asm!("wfe");
72 };
73 }
74 }
75 }
20} 76}
21 77
22impl Executor { 78#[cfg(feature = "executor-interrupt")]
23 /// Create a new Executor. 79pub use interrupt::*;
24 pub fn new() -> Self { 80#[cfg(feature = "executor-interrupt")]
25 Self { 81mod interrupt {
26 inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()), 82 use core::cell::UnsafeCell;
27 not_send: PhantomData, 83 use core::mem::MaybeUninit;
84
85 use atomic_polyfill::{AtomicBool, Ordering};
86 use cortex_m::interrupt::InterruptNumber;
87 use cortex_m::peripheral::NVIC;
88
89 use crate::raw::{self, Pender, PenderInner};
90
91 #[derive(Clone, Copy)]
92 pub(crate) struct InterruptPender(u16);
93
94 impl InterruptPender {
95 pub(crate) fn pend(self) {
96 // STIR is faster, but is only available in v7 and higher.
97 #[cfg(not(armv6m))]
98 {
99 let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
100 nvic.request(self);
101 }
102
103 #[cfg(armv6m)]
104 cortex_m::peripheral::NVIC::pend(self);
28 } 105 }
29 } 106 }
30 107
31 /// Run the executor. 108 unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
109 fn number(self) -> u16 {
110 self.0
111 }
112 }
113
114 /// Interrupt mode executor.
32 /// 115 ///
33 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 116 /// This executor runs tasks in interrupt mode. The interrupt handler is set up
34 /// this executor. Use it to spawn the initial task(s). After `init` returns, 117 /// to poll tasks, and when a task is woken the interrupt is pended from software.
35 /// the executor starts running the tasks.
36 /// 118 ///
37 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 119 /// This allows running async tasks at a priority higher than thread mode. One
38 /// for example by passing it as an argument to the initial tasks. 120 /// use case is to leave thread mode free for non-async tasks. Another use case is
121 /// to run multiple executors: one in thread mode for low priority tasks and another in
122 /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
123 /// priority ones.
39 /// 124 ///
40 /// This function requires `&'static mut self`. This means you have to store the 125 /// It is even possible to run multiple interrupt mode executors at different priorities,
41 /// Executor instance in a place where it'll live forever and grants you mutable 126 /// by assigning different priorities to the interrupts. For an example on how to do this,
42 /// access. There's a few ways to do this: 127 /// See the 'multiprio' example for 'embassy-nrf'.
43 /// 128 ///
44 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 129 /// To use it, you have to pick an interrupt that won't be used by the hardware.
45 /// - a `static mut` (unsafe) 130 /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
46 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 131 /// If this is not the case, you may use an interrupt from any unused peripheral.
47 /// 132 ///
48 /// This function never returns. 133 /// It is somewhat more complex to use, it's recommended to use the thread-mode
49 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 134 /// [`Executor`] instead, if it works for your use case.
50 init(self.inner.spawner()); 135 pub struct InterruptExecutor {
136 started: AtomicBool,
137 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
138 }
139
140 unsafe impl Send for InterruptExecutor {}
141 unsafe impl Sync for InterruptExecutor {}
142
143 impl InterruptExecutor {
144 /// Create a new, not started `InterruptExecutor`.
145 #[inline]
146 pub const fn new() -> Self {
147 Self {
148 started: AtomicBool::new(false),
149 executor: UnsafeCell::new(MaybeUninit::uninit()),
150 }
151 }
152
153 /// Executor interrupt callback.
154 ///
155 /// # Safety
156 ///
157 /// You MUST call this from the interrupt handler, and from nowhere else.
158 pub unsafe fn on_interrupt(&'static self) {
159 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
160 executor.poll();
161 }
162
163 /// Start the executor.
164 ///
165 /// This initializes the executor, enables the interrupt, and returns.
166 /// The executor keeps running in the background through the interrupt.
167 ///
168 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
169 /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
170 /// different "thread" (the interrupt), so spawning tasks on it is effectively
171 /// sending them.
172 ///
173 /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
174 /// a task running in it.
175 ///
176 /// # Interrupt requirements
177 ///
178 /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
179 ///
180 /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
181 ///
182 /// You must set the interrupt priority before calling this method. You MUST NOT
183 /// do it after.
184 ///
185 pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
186 if self
187 .started
188 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
189 .is_err()
190 {
191 panic!("InterruptExecutor::start() called multiple times on the same executor.");
192 }
51 193
52 loop {
53 unsafe { 194 unsafe {
54 self.inner.poll(); 195 (&mut *self.executor.get())
55 asm!("wfe"); 196 .as_mut_ptr()
56 }; 197 .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
198 irq.number(),
199 )))))
200 }
201
202 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
203
204 unsafe { NVIC::unmask(irq) }
205
206 executor.spawner().make_send()
57 } 207 }
58 } 208 }
59} 209}