mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-26 20:00:32 +00:00
Move exception handler from esp-backtrace to esp-hal (#3887)
* Move exception handler from esp-backtrace to esp-hal * CHANGELOG.md * Fix * Only use defmt compatible display hints * Workaround xtensa-lx-rt and riscv-rt not implementing defmt::Format * Add `defmt` feature to xtensa-lx-rt and esp-riscv-rt * Favor feature gates over random `allow`s - remove unnecessary `allow`s
This commit is contained in:
parent
0fbbe2d22c
commit
3c12be8d24
@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Removed
|
||||
|
||||
- the `exception-handler` feature got removed (#3887)
|
||||
|
||||
## [v0.17.0] - 2025-07-16
|
||||
|
||||
|
@ -53,13 +53,11 @@ print-float-registers = [] # TODO support esp32p4
|
||||
# additional functionality:
|
||||
## Print messages in red
|
||||
colors = []
|
||||
## Invoke the extern function `custom_halt()` instead of doing a loop {} in case of a panic or exception
|
||||
## Invoke the extern function `custom_halt()` instead of doing a loop {} in case of a panic
|
||||
custom-halt = []
|
||||
## Invoke the extern function `custom_pre_backtrace()` before handling a panic or exception
|
||||
## Invoke the extern function `custom_pre_backtrace()` before handling a panic
|
||||
custom-pre-backtrace = []
|
||||
## Turn unhandled exceptions into panics
|
||||
exception-handler = []
|
||||
## Halt both CPUs on ESP32 / ESP32-S3 instead of doing a `loop {}` in case of a panic or exception
|
||||
## Halt both CPUs on ESP32 / ESP32-S3 instead of doing a `loop {}` in case of a panic
|
||||
halt-cores = []
|
||||
## Include a panic handler
|
||||
panic-handler = []
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! ## Feature Flags
|
||||
#![doc = document_features::document_features!()]
|
||||
#![allow(rustdoc::bare_urls, unused_macros)]
|
||||
#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))]
|
||||
//! This is a lightweight crate for obtaining backtraces during panics, exceptions, and hard faults
|
||||
//! on Espressif devices. It provides optional panic and exception handlers and supports a range of
|
||||
@ -60,24 +59,26 @@ impl BacktraceFrame {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "panic-handler")]
|
||||
const RESET: &str = "\u{001B}[0m";
|
||||
#[cfg(feature = "panic-handler")]
|
||||
const RED: &str = "\u{001B}[31m";
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
#[cfg(all(feature = "panic-handler", feature = "defmt"))]
|
||||
macro_rules! println {
|
||||
($($arg:tt)*) => {
|
||||
defmt::error!($($arg)*);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "println", not(feature = "defmt")))]
|
||||
#[cfg(all(feature = "panic-handler", feature = "println"))]
|
||||
macro_rules! println {
|
||||
($($arg:tt)*) => {
|
||||
esp_println::println!($($arg)*);
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused, unused_variables)]
|
||||
#[cfg(feature = "panic-handler")]
|
||||
fn set_color_code(code: &str) {
|
||||
#[cfg(all(feature = "colors", feature = "println"))]
|
||||
{
|
||||
@ -94,10 +95,12 @@ pub mod arch;
|
||||
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||
pre_backtrace();
|
||||
|
||||
set_color_code(RED);
|
||||
println!("");
|
||||
println!("====================== PANIC ======================");
|
||||
|
||||
println!("{}", info);
|
||||
set_color_code(RESET);
|
||||
|
||||
println!("");
|
||||
println!("Backtrace:");
|
||||
@ -117,53 +120,6 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||
abort();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "exception-handler", target_arch = "xtensa"))]
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
unsafe fn __user_exception(cause: arch::ExceptionCause, context: arch::Context) {
|
||||
panic!("\n\nException occurred '{}'\n{:?}", cause, context);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "exception-handler", target_arch = "riscv32"))]
|
||||
#[unsafe(export_name = "ExceptionHandler")]
|
||||
fn exception_handler(context: &arch::TrapFrame) -> ! {
|
||||
let mepc = context.pc;
|
||||
let code = context.mcause & 0xff;
|
||||
let mtval = context.mtval;
|
||||
|
||||
if code == 14 {
|
||||
panic!(
|
||||
"Stack overflow detected at 0x{:x} called by 0x{:x}",
|
||||
mepc, context.ra
|
||||
);
|
||||
} else {
|
||||
let code = match code {
|
||||
0 => "Instruction address misaligned",
|
||||
1 => "Instruction access fault",
|
||||
2 => "Illegal instruction",
|
||||
3 => "Breakpoint",
|
||||
4 => "Load address misaligned",
|
||||
5 => "Load access fault",
|
||||
6 => "Store/AMO address misaligned",
|
||||
7 => "Store/AMO access fault",
|
||||
8 => "Environment call from U-mode",
|
||||
9 => "Environment call from S-mode",
|
||||
10 => "Reserved",
|
||||
11 => "Environment call from M-mode",
|
||||
12 => "Instruction page fault",
|
||||
13 => "Load page fault",
|
||||
14 => "Reserved",
|
||||
15 => "Store/AMO page fault",
|
||||
_ => "UNKNOWN",
|
||||
};
|
||||
|
||||
panic!(
|
||||
"Exception '{}' mepc=0x{:08x}, mtval=0x{:08x}\n{:?}",
|
||||
code, mepc, mtval, context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the address is in DRAM and that it is 16-byte aligned.
|
||||
//
|
||||
// Based loosely on the `esp_stack_ptr_in_dram` function from
|
||||
@ -219,7 +175,7 @@ fn is_valid_ram_address(address: u32) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[cfg(feature = "panic-handler")]
|
||||
fn halt() -> ! {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "custom-halt")] {
|
||||
@ -268,11 +224,10 @@ fn halt() -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::empty_loop)]
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[cfg(feature = "panic-handler")]
|
||||
fn pre_backtrace() {
|
||||
#[cfg(feature = "custom-pre-backtrace")]
|
||||
{
|
||||
@ -281,18 +236,14 @@ fn pre_backtrace() {
|
||||
}
|
||||
unsafe { custom_pre_backtrace() }
|
||||
}
|
||||
|
||||
set_color_code(RED);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[cfg(feature = "panic-handler")]
|
||||
fn abort() -> ! {
|
||||
println!("");
|
||||
println!("");
|
||||
println!("");
|
||||
|
||||
set_color_code(RESET);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "semihosting")] {
|
||||
semihosting::process::abort();
|
||||
|
@ -7,159 +7,8 @@ use crate::{Backtrace, BacktraceFrame};
|
||||
// we get better results (especially if the caller was the last instruction in
|
||||
// the calling function) if we report the address of the JALR itself
|
||||
// even if it was a C.JALR we should get good results using RA - 4
|
||||
#[allow(unused)]
|
||||
pub(super) const RA_OFFSET: usize = 4;
|
||||
|
||||
/// Registers saved in trap handler
|
||||
#[doc(hidden)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
#[cfg(feature = "exception-handler")]
|
||||
pub(crate) struct TrapFrame {
|
||||
/// Return address, stores the address to return to after a function call or
|
||||
/// interrupt.
|
||||
pub ra: usize,
|
||||
/// Temporary register t0, used for intermediate values.
|
||||
pub t0: usize,
|
||||
/// Temporary register t1, used for intermediate values.
|
||||
pub t1: usize,
|
||||
/// Temporary register t2, used for intermediate values.
|
||||
pub t2: usize,
|
||||
/// Temporary register t3, used for intermediate values.
|
||||
pub t3: usize,
|
||||
/// Temporary register t4, used for intermediate values.
|
||||
pub t4: usize,
|
||||
/// Temporary register t5, used for intermediate values.
|
||||
pub t5: usize,
|
||||
/// Temporary register t6, used for intermediate values.
|
||||
pub t6: usize,
|
||||
/// Argument register a0, typically used to pass the first argument to a
|
||||
/// function.
|
||||
pub a0: usize,
|
||||
/// Argument register a1, typically used to pass the second argument to a
|
||||
/// function.
|
||||
pub a1: usize,
|
||||
/// Argument register a2, typically used to pass the third argument to a
|
||||
/// function.
|
||||
pub a2: usize,
|
||||
/// Argument register a3, typically used to pass the fourth argument to a
|
||||
/// function.
|
||||
pub a3: usize,
|
||||
/// Argument register a4, typically used to pass the fifth argument to a
|
||||
/// function.
|
||||
pub a4: usize,
|
||||
/// Argument register a5, typically used to pass the sixth argument to a
|
||||
/// function.
|
||||
pub a5: usize,
|
||||
/// Argument register a6, typically used to pass the seventh argument to a
|
||||
/// function.
|
||||
pub a6: usize,
|
||||
/// Argument register a7, typically used to pass the eighth argument to a
|
||||
/// function.
|
||||
pub a7: usize,
|
||||
/// Saved register s0, used to hold values across function calls.
|
||||
pub s0: usize,
|
||||
/// Saved register s1, used to hold values across function calls.
|
||||
pub s1: usize,
|
||||
/// Saved register s2, used to hold values across function calls.
|
||||
pub s2: usize,
|
||||
/// Saved register s3, used to hold values across function calls.
|
||||
pub s3: usize,
|
||||
/// Saved register s4, used to hold values across function calls.
|
||||
pub s4: usize,
|
||||
/// Saved register s5, used to hold values across function calls.
|
||||
pub s5: usize,
|
||||
/// Saved register s6, used to hold values across function calls.
|
||||
pub s6: usize,
|
||||
/// Saved register s7, used to hold values across function calls.
|
||||
pub s7: usize,
|
||||
/// Saved register s8, used to hold values across function calls.
|
||||
pub s8: usize,
|
||||
/// Saved register s9, used to hold values across function calls.
|
||||
pub s9: usize,
|
||||
/// Saved register s10, used to hold values across function calls.
|
||||
pub s10: usize,
|
||||
/// Saved register s11, used to hold values across function calls.
|
||||
pub s11: usize,
|
||||
/// Global pointer register, holds the address of the global data area.
|
||||
pub gp: usize,
|
||||
/// Thread pointer register, holds the address of the thread-local storage
|
||||
/// area.
|
||||
pub tp: usize,
|
||||
/// Stack pointer register, holds the address of the top of the stack.
|
||||
pub sp: usize,
|
||||
/// Program counter, stores the address of the next instruction to be
|
||||
/// executed.
|
||||
pub pc: usize,
|
||||
/// Machine status register, holds the current status of the processor,
|
||||
/// including interrupt enable bits and privilege mode.
|
||||
pub mstatus: usize,
|
||||
/// Machine cause register, contains the reason for the trap (e.g.,
|
||||
/// exception or interrupt number).
|
||||
pub mcause: usize,
|
||||
/// Machine trap value register, contains additional information about the
|
||||
/// trap (e.g., faulting address).
|
||||
pub mtval: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "exception-handler")]
|
||||
impl core::fmt::Debug for TrapFrame {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"TrapFrame
|
||||
PC=0x{:08x} RA/x1=0x{:08x} SP/x2=0x{:08x} GP/x3=0x{:08x} TP/x4=0x{:08x}
|
||||
T0/x5=0x{:08x} T1/x6=0x{:08x} T2/x7=0x{:08x} S0/FP/x8=0x{:08x} S1/x9=0x{:08x}
|
||||
A0/x10=0x{:08x} A1/x11=0x{:08x} A2/x12=0x{:08x} A3/x13=0x{:08x} A4/x14=0x{:08x}
|
||||
A5/x15=0x{:08x} A6/x16=0x{:08x} A7/x17=0x{:08x} S2/x18=0x{:08x} S3/x19=0x{:08x}
|
||||
S4/x20=0x{:08x} S5/x21=0x{:08x} S6/x22=0x{:08x} S7/x23=0x{:08x} S8/x24=0x{:08x}
|
||||
S9/x25=0x{:08x} S10/x26=0x{:08x} S11/x27=0x{:08x} T3/x28=0x{:08x} T4/x29=0x{:08x}
|
||||
T5/x30=0x{:08x} T6/x31=0x{:08x}
|
||||
|
||||
MSTATUS=0x{:08x}
|
||||
MCAUSE=0x{:08x}
|
||||
MTVAL=0x{:08x}
|
||||
",
|
||||
self.pc,
|
||||
self.ra,
|
||||
self.gp,
|
||||
self.sp,
|
||||
self.tp,
|
||||
self.t0,
|
||||
self.t1,
|
||||
self.t2,
|
||||
self.s0,
|
||||
self.s1,
|
||||
self.a0,
|
||||
self.a1,
|
||||
self.a2,
|
||||
self.a3,
|
||||
self.a4,
|
||||
self.a5,
|
||||
self.a6,
|
||||
self.a7,
|
||||
self.s2,
|
||||
self.s3,
|
||||
self.s4,
|
||||
self.s5,
|
||||
self.s6,
|
||||
self.s7,
|
||||
self.s8,
|
||||
self.s9,
|
||||
self.s10,
|
||||
self.s11,
|
||||
self.t3,
|
||||
self.t4,
|
||||
self.t5,
|
||||
self.t6,
|
||||
self.mstatus,
|
||||
self.mcause,
|
||||
self.mtval,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an array of backtrace addresses.
|
||||
///
|
||||
/// This needs `force-frame-pointers` enabled.
|
||||
|
@ -1,4 +1,4 @@
|
||||
use core::{arch::asm, fmt::Display};
|
||||
use core::arch::asm;
|
||||
|
||||
use crate::{Backtrace, BacktraceFrame};
|
||||
|
||||
@ -6,360 +6,8 @@ use crate::{Backtrace, BacktraceFrame};
|
||||
// the return address is the address following the callxN
|
||||
// we get better results (especially if the caller was the last function in the
|
||||
// calling function) if we report the address of callxN itself
|
||||
#[allow(unused)]
|
||||
pub(super) const RA_OFFSET: usize = 3;
|
||||
|
||||
/// Exception Cause
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
pub enum ExceptionCause {
|
||||
/// Illegal Instruction
|
||||
IllegalInstruction = 0,
|
||||
/// System Call (Syscall Instruction)
|
||||
Syscall = 1,
|
||||
/// Instruction Fetch Error
|
||||
InstrFetchError = 2,
|
||||
/// Load Store Error
|
||||
LoadStoreError = 3,
|
||||
/// Level 1 Interrupt
|
||||
LevelOneInterrupt = 4,
|
||||
/// Stack Extension Assist (movsp Instruction) For Alloca
|
||||
Alloca = 5,
|
||||
/// Integer Divide By Zero
|
||||
DivideByZero = 6,
|
||||
/// Use Of Failed Speculative Access (Not Implemented)
|
||||
NextPCValueIllegal = 7,
|
||||
/// Privileged Instruction
|
||||
PrivilegedInstruction = 8,
|
||||
/// Unaligned Load Or Store
|
||||
UnalignedLoadOrStore = 9,
|
||||
/// Reserved
|
||||
ExternalRegisterPrivilegeError = 10,
|
||||
/// Reserved
|
||||
ExclusiveError = 11,
|
||||
/// Pif Data Error On Instruction Fetch (Rb-200x And Later)
|
||||
InstrDataError = 12,
|
||||
/// Pif Data Error On Load Or Store (Rb-200x And Later)
|
||||
LoadStoreDataError = 13,
|
||||
/// Pif Address Error On Instruction Fetch (Rb-200x And Later)
|
||||
InstrAddrError = 14,
|
||||
/// Pif Address Error On Load Or Store (Rb-200x And Later)
|
||||
LoadStoreAddrError = 15,
|
||||
/// Itlb Miss (No Itlb Entry Matches, Hw Refill Also Missed)
|
||||
ItlbMiss = 16,
|
||||
/// Itlb Multihit (Multiple Itlb Entries Match)
|
||||
ItlbMultiHit = 17,
|
||||
/// Ring Privilege Violation On Instruction Fetch
|
||||
InstrRing = 18,
|
||||
/// Size Restriction On Ifetch (Not Implemented)
|
||||
Reserved19 = 19,
|
||||
/// Cache Attribute Does Not Allow Instruction Fetch
|
||||
InstrProhibited = 20,
|
||||
/// Reserved
|
||||
Reserved21 = 21,
|
||||
/// Reserved
|
||||
Reserved22 = 22,
|
||||
/// Reserved
|
||||
Reserved23 = 23,
|
||||
/// Dtlb Miss (No Dtlb Entry Matches, Hw Refill Also Missed)
|
||||
DtlbMiss = 24,
|
||||
/// Dtlb Multihit (Multiple Dtlb Entries Match)
|
||||
DtlbMultiHit = 25,
|
||||
/// Ring Privilege Violation On Load Or Store
|
||||
LoadStoreRing = 26,
|
||||
/// Size Restriction On Load/Store (Not Implemented)
|
||||
Reserved27 = 27,
|
||||
/// Cache Attribute Does Not Allow Load
|
||||
LoadProhibited = 28,
|
||||
/// Cache Attribute Does Not Allow Store
|
||||
StoreProhibited = 29,
|
||||
/// Reserved
|
||||
Reserved30 = 30,
|
||||
/// Reserved
|
||||
Reserved31 = 31,
|
||||
/// Access To Coprocessor 0 When Disabled
|
||||
Cp0Disabled = 32,
|
||||
/// Access To Coprocessor 1 When Disabled
|
||||
Cp1Disabled = 33,
|
||||
/// Access To Coprocessor 2 When Disabled
|
||||
Cp2Disabled = 34,
|
||||
/// Access To Coprocessor 3 When Disabled
|
||||
Cp3Disabled = 35,
|
||||
/// Access To Coprocessor 4 When Disabled
|
||||
Cp4Disabled = 36,
|
||||
/// Access To Coprocessor 5 When Disabled
|
||||
Cp5Disabled = 37,
|
||||
/// Access To Coprocessor 6 When Disabled
|
||||
Cp6Disabled = 38,
|
||||
/// Access To Coprocessor 7 When Disabled
|
||||
Cp7Disabled = 39,
|
||||
/// None
|
||||
None = 255,
|
||||
}
|
||||
|
||||
impl Display for ExceptionCause {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if *self == Self::Cp0Disabled {
|
||||
write!(
|
||||
f,
|
||||
"Cp0Disabled (Access to the floating point coprocessor is not allowed. You may want to enable the `float-save-restore` feature of the `xtensa-lx-rt` crate.)"
|
||||
)
|
||||
} else {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
pub struct Context {
|
||||
/// Program counter, stores the address of the next instruction to be
|
||||
/// executed.
|
||||
pub PC: u32,
|
||||
/// Processor status, holds various status flags for the CPU.
|
||||
pub PS: u32,
|
||||
/// General-purpose register A0 used for data storage and manipulation.
|
||||
pub A0: u32,
|
||||
/// General-purpose register A1 used for data storage and manipulation.
|
||||
pub A1: u32,
|
||||
/// General-purpose register A2 used for data storage and manipulation.
|
||||
pub A2: u32,
|
||||
/// General-purpose register A3 used for data storage and manipulation.
|
||||
pub A3: u32,
|
||||
/// General-purpose register A4 used for data storage and manipulation.
|
||||
pub A4: u32,
|
||||
/// General-purpose register A5 used for data storage and manipulation.
|
||||
pub A5: u32,
|
||||
/// General-purpose register A6 used for data storage and manipulation.
|
||||
pub A6: u32,
|
||||
/// General-purpose register A7 used for data storage and manipulation.
|
||||
pub A7: u32,
|
||||
/// General-purpose register A8 used for data storage and manipulation.
|
||||
pub A8: u32,
|
||||
/// General-purpose register A9 used for data storage and manipulation.
|
||||
pub A9: u32,
|
||||
/// General-purpose register A10 used for data storage and manipulation.
|
||||
pub A10: u32,
|
||||
/// General-purpose register A11 used for data storage and manipulation.
|
||||
pub A11: u32,
|
||||
/// General-purpose register A12 used for data storage and manipulation.
|
||||
pub A12: u32,
|
||||
/// General-purpose register A13 used for data storage and manipulation.
|
||||
pub A13: u32,
|
||||
/// General-purpose register A14 used for data storage and manipulation.
|
||||
pub A14: u32,
|
||||
/// General-purpose register A15 used for data storage and manipulation.
|
||||
pub A15: u32,
|
||||
/// Shift amount register, used for shift and rotate instructions.
|
||||
pub SAR: u32,
|
||||
/// Exception cause, indicates the reason for the last exception.
|
||||
pub EXCCAUSE: u32,
|
||||
/// Exception address, holds the address related to the exception.
|
||||
pub EXCVADDR: u32,
|
||||
/// Loop start address, used in loop instructions.
|
||||
pub LBEG: u32,
|
||||
/// Loop end address, used in loop instructions.
|
||||
pub LEND: u32,
|
||||
/// Loop counter, used to count iterations in loop instructions.
|
||||
pub LCOUNT: u32,
|
||||
/// Thread pointer, used for thread-local storage.
|
||||
pub THREADPTR: u32,
|
||||
/// Compare register, used for certain compare instructions.
|
||||
pub SCOMPARE1: u32,
|
||||
/// Break register, used for breakpoint-related operations.
|
||||
pub BR: u32,
|
||||
/// Accumulator low register, used for extended arithmetic operations.
|
||||
pub ACCLO: u32,
|
||||
/// Accumulator high register, used for extended arithmetic operations.
|
||||
pub ACCHI: u32,
|
||||
/// Additional register M0 used for special operations.
|
||||
pub M0: u32,
|
||||
/// Additional register M1 used for special operations.
|
||||
pub M1: u32,
|
||||
/// Additional register M2 used for special operations.
|
||||
pub M2: u32,
|
||||
/// Additional register M3 used for special operations.
|
||||
pub M3: u32,
|
||||
/// 64-bit floating-point register (low part), available if the
|
||||
/// `print-float-registers` feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F64R_LO: u32,
|
||||
/// 64-bit floating-point register (high part), available if the
|
||||
/// `print-float-registers` feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F64R_HI: u32,
|
||||
/// Floating-point status register, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F64S: u32,
|
||||
/// Floating-point control register, available if the
|
||||
/// `print-float-registers` feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub FCR: u32,
|
||||
/// Floating-point status register, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub FSR: u32,
|
||||
/// Floating-point register F0, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F0: u32,
|
||||
/// Floating-point register F1, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F1: u32,
|
||||
/// Floating-point register F2, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F2: u32,
|
||||
/// Floating-point register F3, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F3: u32,
|
||||
/// Floating-point register F4, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F4: u32,
|
||||
/// Floating-point register F5, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F5: u32,
|
||||
/// Floating-point register F6, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F6: u32,
|
||||
/// Floating-point register F7, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F7: u32,
|
||||
/// Floating-point register F8, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F8: u32,
|
||||
/// Floating-point register F9, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F9: u32,
|
||||
/// Floating-point register F10, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F10: u32,
|
||||
/// Floating-point register F11, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F11: u32,
|
||||
/// Floating-point register F12, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F12: u32,
|
||||
/// Floating-point register F13, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F13: u32,
|
||||
/// Floating-point register F14, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F14: u32,
|
||||
/// Floating-point register F15, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F15: u32,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Context {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"Context
|
||||
PC=0x{:08x} PS=0x{:08x}
|
||||
A0=0x{:08x} A1=0x{:08x} A2=0x{:08x} A3=0x{:08x} A4=0x{:08x}
|
||||
A5=0x{:08x} A6=0x{:08x} A7=0x{:08x} A8=0x{:08x} A9=0x{:08x}
|
||||
A10=0x{:08x} A11=0x{:08x} A12=0x{:08x} A13=0x{:08x} A14=0x{:08x}
|
||||
A15=0x{:08x}
|
||||
SAR={:08x}
|
||||
EXCCAUSE=0x{:08x} EXCVADDR=0x{:08x}
|
||||
LBEG=0x{:08x} LEND=0x{:08x} LCOUNT=0x{:08x}
|
||||
THREADPTR=0x{:08x}
|
||||
SCOMPARE1=0x{:08x}
|
||||
BR=0x{:08x}
|
||||
ACCLO=0x{:08x} ACCHI=0x{:08x}
|
||||
M0=0x{:08x} M1=0x{:08x} M2=0x{:08x} M3=0x{:08x}
|
||||
",
|
||||
self.PC,
|
||||
self.PS,
|
||||
self.A0,
|
||||
self.A1,
|
||||
self.A2,
|
||||
self.A3,
|
||||
self.A4,
|
||||
self.A5,
|
||||
self.A6,
|
||||
self.A7,
|
||||
self.A8,
|
||||
self.A9,
|
||||
self.A10,
|
||||
self.A11,
|
||||
self.A12,
|
||||
self.A13,
|
||||
self.A14,
|
||||
self.A15,
|
||||
self.SAR,
|
||||
self.EXCCAUSE,
|
||||
self.EXCVADDR,
|
||||
self.LBEG,
|
||||
self.LEND,
|
||||
self.LCOUNT,
|
||||
self.THREADPTR,
|
||||
self.SCOMPARE1,
|
||||
self.BR,
|
||||
self.ACCLO,
|
||||
self.ACCHI,
|
||||
self.M0,
|
||||
self.M1,
|
||||
self.M2,
|
||||
self.M3,
|
||||
)?;
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
write!(
|
||||
fmt,
|
||||
"F64R_LO=0x{:08x} F64R_HI=0x{:08x} F64S=0x{:08x}
|
||||
FCR=0x{:08x} FSR=0x{:08x}
|
||||
F0=0x{:08x} F1=0x{:08x} F2=0x{:08x} F3=0x{:08x} F4=0x{:08x}
|
||||
F5=0x{:08x} F6=0x{:08x} F7=0x{:08x} F8=0x{:08x} F9=0x{:08x}
|
||||
F10=0x{:08x} F11=0x{:08x} F12=0x{:08x} F13=0x{:08x} F14=0x{:08x}
|
||||
F15=0x{:08x}",
|
||||
self.F64R_LO,
|
||||
self.F64R_HI,
|
||||
self.F64S,
|
||||
self.FCR,
|
||||
self.FSR,
|
||||
self.F0,
|
||||
self.F1,
|
||||
self.F2,
|
||||
self.F3,
|
||||
self.F4,
|
||||
self.F5,
|
||||
self.F6,
|
||||
self.F7,
|
||||
self.F8,
|
||||
self.F9,
|
||||
self.F10,
|
||||
self.F11,
|
||||
self.F12,
|
||||
self.F13,
|
||||
self.F14,
|
||||
self.F15,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This function returns the caller's frame pointer.
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
|
@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- A reimplemntation of the `assign_resources!` macro (#3809)
|
||||
- `TrngSource` to manage random number generator entropy (#3829)
|
||||
- On RISC-V you can opt-out of nested interrupts for an interrupt handler by using `new_not_nested` (#3875)
|
||||
- A new default feature `exception-handler` was added (#3887)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -99,7 +99,7 @@ serde = { version = "1.0.219", default-features = false, features = ["der
|
||||
jiff = { version = "0.2.10", default-features = false, features = ["static"] }
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
default = ["rt", "exception-handler"]
|
||||
|
||||
# These features are considered private and unstable. They are not covered by
|
||||
# semver guarantees and may change or be removed without notice.
|
||||
@ -195,6 +195,10 @@ rt = [
|
||||
"esp32s3?/rt",
|
||||
]
|
||||
|
||||
## Enable a simple exception handler turning exceptions into panics.
|
||||
##
|
||||
## If you are depending on `esp-hal` as a library, you should *not* enable the `exception-handler` feature under any circumstance.
|
||||
exception-handler = []
|
||||
|
||||
#! ### Logging Feature Flags
|
||||
## Enable logging output using version 0.4 of the `log` crate.
|
||||
@ -217,6 +221,8 @@ defmt = [
|
||||
"esp32s2?/defmt",
|
||||
"esp32s3?/defmt",
|
||||
"fugit/defmt",
|
||||
"esp-riscv-rt?/defmt",
|
||||
"xtensa-lx-rt?/defmt",
|
||||
]
|
||||
|
||||
#! ### PSRAM Feature Flags
|
||||
|
51
esp-hal/src/exception_handler/mod.rs
Normal file
51
esp-hal/src/exception_handler/mod.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use crate::trapframe::TrapFrame;
|
||||
|
||||
#[cfg(xtensa)]
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
unsafe extern "C" fn __user_exception(
|
||||
cause: xtensa_lx_rt::exception::ExceptionCause,
|
||||
context: &TrapFrame,
|
||||
) {
|
||||
panic!("\n\nException occurred '{:?}'\n{:?}", cause, context);
|
||||
}
|
||||
|
||||
#[cfg(riscv)]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn ExceptionHandler(context: &TrapFrame) -> ! {
|
||||
let mepc = riscv::register::mepc::read();
|
||||
let code = riscv::register::mcause::read().code();
|
||||
let mtval = riscv::register::mtval::read();
|
||||
|
||||
if code == 14 {
|
||||
panic!(
|
||||
"Stack overflow detected at 0x{:x} called by 0x{:x}",
|
||||
mepc, context.ra
|
||||
);
|
||||
} else {
|
||||
let code = match code {
|
||||
0 => "Instruction address misaligned",
|
||||
1 => "Instruction access fault",
|
||||
2 => "Illegal instruction",
|
||||
3 => "Breakpoint",
|
||||
4 => "Load address misaligned",
|
||||
5 => "Load access fault",
|
||||
6 => "Store/AMO address misaligned",
|
||||
7 => "Store/AMO access fault",
|
||||
8 => "Environment call from U-mode",
|
||||
9 => "Environment call from S-mode",
|
||||
10 => "Reserved",
|
||||
11 => "Environment call from M-mode",
|
||||
12 => "Instruction page fault",
|
||||
13 => "Load page fault",
|
||||
14 => "Reserved",
|
||||
15 => "Store/AMO page fault",
|
||||
_ => "UNKNOWN",
|
||||
};
|
||||
|
||||
panic!(
|
||||
"Exception '{}' mepc={:08x}, mtval={:08x}\n{:?}",
|
||||
code, mepc, mtval, context
|
||||
);
|
||||
}
|
||||
}
|
@ -263,6 +263,9 @@ pub use procmacros::load_lp_code;
|
||||
#[cfg_attr(not(feature = "unstable"), allow(unused))]
|
||||
pub use procmacros::{handler, ram};
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "exception-handler"))]
|
||||
mod exception_handler;
|
||||
|
||||
// can't use instability on inline module definitions, see https://github.com/rust-lang/rust/issues/54727
|
||||
#[doc(hidden)]
|
||||
macro_rules! unstable_module {
|
||||
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- A new feature `defmt` which implements `defmt::Format` on `TrapFrame` (#3887)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -17,14 +17,19 @@ test = false
|
||||
|
||||
[dependencies]
|
||||
document-features = "0.2.11"
|
||||
defmt = {version = "1.0.1", optional = true}
|
||||
riscv = "0.14.0"
|
||||
riscv-rt-macros = "0.5.0"
|
||||
|
||||
[features]
|
||||
## Indicate that the device supports `mie` and `mip` CSRs.
|
||||
has-mie-mip = []
|
||||
|
||||
## Indicate that the device has RTC RAM.
|
||||
rtc-ram = []
|
||||
|
||||
## Implement `defmt::Format`
|
||||
defmt = ["dep:defmt"]
|
||||
|
||||
# This feature is intended for testing; you probably don't want to enable it:
|
||||
ci = ["has-mie-mip", "rtc-ram"]
|
||||
|
@ -70,6 +70,7 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! {
|
||||
|
||||
/// Registers saved in trap handler
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
pub struct TrapFrame {
|
||||
/// Return address, stores the address to return to after a function call or
|
||||
|
@ -22,7 +22,7 @@ embedded-io = { version = "0.6.1", default-features = false }
|
||||
embedded-io-async = "0.6.1"
|
||||
embedded-storage = "0.3.1"
|
||||
esp-alloc = { path = "../esp-alloc" }
|
||||
esp-backtrace = { path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] }
|
||||
esp-backtrace = { path = "../esp-backtrace", features = ["panic-handler", "println"] }
|
||||
esp-bootloader-esp-idf = { path = "../esp-bootloader-esp-idf" }
|
||||
esp-hal = { path = "../esp-hal", features = ["log-04"] }
|
||||
esp-hal-embassy = { path = "../esp-hal-embassy", optional = true }
|
||||
|
@ -249,7 +249,7 @@ embedded-can = "0.4.1"
|
||||
embedded-hal-async = "1.0.0"
|
||||
embedded-hal-nb = "1.0.0"
|
||||
esp-alloc = { path = "../esp-alloc", optional = true }
|
||||
esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "defmt", "semihosting"] }
|
||||
esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["defmt", "semihosting"] }
|
||||
esp-bootloader-esp-idf = { path = "../esp-bootloader-esp-idf" }
|
||||
esp-hal = { path = "../esp-hal" }
|
||||
esp-hal-embassy = { path = "../esp-hal-embassy", optional = true }
|
||||
|
@ -14,7 +14,7 @@ embassy-sync = "0.6.1"
|
||||
embedded-graphics = "0.8.1"
|
||||
embedded-hal-async = "1.0.0"
|
||||
esp-alloc = { path = "../esp-alloc" }
|
||||
esp-backtrace = { path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] }
|
||||
esp-backtrace = { path = "../esp-backtrace", features = ["panic-handler", "println"] }
|
||||
esp-bootloader-esp-idf = { path = "../esp-bootloader-esp-idf" }
|
||||
esp-hal = { path = "../esp-hal", features = ["unstable", "log-04"] }
|
||||
esp-hal-embassy = { path = "../esp-hal-embassy" }
|
||||
|
@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- A new feature `defmt` which implements `defmt::Format` on `Context` (#3887)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -17,6 +17,7 @@ test = false
|
||||
|
||||
[dependencies]
|
||||
document-features = "0.2.11"
|
||||
defmt = {version = "1.0.1", optional = true}
|
||||
macros = { version = "0.4.0", package = "xtensa-lx-rt-proc-macros", path = "../xtensa-lx-rt-proc-macros" }
|
||||
r0 = "1.0.0"
|
||||
xtensa-lx = { version = "0.12.0", path = "../xtensa-lx" }
|
||||
@ -27,5 +28,8 @@ xtensa-lx = { version = "0.12.0", path = "../xtensa-lx" }
|
||||
## Save and restore float registers for exceptions
|
||||
float-save-restore = []
|
||||
|
||||
## Implement `defmt::Format`
|
||||
defmt = ["dep:defmt"]
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = "allow"
|
||||
|
@ -34,6 +34,7 @@ pub use context::Context;
|
||||
/// vectors).
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
pub enum ExceptionCause {
|
||||
/// Illegal Instruction
|
||||
|
@ -6,6 +6,7 @@ use super::ExceptionCause;
|
||||
#[repr(C)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Context {
|
||||
pub PC: u32,
|
||||
pub PS: u32,
|
||||
|
Loading…
x
Reference in New Issue
Block a user