feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+229
@@ -0,0 +1,229 @@
|
||||
//! # Interrupt support
|
||||
//!
|
||||
//! ## Overview
|
||||
//! This module routes one or more peripheral interrupt sources to any one
|
||||
//! of the CPU’s peripheral interrupts.
|
||||
//!
|
||||
//! ## Configuration
|
||||
//! Usually peripheral drivers offer a mechanism to register your interrupt
|
||||
//! handler. e.g. the systimer offers `set_interrupt_handler` to register a
|
||||
//! handler for a specific alarm. Other drivers might take an interrupt handler
|
||||
//! as an optional parameter to their constructor.
|
||||
//!
|
||||
//! This is the preferred way to register handlers.
|
||||
//!
|
||||
//! There are additional ways to register interrupt handlers which are generally
|
||||
//! only meant to be used in very special situations (mostly internal to the HAL
|
||||
//! or the supporting libraries). Those are outside the scope of this
|
||||
//! documentation.
|
||||
//!
|
||||
//! It is even possible, but not recommended, to bind an interrupt directly to a
|
||||
//! CPU interrupt. This can offer lower latency, at the cost of more complexity
|
||||
//! in the interrupt handler. See the `direct_vectoring.rs` example
|
||||
//!
|
||||
//! We reserve a number of CPU interrupts, which cannot be used; see
|
||||
//! [`RESERVED_INTERRUPTS`].
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ### Using the peripheral driver to register an interrupt handler
|
||||
//!
|
||||
//! ```rust, no_run
|
||||
#![doc = crate::before_snippet!()]
|
||||
//! let mut sw_int =
|
||||
//! SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||
//! critical_section::with(|cs| {
|
||||
//! sw_int
|
||||
//! .software_interrupt0
|
||||
//! .set_interrupt_handler(swint0_handler);
|
||||
//! SWINT0
|
||||
//! .borrow_ref_mut(cs)
|
||||
//! .replace(sw_int.software_interrupt0);
|
||||
//! });
|
||||
//!
|
||||
//! critical_section::with(|cs| {
|
||||
//! SWINT0.borrow_ref(cs).as_ref().unwrap().raise();
|
||||
//! });
|
||||
//! #
|
||||
//! # loop {}
|
||||
//! # }
|
||||
//!
|
||||
//! # use core::cell::RefCell;
|
||||
//! #
|
||||
//! # use critical_section::Mutex;
|
||||
//! # use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl};
|
||||
//! # use esp_hal::interrupt::Priority;
|
||||
//! # use esp_hal::interrupt::InterruptHandler;
|
||||
//! #
|
||||
//! static SWINT0: Mutex<RefCell<Option<SoftwareInterrupt<0>>>> =
|
||||
//! Mutex::new(RefCell::new(None));
|
||||
//!
|
||||
//! #[handler(priority = Priority::Priority1)]
|
||||
//! fn swint0_handler() {
|
||||
//! // esp_println::println!("SW interrupt0");
|
||||
//! critical_section::with(|cs| {
|
||||
//! SWINT0.borrow_ref(cs).as_ref().unwrap().reset();
|
||||
//! });
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use core::ops::BitAnd;
|
||||
|
||||
#[cfg(riscv)]
|
||||
pub use self::riscv::*;
|
||||
#[cfg(xtensa)]
|
||||
pub use self::xtensa::*;
|
||||
|
||||
#[cfg(riscv)]
|
||||
mod riscv;
|
||||
#[cfg(xtensa)]
|
||||
mod xtensa;
|
||||
|
||||
pub mod software;
|
||||
|
||||
/// An interrupt handler
|
||||
#[cfg_attr(
|
||||
multi_core,
|
||||
doc = "**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, PartialEq, Eq)]
|
||||
pub struct InterruptHandler {
|
||||
f: extern "C" fn(),
|
||||
prio: Priority,
|
||||
}
|
||||
|
||||
impl InterruptHandler {
|
||||
/// Creates a new [InterruptHandler] which will call the given function at
|
||||
/// the given priority.
|
||||
pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
|
||||
Self { f, prio }
|
||||
}
|
||||
|
||||
/// The function to be called
|
||||
#[inline]
|
||||
pub fn handler(&self) -> extern "C" fn() {
|
||||
self.f
|
||||
}
|
||||
|
||||
/// Priority to be used when registering the interrupt
|
||||
#[inline]
|
||||
pub fn priority(&self) -> Priority {
|
||||
self.prio
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(large_intr_status)]
|
||||
const STATUS_WORDS: usize = 3;
|
||||
|
||||
#[cfg(very_large_intr_status)]
|
||||
const STATUS_WORDS: usize = 4;
|
||||
|
||||
#[cfg(not(any(large_intr_status, very_large_intr_status)))]
|
||||
const STATUS_WORDS: usize = 2;
|
||||
|
||||
/// Representation of peripheral-interrupt status bits.
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct InterruptStatus {
|
||||
status: [u32; STATUS_WORDS],
|
||||
}
|
||||
|
||||
impl InterruptStatus {
|
||||
const fn empty() -> Self {
|
||||
InterruptStatus {
|
||||
status: [0u32; STATUS_WORDS],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(large_intr_status)]
|
||||
const fn from(w0: u32, w1: u32, w2: u32) -> Self {
|
||||
Self {
|
||||
status: [w0, w1, w2],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(very_large_intr_status)]
|
||||
const fn from(w0: u32, w1: u32, w2: u32, w3: u32) -> Self {
|
||||
Self {
|
||||
status: [w0, w1, w2, w3],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(large_intr_status, very_large_intr_status)))]
|
||||
const fn from(w0: u32, w1: u32) -> Self {
|
||||
Self { status: [w0, w1] }
|
||||
}
|
||||
|
||||
/// Is the given interrupt bit set
|
||||
pub fn is_set(&self, interrupt: u16) -> bool {
|
||||
(self.status[interrupt as usize / 32] & (1 << (interrupt as u32 % 32))) != 0
|
||||
}
|
||||
|
||||
/// Set the given interrupt status bit
|
||||
pub fn set(&mut self, interrupt: u16) {
|
||||
self.status[interrupt as usize / 32] |= 1 << (interrupt as u32 % 32);
|
||||
}
|
||||
|
||||
/// Return an iterator over the set interrupt status bits
|
||||
pub fn iterator(&self) -> InterruptStatusIterator {
|
||||
InterruptStatusIterator {
|
||||
status: *self,
|
||||
idx: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for InterruptStatus {
|
||||
type Output = InterruptStatus;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
#[cfg(large_intr_status)]
|
||||
return Self::Output {
|
||||
status: [
|
||||
self.status[0] & rhs.status[0],
|
||||
self.status[1] & rhs.status[1],
|
||||
self.status[2] & rhs.status[2],
|
||||
],
|
||||
};
|
||||
|
||||
#[cfg(very_large_intr_status)]
|
||||
return Self::Output {
|
||||
status: [
|
||||
self.status[0] & rhs.status[0],
|
||||
self.status[1] & rhs.status[1],
|
||||
self.status[2] & rhs.status[2],
|
||||
self.status[3] & rhs.status[3],
|
||||
],
|
||||
};
|
||||
|
||||
#[cfg(not(any(large_intr_status, very_large_intr_status)))]
|
||||
return Self::Output {
|
||||
status: [
|
||||
self.status[0] & rhs.status[0],
|
||||
self.status[1] & rhs.status[1],
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over set interrupt status bits
|
||||
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.idx = i;
|
||||
self.status.status[i] &= !1 << bit;
|
||||
return Some((bit + 32 * i as u32) as u8);
|
||||
}
|
||||
}
|
||||
self.idx = usize::MAX;
|
||||
None
|
||||
}
|
||||
}
|
||||
+820
@@ -0,0 +1,820 @@
|
||||
//! 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
|
||||
//! ```
|
||||
|
||||
pub use esp_riscv_rt::TrapFrame;
|
||||
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::{
|
||||
peripherals::{self, Interrupt},
|
||||
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)]
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
/// The interrupts reserved by the HAL
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub static RESERVED_INTERRUPTS: &[usize] = INTERRUPT_TO_PRIORITY;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is called from an assembly trap handler.
|
||||
#[doc(hidden)]
|
||||
#[link_section = ".trap.rust"]
|
||||
#[export_name = "_start_trap_rust_hal"]
|
||||
pub 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!"
|
||||
);
|
||||
extern "C" {
|
||||
fn ExceptionHandler(tf: *mut TrapFrame);
|
||||
}
|
||||
ExceptionHandler(trap_frame);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
pub fn _setup_interrupts() {
|
||||
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::soc::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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(large_intr_status)]
|
||||
unsafe {
|
||||
InterruptStatus::from(
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_0()
|
||||
.read()
|
||||
.bits(),
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_1()
|
||||
.read()
|
||||
.bits(),
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.int_status_reg_2()
|
||||
.read()
|
||||
.bits(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(very_large_intr_status)]
|
||||
unsafe {
|
||||
InterruptStatus::from(
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_0()
|
||||
.read()
|
||||
.bits(),
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_1()
|
||||
.read()
|
||||
.bits(),
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_2()
|
||||
.read()
|
||||
.bits(),
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_3()
|
||||
.read()
|
||||
.bits(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(any(large_intr_status, very_large_intr_status)))]
|
||||
unsafe {
|
||||
InterruptStatus::from(
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_0()
|
||||
.read()
|
||||
.bits(),
|
||||
(*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.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,
|
||||
};
|
||||
|
||||
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 = intr_map_base.offset(interrupt_number).read_volatile();
|
||||
if cpu_intr > 0 && cpu_intr != DISABLED_CPU_INTERRUPT {
|
||||
Some(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 procmacros::ram;
|
||||
|
||||
use super::*;
|
||||
|
||||
// Setup interrupts ready for vectoring
|
||||
#[doc(hidden)]
|
||||
pub(crate) unsafe fn init_vectoring() {
|
||||
for (prio, num) in PRIORITY_TO_INTERRUPT.iter().enumerate() {
|
||||
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]
|
||||
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,
|
||||
))
|
||||
{
|
||||
if priority_by_core(core, cpu_interrupt) == priority {
|
||||
res.set(interrupt_nr as u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
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> {
|
||||
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::current(), 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()) {
|
||||
let ptr = &peripherals::__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 = peripherals::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler;
|
||||
if addr as usize == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(addr)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 prio: Priority =
|
||||
unsafe { core::mem::transmute(INTERRUPT_TO_PRIORITY[cpu_intr as usize - 1] as u8) };
|
||||
let configured_interrupts = 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) };
|
||||
handle_interrupt(interrupt, context);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn handle_interrupt(interrupt: Interrupt, save_frame: &mut TrapFrame) {
|
||||
extern "C" {
|
||||
// defined in each hal
|
||||
fn EspDefaultHandler(interrupt: Interrupt);
|
||||
}
|
||||
|
||||
let handler = peripherals::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler;
|
||||
|
||||
if core::ptr::eq(
|
||||
handler as *const _,
|
||||
EspDefaultHandler as *const unsafe extern "C" fn(),
|
||||
) {
|
||||
EspDefaultHandler(interrupt);
|
||||
} else {
|
||||
let handler: fn(&mut TrapFrame) =
|
||||
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);
|
||||
}
|
||||
|
||||
#[cfg(not(plic))]
|
||||
mod classic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::Cpu;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 0;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, 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];
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub(super) static INTERRUPT_TO_PRIORITY: &[usize] =
|
||||
&[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 = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
intr.cpu_int_enable()
|
||||
.modify(|r, w| 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 = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
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 = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
intr.cpu_int_pri(which as usize)
|
||||
.write(|w| 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 isize;
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
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 {
|
||||
unsafe { priority(cpu_interrupt) }
|
||||
}
|
||||
|
||||
/// Get interrupt priority - called by assembly code
|
||||
#[inline]
|
||||
pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
core::mem::transmute::<u8, Priority>(
|
||||
intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(),
|
||||
)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[link_section = ".trap"]
|
||||
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
|
||||
use super::mcause;
|
||||
let interrupt_id: usize = mcause::read().code(); // MSB is whether its exception or interrupt.
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
let interrupt_priority = 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| w.bits(interrupt_priority + 1)); // set the prio threshold to 1 more than current interrupt prio
|
||||
unsafe {
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
prev_interrupt_priority
|
||||
}
|
||||
#[no_mangle]
|
||||
#[link_section = ".trap"]
|
||||
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
|
||||
riscv::interrupt::disable();
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
intr.cpu_int_thresh().write(|w| w.bits(stored_prio));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(plic)]
|
||||
mod plic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::Cpu;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub(super) static DISABLED_CPU_INTERRUPT: u32 = 31;
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, 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, 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];
|
||||
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub(super) static INTERRUPT_TO_PRIORITY: &[usize] = &[
|
||||
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 {
|
||||
let plic = &*crate::peripherals::PLIC_MX::PTR;
|
||||
plic.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) {
|
||||
unsafe {
|
||||
let plic = &*crate::peripherals::PLIC_MX::PTR;
|
||||
let interrupt_type = match kind {
|
||||
InterruptKind::Level => 0,
|
||||
InterruptKind::Edge => 1,
|
||||
};
|
||||
|
||||
plic.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 {
|
||||
let plic = &*crate::peripherals::PLIC_MX::PTR;
|
||||
plic.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 {
|
||||
let plic = &*crate::peripherals::PLIC_MX::PTR;
|
||||
plic.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
|
||||
#[inline]
|
||||
pub(super) unsafe extern "C" fn priority_by_core(
|
||||
_core: Cpu,
|
||||
cpu_interrupt: CpuInterrupt,
|
||||
) -> Priority {
|
||||
unsafe { priority(cpu_interrupt) }
|
||||
}
|
||||
|
||||
/// Get interrupt priority - called by assembly code
|
||||
#[inline]
|
||||
pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let plic = &*crate::peripherals::PLIC_MX::PTR;
|
||||
let prio = plic
|
||||
.mxint_pri(cpu_interrupt as usize)
|
||||
.read()
|
||||
.cpu_mxint_pri()
|
||||
.bits();
|
||||
core::mem::transmute::<u8, Priority>(prio)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[link_section = ".trap"]
|
||||
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
|
||||
use super::mcause;
|
||||
let plic = &*crate::peripherals::PLIC_MX::PTR;
|
||||
|
||||
let interrupt_id: usize = mcause::read().code(); // MSB is whether its exception or interrupt.
|
||||
let interrupt_priority = plic.mxint_pri(interrupt_id).read().cpu_mxint_pri().bits();
|
||||
|
||||
let prev_interrupt_priority = plic.mxint_thresh().read().cpu_mxint_thresh().bits();
|
||||
if interrupt_priority < 15 {
|
||||
// leave interrupts disabled if interrupt is of max priority.
|
||||
plic.mxint_thresh()
|
||||
.write(|w| w.cpu_mxint_thresh().bits(interrupt_priority + 1));
|
||||
unsafe {
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
prev_interrupt_priority as u32
|
||||
}
|
||||
#[no_mangle]
|
||||
#[link_section = ".trap"]
|
||||
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
|
||||
riscv::interrupt::disable();
|
||||
let plic = &*crate::peripherals::PLIC_MX::PTR;
|
||||
plic.mxint_thresh()
|
||||
.write(|w| w.cpu_mxint_thresh().bits(stored_prio as u8));
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
//! # Software Interrupts
|
||||
//!
|
||||
//! The [`SoftwareInterruptControl`] struct gives access to the available
|
||||
//! software interrupts.
|
||||
//!
|
||||
//! The [`SoftwareInterrupt`] struct allows raising or resetting software
|
||||
//! interrupts using the [`raise()`][SoftwareInterrupt::raise] and
|
||||
//! [`reset()`][SoftwareInterrupt::reset] methods.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ```rust, no_run
|
||||
#![doc = crate::before_snippet!()]
|
||||
//! let sw_ints =
|
||||
//! SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||
//!
|
||||
//! // Take the interrupt you want to use.
|
||||
//! let mut int0 = sw_ints.software_interrupt0;
|
||||
//!
|
||||
//! // Set up the interrupt handler. Do this in a critical section so the global
|
||||
//! // contains the interrupt object before the interrupt is triggered.
|
||||
//! critical_section::with(|cs| {
|
||||
//! int0.set_interrupt_handler(interrupt_handler);
|
||||
//! SWINT0.borrow_ref_mut(cs).replace(int0);
|
||||
//! });
|
||||
//! # }
|
||||
//!
|
||||
//! # use core::cell::RefCell;
|
||||
//! # use critical_section::Mutex;
|
||||
//! # use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl};
|
||||
//! // ... somewhere outside of your main function
|
||||
//!
|
||||
//! // Define a shared handle to the software interrupt.
|
||||
//! static SWINT0: Mutex<RefCell<Option<SoftwareInterrupt<0>>>> =
|
||||
//! Mutex::new(RefCell::new(None));
|
||||
//!
|
||||
//! #[handler]
|
||||
//! fn interrupt_handler() {
|
||||
//! // esp_println::println!("SW interrupt0 handled");
|
||||
//!
|
||||
//! // Clear the interrupt request.
|
||||
//! critical_section::with(|cs| {
|
||||
//! SWINT0.borrow_ref(cs).as_ref().unwrap().reset();
|
||||
//! });
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::{interrupt::InterruptHandler, InterruptConfigurable};
|
||||
|
||||
/// A software interrupt can be triggered by software.
|
||||
#[non_exhaustive]
|
||||
pub struct SoftwareInterrupt<const NUM: u8>;
|
||||
|
||||
impl<const NUM: u8> SoftwareInterrupt<NUM> {
|
||||
/// Sets the interrupt handler for this software-interrupt
|
||||
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
|
||||
let interrupt = match NUM {
|
||||
0 => crate::peripherals::Interrupt::FROM_CPU_INTR0,
|
||||
1 => crate::peripherals::Interrupt::FROM_CPU_INTR1,
|
||||
2 => crate::peripherals::Interrupt::FROM_CPU_INTR2,
|
||||
3 => crate::peripherals::Interrupt::FROM_CPU_INTR3,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
for core in crate::Cpu::other() {
|
||||
crate::interrupt::disable(core, interrupt);
|
||||
}
|
||||
unsafe { crate::interrupt::bind_interrupt(interrupt, handler.handler()) };
|
||||
unwrap!(crate::interrupt::enable(interrupt, handler.priority()));
|
||||
}
|
||||
|
||||
/// Trigger this software-interrupt
|
||||
pub fn raise(&self) {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2))] {
|
||||
let system = unsafe { &*crate::peripherals::INTPRI::PTR };
|
||||
} else {
|
||||
let system = unsafe { &*crate::peripherals::SYSTEM::PTR };
|
||||
}
|
||||
}
|
||||
|
||||
match NUM {
|
||||
0 => system
|
||||
.cpu_intr_from_cpu_0()
|
||||
.write(|w| w.cpu_intr_from_cpu_0().set_bit()),
|
||||
1 => system
|
||||
.cpu_intr_from_cpu_1()
|
||||
.write(|w| w.cpu_intr_from_cpu_1().set_bit()),
|
||||
2 => system
|
||||
.cpu_intr_from_cpu_2()
|
||||
.write(|w| w.cpu_intr_from_cpu_2().set_bit()),
|
||||
3 => system
|
||||
.cpu_intr_from_cpu_3()
|
||||
.write(|w| w.cpu_intr_from_cpu_3().set_bit()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Resets this software-interrupt
|
||||
pub fn reset(&self) {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2))] {
|
||||
let system = unsafe { &*crate::peripherals::INTPRI::PTR };
|
||||
} else {
|
||||
let system = unsafe { &*crate::peripherals::SYSTEM::PTR };
|
||||
}
|
||||
}
|
||||
|
||||
match NUM {
|
||||
0 => system
|
||||
.cpu_intr_from_cpu_0()
|
||||
.write(|w| w.cpu_intr_from_cpu_0().clear_bit()),
|
||||
1 => system
|
||||
.cpu_intr_from_cpu_1()
|
||||
.write(|w| w.cpu_intr_from_cpu_1().clear_bit()),
|
||||
2 => system
|
||||
.cpu_intr_from_cpu_2()
|
||||
.write(|w| w.cpu_intr_from_cpu_2().clear_bit()),
|
||||
3 => system
|
||||
.cpu_intr_from_cpu_3()
|
||||
.write(|w| w.cpu_intr_from_cpu_3().clear_bit()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Unsafely create an instance of this peripheral out of thin air.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you're only using one instance of this type at a
|
||||
/// time.
|
||||
#[inline]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NUM: u8> crate::peripheral::Peripheral for SoftwareInterrupt<NUM> {
|
||||
type P = SoftwareInterrupt<NUM>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NUM: u8> crate::private::Sealed for SoftwareInterrupt<NUM> {}
|
||||
|
||||
impl<const NUM: u8> InterruptConfigurable for SoftwareInterrupt<NUM> {
|
||||
fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) {
|
||||
SoftwareInterrupt::set_interrupt_handler(self, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/// This gives access to the available software interrupts.
|
||||
///
|
||||
/// This struct contains several instances of software interrupts that can be
|
||||
/// used for signaling between different parts of a program or system. Each
|
||||
/// interrupt is identified by an index (0 to 3).
|
||||
#[cfg_attr(
|
||||
multi_core,
|
||||
doc = r#"
|
||||
|
||||
Please note: Software interrupt 3 is reserved
|
||||
for inter-processor communication when using
|
||||
`esp-hal-embassy`."#
|
||||
)]
|
||||
#[non_exhaustive]
|
||||
pub struct SoftwareInterruptControl {
|
||||
/// Software interrupt 0.
|
||||
pub software_interrupt0: SoftwareInterrupt<0>,
|
||||
/// Software interrupt 1.
|
||||
pub software_interrupt1: SoftwareInterrupt<1>,
|
||||
/// Software interrupt 2.
|
||||
pub software_interrupt2: SoftwareInterrupt<2>,
|
||||
#[cfg(not(all(feature = "__esp_hal_embassy", multi_core)))]
|
||||
/// Software interrupt 3. Only available when not using `esp-hal-embassy`,
|
||||
/// or on single-core systems.
|
||||
pub software_interrupt3: SoftwareInterrupt<3>,
|
||||
}
|
||||
|
||||
impl SoftwareInterruptControl {
|
||||
/// Create a new instance of the software interrupt control.
|
||||
pub fn new(_peripheral: crate::peripherals::SW_INTERRUPT) -> Self {
|
||||
SoftwareInterruptControl {
|
||||
software_interrupt0: SoftwareInterrupt {},
|
||||
software_interrupt1: SoftwareInterrupt {},
|
||||
software_interrupt2: SoftwareInterrupt {},
|
||||
#[cfg(not(all(feature = "__esp_hal_embassy", multi_core)))]
|
||||
software_interrupt3: SoftwareInterrupt {},
|
||||
}
|
||||
}
|
||||
}
|
||||
+714
@@ -0,0 +1,714 @@
|
||||
//! Interrupt handling
|
||||
|
||||
use xtensa_lx::interrupt;
|
||||
use xtensa_lx_rt::exception::Context;
|
||||
|
||||
pub use self::vectored::*;
|
||||
use super::InterruptStatus;
|
||||
use crate::{
|
||||
peripherals::{self, Interrupt},
|
||||
Cpu,
|
||||
};
|
||||
|
||||
/// Interrupt Error
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// The given interrupt is not a valid interrupt
|
||||
InvalidInterrupt,
|
||||
/// The CPU interrupt is a reserved interrupt
|
||||
CpuInterruptReserved,
|
||||
}
|
||||
|
||||
/// Enumeration of available CPU interrupts
|
||||
///
|
||||
/// It's possible to create one handler per priority level. (e.g
|
||||
/// `level1_interrupt`)
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u32)]
|
||||
pub enum CpuInterrupt {
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt0LevelPriority1 = 0,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt1LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt2LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt3LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt4LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt5LevelPriority1,
|
||||
/// Timer 0 interrupt with priority 1.
|
||||
Interrupt6Timer0Priority1,
|
||||
/// Software-triggered interrupt with priority 1.
|
||||
Interrupt7SoftwarePriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt8LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt9LevelPriority1,
|
||||
/// Edge-triggered interrupt with priority 1.
|
||||
Interrupt10EdgePriority1,
|
||||
/// Profiling-related interrupt with priority 3.
|
||||
Interrupt11ProfilingPriority3,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt12LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt13LevelPriority1,
|
||||
/// Non-maskable interrupt (NMI) with priority 7.
|
||||
Interrupt14NmiPriority7,
|
||||
/// Timer 1 interrupt with priority 3.
|
||||
Interrupt15Timer1Priority3,
|
||||
/// Timer 2 interrupt with priority 5.
|
||||
Interrupt16Timer2Priority5,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt17LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 1.
|
||||
Interrupt18LevelPriority1,
|
||||
/// Level-triggered interrupt with priority 2.
|
||||
Interrupt19LevelPriority2,
|
||||
/// Level-triggered interrupt with priority 2.
|
||||
Interrupt20LevelPriority2,
|
||||
/// Level-triggered interrupt with priority 2.
|
||||
Interrupt21LevelPriority2,
|
||||
/// Edge-triggered interrupt with priority 3.
|
||||
Interrupt22EdgePriority3,
|
||||
/// Level-triggered interrupt with priority 3.
|
||||
Interrupt23LevelPriority3,
|
||||
/// Level-triggered interrupt with priority 4.
|
||||
Interrupt24LevelPriority4,
|
||||
/// Level-triggered interrupt with priority 4.
|
||||
Interrupt25LevelPriority4,
|
||||
/// Level-triggered interrupt with priority 5.
|
||||
Interrupt26LevelPriority5,
|
||||
/// Level-triggered interrupt with priority 3.
|
||||
Interrupt27LevelPriority3,
|
||||
/// Edge-triggered interrupt with priority 4.
|
||||
Interrupt28EdgePriority4,
|
||||
/// Software-triggered interrupt with priority 3.
|
||||
Interrupt29SoftwarePriority3,
|
||||
/// Edge-triggered interrupt with priority 4.
|
||||
Interrupt30EdgePriority4,
|
||||
/// Edge-triggered interrupt with priority 5.
|
||||
Interrupt31EdgePriority5,
|
||||
}
|
||||
|
||||
impl CpuInterrupt {
|
||||
fn from_u32(n: u32) -> Option<Self> {
|
||||
if n < 32 {
|
||||
Some(unsafe { core::mem::transmute::<u32, Self>(n) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_internal(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Interrupt6Timer0Priority1
|
||||
| Self::Interrupt7SoftwarePriority1
|
||||
| Self::Interrupt11ProfilingPriority3
|
||||
| Self::Interrupt15Timer1Priority3
|
||||
| Self::Interrupt16Timer2Priority5
|
||||
| Self::Interrupt29SoftwarePriority3
|
||||
)
|
||||
}
|
||||
|
||||
fn is_peripheral(self) -> bool {
|
||||
!self.is_internal()
|
||||
}
|
||||
}
|
||||
|
||||
/// The interrupts reserved by the HAL
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub static RESERVED_INTERRUPTS: &[usize] = &[
|
||||
CpuInterrupt::Interrupt1LevelPriority1 as _,
|
||||
CpuInterrupt::Interrupt19LevelPriority2 as _,
|
||||
CpuInterrupt::Interrupt23LevelPriority3 as _,
|
||||
CpuInterrupt::Interrupt10EdgePriority1 as _,
|
||||
CpuInterrupt::Interrupt22EdgePriority3 as _,
|
||||
];
|
||||
|
||||
pub(crate) fn setup_interrupts() {
|
||||
// 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::soc::peripherals::Interrupt::try_from(peripheral_interrupt)
|
||||
.map(|intr| {
|
||||
#[cfg(multi_core)]
|
||||
disable(Cpu::AppCpu, intr);
|
||||
disable(Cpu::ProCpu, intr);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, cpu_interrupt: CpuInterrupt) -> Result<(), Error> {
|
||||
if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
|
||||
return Err(Error::CpuInterruptReserved);
|
||||
}
|
||||
unsafe {
|
||||
map(Cpu::current(), interrupt, cpu_interrupt);
|
||||
|
||||
xtensa_lx::interrupt::enable_mask(
|
||||
xtensa_lx::interrupt::get_mask() | 1 << cpu_interrupt as u32,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Assign a peripheral interrupt to an CPU interrupt
|
||||
///
|
||||
/// Note: this only maps the interrupt to the CPU interrupt. The CPU interrupt
|
||||
/// still needs to be enabled afterwards
|
||||
///
|
||||
/// # 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;
|
||||
let intr_map_base = match core {
|
||||
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
|
||||
};
|
||||
intr_map_base
|
||||
.offset(interrupt_number)
|
||||
.write_volatile(cpu_interrupt_number as u32);
|
||||
}
|
||||
|
||||
/// Get cpu interrupt assigned to peripheral interrupt
|
||||
pub(crate) fn bound_cpu_interrupt_for(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
|
||||
let interrupt_number = interrupt as isize;
|
||||
|
||||
let intr_map_base = match cpu {
|
||||
Cpu::ProCpu => unsafe { (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr() },
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => unsafe { (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr() },
|
||||
};
|
||||
let cpu_intr = unsafe { intr_map_base.offset(interrupt_number).read_volatile() };
|
||||
let cpu_intr = CpuInterrupt::from_u32(cpu_intr)?;
|
||||
|
||||
if cpu_intr.is_peripheral() {
|
||||
Some(cpu_intr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the given peripheral interrupt
|
||||
pub fn disable(core: Cpu, interrupt: Interrupt) {
|
||||
unsafe {
|
||||
let interrupt_number = interrupt as isize;
|
||||
let intr_map_base = match core {
|
||||
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
|
||||
};
|
||||
// To disable an interrupt, map it to a CPU peripheral interrupt
|
||||
intr_map_base
|
||||
.offset(interrupt_number)
|
||||
.write_volatile(CpuInterrupt::Interrupt16Timer2Priority5 as _);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the given CPU interrupt
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
xtensa_lx::interrupt::clear(1 << which as u32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status of peripheral interrupts
|
||||
#[cfg(large_intr_status)]
|
||||
pub fn status(core: Cpu) -> InterruptStatus {
|
||||
unsafe {
|
||||
match core {
|
||||
Cpu::ProCpu => InterruptStatus::from(
|
||||
(*core0_interrupt_peripheral())
|
||||
.pro_intr_status_0()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core0_interrupt_peripheral())
|
||||
.pro_intr_status_1()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core0_interrupt_peripheral())
|
||||
.pro_intr_status_2()
|
||||
.read()
|
||||
.bits(),
|
||||
),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => InterruptStatus::from(
|
||||
(*core1_interrupt_peripheral())
|
||||
.app_intr_status_0()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core1_interrupt_peripheral())
|
||||
.app_intr_status_1()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core1_interrupt_peripheral())
|
||||
.app_intr_status_2()
|
||||
.read()
|
||||
.bits(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status of peripheral interrupts
|
||||
#[cfg(very_large_intr_status)]
|
||||
pub fn status(core: Cpu) -> InterruptStatus {
|
||||
unsafe {
|
||||
match core {
|
||||
Cpu::ProCpu => InterruptStatus::from(
|
||||
(*core0_interrupt_peripheral())
|
||||
.pro_intr_status_0()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core0_interrupt_peripheral())
|
||||
.pro_intr_status_1()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core0_interrupt_peripheral())
|
||||
.pro_intr_status_2()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core0_interrupt_peripheral())
|
||||
.pro_intr_status_3()
|
||||
.read()
|
||||
.bits(),
|
||||
),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => InterruptStatus::from(
|
||||
(*core1_interrupt_peripheral())
|
||||
.app_intr_status_0()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core1_interrupt_peripheral())
|
||||
.app_intr_status_1()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core1_interrupt_peripheral())
|
||||
.app_intr_status_2()
|
||||
.read()
|
||||
.bits(),
|
||||
(*core1_interrupt_peripheral())
|
||||
.app_intr_status_3()
|
||||
.read()
|
||||
.bits(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
unsafe fn core0_interrupt_peripheral() -> *const crate::peripherals::dport::RegisterBlock {
|
||||
crate::peripherals::DPORT::PTR
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
unsafe fn core1_interrupt_peripheral() -> *const crate::peripherals::dport::RegisterBlock {
|
||||
crate::peripherals::DPORT::PTR
|
||||
}
|
||||
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
unsafe fn core0_interrupt_peripheral() -> *const crate::peripherals::interrupt_core0::RegisterBlock
|
||||
{
|
||||
crate::peripherals::INTERRUPT_CORE0::PTR
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
unsafe fn core1_interrupt_peripheral() -> *const crate::peripherals::interrupt_core1::RegisterBlock
|
||||
{
|
||||
crate::peripherals::INTERRUPT_CORE1::PTR
|
||||
}
|
||||
|
||||
mod vectored {
|
||||
use procmacros::ram;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Interrupt priority levels.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl Priority {
|
||||
/// Maximum interrupt priority
|
||||
pub const fn max() -> Priority {
|
||||
Priority::Priority3
|
||||
}
|
||||
|
||||
/// Minimum interrupt priority
|
||||
pub const fn min() -> Priority {
|
||||
Priority::Priority1
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuInterrupt {
|
||||
#[inline]
|
||||
fn level(&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,
|
||||
|
||||
// we direct these to None because we do not support interrupts at this level
|
||||
// through Rust
|
||||
CpuInterrupt::Interrupt24LevelPriority4
|
||||
| CpuInterrupt::Interrupt25LevelPriority4
|
||||
| CpuInterrupt::Interrupt28EdgePriority4
|
||||
| CpuInterrupt::Interrupt30EdgePriority4
|
||||
| CpuInterrupt::Interrupt31EdgePriority5
|
||||
| CpuInterrupt::Interrupt16Timer2Priority5
|
||||
| CpuInterrupt::Interrupt26LevelPriority5
|
||||
| CpuInterrupt::Interrupt14NmiPriority7 => Priority::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the interrupts configured for the core
|
||||
#[inline(always)]
|
||||
fn configured_interrupts(core: Cpu, status: InterruptStatus, level: u32) -> InterruptStatus {
|
||||
unsafe {
|
||||
let intr_map_base = match core {
|
||||
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
|
||||
};
|
||||
|
||||
let mut res = InterruptStatus::empty();
|
||||
|
||||
for interrupt_nr in status.iterator() {
|
||||
let i = interrupt_nr as isize;
|
||||
let cpu_interrupt = intr_map_base.offset(i).read_volatile();
|
||||
// safety: cast is safe because of repr(u32)
|
||||
let cpu_interrupt: CpuInterrupt =
|
||||
core::mem::transmute::<u32, CpuInterrupt>(cpu_interrupt);
|
||||
let int_level = cpu_interrupt.level() as u8 as u32;
|
||||
|
||||
if int_level == level {
|
||||
res.set(interrupt_nr as u16);
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable the given peripheral interrupt
|
||||
pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
|
||||
let cpu_interrupt =
|
||||
interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?;
|
||||
|
||||
unsafe {
|
||||
map(Cpu::current(), interrupt, cpu_interrupt);
|
||||
|
||||
xtensa_lx::interrupt::enable_mask(
|
||||
xtensa_lx::interrupt::get_mask() | 1 << cpu_interrupt as u32,
|
||||
);
|
||||
}
|
||||
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()) {
|
||||
let ptr = &peripherals::__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 = peripherals::__INTERRUPTS[interrupt as usize]._handler;
|
||||
if addr as usize == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(addr)
|
||||
}
|
||||
}
|
||||
|
||||
fn interrupt_level_to_cpu_interrupt(
|
||||
level: Priority,
|
||||
is_edge: bool,
|
||||
) -> Result<CpuInterrupt, Error> {
|
||||
Ok(if is_edge {
|
||||
match level {
|
||||
Priority::None => return Err(Error::InvalidInterrupt),
|
||||
Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
|
||||
Priority::Priority2 => return Err(Error::InvalidInterrupt),
|
||||
Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
|
||||
}
|
||||
} else {
|
||||
match level {
|
||||
Priority::None => return Err(Error::InvalidInterrupt),
|
||||
Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
|
||||
Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
|
||||
Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO use CpuInterrupt::LevelX.mask() // TODO make it const
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
static CPU_INTERRUPT_LEVELS: [u32; 8] = [
|
||||
0b_0000_0000_0000_0000_0000_0000_0000_0000, // Dummy level 0
|
||||
0b_0000_0000_0000_0110_0011_0111_1111_1111, // Level_1
|
||||
0b_0000_0000_0011_1000_0000_0000_0000_0000, // Level 2
|
||||
0b_0010_1000_1100_0000_1000_1000_0000_0000, // Level 3
|
||||
0b_0101_0011_0000_0000_0000_0000_0000_0000, // Level 4
|
||||
0b_1000_0100_0000_0001_0000_0000_0000_0000, // Level 5
|
||||
0b_0000_0000_0000_0000_0000_0000_0000_0000, // Level 6
|
||||
0b_0000_0000_0000_0000_0100_0000_0000_0000, // Level 7
|
||||
];
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
|
||||
|
||||
#[inline]
|
||||
fn cpu_interrupt_nr_to_cpu_interrupt_handler(
|
||||
number: u32,
|
||||
) -> Option<unsafe extern "C" fn(u32, save_frame: &mut Context)> {
|
||||
use xtensa_lx_rt::*;
|
||||
// we're fortunate that all esp variants use the same CPU interrupt layout
|
||||
Some(match number {
|
||||
6 => Timer0,
|
||||
7 => Software0,
|
||||
11 => Profiling,
|
||||
14 => NMI,
|
||||
15 => Timer1,
|
||||
16 => Timer2,
|
||||
29 => Software1,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
// The linker script defines `__level_1_interrupt`, `__level_2_interrupt` and
|
||||
// `__level_3_interrupt` as `handle_interrupts`
|
||||
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
unsafe fn handle_interrupts(level: u32, save_frame: &mut Context) {
|
||||
let core = Cpu::current();
|
||||
|
||||
let cpu_interrupt_mask =
|
||||
interrupt::get() & interrupt::get_mask() & CPU_INTERRUPT_LEVELS[level as usize];
|
||||
|
||||
if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
|
||||
// Let's handle CPU-internal interrupts (NMI, Timer, Software, Profiling).
|
||||
// These are rarely used by the HAL.
|
||||
|
||||
// Mask the relevant bits
|
||||
let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
|
||||
|
||||
// Pick one
|
||||
let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
|
||||
|
||||
// If the interrupt is edge triggered, we need to clear the request on the CPU's
|
||||
// side.
|
||||
if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
|
||||
interrupt::clear(1 << cpu_interrupt_nr);
|
||||
}
|
||||
|
||||
if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
|
||||
handler(level, save_frame);
|
||||
}
|
||||
} else {
|
||||
let status = if (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
|
||||
// Next, handle edge triggered peripheral interrupts.
|
||||
|
||||
// If the interrupt is edge triggered, we need to clear the
|
||||
// request on the CPU's side
|
||||
interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE);
|
||||
|
||||
// For edge interrupts we cannot rely on the peripherals' interrupt status
|
||||
// registers, therefore call all registered handlers for current level.
|
||||
chip_specific::INTERRUPT_EDGE
|
||||
} else {
|
||||
// Finally, check level-triggered peripheral sources.
|
||||
// These interrupts are cleared by the peripheral.
|
||||
status(core)
|
||||
};
|
||||
|
||||
let configured_interrupts = configured_interrupts(core, status, level);
|
||||
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) };
|
||||
handle_interrupt(level, interrupt, save_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[ram]
|
||||
unsafe fn handle_interrupt(level: u32, interrupt: Interrupt, save_frame: &mut Context) {
|
||||
extern "C" {
|
||||
// defined in each hal
|
||||
fn EspDefaultHandler(level: u32, interrupt: Interrupt);
|
||||
}
|
||||
|
||||
let handler = peripherals::__INTERRUPTS[interrupt as usize]._handler;
|
||||
if core::ptr::eq(
|
||||
handler as *const _,
|
||||
EspDefaultHandler as *const unsafe extern "C" fn(),
|
||||
) {
|
||||
EspDefaultHandler(level, interrupt);
|
||||
} else {
|
||||
let handler: fn(&mut Context) =
|
||||
core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler);
|
||||
handler(save_frame);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unusual_byte_groupings)]
|
||||
#[cfg(esp32)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b1111_1100_0000_0000_0000_0000_0000_0000,
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0011,
|
||||
);
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
use peripherals::Interrupt::*;
|
||||
[
|
||||
TG0_T0_EDGE,
|
||||
TG0_T1_EDGE,
|
||||
TG0_WDT_EDGE,
|
||||
TG0_LACT_EDGE,
|
||||
TG1_T0_EDGE,
|
||||
TG1_T1_EDGE,
|
||||
TG1_WDT_EDGE,
|
||||
TG1_LACT_EDGE,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
|
||||
0b0000_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b1100_0000_0000_0000_0000_0000_0000_0000,
|
||||
0b0000_0000_0000_0000_0000_0011_1011_1111,
|
||||
);
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
use peripherals::Interrupt::*;
|
||||
[
|
||||
TG0_T0_EDGE,
|
||||
TG0_T1_EDGE,
|
||||
TG0_WDT_EDGE,
|
||||
TG0_LACT_EDGE,
|
||||
TG1_T0_EDGE,
|
||||
TG1_T1_EDGE,
|
||||
TG1_WDT_EDGE,
|
||||
TG1_LACT_EDGE,
|
||||
SYSTIMER_TARGET0,
|
||||
SYSTIMER_TARGET1,
|
||||
SYSTIMER_TARGET2,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
|
||||
pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::empty();
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod raw {
|
||||
use super::*;
|
||||
|
||||
extern "C" {
|
||||
fn level4_interrupt(save_frame: &mut Context);
|
||||
fn level5_interrupt(save_frame: &mut Context);
|
||||
fn level6_interrupt(save_frame: &mut Context);
|
||||
fn level7_interrupt(save_frame: &mut Context);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_4_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level4_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_5_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level5_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_6_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level6_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_7_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level7_interrupt(save_frame)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user