Scott Mabin 6db771c80e
(re-)introduce the rt feature on esp-hal (#3706)
Adds the `rt` feature which disables `esp_hal::init` and removes
runtime symbols from the global name space.

Disabling the `rt` feature is the recommended way to use esp-hal as a
library.
2025-07-15 09:20:26 +00:00

940 lines
29 KiB
Rust

//! Interrupt handling
//!
//! CPU interrupts 1 through 15 are reserved for each of the possible interrupt
//! priorities.
//!
//! On chips with a PLIC CPU interrupts 1,2,5,6,9 .. 19 are used.
//!
//! ```rust, ignore
//! interrupt1() => Priority::Priority1
//! interrupt2() => Priority::Priority2
//! ...
//! interrupt15() => Priority::Priority15
//! ```
#[cfg(feature = "rt")]
pub use esp_riscv_rt::TrapFrame;
use procmacros::ram;
use riscv::register::{mcause, mtvec};
#[cfg(not(plic))]
pub use self::classic::*;
#[cfg(plic)]
pub use self::plic::*;
pub use self::vectored::*;
use super::InterruptStatus;
use crate::{
pac,
peripherals::{INTERRUPT_CORE0, Interrupt},
system::Cpu,
};
/// Interrupt Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// The priority is not valid
InvalidInterruptPriority,
/// The CPU interrupt is a reserved interrupt
CpuInterruptReserved,
}
/// Interrupt kind
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptKind {
/// Level interrupt
Level,
/// Edge interrupt
Edge,
}
/// Enumeration of available CPU interrupts.
/// It is possible to create a handler for each of the interrupts. (e.g.
/// `interrupt3`)
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CpuInterrupt {
/// Interrupt number 1.
Interrupt1 = 1,
/// Interrupt number 2.
Interrupt2,
/// Interrupt number 3.
Interrupt3,
/// Interrupt number 4.
Interrupt4,
/// Interrupt number 5.
Interrupt5,
/// Interrupt number 6.
Interrupt6,
/// Interrupt number 7.
Interrupt7,
/// Interrupt number 8.
Interrupt8,
/// Interrupt number 9.
Interrupt9,
/// Interrupt number 10.
Interrupt10,
/// Interrupt number 11.
Interrupt11,
/// Interrupt number 12.
Interrupt12,
/// Interrupt number 13.
Interrupt13,
/// Interrupt number 14.
Interrupt14,
/// Interrupt number 15.
Interrupt15,
/// Interrupt number 16.
Interrupt16,
/// Interrupt number 17.
Interrupt17,
/// Interrupt number 18.
Interrupt18,
/// Interrupt number 19.
Interrupt19,
/// Interrupt number 20.
Interrupt20,
/// Interrupt number 21.
Interrupt21,
/// Interrupt number 22.
Interrupt22,
/// Interrupt number 23.
Interrupt23,
/// Interrupt number 24.
Interrupt24,
/// Interrupt number 25.
Interrupt25,
/// Interrupt number 26.
Interrupt26,
/// Interrupt number 27.
Interrupt27,
/// Interrupt number 28.
Interrupt28,
/// Interrupt number 29.
Interrupt29,
/// Interrupt number 30.
Interrupt30,
/// Interrupt number 31.
Interrupt31,
}
/// Interrupt priority levels.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
/// No priority.
None = 0,
/// Priority level 1.
Priority1,
/// Priority level 2.
Priority2,
/// Priority level 3.
Priority3,
/// Priority level 4.
Priority4,
/// Priority level 5.
Priority5,
/// Priority level 6.
Priority6,
/// Priority level 7.
Priority7,
/// Priority level 8.
Priority8,
/// Priority level 9.
Priority9,
/// Priority level 10.
Priority10,
/// Priority level 11.
Priority11,
/// Priority level 12.
Priority12,
/// Priority level 13.
Priority13,
/// Priority level 14.
Priority14,
/// Priority level 15.
Priority15,
}
impl Priority {
/// Maximum interrupt priority
pub const fn max() -> Priority {
Priority::Priority15
}
/// Minimum interrupt priority
pub const fn min() -> Priority {
Priority::Priority1
}
}
impl TryFrom<u32> for Priority {
type Error = Error;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Priority::None),
1 => Ok(Priority::Priority1),
2 => Ok(Priority::Priority2),
3 => Ok(Priority::Priority3),
4 => Ok(Priority::Priority4),
5 => Ok(Priority::Priority5),
6 => Ok(Priority::Priority6),
7 => Ok(Priority::Priority7),
8 => Ok(Priority::Priority8),
9 => Ok(Priority::Priority9),
10 => Ok(Priority::Priority10),
11 => Ok(Priority::Priority11),
12 => Ok(Priority::Priority12),
13 => Ok(Priority::Priority13),
14 => Ok(Priority::Priority14),
15 => Ok(Priority::Priority15),
_ => Err(Error::InvalidInterruptPriority),
}
}
}
impl TryFrom<u8> for Priority {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Priority::try_from(value as u32)
}
}
/// The interrupts reserved by the HAL
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub static RESERVED_INTERRUPTS: &[usize] = PRIORITY_TO_INTERRUPT;
/// Enable an interrupt by directly binding it to a available CPU interrupt
///
/// Unless you are sure, you most likely want to use [`enable`] instead.
///
/// Trying using a reserved interrupt from [`RESERVED_INTERRUPTS`] will return
/// an error.
pub fn enable_direct(
interrupt: Interrupt,
level: Priority,
cpu_interrupt: CpuInterrupt,
) -> Result<(), Error> {
if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
return Err(Error::CpuInterruptReserved);
}
if matches!(level, Priority::None) {
return Err(Error::InvalidInterruptPriority);
}
unsafe {
map(Cpu::current(), interrupt, cpu_interrupt);
set_priority(Cpu::current(), cpu_interrupt, level);
enable_cpu_interrupt(cpu_interrupt);
}
Ok(())
}
/// Disable the given peripheral interrupt.
pub fn disable(_core: Cpu, interrupt: Interrupt) {
unsafe {
let interrupt_number = interrupt as isize;
let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
// set to 0 to disable the peripheral interrupt on chips with an interrupt
// controller other than PLIC use the disabled interrupt 31 otherwise
intr_map_base
.offset(interrupt_number)
.write_volatile(DISABLED_CPU_INTERRUPT);
}
}
/// Get status of peripheral interrupts
#[inline]
pub fn status(_core: Cpu) -> InterruptStatus {
cfg_if::cfg_if! {
if #[cfg(interrupts_status_registers = "3")] {
InterruptStatus::from(
INTERRUPT_CORE0::regs().intr_status_reg_0().read().bits(),
INTERRUPT_CORE0::regs().intr_status_reg_1().read().bits(),
INTERRUPT_CORE0::regs().int_status_reg_2().read().bits(),
)
} else if #[cfg(interrupts_status_registers = "4")] {
InterruptStatus::from(
INTERRUPT_CORE0::regs().intr_status_reg_0().read().bits(),
INTERRUPT_CORE0::regs().intr_status_reg_1().read().bits(),
INTERRUPT_CORE0::regs().intr_status_reg_2().read().bits(),
INTERRUPT_CORE0::regs().intr_status_reg_3().read().bits(),
)
} else {
InterruptStatus::from(
INTERRUPT_CORE0::regs().intr_status_reg_0().read().bits(),
INTERRUPT_CORE0::regs().intr_status_reg_1().read().bits(),
)
}
}
}
/// Assign a peripheral interrupt to an CPU interrupt.
///
/// # Safety
///
/// Do not use CPU interrupts in the [`RESERVED_INTERRUPTS`].
pub unsafe fn map(_core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
let interrupt_number = interrupt as isize;
let cpu_interrupt_number = which as isize;
#[cfg(not(multi_core))]
let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
#[cfg(multi_core)]
let intr_map_base = match _core {
Cpu::ProCpu => crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32,
Cpu::AppCpu => crate::soc::registers::INTERRUPT_MAP_BASE_APP_CPU as *mut u32,
};
unsafe {
intr_map_base
.offset(interrupt_number)
.write_volatile(cpu_interrupt_number as u32 + EXTERNAL_INTERRUPT_OFFSET);
}
}
/// Get cpu interrupt assigned to peripheral interrupt
#[inline]
unsafe fn assigned_cpu_interrupt(interrupt: Interrupt) -> Option<CpuInterrupt> {
let interrupt_number = interrupt as isize;
let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
let cpu_intr = unsafe { intr_map_base.offset(interrupt_number).read_volatile() };
if cpu_intr > 0 && cpu_intr != DISABLED_CPU_INTERRUPT {
Some(unsafe {
core::mem::transmute::<u32, CpuInterrupt>(cpu_intr - EXTERNAL_INTERRUPT_OFFSET)
})
} else {
None
}
}
pub(crate) fn bound_cpu_interrupt_for(_cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
unsafe { assigned_cpu_interrupt(interrupt) }
}
mod vectored {
use super::*;
// Setup interrupts ready for vectoring
#[doc(hidden)]
pub(crate) unsafe fn init_vectoring() {
for (prio, num) in PRIORITY_TO_INTERRUPT.iter().enumerate() {
unsafe {
set_kind(
Cpu::current(),
core::mem::transmute::<u32, CpuInterrupt>(*num as u32),
InterruptKind::Level,
);
set_priority(
Cpu::current(),
core::mem::transmute::<u32, CpuInterrupt>(*num as u32),
core::mem::transmute::<u8, Priority>((prio as u8) + 1),
);
enable_cpu_interrupt(core::mem::transmute::<u32, CpuInterrupt>(*num as u32));
}
}
}
/// Get the interrupts configured for the core at the given priority
/// matching the given status
#[inline]
pub(crate) fn configured_interrupts(
core: Cpu,
status: InterruptStatus,
priority: Priority,
) -> InterruptStatus {
unsafe {
let mut res = InterruptStatus::empty();
for interrupt_nr in status.iterator() {
// safety: cast is safe because of repr(u16)
if let Some(cpu_interrupt) =
assigned_cpu_interrupt(core::mem::transmute::<u16, Interrupt>(
interrupt_nr as u16,
))
&& priority_by_core(core, cpu_interrupt) == priority
{
res.set(interrupt_nr);
}
}
res
}
}
/// Enables a interrupt at a given priority
///
/// Note that interrupts still need to be enabled globally for interrupts
/// to be serviced.
pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
enable_on_cpu(Cpu::current(), interrupt, level)
}
pub(crate) fn enable_on_cpu(
cpu: Cpu,
interrupt: Interrupt,
level: Priority,
) -> Result<(), Error> {
if matches!(level, Priority::None) {
return Err(Error::InvalidInterruptPriority);
}
unsafe {
let cpu_interrupt = core::mem::transmute::<u32, CpuInterrupt>(
PRIORITY_TO_INTERRUPT[(level as usize) - 1] as u32,
);
map(cpu, interrupt, cpu_interrupt);
enable_cpu_interrupt(cpu_interrupt);
}
Ok(())
}
/// Binds the given interrupt to the given handler.
///
/// # Safety
///
/// This will replace any previously bound interrupt handler
pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) {
unsafe {
let ptr = &pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as *const _
as *mut unsafe extern "C" fn();
ptr.write_volatile(handler);
}
}
/// Returns the currently bound interrupt handler.
pub fn bound_handler(interrupt: Interrupt) -> Option<unsafe extern "C" fn()> {
unsafe {
let addr = pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler;
if addr as usize == 0 {
return None;
}
Some(addr)
}
}
}
#[cfg(not(plic))]
mod classic {
use super::{CpuInterrupt, InterruptKind, Priority};
use crate::{peripherals::INTERRUPT_CORE0, system::Cpu};
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 0;
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
// First element is not used, just there to avoid a -1 in the interrupt handler.
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static INTERRUPT_TO_PRIORITY: [u8; 16] =
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
/// Enable a CPU interrupt
///
/// # Safety
///
/// Make sure there is an interrupt handler registered.
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
let cpu_interrupt_number = which as isize;
let intr = INTERRUPT_CORE0::regs();
intr.cpu_int_enable()
.modify(|r, w| unsafe { w.bits((1 << cpu_interrupt_number) | r.bits()) });
}
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
///
/// The vectored interrupt handler will take care of clearing edge interrupt
/// bits.
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
unsafe {
let intr = INTERRUPT_CORE0::regs();
let cpu_interrupt_number = which as isize;
let interrupt_type = match kind {
InterruptKind::Level => 0,
InterruptKind::Edge => 1,
};
intr.cpu_int_type().modify(|r, w| {
w.bits(
r.bits() & !(1 << cpu_interrupt_number)
| (interrupt_type << cpu_interrupt_number),
)
});
}
}
/// Set the priority level of an CPU interrupt
///
/// # Safety
///
/// Great care must be taken when using this function; avoid changing the
/// priority of interrupts 1 - 15.
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
let intr = INTERRUPT_CORE0::regs();
intr.cpu_int_pri(which as usize)
.write(|w| unsafe { w.map().bits(priority as u8) });
}
/// Clear a CPU interrupt
#[inline]
pub fn clear(_core: Cpu, which: CpuInterrupt) {
unsafe {
let cpu_interrupt_number = which as usize;
let intr = INTERRUPT_CORE0::regs();
intr.cpu_int_clear()
.write(|w| w.bits(1 << cpu_interrupt_number));
}
}
/// Get interrupt priority
#[inline]
pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
priority(cpu_interrupt)
}
/// Get interrupt priority - called by assembly code
#[inline]
pub(super) fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
let intr = INTERRUPT_CORE0::regs();
unsafe {
core::mem::transmute::<u8, Priority>(
intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(),
)
}
}
/// Get the current run level (the level below which interrupts are masked).
pub fn current_runlevel() -> Priority {
let intr = INTERRUPT_CORE0::regs();
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits().saturating_sub(1) as u8;
unwrap!(Priority::try_from(prev_interrupt_priority))
}
/// Changes the current run level (the level below which interrupts are
/// masked), and returns the previous run level.
///
/// # Safety
///
/// This function must only be used to raise the runlevel and to restore it
/// to a previous value. It must not be used to arbitrarily lower the
/// runlevel.
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
let prev_interrupt_priority = current_runlevel();
// The CPU responds to interrupts `>= level`, but we want to also disable
// interrupts at `level` so we set the threshold to `level + 1`.
INTERRUPT_CORE0::regs()
.cpu_int_thresh()
.write(|w| unsafe { w.bits(level as u32 + 1) });
prev_interrupt_priority
}
#[cfg(feature = "rt")]
mod rt {
use super::*;
#[unsafe(link_section = ".trap")]
#[unsafe(no_mangle)]
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
// Both C6 and H2 have 5 bits of code. The riscv crate masks 31 bits, which then
// causes a bounds check to be present.
let interrupt_id: usize = riscv::register::mcause::read().bits() & 0x1f;
let intr = INTERRUPT_CORE0::regs();
let interrupt_priority = unsafe {
intr.cpu_int_pri(0)
.as_ptr()
.add(interrupt_id)
.read_volatile()
};
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits();
if interrupt_priority < 15 {
// leave interrupts disabled if interrupt is of max priority.
intr.cpu_int_thresh()
.write(|w| unsafe { w.bits(interrupt_priority + 1) }); // set the prio threshold to 1 more than current interrupt prio
unsafe { riscv::interrupt::enable() };
}
prev_interrupt_priority
}
#[unsafe(link_section = ".trap")]
#[unsafe(no_mangle)]
unsafe extern "C" fn _restore_priority(stored_prio: u32) {
riscv::interrupt::disable();
let intr = INTERRUPT_CORE0::regs();
intr.cpu_int_thresh()
.write(|w| unsafe { w.bits(stored_prio) });
}
/// Globally exported so assembly code can call it.
#[unsafe(no_mangle)]
unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
super::priority(cpu_interrupt)
}
}
}
#[cfg(plic)]
mod plic {
use super::{CpuInterrupt, InterruptKind, Priority};
use crate::{peripherals::PLIC_MX, system::Cpu};
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 31;
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
// don't use interrupts reserved for CLIC (0,3,4,7)
// for some reason also CPU interrupt 8 doesn't work by default since it's
// disabled after reset - so don't use that, too
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
&[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
// First element is not used, just there to avoid a -1 in the interrupt handler.
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static INTERRUPT_TO_PRIORITY: [u8; 20] = [
0, 1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
];
/// Enable a CPU interrupt
///
/// # Safety
///
/// Make sure there is an interrupt handler registered.
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
unsafe {
PLIC_MX::regs().mxint_enable().modify(|r, w| {
let old = r.cpu_mxint_enable().bits();
let new = old | (1 << (which as isize));
w.cpu_mxint_enable().bits(new)
});
}
}
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
///
/// The vectored interrupt handler will take care of clearing edge interrupt
/// bits.
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
let interrupt_type = match kind {
InterruptKind::Level => 0,
InterruptKind::Edge => 1,
};
unsafe {
PLIC_MX::regs().mxint_type().modify(|r, w| {
let old = r.cpu_mxint_type().bits();
let new = old & !(1 << (which as isize)) | (interrupt_type << (which as isize));
w.cpu_mxint_type().bits(new)
});
}
}
/// Set the priority level of an CPU interrupt
///
/// # Safety
///
/// Great care must be taken when using this function; avoid changing the
/// priority of interrupts 1 - 15.
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
unsafe {
PLIC_MX::regs()
.mxint_pri(which as usize)
.modify(|_, w| w.cpu_mxint_pri().bits(priority as u8));
}
}
/// Clear a CPU interrupt
#[inline]
pub fn clear(_core: Cpu, which: CpuInterrupt) {
unsafe {
PLIC_MX::regs().mxint_clear().modify(|r, w| {
let old = r.cpu_mxint_clear().bits();
let new = old | (1 << (which as isize));
w.cpu_mxint_clear().bits(new)
});
}
}
/// Get interrupt priority for the CPU
#[inline]
pub fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
priority(cpu_interrupt)
}
#[inline]
/// Get interrupt priority.
pub fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
let prio = PLIC_MX::regs()
.mxint_pri(cpu_interrupt as usize)
.read()
.cpu_mxint_pri()
.bits();
unsafe { core::mem::transmute::<u8, Priority>(prio) }
}
/// Get the current run level (the level below which interrupts are masked).
pub fn current_runlevel() -> Priority {
let prev_interrupt_priority = PLIC_MX::regs()
.mxint_thresh()
.read()
.cpu_mxint_thresh()
.bits()
.saturating_sub(1);
unwrap!(Priority::try_from(prev_interrupt_priority))
}
/// Changes the current run level (the level below which interrupts are
/// masked), and returns the previous run level.
///
/// # Safety
///
/// This function must only be used to raise the runlevel and to restore it
/// to a previous value. It must not be used to arbitrarily lower the
/// runlevel.
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
let prev_interrupt_priority = current_runlevel();
// The CPU responds to interrupts `>= level`, but we want to also disable
// interrupts at `level` so we set the threshold to `level + 1`.
PLIC_MX::regs()
.mxint_thresh()
.write(|w| unsafe { w.cpu_mxint_thresh().bits(level as u8 + 1) });
prev_interrupt_priority
}
#[cfg(feature = "rt")]
mod rt {
use super::*;
/// Get interrupt priority - called by assembly code
#[unsafe(no_mangle)]
pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
super::priority(cpu_interrupt)
}
#[unsafe(no_mangle)]
#[unsafe(link_section = ".trap")]
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
let interrupt_id: usize = riscv::register::mcause::read().code(); // MSB is whether its exception or interrupt.
let interrupt_priority = PLIC_MX::regs()
.mxint_pri(interrupt_id)
.read()
.cpu_mxint_pri()
.bits();
let prev_interrupt_priority = PLIC_MX::regs()
.mxint_thresh()
.read()
.cpu_mxint_thresh()
.bits();
if interrupt_priority < 15 {
// leave interrupts disabled if interrupt is of max priority.
PLIC_MX::regs()
.mxint_thresh()
.write(|w| unsafe { w.cpu_mxint_thresh().bits(interrupt_priority + 1) });
unsafe {
riscv::interrupt::enable();
}
}
prev_interrupt_priority as u32
}
#[unsafe(no_mangle)]
#[unsafe(link_section = ".trap")]
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
riscv::interrupt::disable();
PLIC_MX::regs()
.mxint_thresh()
.write(|w| unsafe { w.cpu_mxint_thresh().bits(stored_prio as u8) });
}
}
}
#[cfg(feature = "rt")]
mod rt {
use esp_riscv_rt::TrapFrame;
use super::*;
/// # Safety
///
/// This function is called from an assembly trap handler.
#[doc(hidden)]
#[unsafe(link_section = ".trap.rust")]
#[unsafe(export_name = "_start_trap_rust_hal")]
unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
assert!(
mcause::read().is_exception(),
"Arrived into _start_trap_rust_hal but mcause is not an exception!"
);
unsafe extern "C" {
fn ExceptionHandler(tf: *mut TrapFrame);
}
unsafe {
ExceptionHandler(trap_frame);
}
}
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe fn _setup_interrupts() {
unsafe extern "C" {
static _vector_table: *const u32;
}
unsafe {
// disable all known interrupts
// at least after the 2nd stage bootloader there are some interrupts enabled
// (e.g. UART)
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();
}
let vec_table = &_vector_table as *const _ as usize;
mtvec::write(vec_table, mtvec::TrapMode::Vectored);
crate::interrupt::init_vectoring();
};
#[cfg(plic)]
unsafe {
core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
}
}
#[unsafe(no_mangle)]
#[ram]
unsafe fn handle_interrupts(cpu_intr: CpuInterrupt, context: &mut TrapFrame) {
let core = Cpu::current();
let status = status(core);
// this has no effect on level interrupts, but the interrupt may be an edge one
// so we clear it anyway
clear(core, cpu_intr);
let priority = INTERRUPT_TO_PRIORITY[cpu_intr as usize];
let prio: Priority = unsafe { core::mem::transmute(priority) };
let configured_interrupts = vectored::configured_interrupts(core, status, prio);
for interrupt_nr in configured_interrupts.iterator() {
// Don't use `Interrupt::try_from`. It's slower and placed in flash
let interrupt: Interrupt = unsafe { core::mem::transmute(interrupt_nr as u16) };
unsafe {
handle_interrupt(interrupt, context);
}
}
}
#[inline(always)]
unsafe fn handle_interrupt(interrupt: Interrupt, save_frame: &mut TrapFrame) {
unsafe extern "C" {
// defined in each hal
fn EspDefaultHandler(interrupt: Interrupt);
}
let handler = unsafe { pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler };
if core::ptr::eq(
handler as *const _,
EspDefaultHandler as *const unsafe extern "C" fn(),
) {
unsafe { EspDefaultHandler(interrupt) };
} else {
let handler: fn(&mut TrapFrame) = unsafe {
core::mem::transmute::<unsafe extern "C" fn(), fn(&mut TrapFrame)>(handler)
};
handler(save_frame);
}
}
// The compiler generates quite unfortunate code for
// ```rust,ignore
// #[no_mangle]
// #[ram]
// unsafe fn interrupt1(context: &mut TrapFrame) {
// handle_interrupts(CpuInterrupt::Interrupt1, context)
// }
// ```
//
// Resulting in
// ```asm,ignore
// interrupt1:
// add sp,sp,-16
// sw ra,12(sp)
// sw s0,8(sp)
// add s0,sp,16
// mv a1,a0
// li a0,1
// lw ra,12(sp)
// lw s0,8(sp)
// add sp,sp,16
// auipc t1,0x0
// jr handle_interrupts
// ```
//
// We can do better manually - use Rust again once/if that changes
macro_rules! interrupt_handler {
($num:literal) => {
core::arch::global_asm! {
concat!(
r#"
.section .rwtext, "ax"
.global interrupt"#,$num,r#"
interrupt"#,$num,r#":
mv a1, a0
li a0,"#,$num,r#"
j handle_interrupts
"#
)
}
};
}
interrupt_handler!(1);
interrupt_handler!(2);
interrupt_handler!(3);
interrupt_handler!(4);
interrupt_handler!(5);
interrupt_handler!(6);
interrupt_handler!(7);
interrupt_handler!(8);
interrupt_handler!(9);
interrupt_handler!(10);
interrupt_handler!(11);
interrupt_handler!(12);
interrupt_handler!(13);
interrupt_handler!(14);
interrupt_handler!(15);
#[cfg(plic)]
interrupt_handler!(16);
#[cfg(plic)]
interrupt_handler!(17);
#[cfg(plic)]
interrupt_handler!(18);
#[cfg(plic)]
interrupt_handler!(19);
}