use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit};
use embassy_executor::{raw, SendSpawner};
#[cfg(any(esp32c6, esp32h2))]
use peripherals::INTPRI as SystemPeripheral;
#[cfg(not(any(esp32c6, esp32h2)))]
use peripherals::SYSTEM as SystemPeripheral;
use portable_atomic::{AtomicUsize, Ordering};
use crate::{get_core, interrupt, peripherals};
static FROM_CPU_IRQ_USED: AtomicUsize = AtomicUsize::new(0);
pub trait SwPendableInterrupt {
fn enable(priority: interrupt::Priority);
fn number() -> usize;
fn pend();
fn clear();
}
macro_rules! from_cpu {
($irq:literal) => {
paste::paste! {
pub struct [<FromCpu $irq>];
impl [<FromCpu $irq>] {
fn set_bit(value: bool) {
let system = unsafe { &*SystemPeripheral::PTR };
system
.[<cpu_intr_from_cpu_ $irq>]()
.write(|w| w.[<cpu_intr_from_cpu_ $irq>]().bit(value));
}
}
impl SwPendableInterrupt for [<FromCpu $irq>] {
fn enable(priority: interrupt::Priority) {
let mask = 1 << $irq;
if FROM_CPU_IRQ_USED.fetch_or(mask, Ordering::SeqCst) & mask != 0 {
panic!("FROM_CPU_{} is already used by a different executor.", $irq);
}
#[allow(unused_unsafe)]
unsafe {
unwrap!(interrupt::enable(peripherals::Interrupt::[<FROM_CPU_INTR $irq>], priority));
}
}
fn number() -> usize {
$irq
}
fn pend() {
Self::set_bit(true);
}
fn clear() {
Self::set_bit(false);
}
}
}
};
}
from_cpu!(1);
from_cpu!(2);
from_cpu!(3);
pub struct InterruptExecutor<SWI>
where
SWI: SwPendableInterrupt,
{
core: AtomicUsize,
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
_interrupt: PhantomData<SWI>,
}
unsafe impl<SWI: SwPendableInterrupt> Send for InterruptExecutor<SWI> {}
unsafe impl<SWI: SwPendableInterrupt> Sync for InterruptExecutor<SWI> {}
impl<SWI> InterruptExecutor<SWI>
where
SWI: SwPendableInterrupt,
{
#[inline]
pub const fn new() -> Self {
Self {
core: AtomicUsize::new(usize::MAX),
executor: UnsafeCell::new(MaybeUninit::uninit()),
_interrupt: PhantomData,
}
}
pub unsafe fn on_interrupt(&'static self) {
SWI::clear();
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.poll();
}
pub fn start(&'static 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::number() as *mut ()))
}
SWI::enable(priority);
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.spawner().make_send()
}
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()
}
}