#[cfg(feature = "rt")]
#[instability::unstable]
pub use esp_riscv_rt::TrapFrame;
#[cfg_attr(interrupt_controller = "riscv_basic", path = "riscv/basic.rs")]
#[cfg_attr(interrupt_controller = "plic", path = "riscv/plic.rs")]
#[cfg_attr(interrupt_controller = "clic", path = "riscv/clic.rs")]
mod cpu_int;
use crate::{
interrupt::{PriorityError, RunLevel},
peripherals::Interrupt,
system::Cpu,
};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum InterruptKind {
Level,
Edge,
}
for_each_interrupt!(
(all $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
paste::paste! {
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum CpuInterrupt {
$(
#[doc = concat!(" Interrupt number ", stringify!($n), ".")]
[<Interrupt $n>] = $n,
)*
}
impl CpuInterrupt {
#[inline]
pub(crate) fn from_u32(n: u32) -> Option<Self> {
match n {
$(n if n == $n && n != DISABLED_CPU_INTERRUPT => Some(Self:: [<Interrupt $n>]),)*
_ => None
}
}
}
}
};
);
for_each_classified_interrupt!(
(direct_bindable $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
paste::paste! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DirectBindableCpuInterrupt {
$(
#[doc = concat!(" Direct bindable CPU interrupt number ", stringify!($idx_in_class), ".")]
#[doc = " "]
#[doc = concat!(" Corresponds to CPU interrupt ", stringify!($n), ".")]
[<Interrupt $idx_in_class>] = $n,
)*
}
impl From<DirectBindableCpuInterrupt> for CpuInterrupt {
fn from(bindable: DirectBindableCpuInterrupt) -> CpuInterrupt {
match bindable {
$(
DirectBindableCpuInterrupt::[<Interrupt $idx_in_class>] => CpuInterrupt::[<Interrupt $n>],
)*
}
}
}
}
};
);
impl CpuInterrupt {
#[inline]
#[cfg(feature = "rt")]
pub(crate) fn is_vectored(self) -> bool {
const VECTORED_CPU_INTERRUPT_RANGE: core::ops::RangeInclusive<u32> = PRIORITY_TO_INTERRUPT
[0] as u32
..=PRIORITY_TO_INTERRUPT[PRIORITY_TO_INTERRUPT.len() - 1] as u32;
VECTORED_CPU_INTERRUPT_RANGE.contains(&(self as u32))
}
#[inline]
#[instability::unstable]
pub fn enable(self) {
cpu_int::enable_cpu_interrupt_raw(self as u32);
}
#[inline]
#[instability::unstable]
pub fn clear(self) {
cpu_int::clear_raw(self as u32);
}
#[inline]
#[instability::unstable]
pub fn set_kind(self, kind: InterruptKind) {
cpu_int::set_kind_raw(self as u32, kind);
}
#[inline]
#[instability::unstable]
pub fn set_priority(self, priority: Priority) {
cpu_int::set_priority_raw(self as u32, priority);
}
#[inline]
#[instability::unstable]
pub fn priority(self) -> Priority {
unwrap!(Priority::try_from_u32(self.level()))
}
#[inline]
pub(crate) fn level(self) -> u32 {
cpu_int::cpu_interrupt_priority_raw(self as u32) as u32
}
}
for_each_interrupt_priority!(
(all $( ($idx:literal, $n:literal, $ident:ident, $level:ident) ),*) => {
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
$(
#[doc = concat!(" Priority level ", stringify!($n), ".")]
$ident = $n,
)*
}
impl Priority {
fn iter() -> impl Iterator<Item = Priority> {
[$(Priority::$ident,)*].into_iter()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum ElevatedRunLevel {
$(
#[doc = concat!("Run level ", stringify!($n), ".")]
$level = $n,
)*
}
impl ElevatedRunLevel {
pub const fn from_priority(priority: Priority) -> Self {
match priority {
$(Priority::$ident => Self::$level,)*
}
}
}
};
);
impl Priority {
#[allow(unused_assignments)]
#[instability::unstable]
pub const fn max() -> Priority {
const {
let mut last = Self::min();
for_each_interrupt_priority!(
($_idx:literal, $_n:literal, $ident:ident, $_level:ident) => {
last = Self::$ident;
};
);
last
}
}
pub const fn min() -> Priority {
Priority::Priority1
}
pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
let result;
for_each_interrupt_priority!(
(all $( ($idx:literal, $n:literal, $ident:ident, $_level:ident) ),*) => {
result = match priority {
$($n => Ok(Priority::$ident),)*
_ => Err(PriorityError::InvalidInterruptPriority),
}
};
);
result
}
}
impl ElevatedRunLevel {
#[instability::unstable]
pub const fn max() -> ElevatedRunLevel {
Self::from_priority(Priority::max())
}
pub const fn min() -> ElevatedRunLevel {
Self::from_priority(Priority::min())
}
pub(crate) fn try_from_u32(level: u32) -> Result<Self, PriorityError> {
Priority::try_from_u32(level).map(Self::from_priority)
}
}
#[instability::unstable]
impl TryFrom<u32> for ElevatedRunLevel {
type Error = PriorityError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::try_from_u32(value)
}
}
impl From<Priority> for ElevatedRunLevel {
fn from(priority: Priority) -> Self {
Self::from_priority(priority)
}
}
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static DISABLED_CPU_INTERRUPT: u32 = property!("interrupts.disabled_interrupt");
const VECTOR_COUNT: usize = const {
let mut count = 0;
for_each_interrupt!(([vector $n:tt] $_:literal) => { count += 1; };);
core::assert!(count == Priority::max() as usize);
count
};
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static PRIORITY_TO_INTERRUPT: [CpuInterrupt; VECTOR_COUNT] = const {
let mut counter = 0;
let mut vector = [CpuInterrupt::Interrupt0; VECTOR_COUNT];
for_each_interrupt!(
([vector $_n:tt] $interrupt:literal) => {
vector[counter] = paste::paste! { CpuInterrupt::[<Interrupt $interrupt>] };
counter += 1;
};
);
vector
};
#[instability::unstable]
pub fn enable_direct(
interrupt: Interrupt,
level: Priority,
cpu_interrupt: DirectBindableCpuInterrupt,
handler: unsafe extern "C" fn(),
) {
cfg_if::cfg_if! {
if #[cfg(interrupt_controller = "clic")] {
let clic = unsafe { crate::soc::pac::CLIC::steal() };
clic.int_attr(cpu_interrupt as usize).modify(|_, w| {
w.shv().hardware();
w.trig().positive_level()
});
let mtvt_table: *mut [u32; 48];
unsafe { core::arch::asm!("csrr {0}, 0x307", out(reg) mtvt_table) };
let int_slot = mtvt_table
.cast::<u32>()
.wrapping_add(cpu_interrupt as usize);
let instr = handler as usize as u32;
} else {
use riscv::register::mtvec;
let mt = mtvec::read();
assert_eq!(
mt.trap_mode().into_usize(),
mtvec::TrapMode::Vectored.into_usize()
);
let base_addr = mt.address() as usize;
let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4) as *mut u32;
let instr = encode_jal_x0(handler as usize, int_slot as usize);
}
}
if crate::debugger::debugger_connected() {
unsafe { core::ptr::write_volatile(int_slot, instr) };
} else {
crate::debugger::DEBUGGER_LOCK.lock(|| unsafe {
let wp = crate::debugger::clear_watchpoint(1);
core::ptr::write_volatile(int_slot, instr);
crate::debugger::restore_watchpoint(1, wp);
});
}
unsafe {
core::arch::asm!("fence.i");
}
super::map_raw(Cpu::current(), interrupt, cpu_interrupt as u32);
cpu_int::set_priority_raw(cpu_interrupt as u32, level);
cpu_int::set_kind_raw(cpu_interrupt as u32, InterruptKind::Level);
cpu_int::enable_cpu_interrupt_raw(cpu_interrupt as u32);
}
#[cfg(not(interrupt_controller = "clic"))]
fn encode_jal_x0(target: usize, pc: usize) -> u32 {
let offset = (target as isize) - (pc as isize);
const MIN: isize = -(1isize << 20);
const MAX: isize = (1isize << 20) - 1;
assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));
let imm = offset as u32;
let imm20 = (imm >> 20) & 0x1;
let imm10_1 = (imm >> 1) & 0x3ff;
let imm11 = (imm >> 11) & 0x1;
let imm19_12 = (imm >> 12) & 0xff;
(imm20 << 31)
| (imm19_12 << 12)
| (imm11 << 20)
| (imm10_1 << 21)
| 0b1101111u32
}
pub(crate) fn current_raw_runlevel() -> u32 {
cpu_int::current_runlevel() as u32
}
pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
let previous = cpu_int::change_current_runlevel(level);
unwrap!(RunLevel::try_from_u32(previous as u32))
}
fn cpu_wait_mode_on() -> bool {
cfg_if::cfg_if! {
if #[cfg(soc_has_pcr)] {
crate::peripherals::PCR::regs().cpu_waiti_conf().read().cpu_wait_mode_force_on().bit_is_set()
} else {
crate::peripherals::SYSTEM::regs()
.cpu_per_conf()
.read()
.cpu_wait_mode_force_on()
.bit_is_set()
}
}
}
#[inline(always)]
#[instability::unstable]
pub fn wait_for_interrupt() {
if crate::debugger::debugger_connected() && !cpu_wait_mode_on() {
return;
}
unsafe { core::arch::asm!("wfi") };
}
pub(crate) fn priority_to_cpu_interrupt(_interrupt: Interrupt, level: Priority) -> CpuInterrupt {
PRIORITY_TO_INTERRUPT[(level as usize) - 1]
}
#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
pub(crate) unsafe fn init_vectoring() {
use riscv::register::mtvec;
unsafe extern "C" {
static _vector_table: u32;
#[cfg(interrupt_controller = "clic")]
static _mtvt_table: u32;
}
unsafe {
let vec_table = (&raw const _vector_table).addr();
#[cfg(not(interrupt_controller = "clic"))]
{
mtvec::write({
let mut mtvec = mtvec::Mtvec::from_bits(0);
mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
mtvec.set_address(vec_table);
mtvec
});
}
#[cfg(interrupt_controller = "clic")]
{
mtvec::write({
let mut mtvec = mtvec::Mtvec::from_bits(0x03); mtvec.set_address(vec_table);
mtvec
});
let mtvt_table = (&raw const _mtvt_table).addr();
core::arch::asm!("csrw 0x307, {0}", in(reg) mtvt_table);
}
};
for (int, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(Priority::iter()) {
let num = int as u32;
cpu_int::set_kind_raw(num, InterruptKind::Level);
cpu_int::set_priority_raw(num, prio);
cpu_int::enable_cpu_interrupt_raw(num);
}
}
#[cfg(feature = "rt")]
pub(crate) mod rt {
use esp_riscv_rt::TrapFrame;
use riscv::register::mcause;
use super::*;
use crate::interrupt::InterruptStatus;
#[cfg(not(interrupt_controller = "clic"))]
const INTERRUPT_COUNT: usize = const {
let mut count = 0;
for_each_interrupt!(([$_class:tt $n:tt] $_:literal) => { count += 1; };);
count
};
#[cfg(not(interrupt_controller = "clic"))]
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static INTERRUPT_TO_PRIORITY: [Option<Priority>; INTERRUPT_COUNT] = const {
let mut priorities = [None; INTERRUPT_COUNT];
for_each_interrupt!(
([vector $n:tt] $int:literal) => {
for_each_interrupt_priority!(($n, $__:tt, $ident:ident, $_level:ident) => { priorities[$int] = Some(Priority::$ident); };);
};
);
priorities
};
#[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() {
crate::interrupt::setup_interrupts();
cpu_int::init();
#[cfg(interrupt_controller = "plic")]
unsafe {
core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
}
}
#[unsafe(no_mangle)]
#[crate::ram]
unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
let status = InterruptStatus::current();
cpu_intr.clear();
cfg_if::cfg_if! {
if #[cfg(interrupt_controller = "clic")] {
let prio = cpu_int::current_runlevel();
let mcause = riscv::register::mcause::read();
} else {
let prio = unwrap!(INTERRUPT_TO_PRIORITY[cpu_intr as usize]);
let level = unsafe { change_current_runlevel(RunLevel::Interrupt(ElevatedRunLevel::from(prio))) };
let prio = prio as u8;
}
}
let handle_interrupts = || unsafe {
for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
crate::interrupt::should_handle(Cpu::current(), interrupt_nr as u32, prio as u32)
}) {
let handler =
crate::soc::pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler;
handler();
}
};
if prio != Priority::max() as u8 {
unsafe {
riscv::interrupt::nested(handle_interrupts);
}
} else {
handle_interrupts();
}
cfg_if::cfg_if! {
if #[cfg(interrupt_controller = "clic")] {
unsafe {
core::arch::asm!("csrw 0x342, {}", in(reg) mcause.bits())
}
} else {
unsafe { change_current_runlevel(level) };
}
}
}
}