From 7113e41550e3a1f18f2b36102da0489a35d211ef Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 10 Mar 2026 13:38:12 -1000 Subject: [PATCH] Address review feedback: RP2350 compat, noreturn, addr2line hint - Add #if defined(PICO_RP2350) for SRAM end (520KB vs 264KB) so stack scanning works on both RP2040 and RP2350 - Widen flash range check to 4MB for RP2350 - Add __attribute__((noreturn)) to hard_fault_handler_c - Mark exc_return parameter as unused via /*exc_return*/ - Build addr2line hint line with all addresses (PC, LR, and BT*) so users can copy-paste a single command for full decode --- esphome/components/rp2040/crash_handler.cpp | 35 +++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/esphome/components/rp2040/crash_handler.cpp b/esphome/components/rp2040/crash_handler.cpp index 54548765764..65966d415cd 100644 --- a/esphome/components/rp2040/crash_handler.cpp +++ b/esphome/components/rp2040/crash_handler.cpp @@ -3,6 +3,7 @@ #include "crash_handler.h" #include "esphome/core/log.h" +#include #include #include @@ -24,13 +25,18 @@ static constexpr uint32_t CRASH_MAGIC = 0xDEADBEEF; // [4..7] = up to 4 additional code addresses found by scanning the stack // (return addresses from callers, giving a deeper backtrace) -// RP2040 flash is mapped at 0x10000000 with up to 16MB address space. -// We use 2MB as the upper bound — large enough for any typical ESPHome firmware -// while keeping false positives low during stack scanning. Wider ranges would -// match more stale data on the stack that happens to look like code addresses. +// Flash is mapped at 0x10000000. RP2040 supports up to 16MB, RP2350 up to 32MB. +// We use a conservative upper bound to keep false positives low during stack scanning. +// Wider ranges would match more stale data on the stack that happens to look like code addresses. +#if defined(PICO_RP2350) +static constexpr uint32_t FLASH_END = 0x10400000; // 4MB — RP2350 typical max +#else +static constexpr uint32_t FLASH_END = 0x10200000; // 2MB — RP2040 typical max +#endif + static inline bool is_code_addr(uint32_t val) { uint32_t cleared = val & ~1u; // Clear Thumb bit - return cleared >= 0x10000000 && cleared < 0x10200000; + return cleared >= 0x10000000 && cleared < FLASH_END; } static constexpr size_t MAX_BACKTRACE = 4; @@ -85,7 +91,14 @@ void crash_handler_log() { for (uint8_t i = 0; i < s_crash_data.backtrace_count; i++) { ESP_LOGE(TAG, " BT%d: 0x%08X (stack backtrace)", i, s_crash_data.backtrace[i]); } - ESP_LOGE(TAG, "Use addr2line -e firmware.elf 0x%08X 0x%08X to decode", s_crash_data.pc, s_crash_data.lr); + // Build addr2line hint with all captured addresses for easy copy-paste + char hint[160]; + int pos = snprintf(hint, sizeof(hint), "Use: addr2line -pfiaC -e firmware.elf 0x%08" PRIX32 " 0x%08" PRIX32, + s_crash_data.pc, s_crash_data.lr); + for (uint8_t i = 0; i < s_crash_data.backtrace_count && pos < (int) sizeof(hint) - 12; i++) { + pos += snprintf(hint + pos, sizeof(hint) - pos, " 0x%08" PRIX32, s_crash_data.backtrace[i]); + } + ESP_LOGE(TAG, "%s", hint); } } // namespace esphome::rp2040 @@ -98,7 +111,7 @@ void crash_handler_log() { // (which survive watchdog reboot), then trigger a reboot. // C handler called from the asm wrapper with the exception frame pointer. -static void __attribute__((used)) hard_fault_handler_c(uint32_t *frame, uint32_t exc_return) { +static void __attribute__((used, noreturn)) hard_fault_handler_c(uint32_t *frame, uint32_t /*exc_return*/) { // watchdog_reboot() overwrites scratch[4]-[7], so we must call it first // then write ALL our data after. The 10ms timeout gives us plenty of time. watchdog_reboot(0, 0, 10); @@ -113,8 +126,12 @@ static void __attribute__((used)) hard_fault_handler_c(uint32_t *frame, uint32_t // The exception frame is 8 words (32 bytes) at 'frame'. The pre-fault // stack starts at frame+8. Walk up to 64 words looking for return addresses. uint32_t *scan_start = frame + 8; // Past exception frame - // RP2040 RAM ends at 0x20042000 (264KB SRAM) - uint32_t *stack_top = (uint32_t *) 0x20042000; + // SRAM end address differs by chip variant +#if defined(PICO_RP2350) + uint32_t *stack_top = (uint32_t *) 0x20082000; // RP2350: 520KB SRAM +#else + uint32_t *stack_top = (uint32_t *) 0x20042000; // RP2040: 264KB SRAM +#endif uint32_t bt_count = 0; for (uint32_t *p = scan_start; p < stack_top && p < scan_start + 64 && bt_count < MAX_BACKTRACE; p++) {