aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/arch/cortex_m.rs
diff options
context:
space:
mode:
authorDániel Buga <[email protected]>2023-08-12 16:00:18 +0200
committerDániel Buga <[email protected]>2023-08-12 18:29:56 +0200
commit675b7fb6056d8c3dfaca759b7cd373e2f4a0e111 (patch)
treeef4f786edd849f9ce82cffa3b1d58e939a4f53a7 /embassy-executor/src/arch/cortex_m.rs
parent0727f8690c4684d0622547edee2cf9dc22215a9b (diff)
POC: allow custom executors
Diffstat (limited to 'embassy-executor/src/arch/cortex_m.rs')
-rw-r--r--embassy-executor/src/arch/cortex_m.rs238
1 files changed, 56 insertions, 182 deletions
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 94c8134d6..ca1675c03 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -1,224 +1,98 @@
1#[cfg(feature = "executor-thread")] 1#[cfg(feature = "executor-thread")]
2pub use thread::*; 2pub use thread::*;
3
3#[cfg(feature = "executor-thread")] 4#[cfg(feature = "executor-thread")]
4mod thread { 5mod thread {
5 use core::arch::asm;
6 use core::marker::PhantomData;
7 6
8 #[cfg(feature = "nightly")] 7 #[cfg(feature = "nightly")]
9 pub use embassy_macros::main_cortex_m as main; 8 pub use embassy_macros::main_cortex_m as main;
10 9
11 use crate::raw::{Pender, PenderInner}; 10 use crate::raw::OpaqueThreadContext;
12 use crate::{raw, Spawner}; 11 use crate::thread::ThreadContext;
13
14 #[derive(Copy, Clone)]
15 pub(crate) struct ThreadPender;
16 12
17 impl ThreadPender { 13 #[export_name = "__thread_mode_pender"]
18 pub(crate) fn pend(self) { 14 fn __thread_mode_pender(_core_id: OpaqueThreadContext) {
19 unsafe { core::arch::asm!("sev") } 15 unsafe { core::arch::asm!("sev") }
20 }
21 } 16 }
22 17
23 /// Thread mode executor, using WFE/SEV. 18 /// TODO
24 /// 19 // Name pending
25 /// This is the simplest and most common kind of executor. It runs on 20 #[derive(Default)] // Default enables Executor::new
26 /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction 21 pub struct CortexMThreadContext {
27 /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction 22 _not_send: core::marker::PhantomData<*mut ()>,
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 } 23 }
37 24
38 impl Executor { 25 impl ThreadContext for CortexMThreadContext {
39 /// Create a new Executor. 26 #[cfg(feature = "thread-context")]
40 pub fn new() -> Self { 27 fn context(&self) -> OpaqueThreadContext {
41 Self { 28 // Enabling thread-context is not incorrect, just wasteful.
42 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), 29 OpaqueThreadContext(0)
43 not_send: PhantomData, 30 }
44 } 31
32 #[cfg(not(feature = "thread-context"))]
33 fn context(&self) -> OpaqueThreadContext {
34 OpaqueThreadContext(())
45 } 35 }
46 36
47 /// Run the executor. 37 fn wait(&mut self) {
48 /// 38 unsafe { core::arch::asm!("wfe") }
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 } 39 }
75 } 40 }
41
42 /// TODO
43 // Type alias for backwards compatibility
44 pub type Executor = crate::thread::ThreadModeExecutor<CortexMThreadContext>;
76} 45}
77 46
47// None of this has to be public, I guess?
78#[cfg(feature = "executor-interrupt")] 48#[cfg(feature = "executor-interrupt")]
79pub use interrupt::*; 49pub use interrupt::*;
80#[cfg(feature = "executor-interrupt")] 50#[cfg(feature = "executor-interrupt")]
81mod interrupt { 51mod interrupt {
82 use core::cell::UnsafeCell;
83 use core::mem::MaybeUninit;
84
85 use atomic_polyfill::{AtomicBool, Ordering};
86 use cortex_m::interrupt::InterruptNumber; 52 use cortex_m::interrupt::InterruptNumber;
87 use cortex_m::peripheral::NVIC; 53 use cortex_m::peripheral::NVIC;
88 54
89 use crate::raw::{self, Pender, PenderInner}; 55 use crate::interrupt::InterruptContext;
56 use crate::raw::OpaqueInterruptContext;
90 57
91 #[derive(Clone, Copy)] 58 #[derive(Clone, Copy)]
92 pub(crate) struct InterruptPender(u16); 59 struct CortexMInterruptContext(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);
105 }
106 }
107 60
108 unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { 61 unsafe impl cortex_m::interrupt::InterruptNumber for CortexMInterruptContext {
109 fn number(self) -> u16 { 62 fn number(self) -> u16 {
110 self.0 63 self.0
111 } 64 }
112 } 65 }
113 66
114 /// Interrupt mode executor. 67 impl<T> InterruptContext for T
115 /// 68 where
116 /// This executor runs tasks in interrupt mode. The interrupt handler is set up 69 T: InterruptNumber,
117 /// to poll tasks, and when a task is woken the interrupt is pended from software. 70 {
118 /// 71 fn context(&self) -> OpaqueInterruptContext {
119 /// This allows running async tasks at a priority higher than thread mode. One 72 OpaqueInterruptContext(self.number() as usize)
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.
124 ///
125 /// It is even possible to run multiple interrupt mode executors at different priorities,
126 /// by assigning different priorities to the interrupts. For an example on how to do this,
127 /// See the 'multiprio' example for 'embassy-nrf'.
128 ///
129 /// To use it, you have to pick an interrupt that won't be used by the hardware.
130 /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
131 /// If this is not the case, you may use an interrupt from any unused peripheral.
132 ///
133 /// It is somewhat more complex to use, it's recommended to use the thread-mode
134 /// [`Executor`] instead, if it works for your use case.
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 } 73 }
152 74
153 /// Executor interrupt callback. 75 fn enable(&self) {
154 /// 76 unsafe { NVIC::unmask(*self) }
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 } 77 }
78 }
162 79
163 /// Start the executor. 80 #[export_name = "__interrupt_mode_pender"]
164 /// 81 fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext) {
165 /// This initializes the executor, enables the interrupt, and returns. 82 let interrupt = CortexMInterruptContext(unsafe { core::mem::transmute::<_, usize>(interrupt) as u16 });
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 }
193
194 unsafe {
195 (&mut *self.executor.get())
196 .as_mut_ptr()
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()
207 }
208 83
209 /// Get a SendSpawner for this executor 84 // STIR is faster, but is only available in v7 and higher.
210 /// 85 #[cfg(not(armv6m))]
211 /// This returns a [`SendSpawner`] you can use to spawn tasks on this 86 {
212 /// executor. 87 let mut nvic: NVIC = unsafe { core::mem::transmute(()) };
213 /// 88 nvic.request(interrupt);
214 /// This MUST only be called on an executor that has already been spawned.
215 /// The function will panic otherwise.
216 pub fn spawner(&'static self) -> crate::SendSpawner {
217 if !self.started.load(Ordering::Acquire) {
218 panic!("InterruptExecutor::spawner() called on uninitialized executor.");
219 }
220 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
221 executor.spawner().make_send()
222 } 89 }
90
91 #[cfg(armv6m)]
92 NVIC::pend(interrupt);
223 } 93 }
94
95 /// TODO
96 // Type alias for backwards compatibility
97 pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor;
224} 98}