aboutsummaryrefslogtreecommitdiff
path: root/embassy-cortex-m
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-cortex-m
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-cortex-m')
-rw-r--r--embassy-cortex-m/src/executor.rs116
-rw-r--r--embassy-cortex-m/src/lib.rs2
2 files changed, 1 insertions, 117 deletions
diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs
deleted file mode 100644
index 558539e73..000000000
--- a/embassy-cortex-m/src/executor.rs
+++ /dev/null
@@ -1,116 +0,0 @@
1//! Executor specific to cortex-m devices.
2
3use core::cell::UnsafeCell;
4use core::mem::MaybeUninit;
5
6use atomic_polyfill::{AtomicBool, Ordering};
7use cortex_m::interrupt::InterruptNumber;
8use cortex_m::peripheral::NVIC;
9pub use embassy_executor::*;
10
11#[derive(Clone, Copy)]
12struct N(u16);
13unsafe impl cortex_m::interrupt::InterruptNumber for N {
14 fn number(self) -> u16 {
15 self.0
16 }
17}
18
19fn pend_by_number(n: u16) {
20 cortex_m::peripheral::NVIC::pend(N(n))
21}
22
23/// Interrupt mode executor.
24///
25/// This executor runs tasks in interrupt mode. The interrupt handler is set up
26/// to poll tasks, and when a task is woken the interrupt is pended from software.
27///
28/// This allows running async tasks at a priority higher than thread mode. One
29/// use case is to leave thread mode free for non-async tasks. Another use case is
30/// to run multiple executors: one in thread mode for low priority tasks and another in
31/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
32/// priority ones.
33///
34/// It is even possible to run multiple interrupt mode executors at different priorities,
35/// by assigning different priorities to the interrupts. For an example on how to do this,
36/// See the 'multiprio' example for 'embassy-nrf'.
37///
38/// To use it, you have to pick an interrupt that won't be used by the hardware.
39/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
40/// If this is not the case, you may use an interrupt from any unused peripheral.
41///
42/// It is somewhat more complex to use, it's recommended to use the thread-mode
43/// [`Executor`] instead, if it works for your use case.
44pub struct InterruptExecutor {
45 started: AtomicBool,
46 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
47}
48
49unsafe impl Send for InterruptExecutor {}
50unsafe impl Sync for InterruptExecutor {}
51
52impl InterruptExecutor {
53 /// Create a new, not started `InterruptExecutor`.
54 #[inline]
55 pub const fn new() -> Self {
56 Self {
57 started: AtomicBool::new(false),
58 executor: UnsafeCell::new(MaybeUninit::uninit()),
59 }
60 }
61
62 /// Executor interrupt callback.
63 ///
64 /// # Safety
65 ///
66 /// You MUST call this from the interrupt handler, and from nowhere else.
67 pub unsafe fn on_interrupt(&'static self) {
68 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
69 executor.poll();
70 }
71
72 /// Start the executor.
73 ///
74 /// This initializes the executor, enables the interrupt, and returns.
75 /// The executor keeps running in the background through the interrupt.
76 ///
77 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
78 /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
79 /// different "thread" (the interrupt), so spawning tasks on it is effectively
80 /// sending them.
81 ///
82 /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
83 /// a task running in it.
84 ///
85 /// # Interrupt requirements
86 ///
87 /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
88 ///
89 /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
90 ///
91 /// You must set the interrupt priority before calling this method. You MUST NOT
92 /// do it after.
93 ///
94 pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner {
95 if self
96 .started
97 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
98 .is_err()
99 {
100 panic!("InterruptExecutor::start() called multiple times on the same executor.");
101 }
102
103 unsafe {
104 (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new(
105 |ctx| pend_by_number(ctx as u16),
106 irq.number() as *mut (),
107 ))
108 }
109
110 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
111
112 unsafe { NVIC::unmask(irq) }
113
114 executor.spawner().make_send()
115 }
116}
diff --git a/embassy-cortex-m/src/lib.rs b/embassy-cortex-m/src/lib.rs
index fba23367b..e4b713a06 100644
--- a/embassy-cortex-m/src/lib.rs
+++ b/embassy-cortex-m/src/lib.rs
@@ -5,6 +5,6 @@
5// This mod MUST go first, so that the others see its macros. 5// This mod MUST go first, so that the others see its macros.
6pub(crate) mod fmt; 6pub(crate) mod fmt;
7 7
8pub mod executor; 8pub use embassy_executor as executor;
9pub mod interrupt; 9pub mod interrupt;
10pub mod peripheral; 10pub mod peripheral;