Skip to content

stm32: a HardFault resets without a trace and MemManage/BusFault/UsageFault hang; add a board hook to record faults #19745

Description

@lvdlvd

Description

In ports/stm32/stm32_it.c (v1.29.0), faults leave nothing behind to diagnose them with in the field:

HardFault resets silently. HardFault_C_Handler calls powerctrl_mcu_reset() first unless pyb.fault_debug(True) was set (lines 149-151). A crash in the field then looks exactly like a board that rebooted on its own, and machine.reset_cause() can't tell it apart from a software or pin reset.
The configurable faults hang. mpu.h enables MemManage (SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk), and MemManage_Handler, BusFault_Handler and UsageFault_Handler loop forever in MICROPY_BOARD_FATAL_ERROR. An MPU violation hangs the board. A hardware watchdog can reset it, but at default priorities a watchdog early-warning interrupt can't preempt the fault handler, so nothing records where the fault happened.

Proposal: an optional board hook, called before the reset. A board can save the fault to backup registers or no-init RAM and report it after reboot. When a board defines the hook, the configurable faults take the HardFault path too, so they are recorded and reset instead of hanging.

see code below

BusFault_Handler and UsageFault_Handler get the same treatment. The board side is a few lines: store pc, lr, xpsr plus SCB->CFSR and SCB->HFSR.

Tested on an STM32N657 board. An inline-asm branch to address 0 reset within 1 s and left CFSR = INVSTATE, HFSR = FORCED, PC = 0, and LR in fun_asm_call. We use this as a local patch and can open a PR if the approach is acceptable.

Code Size

void HardFault_C_Handler(ExceptionRegisters_t *regs) {

  • #if defined(MICROPY_BOARD_HARD_FAULT)
  • // Last chance to note where it happened: the reset below is silent.
  • MICROPY_BOARD_HARD_FAULT(regs->pc, regs->lr, regs->xpsr);
  • #endif
  • if (!pyb_hard_fault_debug) {
    powerctrl_mcu_reset();
    }

+#if defined(MICROPY_BOARD_HARD_FAULT)
+// The board records faults, so the configurable ones take the HardFault
+// path too: recorded, then reset (or dumped with pyb.fault_debug()), instead
+// of a silent hang. CFSR still says which fault it was. A plain branch keeps
+// LR as EXC_RETURN for HardFault_Handler's choice of stack.
+attribute((naked)) void MemManage_Handler(void) {

  • __asm volatile ("b HardFault_Handler");
    +}
    +#else
    void MemManage_Handler(void) {
    /* Go to infinite loop when Memory Manage exception occurs */
    while (1) {
    MICROPY_BOARD_FATAL_ERROR("MemManage");
    }
    }
    +#endif

Implementation

I intend to implement this feature and would submit a Pull Request if desirable

Code of Conduct

Yes, I agree

Activity

  1. added
    enhancementFeature requests, new feature implementations
    on Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementFeature requests, new feature implementations

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions