RISCV: remove the direct-vectoring & interrupt-preemption features and enable them by default (#1310)

* Remove the `direct-vectoring` feature

* Enables the feature by default
* renames the old direct_vectoring enable function `enable_direct`

* Make enable_direct safe, move it out of vectored module

* enable interrupt preemption by default for riscv

* remove pub from cpu intr handlers

* add enable_direct for Xtensa too

* Fix flip-link feature

* Fix up interrupt docs

* changelog

* fix clippy suggestions

* Disable P4 workflow
This commit is contained in:
Scott Mabin 2024-03-20 16:19:22 +00:00 committed by GitHub
parent 1f155cf301
commit a61ffef909
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 411 additions and 561 deletions

View File

@ -56,7 +56,7 @@ jobs:
"esp32c3",
"esp32c6",
"esp32h2",
"esp32p4",
# "esp32p4",
# Xtensa devices:
"esp32",
"esp32s2",
@ -179,7 +179,7 @@ jobs:
cargo xtask build-package --features=esp32c3 --target=riscv32imc-unknown-none-elf esp-hal
cargo xtask build-package --features=esp32c6 --target=riscv32imac-unknown-none-elf esp-hal
cargo xtask build-package --features=esp32h2 --target=riscv32imac-unknown-none-elf esp-hal
cargo xtask build-package --features=esp32p4 --target=riscv32imafc-unknown-none-elf esp-hal
# cargo xtask build-package --features=esp32p4 --target=riscv32imafc-unknown-none-elf esp-hal
- name: msrv (esp-lp-hal)
run: |
cargo xtask build-package --features=esp32c6 --target=riscv32imac-unknown-none-elf esp-lp-hal

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- ESP32-C6 / ESP32-H2: Implement `ETM` for general purpose timers (#1274)
- `interrupt::enable` now has a direct CPU enable counter part, `interrupt::enable_direct` (#1310)
### Fixed
@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- Remove package-level type exports (#1275)
- Removed `direct-vectoring` & `interrupt-preemption` features, as they are now enabled by default (#1310)
## [0.16.1] - 2024-03-12

View File

@ -131,13 +131,9 @@ esp32s2 = ["dep:esp32s2", "xtensa", "portable-atomic/critical-section", "procmac
esp32s3 = ["dep:esp32s3", "xtensa", "procmacros/has-ulp-core", "xtensa-lx/spin", "xtensa-lx-rt?/esp32s3", "usb-otg"]
#! ### RISC-V Exclusive Feature Flags
## Enable direct interrupt vectoring.
direct-vectoring = ["esp-riscv-rt/direct-vectoring"]
## Move the stack to start of RAM to get zero-cost stack overflow protection
## (ESP32-C6 and ESPS32-H2 only!).
flip-link = ["esp-riscv-rt/fix-sp"]
## Enable interrupt preemption.
interrupt-preemption = ["esp-riscv-rt/interrupt-preemption"]
## Configuration for placing device drivers in the IRAM for faster access.
place-spi-driver-in-ram = []
## Initialize the `.data` section of memory.

View File

@ -7,7 +7,7 @@ use std::{
str::FromStr,
};
use esp_metadata::{Arch, Chip, Config};
use esp_metadata::{Chip, Config};
// Macros taken from:
// https://github.com/TheDan64/inkwell/blob/36c3b10/src/lib.rs#L81-L110
@ -113,21 +113,13 @@ fn main() -> Result<(), Box<dyn Error>> {
panic!("The target does not support PSRAM");
}
// Don't support "interrupt-preemption" and "direct-vectoring" on Xtensa and
// RISC-V with CLIC:
if (config.contains(&String::from("clic")) || config.arch() == Arch::Xtensa)
&& (cfg!(feature = "direct-vectoring") || cfg!(feature = "interrupt-preemption"))
{
panic!("The target does not support interrupt-preemption and direct-vectoring");
}
// Define all necessary configuration symbols for the configured device:
config.define_symbols();
#[allow(unused_mut)]
let mut config_symbols = config.all();
#[cfg(feature = "flip-link")]
config_symbols.push("flip-link");
config_symbols.push("flip-link".to_owned());
// Place all linker scripts in `OUT_DIR`, and instruct Cargo how to find these
// files:

View File

@ -42,11 +42,7 @@ macro_rules! from_cpu {
panic!("FROM_CPU_{} is already used by a different executor.", $irq);
}
// unsafe block because of direct-vectoring on riscv
#[allow(unused_unsafe)]
unsafe {
unwrap!(interrupt::enable(peripherals::Interrupt::[<FROM_CPU_INTR $irq>], priority));
}
unwrap!(interrupt::enable(peripherals::Interrupt::[<FROM_CPU_INTR $irq>], priority));
}
fn number() -> usize {

View File

@ -1,27 +1,18 @@
//! # Interrupt support
//!
//! ## Overview
//! The `interrupt` driver is a crucial module for ESP chips. Its primary
//! purpose is to manage and handle interrupts, which are asynchronous events
//! requiring immediate attention from the CPU. Interrupts are essential in
//! various applications, such as real-time tasks, I/O communications, and
//! handling external events like hardware signals.
//! Interrupt support functionality depends heavily on the features enabled.
//!
//! The core functionality of the `interrupt` driver revolves around the
//! management of interrupts. When an interrupt occurs, it temporarily stops the
//! ongoing CPU operations, saves its current state, and starts executing the
//! corresponding interrupt service routine (ISR). The interrupt service routine
//! is a user-defined function that performs the necessary actions to handle the
//! specific interrupt. Once the ISR is executed, the driver restores the saved
//! CPU state and resumes normal program execution.
//! When the `vectored` feature is enabled, the
//! [`enable`] method will map interrupt to a CPU
//! interrupt, and handle the `vector`ing to the peripheral interrupt, for
//! example `UART0`.
//!
//! In scenarios where multiple interrupts may occur simultaneously, the
//! interrupt driver determines the `priority` of each interrupt. This
//! prioritization ensures that critical or high-priority tasks are handled
//! first. It helps prevent delays in time-sensitive applications and allows the
//! system to allocate resources efficiently. This functionality is provided and
//! implemented by the `priority` enum.
//! It is also 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.
//!
//! The `vectored` reserves a number of CPU interrupts, which cannot be used see
//! [`RESERVED_INTERRUPTS`].
//!
//! ## Example
//! ```no_run
@ -30,12 +21,20 @@
//! ...
//! critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int));
//!
//! // enable the interrupt
//! interrupt::enable(
//! peripherals::Interrupt::FROM_CPU_INTR0,
//! interrupt::Priority::Priority1,
//! )
//! .unwrap();
//!
//! // trigger the interrupt
//! SWINT
//! .borrow_ref_mut(cs)
//! .as_mut()
//! .unwrap()
//! .raise(SoftwareInterrupt::SoftwareInterrupt0);
//!
//! loop {}
//! }
//!

View File

@ -29,6 +29,15 @@ use crate::{
Cpu,
};
/// Interrupt Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
InvalidInterruptPriority,
#[cfg(feature = "vectored")]
CpuInterruptReserved,
}
/// Interrupt kind
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptKind {
@ -128,269 +137,7 @@ impl Priority {
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)
if let Some(cpu_interrupt) =
get_assigned_cpu_interrupt(core::mem::transmute(interrupt_nr))
{
let prio = get_priority_by_core(core, cpu_interrupt);
prios[prio as usize] |= 1 << (interrupt_nr as usize);
}
status &= !(1u128 << interrupt_nr);
}
prios
}
}
/// Interrupt Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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.
#[cfg(not(feature = "direct-vectoring"))]
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(())
}
/// Bind the given interrupt to the given 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);
}
/// Enables an interrupt at a given priority, maps it to the given CPU
/// interrupt and assigns the given priority.
///
/// This can be side-effectful since no guarantees can be made about the
/// CPU interrupt not already being in use.
///
/// Note that interrupts still need to be enabled globally for interrupts
/// to be serviced.
#[cfg(feature = "direct-vectoring")]
pub unsafe fn enable(
interrupt: Interrupt,
level: Priority,
cpu_interrupt: CpuInterrupt,
) -> Result<(), Error> {
if matches!(level, Priority::None) {
return Err(Error::InvalidInterruptPriority);
}
unsafe {
map(crate::get_core(), interrupt, cpu_interrupt);
set_priority(crate::get_core(), cpu_interrupt, level);
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 core::ptr::eq(
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)
}
}
pub const RESERVED_INTERRUPTS: &[usize] = INTERRUPT_TO_PRIORITY;
/// # Safety
///
@ -399,125 +146,14 @@ mod vectored {
#[link_section = ".trap.rust"]
#[export_name = "_start_trap_rust_hal"]
pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
// User code shouldn't usually take the mutable TrapFrame or the TrapFrame in
// general. However this makes things like preemtive multitasking easier in
// future
assert!(
mcause::read().is_exception(),
"Arrived into _start_trap_rust_hal but mcause is not an exception!"
);
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);
// Defined in `esp-riscv-rt`
pub fn DefaultHandler();
}
let cause = mcause::read();
if cause.is_exception() {
extern "C" {
fn ExceptionHandler(tf: *mut TrapFrame);
}
ExceptionHandler(trap_frame);
} else {
#[cfg(feature = "interrupt-preemption")]
let interrupt_priority = _handle_priority();
let code = mcause::read().code();
// with CLIC the MCAUSE register changed
// 31: Interrupt flag (same as before)
//
// 30: MINHV: Used to indicate whether the processor is fetching the vector
// interrupt entry address. This bit will be set high when the processor
// responds to the vector interrupt. Cleared to 0 after successfully obtaining
// the vector interrupt service routine entry address
//
// 29-28: MPP: This bit is the mirror image of MSTATUS.MPP[1:0], that is,
// reading and writing MCAUSE.MPP will produce the same result as
// reading and writing MSTATUS.MPP.
//
// 27: MPIE: This bit mirrors MSTATUS.MPIE
//
// 23-16: MPIL: This bit saves the interrupt priority level before the
// processor enters the interrupt service routine, that is, the MINTSTATUS.MIL
// bit is copied to this bit. When executing the MRET instruction and returning
// from an interrupt, the processor copies the MPIL bit to the MIL bit in the
// MINTSTATUS register.
//
// 11-0: Exception code: When the processor is configured in CLIC mode, this
// bit field is expanded to 12 bits, supporting up to 4096 interrupt ID number
// records.
//
// So we need to mask out bits other than > 12. We currently only support
// external interrupts so we subtract EXTERNAL_INTERRUPT_OFFSET
#[cfg(clic)]
let code = (code & 0b1111_1111_1111) - EXTERNAL_INTERRUPT_OFFSET as usize;
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);
fn ExceptionHandler(tf: *mut TrapFrame);
}
ExceptionHandler(trap_frame);
}
#[doc(hidden)]
@ -554,6 +190,33 @@ pub fn _setup_interrupts() {
}
}
/// Enable an interrupt by directly binding it to a available CPU interrupt
///
/// Unless you are sure, you most likely want to use [`enable`] with the
/// `vectored` feature enabled instead.
///
/// When the `vectored` feature is enabled, 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> {
#[cfg(feature = "vectored")]
if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
return Err(Error::CpuInterruptReserved);
}
if matches!(level, Priority::None) {
return Err(Error::InvalidInterruptPriority);
}
unsafe {
map(crate::get_core(), interrupt, cpu_interrupt);
set_priority(crate::get_core(), cpu_interrupt, level);
enable_cpu_interrupt(cpu_interrupt);
}
Ok(())
}
/// Disable the given peripheral interrupt.
pub fn disable(_core: Cpu, interrupt: Interrupt) {
unsafe {
@ -628,8 +291,9 @@ pub fn get_status(_core: Cpu) -> u128 {
/// 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.
/// Great care must be taken when using the `vectored` feature,
/// do not use CPU interrupts in the [`RESERVED_INTERRUPTS`] when
/// the `vectored` feature 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;
@ -660,6 +324,238 @@ unsafe fn get_assigned_cpu_interrupt(interrupt: Interrupt) -> Option<CpuInterrup
}
}
#[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)
if let Some(cpu_interrupt) =
get_assigned_cpu_interrupt(core::mem::transmute(interrupt_nr))
{
let prio = get_priority_by_core(core, cpu_interrupt);
prios[prio as usize] |= 1 << (interrupt_nr as usize);
}
status &= !(1u128 << interrupt_nr);
}
prios
}
}
/// 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(())
}
/// Bind the given interrupt to the given 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);
}
#[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 core::ptr::eq(
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]
unsafe fn cpu_int_1_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt1, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_2_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt2, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_3_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt3, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_4_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt4, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_5_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt5, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_6_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt6, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_7_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt7, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_8_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt8, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_9_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt9, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_10_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt10, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_11_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt11, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_12_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt12, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_13_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt13, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_14_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt14, context)
}
#[no_mangle]
#[ram]
unsafe fn cpu_int_15_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt15, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
unsafe fn cpu_int_16_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt16, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
unsafe fn cpu_int_17_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt17, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
unsafe fn cpu_int_18_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt18, context)
}
#[cfg(plic)]
#[no_mangle]
#[ram]
unsafe fn cpu_int_19_handler(context: &mut TrapFrame) {
handle_interrupts(CpuInterrupt::Interrupt19, context)
}
}
#[cfg(not(any(plic, clic)))]
mod classic {
use super::{CpuInterrupt, InterruptKind, Priority};
@ -669,11 +565,11 @@ mod classic {
pub(super) const EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
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 PRIORITY_TO_INTERRUPT: &[usize] =
&[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];
pub(super) const INTERRUPT_TO_PRIORITY: &[usize] =
&[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) {
@ -749,7 +645,6 @@ mod classic {
.read_volatile();
core::mem::transmute(prio as u8)
}
#[cfg(any(feature = "interrupt-preemption", feature = "direct-vectoring"))]
#[no_mangle]
#[link_section = ".trap"]
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
@ -759,7 +654,7 @@ mod classic {
let interrupt_priority = intr
.cpu_int_pri_0()
.as_ptr()
.offset(interrupt_id as isize)
.add(interrupt_id)
.read_volatile();
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits();
@ -773,7 +668,6 @@ mod classic {
}
prev_interrupt_priority
}
#[cfg(any(feature = "interrupt-preemption", feature = "direct-vectoring"))]
#[no_mangle]
#[link_section = ".trap"]
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
@ -795,10 +689,10 @@ mod plic {
// 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 PRIORITY_TO_INTERRUPT: &[usize] =
&[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
pub(super) const INTERRUPT_TO_PRIORITY: [usize; 19] = [
pub(super) const INTERRUPT_TO_PRIORITY: &[usize] = &[
1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
];
@ -807,7 +701,6 @@ mod plic {
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(any(feature = "interrupt-preemption", feature = "direct-vectoring"))]
const PLIC_MXINT_THRESH_REG: u32 = DR_REG_PLIC_MX_BASE + 0x90;
/// Enable a CPU interrupt
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
@ -883,13 +776,12 @@ mod plic {
.read_volatile();
core::mem::transmute(prio as u8)
}
#[cfg(any(feature = "interrupt-preemption", feature = "direct-vectoring"))]
#[no_mangle]
#[link_section = ".trap"]
pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
use super::mcause;
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_id: isize = unwrap!(mcause::read().code().try_into()); // 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;
@ -904,7 +796,6 @@ mod plic {
}
prev_interrupt_priority
}
#[cfg(any(feature = "interrupt-preemption", feature = "direct-vectoring"))]
#[no_mangle]
#[link_section = ".trap"]
pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
@ -923,11 +814,11 @@ mod clic {
pub(super) const EXTERNAL_INTERRUPT_OFFSET: u32 = 16;
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 PRIORITY_TO_INTERRUPT: &[usize] =
&[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];
pub(super) const INTERRUPT_TO_PRIORITY: &[usize] =
&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
// The memory map for interrupt registers is on a per-core basis,
// base points to the current core interrupt register,

View File

@ -9,6 +9,14 @@ use crate::{
Cpu,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
InvalidInterrupt,
#[cfg(feature = "vectored")]
CpuInterruptReserved,
}
/// Enumeration of available CPU interrupts
///
/// It's possible to create one handler per priority level. (e.g
@ -52,16 +60,41 @@ pub enum CpuInterrupt {
Interrupt31EdgePriority5,
}
pub const RESERVED_INTERRUPTS: &[usize] = &[
CpuInterrupt::Interrupt1LevelPriority1 as _,
CpuInterrupt::Interrupt19LevelPriority2 as _,
CpuInterrupt::Interrupt23LevelPriority3 as _,
CpuInterrupt::Interrupt10EdgePriority1 as _,
CpuInterrupt::Interrupt22EdgePriority3 as _,
];
/// Enable an interrupt by directly binding it to a available CPU interrupt
///
/// Unless you are sure, you most likely want to use [`enable`] with the
/// `vectored` feature enabled instead.
///
/// When the `vectored` feature is enabled, trying using a reserved interrupt
/// from [`RESERVED_INTERRUPTS`] will return an error.
pub fn enable_direct(interrupt: Interrupt, cpu_interrupt: CpuInterrupt) -> Result<(), Error> {
#[cfg(feature = "vectored")]
if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
return Err(Error::CpuInterruptReserved);
}
unsafe {
map(crate::get_core(), 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
///
/// Great care **must** be taken when using this function with interrupt
/// vectoring (enabled by default). Avoid the following CPU interrupts:
/// - Interrupt1LevelPriority1
/// - Interrupt19LevelPriority2
/// - Interrupt23LevelPriority3
/// - Interrupt10EdgePriority1
/// - Interrupt22EdgePriority3
/// As they are preallocated for interrupt vectoring.
/// vectoring (enabled by default). Avoid the interrupts listed in
/// [`RESERVED_INTERRUPTS`] as they are preallocated for interrupt vectoring.
///
/// Note: this only maps the interrupt to the CPU interrupt. The CPU interrupt
/// still needs to be enabled afterwards
@ -196,12 +229,6 @@ mod vectored {
use super::*;
use crate::get_core;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
InvalidInterrupt,
}
/// Interrupt priority levels.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]

View File

@ -35,22 +35,14 @@ zero-bss = []
## Zero the `.rtc_fast.bss` section.
zero-rtc-fast-bss = []
#! ### Interrupt Feature Flags
## Enable direct interrupt vectoring.
direct-vectoring = []
## Enable interrupt preemption.
interrupt-preemption = []
# This feature is intended for testing; you probably don't want to enable it:
ci = [
"direct-vectoring",
"fix-sp",
"has-mie-mip",
"init-data",
"init-rtc-fast-data",
"init-rtc-fast-text",
"init-rw-text",
"interrupt-preemption",
"zero-bss",
"zero-rtc-fast-bss",
]

View File

@ -442,7 +442,7 @@ _abs_start:
_start_trap_rust, then restores all saved registers before `mret`
*/
.section .trap, "ax"
.weak _start_trap
.weak _start_trap /* Exceptions call into _start_trap in vectored mode */
.weak _start_trap1
.weak _start_trap2
.weak _start_trap3
@ -475,8 +475,40 @@ _abs_start:
.weak _start_trap30
.weak _start_trap31
"#,
#[cfg(feature="direct-vectoring")]
r#"
_start_trap: // Handle exceptions in vectored mode
"#,
#[cfg(feature="fix-sp")]
r#"
// move SP to some save place if it's pointing below the RAM
// otherwise we won't be able to do anything reasonable
// (since we don't have a working stack)
//
// most probably we will just print something and halt in this case
// we actually can't do anything else
csrw mscratch, t0
la t0, _stack_end
bge sp, t0, 1f
// use the reserved exception cause 14 to signal we detected a stack overflow
li t0, 14
csrw mcause, t0
// set SP to the start of the stack
la sp, _stack_start
li t0, 4 // make sure stack start is in RAM
sub sp, sp, t0
andi sp, sp, -16 // Force 16-byte alignment
1:
csrr t0, mscratch
// now SP is in RAM - continue
"#,
r#"
addi sp, sp, -40*4
sw ra, 0(sp)
la ra, _start_trap_rust_hal /* Load the HAL trap handler */
j _start_trap_direct
_start_trap1:
addi sp, sp, -40*4
sw ra, 0(sp)
@ -632,77 +664,7 @@ _start_trap31:
sw ra, 0(sp)
la ra, cpu_int_31_handler
j _start_trap_direct
"#,
#[cfg(not(feature="direct-vectoring"))]
r#"
_start_trap1:
_start_trap2:
_start_trap3:
_start_trap4:
_start_trap5:
_start_trap6:
_start_trap7:
_start_trap8:
_start_trap9:
_start_trap10:
_start_trap11:
_start_trap12:
_start_trap13:
_start_trap14:
_start_trap15:
_start_trap16:
_start_trap17:
_start_trap18:
_start_trap19:
_start_trap20:
_start_trap21:
_start_trap22:
_start_trap23:
_start_trap24:
_start_trap25:
_start_trap26:
_start_trap27:
_start_trap28:
_start_trap29:
_start_trap30:
_start_trap31:
"#,
r#"
_start_trap:
"#,
#[cfg(feature="fix-sp")]
r#"
// move SP to some save place if it's pointing below the RAM
// otherwise we won't be able to do anything reasonable
// (since we don't have a working stack)
//
// most probably we will just print something and halt in this case
// we actually can't do anything else
csrw mscratch, t0
la t0, _stack_end
bge sp, t0, 1f
// use the reserved exception cause 14 to signal we detected a stack overflow
li t0, 14
csrw mcause, t0
// set SP to the start of the stack
la sp, _stack_start
li t0, 4 // make sure stack start is in RAM
sub sp, sp, t0
andi sp, sp, -16 // Force 16-byte alignment
1:
csrr t0, mscratch
// now SP is in RAM - continue
"#,
r#"
addi sp, sp, -40*4
sw ra, 0*4(sp)"#,
#[cfg(feature="direct-vectoring")] //for the directly vectored handlers the above is stacked beforehand
r#"
la ra, _start_trap_rust_hal #this runs on exception, use regular fault handler
la ra, _start_trap_rust_hal /* this runs on exception, use regular fault handler */
_start_trap_direct:
"#,
r#"
@ -749,7 +711,7 @@ r#"
add a0, sp, zero
"#,
#[cfg(all(feature="interrupt-preemption", feature="direct-vectoring"))] //store current priority, set threshold, enable interrupts
// store current priority, set threshold, enable interrupts
r#"
addi sp, sp, -4 #build stack
sw ra, 0(sp)
@ -758,15 +720,11 @@ r#"
sw a0, 0(sp) #reuse old stack, a0 is return of _handle_priority
addi a0, sp, 4 #the proper stack pointer is an argument to the HAL handler
"#,
#[cfg(not(feature="direct-vectoring"))] //jump to HAL handler
r#"
jal ra, _start_trap_rust_hal
"#,
#[cfg(feature="direct-vectoring")] //jump to handler loaded in direct handler
// jump to handler loaded in direct handler
r#"
jalr ra, ra #jump to label loaded in _start_trapx
"#,
#[cfg(all(feature="interrupt-preemption", feature="direct-vectoring"))] //restore threshold
// restore threshold
r#"
lw a0, 0(sp) #load stored priority
jal ra, _restore_priority
@ -866,10 +824,8 @@ _vector_table:
j _start_trap29
j _start_trap30
j _start_trap31
.option pop
"#,
#[cfg(feature="direct-vectoring")]
r#"
#this is required for the linking step, these symbols for in-use interrupts should always be overwritten by the user.
.section .trap, "ax"

View File

@ -27,5 +27,8 @@ rustflags = [
# "-C", "linker=rust-lld",
]
[env]
ESP_LOGLEVEL = "info"
[unstable]
build-std = ["alloc", "core"]

View File

@ -23,9 +23,9 @@ embedded-hal-bus = "0.1.0"
embedded-io-async = "0.6.1"
esp-alloc = "0.3.0"
esp-backtrace = { version = "0.11.1", features = ["exception-handler", "panic-handler", "println"] }
esp-hal = { version = "0.16.0", path = "../esp-hal" }
esp-hal = { version = "0.16.0", path = "../esp-hal", features = ["log"] }
esp-hal-smartled = { version = "0.9.0", path = "../esp-hal-smartled", optional = true }
esp-println = "0.9.1"
esp-println = { version = "0.9.1", features = ["log"] }
heapless = "0.8.0"
hex-literal = "0.4.1"
hmac = { version = "0.12.1", default-features = false }
@ -39,6 +39,7 @@ ssd1306 = "0.8.4"
static_cell = { version = "2.0.0", features = ["nightly"] }
usb-device = "0.3.2"
usbd-serial = "0.2.1"
log = "0.4"
[features]
esp32 = ["esp-hal/esp32", "esp-backtrace/esp32", "esp-println/esp32", "esp-hal-smartled/esp32"]
@ -63,9 +64,6 @@ embassy-executor-interrupt = ["esp-hal/embassy-executor-interrupt"]
embassy-time-timg0 = ["esp-hal/embassy-time-timg0"]
embassy-generic-timers = ["embassy-time/generic-queue-8"]
direct-vectoring = ["esp-hal/direct-vectoring"]
interrupt-preemption = ["esp-hal/interrupt-preemption"]
opsram-2m = ["esp-hal/opsram-2m"]
psram-2m = ["esp-hal/psram-2m"]

View File

@ -2,7 +2,6 @@
#![no_std]
//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2
//% FEATURES: direct-vectoring
use core::{arch::asm, cell::RefCell};
@ -33,14 +32,13 @@ fn main() -> ! {
let sw_int = system.software_interrupt_control;
critical_section::with(|cs| SWINT.borrow_ref_mut(cs).replace(sw_int));
interrupt::enable_direct(
Interrupt::FROM_CPU_INTR0,
Priority::Priority3,
CpuInterrupt::Interrupt20,
)
.unwrap();
unsafe {
interrupt::enable(
Interrupt::FROM_CPU_INTR0,
Priority::Priority3,
CpuInterrupt::Interrupt1,
)
.unwrap();
asm!(
"
csrrwi x0, 0x7e0, 1 #what to count, for cycles write 1 for instructions write 2
@ -69,7 +67,7 @@ fn main() -> ! {
}
#[no_mangle]
fn cpu_int_1_handler() {
fn cpu_int_20_handler() {
unsafe { asm!("csrrwi x0, 0x7e1, 0 #disable timer") }
critical_section::with(|cs| {
SWINT

View File

@ -33,7 +33,10 @@ async fn main(_spawner: Spawner) {
embassy::init(&clocks, timg0);
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))]
let mut input = io.pins.gpio0.into_pull_down_input();
#[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))]
let mut input = io.pins.gpio9.into_pull_down_input();
loop {
esp_println::println!("Waiting...");

View File

@ -4,9 +4,6 @@
//! priority. Should show higher-numbered software interrupts happening during
//! the handling of lower-numbered ones.
//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2
//% FEATURES: interrupt-preemption
#![no_std]
#![no_main]
@ -34,7 +31,7 @@ fn main() -> ! {
interrupt::enable(Interrupt::FROM_CPU_INTR0, Priority::Priority1).unwrap();
interrupt::enable(Interrupt::FROM_CPU_INTR1, Priority::Priority2).unwrap();
interrupt::enable(Interrupt::FROM_CPU_INTR2, Priority::Priority2).unwrap();
interrupt::enable(Interrupt::FROM_CPU_INTR3, Priority::Priority15).unwrap();
interrupt::enable(Interrupt::FROM_CPU_INTR3, Priority::Priority3).unwrap();
// Raise mid priority interrupt.
//