Cortex-M 處理器 hardfault 定位方法和步驟(基於Keil mdk)


一. 問題的產生

Hard fault (硬錯誤,也有譯為硬件錯誤的)是在STM32上編寫程序中所產生的錯誤,造成Hard Fault錯誤的可能原因較多,排除硬件問題,如何在代碼量較大的情況下,快速定位造成的hardfault的問題代碼,就成為比較關鍵的問題。

本文將基於STM32處理器(stm32f091),keil-MDK開發環境,總結hardfault的調試定位方法。在其他Cortex-M0 (m3,m4)內核處理器,和其他開發環境下,也可作為參考。

二. Cortex-M 處理器內核異常中斷簡介

1)錯誤種類

對於Cortex-M內核,架構采用錯誤異常的機制來檢測問題,當核心檢測到一個錯誤時,異常中斷會被觸發,並且核心會跳轉到相應的異常終端處理函數執行,錯誤異常的終端分為以下四種:

HardFault
MemManage
BusFault
UsageFault

其中hardfault為最常見的錯誤類型,並且,在沒有開啟其他異常處理的情況下,默認進入hardfault異常中斷處理函數:

void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}
2) 可能的原因

從軟件角度,產生hardfault的可能原因有:

(1) 數組越界
(2)野指針
(3)未初始化硬件卻開始操作,或無中斷服務函數等

(4)任務堆棧溢出

《ARM Cortex-M0權威指南》中提到,關於 Cortex M0+內核主要有以下幾點引起 HardFault 的原因:


非法存儲器訪問
非對齊數據訪問
從總線返回錯誤
異常處理中的棧被破壞
程序在某些 C 函數中崩潰
意外地試圖切換至 ARM 狀態
在錯誤的優先級上執行系統服務調用指令(SVC)

下面將以一個stm32f091上運行的數組越界代碼為例,具體闡述定位步驟:

產生越界的代碼段:

void StackFlow(void)
{

  int a[3],i;

  for(i=0; i<10000; i++)
  {

    a[i]=1;

  }
}
三. 人工查找hardfault 方法和步驟

方法1.查看寄存器

1)查看fault種類

通過菜單欄Peripherals >Core Peripherals >Fault Reports打開fault reports

 

2)調試定位步驟

查看fault種類有時可能對解決問題並沒有直接幫助,關鍵是如何定位在進入異常中斷前執行的代碼段,步驟如下:

a. 確定當前使用堆棧是MSP還是PSP

異常發生后會把進入異常前的 R0-R3,R12, LR, PC,PSR 寄存器值棧入 Main Stack 或Process Stack(取決於異常發生時使用的哪個棧)。 進入異常后鏈接寄存器 LR 中存放異常返回值 EXC_RETURN, 如果其 bit 2=0 那么用的就是 Main Stack,如果 bit 2=1,那么用的就是 Process Stack。

 

 

 

由下圖可以看出,當前使用的堆棧為PSP

 

 

b.找到異常發生代碼地址

在memory中,定位到堆棧地址:0x200020E0,依據:R0~R3、R12、LR、PC、XPRS 順序,找到LR的值,即第6個寄存器值

 

 

LR = 0x0800632B

PC = 0x08006300

c.Disassembly中,查找定位代碼

在反匯編窗口中點擊右鍵,選中show disassembly at address 。

輸入LR地址:為發生異常后調用的下一條指令的地址,可看到發生異常的為StackFlow()函數

 

 

 

輸入PC地址:可以定位到發生異常的調用語句

 

 

方法2:調試步驟
在仿真狀態下,調出Call Stack Window,可直接跳轉到調用代碼

 

 

四. 程序查找hardfault 方法和步驟

實際環境中,由於測試時產品常常無法連接調試器,故需要代碼來定位目標語句地址,並通過一定手段保存:

在stm32中,需先修改啟動文件startup_stm32f091xc.s:

HardFault_Handler\
PROC
MOVS r0, #4
MOV r1, LR
TST r0, r1
BEQ stacking_used_MSP
MRS R0, PSP
B get_LR_and_branch
stacking_used_MSP
MRS R0, MSP
get_LR_and_branch
MOV R1, LR
IMPORT hard_fault_handler_c
BL hard_fault_handler_c
ENDP
該段代碼會判斷當前堆棧使用的是MSP或PSP,然后將堆棧參數傳遞給hard_fault_handler_c函數,該函數定義如下:

void hard_fault_handler_c(unsigned int * hardfault_args, unsigned lr_value)
{
  unsigned int stacked_r0;
  unsigned int stacked_r1;
  unsigned int stacked_r2;
  unsigned int stacked_r3;
  unsigned int stacked_r12;
  unsigned int stacked_lr;
  unsigned int stacked_pc;
  unsigned int stacked_psr;
  stacked_r0 = ((unsigned long) hardfault_args[0]);
  stacked_r1 = ((unsigned long) hardfault_args[1]);
  stacked_r2 = ((unsigned long) hardfault_args[2]);
  stacked_r3 = ((unsigned long) hardfault_args[3]);
  stacked_r12 = ((unsigned long) hardfault_args[4]);
  stacked_lr = ((unsigned long) hardfault_args[5]);
  stacked_pc = ((unsigned long) hardfault_args[6]);
  stacked_psr = ((unsigned long) hardfault_args[7]);
  while(1)
  {
    printf ("[Hard fault handler]\n");
    printf ("R0 = %x\n", stacked_r0);
    printf ("R1 = %x\n", stacked_r1);
    printf ("R2 = %x\n", stacked_r2);
    printf ("R3 = %x\n", stacked_r3);
    printf ("R12 = %x\n", stacked_r12);
    printf ("Stacked LR = %x\n", stacked_lr);
    printf ("Stacked PC = %x\n", stacked_pc);
    printf ("Stacked PSR = %x\n", stacked_psr);
    printf ("Current LR = %x\n", lr_value);

  for(int i = 10000;i>0;i--)
  for(int j = 1000;j>0;j--);
  }

  //while(1); // endless loop
}

代碼跑死后,會將R0~R3、R12、LR、PC信息通過串口打印,之后根據寄存器信息排查問題代碼即可~

參考資料:

(1)https://blog.csdn.net/geek_liyang/article/details/83510518

(2)https://blog.csdn.net/Maple_Leaf_15/article/details/51443310

(3)https://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html

(4)https://blog.csdn.net/Wang_yf_/article/details/75003611

(5)http://blog.sina.com.cn/s/blog_908da74601011g31.html

(6)https://blog.csdn.net/guozhongwei1/article/details/88418760

(7)https://community.nxp.com/thread/306244

(8)http://www.keil.com/appnotes/files/apnt209.pdf

(9)《ARM Cortex-M0權威指南》Joseph Yiu 著,宋岩 譯


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM