ARM cortex-M 系列 HardFault的诊断

[复制链接]
2072|9
 楼主| keer_zu 发表于 2022-1-26 11:20 | 显示全部楼层 |阅读模式
本帖最后由 keer_zu 于 2022-1-26 11:22 编辑

(转自 aozima)rt-thread问答。

ARM cortex-M上面的fault想必大家都不陌生,我相信还没有谁从来没有出现过fault。
但出现fault后如何排查,相信很多人都是一筹莫展。
在我的项目中加了一些代码,Fault 后可以打印出更多的信息。
  1. #define SCB_CFSR        (*(volatile const unsigned *)0xE000ED28) /* Configurable Fault Status Register */
  2. #define SCB_HFSR        (*(volatile const unsigned *)0xE000ED2C) /* HardFault Status Register */
  3. #define SCB_MMAR        (*(volatile const unsigned *)0xE000ED34) /* MemManage Fault Address register */
  4. #define SCB_BFAR        (*(volatile const unsigned *)0xE000ED38) /* Bus Fault Address Register */

  5. #define SCB_CFSR_MFSR   (*(volatile const unsigned char*)0xE000ED28)  /* Memory-management Fault Status Register */
  6. #define SCB_CFSR_BFSR   (*(volatile const unsigned char*)0xE000ED29)  /* Bus Fault Status Register */
  7. #define SCB_CFSR_UFSR   (*(volatile const unsigned short*)0xE000ED2A) /* Usage Fault Status Register */

  8. static void usage_fault_track(void)
  9. {
  10.     rt_kprintf("usage fault:
  11. ");
  12.     rt_kprintf("SCB_CFSR_UFSR:0x%02X ", SCB_CFSR_UFSR);

  13.     if(SCB_CFSR_UFSR & (1<<0))
  14.     {
  15.         /* [0]:UNDEFINSTR */
  16.         rt_kprintf("UNDEFINSTR ");
  17.     }

  18.     if(SCB_CFSR_UFSR & (1<<1))
  19.     {
  20.         /* [1]:INVSTATE */
  21.         rt_kprintf("INVSTATE ");
  22.     }

  23.     if(SCB_CFSR_UFSR & (1<<2))
  24.     {
  25.         /* [2]:INVPC */
  26.         rt_kprintf("INVPC ");
  27.     }

  28.     if(SCB_CFSR_UFSR & (1<<3))
  29.     {
  30.         /* [3]:NOCP */
  31.         rt_kprintf("NOCP ");
  32.     }

  33.     if(SCB_CFSR_UFSR & (1<<8))
  34.     {
  35.         /* [8]:UNALIGNED */
  36.         rt_kprintf("UNALIGNED ");
  37.     }

  38.     if(SCB_CFSR_UFSR & (1<<9))
  39.     {
  40.         /* [9]:DIVBYZERO */
  41.         rt_kprintf("DIVBYZERO ");
  42.     }

  43.     rt_kprintf("
  44. ");
  45. }

  46. static void bus_fault_track(void)
  47. {
  48.     rt_kprintf("bus fault:
  49. ");
  50.     rt_kprintf("SCB_CFSR_BFSR:0x%02X ", SCB_CFSR_BFSR);

  51.     if(SCB_CFSR_BFSR & (1<<0))
  52.     {
  53.         /* [0]:IBUSERR */
  54.         rt_kprintf("IBUSERR ");
  55.     }

  56.     if(SCB_CFSR_BFSR & (1<<1))
  57.     {
  58.         /* [1]:PRECISERR */
  59.         rt_kprintf("PRECISERR ");
  60.     }

  61.     if(SCB_CFSR_BFSR & (1<<2))
  62.     {
  63.         /* [2]:IMPRECISERR */
  64.         rt_kprintf("IMPRECISERR ");
  65.     }

  66.     if(SCB_CFSR_BFSR & (1<<3))
  67.     {
  68.         /* [3]:UNSTKERR */
  69.         rt_kprintf("UNSTKERR ");
  70.     }

  71.     if(SCB_CFSR_BFSR & (1<<4))
  72.     {
  73.         /* [4]:STKERR */
  74.         rt_kprintf("STKERR ");
  75.     }

  76.     if(SCB_CFSR_BFSR & (1<<7))
  77.     {
  78.         rt_kprintf("SCB->BFAR:%08X
  79. ", SCB_BFAR);
  80.     }
  81.     else
  82.     {
  83.         rt_kprintf("
  84. ");
  85.     }
  86. }

  87. static void mem_manage_fault_track(void)
  88. {
  89.     rt_kprintf("mem manage fault:
  90. ");
  91.     rt_kprintf("SCB_CFSR_MFSR:0x%02X ", SCB_CFSR_MFSR);

  92.     if(SCB_CFSR_MFSR & (1<<0))
  93.     {
  94.         /* [0]:IACCVIOL */
  95.         rt_kprintf("IACCVIOL ");
  96.     }

  97.     if(SCB_CFSR_MFSR & (1<<1))
  98.     {
  99.         /* [1]:DACCVIOL */
  100.         rt_kprintf("DACCVIOL ");
  101.     }

  102.     if(SCB_CFSR_MFSR & (1<<3))
  103.     {
  104.         /* [3]:MUNSTKERR */
  105.         rt_kprintf("MUNSTKERR ");
  106.     }

  107.     if(SCB_CFSR_MFSR & (1<<4))
  108.     {
  109.         /* [4]:MSTKERR */
  110.         rt_kprintf("MSTKERR ");
  111.     }

  112.     if(SCB_CFSR_MFSR & (1<<7))
  113.     {
  114.         /* [7]:MMARVALID */
  115.         rt_kprintf("SCB->MMAR:%08X
  116. ", SCB_MMAR);
  117.     }
  118.     else
  119.     {
  120.         rt_kprintf("
  121. ");
  122.     }
  123. }

  124. static void hard_fault_track(void)
  125. {
  126.     if(SCB_HFSR & (1UL<<1))
  127.     {
  128.         /* [1]:VECTBL, Indicates hard fault is caused by failed vector fetch. */
  129.         rt_kprintf("failed vector fetch
  130. ");
  131.     }

  132.     if(SCB_HFSR & (1UL<<30))
  133.     {
  134.         /* [30]:FORCED, Indicates hard fault is taken because of bus fault,
  135.                         memory management fault, or usage fault. */
  136.         if(SCB_CFSR_BFSR)
  137.         {
  138.             bus_fault_track();
  139.         }

  140.         if(SCB_CFSR_MFSR)
  141.         {
  142.             mem_manage_fault_track();
  143.         }

  144.         if(SCB_CFSR_UFSR)
  145.         {
  146.             usage_fault_track();
  147.         }
  148.     }

  149.     if(SCB_HFSR & (1UL<<31))
  150.     {
  151.         /* [31]:DEBUGEVT, Indicates hard fault is triggered by debug event. */
  152.         rt_kprintf("debug event
  153. ");
  154.     }
  155. }

  156. /**
  157. * fault exception handling
  158. */
  159. void rt_hw_hard_fault_exception(struct stack_context* contex)
  160. {
  161.     rt_kprintf("psr: 0x%08x
  162. ", contex->psr);
  163.     rt_kprintf(" pc: 0x%08x
  164. ", contex->pc);
  165.     rt_kprintf(" lr: 0x%08x
  166. ", contex->lr);
  167.     rt_kprintf("r12: 0x%08x
  168. ", contex->r12);
  169.     rt_kprintf("r03: 0x%08x
  170. ", contex->r3);
  171.     rt_kprintf("r02: 0x%08x
  172. ", contex->r2);
  173.     rt_kprintf("r01: 0x%08x
  174. ", contex->r1);
  175.     rt_kprintf("r00: 0x%08x
  176. ", contex->r0);

  177.     hard_fault_track();

  178.     rt_kprintf("hard fault on thread: %s
  179. ", rt_current_thread->name);
  180. #ifdef RT_USING_FINSH
  181.     list_thread();
  182. #endif
  183.     while (1);
  184. }

 楼主| keer_zu 发表于 2022-1-26 11:23 | 显示全部楼层
再写了两个测试代码,以手动触发fault。
  1. void div0_test(void)
  2. {
  3.     volatile int * SCB_CCR = (volatile int *)0xE000ED14; // SCB->CCR
  4.     int x,y,z;

  5.     *SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */

  6.     x = 10;
  7.     y = 0;
  8.     z = x / y;
  9.     rt_kprintf("z:%d
  10. ", z);
  11. }

  12. void unalign_test(void)
  13. {
  14.     volatile int * SCB_CCR = (volatile int *)0xE000ED14; // SCB->CCR
  15.     volatile int * p;
  16.     volatile int value;

  17.     *SCB_CCR |= (1 << 3); /* bit3: UNALIGN_TRP. */

  18.     p = (int *)0x00;
  19.     value = *p;
  20.     rt_kprintf("addr:0x%02X value:0x%08X
  21. ", (int)p, value);

  22.     p = (int *)0x04;
  23.     value = *p;
  24.     rt_kprintf("addr:0x%02X value:0x%08X
  25. ", (int)p, value);

  26.     p = (int *)0x03;
  27.     value = *p;
  28.     rt_kprintf("addr:0x%02X value:0x%08X
  29. ", (int)p, value);
  30. }

  31. #ifdef  RT_USING_FINSH
  32. #include <finsh.h>
  33. FINSH_FUNCTION_EXPORT(div0_test, div0_test)
  34. FINSH_FUNCTION_EXPORT(unalign_test, unalign_test)
  35. #endif /* RT_USING_FINSH */
 楼主| keer_zu 发表于 2022-1-26 11:23 | 显示全部楼层
测试访问末授权区域
  1. finsh>>int * p //声明一个指针变量
  2.         0, 0x00000000
  3. finsh>>p = 0xDFFFFFF0 // 指向片上外设区结束处,一般不可能用完,所以此处一般不可访问。
  4.         -536870928, 0xdffffff0
  5. finsh>>*p // 读取指针处数据
  6. psr: 0x01000000
  7. pc: 0x00000e3e
  8. lr: 0x0000451d
  9. r12: 0x00000000
  10. r03: 0x00000000
  11. r02: 0x1fff0180
  12. r01: 0x1fff0814
  13. r00: 0xdffffff0
  14. bus fault:
  15. SCB_CFSR_BFSR:0x82 PRECISERR SCB->BFAR:DFFFFFF0
  16. hard fault on thread: tshell
  17. thread  pri  status      sp     stack size max used   left tick  error
  18. -------- ---- ------- ---------- ---------- ---------- ---------- ---
  19. tidle    0x1f ready   0x00000040 0x00000100 0x00000060 0x00000015 000
  20. tshell   0x14 ready   0x00000088 0x00000400 0x00000218 0x00000009 000
 楼主| keer_zu 发表于 2022-1-26 11:24 | 显示全部楼层
非对齐访问测试
  1. finsh>>unalign_test()
  2. addr:0x00 value:0x20001B80
  3. addr:0x04 value:0x0800DE81
  4. psr: 0x21000000
  5. r00: 0x00000000
  6. r01: 0x40013800
  7. r02: 0x20000690
  8. r03: 0x00000000
  9. r04: 0x00000003
  10. r05: 0xe000ed14
  11. r06: 0xdeadbeef
  12. r07: 0x20002678
  13. r08: 0xdeadbeef
  14. r09: 0xdeadbeef
  15. r10: 0xdeadbeef
  16. r11: 0xdeadbeef
  17. r12: 0x08000a95
  18. lr: 0x08002eb5
  19. pc: 0x08000386
  20. usage fault:
  21. SCB_CFSR_UFSR:0x100 UNALIGNED
  22. hard fault on thread: tshell
  23. thread  pri  status      sp     stack size max used   left tick  error
  24. -------- ---- ------- ---------- ---------- ---------- ---------- ---
  25. tidle    0x1f ready   0x00000040 0x00000100 0x0000005c 0x00000009 000
  26. tshell   0x14 ready   0x00000088 0x00000800 0x000001b0 0x0000000a 000
  27. led      0x14 suspend 0x00000078 0x00000200 0x00000078 0x00000005 000
 楼主| keer_zu 发表于 2022-1-26 11:24 | 显示全部楼层
除零异常测试
  1. finsh>>div0_test()
  2. psr: 0x41000000
  3. r00: 0x00000010
  4. r01: 0x08000337
  5. r02: 0x20000bb7
  6. r03: 0x20000130
  7. r04: 0xe000ed14
  8. r05: 0x00000000
  9. r06: 0xdeadbeef
  10. r07: 0x0000000a
  11. r08: 0xdeadbeef
  12. r09: 0xdeadbeef
  13. r10: 0xdeadbeef
  14. r11: 0xdeadbeef
  15. r12: 0x00000000
  16. lr: 0x08008d91
  17. pc: 0x08000348
  18. usage fault:
  19. SCB_CFSR_UFSR:0x200 DIVBYZERO
  20. hard fault on thread: tshell
  21. thread  pri  status      sp     stack size max used   left tick  error
  22. -------- ---- ------- ---------- ---------- ---------- ---------- ---
  23. tidle    0x1f ready   0x00000058 0x00000100 0x0000005c 0x0000000f 000
  24. tshell   0x14 ready   0x00000088 0x00000800 0x000001b0 0x0000000a 000
  25. led      0x14 suspend 0x00000078 0x00000200 0x00000078 0x00000005 000
 楼主| keer_zu 发表于 2022-1-26 11:24 | 显示全部楼层
问题追踪
上面测试出了问题,是我们人为设置的故障,但在平时调试中出了问题如何追综呢?
以非对齐访问为例,开发环境使用MDK。

进入JTAG仿真状态,并触发非对齐异常。
此时串口会打印出异常时的寄存器值,此时停止仿真器发现程序停在rt_hw_hard_fault_exception中。

根据上面打印出来的寄存器,提取出关键值是 pc: 0x08000386
我们在MDK的command窗口中输入 pc = 0x08000386
 楼主| keer_zu 发表于 2022-1-26 11:24 | 显示全部楼层
 楼主| keer_zu 发表于 2022-1-26 11:25 | 显示全部楼层
可以把PC指针临时设回问题发生时的场景,我们看到出现问题的指令是
  1.    239:     p = (int *)0x03;
  2. 0x08000384 2403      MOVS     r4,#0x03
  3.    240:     value = *p;
  4. 0x08000386 6820      LDR      r0,[r4,#0x00]
  5. 0x08000388 9000      STR      r0,[sp,#0x00]
 楼主| keer_zu 发表于 2022-1-26 11:25 | 显示全部楼层
分析 #386 这条指令从 R4+0 的问题读取4字节到R0中,
先前打印出的R4的值为 r04: 0x00000003
因此可以确定为这是因为地址不对齐造成的。

9874161f0bf29a89c3.png
 楼主| keer_zu 发表于 2022-1-26 11:26 | 显示全部楼层
本帖最后由 keer_zu 于 2022-1-26 11:28 编辑

当然,具体情况要具体分析,有时候某个步骤出现问题并不会马上崩溃,
而是过一段时间以后才出问题,因此要结合上下文综合分析。
比如上面这个案例真正有问题的指令是 0x08000384。

根据以上的案例,并结合实际调试经验,相信大家可以更快地找出问题。

下面是ST研究会上面的PPT,供参考。
stm32Hard Fault的诊断.pdf
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1488

主题

12949

帖子

55

粉丝
快速回复 在线客服 返回列表 返回顶部