From f953c98659d3c9eb0d3f2a3d331710f01f966d78 Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Tue, 4 Feb 2020 15:37:13 -0800 Subject: [PATCH] Improve ASSERT error handling and reporting * Motors are now disabled after an assert. This is in particular important if the watchdog is disabled. * ASSERT information is now stored for FreeRTOS related issues (out of memory, stack overflow). Note that the stack overflow detection has to be enabled manually for release builds. * HardFault asserts now include more detailed information, e.g., the program counter. One can use GDB to find the source code line that triggered a hard fault. --- src/drivers/interface/motors.h | 2 + src/drivers/src/motors.c | 7 +++ src/drivers/src/nvic.c | 22 ++++++-- src/hal/src/freeRTOSdebug.c | 17 +++--- src/utils/interface/cfassert.h | 22 +++++++- src/utils/src/cfassert.c | 95 ++++++++++++++++++++++++++++++---- 6 files changed, 144 insertions(+), 21 deletions(-) diff --git a/src/drivers/interface/motors.h b/src/drivers/interface/motors.h index d3f433d6d5..9b4965e54a 100644 --- a/src/drivers/interface/motors.h +++ b/src/drivers/interface/motors.h @@ -229,6 +229,8 @@ void motorsDeInit(const MotorPerifDef** motorMapSelect); */ bool motorsTest(void); +void motorsDisable(void); + /** * Set the PWM ratio of the motor 'id' */ diff --git a/src/drivers/src/motors.c b/src/drivers/src/motors.c index e685852c5d..fa9fd1e8fa 100644 --- a/src/drivers/src/motors.c +++ b/src/drivers/src/motors.c @@ -216,6 +216,13 @@ bool motorsTest(void) return isInit; } +void motorsDisable(void) +{ + for (int id = 0; id < NBR_OF_MOTORS; ++id) { + motorsSetRatio(id, 0); + } +} + // Ithrust is thrust mapped for 65536 <==> 60 grams void motorsSetRatio(uint32_t id, uint16_t ithrust) { diff --git a/src/drivers/src/nvic.c b/src/drivers/src/nvic.c index 925f80fae2..02ed701b88 100644 --- a/src/drivers/src/nvic.c +++ b/src/drivers/src/nvic.c @@ -138,8 +138,17 @@ void DONT_DISCARD printHardFault(uint32_t* hardfaultArgs) ledClearAll(); ledSet(ERR_LED1, 1); ledSet(ERR_LED2, 1); - - storeAssertSnapshotData(__FILE__, __LINE__); + motorsDisable(); + + storeAssertHardfaultData( + stacked_r0, + stacked_r1, + stacked_r2, + stacked_r3, + stacked_r12, + stacked_lr, + stacked_pc, + stacked_psr); while (1) {} } @@ -157,8 +166,9 @@ void DONT_DISCARD MemManage_Handler(void) ledClearAll(); ledSet(ERR_LED1, 1); ledSet(ERR_LED2, 1); + motorsDisable(); - storeAssertSnapshotData(__FILE__, __LINE__); + storeAssertTextData("MemManage"); while (1) {} } @@ -177,8 +187,9 @@ void DONT_DISCARD BusFault_Handler(void) ledClearAll(); ledSet(ERR_LED1, 1); ledSet(ERR_LED2, 1); + motorsDisable(); - storeAssertSnapshotData(__FILE__, __LINE__); + storeAssertTextData("BusFault"); while (1) {} } @@ -197,8 +208,9 @@ void DONT_DISCARD UsageFault_Handler(void) ledClearAll(); ledSet(ERR_LED1, 1); ledSet(ERR_LED2, 1); + motorsDisable(); - storeAssertSnapshotData(__FILE__, __LINE__); + storeAssertTextData("UsageFault"); while (1) {} } diff --git a/src/hal/src/freeRTOSdebug.c b/src/hal/src/freeRTOSdebug.c index dff8c7cdd3..6882491578 100644 --- a/src/hal/src/freeRTOSdebug.c +++ b/src/hal/src/freeRTOSdebug.c @@ -33,25 +33,30 @@ #include "debug.h" #include "nvicconf.h" #include "led.h" +#include "motors.h" uint32_t traceTickCount; void vApplicationMallocFailedHook( void ) { - portDISABLE_INTERRUPTS(); - DEBUG_PRINT("\nMalloc failed!\n"); - ledSet(ERR_LED1, 1); - ledSet(ERR_LED2, 1); - while(1); + portDISABLE_INTERRUPTS(); + DEBUG_PRINT("\nMalloc failed!\n"); + ledSet(ERR_LED1, 1); + ledSet(ERR_LED2, 1); + motorsDisable(); + storeAssertTextData("Malloc failed"); + while(1); } -#if (configCHECK_FOR_STACK_OVERFLOW == 1) +#if (configCHECK_FOR_STACK_OVERFLOW > 0) void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName) { portDISABLE_INTERRUPTS(); DEBUG_PRINT("\nStack overflow!\n"); ledSet(ERR_LED1, 1); ledSet(ERR_LED2, 1); + motorsDisable(); + storeAssertTextData("Stack overflow"); while(1); } #endif diff --git a/src/utils/interface/cfassert.h b/src/utils/interface/cfassert.h index b3715676c5..ea21d6fe15 100644 --- a/src/utils/interface/cfassert.h +++ b/src/utils/interface/cfassert.h @@ -52,6 +52,26 @@ void printAssertSnapshotData(); /** * Store assert snapshot data to be read at startup if a reset is triggered (watchdog) */ -void storeAssertSnapshotData(char *file, int line); +void storeAssertFileData(const char *file, int line); +/** + * Store hardfault data to be read at startup if a reset is triggered (watchdog) + * Line information can be printed using: + * > make gdb + * gdb> info line *0x + */ +void storeAssertHardfaultData( + unsigned int r0, + unsigned int r1, + unsigned int r2, + unsigned int r3, + unsigned int r12, + unsigned int lr, + unsigned int pc, + unsigned int psr); + +/** + * Store assert data to be read at startup if a reset is triggered (watchdog) + */ +void storeAssertTextData(const char *text); #endif //__CFASSERT_H__ diff --git a/src/utils/src/cfassert.c b/src/utils/src/cfassert.c index 61fa28a503..d79c8e429e 100644 --- a/src/utils/src/cfassert.c +++ b/src/utils/src/cfassert.c @@ -35,25 +35,50 @@ #define MAGIC_ASSERT_INDICATOR 0x2f8a001f +enum snapshotType_e +{ + SnapshotTypeInvalid = 0, + SnapshotTypeFile = 1, + SnapshotTypeHardFault = 2, + SnapshotTypeText = 3, +}; + typedef struct SNAPSHOT_DATA { uint32_t magicNumber; - char* fileName; - int line; + enum snapshotType_e type; + union { + struct { + const char* fileName; + int line; + } file; + struct { + unsigned int r0; + unsigned int r1; + unsigned int r2; + unsigned int r3; + unsigned int r12; + unsigned int lr; + unsigned int pc; + unsigned int psr; + } hardfault; + struct { + const char* text; + } text; + }; } SNAPSHOT_DATA; // The .nzds section is not cleared at startup, data here will survive a // reset (by the watch dog for instance) SNAPSHOT_DATA snapshot __attribute__((section(".nzds"))) = { .magicNumber = 0, - .fileName = "", - .line = 0 + .type = SnapshotTypeInvalid, }; void assertFail(char *exp, char *file, int line) { portDISABLE_INTERRUPTS(); - storeAssertSnapshotData(file, line); + storeAssertFileData(file, line); DEBUG_PRINT("Assert failed %s:%d\n", file, line); motorsSetRatio(MOTOR_M1, 0); @@ -64,21 +89,73 @@ void assertFail(char *exp, char *file, int line) ledClearAll(); ledSet(ERR_LED1, 1); ledSet(ERR_LED2, 1); + motorsDisable(); while (1); } -void storeAssertSnapshotData(char *file, int line) +void storeAssertFileData(const char *file, int line) +{ + snapshot.magicNumber = MAGIC_ASSERT_INDICATOR; + snapshot.type = SnapshotTypeFile; + snapshot.file.fileName = file; + snapshot.file.line = line; +} + +void storeAssertHardfaultData( + unsigned int r0, + unsigned int r1, + unsigned int r2, + unsigned int r3, + unsigned int r12, + unsigned int lr, + unsigned int pc, + unsigned int psr) +{ + snapshot.magicNumber = MAGIC_ASSERT_INDICATOR; + snapshot.type = SnapshotTypeHardFault; + snapshot.hardfault.r0 = r0; + snapshot.hardfault.r1 = r1; + snapshot.hardfault.r2 = r2; + snapshot.hardfault.r3 = r3; + snapshot.hardfault.r12 = r12; + snapshot.hardfault.lr = lr; + snapshot.hardfault.pc = pc; + snapshot.hardfault.psr = psr; +} + +void storeAssertTextData(const char *text) { snapshot.magicNumber = MAGIC_ASSERT_INDICATOR; - snapshot.fileName = file; - snapshot.line = line; + snapshot.type = SnapshotTypeText; + snapshot.text.text = text; } void printAssertSnapshotData() { if (MAGIC_ASSERT_INDICATOR == snapshot.magicNumber) { - DEBUG_PRINT("Assert failed at %s:%d\n", snapshot.fileName, snapshot.line); + switch (snapshot.type) { + case SnapshotTypeFile: + DEBUG_PRINT("Assert failed at %s:%d\n", snapshot.file.fileName, snapshot.file.line); + break; + case SnapshotTypeHardFault: + DEBUG_PRINT("Hardfault. r0: %X, r1: %X, r2: %X, r3: %X, r12: %X, lr: %X, pc: %X, psr: %X\n", + snapshot.hardfault.r0, + snapshot.hardfault.r1, + snapshot.hardfault.r2, + snapshot.hardfault.r3, + snapshot.hardfault.r12, + snapshot.hardfault.lr, + snapshot.hardfault.pc, + snapshot.hardfault.psr); + break; + case SnapshotTypeText: + DEBUG_PRINT("Assert failed: %s\n", snapshot.text.text); + break; + default: + DEBUG_PRINT("Assert failed, but unknown type\n"); + break; + } } else { DEBUG_PRINT("No assert information found\n"); }