esp-hal/esp-hal-common/src/interrupt/riscv.rs
onsdagens fe84e74151
Adding priority based interrupt preemption (#448)
* Software interrupt support added, not sure if the code is good

* Added support for remaining SW interrupts

* Added support for remaining SW interrupts

* Added support for remaining SW interrupts

* Added support for esp32c2, esp32s2, esp32s3

* Added support for esp32c2, esp32s2, esp32s3

* Added support for esp32c2, esp32s2, esp32s3

* Added support for esp32c2, esp32s2, esp32s3

* Software interrupt example for esp32c3

* Added support for esp32c2, esp32s2, esp32s3

* Software interrupt example for esp32c3

* prio based preemption only, vector table reverted

* prio based preemption only, vector table reverted

* fixed a rare bug causing misaligned memory accesses

* fixed a rare bug causing misaligned memory accesses

* fixed a rare bug causing misaligned memory accesses

* fixed rare bug causing misaligned memory access when emulating atomics

* fixed a rare bug causing misaligned memory accesses

* fixed a rare bug causing misaligned memory accesses

* broke something

* broke something

* fixed alignment bug

* Tentative: added support for interrupt preemption without involving the rt

* Added feature enabling priority based interrupt preemption

* Fixed failed merge

* Tagged preemption helpers with inline always

* Disable interrupts before restoring context to avoid ruining it

* Fix max priority edge case

* Fix broken merge

* Added examples for the remaining RISC-V ESPs

* Update esp-hal-common/src/interrupt/riscv.rs

Co-authored-by: sethp <seth.pellegrino@gmail.com>

* Update esp32c2-hal/examples/interrupt_preemption.rs

Co-authored-by: sethp <seth.pellegrino@gmail.com>

* Update esp-hal-common/src/interrupt/riscv.rs

Co-authored-by: sethp <seth.pellegrino@gmail.com>

* Example comments courtesy of @sethp

* Reverted irrelevant changes, raised high prio interrupt to max prio

* Rolling back an irrelevant change

* Rolling back an irrelevant change

* Update esp-hal-common/src/interrupt/riscv.rs

Co-authored-by: Scott Mabin <scott@mabez.dev>

* Update esp-hal-common/src/interrupt/riscv.rs

Co-authored-by: Scott Mabin <scott@mabez.dev>

* Moved imports to avoid warnings, moved functions to ram, moved interrupt disable to before prio threshold is restored

* Added preemption for the ESP32C6

* Moved helper functions into the relevant modules, changed threshold for ESP32C6 to machine mode one

* ESP32C6 Threshold register changed to machine mode one, corrected threshold set.

---------

Co-authored-by: sethp <seth.pellegrino@gmail.com>
Co-authored-by: Scott Mabin <scott@mabez.dev>
2023-05-05 11:40:54 +01:00

842 lines
27 KiB
Rust

//! Interrupt handling - RISCV
//!
//! When the `vectored` feature is enabled, 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
//! interrupt1() => Priority::Priority1
//! interrupt2() => Priority::Priority2
//! ...
//! interrupt15() => Priority::Priority15
//! ```
use esp_riscv_rt::riscv::register::{mcause, mepc, mtvec};
pub use esp_riscv_rt::TrapFrame;
#[cfg(not(plic))]
pub use self::classic::*;
#[cfg(plic)]
pub use self::plic::*;
use crate::{
peripherals::{self, Interrupt},
Cpu,
};
// User code shouldn't usually take the mutable TrapFrame or the TrapFrame in
// general. However this makes things like preemtive multitasking easier in
// future
extern "C" {
fn interrupt1(frame: &mut TrapFrame);
fn interrupt2(frame: &mut TrapFrame);
fn interrupt3(frame: &mut TrapFrame);
fn interrupt4(frame: &mut TrapFrame);
fn interrupt5(frame: &mut TrapFrame);
fn interrupt6(frame: &mut TrapFrame);
fn interrupt7(frame: &mut TrapFrame);
fn interrupt8(frame: &mut TrapFrame);
fn interrupt9(frame: &mut TrapFrame);
fn interrupt10(frame: &mut TrapFrame);
fn interrupt11(frame: &mut TrapFrame);
fn interrupt12(frame: &mut TrapFrame);
fn interrupt13(frame: &mut TrapFrame);
fn interrupt14(frame: &mut TrapFrame);
fn interrupt15(frame: &mut TrapFrame);
fn interrupt16(frame: &mut TrapFrame);
fn interrupt17(frame: &mut TrapFrame);
fn interrupt18(frame: &mut TrapFrame);
fn interrupt19(frame: &mut TrapFrame);
fn interrupt20(frame: &mut TrapFrame);
fn interrupt21(frame: &mut TrapFrame);
fn interrupt22(frame: &mut TrapFrame);
fn interrupt23(frame: &mut TrapFrame);
fn interrupt24(frame: &mut TrapFrame);
fn interrupt25(frame: &mut TrapFrame);
fn interrupt26(frame: &mut TrapFrame);
fn interrupt27(frame: &mut TrapFrame);
fn interrupt28(frame: &mut TrapFrame);
fn interrupt29(frame: &mut TrapFrame);
fn interrupt30(frame: &mut TrapFrame);
fn interrupt31(frame: &mut TrapFrame);
}
/// Interrupt kind
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)]
pub enum CpuInterrupt {
Interrupt1 = 1,
Interrupt2,
Interrupt3,
Interrupt4,
Interrupt5,
Interrupt6,
Interrupt7,
Interrupt8,
Interrupt9,
Interrupt10,
Interrupt11,
Interrupt12,
Interrupt13,
Interrupt14,
Interrupt15,
Interrupt16,
Interrupt17,
Interrupt18,
Interrupt19,
Interrupt20,
Interrupt21,
Interrupt22,
Interrupt23,
Interrupt24,
Interrupt25,
Interrupt26,
Interrupt27,
Interrupt28,
Interrupt29,
Interrupt30,
Interrupt31,
}
/// Interrupt priority levels.
#[repr(u8)]
pub enum Priority {
None = 0,
Priority1,
Priority2,
Priority3,
Priority4,
Priority5,
Priority6,
Priority7,
Priority8,
Priority9,
Priority10,
Priority11,
Priority12,
Priority13,
Priority14,
Priority15,
}
impl Priority {
pub fn max() -> Priority {
Priority::Priority15
}
pub fn min() -> Priority {
Priority::Priority1
}
}
#[cfg(feature = "vectored")]
pub use vectored::*;
#[cfg(feature = "vectored")]
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(
crate::get_core(),
core::mem::transmute(*num as u32),
InterruptKind::Level,
);
set_priority(
crate::get_core(),
core::mem::transmute(*num as u32),
core::mem::transmute((prio as u8) + 1),
);
enable_cpu_interrupt(core::mem::transmute(*num as u32));
}
}
/// Get the interrupts configured for the core
#[inline]
fn get_configured_interrupts(_core: Cpu, mut status: u128) -> [u128; 16] {
unsafe {
let mut prios = [0u128; 16];
while status != 0 {
let interrupt_nr = status.trailing_zeros() as u16;
// safety: cast is safe because of repr(u16)
let cpu_interrupt: CpuInterrupt =
get_assigned_cpu_interrupt(core::mem::transmute(interrupt_nr as u16));
let prio = get_priority(cpu_interrupt);
prios[prio as usize] |= 1 << (interrupt_nr as usize);
status &= !(1u128 << interrupt_nr);
}
prios
}
}
/// Interrupt Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error {
InvalidInterruptPriority,
}
/// 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(PRIORITY_TO_INTERRUPT[(level as usize) - 1] as u32);
map(crate::get_core(), interrupt, cpu_interrupt);
enable_cpu_interrupt(cpu_interrupt);
}
Ok(())
}
#[ram]
unsafe fn handle_interrupts(cpu_intr: CpuInterrupt, context: &mut TrapFrame) {
let status = get_status(crate::get_core());
// this has no effect on level interrupts, but the interrupt may be an edge one
// so we clear it anyway
clear(crate::get_core(), cpu_intr);
let configured_interrupts = get_configured_interrupts(crate::get_core(), status);
let mut interrupt_mask =
status & configured_interrupts[INTERRUPT_TO_PRIORITY[cpu_intr as usize - 1]];
while interrupt_mask != 0 {
let interrupt_nr = interrupt_mask.trailing_zeros();
// Interrupt::try_from can fail if interrupt already de-asserted:
// silently ignore
if let Ok(interrupt) = peripherals::Interrupt::try_from(interrupt_nr as u8) {
handle_interrupt(interrupt, context)
}
interrupt_mask &= !(1u128 << interrupt_nr);
}
}
#[ram]
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 handler as *const _ == EspDefaultHandler as *const unsafe extern "C" fn() {
EspDefaultHandler(interrupt);
} else {
let handler: fn(&mut TrapFrame) = core::mem::transmute(handler);
handler(save_frame);
}
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt1(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt1, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt2(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt2, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt3(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt3, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt4(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt4, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt5(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt5, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt6(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt6, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt7(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt7, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt8(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt8, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt9(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt9, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt10(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt10, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt11(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt11, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt12(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt12, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt13(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt13, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt14(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt14, context)
}
#[no_mangle]
#[ram]
pub unsafe fn interrupt15(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt15, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
pub unsafe fn interrupt16(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt16, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
pub unsafe fn interrupt17(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt17, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
pub unsafe fn interrupt18(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt18, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
pub unsafe fn interrupt19(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt19, context)
}
}
/// # 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) {
extern "C" {
// defined in riscv-rt
pub fn DefaultHandler();
}
let cause = mcause::read();
if cause.is_exception() {
let pc = mepc::read();
handle_exception(pc, trap_frame);
} else {
#[cfg(feature = "interrupt-preemption")]
let interrupt_priority = handle_priority();
let code = mcause::read().code();
match code {
1 => interrupt1(trap_frame.as_mut().unwrap()),
2 => interrupt2(trap_frame.as_mut().unwrap()),
3 => interrupt3(trap_frame.as_mut().unwrap()),
4 => interrupt4(trap_frame.as_mut().unwrap()),
5 => interrupt5(trap_frame.as_mut().unwrap()),
6 => interrupt6(trap_frame.as_mut().unwrap()),
7 => interrupt7(trap_frame.as_mut().unwrap()),
8 => interrupt8(trap_frame.as_mut().unwrap()),
9 => interrupt9(trap_frame.as_mut().unwrap()),
10 => interrupt10(trap_frame.as_mut().unwrap()),
11 => interrupt11(trap_frame.as_mut().unwrap()),
12 => interrupt12(trap_frame.as_mut().unwrap()),
13 => interrupt13(trap_frame.as_mut().unwrap()),
14 => interrupt14(trap_frame.as_mut().unwrap()),
15 => interrupt15(trap_frame.as_mut().unwrap()),
16 => interrupt16(trap_frame.as_mut().unwrap()),
17 => interrupt17(trap_frame.as_mut().unwrap()),
18 => interrupt18(trap_frame.as_mut().unwrap()),
19 => interrupt19(trap_frame.as_mut().unwrap()),
20 => interrupt20(trap_frame.as_mut().unwrap()),
21 => interrupt21(trap_frame.as_mut().unwrap()),
22 => interrupt22(trap_frame.as_mut().unwrap()),
23 => interrupt23(trap_frame.as_mut().unwrap()),
24 => interrupt24(trap_frame.as_mut().unwrap()),
25 => interrupt25(trap_frame.as_mut().unwrap()),
26 => interrupt26(trap_frame.as_mut().unwrap()),
27 => interrupt27(trap_frame.as_mut().unwrap()),
28 => interrupt28(trap_frame.as_mut().unwrap()),
29 => interrupt29(trap_frame.as_mut().unwrap()),
30 => interrupt30(trap_frame.as_mut().unwrap()),
31 => interrupt31(trap_frame.as_mut().unwrap()),
_ => DefaultHandler(),
};
#[cfg(feature = "interrupt-preemption")]
restore_priority(interrupt_priority);
}
}
/// Apply atomic emulation if needed. Call the default exception handler
/// otherwise.
///
/// # Safety
///
/// This function is called from an trap handler.
#[doc(hidden)]
unsafe fn handle_exception(pc: usize, trap_frame: *mut TrapFrame) {
let insn: usize = *(pc as *const _);
let needs_atomic_emulation = (insn & 0b1111111) == 0b0101111;
if !needs_atomic_emulation {
extern "C" {
fn ExceptionHandler(tf: *mut TrapFrame);
}
ExceptionHandler(trap_frame);
return;
}
let mut frame = [
0,
(*trap_frame).ra,
(*trap_frame).sp,
(*trap_frame).gp,
(*trap_frame).tp,
(*trap_frame).t0,
(*trap_frame).t1,
(*trap_frame).t2,
(*trap_frame).s0,
(*trap_frame).s1,
(*trap_frame).a0,
(*trap_frame).a1,
(*trap_frame).a2,
(*trap_frame).a3,
(*trap_frame).a4,
(*trap_frame).a5,
(*trap_frame).a6,
(*trap_frame).a7,
(*trap_frame).s2,
(*trap_frame).s3,
(*trap_frame).s4,
(*trap_frame).s5,
(*trap_frame).s6,
(*trap_frame).s7,
(*trap_frame).s8,
(*trap_frame).s9,
(*trap_frame).s10,
(*trap_frame).s11,
(*trap_frame).t3,
(*trap_frame).t4,
(*trap_frame).t5,
(*trap_frame).t6,
];
riscv_atomic_emulation_trap::atomic_emulation((*trap_frame).pc, &mut frame);
(*trap_frame).ra = frame[1];
(*trap_frame).sp = frame[2];
(*trap_frame).gp = frame[3];
(*trap_frame).tp = frame[4];
(*trap_frame).t0 = frame[5];
(*trap_frame).t1 = frame[6];
(*trap_frame).t2 = frame[7];
(*trap_frame).s0 = frame[8];
(*trap_frame).s1 = frame[9];
(*trap_frame).a0 = frame[10];
(*trap_frame).a1 = frame[11];
(*trap_frame).a2 = frame[12];
(*trap_frame).a3 = frame[13];
(*trap_frame).a4 = frame[14];
(*trap_frame).a5 = frame[15];
(*trap_frame).a6 = frame[16];
(*trap_frame).a7 = frame[17];
(*trap_frame).s2 = frame[18];
(*trap_frame).s3 = frame[19];
(*trap_frame).s4 = frame[20];
(*trap_frame).s5 = frame[21];
(*trap_frame).s6 = frame[22];
(*trap_frame).s7 = frame[23];
(*trap_frame).s8 = frame[24];
(*trap_frame).s9 = frame[25];
(*trap_frame).s10 = frame[26];
(*trap_frame).s11 = frame[27];
(*trap_frame).t3 = frame[28];
(*trap_frame).t4 = frame[29];
(*trap_frame).t5 = frame[30];
(*trap_frame).t6 = frame[31];
(*trap_frame).pc = pc + 4;
}
#[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);
#[cfg(feature = "vectored")]
crate::interrupt::init_vectoring();
};
#[cfg(plic)]
unsafe {
core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
}
}
/// 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
intr_map_base.offset(interrupt_number).write_volatile(0);
}
}
/// Get status of peripheral interrupts
#[inline]
pub fn get_status(_core: Cpu) -> u128 {
#[cfg(large_intr_status)]
unsafe {
((*crate::peripherals::INTERRUPT_CORE0::PTR)
.intr_status_reg_0
.read()
.bits() as u128)
| ((*crate::peripherals::INTERRUPT_CORE0::PTR)
.intr_status_reg_1
.read()
.bits() as u128)
<< 32
| ((*crate::peripherals::INTERRUPT_CORE0::PTR)
.int_status_reg_2
.read()
.bits() as u128)
<< 64
}
#[cfg(not(large_intr_status))]
unsafe {
((*crate::peripherals::INTERRUPT_CORE0::PTR)
.intr_status_reg_0
.read()
.bits() as u128)
| ((*crate::peripherals::INTERRUPT_CORE0::PTR)
.intr_status_reg_1
.read()
.bits() as u128)
<< 32
}
}
/// Assign a peripheral interrupt to an CPU interrupt.
///
/// Great care must be taken when using the `vectored` feature (enabled by
/// default). Avoid interrupts 1 - 15 when interrupt vectoring is enabled.
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 = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
intr_map_base
.offset(interrupt_number)
.write_volatile(cpu_interrupt_number as u32);
}
/// Get cpu interrupt assigned to peripheral interrupt
#[inline]
unsafe fn get_assigned_cpu_interrupt(interrupt: Interrupt) -> 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();
core::mem::transmute(cpu_intr)
}
#[cfg(not(plic))]
mod classic {
use super::{CpuInterrupt, InterruptKind, Priority};
use crate::Cpu;
pub(super) const PRIORITY_TO_INTERRUPT: [usize; 15] =
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
pub(super) const INTERRUPT_TO_PRIORITY: [usize; 15] =
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
/// Enable a CPU interrupt
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
///
/// This is safe to call when the `vectored` feature is enabled. 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
///
/// Great care must be taken when using the `vectored` feature (enabled by
/// default). Avoid changing the priority of interrupts 1 - 15 when
/// interrupt vectoring is enabled.
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
let cpu_interrupt_number = which as isize;
let intr_prio_base = intr.cpu_int_pri_0.as_ptr();
intr_prio_base
.offset(cpu_interrupt_number)
.write_volatile(priority as u32);
}
/// 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) unsafe fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority {
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
let intr_prio_base = intr.cpu_int_pri_0.as_ptr();
let prio = intr_prio_base
.offset(cpu_interrupt as isize)
.read_volatile();
core::mem::transmute(prio as u8)
}
#[cfg(all(feature = "interrupt-preemption"))]
use procmacros::ram;
#[cfg(all(feature = "interrupt-preemption"))]
#[ram]
pub(super) unsafe fn handle_priority() -> u32 {
use super::mcause;
use crate::riscv;
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()
.offset(interrupt_id as isize)
.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
}
#[cfg(all(feature = "interrupt-preemption"))]
#[ram]
pub(super) unsafe fn restore_priority(stored_prio: u32) {
use crate::riscv;
unsafe {
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;
// 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
pub(super) const PRIORITY_TO_INTERRUPT: [usize; 15] =
[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
pub(super) const INTERRUPT_TO_PRIORITY: [usize; 19] = [
1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
];
const DR_REG_PLIC_MX_BASE: u32 = 0x20001000;
const PLIC_MXINT_ENABLE_REG: u32 = DR_REG_PLIC_MX_BASE + 0x0;
const PLIC_MXINT_TYPE_REG: u32 = DR_REG_PLIC_MX_BASE + 0x4;
const PLIC_MXINT_CLEAR_REG: u32 = DR_REG_PLIC_MX_BASE + 0x8;
const PLIC_MXINT0_PRI_REG: u32 = DR_REG_PLIC_MX_BASE + 0x10;
#[cfg(feature = "interrupt-preemption")]
const PLIC_MXINT_THRESH_REG: u32 = DR_REG_PLIC_MX_BASE + 0x90;
/// Enable a CPU interrupt
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
let cpu_interrupt_number = which as isize;
let mxint_enable = PLIC_MXINT_ENABLE_REG as *mut u32;
unsafe {
mxint_enable.write_volatile(mxint_enable.read_volatile() | 1 << cpu_interrupt_number);
}
}
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
///
/// This is safe to call when the `vectored` feature is enabled. 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 = PLIC_MXINT_TYPE_REG as *mut u32;
let cpu_interrupt_number = which as isize;
let interrupt_type = match kind {
InterruptKind::Level => 0,
InterruptKind::Edge => 1,
};
intr.write_volatile(
intr.read_volatile() & !(1 << cpu_interrupt_number)
| (interrupt_type << cpu_interrupt_number),
);
}
}
/// Set the priority level of an CPU interrupt
///
/// Great care must be taken when using the `vectored` feature (enabled by
/// default). Avoid changing the priority of interrupts 1 - 15 when
/// interrupt vectoring is enabled.
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32;
let cpu_interrupt_number = which as isize;
plic_mxint_pri_ptr
.offset(cpu_interrupt_number)
.write_volatile(priority as u32);
}
/// Clear a CPU interrupt
#[inline]
pub fn clear(_core: Cpu, which: CpuInterrupt) {
unsafe {
let cpu_interrupt_number = which as isize;
let intr = PLIC_MXINT_CLEAR_REG as *mut u32;
intr.write_volatile(1 << cpu_interrupt_number);
}
}
/// Get interrupt priority
#[inline]
pub(super) unsafe fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority {
let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32;
let cpu_interrupt_number = cpu_interrupt as isize;
let prio = plic_mxint_pri_ptr
.offset(cpu_interrupt_number)
.read_volatile();
core::mem::transmute(prio as u8)
}
#[cfg(all(feature = "interrupt-preemption"))]
use procmacros::ram;
#[cfg(all(feature = "interrupt-preemption"))]
#[ram]
pub(super) unsafe fn handle_priority() -> u32 {
use super::mcause;
use crate::riscv;
let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32;
let interrupt_id: isize = mcause::read().code().try_into().unwrap(); // MSB is whether its exception or interrupt.
let interrupt_priority = plic_mxint_pri_ptr.offset(interrupt_id).read_volatile();
let thresh_reg = PLIC_MXINT_THRESH_REG as *mut u32;
let prev_interrupt_priority = thresh_reg.read_volatile() & 0x000000FF;
// this is a u8 according to esp-idf, so mask everything else.
if interrupt_priority < 15 {
// leave interrupts disabled if interrupt is of max priority.
thresh_reg.write_volatile(interrupt_priority + 1);
unsafe {
riscv::interrupt::enable();
}
}
prev_interrupt_priority
}
#[cfg(all(feature = "interrupt-preemption"))]
#[ram]
pub(super) unsafe fn restore_priority(stored_prio: u32) {
use crate::riscv;
unsafe {
riscv::interrupt::disable();
}
let thresh_reg = PLIC_MXINT_THRESH_REG as *mut u32;
thresh_reg.write_volatile(stored_prio);
}
}