stm32的fault错误解析

更新时间:2023-06-18 23:18:46 阅读: 评论:0

C 中上报入栈的寄存器和各 fault 状态寄存器
大多数的 CM3 项目还是以 C 语言为主的。然而,在 C 中不方便定位和直接访问堆栈帧(入栈的
寄存器)。因为在标准 C 语言中是不能获取 SP 指针的。因此,如果使用 C 来写 fault 服务例程,最
好配合一小段汇编码来获取 SP 的值,再把该值以一个参数传送给 fault 上报函数。
译注:在使用 MDK 自带的 ARM 编译器时,可以使用__builtin_frame_address()函数来获取堆栈帧的地址。
GNU 工具中也可以这样做。此法方便,并且可取代上文的通用作法,但降低了编译器间的可移植性。
这个机制与第 12 章讲的 SVC 范例相同(“在 C 中使用 SVC”)。下例就以嵌入式汇编的方式来演
示。这个例子可以在 RealView MDK 中编译。
示例程序的第一部分是个汇编封皮。在使用前,要在向量表中的硬 fault 入口地址项中,填写
好该封皮的入口地址。这个封皮代码把正确的堆栈指针值拷贝到 R0 中,以作为参数来传送
C
数。
// 使用汇编写就的硬 fault 服务例程
// 该例程提取堆栈帧的位置并且把它传递
// C 程序
__asm void hard_fault_handler_asm(void)
{
IMPORT hard_fault_handler_c
TST LR, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B hard_fault_handler_c
}
示例程序的第二部分,也是主体部分,使用 C 语言来写。在这里,我们主要是演示如何访
问入
栈的寄存器和 fault 状态寄存器。
// 使用 C 写就的硬 fault 服务例程
// 第一个参数即堆栈帧的位置
void hard_fault_handler_c(unsigned int * hardfault_args)
{
unsigned int stacked_r0;
unsigned int stacked_r1;
unsigned int stacked_r2;
unsigned int stacked_r3;
unsigned int stacked_r12;
unsigned int stacked_lr;
unsigned int stacked_pc;
unsigned int stacked_psr;
stacked_r0 = ((unsigned long) hardfault_args[0]);
stacked_r1 = ((unsigned long) hardfault_args[1]);
stacked_r2 = ((unsigned long) hardfault_args[2]);
stacked_r3 = ((unsigned long) hardfault_args[3]);
stacked_r12 = ((unsigned long) hardfault_args[4]);
stacked_lr = ((unsigned long) hardfault_args[5]);
stacked_pc = ((unsigned long) hardfault_args[6]);
stacked_psr = ((unsigned long) hardfault_args[7]);
printf ("[Hard fault handler]\n");
printf ("R0 = %x\n", stacked_r0);
printf ("R1 = %x\n", stacked_r1);
printf ("R2 = %x\n", stacked_r2);
printf ("R3 = %x\n", stacked_r3);
printf ("R12 = %x\n", stacked_r12);
printf ("LR = %x\n", stacked_lr);
printf ("PC = %x\n", stacked_pc);
printf ("PSR = %x\n", stacked_psr);
printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38))));
printf ("CFSR = %x\n", (*((volatile unsigned long *)(0xE000ED28))));
printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C))));
printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30))));
printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C))));
exit(0); // terminate
return;
}
请注意:如果发生了堆栈溢出或其它错误,使 SP 指向了无效的存储空对空区域,则上段代码会失
能。在大多数情况下,这种错误都会影响 C 代码,因为所有 C 代码都需要堆栈。
理解发生 fault 的原因
在收集到所需的信息后,就需要分析问题了。表 E.3-E.7 列出了导致 faults 的典型原因。
表 E.3 MemManage fault 状态寄存器提供的讯息
可能的原因
MSTKERR
入栈时发生错误(异常响应序列开始时)
1) 堆栈指针的值被破坏
2) 堆栈容易过大,已经超出 MPU 允许的 region 范围
MUNSTKERR
出栈时发生错误(异常响应序列终止时)。入栈时没有发生错误,出栈时却出
错,总令人有些匪夷所思,可能的原因是
1. 异常服务例程破坏了堆栈指针
2. 异常服务例程更改了 MPU 配置
DACCVIOL
内存访问保护违例。这是 MPU 发挥作用的体现。常常是用户应用程序企图访
问特权级 region 所致
IACCVIOL
1. 内存访问保护违例。常常是用户应用程序企图访问特权级 region。在这种情
况下,入栈的 PC 给出的地址,就是产生问题的代码之所在
2. 跳转到不可执行指令的 regions
3. 异常返回时,使用了无效的 EXC_RETURN
4. 向量表中有无效的向量。例如,异常在向量建立之前就发生了,或者加载的
是用于传统 ARM 内核的可执行映像
5. 在异常处理期间,入栈的 PC 值被破坏了

表 E.4 总线 fault 状态寄存器提供的讯息
可能的原因
STKERR
(自动)入栈期间出错
1. 堆栈指针的值被破坏
2. 堆栈用量太大,到达了未定义存储器的区域
3. PSP 未经初始化就使用
UNSTKERR
(自动)出栈期间出错。如果没有发生过 STKERR,则最可能的就是在异常
处理期间把 SP 方仲永的故事的值破坏了
IMPRECISERR
与设备之间传送数据的过程中发生总线错误。可能是因为设备未经初始化而
引起;或者在用户级访问了特权级的设备,或者传送的数据单位尺寸不能为
设备所接受。此时,有可能是 LDM/STM 指令造成了非精确总线 fault
PRECISERR
在数据访问期间的总线错误。通过 春的诗BFAR 可以获取具体的地址。发生 fault
的原因同上。
IBUSERR
MemManage fault 中的 IACCVIOL

表 E.5 用法 fault 状态寄存器提供的讯息
可能的原因
DIVBYZERO
DIV_0_TRP 置位时则发生了除数为零的情况。引发此 fault 的指令可以从入栈
PC 读取
UNALIGNED
UNALIGN_TRP 置位时发生未对齐访问。引发此 fault 的指令可以从入栈的 PC

读取
NOCP
企图执行一个协处理器指令。引发此 fault 的指令可以从入栈的 PC 读取
INVPC
1. 异常返回时使用了无效的 EXC_RETURN,例如
1) EXC_RETURN=0xFFFF_FFF1 时却要返回线程模式
2) EXC_RETURN=0xFFFF_FFF9 时却要返回 handler 模式
2. 无效的异常活动状态,例如
1) 当前异常的活动状态已经清除了,却在此时执行异常返回。往往是因为滥
VECTCLRACTIVE 或清除了 SHCSR 中活动状态所致
2) 在尚有异常的活动位置位时,却要返回线程模式
3. 由于堆栈指针错误导致了 IPSR 的值不正确。对于 INVPC fault,入栈的 PC
指出了该 fault 服务例程在何处抢占了其它代码。这个问题往往是由比较隐晦
的程序错误造成的,欲详细调查该问题的原因,最好使用 ITM 的跟踪功能。
4. ICI/IT 位对当前指令无效。当 LDM/STM 指令被异常打断后,在异常服务例程
中又更改了入栈的 PC。结果在中断返回时,非零的 ICI 位段作用到了不使用
ICI 位段的指令上。如果是其它原因破坏了 PSR 的值,也可能导致此 fault
房产策划INVSTATE
1. 加载到 PC 中的跳转地址值是偶数(LSB=0)。通过检查入栈 PC 的值,一下子
就可以查出该问题。
2. 向量地址的 LSB=0,诊断方法同上。
3. 入栈的 PSR 在异常处理过程中被破坏,使得在返回时内核尝试进入 ARM 状态。
UNDEFINSTR
1. 使用了 CM3 不支持的指令
2. 代码段中的数据被破坏
3. 连接时加载了 ARM 目标码。请检查编译阶段的设置
4. 指令对齐的问题。例如,在使用 GNU 工具链时,忘记了在.ascii 后使用.align
就有可能导致下一条指令没有对齐
画乌龟

表 E.6 硬 fault 状态寄存器提供的讯息
可能的原因
DEBUGEVF
因调试事件导致的 fault
1. 断点/观察点事件
2. 在硬 fault 服务例程的执行过程中,没有使能监视器异常(MON_EN=0)也
没有使能停机调试(C_DEBUGEN=0),却执行了 BKPT 指令。缺省时,有些
C 编译器可能会在半主机代码中使用 BKPT 指令。
FORCED
这是 fault“上访”的情况
长江水位查询1. 试图在 SVC/监视器服务例程中执行 SVC/BKPT,或者在其它拥有相同或更
高优先级的服务例程中执行 SVC/BKPT赤壁古诗翻译。
2. 发生了 fault,但是它的服务例程被除能
3. 发生了 fault,但是当前处理器在响应同级或更高优先级的异常
4. 发生了 fault,但是它被掩蔽了
遇的笔顺
VECTBL
取向量失败,
1. 在取向量过程中发生总线 fault
2. 向量表偏移量设置有误

表 E.7 调试 fault 状态寄存器提供的讯息
七月的雨
可能的原因
EXTERNAL
EDBGREQ 信号置为有效
VCATCH
发生了向量抓捕事件
DWTTRAP
发生了 DWT 观察点事件
BKPT
1. 执行了 BKPT 指令
2. FPB 单元产生了断点事件

本文发布于:2023-06-18 23:18:46,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1044696.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:发生   堆栈   指令   错误   地址   返回   向量
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图