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:
Björn Quentin 2025-08-01 09:57:42 +02:00 committed by GitHub
parent 0fbbe2d22c
commit 3c12be8d24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 95 additions and 572 deletions

View File

@ -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

View File

@ -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 = []

View File

@ -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();

View File

@ -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.

View File

@ -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]

View File

@ -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

View File

@ -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

View 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
);
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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"]

View File

@ -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

View File

@ -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 }

View File

@ -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 }

View File

@ -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" }

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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,