zoukankan      html  css  js  c++  java
  • stm32 HardFault_Handler调试及问题查找方法——飞思卡尔

    看到有朋友遇到Hard Fault 异常错误,特地找到一篇飞思卡尔工程师写的一片经验帖,定位Hard Fault 异常。

    Kinetis MCU 采用 Cortex-M4 的内核,该内核的 Fault 异常可以捕获非法的内存访问和非法的编程行为。Fault异常能够检测到以下几类非法行为:

    ·        总线 Fault:  在取址、数据读/写、取中断变量、进入/退出中断时寄存器堆栈操作(入栈/出栈)时检测到内存访问错误。

    ·        存储器管理 Fault: 检测到内存访问违反了内存保护单元(MPU, MemoryProtection Unit)定义的区域。

    ·        用法 Fault:  检测到未定义的指令异常,未对其的多重加载/存储内存访问。如果使能相应控制位,还可以检测出除数为零以及其他未对齐的内存访问。

     

    ·        硬 Fault:  如果上述的总线 Fault、存储器管理 Fault、用法 Fault 的处理程序不能被执行(例如禁能了总线 Fault、存储器管理Fault、用法Fault 的异常或者在这些异常处理程序中又出现了新的Fault)则触发硬Fault。

           在 MQX 操作系统启动的时候会安装上默认的异常中断处理函数,当系统异常时会产生一个“unexpected”中断,内核就会自动调用异常处理函数,同时也将运行用户自定义的处理函数,来实现特殊故障的定位方法。

           默认情况下,MQX把出现异常的任务挂起,避免故障进一步扩大。通过TAD 任务感知调试插件的Task summary 功能,我们可以观察到出现异常的任务情况。

    开发人员在调试期间,需要弄清楚系统异常触发了哪类Fault,由什么原因触发了Fault 以及定位触发Fault 的代码。在这种情况下,可以利用自定义的Fault 中断处理程序来分析

    Fault 出错原因。

         为了解释所述的 Fault 中断处理程序的原理,这里重述一下当系统产生异常时 MCU 的处理过程:

    ·        有一个压栈的过程,若产生异常时使用 PSP(进程栈指针),就压入到 PSP 中,若产生异常时使用MSP(主栈指针),就压入MSP 中。

    ·        会根据处理器的模式和使用的堆栈,设置 LR 的值(当然设置完的LR 的值再压栈)。

    ·        异常保存,硬件自动把 8 个寄存器的值压入堆栈(8 个寄存器依次为 xPSR、PC、LR、R12以及 R3~R0)。如果异常发生时,当前的代码正在使用PSP,则上面8 个寄存器压入PSP; 否则就压入MSP。

           当系统产生异常时,我们需要两个关键寄存器值,一个是 PC ,一个是 LR (链接寄存器),通过 LR找到相应的堆栈,再通过堆栈找到触发异常的PC 值。将产生异常时压入栈的 PC 值取出,并与反汇编的代码对比就能得到哪条指令产生了异常。

            这里解释一下关于 LR 寄存器的工作原理。如上所述,当 Cortex-M4 处理器接受了一个异常后,寄存器组中的一些寄存器值会被自动压入当前栈空间里,这其中就包括链接寄存器(LR )。这时的 LR 会被更新为异常返回时需要使用的特殊值(EXC_RETURN)。关于

    EXC_RETURN 的定义如下,其为 32 位数值,高 28 位置 1,第 0 位到第三位则提供了异常返回机制所需的信息,如下表所示。可见其中第 2 位标示着进入异常前使用的栈是 MSP还是PSP。在异常处理过程结束时,MCU 需要根据该值来分配 SP 的值。这也是本方法中用来判断所使用堆栈的原理,其实现方法可以从后面_init_hardfault_isr 中看到。

       另外,我们可以利用 MQX 的控制台串口输出Fault 异常信息来帮助调试。编写Fault 处理程序时,将启动代码中默认的Fault 处理程序跟换成自己需要的Fault 处理程序。需要注意的是,由于是在中断中进行打印输出,MQX的控制台串口只能使用POLL 轮询模式的驱动,不能使用中断模式的驱动。

         用户可以编写自定义的硬 Fault 处理程序_int_hardfault_isr,修改 MQX 的中断向量定义vector.c,把里面的DEFAULT_VECTOR 代码段换成下面的代码。当系统出现硬Fault 异常时,将会调用自定义的Fault 处理_int_hardfault_isr函数。在这个函数,我们可以通过StackTrace-back 回溯出现问题的代码。

    我们可以在_int_hardfault_isr 函数里将出现异常时的寄存器、堆栈、状态寄存器等信息打印出来。如果系统出现异常时,一般情况都会通过串口控制台打印出LR,PC的值。然后根据编译器生成的map 文件,找到出现问题的具体函数。

         从上图的串口输出我们可以看到 PC 和 LR 寄存器值,PC 的值为 0x56c6,我们根据汇编代码可以找到出现问题的指令。从而大大缩小了查找出现问题的范围,可以帮助开发人员快速定位问题的根本原因。


    附录Fault异常中断处理代码:

    1. // hard fault handler in C,

    2. // with stack frame location as input parameter

    3. void hard_fault_handler_c (unsigned int * hardfault_args)

    4. {

    5.   unsigned int stacked_r0;

    6.   unsigned int stacked_r1;

    7.   unsigned int stacked_r2;

    8.   unsigned int stacked_r3;

    9.   unsigned int stacked_r12;

    10.   unsigned int stacked_lr;

    11.   unsigned int stacked_pc;

    12.   unsigned int stacked_psr;

    13.  

    14.   stacked_r0 = ((unsigned long)hardfault_args[0]);

    15.   stacked_r1 = ((unsigned long)hardfault_args[1]);

    16.   stacked_r2 = ((unsigned long)hardfault_args[2]);

    17.   stacked_r3 = ((unsigned long)hardfault_args[3]);

    18.  

    19.   stacked_r12 = ((unsigned long)hardfault_args[4]);

    20.   stacked_lr = ((unsigned long)hardfault_args[5]);

    21.   stacked_pc = ((unsigned long)hardfault_args[6]);

    22.   stacked_psr = ((unsigned long) hardfault_args[7]);

    23.  

    24.   printf (" [Hard faulthandler - all numbers in hex] ");

    25.   printf ("R0 = %x ",stacked_r0);

    26.   printf ("R1 = %x ",stacked_r1);

    27.   printf ("R2 = %x ",stacked_r2);

    28.   printf ("R3 = %x ",stacked_r3);

    29.   printf ("R12 = %x ",stacked_r12);

    30.   printf ("LR [R14] = %x  subroutine call return address ",stacked_lr);

    31.   printf ("PC [R15] = %x  program counter ", stacked_pc);

    32.   printf ("PSR = %x ",stacked_psr);

    33.  

    34.   /******************* Add yourdebug trace here ***********************/

    35.   _int_kernel_isr();

    36. }

    37.  

    38. /* hard fault interrupt handler */

    39. void _int_hardfault_isr( )

    40. {

    41.   __asm("TST LR, #4");

    42.   __asm("ITE EQ");

    43.   __asm("MRSEQ R0,MSP");

    44.   __asm("MRSNE R0,PSP");

    45.   __asm("Bhard_fault_handler_c");

    46. }

    复制代码

     

  • 相关阅读:
    js实现快速排序
    使用NodeList
    变量提升问题
    使用Location对象查询字符串参数
    一天一小段js代码(no.2)
    一天一小段js代码(no.1)
    JS闭包中的this对象
    JS数组去重
    FreeCodeCamp----Intermediate Algorithm Scripting解法
    关于表单验证
  • 原文地址:https://www.cnblogs.com/CodeWorkerLiMing/p/12007341.html
Copyright © 2011-2022 走看看