首页|资源下载
登录|注册

您现在的位置是:首页 > 技术阅读 >  讨论Cortex-M4也能如此精彩?(上)你以为AC6编译器优化了啥?

讨论Cortex-M4也能如此精彩?(上)你以为AC6编译器优化了啥?

时间:2024-01-05

周末,午休起来看到群里有上几十条消息,没细看、划掉。没多长时间又有几十条信息,这次仔细阅读他们究竟聊着啥。

群里大佬围绕着群友的问题展开长达6小时的讨论,提供排查思路。

又不是社会事实探讨,技术群里可曾能围绕一个问题聊数个小时!

正文

10多天前,群里有个家伙Dino讨论了好几次请教如何从bootloader跳转到app,再从app跳转到bootloader的方法。前半部分,从bootloader跳转到app想必搞过STM32的同学都知道怎么个套路, 至于他所说的app跳转到bootloader我还是第一次听说,我一般采用软复位的方式, 一切交给上电时序。

折腾几日后群友完成了设计雏形,但代码的可移植性出现问题——切换Keil编译器AC5和AC6出现不一样的执行效果。

他问:“用AC6的-O0优化等级,从app跳转至bootloader无法正常运行,其他等级都正常,包括AC5的-O0也是正常的,可能是什么原因呢?”

(笔者注:聊天记录提问者口误,根据他聊天记录提供的源码可知真实需求:从app跳转到booloader,源码图片见下文)


 

群里的大佬在协助调试时思路清晰,也涉及好几个试错的步骤,对小白和刚入行的从业者特有借鉴意义,所以我特意把群聊天给截取了下来。

整个聊天记录特别长,我只节选部分内容分成2篇文章发布(如果在一篇文章发布,图片太多,影响加载速度,看官没耐心)。

本文是聊天记录第1篇文章:暂且将问题原因归结到Cortex-M4的双栈(PSP、MSP)设计上。

第2篇文章(整理中):找到根本原因——全局变量、局部变量存储APP跳转指针,会有什么缺陷?

至于有兴趣阅读完整聊天内容的看官,在我公众号发送:“chat1201”。

友情提示,原聊天内容图片特别多(10-20MB),长图加载可能会有些慢。

1. 成员介绍

傻孩子:群主,ARM原厂技术支持,“裸机思维”的号主

Dino:提问者

斯宾诺沙小镜子:最终点明方向的大佬。

2. 进入HardFault

首先Dino向群主求助,此时他把原因暂时归结于AC6编译器存在缺陷。

群主建议他先排查是不是semihosting和栈溢出。


为什么要排查semihosting我不太清除。semihosting我在项目用过,它是一个调试出处方式(当然除了调试输出字符串外还能做啥我就不懂了),嵌入式工程师调试“大招”——打印调试,首先得实现串口初始化,然后重定向_putc()到串口IO上,编译时勾选keil的 microC库,如此一来调用printf可在串口上看到打印内容。

当然如果串口也懒得初始化,使用semihosting技术,printf的输出内容将从调试连接器传输到host(PC)端,调试器例如J-Link。

确认不是semihosting、也不是栈溢出。

确认当前加入的是HardFault异常。

按照我以前的经验,加入HardFault的情况大概率是数据溢出引起的, 在调试bootloader跳转时,忘记给PC地址+4 也会触发HardFault。

3. 查看Faults Report

Keil的Fault Report(故障报告)对话框,对话框里被勾选的位置表示发生了相应的错误。

  • “FORCED”被勾选,暗示处理器当前处于 HardFault_Handler 异常里,CPU处于空转状态(停止)。

Debug Faults的内容是调试器触发的错误,与本例子无关。

  • 当处理器暂停时,将设置为 “HALTED” 。当前程序已经加入Hard Fault,当然暂停咯。

  • 遇到软件断点时设置 “BKPT” 。给调试器上报错误,当然也是断点暂停。

故障报告对话框显示故障的类型,用于故障的处理程序,相应的故障状态寄存器以及指示故障已发生的寄存器位。

  • “INVSTATE” 被勾选,表示一条指令执行失败,或者说执行一条不可识别的指令。

我不知道为什么Dino回去检查 0xE00EDF8 ,Memory Manage Faults和Bus Faults分明没有任何勾选项呀。

至于为什么发生 “INVSTATE” ,猜测是PC指针发生了偏移。

4. 提问者源码解释

源码很精简,没有什么花里胡哨的(远程技术支持也方便),各个地方都给了注释。

既然他的目的是从app跳转得到bootloader,那么就要模拟CM4的复位过程。

CM4和CM3的复位过程应该差不多,中断向量表第1项地址存放的是栈地址,传递给MSP用, 第2项地址是复位异常处理(Reset_Handler),提问者C代码的__set_MSP()等同于 Reset_handler的前面两行汇编代码。

文稿贴的代码来源于FreeRTOS CM3通用代码。

// 来源于FreeRTOSvoid Reset_Handler( void ){/* set stack pointer */ __asm volatile ( "ldr r0, =_estack" ); /* _estack SRAM起始地址 */ __asm volatile ( "mov sp, r0" );/* copy .data section from flash to RAM *//* zero out .bss section *//* jump to board initialisation */void _start( void ); _start();}const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) ={ ( uint32_t * ) &_estack, ( uint32_t * ) &Reset_Handler, /* Reset -15 */ ( uint32_t * ) &NMI_Handler, /* NMI_Handler -14 */ ( uint32_t * ) &HardFault_Handler, /* HardFault_Handler -13 */
}

5. 双bootloader设计

5.1. 我的疑惑:

按照CM3/CM4的中断向量表设计要求,SRAM第1项是栈地址,仅存储数据,不跳转异常入口,第2项目是Reset_Handler异常处理入口,既然他把栈地址填写成APP_ADDRESS,则若要执行Reset_Handler应该是APP_ADDRESS加8,起初我很怀疑发生 “INVSTATE” 的原因是否和它有关。然而提问者说在其他优化模式下可以正常执行?我开始怀疑他的需求究竟是从app跳转到bootloader,还是从bootloader跳转到app。

如果APP_ADDRESS指的不是中断向量表,而是某APP的函数入口,他这么写,大致准确。

5.2. 解答疑惑:

小镜子的问题帮我解答上面的疑惑。

小镜子:你的APP_ADDRESS究竟存储的是啥?为什么要取地址的值。

Dino:0x0800c000,需要的值在这个地址。

由此可推断APP_ADDRESS并不是我“疑惑”里猜测的系统复位地址,而是采用 “双bootloader设计”。

或许他的bootloader也是有两个,动态切换。

他取名有歧义,函数名写得有迷惑性,reset_handler改成 bootloader_entry避免引起歧义,handler也容易让人联想到是CM3/CM4的异常处理入口。

小镜子不只炸的,问出一句:“你在函数里用的是PSP,但是在reset里用的是MSP,不同的函数调用能切换过去吗?”

Dino好像悟出了什么。

小镜子推断Dino上了实时操作系统,否则一般裸机仅使用MSP栈。仅几行C代码,我很好奇小镜子是如何推断Dino使用的PSP栈。

小镜子:“猜的。”

目前为止所看到的证据碎片,可以组成自圆其说的逻辑,我推断是如下场景(待会被打脸):

  • 1、系统上电,使用MSP

  • 2、Reset_Handler根据0x0800c000的值选择跳转哪个bootloader,使用MSP

  • 3、bootloader再次设置MSP栈顶部

  • 4、启动实时操作系统,或者说启动APP,使用PSP

  • 5、某种外部输入事件(比如升级bootloader)触发APP跳转bootloader

  • 6、APP根据0x0800c000的值选择跳转哪个bootloader(聊天里的贴图),使用PSP

  • 7、从步骤3继续执行

我预感到本次调试回是个很棒的案例,于是催促Dino快确认设置PSP是否能万却解决问题, 到时候发一篇文章记录。

我:“如果真解决了,请在群里吼一声”

Dino:“用 __set_PSP 就可以了”

好的,正在欢呼找到问题根源时,群主建议检查CONTROL寄存器的SPSEL值。CONTROL是系统寄存器,它的SPSEL直观的告知当前处理器使用的是哪个栈。SPSEL(CONTROL寄存器第1位)为1表示使用PSP,为0表示使用MSP。

Dino:“CONTROL里的值是0”。

显然他没上实时操作系统,用的是MSP栈,我前面推断的场景崩了,脸被打得莫名其妙。

嘿~有趣了,为什么没使用PSP栈,偏偏修改它的值,程序就能正常执行,什么天方夜谭!

感兴趣的看官翻阅到文章的开头,以及Dino在C代码里如何设计APP_Main变量的,通过反汇编后,自己解释原因。

未完待续:以上内容是16点-18点的聊天记录概要,余下18点-22点聊天记录安排整理中。

获取完整聊天记录发送:“chat1201”。


推荐阅读