mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-01 06:11:03 +00:00
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:
parent
1f155cf301
commit
a61ffef909
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
@ -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 {}
|
||||
//! }
|
||||
//!
|
||||
|
@ -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,
|
||||
|
@ -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))]
|
||||
|
@ -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",
|
||||
]
|
||||
|
@ -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"
|
||||
|
@ -27,5 +27,8 @@ rustflags = [
|
||||
# "-C", "linker=rust-lld",
|
||||
]
|
||||
|
||||
[env]
|
||||
ESP_LOGLEVEL = "info"
|
||||
|
||||
[unstable]
|
||||
build-std = ["alloc", "core"]
|
||||
|
@ -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"]
|
||||
|
||||
|
@ -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
|
||||
|
@ -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...");
|
||||
|
@ -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.
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user