From d7e708b9bc0c89e955b10f64929a868764df56b8 Mon Sep 17 00:00:00 2001 From: cpq Date: Fri, 2 Oct 2026 12:17:50 +0100 Subject: [PATCH] Move crash handler to src/health.c --- mongoose.c | 98 ++++++++++++++----- mongoose.h | 48 ++------- src/config.h | 4 + src/health.c | 68 +++++++++++++ src/health.h | 44 +-------- src/mdash.c | 31 +----- .../cubemx/Core/Inc/stm32h7xx_it.h | 4 - .../nucleo-h723zg/cubemx/Core/Src/main.c | 6 +- .../cubemx/Core/Src/stm32h7xx_it.c | 60 ------------ .../nucleo-h723zg/cubemx/STM32H723XG_FLASH.ld | 6 +- .../nucleo-h723zg/cubemx/nucleo-h723zg.ioc | 14 +-- tutorials/stm32/nucleo-h723zg/minimal/link.ld | 2 +- tutorials/stm32/nucleo-h723zg/minimal/main.c | 54 +--------- .../nucleo-h723zg/minimal/mongoose_config.h | 7 +- 14 files changed, 179 insertions(+), 267 deletions(-) diff --git a/mongoose.c b/mongoose.c index 91bb05a8..a0a80559 100644 --- a/mongoose.c +++ b/mongoose.c @@ -3485,6 +3485,73 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read, // MG_HEALTH_RAM and the .mg_health region in the linker script struct mg_health mg_health_record MG_HEALTH_RAM; +// STM32 H723 support. Needs the _estack symbol from the linker script +#if MG_ENABLE_HEALTH && defined(STM32H723xx_H) +// Fault handler body. Runs in exception context: no printf, no malloc, no +// blocking calls. Copies the unwind registers and raw stack into the health +// record, then resets. +// "used" keeps the linker from garbage-collecting this section: the only +// reference is the "b fault_c" branch in the naked handler below +__attribute__((used, noinline)) static void fault_c(uint32_t *frame, + uint32_t exc_return) { + extern uint32_t _estack; + uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18); + uint32_t *r = mg_health_record.data; + + if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding + r[0] = (uint32_t) (uintptr_t) stack; // SP + r[1] = frame[5]; // LR + r[2] = frame[6]; // PC + for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - 3; i++) { // Raw stack + r[3 + i] = stack + i < &_estack ? stack[i] : 0; + } + mg_health_record.cpuid = SCB->CPUID; + mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT; + NVIC_SystemReset(); +} + +// Common fault entry. EXC_RETURN bit 2 tells which stack was in use: +// 0 = MSP, 1 = PSP. Load the faulting SP into r0 and hand it to fault_c() +__attribute__((naked)) void HardFault_Handler(void) { + __asm volatile( + "mov r1, lr\n\t" + "tst lr, #4\n\t" + "ite eq\n\t" + "mrseq r0, msp\n\t" + "mrsne r0, psp\n\t" + "b fault_c\n\t"); +} + +// Route the other fault types through the same entry point +void MemManage_Handler(void) __attribute__((alias("HardFault_Handler"))); +void BusFault_Handler(void) __attribute__((alias("HardFault_Handler"))); +void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); +#endif + +static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { + size_t n = va_arg(*ap, size_t); + uint32_t *p = va_arg(*ap, uint32_t *); + size_t len = 0; + while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++); + return len; +} + +size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) { + size_t len = 0; + if (mg_health_valid() && + mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) { + size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); + len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", // + MG_ESC("version"), mg_health_record.magic[3], // + MG_ESC("cpuid"), mg_health_record.cpuid, // + MG_ESC("data"), print_uint32, n, mg_health_record.data); + } else { + len += mg_xprintf(fn, arg, "{}"); + } + (void) ap; + return len; +} + #ifdef MG_ENABLE_LINES #line 1 "src/http.c" #endif @@ -7031,35 +7098,14 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) { (void) c; } -static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { - size_t n = va_arg(*ap, size_t); - uint32_t *p = va_arg(*ap, uint32_t *); - size_t len = 0; - while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++); - return len; -} - -static size_t print_crash(void (*fn)(char, void *), void *arg, va_list *ap) { - size_t len = 0; - if (mg_health_valid()) { - size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); - len += mg_xprintf(fn, arg, ",%m:{%m:%hhu,%m:%u,%m:[%M]}", MG_ESC("crash"), - MG_ESC("version"), mg_health_record.magic[3], // - MG_ESC("cpuid"), mg_health_record.cpuid, // - MG_ESC("data"), print_uint32, n, mg_health_record.data); - } - (void) ap; - return len; -} - static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) { const char *reset_reason = mg_health_reason_str(mg_health_reason()); - mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\"%M}", // + mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:%M}", // MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), // - MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), // - MG_ESC("reboot_reason"), MG_ESC(reset_reason), // - MG_ESC("arch"), MG_ARCH, // - print_crash); + MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), // + MG_ESC("reboot_reason"), MG_ESC(reset_reason), // + MG_ESC("arch"), MG_ARCH, // + MG_ESC("crash"), mg_print_crash_record); } static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) { diff --git a/mongoose.h b/mongoose.h index 636f6605..c2aeae60 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1490,6 +1490,10 @@ struct timeval { #define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN #endif +#ifndef MG_ENABLE_HEALTH +#define MG_ENABLE_HEALTH 0 +#endif + @@ -4914,30 +4918,7 @@ void mg_ota_device_id(char *buf, size_t len); // directly. void mg_ota_poll(struct mg_mgr *); // Device health monitoring -// -// Keeps a small record in a RAM region that survives a warm reset, so a device -// can tell why it restarted. The record holds a boot counter, the uptime this -// boot has reached, and the reset reason reported by the hardware. A crash -// loop, a hang loop and a marginal power supply each leave a different mark. -// -// Put MG_HEALTH_INIT() in main(), right after clock and memory init: -// -// ```c -// int main(void) { -// hal_clock_init(); -// MG_HEALTH_INIT(); -// ... -// } -// ``` -// -// The record must live in RAM that startup code neither copies nor zeroes. -// See tutorials/stm32/nucleo-h723zg/minimal/link.ld for the .mg_health region, -// and tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h for the -// reset-reason hooks. -// -// On Cortex-M, a fault handler can record the crash: it sets reset_reason to -// MG_HEALTH_RESET_FAULT and walks the stack into backtrace[]. See -// tutorials/stm32/nucleo-h723zg/minimal/main.c for a sample HardFault_Handler. + @@ -5016,27 +4997,10 @@ static inline void mg_health_init(void) { memset(&mg_health_record, 0, sizeof(mg_health_record)); memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic)); } - // h->prev_uptime = h->uptime; - // h->prev_reason = h->reason; - // h->counter++; - // h->uptime = 0; - // h->reason = mg_health_decode_reason(); - // MG_HEALTH_RESET_CLEAR(); } -// // Record that the main loop is still alive. Called from mg_mgr_poll(), so a -// // hung application leaves uptime frozen at the hang -// extern uint64_t mg_health_next_ms; // Defined in health.c - -// static inline void mg_health_uptime(uint64_t now_ms) { -// struct mg_health *h = mg_health_get(); -// if (now_ms >= mg_health_next_ms && mg_health_valid()) { -// mg_health_next_ms = now_ms + 1000; -// h->uptime++; -// } -// } - #define MG_HEALTH_INIT() mg_health_init() +extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *); // Cloud connector API to https://mdash.net - device management cloud diff --git a/src/config.h b/src/config.h index 79083c3a..1944fc08 100644 --- a/src/config.h +++ b/src/config.h @@ -226,3 +226,7 @@ #ifndef MG_ENABLE_CHACHA20 #define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN #endif + +#ifndef MG_ENABLE_HEALTH +#define MG_ENABLE_HEALTH 0 +#endif diff --git a/src/health.c b/src/health.c index bc82c602..b4a2eebc 100644 --- a/src/health.c +++ b/src/health.c @@ -1,5 +1,73 @@ +#include "config.h" #include "health.h" // The one health record. Lives in RAM that survives a warm reset, see // MG_HEALTH_RAM and the .mg_health region in the linker script struct mg_health mg_health_record MG_HEALTH_RAM; + +// STM32 H723 support. Needs the _estack symbol from the linker script +#if MG_ENABLE_HEALTH && defined(STM32H723xx_H) +// Fault handler body. Runs in exception context: no printf, no malloc, no +// blocking calls. Copies the unwind registers and raw stack into the health +// record, then resets. +// "used" keeps the linker from garbage-collecting this section: the only +// reference is the "b fault_c" branch in the naked handler below +__attribute__((used, noinline)) static void fault_c(uint32_t *frame, + uint32_t exc_return) { + extern uint32_t _estack; + uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18); + uint32_t *r = mg_health_record.data; + + if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding + r[0] = (uint32_t) (uintptr_t) stack; // SP + r[1] = frame[5]; // LR + r[2] = frame[6]; // PC + for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - 3; i++) { // Raw stack + r[3 + i] = stack + i < &_estack ? stack[i] : 0; + } + mg_health_record.cpuid = SCB->CPUID; + mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT; + NVIC_SystemReset(); +} + +// Common fault entry. EXC_RETURN bit 2 tells which stack was in use: +// 0 = MSP, 1 = PSP. Load the faulting SP into r0 and hand it to fault_c() +__attribute__((naked)) void HardFault_Handler(void) { + __asm volatile( + "mov r1, lr\n\t" + "tst lr, #4\n\t" + "ite eq\n\t" + "mrseq r0, msp\n\t" + "mrsne r0, psp\n\t" + "b fault_c\n\t"); +} + +// Route the other fault types through the same entry point +void MemManage_Handler(void) __attribute__((alias("HardFault_Handler"))); +void BusFault_Handler(void) __attribute__((alias("HardFault_Handler"))); +void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); +#endif + +static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { + size_t n = va_arg(*ap, size_t); + uint32_t *p = va_arg(*ap, uint32_t *); + size_t len = 0; + while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++); + return len; +} + +size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) { + size_t len = 0; + if (mg_health_valid() && + mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) { + size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); + len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", // + MG_ESC("version"), mg_health_record.magic[3], // + MG_ESC("cpuid"), mg_health_record.cpuid, // + MG_ESC("data"), print_uint32, n, mg_health_record.data); + } else { + len += mg_xprintf(fn, arg, "{}"); + } + (void) ap; + return len; +} diff --git a/src/health.h b/src/health.h index d95116b1..be662bdc 100644 --- a/src/health.h +++ b/src/health.h @@ -1,32 +1,9 @@ // Device health monitoring -// -// Keeps a small record in a RAM region that survives a warm reset, so a device -// can tell why it restarted. The record holds a boot counter, the uptime this -// boot has reached, and the reset reason reported by the hardware. A crash -// loop, a hang loop and a marginal power supply each leave a different mark. -// -// Put MG_HEALTH_INIT() in main(), right after clock and memory init: -// -// ```c -// int main(void) { -// hal_clock_init(); -// MG_HEALTH_INIT(); -// ... -// } -// ``` -// -// The record must live in RAM that startup code neither copies nor zeroes. -// See tutorials/stm32/nucleo-h723zg/minimal/link.ld for the .mg_health region, -// and tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h for the -// reset-reason hooks. -// -// On Cortex-M, a fault handler can record the crash: it sets reset_reason to -// MG_HEALTH_RESET_FAULT and walks the stack into backtrace[]. See -// tutorials/stm32/nucleo-h723zg/minimal/main.c for a sample HardFault_Handler. #pragma once #include "arch.h" +#include "printf.h" #ifndef MG_HEALTH_MAGIC #define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version @@ -101,24 +78,7 @@ static inline void mg_health_init(void) { memset(&mg_health_record, 0, sizeof(mg_health_record)); memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic)); } - // h->prev_uptime = h->uptime; - // h->prev_reason = h->reason; - // h->counter++; - // h->uptime = 0; - // h->reason = mg_health_decode_reason(); - // MG_HEALTH_RESET_CLEAR(); } -// // Record that the main loop is still alive. Called from mg_mgr_poll(), so a -// // hung application leaves uptime frozen at the hang -// extern uint64_t mg_health_next_ms; // Defined in health.c - -// static inline void mg_health_uptime(uint64_t now_ms) { -// struct mg_health *h = mg_health_get(); -// if (now_ms >= mg_health_next_ms && mg_health_valid()) { -// mg_health_next_ms = now_ms + 1000; -// h->uptime++; -// } -// } - #define MG_HEALTH_INIT() mg_health_init() +extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *); diff --git a/src/mdash.c b/src/mdash.c index c409e536..b082d616 100644 --- a/src/mdash.c +++ b/src/mdash.c @@ -95,35 +95,14 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) { (void) c; } -static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) { - size_t n = va_arg(*ap, size_t); - uint32_t *p = va_arg(*ap, uint32_t *); - size_t len = 0; - while (n--) len += mg_xprintf(fn, arg, "%s%lu", len == 0 ? "" : ",", *p++); - return len; -} - -static size_t print_crash(void (*fn)(char, void *), void *arg, va_list *ap) { - size_t len = 0; - if (mg_health_valid()) { - size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]); - len += mg_xprintf(fn, arg, ",%m:{%m:%hhu,%m:%u,%m:[%M]}", MG_ESC("crash"), - MG_ESC("version"), mg_health_record.magic[3], // - MG_ESC("cpuid"), mg_health_record.cpuid, // - MG_ESC("data"), print_uint32, n, mg_health_record.data); - } - (void) ap; - return len; -} - static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) { const char *reset_reason = mg_health_reason_str(mg_health_reason()); - mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\"%M}", // + mg_rpc_ok(r, "{%m:%m,%m:%llu,%m:%m,%m:\"mws.%d\",%m:%M}", // MG_ESC("fw_version"), MG_ESC(MG_FIRMWARE_VERSION), // - MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), // - MG_ESC("reboot_reason"), MG_ESC(reset_reason), // - MG_ESC("arch"), MG_ARCH, // - print_crash); + MG_ESC("uptime"), (uint64_t) (mg_millis() / 1000), // + MG_ESC("reboot_reason"), MG_ESC(reset_reason), // + MG_ESC("arch"), MG_ARCH, // + MG_ESC("crash"), mg_print_crash_record); } static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) { diff --git a/tutorials/stm32/nucleo-h723zg/cubemx/Core/Inc/stm32h7xx_it.h b/tutorials/stm32/nucleo-h723zg/cubemx/Core/Inc/stm32h7xx_it.h index 9fc4283d..b52ec173 100644 --- a/tutorials/stm32/nucleo-h723zg/cubemx/Core/Inc/stm32h7xx_it.h +++ b/tutorials/stm32/nucleo-h723zg/cubemx/Core/Inc/stm32h7xx_it.h @@ -47,10 +47,6 @@ extern "C" { /* Exported functions prototypes ---------------------------------------------*/ void NMI_Handler(void); -void HardFault_Handler(void); -void MemManage_Handler(void); -void BusFault_Handler(void); -void UsageFault_Handler(void); void SVC_Handler(void); void DebugMon_Handler(void); void PendSV_Handler(void); diff --git a/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/main.c b/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/main.c index 0b3ecfe6..9498d2b8 100644 --- a/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/main.c +++ b/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/main.c @@ -18,7 +18,6 @@ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" -#include "stm32h7xx_hal.h" #include "string.h" /* Private includes ----------------------------------------------------------*/ @@ -94,6 +93,9 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick()); + } else if (mg_match(hm->uri, mg_str("/api/kill"), NULL)) { + SCB->SHCSR &= ~SCB_SHCSR_USGFAULTENA_Msk; + __asm volatile("udf #0"); } else { mg_http_reply(c, 200, "", "Hi from Mongoose, tick %lu\n", HAL_GetTick()); } @@ -109,7 +111,7 @@ int main(void) { /* USER CODE BEGIN 1 */ - + MG_HEALTH_INIT(); /* USER CODE END 1 */ /* MPU Configuration--------------------------------------------------------*/ diff --git a/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/stm32h7xx_it.c b/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/stm32h7xx_it.c index 430a6ace..bc85222e 100644 --- a/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/stm32h7xx_it.c +++ b/tutorials/stm32/nucleo-h723zg/cubemx/Core/Src/stm32h7xx_it.c @@ -78,66 +78,6 @@ void NMI_Handler(void) /* USER CODE END NonMaskableInt_IRQn 1 */ } -/** - * @brief This function handles Hard fault interrupt. - */ -void HardFault_Handler(void) -{ - /* USER CODE BEGIN HardFault_IRQn 0 */ - - /* USER CODE END HardFault_IRQn 0 */ - while (1) - { - /* USER CODE BEGIN W1_HardFault_IRQn 0 */ - /* USER CODE END W1_HardFault_IRQn 0 */ - } -} - -/** - * @brief This function handles Memory management fault. - */ -void MemManage_Handler(void) -{ - /* USER CODE BEGIN MemoryManagement_IRQn 0 */ - - /* USER CODE END MemoryManagement_IRQn 0 */ - while (1) - { - /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */ - /* USER CODE END W1_MemoryManagement_IRQn 0 */ - } -} - -/** - * @brief This function handles Pre-fetch fault, memory access fault. - */ -void BusFault_Handler(void) -{ - /* USER CODE BEGIN BusFault_IRQn 0 */ - - /* USER CODE END BusFault_IRQn 0 */ - while (1) - { - /* USER CODE BEGIN W1_BusFault_IRQn 0 */ - /* USER CODE END W1_BusFault_IRQn 0 */ - } -} - -/** - * @brief This function handles Undefined instruction or illegal state. - */ -void UsageFault_Handler(void) -{ - /* USER CODE BEGIN UsageFault_IRQn 0 */ - - /* USER CODE END UsageFault_IRQn 0 */ - while (1) - { - /* USER CODE BEGIN W1_UsageFault_IRQn 0 */ - /* USER CODE END W1_UsageFault_IRQn 0 */ - } -} - /** * @brief This function handles System service call via SWI instruction. */ diff --git a/tutorials/stm32/nucleo-h723zg/cubemx/STM32H723XG_FLASH.ld b/tutorials/stm32/nucleo-h723zg/cubemx/STM32H723XG_FLASH.ld index ea6fa2ee..b37d2909 100644 --- a/tutorials/stm32/nucleo-h723zg/cubemx/STM32H723XG_FLASH.ld +++ b/tutorials/stm32/nucleo-h723zg/cubemx/STM32H723XG_FLASH.ld @@ -154,9 +154,9 @@ SECTIONS /* used by the startup to initialize data */ _sidata = LOADADDR(.data); - - /* mongoose Ethernet driver */ - .eth_ram : { *(.eth_ram .eth_ram*) } > RAM_D2 AT > FLASH + + /* mongoose Ethernet driver */ + .eth_ram : { *(.eth_ram .eth_ram*) } > RAM_D2 AT > FLASH /* Initialized data sections goes into RAM, load LMA copy after code */ .data : diff --git a/tutorials/stm32/nucleo-h723zg/cubemx/nucleo-h723zg.ioc b/tutorials/stm32/nucleo-h723zg/cubemx/nucleo-h723zg.ioc index 7bddbf21..f5207316 100644 --- a/tutorials/stm32/nucleo-h723zg/cubemx/nucleo-h723zg.ioc +++ b/tutorials/stm32/nucleo-h723zg/cubemx/nucleo-h723zg.ioc @@ -42,23 +42,23 @@ Mcu.Pin6=PB0 Mcu.Pin7=PB13 Mcu.Pin8=PB14 Mcu.Pin9=PD8 -Mcu.PinsNb=19 +Mcu.PinsNb=17 Mcu.ThirdPartyNb=0 Mcu.UserConstants= Mcu.UserName=STM32H723ZGTx MxCube.Version=6.17.0 MxDb.Version=DB.6.0.170 -NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.ForceEnableDMAVector=true -NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false -NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false +NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4 NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false -NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false PA1.Mode=RMII PA1.Signal=ETH_REF_CLK PA2.Mode=RMII @@ -114,8 +114,8 @@ ProjectManager.MainLocation=Core/Src ProjectManager.NoMain=false ProjectManager.PreviousToolchain= ProjectManager.ProjectBuild=false -ProjectManager.ProjectFileName=h723.ioc -ProjectManager.ProjectName=h723 +ProjectManager.ProjectFileName=nucleo-h723zg.ioc +ProjectManager.ProjectName=nucleo-h723zg ProjectManager.ProjectStructure= ProjectManager.RegisterCallBack= ProjectManager.StackSize=0x2000 diff --git a/tutorials/stm32/nucleo-h723zg/minimal/link.ld b/tutorials/stm32/nucleo-h723zg/minimal/link.ld index c4ad5b39..617b312a 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/link.ld +++ b/tutorials/stm32/nucleo-h723zg/minimal/link.ld @@ -28,5 +28,5 @@ SECTIONS { _end = .; /* Retained health record. Startup code must neither copy nor clear it. */ - .mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > bkpsram + .mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > ram_d3 } diff --git a/tutorials/stm32/nucleo-h723zg/minimal/main.c b/tutorials/stm32/nucleo-h723zg/minimal/main.c index 2b1098c4..83813d13 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/main.c +++ b/tutorials/stm32/nucleo-h723zg/minimal/main.c @@ -17,19 +17,10 @@ #define LED2 PIN('E', 1) #define LED3 PIN('B', 14) -enum { CRASH_SP, CRASH_LR, CRASH_PC, CRASH_STACK }; - static void log_fn(char ch, void *param) { hal_uart_write_buf(param, &ch, 1); } -static void hal_storage_init(void) { - RCC->AHB4ENR |= RCC_AHB4ENR_BKPRAMEN; - (void) RCC->AHB4ENR; - PWR->CR2 |= PWR_CR2_BREN; - while ((PWR->CR2 & PWR_CR2_BRRDY) == 0) (void) 0; -} - static void blink_task(void) { static uint64_t blink_timer = 0; if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) { @@ -37,46 +28,6 @@ static void blink_task(void) { } } -// Fault handler body. Runs in exception context: no printf, no malloc, no -// blocking calls. Copies the unwind registers and raw stack into the health -// record, then resets. -// "used" keeps the linker from garbage-collecting this section: the only -// reference is the "b fault_c" branch in the naked handler below -__attribute__((used, noinline)) static void fault_c(uint32_t *frame, - uint32_t exc_return) { - extern uint32_t _estack; - uint32_t *stack = frame + 8 + ((exc_return & (1U << 4)) ? 0 : 18); - uint32_t *r = mg_health_record.data; - - if (frame[7] & (1U << 9)) stack++; // Eight-byte stack alignment padding - r[CRASH_SP] = (uint32_t) (uintptr_t) stack; - r[CRASH_LR] = frame[5]; - r[CRASH_PC] = frame[6]; - for (size_t i = 0; i < MG_HEALTH_DATA_SIZE - CRASH_STACK; i++) { - r[CRASH_STACK + i] = stack + i < &_estack ? stack[i] : 0; - } - mg_health_record.cpuid = SCB->CPUID; - mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT; - NVIC_SystemReset(); -} - -// Common fault entry. EXC_RETURN bit 2 tells which stack was in use: -// 0 = MSP, 1 = PSP. Load the faulting SP into r0 and hand it to fault_c() -__attribute__((naked)) void HardFault_Handler(void) { - __asm volatile( - "mov r1, lr\n\t" - "tst lr, #4\n\t" - "ite eq\n\t" - "mrseq r0, msp\n\t" - "mrsne r0, psp\n\t" - "b fault_c\n\t"); -} - -// Route the other fault types through the same entry point -void MemManage_Handler(void) __attribute__((alias("HardFault_Handler"))); -void BusFault_Handler(void) __attribute__((alias("HardFault_Handler"))); -void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler"))); - uint64_t mg_millis(void) { return hal_get_tick(); } @@ -105,9 +56,9 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { } int main(void) { + MG_HEALTH_INIT(); + hal_clock_init(); - hal_storage_init(); - MG_HEALTH_INIT(); // Must be called after clock init hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200); mg_log_set_fn(log_fn, UART_DEBUG); hal_rng_init(); @@ -117,6 +68,7 @@ int main(void) { hal_gpio_output(LED3); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); + MG_INFO(("Crash record: %M", mg_print_crash_record)); struct mg_mgr mgr; mg_mgr_init(&mgr); diff --git a/tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h b/tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h index dcbeee91..7cd6beba 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h +++ b/tutorials/stm32/nucleo-h723zg/minimal/mongoose_config.h @@ -27,9 +27,10 @@ } while (0) // Crash report support. Health record lives in the .mg_health region, see link.ld +#define MG_ENABLE_HEALTH 1 #define MG_HEALTH_RAM __attribute__((section(".mg_health"))) -#define MG_HEALTH_DATA_SIZE 1000 // fill the 4KB backup SRAM +#define MG_HEALTH_DATA_SIZE 1000 // mdash.net device management service support -#define MG_ENABLE_MDASH 1 -#define MG_MDASH_KEY "u8eyWvBmkc6gw9991z1SQ8Lg" // Set this - mDash device token +#define MG_ENABLE_MDASH 0 +#define MG_MDASH_KEY "..." // Set this - mDash device token