From b019e3304ff6903619e58af95772b0156c11a2d1 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Wed, 19 Aug 2026 22:29:38 +0200 Subject: [PATCH 1/3] handle and report NMI/hardfaults --- board/early_init.h | 16 ++++++++++++++++ board/main.c | 7 +++++++ board/stm32h7/stm32h7x5_flash.ld | 2 ++ board/sys/sys.h | 2 ++ 4 files changed, 27 insertions(+) diff --git a/board/early_init.h b/board/early_init.h index 3b66262381b..b5c36619524 100644 --- a/board/early_init.h +++ b/board/early_init.h @@ -63,3 +63,19 @@ void early_initialization(void) { jump_to_bootloader(); } } + +// we might hit NMI / HardFault close to brown-out. +// we don't want to infinitely loop, so let's set a logging cookie and reset. +#define NMI_COOKIE 0x4E4D4921U +#define HARDFAULT_COOKIE 0x48464C54U +extern uint32_t fault_cookie; + +void NMI_Handler(void) { + fault_cookie = NMI_COOKIE; + NVIC_SystemReset(); +} + +void HardFault_Handler(void) { + fault_cookie = HARDFAULT_COOKIE; + NVIC_SystemReset(); +} diff --git a/board/main.c b/board/main.c index 01c7e245592..31f65f9ebd3 100644 --- a/board/main.c +++ b/board/main.c @@ -316,6 +316,13 @@ int main(void) { led_set(LED_GREEN, false); led_set(LED_BLUE, false); + if (fault_cookie == NMI_COOKIE) { + fault_occurred(FAULT_NMI_RESET); + } else if (fault_cookie == HARDFAULT_COOKIE) { + fault_occurred(FAULT_HARDFAULT_RESET); + } + fault_cookie = 0U; + print("**** INTERRUPTS ON ****\n"); enable_interrupts(); diff --git a/board/stm32h7/stm32h7x5_flash.ld b/board/stm32h7/stm32h7x5_flash.ld index 2c0c19d0939..ae8800e7de1 100644 --- a/board/stm32h7/stm32h7x5_flash.ld +++ b/board/stm32h7/stm32h7x5_flash.ld @@ -54,6 +54,8 @@ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ enter_bootloader_mode = 0x38001FFC; +fault_cookie = 0x38003FFC; + _estack = 0x20020000; /* end of RAM */ _app_start = 0x08020000; /* Reserve Sector 0(128K) for bootloader */ diff --git a/board/sys/sys.h b/board/sys/sys.h index 0907cf1e109..3a8910cfae4 100644 --- a/board/sys/sys.h +++ b/board/sys/sys.h @@ -45,6 +45,8 @@ extern uint8_t global_critical_depth; #define FAULT_SIREN_MALFUNCTION (1UL << 25) #define FAULT_HEARTBEAT_LOOP_WATCHDOG (1UL << 26) #define FAULT_INTERRUPT_RATE_SOUND_DMA (1UL << 27) +#define FAULT_NMI_RESET (1UL << 28) +#define FAULT_HARDFAULT_RESET (1UL << 29) // Permanent faults #define PERMANENT_FAULTS 0U From 44f45abaf0cce6766c6af85dbe26dc115cb2622e Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Wed, 19 Aug 2026 22:36:02 +0200 Subject: [PATCH 2/3] misra fix --- board/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/board/main.c b/board/main.c index 31f65f9ebd3..b9c17ff63c6 100644 --- a/board/main.c +++ b/board/main.c @@ -318,7 +318,8 @@ int main(void) { if (fault_cookie == NMI_COOKIE) { fault_occurred(FAULT_NMI_RESET); - } else if (fault_cookie == HARDFAULT_COOKIE) { + } + if (fault_cookie == HARDFAULT_COOKIE) { fault_occurred(FAULT_HARDFAULT_RESET); } fault_cookie = 0U; From 125bf58f694803d9406f97b8ead27da73c6991ad Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Wed, 26 Aug 2026 15:45:19 +0200 Subject: [PATCH 3/3] report NMI and hardfault resets in health flags --- board/health.h | 2 ++ board/main.c | 4 ++-- board/main_comms.h | 1 + board/main_declarations.h | 1 + board/main_definitions.h | 1 + board/sys/sys.h | 2 -- python/__init__.py | 4 ++++ 7 files changed, 11 insertions(+), 4 deletions(-) diff --git a/board/health.h b/board/health.h index 24e6a8dfb64..69e2630bf0f 100644 --- a/board/health.h +++ b/board/health.h @@ -7,6 +7,8 @@ #define HEALTH_FLAG_HEARTBEAT_LOST (1U << 4) #define HEALTH_FLAG_SAFETY_RX_CHECKS_INVALID (1U << 5) #define HEALTH_FLAG_SOM_RESET_TRIGGERED (1U << 6) +#define HEALTH_FLAG_NMI_RESET (1U << 7) +#define HEALTH_FLAG_HARDFAULT_RESET ((uint16_t)1U << 8) struct __attribute__((packed)) health_t { uint32_t uptime_pkt; diff --git a/board/main.c b/board/main.c index b9c17ff63c6..da7e8e3f215 100644 --- a/board/main.c +++ b/board/main.c @@ -317,10 +317,10 @@ int main(void) { led_set(LED_BLUE, false); if (fault_cookie == NMI_COOKIE) { - fault_occurred(FAULT_NMI_RESET); + reset_flags |= HEALTH_FLAG_NMI_RESET; } if (fault_cookie == HARDFAULT_COOKIE) { - fault_occurred(FAULT_HARDFAULT_RESET); + reset_flags |= HEALTH_FLAG_HARDFAULT_RESET; } fault_cookie = 0U; diff --git a/board/main_comms.h b/board/main_comms.h index 1f02bd0afb9..8c75ed47721 100644 --- a/board/main_comms.h +++ b/board/main_comms.h @@ -22,6 +22,7 @@ static int get_health_pkt(void *dat) { health->flags_pkt |= heartbeat_lost ? HEALTH_FLAG_HEARTBEAT_LOST : 0U; health->flags_pkt |= safety_rx_checks_invalid ? HEALTH_FLAG_SAFETY_RX_CHECKS_INVALID : 0U; health->flags_pkt |= bootkick_reset_triggered ? HEALTH_FLAG_SOM_RESET_TRIGGERED : 0U; + health->flags_pkt |= reset_flags; health->safety_tx_blocked_pkt = safety_tx_blocked; health->safety_rx_invalid_pkt = safety_rx_invalid; diff --git a/board/main_declarations.h b/board/main_declarations.h index af2a299374d..920e5ae66bb 100644 --- a/board/main_declarations.h +++ b/board/main_declarations.h @@ -12,6 +12,7 @@ void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage); extern uint8_t hw_type; extern board *current_board; extern uint32_t uptime_cnt; +extern uint16_t reset_flags; // heartbeat state extern uint32_t heartbeat_counter; diff --git a/board/main_definitions.h b/board/main_definitions.h index 3f408cd58ef..6996c8939c9 100644 --- a/board/main_definitions.h +++ b/board/main_definitions.h @@ -6,6 +6,7 @@ uint8_t hw_type = 0; board *current_board; uint32_t uptime_cnt = 0; +uint16_t reset_flags = 0U; // heartbeat state uint32_t heartbeat_counter = 0; diff --git a/board/sys/sys.h b/board/sys/sys.h index 3a8910cfae4..0907cf1e109 100644 --- a/board/sys/sys.h +++ b/board/sys/sys.h @@ -45,8 +45,6 @@ extern uint8_t global_critical_depth; #define FAULT_SIREN_MALFUNCTION (1UL << 25) #define FAULT_HEARTBEAT_LOOP_WATCHDOG (1UL << 26) #define FAULT_INTERRUPT_RATE_SOUND_DMA (1UL << 27) -#define FAULT_NMI_RESET (1UL << 28) -#define FAULT_HARDFAULT_RESET (1UL << 29) // Permanent faults #define PERMANENT_FAULTS 0U diff --git a/python/__init__.py b/python/__init__.py index 1a6ab7d994a..be86a233932 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -146,6 +146,8 @@ class Panda: HEALTH_FLAG_HEARTBEAT_LOST = 1 << 4 HEALTH_FLAG_SAFETY_RX_CHECKS_INVALID = 1 << 5 HEALTH_FLAG_SOM_RESET_TRIGGERED = 1 << 6 + HEALTH_FLAG_NMI_RESET = 1 << 7 + HEALTH_FLAG_HARDFAULT_RESET = 1 << 8 H7_DEVICES = [HW_TYPE_RED_PANDA, HW_TYPE_TRES, HW_TYPE_CUATRO, HW_TYPE_BODY] SUPPORTED_DEVICES = H7_DEVICES @@ -549,6 +551,8 @@ def health(self): "sbu1_voltage_mV": a[17], "sbu2_voltage_mV": a[18], "som_reset_triggered": bool(flags & self.HEALTH_FLAG_SOM_RESET_TRIGGERED), + "nmi_reset": bool(flags & self.HEALTH_FLAG_NMI_RESET), + "hardfault_reset": bool(flags & self.HEALTH_FLAG_HARDFAULT_RESET), "sound_output_level": a[19], "temperature": a[20] - 40.0, }