Cortex-M处理器中,所有的错误异常默认都会触发HardFault异常
HardFault 无需使能,它有固定的异常优先级-1,Bus Fault/Memory Management Fault/Usage Fault在默认状态都是关闭状态,所以发生异常时都会进入 HardFault 中断服务程序。使能前三种异常需要要在初始化时配置:
1 | SCB->SHCSR |= 0x00007000; |
Bus Fault:总线异常是指CPU在进行总线读写操作过程中,总线上返回一个错误状态,比如总线地址不可访问或者有ECC错误等。总线异常可以能产生在CPU取指,数据读写,中断向量表读取,出入栈和中断进出等阶段。
Memory Management Fault:MPU保护异常,产生原因是系统检测到违反MPU区域配置属性的区域访问操作,比如在一个只是读写的区域执行了代码。
Usage Fault:这种异常包含CPU取到一个未定义指令,非对齐的数据访问,除零异常等等
Hard Fault:因为上面几种错误默认是可以关闭,并且可以设定优先级的,如果相应的异常在系统中未使能,或者系统在执行更高优先级的中断中产生了上述异常,该异常就会直接上访成HardFault。另外在CM0内核中并没有上述的单独异常,所以前述的异常都会直接进入Hard Fault。
栈区空间
栈是一种受限的数据结构模型,其数据总是只能在顶部追加,利用一个指针进行索引,顶端叫栈顶,相对的一端底部称为栈底。栈是一种 LIFO 后入先出的数据结构栈的两种操作:
- PUSH,压栈,向栈内写入环境变量
- POP,出栈,恢复环境变量
首先将栈与堆区分,这是两个不同的独立空间!(堆本文不深入讨论) - 栈:由编译器分配,存放函数的参数值,局部变量,寄存器组(不同的单片机/处理器各有不同)、函数调用参数传递、中断异常产生时须保存处理器状态的寄存器值等
- 堆:由程序员分配释放,对于 C 而言,malloc、realloc/free 进行分配/释放,对 C++而言,由 new/delete分配/释放。
栈空间与运行环境
如果因为某些错误导致了芯片硬件错误,处理器会调用Hardfault异常中断处理函数,它自动将下面的8个寄存器(栈帧)按以下顺序压栈:
- xPSR :状态寄存器
- PC :异常返回的地址
- LR :反馈函数指针
- R12 :通用寄存器R12
- R0-R3 :通用寄存器R0-R3
在完成压栈之后,SP减小8个字。下图显示了异常抢占当前的程序之后栈中的内容
我们需要重点关注的是PC和LR两个寄存器。根据得到的两个地址,配合代码编译后得到的.map 文件,找到出错的函数,进一步分析代码,可以大大缩小排查范围,找到最终引起错误的代码位置。
SCB(System Control Block)包含系统的配置信息,系统控制等相关信息,SCB包含多个寄存器,通过这些寄存器我们可以查看系统当前等运行状态和异常的具体信息,熟悉SCB中寄存器的含义对于异常的分析有很大帮助
地址 | 名称 | 描述 |
---|---|---|
0xE000E008 | ACTLR | 和系统性能相关的详细配置,开启 DISDEFWBUF 可以禁用系统 buffer,捕获更精确的异常地址(一般无需更改) |
0xE000ED00 | CPUID | 只读的 CPUID,ARM 的版本信息 |
0xE000ED04 | ICSR | 中断控制和状态寄存器,查看当前系统悬起的中断/正在执行的中断号信息 |
0xE000ED08 | VTOR | 中断向量表偏移地址 |
0xE000ED0C | AIRCR | 应用中断和复位控制寄存器,写入特定值可产生软件复位 |
0xE000ED10 | SCR | 系统控制寄存器,控制 CPU 的低功耗行为 |
0xE000ED14 | CCR | 查看非对齐访问/除零异常标志,控制系统异常行为 |
0xE000ED18 | SHPR1 | 定义 Usage Fault/Bus Fault/MemManage Fault 异常的优先级 |
0xE000ED1C | SHPR2 | 定义 SVCall 异常优先级 |
0xE000ED20 | SHPR3 | 定义 SysTick 和 PendSV 异常优先级 |
0xE000ED24 | SHCRS | 查看已触发的系统异常,控制开启哪些系统异常(对异常分析重要) |
0xE000ED28 | MMSR | MemManage 异常状态的具体信息 |
0xE000ED29 | BFSR | BusFault 异常的具体信息 |
0xE000ED2A | UFSR | UsageFault 异常的具体信息 |
0xE000ED2C | HFSR | HardFault 异常的具体信息 |
0xE000ED34 | MMFAR | MemManage 异常地址(CM0 没有) |
0xE000ED38 | BFAR | BusFault 异常地址(CM0 没有) |
0xE000ED3C | AFSR | 额外异常状态 |
下面是两个具体的例子:
案例1 通过SCB相关寄存器定位问题:
注意到其中的PRECISERR位被置1,即为精确的总线 fault,BFAR中有记录BusFault的异常地址即0x2008 8000
案例2 通过PC寄存器定位
1.仿真时,停止程序全速运行。可以看到停在HardFault中
2.查看左边的各个寄存器值,LR是0xFFFFFFF9,则需要查看MSP的值,也就是0x20006CB0
根据我们上文提到的压栈顺序,LR的值为0x08001FCF,PC值为0x08001F24
引起芯片异常错误的常见的原因
- 数组越界操作
- 内存溢出/非法访问
- 堆栈溢出/执行非法代码
- 触发芯片硬件错误处理:超频/Flash擦写不规范/外设初始化不规范/除数为0…
《AN_N32_CortexM4系列系统异常分析应用笔记_V1.0》
《Cortex-M3权威指南》
https://linmingjie.cn/index.php/archives/368/
https://blog.csdn.net/qq446252221/article/details/111359807
https://blog.csdn.net/m0_37202877/article/details/142914089
https://blog.csdn.net/tilblackout/article/details/128185998