?1 现象
SAMG55(CM4)在加了bootloader后跑app总是出现异常复位的现象。当时是在如下情况下复现:
打开spi开始接收数据,一段时间后就直接进入HardFault_Handler异常中断。
2 调试过程
- 首先在HardFault_Handler处打断点通过MSP指针反推出是在函数orignal_self_corr_data内产生的异常中断;
- 然后仔细调试这个函数发现,在操作CORR_PMA_A(32k的变量)的某一段内存时直接进入HardFault_Handler异常中断,通过反汇编后提示操作了非法内存;
- 一般这种情况是由堆栈溢出、指针跑飞、访问空指针、除0操作产生的,分析map然后逐一进行排查,发现均没有上诉情况,栈空间离那片异常内存还非常遥远,栈空间也还很富裕;
- 后来经过多次实验无果后,我将bootloader的ram区域和app的ram区域分开来后解决了问题,但隐隐觉得并没有解决问题的实际根源。
3 原因分析
本着挖掘问题根源的想法在继续查询一些资料后受到一些启发,反汇编提示访问了非法内存,那还是得从栈这里分析,会不会是从boot跳转到applacation中实际的栈空间划分和map里面的栈空间不一样呢?
我们知道,中断向量表里面的第一个4字节单位存的就是栈顶指针,然后我将bootloader中的栈顶指针和app中的栈顶指针分别在他们刚开始运行的时候打印出来,果然发现了问题:
如上图所示,因为bootloader的ram占用比较少,因此在进入main函数的时候分配的栈顶指针在0x20000690处,main函数里面的所有东西将从这个地址开始分配栈空间,因为栈指针是自上而下生长的,那我们剩余的ram空间范围是0x20000000的0x20000690,共1k字节。
当跳转到app后,栈指针居然是从boot分配后的栈指针开始的,也就是说app的总共可用的ram空间从复位开始就只有1kb可用,然而map里面是正常的:
这也是一开始没找到问题的原因。
分析到这里就知道出现这个bug的具体原因了,因为没有在跳转到app前重新初始化栈顶指针…
4 解决方法
分析出了具体原因解决方法就很简单,在boot程序中,要跳到app前重新初始化栈顶指针即可,其实这个操作在正常的MCU厂商提供的启动文件里的复位函数里面就做好了,然而microchip这个折磨人的烂库没有如此操作!只能自己添加。
加上这一句程序就正常了,测试如下:
由上图可以看出来,跳转到app后栈顶指针为0x200144b0,到0x20000000尚有83k的空间,说明app占用的ram为83k,至此再无复位现象。
5 总结
之前用的很多库都比较成熟,很多东西帮我们做好了,导致一些东西、流程没有理解清楚,在用到比较烂的库的时候就一时半会找不到原因,所以以后还是不要太依赖已有的东西,自己理解清楚才比较好。
下次在遇到这种非法内存访问的时候,可以按以下流程走:
- 先定位到产生异常的函数,可以通过msp指针反推、或者通过逐模块、段、行屏蔽代码找到;
- 找到异常函数后分析该函数,看是否有大容量局部数组,检查是否堆栈溢出,然后在看是否有除0操作,是否有指针的异常问题,逐一排查;
- 异常操作无果后检查main函数起来的时候的堆栈指针是否正常,可以通过cm4的提供的函数__get_MSP()看。
- 如果main函数中的堆栈指针正常,一路添加__get_MSP()到异常函数,逐步观察堆栈指针的变化,这时候问题应该也就能排解出来了。
|