ES32F36xx芯片发生HardFault异常时的函数调用关系及问题定位

1、引言
cortex-m3微控制器因其功能强大、性价比高以及易用性好,在嵌入式体系结构中得到了广泛应用。然而,在实际开发过程中,如果程序很大或运行很久后可能会遇到hardfault异常。为了快速有效地解决hardfault异常,本文将讨论定位hardfault问题的方法。
2、常见引发hardfault原因
1)访问非法内存地址:编程中处理指针时可能导致访问未定义的内存地址,特别是在数组越界、非法指针解引用等情况下。
2)叠栈溢出:程序运行过程中如果栈溢出,会导致栈上数据错误,从而引发异常。
3)寄存器未正确初始化:寄存器配置和使用不正确,可能导致硬件异常。
4)除0运算:当程序试图执行除0运算时,可能发生硬件故障异常。
5)总线错误:外设总线通信发生错误,可能引起硬件故障。
3、软件触发hardfault异常
es32f36xx是cortex-m3内核,当被除数为0时,硬件将触发一个异常,导致系统进入hard fault异常状态。这种情况下,处理器不能正常运行,并且不能恢复,直到硬件或软件采取措施使得系统回到正常状态。在es32_sdk中,es32f36xx 的key_led_adc例程中增加如下程序:
// b函数void b_function(void){    printf_e(enter b functionrn);    c_function(0);    printf_e(exit b functionrn);}// a函数void a_function(void){    printf_e(enter a functionrn);    b_function();    printf_e(exit a functionrn);}// c函数int c_function(int val){    return 100/val;    }// d函数void d_function(void){    printf_e(enter d functionrn);    c_function(1);    printf_e(exit d functionrn);}// testdebug函数void testdebug(void){    // 使能除0异常volatile int *ccr = (volatile int *)0xe000ed14;    *ccr |= (1 < z=1, get fault context from handlermrseq        r0, msp              ;[2]=1 ==> z=0, get fault context from threadmrsne        r0, psp                 stmfd        r0!, {r4 - r11}  ; push r4 - r11 register    stmfd        r0!, {lr}         ; 将exc_return值压栈        tst          lr, #0x04        ;if (!exc_return[2])    ite          eq                 ; r0为栈值,作为hw_hardfault_exception函数首个参数msreq        msp, r0;[2]=1 ==> z=0, get fault context from thread         msrne        psp, r0              ;再次将exc_return值压栈    push         {lr}                  ;跳转至hw_hardfault_exception函数    bl           hw_hardfault_exception     pop          {lr}    orr           lr, lr, #0x04    bx            lr    endp  
上述汇编代码的主要功能是获取栈(sp)地址,并将r4 ~ r11压栈,跳转执行hw_hardfault_exception函数。压栈后栈中的数据情况如图 3所示:
图3 hardfault异常瞬间栈中完整寄存器
return address是发生异常指令点,lr是发生异常指令所在函数的下一条指令地址。在hw_hardfault_exception函数中将栈中的寄存器值都通过串口打印出来。
void hw_hardfault_exception(struct exception_info *exception_info){    uint32_t *app_sp = null;int i = 0;/*sp指向发生hardfault前栈地址*/    app_sp = (uint32_t *)(exception_info + 1);  /* context + 16*4 */        printf_e(psr: 0x%08xrn, exception_info->psr);    printf_e(r00: 0x%08xrn, exception_info->r0);    printf_e(r01: 0x%08xrn, exception_info->r1);    printf_e(r02: 0x%08xrn, exception_info->r2);    printf_e(r03: 0x%08xrn, exception_info->r3);    printf_e(r04: 0x%08xrn, exception_info->r4);    printf_e(r05: 0x%08xrn, exception_info->r5);    printf_e(r06: 0x%08xrn, exception_info->r6);    printf_e(r07: 0x%08xrn, exception_info->r7);    printf_e(r08: 0x%08xrn, exception_info->r8);    printf_e(r09: 0x%08xrn, exception_info->r9);    printf_e(r10: 0x%08xrn, exception_info->r10);    printf_e(r11: 0x%08xrn, exception_info->r11);    printf_e(r12: 0x%08xrn, exception_info->r12);    printf_e( lr: 0x%08xrn, exception_info->lr);    printf_e( pc: 0x%08xrn, exception_info->pc);        printf_e(stacks: rn);        for (i = 0; i < 1024; ++i)    {        printf_e(%08x , *app_sp);        app_sp++;        ++i;        if (i % 16 == 0)            printf_e(rn);    }    printf_e(rn);    while(1);}  
在hw_hardfault_handler函数中打印出了所有相关寄存器,如图 4所示:
图4 发生异常时栈数据
从打出来的返回地址值(pc)为0x00000644,在生成的all.dis汇编文件搜索该地址,如图 5所示,该地址是c_function函数中的一个除法指令,r0寄存器值除以r1寄存器值,并将结果存放r0中。r0为0x64,确认r1寄存器值即可。
图5 发生hardfault异常瞬间执行的指令
图 4中,lr的值为0x0000060f,all.dis无法搜索到该地址。由于cortex-m3使用的是thumb指令集,bit0置位指示该地址地址指令是thumb指令。bit0复位,搜索0x0000060e,如图 6所示,该地址在b_function函数中。b_function函数调用了c_function函数,r0为传递的参数0。由此可知,图 5中,r1的除数值0,故程序会发生hardfault异常。
图6 b_function函数的汇编代码
图 6中,调用c_function函数前,r4和lr寄存器被压入了栈中。即图 7中,lr的值为0x000005d1,r4的值为0xe000ed14。
图 7 b_function函数压栈值
如图 8所示,在all.dis文件搜索0x000005d0地址在a_function函数中,在执行a_function函数前,对r4和lr进行了压栈。a_function函数调用了b_function函数。
图 8 a_function函数汇编代码
如图 9所示,a_function函数压入的r4值为0xe000ed14,lr值为0x000006a1。
图 9 a_function函数压栈值
如图 10,在all.dis文件中,搜索0x000006a0,发现该地址在testdebug函数中,且该函数将r4和lr压入栈中。
图 10 testdebug函数汇编代码
如图 11所示,a_function函数压入的r4值为0xe0001c18,lr值为0x00001ab9。
图 11 testdebug函数压栈值
如图 12所示,在all.dis文件中,搜索0x00001ab8,该地址在main函数中。
图 12 main函数的汇编代码
至此,如图 13所示为发生hardfault异常时函数的调用关系,在c_function函数中,被除数为0是导致进入hardfault异常的原因。
图 13 发生hardfault时的函数调用关系
来源:东软载波微电子


罗德与施瓦茨宣布针对5G NR第17版的综合测试解决方案
关于FreeRTOS内存分配多少才合适
过程控制系统经历过的三个发展阶段
70年人工智能研究,解读研究者最大的惨痛教训经验
CASAIM广州中大型工件形位公差三维扫描尺寸三维测量解决方案
ES32F36xx芯片发生HardFault异常时的函数调用关系及问题定位
那部在战狼2里面出现的AGMX2手机怎么样?值得购买吗?
骁途最新消息:是否能引领长安铃木在国内崛起?将于7月底正式上市,报价及配置信息!
创新示波器抖动分离方法 助您改进信号完整性调试
电网+通信共建共享,助力南京构建能源互联网生态圈
电力变压器电抗标幺值公式
阿吉兰兄弟控股集团与正泰成立合资公司
中国电信福建分公司实现5G和现网4G互操作,加速5G SA的全面商用
释放智能制造新能动,“汇川号”西部之旅完美收官
电容放电电阻接线_法拉电容放电时间计算
索尼大法好:利用屏幕发声的黑科技 索尼A1E电视是怎么做到的?
融入健身房的黑科技智能魔镜,它将带来什么改变
中兴已经率先完成IMT-2020三阶段核心网性能稳定性测试
声悬浮液滴表面的纳米颗粒自组装
2023广州国际创新节精彩纷呈,英码科技荣膺“广州市隐形冠军企业”!