esp-hal/esp-hal-embassy/src/executor/interrupt.rs
Jesse Braham 776f34cff1
Re-export the esp_hal_procmacros::main macro from esp-hal-embassy instead of esp-hal (#1828)
* Re-export the `main` procmacro from `esp-hal-embassy` rather than `esp-hal`

* Fix documentation warnings

* Flatten the `time_driver` module

* clippy

* Update `CHANGELOG.md`
2024-07-18 09:31:03 +00:00

160 lines
4.6 KiB
Rust

//! Interrupt-mode executor.
use core::{cell::UnsafeCell, mem::MaybeUninit};
use embassy_executor::{raw, SendSpawner};
use esp_hal::{
get_core,
interrupt::{self, InterruptHandler},
system::SoftwareInterrupt,
};
use portable_atomic::{AtomicUsize, Ordering};
static mut EXECUTORS: [CallbackContext; 4] = [
CallbackContext::new(),
CallbackContext::new(),
CallbackContext::new(),
CallbackContext::new(),
];
/// Interrupt mode executor.
///
/// This executor runs tasks in interrupt mode. The interrupt handler is set up
/// to poll tasks, and when a task is woken the interrupt is pended from
/// software.
pub struct InterruptExecutor<const SWI: u8> {
core: AtomicUsize,
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
interrupt: SoftwareInterrupt<SWI>,
}
unsafe impl<const SWI: u8> Send for InterruptExecutor<SWI> {}
unsafe impl<const SWI: u8> Sync for InterruptExecutor<SWI> {}
struct CallbackContext {
raw_executor: UnsafeCell<*mut raw::Executor>,
}
impl CallbackContext {
const fn new() -> Self {
Self {
raw_executor: UnsafeCell::new(core::ptr::null_mut()),
}
}
fn get(&self) -> *mut raw::Executor {
unsafe { *self.raw_executor.get() }
}
fn set(&self, executor: *mut raw::Executor) {
unsafe {
self.raw_executor.get().write(executor);
}
}
}
fn handle_interrupt<const NUM: u8>() {
let swi = unsafe { SoftwareInterrupt::<NUM>::steal() };
swi.reset();
unsafe {
let executor = unwrap!(EXECUTORS[NUM as usize].get().as_mut());
executor.poll();
}
}
extern "C" fn swi_handler0() {
handle_interrupt::<0>();
}
extern "C" fn swi_handler1() {
handle_interrupt::<1>();
}
extern "C" fn swi_handler2() {
handle_interrupt::<2>();
}
extern "C" fn swi_handler3() {
handle_interrupt::<3>();
}
impl<const SWI: u8> InterruptExecutor<SWI> {
/// Create a new `InterruptExecutor`.
/// This takes the software interrupt to be used internally.
#[inline]
pub const fn new(interrupt: SoftwareInterrupt<SWI>) -> Self {
Self {
core: AtomicUsize::new(usize::MAX),
executor: UnsafeCell::new(MaybeUninit::uninit()),
interrupt,
}
}
/// Start the executor at the given priority level.
///
/// This initializes the executor, enables the interrupt, and returns.
/// The executor keeps running in the background through the interrupt.
///
/// This returns a [`SendSpawner`] you can use to spawn tasks on it. A
/// [`SendSpawner`] is returned instead of a
/// [`Spawner`](embassy_executor::Spawner) because the
/// executor effectively runs in a different "thread" (the interrupt),
/// so spawning tasks on it is effectively sending them.
///
/// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor,
/// use [`Spawner::for_current_executor`](embassy_executor::Spawner::for_current_executor)
/// from a task running in it.
pub fn start(&'static mut self, priority: interrupt::Priority) -> SendSpawner {
if self
.core
.compare_exchange(
usize::MAX,
get_core() as usize,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_err()
{
panic!("InterruptExecutor::start() called multiple times on the same executor.");
}
unsafe {
(*self.executor.get())
.as_mut_ptr()
.write(raw::Executor::new(SWI as *mut ()));
EXECUTORS[SWI as usize].set((*self.executor.get()).as_mut_ptr());
}
let swi_handler = match SWI {
0 => swi_handler0,
1 => swi_handler1,
2 => swi_handler2,
3 => swi_handler3,
_ => unreachable!(),
};
self.interrupt
.set_interrupt_handler(InterruptHandler::new(swi_handler, priority));
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.spawner().make_send()
}
/// Get a SendSpawner for this executor
///
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
/// executor.
///
/// This MUST only be called on an executor that has already been started.
/// The function will panic otherwise.
pub fn spawner(&'static self) -> SendSpawner {
if self.core.load(Ordering::Acquire) == usize::MAX {
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
}
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.spawner().make_send()
}
}