电子产业一站式赋能平台

PCB联盟网

搜索
查看: 18|回复: 0
收起左侧

MCU HardFault问题查找和破解方法

[复制链接]

208

主题

208

帖子

1635

积分

三级会员

Rank: 3Rank: 3

积分
1635
发表于 2021-10-14 20:03:00 | 显示全部楼层 |阅读模式
一、HardFault产生原因和常规分析方法
二、HardFault解决方法分析
三、HardFault回溯的原理
四、操作分析流程:
1.  心里明白徒手分析法
2. CmBacktrace 天龙大法
五、总结:
一、HardFault产生原因和常规分析方法 在嵌入式开发中,偶尔会遇到Hard Fault死机的异常,常见产生Hard Fault的原因大致有以下几类:
  • 数组越界和内存溢出,譬如访问数组时,动态访问的数组标号超过数组长度或者动态分配内存太小等;
  • 堆栈溢出,例如在使用中,局部变量分配过大,超过栈大小,也会导致程序跑飞;
  • 在外设时钟开启前,访问对应外设寄存器,例如Kinetis中未打开外设时钟去配置外设的寄存器;
  • 不当的用法操作,例如非对齐的数据访问、除0操作(默认情况下M3/M4/M7,除0默认都不会触发Fault,因为ARM内核CCR寄存器DIV_0_TRP位复位值为0,而对M0来说DIV_0_TRP位是reserved的,也不会产生Fault错误)、强行访问受保护的内存区域等;出现Hardfault错误时,问题比较难定位的原因在于此时代码无法像正常运行时一样,在debug IDE的stack callback窗口能直接找到出错时上一级的调用函数,所以显得无从下手。通常情况下我们都是通过在某个区间打断点,然后通过单步执行去逐步缩小“包围圈”去找到产生Hard Fault的代码位置,接着再去推敲、猜测问题的原因。对于不是很复杂的程序,这种方法是有效的,但是当用户代码量进一步增大,再用这种单步+断点去逐步缩小包围圈的方式就很难查到问题点,效率也很低。尤其是在有操作系统的应用中,很多代码的跳转是由操作系统调度的,不是严格的顺序执行,所以很难依靠缩小包围圈的方式去有效找到问题产生的点,进一步增加了定位到Hard Fault触发原因的难度。
    尽管本测试是针对NXP KW36芯片的,但该步骤和方法也适用于其他的Arm Cortex-M内核MCU;
    二、HardFault解决方法分析 笔者在实际支持客户过程中也遇到这种困惑,网上的介绍资料比较零散,理论很多,很少详细描述实战操作的步骤,借助同事的点拨,摸索出两种定位Hard Fault问题的方法,在实际使用中操作性也很强,此处分别做一介绍。
  • 第一种:心里明白徒手分析法,就是在了解Hard Fault出错原理以及程序调用压栈出栈原理的基础上(当然按照本文的练就心法,心里不明白也可以),在Debug仿真模式下徒手去回溯分析CPU通用寄存器(LR/MSP/PSP/PC),然后结合调试IDE去定位到产生Hard Fault的代码位置;
  • 第二种:CmBacktrace 天龙大法,该方法是朱天龙大神针对 ARM Cortex-M系列MCU开发的一套错误代码自动追踪、定位、错误原因自动分析的开源库,已开源在Github上,该方法支持在非Debug模式下,自动分析定位到出错的行号,无需了解复杂的压栈出栈过程。两者的区别在于:前者不需要额外添加代码,缺点是只能在仿真状态下调试,需要用户对程序调用压栈/出栈原理有清晰的理解,后者的唯一的缺点是需要适当添加代码,并稍微配置工程和打印输出,优点就太多了。首先,产品真机调试时可以断开仿真器,并将错误信息输出到控制台上,甚至可以将错误信息使用 Easy Flash 的 Log 功能保存至 Flash 中,待设备死机后重启依然能够读取上次的错误信息。这个功能真的是very very重要了,尤其在有些Hard Fault问题偶发的情况下,很多时候一天可能也复现不了一次问题,但借助CmBacktrace 天龙大法便可以轻松脱离仿真器get每一次错误,最后再配合 addr2line 工具进行精确定位出错代码的行号,方便用户进行后续的精确分析。
    三、HardFault回溯的原理 为了找到Hard Fault 的原因和触发的代码段,就需要深刻理解当系统产生异常时 MCU 的处理过程: 当处理器接收一个异常后,芯片硬件会自动将8个通用寄存器组中压入当前栈空间里(依次为 xPSR、PC、LR、R12以及 R3~R0),如果异常发生时,当前的代码正在使用PSP,则上面8个寄存器压入PSP,否则就压入MSP。那问题来了,如何找到这个栈空间的地址呢?答案是SP, 但是前面提到压栈时会有MSP和PSP,如何判断触发异常时使用的MSP还是PSP呢?答案是LR。到此确定完SP后,用户便可以通过堆栈找到触发异常的PC 值,并与反汇编的代码对比就能得到哪条指令产生了异常。
    总结下来,总体思路就是:首先通过LR判断出异常产生时当前使用的SP是MSP还是PSP,接着通过SP去得到产生异常时保存的PC值,最后与反汇编的代码对比就能得到哪条指令产生了异常。
    回到前面的第二个问题,如何通过LR判断当前使用的MSP还是PSP呢?参见如下图,当异常产生时,LR 会被更新为异常返回时需要使用的特殊值(EXC_RETURN),其定义如下,其高 28 位置 1,第 0 位到第3位则提供了异常返回机制所需的信息,可见其中第 2 位标示着进入异常前使用的栈是 MSP还是PSP。
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表