定位產生HARD FAULT之前的代碼

轉自:https://www.cnblogs.com/Ilmen/p/3356147.html

     最近在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的代碼,也可以鬆一口氣了。總之這個方法在處理這個問題上的確很有效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章