1.概述
客戶在使用 STM32G070 的時候,KEIL MDK 為編譯工具,當編譯優化選項設置為Level0 的時候,程序會出現 Hard Fault 異常,而當編譯優化選項設置為 Level1 的時候,則程序運行正常。
表面上看,這似乎是 KEIL MDK 的問題,通過分析,導致這個問題的本質原因是內存地址沒有對齊引起的,下面章節將詳細分析該問題的來龍去脈以及解決方法。
2.問題描述與分析
根據客戶的反饋,引起問題的代碼很簡單,客戶定義了幾個全局數組,在主程序中訪問這幾個數組就會出現 Hard Fault 異常,參考代碼如下。
把客戶提供的代碼片段移植到 NUCLEO-G070RB 開發板上,問題很容易就復現了,代碼本身功能簡單,寫法上也沒有錯誤,所以從代碼片段本身上看,無法確定問題出在哪里,通過 KEIL 調試器,在匯編窗口單步調試下,最終發現導致 HardFault 異常的語句為下圖所示語句。
根據單步調試得知出現問題的語句為 LDR 指令,參考 Cortex M0 編程手冊 PM0223 得知 LDR 指令的作用是從內存地址中加載一個 WORD 數據到目的寄存器 Rt 中,其中內存地址根據 Rn 或者 SP 寄存器的值以及立即數 imm 得到。
根據指令的描述,使用 LDR 指令的時候,通過 Rn 和 imm 計算得到的內存地址必須是讀取字節數的倍數,LDR 每次讀取一個 WORD,所以使用 LDR 指令時,內存地址必須 4字節對齊。如果地址沒有對齊,則會導致 HardFault 異常。
結合 LDR 指令的描述,在調試狀態下,通過查看寄存器值,圖 2 出錯語句中根據 Rn和 imm 計算得到的內存地址為 R0=0x2000000B,imm=4 所以內存地址為 0x2000000F,很顯然這個地址不是 4 字節對齊的。
而當我們改變編譯優化選項為 Level1 時,得到的內存地址為R0=0x20000000,imm=0x04 顯然這個地址是按照 4 字節對齊的,所以這種情況下是不會出現 HardFault 異常的,印證了客戶的問題現象。
3.問題解決
通過上一節的分析,明確了導致該問題的本質原因是內存地址沒有對齊,這個內存地址實際上是代碼中定義的全局變量 g_curPlaySound_app 指向的地址,也就是全局數組變量 SoundFile 的地址,在編譯器不同的優化選項下,分配給 SoundFile 變量的地址是不一樣的,在本案例中,編譯優化選項 Level0 條件下,SoundFile 分配的地址沒有按照WORD 對齊,而在優化選項 Level1 條件下,SoundFile 分配的地址是 WORD 對齊,所以在兩種優化選項下,出現了不一樣的運行結果。
所以要保證程序不出錯,當通過指針訪問變量的時候,要確保指針指向的地址是 4 字節對齊的,在 Keil 環境下,可以通過__attribute__((aligned (4))) 關鍵字實現,如下圖所示,通過該關鍵字,對齊了地址,也就不會出現 HardFault 異常了。
圖6 確保地址對齊
4.總結
地址未對齊是嵌入式系統中容易忽視的一個細節,忽視這點往往會導致一些奇怪的問題,所以在開發過程中,注意這些細節還是很有必要的。
來源:STM32單片機
審核編輯:湯梓紅
-
單片機
+關注
關注
6037文章
44562瀏覽量
635749 -
嵌入式系統
+關注
關注
41文章
3593瀏覽量
129512 -
STM32
+關注
關注
2270文章
10903瀏覽量
356271 -
MDK
+關注
關注
4文章
209瀏覽量
32079
發布評論請先 登錄
相關推薦
評論