上传个文档:《HardFault的诊断》 自问自答

[复制链接]
32643|35
 楼主| aozima 发表于 2012-11-30 00:23 | 显示全部楼层 |阅读模式
本帖最后由 aozima 于 2012-11-30 00:26 编辑

上次ST研究中,有位ST的漂亮女工程师讲了关于hardfault的排查,讲得非常好。
以为光盘中会有PPT,今天搜索光盘结果发现没有。
不知道这个文档是否可以提供?
 楼主| aozima 发表于 2012-11-30 00:25 | 显示全部楼层
刚网上搜了一番,在http://st.**.com/down/index.php?id=120找到了。
上传之。
HardFault的诊断.pdf (226.79 KB, 下载次数: 2216)
acgean 发表于 2012-11-30 08:57 | 显示全部楼层
嗯, 好东西, 要进一步写好 Cortrx M3 的程序, 这一块东西必须得学习掌握. 感谢楼主
 楼主| aozima 发表于 2012-12-2 21:58 | 显示全部楼层
本帖最后由 aozima 于 2012-12-2 22:07 编辑

项目中加了一些代码,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:\n");
  11.     rt_kprintf("SCB_CFSR_UFSR:0x%02X ", SCB_CFSR_UFSR);

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

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

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

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

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

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

  42.     rt_kprintf("\n");
  43. }

  44. static void bus_fault_track(void)
  45. {
  46.     rt_kprintf("bus fault:\n");
  47.     rt_kprintf("SCB_CFSR_BFSR:0x%02X ", SCB_CFSR_BFSR);

  48.     if(SCB_CFSR_BFSR & (1<<0))
  49.     {
  50.         /* [0]:IBUSERR */
  51.         rt_kprintf("IBUSERR ");
  52.     }

  53.     if(SCB_CFSR_BFSR & (1<<1))
  54.     {
  55.         /* [1]:PRECISERR */
  56.         rt_kprintf("PRECISERR ");
  57.     }

  58.     if(SCB_CFSR_BFSR & (1<<2))
  59.     {
  60.         /* [2]:IMPRECISERR */
  61.         rt_kprintf("IMPRECISERR ");
  62.     }

  63.     if(SCB_CFSR_BFSR & (1<<3))
  64.     {
  65.         /* [3]:UNSTKERR */
  66.         rt_kprintf("UNSTKERR ");
  67.     }

  68.     if(SCB_CFSR_BFSR & (1<<4))
  69.     {
  70.         /* [4]:STKERR */
  71.         rt_kprintf("STKERR ");
  72.     }

  73.     if(SCB_CFSR_BFSR & (1<<7))
  74.     {
  75.         rt_kprintf("SCB->BFAR:%08X\n", SCB_BFAR);
  76.     }
  77.     else
  78.     {
  79.         rt_kprintf("\n");
  80.     }
  81. }

  82. static void mem_manage_fault_track(void)
  83. {
  84.     rt_kprintf("mem manage fault:\n");
  85.     rt_kprintf("SCB_CFSR_MFSR:0x%02X ", SCB_CFSR_MFSR);

  86.     if(SCB_CFSR_MFSR & (1<<0))
  87.     {
  88.         /* [0]:IACCVIOL */
  89.         rt_kprintf("IACCVIOL ");
  90.     }

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

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

  101.     if(SCB_CFSR_MFSR & (1<<4))
  102.     {
  103.         /* [4]:MSTKERR */
  104.         rt_kprintf("MSTKERR ");
  105.     }

  106.     if(SCB_CFSR_MFSR & (1<<7))
  107.     {
  108.         /* [7]:MMARVALID */
  109.         rt_kprintf("SCB->MMAR:%08X\n", SCB_MMAR);
  110.     }
  111.     else
  112.     {
  113.         rt_kprintf("\n");
  114.     }
  115. }

  116. static void hard_fault_track(void)
  117. {
  118.     if(SCB_HFSR & (1UL<<1))
  119.     {
  120.         /* [1]:VECTBL, Indicates hard fault is caused by failed vector fetch. */
  121.         rt_kprintf("failed vector fetch\n");
  122.     }

  123.     if(SCB_HFSR & (1UL<<30))
  124.     {
  125.         /* [30]:FORCED, Indicates hard fault is taken because of bus fault,
  126.                         memory management fault, or usage fault. */
  127.         if(SCB_CFSR_BFSR)
  128.         {
  129.             bus_fault_track();
  130.         }

  131.         if(SCB_CFSR_MFSR)
  132.         {
  133.             mem_manage_fault_track();
  134.         }

  135.         if(SCB_CFSR_UFSR)
  136.         {
  137.             usage_fault_track();
  138.         }
  139.     }

  140.     if(SCB_HFSR & (1UL<<31))
  141.     {
  142.         /* [31]:DEBUGEVT, Indicates hard fault is triggered by debug event. */
  143.         rt_kprintf("debug event\n");
  144.     }
  145. }

  146. /**
  147. * fault exception handling
  148. */
  149. void rt_hw_hard_fault_exception(struct stack_context* contex)
  150. {
  151.    rt_kprintf("psr: 0x%08x\n", contex->psr);
  152.    rt_kprintf(" pc: 0x%08x\n", contex->pc);
  153.    rt_kprintf(" lr: 0x%08x\n", contex->lr);
  154.    rt_kprintf("r12: 0x%08x\n", contex->r12);
  155.    rt_kprintf("r03: 0x%08x\n", contex->r3);
  156.    rt_kprintf("r02: 0x%08x\n", contex->r2);
  157.    rt_kprintf("r01: 0x%08x\n", contex->r1);
  158.    rt_kprintf("r00: 0x%08x\n", contex->r0);

  159.    hard_fault_track();

  160.     rt_kprintf("hard fault on thread: %s\n", rt_current_thread->name);
  161. #ifdef RT_USING_FINSH
  162.    list_thread();
  163. #endif
  164.    while (1);
  165. }
再写了两个测试代码,以手动触发fault。
  1. #include <finsh.h>
  2. static void div0_test(void)
  3. {
  4.     int x,y,z;
  5.     x = 10;
  6.     y = 0;
  7.     z = x / y;
  8.     rt_kprintf("z:%d\n", z);
  9. }
  10. FINSH_FUNCTION_EXPORT(div0_test, div0_test)

  11. static void unalign_test(void)
  12. {
  13.     int * p;
  14.     p = (int *)0x00;
  15.     rt_kprintf("00:0x%08X\n", *p);
  16.     p = (int *)0x04;
  17.     rt_kprintf("04:0x%08X\n", *p);
  18.     p = (int *)0x03;
  19.     rt_kprintf("03:0x%08X\n", *p);
  20. }
  21. FINSH_FUNCTION_EXPORT(unalign_test, unalign_test)
测试时可以使用finsh强大的功能来配置CPU的状态:

1. 访问末授权区域
  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
2. 非对齐访问测试
  1. finsh>>int * p //声明一个指针变量
  2.         0, 0x00000000
  3. finsh>>p = 0xE000ED14 // 指向SCB->CCR
  4.         -536810220, 0xe000ed14
  5. finsh>>*p = 0x00000208 // 打开非对齐异常
  6.         520, 0x00000208
  7. finsh>>unalign_test() // 非对齐访问测试
  8. psr: 0x21000000
  9. pc: 0x00000430
  10. lr: 0x00003cbd
  11. r12: 0x0000006e
  12. r03: 0x00000002
  13. r02: 0x00000004
  14. r01: 0x1fff1b6a
  15. r00: 0x1fff1a2c
  16. usage fault:
  17. SCB_CFSR_UFSR:0x100 UNALIGNED
  18. hard fault on thread: tshell
  19. thread  pri  status      sp     stack size max used   left tick  error
  20. -------- ---- ------- ---------- ---------- ---------- ---------- ---
  21. tidle    0x1f ready   0x00000058 0x00000100 0x00000060 0x00000001 000
  22. tshell   0x14 ready   0x00000088 0x00000400 0x00000218 0x00000008 000
3. 除零异常测试
  1. finsh>>int * p //声明一个指针变量
  2.         0, 0x00000000
  3. finsh>>p = 0xE000ED14 // 指向SCB->CCR
  4.         -536810220, 0xe000ed14
  5. finsh>>*p = 0x00000210 // 打开除零异常
  6.         528, 0x00000210
  7. finsh>>div0_test() // 除零异常测试
  8. psr: 0x41000000
  9. pc: 0x00001efe
  10. lr: 0x0000096d
  11. r12: 0x00000000
  12. r03: 0x1fff012c
  13. r02: 0x1fff0917
  14. r01: 0x00001ef9
  15. r00: 0x00000000
  16. usage fault:
  17. SCB_CFSR_UFSR:0x200 DIVBYZERO
  18. hard fault on thread: tshell
  19. thread  pri  status      sp     stack size max used   left tick  error
  20. -------- ---- ------- ---------- ---------- ---------- ---------- ---
  21. tidle    0x1f ready   0x00000040 0x00000100 0x00000060 0x0000000b 000
  22. tshell   0x14 ready   0x00000088 0x00000400 0x00000218 0x00000009 000
LDTEST 发表于 2012-12-3 21:18 | 显示全部楼层
  感谢
Xflyan 发表于 2012-12-3 23:37 | 显示全部楼层
谢谢分享!其实发现HARTFAULT大部分原因并不是难题 而是自己没有注意到的细节问题
Lyc1992 发表于 2012-12-4 09:06 | 显示全部楼层
拿起书本 发表于 2012-12-4 14:13 | 显示全部楼层
不错,资料挺详细又全面,学习中,感谢楼主的分享,顶了
 楼主| aozima 发表于 2013-7-1 12:06 | 显示全部楼层
本帖最后由 aozima 于 2013-7-1 12:09 编辑

更新一下出现hard fault后如何反查。

问题追踪

上面测试出了问题,是我们人为设置的故障,但在平时调试中出了问题如何追综呢?
以非对齐访问为例,开发环境使用MDK。

1. 进入JTAG仿真状态,并触发非对齐异常。
此时串口会打印出异常时的寄存器值,此时停止仿真器发现程序停在rt_hw_hard_fault_exception中。
  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
2. 根据上面打印出来的寄存器,提取出关键值是 pc: 0x08000386
我们在MDK的command窗口中输入 pc = 0x08000386
command.jpg

可以把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]
instruction.jpg

分析 #386 这条指令从 R4+0 的问题读取4字节到R0中,
先前打印出的R4的值为 r04: 0x00000003
因此可以确定为这是因为地址不对齐造成的。

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

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


 楼主| aozima 发表于 2013-7-1 12:08 | 显示全部楼层
再更新一下测试代码,以手动触发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\n", z);
  10. }

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

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

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

  20.     p = (int *)0x04;
  21.     value = *p;
  22.     rt_kprintf("addr:0x%02X value:0x%08X\n", (int)p, value);

  23.     p = (int *)0x03;
  24.     value = *p;
  25.     rt_kprintf("addr:0x%02X value:0x%08X\n", (int)p, value);
  26. }

  27. #ifdef  RT_USING_FINSH
  28. #include <finsh.h>
  29. FINSH_FUNCTION_EXPORT(div0_test, div0_test)
  30. FINSH_FUNCTION_EXPORT(unalign_test, unalign_test)
  31. #endif /* RT_USING_FINSH */
SwPwr 发表于 2013-7-4 21:55 来自手机 | 显示全部楼层
aozima 发表于 2012-11-30 00:25
刚网上搜了一番,在http://st.**.com/down/index.php?id=120找到了。
上传之。

谢谢共享
freezz 发表于 2013-12-10 20:44 | 显示全部楼层
,mark,慢慢消化。
outstanding 发表于 2013-12-10 21:08 | 显示全部楼层
不错。。。。。。。。。。。
hjl2832 发表于 2014-7-10 11:19 | 显示全部楼层
不错,查异常的方法。
fvcvxvcv 发表于 2014-7-11 14:24 | 显示全部楼层
xzezhen 发表于 2014-10-25 12:24 | 显示全部楼层
很赞的分析:lol
zook0k 发表于 2014-10-27 10:00 | 显示全部楼层
xlsf1048 发表于 2014-12-16 14:17 | 显示全部楼层
jemy00 发表于 2014-12-17 11:48 | 显示全部楼层
FAQ 发表于 2014-12-17 12:01 | 显示全部楼层
表示出现过一次hardfault
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:RTOS RT-Thread

55

主题

2040

帖子

23

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