本帖最后由 小恩GG 于 2021-1-5 16:39 编辑
莫名的的复位—Bootable image结构的‘秘密’
前提前段时间,小编使用tensorflow lite库在MIMXRT1050 EVK开发板上部署CNN模型过程中,无意中发现如果工程在使用比较多大的SDRAM空间时,使用MCUBootUtility或MCUXpresso Secure Provisioning工具生成bootable image在MIMXRT1050 EVK开发板上运行过程中,调用CNN模型时会触发莫名的复位现象,通过打印SRC_SRSR寄存器发现是lockup_sysresetreq复位源引发的,但是通过调试工具如Jlink或OpenSDA直接烧录的话,工程代码却能正常运行,最后通过反复比较与测试发现复位现象与bootable image结构有关(不了解bootable image结构的小伙伴,可以通过此博文—《恩智浦i.MX RT1xxx系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd)了解,那我们开始吧。 细节介绍因为tensorflow lite库运行ML模型需要较大的RAM空间,而i.MX RT1050本身的FlexRAM只有512 byte,远远无法满足需求,所以需要SDRAM来提供充足的RAM空间,这就需要bootable image中添加DCD部分。
下面列出三组bootable image的IVT,Bootable image和DCD部分,分别对应:a) SD 卡的non-XIP启动,b) FlexSPI Nor flash的XIP启动,c) FlexSPI Nor flash的XIP启动,其中a,b项的bootable image是MCUXpresso Secure Provisioning工具生成的,而c项则是通过Jlink直接烧录的。 a) SD 卡的non-XIP启动
图 1
b) FlexSPI Nor flash的XIP启动 (MCUXpresso Secure Provisioning工具生成的)
图 2
c)FlexSPI Nor flash的XIP启动(Jlink直接烧录)
图 3
根据《恩智浦i.MX RT1xxx系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd),上面三组bootable image的IVT,Bootable image和DCD部分都是符合结构规范要求的,那到底是什么原因导致a,b项的bootable image不能正常工作呢?
问题解决经过多次比较与测试发现,问题与IVT的版本和application code的绝对地址设置有关(IVT的结构如下所示),其中结构体中的par代表版本号,通常为0x40或0x41,entry存储的是application code第一条执行指令的绝对地址,也有两种设置方式,一种是application code存储的首地址,另一种为application code的ResetISR中断函数指针指向的地址。 - /** @ref hab_header structure */
- typedef struct hab_hdr {
- uint8_t tag; /**< Tag field */
- uint8_t len[2]; /**< Length field in bytes (big-endian) */
- uint8_t par; /**< Parameters field */
- } hab_hdr_t;
- /** @ref ivt structure */
- struct hab_ivt_v0 {
- /** @ref hdr with tag #HAB_TAG_IVT0, length and HAB version fields */
- hab_hdr_t hdr;
- /** Absolute address of the first instruction to execute from the image */
- uint32_t entry;
- /** Reserved in this version of HAB: should be NULL. */
- uint32_t reserved1;
- /** Absolute address of the image DCD: may be NULL. */
- uint32_t dcd;
- /** Absolute address of the Boot Data: may be NULL, but not interpreted any further by HAB */
- uint32_t boot_data;
- /** Absolute address of the IVT.*/
- uint32_t self;
- /** Absolute address of the image CSF.*/
- uint32_t csf;
- /** Reserved in this version of HAB: should be zero. */
- uint32_t reserved2;
- };
复制代码本着大胆猜测,小心求证的心态,发现将a,b项bootable image 中的par和entry按照c项格式修改后(如下所示),代码居然能成功地运行了。 a) SD 卡的non-XIP启动
图 4
b) FlexSPI Nor flash的XIP启动 (MCUXpresso Secure Provisioning工具生成的)
图 5
原因探究跟同事讨论后,发现问题的根本原因与ROM code根据entry值实现跳转的方式有关,跳转函数的具体代码如下所示
- void jump_to_entry(uint32_t entry)
- {
- typedef void (*application_callback_t)(void);
- static application_callback_t s_app_callback;
- pu_irom_mpu_disable();
- __DMB();
- __DSB();
- __ISB();
- // The entry point is the absolute address of the call back function
- if ((uint32_t)entry & 1)
- {
- s_app_callback = (application_callback_t)entry;
- }
- // The entry point is the base address of vector table
- else
- {
- static uint32_t s_stack_pointer;
- // Ensure Core read vector table for destination instead of register
- volatile uint32_t *vector_table = (volatile uint32_t *)entry;
- s_stack_pointer = vector_table[0];
- s_app_callback = (application_callback_t)vector_table[1];
- // Update Stack pointer
- __set_MSP(s_stack_pointer);
- __set_PSP(s_stack_pointer);
- }
- __DSB();
- __ISB();
- // Jump to user application in the end
- s_app_callback();
- // Should never reach here
- __NOP();
- __NOP();
- }
复制代码 当entry值是复位向量(Reset_Handler)函数地址(即奇地址)时,ROM code是直接通过函数调用的方式跳转到复位函数执行,而如果entry值是中断向量表首地址(即偶地址),ROM code会先将当前SP重设到application code指定的栈顶,然后再跳转到复位函数。
但是,MCUXpresso IDE的ResetISR函数没有重设SP的动作,所以当IVT中的entry是复位向量时,ROM code跳转到application code后依旧延用ROM code中的栈,而如果栈空间不足时,application code就会发生程序跑飞或Hard fault等错误。所以解决方法就需要手动在在MCUXpresso IDE的startup流程中加入重设SP操作,具体如下。
- __attribute__ ((section(".after_vectors.reset")))
- void ResetISR(void) {
- __asm volatile ("cpsid i");
- /* 新增SP重设代码 */
- __asm volatile ("MSR msp, %0" : : "r" (&_vStackTop) : );
- __asm volatile ("MSR psp, %0" : : "r" (&_vStackTop) : );
- SystemInit();
- // ...
- }
复制代码 参考:IVT里的不同entry设置可能会造成i.MXRT1xxx系列启动App后发生异常跑飞
|