對于其他的stm32芯片或者其他ARM Cortex-M芯片,其實(shí)解決方法都相通。建議先完整閱讀了本文之后,再對照著(zhù)你所遇到問(wèn)題的現象進(jìn)行調試。
1.基礎知識
在A(yíng)RM Cortex-M系列處理器中,有若干個(gè)系統異常專(zhuān)用于 fault 處理。CM3 中的 Faults 可分為以下幾類(lèi):
(1).總線(xiàn) faults;
(2).存儲器管理 faults;
(3).用法 faults;
(4).硬 fault;
1.1.總線(xiàn) faults
當 AHB 接口上正在傳送數據時(shí),如果回復了一個(gè)錯誤信號(error response),則會(huì )產(chǎn)生總線(xiàn)faults,產(chǎn)生的場(chǎng)合可以是:
(1).取指,通常被稱(chēng)作“預取流產(chǎn)”(prefetch abort);
(2).數據讀/寫(xiě),通常被稱(chēng)作“數據流產(chǎn)”(data abort);
在 CM3 中,執行如下動(dòng)作時(shí),如果地址有誤,亦會(huì )觸發(fā)總線(xiàn)異常:
(1).中斷處理起始階段的堆棧 PUSH 動(dòng)作。此時(shí)若發(fā)生總線(xiàn) fault,則稱(chēng)為“入棧錯誤”;
(2).中斷處理收尾階段的堆棧 POP 動(dòng)作。此時(shí)若發(fā)生總線(xiàn) fault,則稱(chēng)為“出棧錯誤”;
(3).在處理器啟動(dòng)中斷服務(wù)序列(sequence)后讀取向量時(shí)。這是一種極度罕見(jiàn)的特殊情況,被歸類(lèi)為硬 fault。
總線(xiàn) fault 狀態(tài)寄存器(BFSR),地址:0xE000_ED29,BFSR的各個(gè)位的定義如下:
Snipaste_2020-12-11_14-13-31
1.2.存儲器管理 faults
存儲器管理 faults 多與 MPU 有關(guān),其誘因常常是某次訪(fǎng)問(wèn)觸犯了 MPU 設置的保護規范。另外,某些非法訪(fǎng)問(wèn),例如,在不可執行的存儲器區域試圖取指,也會(huì )觸發(fā)一個(gè) MemManage fault,而且在這種場(chǎng)合下,即使沒(méi)有 MPU 也會(huì )觸發(fā) MemMange fault。MemManage faults 的常見(jiàn)誘因如下所示:
(1).訪(fǎng)問(wèn)了所有 MPU regions 覆蓋范圍之外的地址;
(2).訪(fǎng)問(wèn)了沒(méi)有存儲器與之對應的空地址;
(3).往只讀 region 寫(xiě)數據;
(4).用戶(hù)級下訪(fǎng)問(wèn)了只允許在特權級下訪(fǎng)問(wèn)的地址;
存儲器管理 fault 狀態(tài)寄存器(MFSR),地址:0xE000_ED28,MFSR的各個(gè)位的定義如下:
Snipaste_2020-12-11_14-19-19
1.3.用法 faults
用法 faults 發(fā)生的場(chǎng)合可以是:
(1).執行了協(xié)處理器指令。Cortex-M3 本身并不支持協(xié)處理器,但是通過(guò) fault 異常機制,可以建立一套“軟件模擬”的機制,來(lái)執行一段程序模擬協(xié)處理器的功能,從而可以方便地在其它 Cortex 處理器間移植。
(2).執行了未定義的指令。同上一點(diǎn)的道理,亦可以軟件模擬未定義指令的功能。
(3).嘗試進(jìn)入 ARM 狀態(tài)。因為 CM3 不支持 ARM 狀態(tài),所以用法 fault 會(huì )在切換時(shí)產(chǎn)生。軟件可以利用此機制來(lái)測試某處理器是否支持 ARM 狀態(tài)。
(4).無(wú)效的中斷返回(LR 中包含了無(wú)效/錯誤的值);
(5).使用多重加載/存儲指令時(shí),地址沒(méi)有對齊。
另外,如果需要嚴格要求程序的質(zhì)量,還可以讓 CM3 在遇到除數為零的時(shí)候,以及遇到未對齊訪(fǎng)問(wèn)的時(shí)候也產(chǎn)生用法 fault。在 NVIC 中有兩個(gè)控制位分別與它們對應。通過(guò)設置這兩個(gè)控制位,就可以激活它們。
用法 fault 狀態(tài)寄存器(UFSR),地址:0xE000_ED2A,UFSR的各個(gè)位的定義如下:
Snipaste_2020-12-11_14-24-49
1.4.硬 fault
硬 fault 是上文討論的總線(xiàn) fault、存儲器管理 fault 以及用法 fault 上訪(fǎng)的結果。如果這些 fault 的服務(wù)例程無(wú)法執行,它們就會(huì )成為“硬傷” ——上訪(fǎng)( escalation)成硬 fault。另外,在取向量(異常處理時(shí)對異常向量表的讀?。r(shí)產(chǎn)生的總線(xiàn) fault 也按硬 fault 處理。在 NVIC中有一個(gè)硬 fault 狀態(tài)寄存器(HFSR),它指出產(chǎn)生硬 fault 的原因。如果不是由于取向量造成的,則硬 fault 服務(wù)例程必須檢查其它的 fault 狀態(tài)寄存器,以最終決定是誰(shuí)上訪(fǎng)的。
硬 fault 狀態(tài)寄存器(HFSR),地址:0xE000_ED2C,HFSR的各個(gè)位的定義如下:
Snipaste_2020-12-11_14-29-38
2.UsageFault INVPC置1解決過(guò)程
最近在使用RTOS增加DMA驅動(dòng)時(shí),在對內存到設備和設備到內存的DMA傳輸測試時(shí),出現了UsageFault,并且UFSR中的INVPC置1了。最開(kāi)始,單獨測試DMA發(fā)送是沒(méi)有問(wèn)題的,但是DMA發(fā)送和接收一起測試時(shí),就會(huì )出現UsageFault(INVPC置1)。這個(gè)異常不太好定位出現問(wèn)題的具體位置,因此就檢查DMA驅動(dòng),并且逐步調試吧。最終,DMA驅動(dòng)檢查和修改好了,仍然出現UsageFault,實(shí)在沒(méi)法了,還是從為什么會(huì )出現UsageFault(INVPC置1)開(kāi)始分析吧。
2.1.出現UsageFault(INVPC置1)的原因
如果LR中的EXC_RETURN不是合法的值(合法值見(jiàn)下圖,包括企圖返回ARM狀態(tài)),則引起用法fault。如果用法fault被除能,也上訪(fǎng)成硬fault。此時(shí),用法Fault狀態(tài)寄存器(UFSR,地址:0xE000_ED2A)中的INVPC位(位偏移:2),或者是INVSTATE位(位偏移:1)置位。
Snipaste_2020-12-11_15-13-23
上面就是出現該異常的文字分析了。
2.2.UsageFault(INVPC置1)的解決過(guò)程
因為該異常是異常響應期間才可能出現的異常(<
我這里是每次開(kāi)始DMA測試之后,就進(jìn)入UsageFault異常,并且我的系統中目前就打開(kāi)了SysTick,PendSV,DMA1_STREAM5,DMA1_STREAM6,NMI,HardFault,MemFault,BusFault,UsageFault這些異常和中斷。因此我在開(kāi)始DMA測試的時(shí)候打一個(gè)斷點(diǎn),在程序運行到DMA測試開(kāi)始的斷點(diǎn)處時(shí),再在DMA1_STREAM5,DMA1_STREAM6,NMI,HardFault,MemFault,BusFault,UsageFault這些函數的入口打斷點(diǎn),在PendSV的返回處打斷點(diǎn),SysTick就暫時(shí)先不管。然后全力運行,發(fā)現每次都會(huì )在PendSV的異常返回斷點(diǎn)處停留(因為任務(wù)切換嘛)總共在PendSV的斷點(diǎn)處停留了大概7到8次,就進(jìn)入到了UsageFault。
有了上面步驟的鋪墊,先去除中斷和異常中的斷點(diǎn),還是先在DMA測試開(kāi)始處打斷點(diǎn),等運行到DMA測試開(kāi)始處,再在上述的中斷和異常相關(guān)位置打斷點(diǎn)。接下來(lái)我就慢慢的調試,在開(kāi)始DMA測試之后,全速運行,在退出PendSV異常時(shí),執行單步運行到下一步,重復7到8次,從PendSV就進(jìn)入了UsageFault,在這7到8次中,我看在退出PendSV時(shí),LR寄存器中的值都是0xFFFFFFFD,是合法的啊,當時(shí)仔細一想,有可能是退出異常時(shí)硬件再將堆棧中的PC賦值給PC時(shí)出問(wèn)題,導致進(jìn)入了UsageFault。果不其然,在PenSV退出之前,我查看每個(gè)PSP(0xFFFFFFFD:返回線(xiàn)程模式,并使用線(xiàn)程堆棧)對應內存數值,能夠正常退出PendSV的寄存器和PSP堆棧內容如下圖所示:
Snipaste_2020-12-11_16-38-20
Snipaste_2020-12-11_16-43-28
進(jìn)入UsageFault異常之前的寄存器和PSP堆棧內容如下圖所示:
Snipaste_2020-12-11_16-45-35
Snipaste_2020-12-11_16-47-17
此處堆棧中的內容,明顯的0x20003D94堆棧中的PC值是有問(wèn)題的,我的程序是燒寫(xiě)到flash中的PC地址應該是08xxxxxx,然而現在堆棧中的PC地址是0x20003DA8,這個(gè)地址是SRAM中的地址,SRAM存儲的數據而不是代碼,出現這個(gè)問(wèn)題的原因,猜想一下,應該就是任務(wù)堆棧溢出導致,當我增加任務(wù)堆棧的大小之后,哈哈,程序正常運行,世界是如此美好。
3.調試小結
3.1.解決過(guò)程小結
其實(shí)上面的步驟2,是我自己的一個(gè)調試解決問(wèn)題的過(guò)程,這里給大家提供一個(gè)比較直接的解決方式。在開(kāi)始運行程序之前,直接在UsageFault異常入口函數中打一個(gè)斷點(diǎn),然后全速運行程序,等程序停止在UsageFault異常函數的斷點(diǎn)處時(shí),需要注意以下幾點(diǎn):
(1).如果LR是合法值,那么根據LR判斷退出異常時(shí)使用的堆棧,然后在Memory查看窗口中,查看堆棧中R0,R1,R2,R3,R12,LR,PC,xPSR這些寄存器的值,根據這些寄存器的值,判斷是否是堆棧溢出導致該異常發(fā)生;如果不是堆棧溢出導致該異常發(fā)生,那么就要根據PC值,在匯編窗口中跳轉到PC值對應的代碼處,分析導致異常發(fā)生的原因;
(2).如果LR不是合法值,就要分析下你的代碼中,有哪些地方修改過(guò)LR的值,確保修改的值要是合法的。
3.2.關(guān)于UsageFault 如何才能讓INVPC置1
(1).在退出異?;蛑袛鄷r(shí),執行BX LR時(shí),LR的值是非法的,此時(shí)就會(huì )觸發(fā)UsageFault異常,并且INVPC置1。
(2).在退出異?;蛑袛鄷r(shí),執行BX LR時(shí),LR的值是合法的,但是退出異常之后要使用的堆棧中,堆棧里面的PC值是有問(wèn)題的,此時(shí)就有可能觸發(fā)UsageFault異常,并且INVPC置1。
對于上面的兩點(diǎn)的模擬其實(shí)也比較好做,在PendSV或者其他異常的退出的地方打一個(gè)斷點(diǎn),然后手動(dòng)修改LR或者堆棧中PC的值,就能觸發(fā)UsageFault異常,并且INVPC置1。這里注意一下,修改堆棧中PC的值,我這里測試時(shí)候,設置PC值為其他值可能引起其他的異常,貌似修改PC的值為RAM中數據區的地址才會(huì )出現該異常,不太清楚為什么會(huì )這樣,可能是數據區是沒(méi)有執行代碼的權限,因此出異常吧,不太確定,有知道的朋友,歡迎留言講解。
審核編輯:劉清
-
ARM
+關(guān)注
關(guān)注
134文章
8726瀏覽量
363108 -
存儲器
+關(guān)注
關(guān)注
38文章
7224瀏覽量
162483 -
中斷處理
+關(guān)注
關(guān)注
0文章
94瀏覽量
10908 -
Cortex-M
+關(guān)注
關(guān)注
2文章
224瀏覽量
29601 -
STM32芯片
+關(guān)注
關(guān)注
0文章
38瀏覽量
4313
原文標題:ARM Cortex-M 異常-HardFault(UsageFault) INVPC置1解決過(guò)程
文章出處:【微信號:嵌入式那些事,微信公眾號:嵌入式那些事】歡迎添加關(guān)注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論