1. 程式人生 > >嵌入式開發筆記——除錯元件SEGGER_HardFaultHandle

嵌入式開發筆記——除錯元件SEGGER_HardFaultHandle

# 一、前言 在使用`Cortex-M`核心的MCU進行開發時,有時候會因為對記憶體錯誤訪問等原因造成程式產生異常從而進入`HardFaultHandler`錯誤中斷。如果程式結構比較複雜,尤其是運行了RTOS時可能短時間內不易定位異常產生的原因。**Segger**提供了一種分析CortexM核心晶片HardFault的方法,我在專案中使用後感覺該方法比較實用,本文用來記錄該異常分析元件的使用。 # 二、元件新增 在SEGGER官網的Application Notes頁面下提供了該元件的[原始碼和文件](https://www.segger.com/downloads/application-notes/) ![](https://img2020.cnblogs.com/blog/2193174/202012/2193174-20201220173803639-1940023455.png) 下載下來後將原始檔新增到工程中,然後將中斷處理檔案中的`void HardFault_Handler(void)`函式遮蔽掉(和新增的檔案中的函式有衝突)就完成添加了。 ![](https://img2020.cnblogs.com/blog/2193174/202012/2193174-20201220173812088-1137326070.png) # 三、元件應用 > 執行以下程式碼使程式進入HardFault_Handler ```c volatile unsigned int* p; p = (unsigned int*)0x007FFFFF; // 小於0x8000000的地址在STM32中無效 *p = 0x123456; SEGGER_RTT_printf(0, "p = %d\r\n", *p); ``` 在除錯模式下執行程式會停在`SEGGER_HardFaultHandler.c`檔案中的`void HardFaultHandler(unsigned int* pStack)`函式中。 ```c /********************************************************************* * * HardFaultHandler() * * Function description * C part of the hard fault handler which is called by the assembler * function HardFault_Handler */ void HardFaultHandler(unsigned int* pStack) { SEGGER_RTT_printf(0, "system enter hard-fault\r\n"); // // In case we received a hard fault because of a breakpoint instruction, we return. // This may happen when using semihosting for printf outputs and no debugger is connected, // i.e. when running a "Debug" configuration in release mode. // if (NVIC_HFSR & (1u << 31)) { NVIC_HFSR |= (1u << 31); // Reset Hard Fault status *(pStack + 6u) += 2u; // PC is located on stack at SP + 24 bytes. Increment PC by 2 to skip break instruction. return; // Return to interrupted application } #if DEBUG // // Read NVIC registers // HardFaultRegs.syshndctrl.byte = SYSHND_CTRL; // System Handler Control and State Register HardFaultRegs.mfsr.byte = NVIC_MFSR; // Memory Fault Status Register HardFaultRegs.bfsr.byte = NVIC_BFSR; // Bus Fault Status Register HardFaultRegs.bfar = NVIC_BFAR; // Bus Fault Manage Address Register HardFaultRegs.ufsr.byte = NVIC_UFSR; // Usage Fault Status Register HardFaultRegs.hfsr.byte = NVIC_HFSR; // Hard Fault Status Register HardFaultRegs.dfsr.byte = NVIC_DFSR; // Debug Fault Status Register HardFaultRegs.afsr = NVIC_AFSR; // Auxiliary Fault Status Register // // Halt execution // If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution. // _Continue = 0u; while (_Continue == 0u) { } // // Read saved registers from the stack. // HardFaultRegs.SavedRegs.r0 = pStack[0]; // Register R0 HardFaultRegs.SavedRegs.r1 = pStack[1]; // Register R1 HardFaultRegs.SavedRegs.r2 = pStack[2]; // Register R2 HardFaultRegs.SavedRegs.r3 = pStack[3]; // Register R3 HardFaultRegs.SavedRegs.r12 = pStack[4]; // Register R12 HardFaultRegs.SavedRegs.lr = pStack[5]; // Link register LR HardFaultRegs.SavedRegs.pc = pStack[6]; // Program counter PC HardFaultRegs.SavedRegs.psr.byte = pStack[7]; // Program status word PSR // // Halt execution // To step out of the HardFaultHandler, change the variable value to != 0. // _Continue = 0u; while (_Continue == 0u) { } #else // // If this module is included in a release configuration, simply stay in the HardFault handler // (void)pStack; do { } while (1); #endif } ``` 在`HardFaultRegs`結構體中包含了用來分析異常原因的暫存器 ```c static struct { struct { volatile unsigned int r0; // Register R0 volatile unsigned int r1; // Register R1 volatile unsigned int r2; // Register R2 volatile unsigned int r3; // Register R3 volatile unsigned int r12; // Register R12 volatile unsigned int lr; // Link register volatile unsigned int pc; // Program counter union { volatile unsigned int byte; struct { unsigned int IPSR : 8; // Interrupt Program Status register (IPSR) unsigned int EPSR : 19; // Execution Program Status register (EPSR) unsigned int APSR : 5; // Application Program Status register (APSR) } bits; } psr; // Program status register. } SavedRegs; union { volatile unsigned int byte; struct { unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active unsigned int UnusedBits1 : 1; unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active unsigned int UnusedBits2 : 3; unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active unsigned int UnusedBits3 : 1; unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a higher-priority exception unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority exception unsigned int MEMFAULTENA : 1; // Memory management fault handler enable unsigned int BUSFAULTENA : 1; // Bus fault handler enable unsigned int USGFAULTENA : 1; // Usage fault handler enable } bits; } syshndctrl; // System Handler Control and State Register (0xE000ED24) union { volatile unsigned char byte; struct { unsigned char IACCVIOL : 1; // Instruction access violation unsigned char DACCVIOL : 1; // Data access violation unsigned char UnusedBits : 1; unsigned char MUNSTKERR : 1; // Unstacking error unsigned char MSTKERR : 1; // Stacking error unsigned char UnusedBits2 : 2; unsigned char MMARVALID : 1; // Indicates the MMAR is valid } bits; } mfsr; // Memory Management Fault Status Register (0xE000ED28) union { volatile unsigned int byte; struct { unsigned int IBUSERR : 1; // Instruction access violation unsigned int PRECISERR : 1; // Precise data access violation unsigned int IMPREISERR : 1; // Imprecise data access violation unsigned int UNSTKERR : 1; // Unstacking error unsigned int STKERR : 1; // Stacking error unsigned int UnusedBits : 2; unsigned int BFARVALID : 1; // Indicates BFAR is valid } bits; } bfsr; // Bus Fault Status Register (0xE000ED29) volatile unsigned int bfar; // Bus Fault Manage Address Register (0xE000ED38) union { volatile unsigned short byte; struct { unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined instruction unsigned short INVSTATE : 1; // Attempts to switch to an invalid state (e.g., ARM) unsigned short INVPC : 1; // Attempts to do an exception with a bad value in the EXC_RETURN number unsigned short NOCP : 1; // Attempts to execute a coprocessor instruction unsigned short UnusedBits : 4; unsigned short UNALIGNED : 1; // Indicates that an unaligned access fault has taken place unsigned short DIVBYZERO : 1; // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set) } bits; } ufsr; // Usage Fault Status Register (0xE000ED2A) union { volatile unsigned int byte; struct { unsigned int UnusedBits : 1; unsigned int VECTBL : 1; // Indicates hard fault is caused by failed vector fetch unsigned int UnusedBits2 : 28; unsigned int FORCED : 1; // Indicates hard fault is taken because of bus fault/memory management fault/usage fault unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by debug event } bits; } hfsr; // Hard Fault Status Register (0xE000ED2C) union { volatile unsigned int byte; struct { unsigned int HALTED : 1; // Halt requested in NVIC unsigned int BKPT : 1; // BKPT instruction executed unsigned int DWTTRAP : 1; // DWT match occurred unsigned int VCATCH : 1; // Vector fetch occurred unsigned int EXTERNAL : 1; // EDBGRQ signal asserted } bits; } dfsr; // Debug Fault Status Register (0xE000ED30) volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional) } HardFaultRegs; ``` 將`HardFaultRegs`結構體新增到`Watch視窗`中,通過一步步向下執行程式可以看到結構體中各引數狀態,每個暫存器及暫存器bit位表示什麼含義在結構體定義中均有說明。 ![](https://img2020.cnblogs.com/blog/2193174/202012/2193174-20201220173835479-1597185694.png) 將`_Continue`變數也新增到Watch視窗,當執行到以下程式碼處時在Watch視窗中改變變數值就可以繼續向下執行。 ```c // // Halt execution // If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution. // _Continue = 0u; while (_Continue == 0u) { } ``` ![](https://img2020.cnblogs.com/blog/2193174/202012/2193174-20201220173845053-1618630359.png) 通過對暫存器分析可得出產生異常的原因。 # 四、擴充套件 > ## Cortex-M故障異常 Cortex-M processors implement different fault exceptions. ### HardFault Exception The HardFault is the default exception, raised on any error which is not associated with another (enabled) exception. The HardFault has a fixed priority of -1, i.e. it has a higher priority than all other interrupts and exceptions except for NMI. Therefore a HardFault exception handler can always be entered when an error happens in application code, an interrupt, or another exception. The HardFault is exception number 3 in the vector table with IRQ number -13. ### MemManage Exception The MemManage exception is available with the use of a Memory Protection Unit (MPU) to raise an exception on memory access violations. The MemManage is exception number 4 in the vector table, IRQ Number -12, and has a configurable priority. ### BusFault Exception The BusFault exception is raised on any memory access error. E.g. by illegal read, write, or vector catch. The BusFault is exception number 5 in the vector table, IRQ number -11, and has configurable priority. BusFaults can explicitly be enabled in the system control block (SCB). When BusFault is not enabled, a HardFault is raised. ### UsageFault Exception The UsageFault exception is raised on execution errors. Unaligned access on load/store multiple instructions are always caught. Exceptions on other unaligned access, as well as division by zero can be additionally enabled in the SCB. The UsageFault is exception number 6 in the vector table, IRQ number -10, and has configurable priority. When UsageFault is not enabled, a HardFault is raised instead. > ## 故障狀態暫存器 The Cortex-M System Control Block (SCB) contains some registers which enable configuration of exceptions and provide information about faults. #### HardFault Status Register (HFSR) The HFSR is in the SCB at address 0xE000ED2C. It is a 32-bit register. Bitfields: ``` [31] DEBUGEVT - Reserved for use by debugger/debug probe. Always write 0. [30] FORCED - If 1, HardFault has been caused by escalation of another exception, because it is disabled or because of priority. [1] VECTTBL - If 1, a BusFault occurred by reading the vector table for exception processing. ``` #### UsageFault Status Register (UFSR) The UFSR is a 16-bit pseudo-register, part of the Configurable Fault Status Register (CFSR) at address 0xE000ED28. It can also be directly accessed with halfword access to 0xE000ED2A. Bitfields: ``` [9] DIVBYZERO - If 1, SDIV or UDIV instruction executed with divisor 0. [8] UNALIGNED - If 1, LDM, STM, LDRD, STRD on unaligned address executed, or single load or store executed when enabled to trap. [3] NOCP - If 1, access to unsupported (e.g. not available or not enabled) coprocessor. [2] INVPC - If 1, illegal or invalid EXC_RETURN value load to PC. [1] INVSTATE - If 1, execution in invalid state. E.g. Thumb bit not set in EPSR, or invalid IT state in EPSR. [0] UNDEFINSTR - If 1, execution of undefined instruction. ``` #### BusFault Status Register (BFSR) and BusFault Address Register (BFAR) The BFSR is a 8-bit pseudo-register in the CFSR. It can be directly accessed with byte access ad 0xE000ED29. The BFAR is a 32-bit register at 0xE000ED38. Bitfields: ``` [7] BFARVALID - If 1, the BFAR contains the address which caused the BusFault. [5] LSPERR - 1f 1, fault during floating-point lazy stack preservation. [4] STKERR - If 1, fault on stacking for exception entry. [3] UNSTKERR - If 1, fault on unstacking on exception return. [2] IMPRECISERR - If 1, return address is not related to fault, e.g. fault caused before. [1] PRECISERR - If 1, return address instruction caused the fault. [0] IBUSERR - If 1, fault on instruction fetch. ``` #### MemManage Fault Status Register (MMFSR) and MemManage fault Address Register (MMFAR) The MMFSR is a 8-bit pseudo-register in the CFSR. It can be directly accessed with byte access ad 0xE000ED28. The MMFAR is a 32-bit register at 0xE000ED34. Bitfields: ``` [7] MMARVALID - If 1, the MMFAR contains the address which caused the MemManageFault. [5] MLSPERR - 1f 1, fault during floating-point lazy stack preservation. [4] MSTKERR - If 1, fault on stacking for exception entry. [3] MUNSTKERR - If 1, fault on unstacking on exception return. [1] DACCVIOL - If 1, data access violation. [0] IACCVIOL - If 1, instruction access violation. ``` ### Stack Recovery On exception entry, the exception handler can check which stack has been used when the fault happened. When bit EXC_RETURN[2] is set, MSP has been used, otherwise PSP has been used. The stack can be used to recover the CPU register values. ### CPU Register Recovery On exception entry, some CPU registers are stored on the stack and can be read from there for error analysis. the following registers are recoverable: ``` r0 = pStack[0]; // Register R0 r1 = pStack[1]; // Register R1 r2 = pStack[2]; // Register R2 r3 = pStack[3]; // Register R3 r12 = pStack[4]; // Register R12 lr = pStack[5]; // Link register LR pc = pStack[6]; // Program counter PC psr.byte = pStack[7]; // Program status word PSR ``` > ## 故障分析示例 The following examples show how/why some faults can be caused, and how to analyze them. A project to test the faults is available [here](https://wiki.segger.com/File:CortexM_FaultTest.zip). ### BusFault Examples #### Illegal Memory Write ``` /********************************************************************* * * _IllegalWrite() * * Function description * Trigger a BusFault or HardFault by writing to a reserved address. * * Additional Information * BusFault is raised some instructions after the write instruction. * Related registers on fault: * HFSR = 0x40000000 * FORCED = 1 - BusFault escalated to HardFault (when BusFault is not activated) * BFSR = 0x00000004 * IMPREISERR = 1 - Imprecise data access violation. Return address not related to fault * BFARVALID = 0 - BFAR not valid */ static int _IllegalWrite(void) { int r; volatile unsigned int* p; r = 0; p = (unsigned int*)0x00100000; // 0x00100000-0x07FFFFFF is reserved on STM32F4 // F44F1380 mov.w r3, #0x00100000 *p = 0x00BADA55; // 4A03 ldr r2, =0x00BADA55 // 601A str r2, [r3] <- Illegal write is done here return r; // 9B00 ldr r3, [sp] // 4618 mov r0, r3 // B002 add sp, sp, #8 <- Fault might be raised here // 4770 bx lr } ``` #### Illegal Memory Read ``` /********************************************************************* * * _IllegalRead() * * Function description * Trigger a BusFault or HardFault by reading from a reserved address. * * Additional Information * BusFault is immediately triggered on the read instruction. * Related registers on fault: * HFSR = 0x40000000 * FORCED = 1 - BusFault escalated to HardFault * BFSR = 0x00000082 * PRECISERR = 1 - Precise data access violation * BFARVALID = 1 - BFAR is valid * BFAR = 0x00100000 - The address read from */ static int _IllegalRead(void) { int r; volatile unsigned int* p; p = (unsigned int*)0x00100000; // 0x00100000-0x07FFFFFF is reserved on STM32F4 // F44F1380 mov.w r3, #0x00100000 <- The read address. Will be found in BFAR r = *p; // 681B ldr r3, [r3] <- Illegal read happens here and raises BusFault // 9300 str r3, [sp] return r; } ``` #### Illegal Function Execution ``` /********************************************************************* * * _IllegalFunc() * * Function description * Trigger a BusFault or HardFault by executing at a reserved address. * * Additional Information * BusFault is triggered on execution at the invalid address. * Related registers on fault: * HFSR = 0x40000000 * FORCED = 1 - BusFault escalated to HardFault * BFSR = 0x00000001 * IBUSERR = 1 - BusFault on instruction prefetch */ static int _IllegalFunc(void) { int r; int (*pF)(void); pF = (int(*)(void))0x00100001; // 0x00100000-0x07FFFFFF is reserved on STM32F4 // F44F1380 mov.w r3, #0x00100001 r = pF(); // 4798 blx r3 <- Branch to illegal address, causes fetch from 0x00100000 and fault exception return r; } ``` ### UsageFault Examples #### Undefined Instruction Execution ``` /********************************************************************* * * _UndefInst() * * Function description * Trigger a UsageFault or HardFault by executing an undefined instruction. * * Additional Information * UsageFault is triggered on execution at the invalid address. * Related registers on hard fault: * HFSR = 0x40000000 * FORCED = 1 - UsageFault escalated to HardFault * UFSR = 0x0001 * UNDEFINSTR = 1 - Undefined instruction executed */ static int _UndefInst(void) { static const unsigned short _UDF[4] = {0xDEAD, 0xDEAD, 0xDEAD, 0xDEAD}; // 0xDEA