HardFault分析
segger官方也有hardFault相关的文档和代码,去这个页面ctrl+F搜索hardfault,可以看到文档和相关代码:https://www.segger.com/downloads/application-notes/
参考keil官方文档和相关代码 apnt209 :http://www.keil.com/appnotes/docs/apnt_209.asp
以及一个开源的hardFault诊断工具CMBackTrace :https://github.com/armink/CmBacktrace
最主要的是,找到 Cortex - M3 Technical Reference Manual。
原来只知道有stm32的datasheet和参考手册,根本不知道还有这个手册。。
大概《cortex M3权威指南》翻译的大部分内容,都是从这本书上来的。
我只是用这个手册翻了一下,找到一些寄存器的定义。
(因为这些寄存器的定义在stm32的参考手册里是没有的,属于ARM公司规定的CMSIS范畴,不是芯片范畴的)
还有一个是stm32官方出示的一个“hardFault诊断”手册,其实跟apnt209差不多。地址:http://www.stmcu.org/search/?q=hardfault&m=
最后我总结一下我在apnt209中截取的一部分:
思路就是在hardFault的ISR中,添加一个死循环,当系统运行到这里时,说明出现了hardfault异常。
下面是apnt209配套的软件工程:
1 /****************************************************************************** 2 * @file HardFault_Handler.c 3 * @brief HardFault handler example 4 * @version V1.00 5 * @date 10. July 2017 6 ******************************************************************************/ 7 /* 8 * Copyright (c) 2017 ARM Limited. All rights reserved. 9 * 10 * SPDX-License-Identifier: Apache-2.0 11 * 12 * Licensed under the Apache License, Version 2.0 (the License); you may 13 * not use this file except in compliance with the License. 14 * You may obtain a copy of the License at 15 * 16 * www.apache.org/licenses/LICENSE-2.0 17 * 18 * Unless required by applicable law or agreed to in writing, software 19 * distributed under the License is distributed on an AS IS BASIS, WITHOUT 20 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 * See the License for the specific language governing permissions and 22 * limitations under the License. 23 */ 24 25 #include "RTE_Components.h" // Component selection 26 #include CMSIS_device_header // Include device header file from 27 #include "stdio.h" // include stdio library for printf output 28 #include "project_configuration.h" // Header file for test case configuration 29 30 #if HARDFAULT_HANDLER == 1 31 32 void HardFault_Handler_C(unsigned long * svc_args, unsigned int lr_value); 33 34 // HardFault handler wrapper in assembly language. 35 // It extracts the location of stack frame and passes it to the handler written 36 // in C as a pointer. We also extract the LR value as second parameter. 37 __asm void HardFault_Handler(void) 38 { 39 TST LR, #4 ;测试LR寄存器的bit2是否为1 40 ITE EQ 41 MRSEQ R0, MSP ;如果LR的bit2是0,表示在发生HardFault之前,程序使用的是主进程堆栈Main Stack 42 MRSNE R0, PSP ;否则LR的bit2是1,说明在发生HF之前,程序使用是进程堆栈Process Stack 43 MOV R1, LR 44 B __cpp(HardFault_Handler_C) ;把栈的值放入R0,把LR的值放入R1,调用C函数 45 } 46 47 48 // HardFault handler in C, with stack frame location and LR value extracted 49 // from the assembly wrapper as input parameters 50 void HardFault_Handler_C(unsigned long * hardfault_args, unsigned int lr_value) //hardfault_args存的是栈的值,lr_value是LR的值 51 { 52 unsigned long stacked_r0; 53 unsigned long stacked_r1; 54 unsigned long stacked_r2; 55 unsigned long stacked_r3; 56 unsigned long stacked_r12; 57 unsigned long stacked_lr; 58 unsigned long stacked_pc; 59 unsigned long stacked_psr; 60 unsigned long cfsr; 61 unsigned long bus_fault_address; 62 unsigned long memmanage_fault_address; 63 64 bus_fault_address = SCB->BFAR; //BusFaultAddrReg 65 memmanage_fault_address = SCB->MMFAR; //MemManageAddrReg 66 cfsr = SCB->CFSR; //ConfigerableFaultStatusReg 67 68 stacked_r0 = ((unsigned long) hardfault_args[0]); //Stack指向地址的内容,按照自动进栈出栈时的顺序 69 stacked_r1 = ((unsigned long) hardfault_args[1]); 70 stacked_r2 = ((unsigned long) hardfault_args[2]); 71 stacked_r3 = ((unsigned long) hardfault_args[3]); 72 stacked_r12 = ((unsigned long) hardfault_args[4]); 73 stacked_lr = ((unsigned long) hardfault_args[5]); 74 stacked_pc = ((unsigned long) hardfault_args[6]); 75 stacked_psr = ((unsigned long) hardfault_args[7]); 76 77 printf ("[HardFault]\n"); 78 printf ("- Stack frame:\n"); 79 printf (" R0 = %x\n", stacked_r0); 80 printf (" R1 = %x\n", stacked_r1); 81 printf (" R2 = %x\n", stacked_r2); 82 printf (" R3 = %x\n", stacked_r3); 83 printf (" R12 = %x\n", stacked_r12); 84 printf (" LR = %x\n", stacked_lr); 85 printf (" PC = %x\n", stacked_pc); //注意这个是在HardFault发生前的PC地址,说明运行到这里发生HF【定位HF发生的指令】 86 printf (" PSR = %x\n", stacked_psr); 87 printf ("- FSR/FAR:\n"); 88 printf (" CFSR = %x\n", cfsr); 89 printf (" HFSR = %x\n", SCB->HFSR); //HardFaultStatusReg 90 printf (" DFSR = %x\n", SCB->DFSR); //DebugFaultStatusReg 91 printf (" AFSR = %x\n", SCB->AFSR); //Auxiliary Fault Status Reg 附加的..具体看Cortex M3 Technical Reference Manual 92 if (cfsr & 0x0080) printf (" MMFAR = %x\n", memmanage_fault_address); 93 if (cfsr & 0x8000) printf (" BFAR = %x\n", bus_fault_address); 94 printf ("- Misc\n"); 95 printf (" LR/EXC_RETURN= %x\n", lr_value); 96 97 while(1); // endless loop 98 } 99 #endif
所有的诊断过程都是类似的,根据LR的bit2判断进入HardFault前是哪个Stack
确定了Stack,就读取Stack指向地址处的几个寄存器,这几个寄存器在每次发生中断和异常都会自动的压栈出栈
从里面读出PC,PC指向的地址,就是发生异常的地址。
这个地址可以用工具add2line,得到函数名,而不是机器码的地址。。。
这部分可以看上边提到的BackTrace开源工具。
它可以回溯包含发生hardfault函数的,所有的函数调用关系!
keil工具,定性(哪种hardfault)和定位(哪个指令)
参考:http://blog.csdn.net/zhzht19861011/article/details/8645661