周末用2天的时间专门研究了一下这个问题,已经解决,做个总结: 运行环境:STM32H743+UCOSIII(V3.08.00) 故障现象: 程序运行一段时间后,总是不定期的进入HardFault_Handler,长则2三天,短则几分钟就会浮现,尤其在串口/SPI口有大量数据收发的时候,更容易浮现。 排查过程: 统计发现,每次总是在执行OSTaskSwHook函数中的OS_CPU_FP_Reg_Pop时进入的错误中断。 第一步还是先查自身的问题,把系统额外的任务全部关闭,浮点运算也全部屏蔽,仅保留通信任务,测试大数据收发,发现故障依旧。之后又在整个数据收发过程中添加多个测试点,发现每次异常前的位置有所区别,但无一例外的都是调用了系统的服务时发生的异常(程序收发涉及到了2个任务用事件同步),至此基本排除程序收发逻辑问题,转向排查系统问题。 因为以前在跑老版本系统时就修改过浮点入栈出栈这块儿代码,所以刚开始还是简单认为是源码问题,可惜老版本没找到M7的DEMO,从M4上迁移过来难度较大,懒得去折腾老版本了。后来试着关掉FPU后,故障更诡异,进入错误中断后压根找不到源头了,最后只能暂时放弃了这个排查方向。 后来无意间发现,进入中断后,OSTCBHighRdyPtr指向的地址竟然为0。于是在OS_CPU_FP_Reg_Pop函数前加了一个if语句,判断OSTCBHighRdyPtr的指向是否为真,果然,异常情况时OSTCBHighRdyPtr的指针指向了0,这个指针总是指向最高优先级就绪任务控制块儿,不可能指向0。 首先想到的是数组越界修改了OSTCBHighRdyPtr的值,但仿真发现,指针指向0时,它在内存中的地址前的数据并没有发生异常,因此排除是数组越界造成的。 仔细分析OSTaskSwHook这个函数,最开始是把当前控制块的浮点寄存器压栈,最后把最高优先级的控制块浮点寄存器出栈。中间是一堆可有可无的系统监控操作,并没有对OSTCBHighRdyPtr的赋值操作。于是接下来就是逐步往前排查,什么时候OSTCBHighRdyPtr的值被修改为了0,先在一进OSTaskSwHook函数的地方添加一个判断指向的语句,没想到还真有一进函数就会出现OSTCBHighRdyPtr指向为空的情况,但更诡异的情况是,大多数时候一进函数判断为空之后,等到执行OS_CPU_FP_Reg_Pop时,指针又不为空了,问题是这个函数里面并没有对OSTCBHighRdyPtr的赋值操作啊。 UCOS系统是有自我修复的功能,但不至于凭空就把指针校准回来了,难不成中间发生过异常中断?也不对啊,任务调度时的第一条汇编指令就是关中断啊“CPSID I ”。这里特别注意到在这句汇编语句后面的注释“Cortex-M7 errata notice. See Note #5”,硬汉哥之前提到过,Cortex-M7r0p1版本这块儿有BUG,用仿真器看了一下,我板子上芯片的版本是r1p1,于是排除是硬件版本的问题。 但是如果期间没有发生过中断,说不通指针的值为何被修改啊。顺着汇编代码往下看,发现个新变量“OS_KA_BASEPRI_Boundary”,这个在之前的系统版本中可没见过,于是顺藤摸瓜,找到了“CPU_CFG_KA_IPL_BOUNDARY”这个宏,英文解释如下“ARMv7-M:Since the port is using BASEPRI to separate kernel vs non-kernel aware ISR,please make sure your external interrupt priorities are set accordingly. Forexample, if CPU_CFG_KA_IPL_BOUNDARY is set to 4 then external interruptpriorities 4-15 will be kernel aware while priorities 0-3 will be use asnon-kernel aware.”,通俗解释,系统调度时关中断不再是以一刀切的方式全关闭了,而是根据设置阈值选择性关闭,即优先级在这个阈值以下的中断是不关闭的,此功能应该是为了照顾一些需要在系统关中断时也要响应的中断。默认值是4,再看我的程序,还真有几个较频繁的中断优先级是4以下的,且中断里面调用了系统发消息的函数。 难道是这个原因造成的?于是乎,把这个值改成1,把程序的中断优先级都改成1以上,我程序中目前没有啥中断是晚个几微妙就不行的,所以这个功能没必要用。修改后再次运行,顿感顺畅无比,对比很明显,之前一个通信端口大量收发数据时,调度1万次左右一般就会出现一次指针指向0的情况,修改后几个通信端口开足马力收发数据跑了一天,截止目前已经调度了一千五百万次左右,还没发生过一次异常,至此,遗留个把月的BUG彻底解决。 折腾了一大圈,最根本原因还是对新版本的操作系统不熟悉导致,看来以后抽空还得多撸撸UCOSIII的源码。
|