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}