#![cfg_attr(docsrs, procmacros::doc_replace)]
#![cfg_attr(
multi_core,
doc = "This mechanism can be used to implement efficient cross-core communication."
)]
#[cfg(riscv)]
pub use self::riscv::*;
#[cfg(xtensa)]
pub use self::xtensa::*;
use crate::{peripherals::Interrupt, system::Cpu};
cfg_if::cfg_if! {
if #[cfg(esp32)] {
use crate::peripherals::DPORT as INTERRUPT_CORE0;
use crate::peripherals::DPORT as INTERRUPT_CORE1;
} else {
use crate::peripherals::INTERRUPT_CORE0;
#[cfg(esp32s3)]
use crate::peripherals::INTERRUPT_CORE1;
}
}
#[cfg(riscv)]
mod riscv;
#[cfg(xtensa)]
mod xtensa;
use crate::pac;
unstable_driver! {
pub mod software;
}
#[cfg(feature = "rt")]
#[unsafe(no_mangle)]
extern "C" fn EspDefaultHandler() {
panic!("Unhandled interrupt on {:?}", crate::system::Cpu::current());
}
#[instability::unstable]
pub const DEFAULT_INTERRUPT_HANDLER: InterruptHandler = InterruptHandler::new(
{
unsafe extern "C" {
fn EspDefaultHandler();
}
unsafe {
core::mem::transmute::<unsafe extern "C" fn(), extern "C" fn()>(EspDefaultHandler)
}
},
Priority::min(),
);
#[instability::unstable]
pub trait InterruptConfigurable: crate::private::Sealed {
#[cfg_attr(
not(multi_core),
doc = "Registers an interrupt handler for the peripheral."
)]
#[cfg_attr(
multi_core,
doc = "Registers an interrupt handler for the peripheral on the current core."
)]
#[doc = ""]
fn set_interrupt_handler(&mut self, handler: InterruptHandler);
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct IsrCallback {
f: extern "C" fn(),
}
impl IsrCallback {
pub fn new(f: extern "C" fn()) -> Self {
Self { f }
}
pub fn address(self) -> usize {
self.f as usize
}
pub fn callback(self) -> extern "C" fn() {
self.f
}
}
impl PartialEq for IsrCallback {
fn eq(&self, other: &Self) -> bool {
core::ptr::fn_addr_eq(self.callback(), other.callback())
}
}
#[cfg_attr(
multi_core,
doc = r"
**Note**: Interrupts are handled on the core they were setup on. If a driver is initialized
on core 0, and moved to core 1, core 0 will still handle the interrupt."
)]
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct InterruptHandler {
f: extern "C" fn(),
prio: Priority,
}
impl InterruptHandler {
pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
Self { f, prio }
}
#[inline]
pub fn handler(&self) -> IsrCallback {
IsrCallback::new(self.f)
}
#[inline]
pub fn priority(&self) -> Priority {
self.prio
}
}
const STATUS_WORDS: usize = property!("interrupts.status_registers");
#[derive(Clone, Copy, Default, Debug)]
#[instability::unstable]
pub struct InterruptStatus {
status: [u32; STATUS_WORDS],
}
impl InterruptStatus {
#[instability::unstable]
pub fn current() -> InterruptStatus {
match Cpu::current() {
Cpu::ProCpu => InterruptStatus {
status: core::array::from_fn(|idx| {
INTERRUPT_CORE0::regs()
.core_0_intr_status(idx)
.read()
.bits()
}),
},
#[cfg(multi_core)]
Cpu::AppCpu => InterruptStatus {
status: core::array::from_fn(|idx| {
INTERRUPT_CORE1::regs()
.core_1_intr_status(idx)
.read()
.bits()
}),
},
}
}
#[instability::unstable]
pub fn is_set(&self, interrupt: u8) -> bool {
(self.status[interrupt as usize / 32] & (1 << (interrupt % 32))) != 0
}
#[instability::unstable]
pub fn set(&mut self, interrupt: u8) {
self.status[interrupt as usize / 32] |= 1 << (interrupt % 32);
}
#[instability::unstable]
pub fn iterator(&self) -> InterruptStatusIterator {
InterruptStatusIterator {
status: *self,
idx: 0,
}
}
}
#[derive(Debug, Clone)]
#[instability::unstable]
pub struct InterruptStatusIterator {
status: InterruptStatus,
idx: usize,
}
impl Iterator for InterruptStatusIterator {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
for i in self.idx..STATUS_WORDS {
if self.status.status[i] != 0 {
let bit = self.status.status[i].trailing_zeros();
self.status.status[i] ^= 1 << bit;
self.idx = i;
return Some((bit + 32 * i as u32) as u8);
}
}
self.idx = usize::MAX;
None
}
}
fn vector_entry(interrupt: Interrupt) -> &'static pac::Vector {
cfg_if::cfg_if! {
if #[cfg(xtensa)] {
&pac::__INTERRUPTS[interrupt as usize]
} else {
&pac::__EXTERNAL_INTERRUPTS[interrupt as usize]
}
}
}
#[instability::unstable]
pub fn bound_handler(interrupt: Interrupt) -> Option<IsrCallback> {
unsafe {
let vector = vector_entry(interrupt);
let addr = vector._handler;
if addr as usize == 0 {
return None;
}
Some(IsrCallback::new(core::mem::transmute::<
unsafe extern "C" fn(),
extern "C" fn(),
>(addr)))
}
}
#[instability::unstable]
pub fn bind_handler(interrupt: Interrupt, handler: InterruptHandler) {
unsafe {
let vector = vector_entry(interrupt);
let ptr = (&raw const vector._handler).cast::<usize>().cast_mut();
#[cfg(all(riscv, write_vec_table_monitoring))]
if crate::soc::trap_section_protected() {
crate::debugger::DEBUGGER_LOCK.lock(|| {
let wp = crate::debugger::clear_watchpoint(1);
ptr.write_volatile(handler.handler().address());
crate::debugger::restore_watchpoint(1, wp);
});
enable(interrupt, handler.priority());
return;
}
ptr.write_volatile(handler.handler().address());
}
enable(interrupt, handler.priority());
}
#[inline]
#[instability::unstable]
pub fn enable(interrupt: Interrupt, level: Priority) {
enable_on_cpu(Cpu::current(), interrupt, level);
}
pub(crate) fn enable_on_cpu(cpu: Cpu, interrupt: Interrupt, level: Priority) {
let cpu_interrupt = priority_to_cpu_interrupt(interrupt, level);
map_raw(cpu, interrupt, cpu_interrupt as u32);
}
#[inline]
#[instability::unstable]
pub fn disable(core: Cpu, interrupt: Interrupt) {
map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
}
pub(super) fn map_raw(core: Cpu, interrupt: Interrupt, cpu_interrupt: u32) {
match core {
Cpu::ProCpu => {
INTERRUPT_CORE0::regs()
.core_0_intr_map(interrupt as usize)
.write(|w| unsafe { w.bits(cpu_interrupt) });
}
#[cfg(multi_core)]
Cpu::AppCpu => {
INTERRUPT_CORE1::regs()
.core_1_intr_map(interrupt as usize)
.write(|w| unsafe { w.bits(cpu_interrupt) });
}
}
}
pub(crate) fn mapped_to(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
mapped_to_raw(cpu, interrupt as u32)
}
pub(crate) fn mapped_to_raw(cpu: Cpu, interrupt: u32) -> Option<CpuInterrupt> {
let cpu_intr = match cpu {
Cpu::ProCpu => INTERRUPT_CORE0::regs()
.core_0_intr_map(interrupt as usize)
.read()
.bits(),
#[cfg(multi_core)]
Cpu::AppCpu => INTERRUPT_CORE1::regs()
.core_1_intr_map(interrupt as usize)
.read()
.bits(),
};
CpuInterrupt::from_u32(cpu_intr)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum RunLevel {
ThreadMode,
Interrupt(ElevatedRunLevel),
}
impl RunLevel {
#[procmacros::doc_replace]
#[inline]
pub fn current() -> Self {
let raw = current_raw_runlevel();
unwrap!(Self::try_from_u32(raw))
}
#[inline]
#[instability::unstable]
pub unsafe fn change(to: Self) -> Self {
unsafe { change_current_runlevel(to) }
}
#[procmacros::doc_replace]
#[inline]
pub fn is_thread(&self) -> bool {
matches!(self, RunLevel::ThreadMode)
}
pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
if priority == 0 {
Ok(RunLevel::ThreadMode)
} else {
ElevatedRunLevel::try_from_u32(priority).map(RunLevel::Interrupt)
}
}
}
impl PartialEq<ElevatedRunLevel> for RunLevel {
fn eq(&self, other: &ElevatedRunLevel) -> bool {
*self == RunLevel::Interrupt(*other)
}
}
impl PartialEq<Priority> for RunLevel {
fn eq(&self, other: &Priority) -> bool {
let runlevel = ElevatedRunLevel::from(*other);
*self == RunLevel::Interrupt(runlevel)
}
}
impl From<RunLevel> for u32 {
fn from(level: RunLevel) -> Self {
match level {
RunLevel::ThreadMode => 0,
RunLevel::Interrupt(priority) => priority as u32,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum PriorityError {
InvalidInterruptPriority,
}
#[instability::unstable]
impl TryFrom<u32> for RunLevel {
type Error = PriorityError;
fn try_from(priority: u32) -> Result<Self, Self::Error> {
Self::try_from_u32(priority)
}
}
#[cfg(feature = "rt")]
pub(crate) fn setup_interrupts() {
for peripheral_interrupt in 0..255 {
crate::peripherals::Interrupt::try_from(peripheral_interrupt)
.map(|intr| {
#[cfg(multi_core)]
disable(Cpu::AppCpu, intr);
disable(Cpu::ProCpu, intr);
})
.ok();
}
unsafe { crate::interrupt::init_vectoring() };
}
#[inline(always)]
#[cfg(feature = "rt")]
fn should_handle(core: Cpu, interrupt_nr: u32, level: u32) -> bool {
if let Some(cpu_interrupt) = crate::interrupt::mapped_to_raw(core, interrupt_nr)
&& cpu_interrupt.is_vectored()
&& cpu_interrupt.level() == level
{
true
} else {
false
}
}