代码调试跟踪与优化(三)--- 如何调试Fault 异常?

本文详细介绍了在开发嵌入式代码时如何分析和处理Cortex-M处理器的Fault异常,包括Fault异常的类别、状态寄存器、初始化配置,以及如何利用Call Stack Trace、Event Trace和Instruction Trace来定位问题。通过实例展示了如何借助IDE和Ozone工具分析Fault异常,同时讲解了如何设计Fault handlers输出故障日志,以便于快速定位和解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在开发嵌入式代码时,经常会遇到处理器无法启动或系统停止响应的情况,引起这种症状的原因可能有很多,其中一些可能是硬件问题(比如供电、时钟等),更常见的是处理器触发了Fault 异常,并停留在Fault 异常处理程序内循环(默认情况下,Fault handler 在启动代码中定义为死循环),我们如何分析产生Fault 异常的原因呢?

一、产生Fault 异常的可能原因?

1.1 Fault 异常类别

为了方便我们分析程序产生Fault 的原因,ARM Cortex-M 提供了系统异常处理机制,当Fault 发生时会执行相应的Fault 异常处理程序,我们可以在Fault 异常处理程序中尝试定位或解决相应的Fault 异常。

Cortex-M 提供了哪些Fault 异常类型呢?我们可以从中断向量表中获知(参见博文:中断向量表):
Cortex-M 系统异常类型
从上表可知,Cortex-M 的Fault 异常主要分为四类(Debug monitor 异常用于前篇博文谈到的两种调试模式中调试监视器模式,也即当发生调试事件时,系统不暂停而是去执行DebugMonitor_Handler 程序):

  • MemManage fault:存储器管理故障,主要由违反MPU 定义的访问规则引起的,比如试图访问不被允许的存储区域、从不允许访问的存储区域取指令或读写数据等;
  • BusFault:总线访问故障,主要由内存访问期间从处理器总线接口接收到的错误响应触发,比如处理器尝试访问无效的内存位置、设备尚未准备好接收数据传输等;
  • Usage fault:用法错误,主要由错误的处理器操作引起的,比如执行未定义的指令、无效的异常返回码EXC_RETURN、未对齐的内存访问、执行除零操作等;
  • Hard fault:硬故障(固定优先级为 -1,即它比除 NMI 以外的所有其它中断或异常具有更高的优先级),可能由MemManage fault、BusFault、Usage fault上访而来(若这些fault 未启用,则会强制进入Hard fault 异常处理程序),也可能因取中断向量失败触发。

Cortex-M 架构可用的Fault 异常类型
Cortex-M 处理器是如何检测到系统出现Fault 异常的呢?当处理器进入Fault 异常处理程序后,我们如何判断系统是因为什么原因、在哪个地方引起的Fault 异常呢?

1.2 Fault 状态寄存器

嵌入式处理器的执行状态一般都是通过相应的寄存器展示的,Cortex-M 为了支持Fault 异常处理机制,也提供了一些状态寄存器及地址寄存器,状态寄存器的每一位表示一种Fault 异常原因,地址寄存器则提供发生Fault 的存储地址:
Fault 状态及地址寄存器
Fault 状态寄存器表示的故障原因如下(方括号内数字表示相应寄存器的第几位,未列出的位为保留位,暂没有使用):

// MMFSR: MemManage Fault Status Register (lowest byte in SCB->CFSR)
[7] MMARVALID	- 如果为1,则表示MMFAR寄存器存储的MemManageFault寻址值有效。
[5] MLSPERR		- 如果为1,则表示惰性保存浮点状态时发生错误。
[4] MSTKERR		- 如果为1,则表示在中断或异常入栈时企图访问不被允许的区域。
[3] MUNSTKERR	- 如果为1,则表示在中断或异常出栈时企图访问不被允许的区域。
[1] DACCVIOL	- 如果为1,则表示企图从不允许访问的区域读、写数据。
[0] IACCVIOL	- 如果为1,则表示企图从不允许访问的区域取指令。 

// MMFAR: MemManage Fault Address Register (0xE000ED34, SCB->MMFAR)
[31:0] ADDRESS	- 当MMARVALID 的值为1时,MMFAR 记录在访问哪个地址时发生了MemManage Fault。 

// BFSR: Bus Fault Status Register (2nd byte in SCB->CFSR)
[15] BFARVALID		- 如果为1,则表示BFAR寄存器存储的BusFault 寻址值有效。
[13] LSPERR			- 如果为1,则表示惰性保存浮点状态时发生错误。
[12] STKERR			- 如果为1,则表示入栈时发生错误。
[11] UNSTKERR		- 如果为1,则表示出栈时发生错误。
[10] IMPRECISERR	- 如果为1,则表示不精确的数据总线错误。
[9] PRECISERR		- 如果为1,则表示精确的数据总线错误。
[8] IBUSERR			- 如果为1,则表示指令提取错误。 

// BFAR: Bus Fault Address Register (0xE000ED38, SCB->BFAR)
[31:0] ADDRESS	- 当BFARVALID的值为1时,BFAR 记录在访问哪个地址时发生了Bus Fault。 

// UFSR: Usage Fault Status Register (Upper half-word in SCB->CFSR)
[25] DIVBYZERO	- 如果为1,则表示企图执行除 0 操作。
[24] UNALIGNED	- 如果为1,则表示企图执行非对齐访问。
[19] NOCP		- 如果为1,则表示企图执行不受支持的协处理器指令。
[18] INVPC		- 如果为1,则表示将非法或无效的EXC_RETURN值加载到PC。
[17] INVSTATE	- 如果为1,则表示试图切换到 ARM 状态。
[16] UNDEFINSTR	- 如果为1,则表示企图执行未定义指令。 

// HFSR: Hard Fault Status Register (0xE000ED2C ,SCB->HFSR)
[31] DEBUGEVT	- 如果为1,则表示发生了一个调试事件。
[30] FORCED		- 如果为1,则表示该Hard Fault 是由MemManage Fault、Bus Fault 或Usage Fault 引起的。
[1] VECTTBL		- 如果为1,则表示取中断向量时出错。

// DFSR: Debug Fault Status Register (0xE000ED30 ,SCB->DFSR)
[4] EXTERNAL	- 如果为1,则表示因外部调试请求触发了调试事件。
[3] VCATCH		- 如果为1,则表示因发生向量捕获触发了调试事件。
[2] DWTTRAP		- 如果为1,则表示因数据监测点匹配触发了调试事件。
[1] BKPT		- 如果为1,则表示因执行BKPT 指令触发了调试事件。
[0] HALTED		- 如果为1,则表示调试器请求处理器进入暂停模式。

// AFSR: Auxiliary Fault Status Register (0xE000ED3C, SCB->AFSR)
[31:0] Implementation Defined	- 允许芯片设计人员添加自己的故障状态信息。

了解了各类Fault 状态寄存器每一位指示的fault 原因,我们就可以在程序进入fault 异常处理程序后,通过查看上述Fault 状态寄存器哪一位被置为 1 了,了解到产生fault 的可能原因。我们如何定位产生fault 的具体代码呢?

1.3 Fault 异常初始化配置

在回答这个问题前,我们还有个问题,这些fault 寄存器及其异常处理程序在使用前需要怎么初始化呢?

我们在使用中断或异常之前,通常需要先为其配置优先级并使能中断,处理器才会处理并响应对应的中断信号,前面介绍的四种 fault 异常只有Hard Fault 是固定优先级-1,默认开启,其余三种都是默认未开启状态。如果我们想让MemManage Fault、Bus Fault、Usage Fault、Debug Fault 异常处理程序可以正常响应,需要先为其配置优先级并使能对应的异常,配置方法如下(如果上述异常未配置或未开启,处理器也会上访为Hard Fault,去执行Hard Fault Handler):

#define SCB_BASE            0xE000ED00UL                    /*!< System Control Block Base Address */

/* Structure type to access the System Control Block (SCB). */
typedef struct
{
   
   
......
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
  __IOM uint8_t  SHP
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流云IoT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值