HardFault 之 INVSTAE 錯誤定位(一)
魚鷹在研究 USB 協議的時候,發現有的時候會出現 hardfault,查看調用棧卻沒找到可用信息,所以隨手上網搜了一下,發現剛好這篇文章就是解決一樣的問題,魚鷹通過該方法成功定位了問題,所以分享給大家學習一下。后面魚鷹又出現了 INVPC (無效 PC 值)的問題,因為代碼改動較少,猜測是棧空間不足導致,最終定位也確實是這樣,但該問題卻無法通過該方法定位,所以不同錯誤需要使用不同方法定位,需要注意這一點。
---------------------------------
最近在STM32做一個關于USB音頻的應用,調試過程中一直被一個隨機產生的HARD FAULT折磨。問題很奇怪,進入HARD FAULT的時間不定,可能連上USB后幾秒就觸發HARD FAULT,也可能程序跑幾分鐘甚至幾十分鐘才會觸發。盡管感覺問題極有可能來自USB部分代碼,但起初一直沒有辦法找到導致問題的代碼,百度上搜素了一下,但是感覺對自己沒什么啟發。經過努力,最終找到了問題所在,同時也學到了新的東西,現在先介紹下調試過程。
首先在KEIL下進入DEBUG模式運行程序,待MCU"死掉"后停止,顯然是卡在HARD FAULT的while(1)里面的。接下來打開FAULT REPORTS窗口:
可以看到HARD FAULT是由USAGE FAULT導致的,原因是INVSTATE,從“STM32常見Hard+Fault的診斷”的PPT中可以了解到,INVSTATE表示MCU嘗試進入ARM狀態,這是非法的,所以產生了USAGE FAULT。此外,PPT里還有這樣的描述:
另外,在Cortex-M3權威指南中也有這樣描述:使用BXL的時候要小心,因為它還帶有改變狀態的功能。因此reg的LSB必須是1,以確保不會嘗試進入ARM狀態,如果忘記置位LSB,則FAULT伺候;同理,你也必須保證送給PC的值必須是奇數(LSB=1)。
雖然了解了這些,但是還是不能直接產生幫助。接下來,在GOOGLE上找到了一個名為“keil_hardfault”的PDF文件,是KEIL公司寫的,該文檔的后半部分通過一個例子介紹了定位產生HARD FAULT之前代碼的方法。通過一些摸索,利用這個方法我成功找到了問題的位置。
首先,看到此時MCU停在了HARD FAULT里,寄存器LR里放存放的是HARD FAULT返回的地址,可以看到此時LR = 0xFFFF_FFF1,顯然是一個錯誤的指令地址,所以可以判斷是程序跳轉到錯誤的地址,并且該地址的LSB是0,故觸發了USAGE FAULT。出錯的流程是這樣的:
[正確代碼] --(出錯)--> [0xFFFF_FFF0]。接下來需要做的是,找到導致錯誤跳轉的代碼,
注意到寄存器SP(R13),它指向當前使用的棧頂,在MEMORY窗口中輸入SP的值[0x2000A190]:
根據CM3內核的棧是從上往下生長,并且按寄存器標號從大到小的順序壓入棧,這樣就可以找到“出事”時的寄存器情況。
在 keil_hardfault.pdf 里的這幅圖可以看得更直觀:
可以看到跳轉到中斷函數HARD FAULT之前的寄存器情況,例如R0 = 0000_0066,R2 = FFFF_FFFF。
這時候我們需要關注的是LR寄存器,其值指向 錯誤跳轉指令 的下一條指令,可以看到棧里的LR = 0x0800_04FF,那么產生錯誤的指令就是 LR = 0x0800_04FC,
在匯編窗口里定位這個地址,如下圖:
可以看到指令是BLX R0,而“當時”寄存器R0的值是0x0000_0066,顯然是錯誤的跳轉地址。找到了指令的地址也就可以看到對應的C語言代碼,大概也能猜到了,問題出在數組越界了,導致越界的原因就是EPindex,之后在這里加了一些調試代碼,很容易的就確認了問題是由EPindex的值為0導致的。
之后的就不多說了,既然找到了產生HARD FAULT的代碼,也可以松一口氣了。總之這個方法在處理這個問題上的確很有效。
*博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。
電容的相關文章:電容屏和電阻屏的區別
電容器相關文章:電容器原理
電容相關文章:電容原理 電容屏相關文章:電容屏原理 電子負載相關文章:電子負載原理