#[cfg(esp32)]
pub(crate) use xtensa_lx::interrupt::free;
use crate::{
interrupt::{PriorityError, RunLevel},
peripherals::Interrupt,
};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u32)]
#[instability::unstable]
pub enum CpuInterrupt {
Interrupt0LevelPriority1 = 0,
Interrupt1LevelPriority1 = 1,
Interrupt2LevelPriority1 = 2,
Interrupt3LevelPriority1 = 3,
Interrupt4LevelPriority1 = 4,
Interrupt5LevelPriority1 = 5,
Interrupt6Timer0Priority1 = 6,
Interrupt7SoftwarePriority1 = 7,
Interrupt8LevelPriority1 = 8,
Interrupt9LevelPriority1 = 9,
Interrupt10EdgePriority1 = 10,
Interrupt11ProfilingPriority3 = 11,
Interrupt12LevelPriority1 = 12,
Interrupt13LevelPriority1 = 13,
Interrupt15Timer1Priority3 = 15,
Interrupt17LevelPriority1 = 17,
Interrupt18LevelPriority1 = 18,
Interrupt19LevelPriority2 = 19,
Interrupt20LevelPriority2 = 20,
Interrupt21LevelPriority2 = 21,
Interrupt22EdgePriority3 = 22,
Interrupt23LevelPriority3 = 23,
Interrupt27LevelPriority3 = 27,
Interrupt29SoftwarePriority3 = 29,
}
impl CpuInterrupt {
pub(super) fn from_u32(n: u32) -> Option<Self> {
match n {
0 => Some(Self::Interrupt0LevelPriority1),
1 => Some(Self::Interrupt1LevelPriority1),
2 => Some(Self::Interrupt2LevelPriority1),
3 => Some(Self::Interrupt3LevelPriority1),
4 => Some(Self::Interrupt4LevelPriority1),
5 => Some(Self::Interrupt5LevelPriority1),
6 => Some(Self::Interrupt6Timer0Priority1),
7 => Some(Self::Interrupt7SoftwarePriority1),
8 => Some(Self::Interrupt8LevelPriority1),
9 => Some(Self::Interrupt9LevelPriority1),
10 => Some(Self::Interrupt10EdgePriority1),
11 => Some(Self::Interrupt11ProfilingPriority3),
12 => Some(Self::Interrupt12LevelPriority1),
13 => Some(Self::Interrupt13LevelPriority1),
15 => Some(Self::Interrupt15Timer1Priority3),
17 => Some(Self::Interrupt17LevelPriority1),
18 => Some(Self::Interrupt18LevelPriority1),
19 => Some(Self::Interrupt19LevelPriority2),
20 => Some(Self::Interrupt20LevelPriority2),
21 => Some(Self::Interrupt21LevelPriority2),
22 => Some(Self::Interrupt22EdgePriority3),
23 => Some(Self::Interrupt23LevelPriority3),
27 => Some(Self::Interrupt27LevelPriority3),
29 => Some(Self::Interrupt29SoftwarePriority3),
_ => None,
}
}
#[inline]
#[cfg(feature = "rt")]
pub(crate) fn is_vectored(self) -> bool {
true
}
#[inline]
#[instability::unstable]
pub fn enable(self) {
enable_cpu_interrupt_raw(self as u32);
}
#[inline]
#[instability::unstable]
pub fn clear(self) {
unsafe { xtensa_lx::interrupt::clear(1 << self as u32) };
}
#[inline]
#[instability::unstable]
pub fn priority(self) -> Priority {
match self {
CpuInterrupt::Interrupt0LevelPriority1
| CpuInterrupt::Interrupt1LevelPriority1
| CpuInterrupt::Interrupt2LevelPriority1
| CpuInterrupt::Interrupt3LevelPriority1
| CpuInterrupt::Interrupt4LevelPriority1
| CpuInterrupt::Interrupt5LevelPriority1
| CpuInterrupt::Interrupt6Timer0Priority1
| CpuInterrupt::Interrupt7SoftwarePriority1
| CpuInterrupt::Interrupt8LevelPriority1
| CpuInterrupt::Interrupt9LevelPriority1
| CpuInterrupt::Interrupt10EdgePriority1
| CpuInterrupt::Interrupt12LevelPriority1
| CpuInterrupt::Interrupt13LevelPriority1
| CpuInterrupt::Interrupt17LevelPriority1
| CpuInterrupt::Interrupt18LevelPriority1 => Priority::Priority1,
CpuInterrupt::Interrupt19LevelPriority2
| CpuInterrupt::Interrupt20LevelPriority2
| CpuInterrupt::Interrupt21LevelPriority2 => Priority::Priority2,
CpuInterrupt::Interrupt11ProfilingPriority3
| CpuInterrupt::Interrupt15Timer1Priority3
| CpuInterrupt::Interrupt22EdgePriority3
| CpuInterrupt::Interrupt27LevelPriority3
| CpuInterrupt::Interrupt29SoftwarePriority3
| CpuInterrupt::Interrupt23LevelPriority3 => Priority::Priority3,
}
}
#[inline]
#[cfg(feature = "rt")]
pub(crate) fn level(self) -> u32 {
self.priority() as u32
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
#[non_exhaustive]
pub enum Priority {
Priority1 = 1,
Priority2 = 2,
Priority3 = 3,
}
impl Priority {
#[instability::unstable]
pub const fn max() -> Priority {
Priority::Priority3
}
pub const fn min() -> Priority {
Priority::Priority1
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
#[non_exhaustive]
pub enum ElevatedRunLevel {
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
Level6 = 6,
Level7 = 7,
}
impl ElevatedRunLevel {
#[instability::unstable]
pub const fn max() -> ElevatedRunLevel {
ElevatedRunLevel::Level7
}
pub const fn min() -> ElevatedRunLevel {
ElevatedRunLevel::Level1
}
pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
match priority {
1 => Ok(ElevatedRunLevel::Level1),
2 => Ok(ElevatedRunLevel::Level2),
3 => Ok(ElevatedRunLevel::Level3),
4 => Ok(ElevatedRunLevel::Level4),
5 => Ok(ElevatedRunLevel::Level5),
6 => Ok(ElevatedRunLevel::Level6),
7 => Ok(ElevatedRunLevel::Level7),
_ => Err(PriorityError::InvalidInterruptPriority),
}
}
pub const fn from_priority(priority: Priority) -> Self {
match priority {
Priority::Priority1 => ElevatedRunLevel::Level1,
Priority::Priority2 => ElevatedRunLevel::Level2,
Priority::Priority3 => ElevatedRunLevel::Level3,
}
}
}
impl From<Priority> for ElevatedRunLevel {
fn from(priority: Priority) -> Self {
Self::from_priority(priority)
}
}
#[instability::unstable]
impl TryFrom<u32> for ElevatedRunLevel {
type Error = PriorityError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::try_from_u32(value)
}
}
#[instability::unstable]
impl TryFrom<u8> for ElevatedRunLevel {
type Error = PriorityError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::try_from(value as u32)
}
}
pub(super) const DISABLED_CPU_INTERRUPT: u32 = 16;
pub(crate) fn enable_cpu_interrupt_raw(cpu_interrupt: u32) {
unsafe { xtensa_lx::interrupt::enable_mask(1 << cpu_interrupt) };
}
pub(crate) fn current_raw_runlevel() -> u32 {
xtensa_lx::interrupt::get_level()
}
pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
let token: u32;
unsafe {
match level {
RunLevel::ThreadMode => core::arch::asm!("rsil {0}, 0", out(reg) token),
RunLevel::Interrupt(ElevatedRunLevel::Level1) => {
core::arch::asm!("rsil {0}, 1", out(reg) token)
}
RunLevel::Interrupt(ElevatedRunLevel::Level2) => {
core::arch::asm!("rsil {0}, 2", out(reg) token)
}
RunLevel::Interrupt(ElevatedRunLevel::Level3) => {
core::arch::asm!("rsil {0}, 3", out(reg) token)
}
RunLevel::Interrupt(ElevatedRunLevel::Level4) => {
core::arch::asm!("rsil {0}, 4", out(reg) token)
}
RunLevel::Interrupt(ElevatedRunLevel::Level5) => {
core::arch::asm!("rsil {0}, 5", out(reg) token)
}
RunLevel::Interrupt(ElevatedRunLevel::Level6) => {
core::arch::asm!("rsil {0}, 6", out(reg) token)
}
RunLevel::Interrupt(ElevatedRunLevel::Level7) => {
core::arch::asm!("rsil {0}, 7", out(reg) token)
}
};
}
unwrap!(RunLevel::try_from_u32(token & 0x0F))
}
#[inline(always)]
#[instability::unstable]
pub fn wait_for_interrupt() {
unsafe { core::arch::asm!("waiti 0") };
}
pub(crate) fn priority_to_cpu_interrupt(interrupt: Interrupt, level: Priority) -> CpuInterrupt {
if EDGE_INTERRUPTS.contains(&interrupt) {
match level {
Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
Priority::Priority2 => {
warn!("Priority 2 edge interrupts are not supported, using Priority 1 instead");
CpuInterrupt::Interrupt10EdgePriority1
}
Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
}
} else {
match level {
Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
}
}
}
cfg_if::cfg_if! {
if #[cfg(esp32)] {
pub(crate) const EDGE_INTERRUPTS: [Interrupt; 8] = [
Interrupt::TG0_T0_EDGE,
Interrupt::TG0_T1_EDGE,
Interrupt::TG0_WDT_EDGE,
Interrupt::TG0_LACT_EDGE,
Interrupt::TG1_T0_EDGE,
Interrupt::TG1_T1_EDGE,
Interrupt::TG1_WDT_EDGE,
Interrupt::TG1_LACT_EDGE,
];
} else if #[cfg(esp32s2)] {
pub(crate) const EDGE_INTERRUPTS: [Interrupt; 11] = [
Interrupt::TG0_T0_EDGE,
Interrupt::TG0_T1_EDGE,
Interrupt::TG0_WDT_EDGE,
Interrupt::TG0_LACT_EDGE,
Interrupt::TG1_T0_EDGE,
Interrupt::TG1_T1_EDGE,
Interrupt::TG1_WDT_EDGE,
Interrupt::TG1_LACT_EDGE,
Interrupt::SYSTIMER_TARGET0,
Interrupt::SYSTIMER_TARGET1,
Interrupt::SYSTIMER_TARGET2,
];
} else if #[cfg(esp32s3)] {
pub(crate) const EDGE_INTERRUPTS: [Interrupt; 0] = [];
} else {
compile_error!("Unsupported chip");
}
}
#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
pub(crate) unsafe fn init_vectoring() {
for cpu_int in [
CpuInterrupt::Interrupt10EdgePriority1,
CpuInterrupt::Interrupt22EdgePriority3,
CpuInterrupt::Interrupt1LevelPriority1,
CpuInterrupt::Interrupt19LevelPriority2,
CpuInterrupt::Interrupt23LevelPriority3,
] {
cpu_int.enable();
}
}
#[cfg(feature = "rt")]
pub(crate) mod rt {
use procmacros::ram;
use xtensa_lx_rt::{exception::Context, interrupt::CpuInterruptLevel};
use super::*;
use crate::{interrupt::InterruptStatus, system::Cpu};
#[cfg_attr(place_switch_tables_in_ram, ram)]
pub(crate) static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
#[cfg_attr(place_switch_tables_in_ram, ram)]
pub(crate) static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
#[cfg_attr(place_switch_tables_in_ram, ram)]
pub(crate) static CPU_INTERRUPT_LEVELS: [u32; 8] = [
0, CpuInterruptLevel::Level1.mask(),
CpuInterruptLevel::Level2.mask(),
CpuInterruptLevel::Level3.mask(),
CpuInterruptLevel::Level4.mask(),
CpuInterruptLevel::Level5.mask(),
CpuInterruptLevel::Level6.mask(),
CpuInterruptLevel::Level7.mask(),
];
#[cfg_attr(place_switch_tables_in_ram, ram)]
pub static INTERRUPT_EDGE: InterruptStatus = const {
let mut masks = [0; crate::interrupt::STATUS_WORDS];
let mut idx = 0;
while idx < EDGE_INTERRUPTS.len() {
let interrupt_idx = EDGE_INTERRUPTS[idx] as usize;
let word_idx = interrupt_idx / 32;
masks[word_idx] |= 1 << (interrupt_idx % 32);
idx += 1;
}
InterruptStatus { status: masks }
};
#[unsafe(no_mangle)]
#[ram]
unsafe fn __level_1_interrupt(save_frame: &mut Context) {
unsafe {
handle_interrupts::<1>(save_frame);
}
}
#[unsafe(no_mangle)]
#[ram]
unsafe fn __level_2_interrupt(save_frame: &mut Context) {
unsafe {
handle_interrupts::<2>(save_frame);
}
}
#[unsafe(no_mangle)]
#[ram]
unsafe fn __level_3_interrupt(save_frame: &mut Context) {
unsafe {
handle_interrupts::<3>(save_frame);
}
}
#[inline(always)]
unsafe fn handle_interrupts<const LEVEL: u32>(save_frame: &mut Context) {
let cpu_interrupt_mask = xtensa_lx::interrupt::get()
& xtensa_lx::interrupt::get_mask()
& CPU_INTERRUPT_LEVELS[LEVEL as usize];
if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
unsafe {
xtensa_lx::interrupt::clear(1 << cpu_interrupt_nr);
}
}
if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
unsafe { handler(save_frame) };
}
} else {
let status = if !cfg!(esp32s3) && (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
unsafe { xtensa_lx::interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE) };
INTERRUPT_EDGE
} else {
InterruptStatus::current()
};
let core = Cpu::current();
for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
crate::interrupt::should_handle(core, interrupt_nr as u32, LEVEL)
}) {
let handler = unsafe { crate::pac::__INTERRUPTS[interrupt_nr as usize]._handler };
let handler: fn(&mut Context) = unsafe {
core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler)
};
handler(save_frame);
}
}
}
#[inline]
pub(crate) fn cpu_interrupt_nr_to_cpu_interrupt_handler(
number: u32,
) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
use xtensa_lx_rt::*;
Some(match number {
6 => Timer0,
7 => Software0,
11 => Profiling,
14 => NMI,
15 => Timer1,
16 => Timer2,
29 => Software1,
_ => return None,
})
}
unsafe extern "C" {
fn level4_interrupt(save_frame: &mut Context);
fn level5_interrupt(save_frame: &mut Context);
#[cfg(not(all(feature = "rt", feature = "exception-handler", stack_guard_monitoring)))]
fn level6_interrupt(save_frame: &mut Context);
fn level7_interrupt(save_frame: &mut Context);
}
#[unsafe(no_mangle)]
#[ram]
unsafe fn __level_4_interrupt(save_frame: &mut Context) {
unsafe { level4_interrupt(save_frame) }
}
#[unsafe(no_mangle)]
#[ram]
unsafe fn __level_5_interrupt(save_frame: &mut Context) {
unsafe { level5_interrupt(save_frame) }
}
#[unsafe(no_mangle)]
#[ram]
unsafe fn __level_6_interrupt(save_frame: &mut Context) {
cfg_if::cfg_if! {
if #[cfg(all(feature = "rt", feature = "exception-handler", stack_guard_monitoring))] {
crate::exception_handler::breakpoint_interrupt(save_frame);
} else {
unsafe { level6_interrupt(save_frame) }
}
}
}
#[unsafe(no_mangle)]
#[ram]
unsafe fn __level_7_interrupt(save_frame: &mut Context) {
unsafe { level7_interrupt(save_frame) }
}
}