mpfs_hal_embassy/executor.rs
1use core::marker::PhantomData;
2use core::sync::atomic::{AtomicBool, Ordering};
3use embassy_executor::{raw, Spawner};
4
5use mpfs_hal::pac;
6
7static SIGNAL_WORK_THREAD_MODE: [AtomicBool; pac::MPFS_HAL_LAST_HART as usize] =
8 [const { AtomicBool::new(false) }; pac::MPFS_HAL_LAST_HART as usize];
9
10#[export_name = "__pender"]
11fn __pender(context: *mut ()) {
12 #[cfg(feature = "debug-logs")]
13 mpfs_hal::println_unguarded!("hart {} has work pending\n", context as usize + 1);
14 SIGNAL_WORK_THREAD_MODE[context as usize].store(true, Ordering::SeqCst);
15 unsafe {
16 pac::raise_soft_interrupt(context as usize + 1);
17 }
18}
19
20pub struct Executor {
21 inner: raw::Executor,
22 not_send: PhantomData<*mut ()>,
23}
24
25impl Executor {
26 /// Create a new Executor.
27 pub fn new() -> Self {
28 Self {
29 inner: raw::Executor::new((pac::hart_id() - 1) as *mut ()),
30 not_send: PhantomData,
31 }
32 }
33
34 /// Run the executor.
35 ///
36 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
37 /// this executor. Use it to spawn the initial task(s). After `init` returns,
38 /// the executor starts running the tasks.
39 ///
40 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
41 /// for example by passing it as an argument to the initial tasks.
42 ///
43 /// This function requires `&'static mut self`. This means you have to store the
44 /// Executor instance in a place where it'll live forever and grants you mutable
45 /// access. There's a few ways to do this:
46 ///
47 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
48 /// - a `static mut` (unsafe)
49 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
50 ///
51 /// This function never returns.
52 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
53 init(self.inner.spawner());
54
55 loop {
56 unsafe {
57 self.inner.poll();
58 let ctx = pac::hart_id() - 1;
59 let mut do_wfi = true;
60 critical_section::with(|_| {
61 // If there is work to do, loop back to polling
62 // TODO can we relax this?
63 if SIGNAL_WORK_THREAD_MODE[ctx].load(Ordering::SeqCst) {
64 #[cfg(feature = "debug-logs")]
65 mpfs_hal::println_unguarded!("hart {} has work to do\n", ctx + 1);
66 SIGNAL_WORK_THREAD_MODE[ctx].store(false, Ordering::SeqCst);
67 do_wfi = false;
68 }
69 });
70 // If not, wait for interrupt
71 // This is not in the critical section, since we want to release the critical-section lock
72 if do_wfi {
73 #[cfg(feature = "debug-logs")]
74 mpfs_hal::println_unguarded!("hart {} going to wfi\n", ctx + 1);
75 core::arch::asm!("wfi");
76 #[cfg(feature = "debug-logs")]
77 mpfs_hal::println_unguarded!("hart {} wfi\n", ctx + 1);
78 }
79 // if an interrupt occurred while waiting, it will be serviced here
80 }
81 }
82 }
83}