新闻  |   论坛  |   博客  |   在线研讨会
HardFault 之 INVSTAE 错误定位(一)
鱼鹰谈单片机 | 2021-11-21 17:33:52    阅读:1235   发布文章

鱼鹰在研究 USB 协议的时候,发现有的时候会出现 hardfault,查看调用栈却没找到可用信息,所以随手上网搜了一下,发现刚好这篇文章就是解决一样的问题,鱼鹰通过该方法成功定位了问题,所以分享给大家学习一下。后面鱼鹰又出现了 INVPC (无效 PC 值)的问题,因为代码改动较少,猜测是栈空间不足导致,最终定位也确实是这样,但该问题却无法通过该方法定位,所以不同错误需要使用不同方法定位,需要注意这一点。

---------------------------------

最近在STM32做一个关于USB音频的应用,调试过程中一直被一个随机产生的HARD FAULT折磨。问题很奇怪,进入HARD FAULT的时间不定,可能连上USB后几秒就触发HARD FAULT,也可能程序跑几分钟甚至几十分钟才会触发。尽管感觉问题极有可能来自USB部分代码,但起初一直没有办法找到导致问题的代码,百度上搜素了一下,但是感觉对自己没什么启发。经过努力,最终找到了问题所在,同时也学到了新的东西,现在先介绍下调试过程。

首先在KEIL下进入DEBUG模式运行程序,待MCU"死掉"后停止,显然是卡在HARD FAULT的while(1)里面的。接下来打开FAULT REPORTS窗口:

1.png

可以看到HARD FAULT是由USAGE FAULT导致的,原因是INVSTATE,从“STM32常见Hard+Fault的诊断”的PPT中可以了解到,INVSTATE表示MCU尝试进入ARM状态,这是非法的,所以产生了USAGE FAULT。此外,PPT里还有这样的描述:

2.png

另外,在Cortex-M3权威指南中也有这样描述:使用BXL的时候要小心,因为它还带有改变状态的功能。因此reg的LSB必须是1,以确保不会尝试进入ARM状态,如果忘记置位LSB,则FAULT伺候;同理,你也必须保证送给PC的值必须是奇数(LSB=1)。

虽然了解了这些,但是还是不能直接产生帮助。接下来,在GOOGLE上找到了一个名为“keil_hardfault”的PDF文件,是KEIL公司写的,该文档的后半部分通过一个例子介绍了定位产生HARD FAULT之前代码的方法。通过一些摸索,利用这个方法我成功找到了问题的位置。

3.png

首先,看到此时MCU停在了HARD FAULT里,寄存器LR里放存放的是HARD FAULT返回的地址,可以看到此时LR = 0xFFFF_FFF1,显然是一个错误的指令地址,所以可以判断是程序跳转到错误的地址,并且该地址的LSB是0,故触发了USAGE FAULT。出错的流程是这样的:

     [正确代码] --(出错)--> [0xFFFF_FFF0]。接下来需要做的是,找到导致错误跳转的代码,

注意到寄存器SP(R13),它指向当前使用的栈顶,在MEMORY窗口中输入SP的值[0x2000A190]:

4.png

根据CM3内核的栈是从上往下生长,并且按寄存器标号从大到小的顺序压入栈,这样就可以找到“出事”时的寄存器情况。

在 keil_hardfault.pdf 里的这幅图可以看得更直观:

5.png

可以看到跳转到中断函数HARD FAULT之前的寄存器情况,例如R0 = 0000_0066,R2 = FFFF_FFFF。

这时候我们需要关注的是LR寄存器,其值指向 错误跳转指令 的下一条指令,可以看到栈里的LR = 0x0800_04FF,那么产生错误的指令就是 LR = 0x0800_04FC,

在汇编窗口里定位这个地址,如下图:

6.png

可以看到指令是BLX R0,而“当时”寄存器R0的值是0x0000_0066,显然是错误的跳转地址。找到了指令的地址也就可以看到对应的C语言代码,大概也能猜到了,问题出在数组越界了,导致越界的原因就是EPindex,之后在这里加了一些调试代码,很容易的就确认了问题是由EPindex的值为0导致的。

之后的就不多说了,既然找到了产生HARD FAULT的代码,也可以松一口气了。总之这个方法在处理这个问题上的确很有效。

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
flyingfish9997  2024-05-16 19:52:43 

大哥为什么定位到0x0800_04FC,是因为字节对齐吗

推荐文章
最近访客