博客專欄

EEPW首頁 > 博客 > HardFault 之 INVSTAE 錯(cuò)誤定位(一)

HardFault 之 INVSTAE 錯(cuò)誤定位(一)

發(fā)布人:魚鷹談單片機(jī) 時(shí)間:2021-11-21 來源:工程師 發(fā)布文章

魚鷹在研究 USB 協(xié)議的時(shí)候,發(fā)現(xiàn)有的時(shí)候會(huì)出現(xiàn) hardfault,查看調(diào)用棧卻沒找到可用信息,所以隨手上網(wǎng)搜了一下,發(fā)現(xiàn)剛好這篇文章就是解決一樣的問題,魚鷹通過該方法成功定位了問題,所以分享給大家學(xué)習(xí)一下。后面魚鷹又出現(xiàn)了 INVPC (無效 PC 值)的問題,因?yàn)榇a改動(dòng)較少,猜測(cè)是??臻g不足導(dǎo)致,最終定位也確實(shí)是這樣,但該問題卻無法通過該方法定位,所以不同錯(cuò)誤需要使用不同方法定位,需要注意這一點(diǎn)。

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

最近在STM32做一個(gè)關(guān)于USB音頻的應(yīng)用,調(diào)試過程中一直被一個(gè)隨機(jī)產(chǎn)生的HARD FAULT折磨。問題很奇怪,進(jìn)入HARD FAULT的時(shí)間不定,可能連上USB后幾秒就觸發(fā)HARD FAULT,也可能程序跑幾分鐘甚至幾十分鐘才會(huì)觸發(fā)。盡管感覺問題極有可能來自USB部分代碼,但起初一直沒有辦法找到導(dǎo)致問題的代碼,百度上搜素了一下,但是感覺對(duì)自己沒什么啟發(fā)。經(jīng)過努力,最終找到了問題所在,同時(shí)也學(xué)到了新的東西,現(xiàn)在先介紹下調(diào)試過程。

首先在KEIL下進(jìn)入DEBUG模式運(yùn)行程序,待MCU"死掉"后停止,顯然是卡在HARD FAULT的while(1)里面的。接下來打開FAULT REPORTS窗口:

1.png

可以看到HARD FAULT是由USAGE FAULT導(dǎo)致的,原因是INVSTATE,從“STM32常見Hard+Fault的診斷”的PPT中可以了解到,INVSTATE表示MCU嘗試進(jìn)入ARM狀態(tài),這是非法的,所以產(chǎn)生了USAGE FAULT。此外,PPT里還有這樣的描述:

2.png

另外,在Cortex-M3權(quán)威指南中也有這樣描述:使用BXL的時(shí)候要小心,因?yàn)樗€帶有改變狀態(tài)的功能。因此reg的LSB必須是1,以確保不會(huì)嘗試進(jìn)入ARM狀態(tài),如果忘記置位LSB,則FAULT伺候;同理,你也必須保證送給PC的值必須是奇數(shù)(LSB=1)。

雖然了解了這些,但是還是不能直接產(chǎn)生幫助。接下來,在GOOGLE上找到了一個(gè)名為“keil_hardfault”的PDF文件,是KEIL公司寫的,該文檔的后半部分通過一個(gè)例子介紹了定位產(chǎn)生HARD FAULT之前代碼的方法。通過一些摸索,利用這個(gè)方法我成功找到了問題的位置。

3.png

首先,看到此時(shí)MCU停在了HARD FAULT里,寄存器LR里放存放的是HARD FAULT返回的地址,可以看到此時(shí)LR = 0xFFFF_FFF1,顯然是一個(gè)錯(cuò)誤的指令地址,所以可以判斷是程序跳轉(zhuǎn)到錯(cuò)誤的地址,并且該地址的LSB是0,故觸發(fā)了USAGE FAULT。出錯(cuò)的流程是這樣的:

     [正確代碼] --(出錯(cuò))--> [0xFFFF_FFF0]。接下來需要做的是,找到導(dǎo)致錯(cuò)誤跳轉(zhuǎn)的代碼,

注意到寄存器SP(R13),它指向當(dāng)前使用的棧頂,在MEMORY窗口中輸入SP的值[0x2000A190]:

4.png

根據(jù)CM3內(nèi)核的棧是從上往下生長(zhǎng),并且按寄存器標(biāo)號(hào)從大到小的順序壓入棧,這樣就可以找到“出事”時(shí)的寄存器情況。

在 keil_hardfault.pdf 里的這幅圖可以看得更直觀:

5.png

可以看到跳轉(zhuǎn)到中斷函數(shù)HARD FAULT之前的寄存器情況,例如R0 = 0000_0066,R2 = FFFF_FFFF。

這時(shí)候我們需要關(guān)注的是LR寄存器,其值指向 錯(cuò)誤跳轉(zhuǎn)指令 的下一條指令,可以看到棧里的LR = 0x0800_04FF,那么產(chǎn)生錯(cuò)誤的指令就是 LR = 0x0800_04FC,

在匯編窗口里定位這個(gè)地址,如下圖:

6.png

可以看到指令是BLX R0,而“當(dāng)時(shí)”寄存器R0的值是0x0000_0066,顯然是錯(cuò)誤的跳轉(zhuǎn)地址。找到了指令的地址也就可以看到對(duì)應(yīng)的C語言代碼,大概也能猜到了,問題出在數(shù)組越界了,導(dǎo)致越界的原因就是EPindex,之后在這里加了一些調(diào)試代碼,很容易的就確認(rèn)了問題是由EPindex的值為0導(dǎo)致的。

之后的就不多說了,既然找到了產(chǎn)生HARD FAULT的代碼,也可以松一口氣了。總之這個(gè)方法在處理這個(gè)問題上的確很有效。

*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。

電容的相關(guān)文章:電容屏和電阻屏的區(qū)別


電容器相關(guān)文章:電容器原理


電容相關(guān)文章:電容原理
電容屏相關(guān)文章:電容屏原理
電子負(fù)載相關(guān)文章:電子負(fù)載原理


關(guān)鍵詞: 單片機(jī)

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉