大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是IAR环境下无法直接下载调试i.MXRT分散链接工程的解决方案。
分散链接与加载一直是嵌入式领域比较劝退新手的难题,在恩智浦 i.MXRT 系列为代表的多存储器架构的 MCU 上,分散链接问题体现得尤为明显,毕竟你在链接应用程序各种段(section)时可能会面对包括内部 ITCM/DTCM/OCRAM 和外部 Flash/SDRAM/PSRAM/HyperRAM 等多种存储器空间选择。
虽然存储器空间选择很多,但是一个最终可离线启动的 i.MXRT 程序(即能被下载进外部非易失存储器,且能被 BootROM 加载启动)其 readonly 段应该是一段连续的数据(SREC/HEX格式镜像文件里仅能包含一段空间地址),即要链接在一个主存储器空间里,这也意味着其它链接在非主存储器空间的 .text 段应该使用重定向方法来实现,不可直接原地链接,参考痞子衡文章 《IAR下将源文件代码重定向到任意RAM中的方法》。
最近有一个 i.MXRT1060 客户,他们就遇到了分散链接工程调试问题,工程 readonly 段被直接分散链接到了两个不同的外部存储器空间,没有用重定向方法,这虽然不符合离线启动要求,但是在 IAR 下直接下载调试也会报错,这是怎么回事?
一、引出客户问题
我们再进一步描述客户工程分散链接问题,下图包含了 i.MXRT 架构下程序段的全部链接选择,根据这些选择组合,我们能产生多种不同的工程链接文件。
先来看不涉及分散链接的简单情况,即 readonly 段全在 Flash 里,readwrite 段在一个或多个 RAM 空间里,这种情况下 IAR 下载调试没有什么特殊注意事项,flashloader 会负责外部 Flash 初始化,并将 readonly 段数据下载进 Flash,然后宏文件负责外部 RAM 初始化,在线调试一切正常。
Case1:APP readonly text/data1 + APP readwrite data2/3/4
再来看第二种情况,这里开始涉及分散链接,readonly 段分散在多个 RAM 空间,readwrite 段在一个或多个 RAM 空间里。这种情况下因为没有链接在 Flash 空间,因此无需 flashloader,完全由宏文件将相关外部 RAM 初始化好,多个 readonly 段都能正常下载,在线调试一切正常。
Case2:APP readonly text/data2/3/4 + APP readonly text/data2/3/4 + APP readwrite data2/3/4
第三种情况再复杂一点,readonly 段除了在 Flash 空间外,还有一部分放在了内部 RAM 里,然后 readwrite 段依然在一个或多个 RAM 空间里。这种情况下 IAR 下载调试感觉上应该没问题,因为内部 RAM 无需初始化可直接访问,两个不连续 readonly 段原则上可以下载,但是很遗憾,IAR 会报错,其 flashloader 无法处理放在内部 RAM 的 readonly 段,调试无法进行。
Case3:APP readonly text/data1 + APP readonly text/data2 + APP readwrite data2/3/4
最后一种分散链接的情况最复杂,也是客户的问题所在,readonly 段除了在 Flash 空间外,还有一部分放在了外部 RAM,然后 readwrite 段在一个或多个 RAM 空间里。这种情况下 IAR 下载调试一定会出问题,默认 flashloader 只做了 Flash 初始化,并不负责初始化外部 RAM,因此部分 readonly 段往外部 RAM 下载时会报错,工程宏文件虽然负责初始化外部 RAM,但其执行阶段在 flashloader 作用之后,鞭长莫及。
Case4:APP readonly text/data1 + APP readonly text/data3/4 + APP readwrite data2/3/4
二、复现客户问题
我们在恩智浦官方 MIMXRT1060-EVK 板上复现一下客户问题,使用 \SDK_2.11.0_EVK-MIMXRT1060\boards\evkmimxrt1060\demo_apps\hello_world\iar 工程,原工程有很多 Build,我们就选用 flexspi_nor_sdram build,它用到了两块外部存储器,符合客户场景。在这个 build 里 readonly 段都链在外部 Flash 里,readwrite 段都链接在外部 SDRAM 里,显然这个情况属于第一节介绍的 case1:
1. Flash初始化工作:\IAR Systems\Embedded Workbench 9.10.2\arm\config\flashloader\NXP\FlashIMXRT1060_FlexSPI.out
2. SDRAM初始化工作:\SDK_2.11.0_EVK-MIMXRT1060\boards\evkmimxrt1060\demo_apps\hello_world\iar\evkmimxrt1060_sdram_init.mac
我们现在要将工程稍微改动一下,在工程源文件里定义一个 sw_delay() 函数(记得要在 main 函数里调用一下),并且将其指定在自定义 .sdramCodeSection 段里:
#pragma default_function_attributes = @ ".sdramCodeSection"
void sw_delay(void)
{
__NOP();
}
#pragma default_function_attributes =
然后在工程链接文件里将这个自定义 .sdramCodeSection 段放到 SDRAM 空间里,这样我们在外部 Flash 和 SDRAM 空间里就都有 readonly 段了,跟客户情况一致了。
place in DATA3_region { section .sdramCodeSection };
板卡上电,直接用板载 DAP-Link 调试器在线下载工程(为了减少对板子设置的依赖,我们将调试器复位类型改为 Core),下载过程中 IAR 果然一直在报错,如果你忽略错误继续调试,虽然断点会停在 main 函数,但是只要单步进放到 SDRAM 空间的函数里时,程序就会跑飞进 hardfault,因为 SDRAM 中根本就没有正确的 .sdramCodeSection 段数据。
三、尝试仅借助宏文件(.mac)解决问题
分析到这里,其实你应该知道问题出在哪里了,工程配套宏文件 evkmimxrt1060_sdram_init.mac 本应负责 SDRAM 初始化,但是其执行顺序在 FlashIMXRT1060_FlexSPI.out 作用之后,所以没有产生其该有的效果,这个具体可见痞子衡旧文 《IAR内部C-SPY调试组件配套宏文件(.mac)用法介绍》 3.1 小节,有非常详细的解释。
现在的解决思路就是,如何让 evkmimxrt1060_sdram_init.mac 里的 SDRAM 初始化语句在 flashloader 作用之前生效,所以我们很自然地想在 flashloader 配套的宏文件 FlashIMXRT1060_FlexSPI.mac 里的 execUserFlashInit() 接口里将 SDRAM 初始化语句都加上,但是很遗憾,这招不凑效,其实在第一节介绍的 case3 里就应该认清现实了,内部 RAM 无需初始化 IAR 也无法正常下载。
四、借助双Flashloader解决问题
其实 IAR 软件设计里,对于两个 readonly 段,只要其中有一个段被放入了 Flash 里(即需要 flashloader),那么另外一个段不管是不是放在 Flash 里也需要有相应 flashloader,这里痞子衡要吐槽下 IAR 的设计,有点呆板了。
所以本文案例里解决问题的关键就是为 SDRAM 也设计一个 flashloader,具体制作方法可以参考痞子衡旧文 《串行NOR Flash下载算法(IAR EWARM篇)》。因为 SDRAM 擦写其实也不需要什么特殊命令时序,就是单纯 AHB 方式地写就行了,所以这个 SDRAM 版本的 flashloader 就是个傀儡 flashloader 而已。
痞子衡为了让这个傀儡 flashloader 更通用一些,是按如下方式实现三个主要 flashloader API 的,其中 FlashInit() 函数里故意没有加 SEMC 模块初始化代码,就是为了让这个 flashloader 适用所有类型的 RAM(ITCM/DTCM/OCRAM/SDRAM/PSRAM/HyperRAM),外设初始化工作放在傀儡 flashloader 配套宏文件里去完成。
FlashInit() - 什么都不做,直接返回
FlashWrite() - 用 memcpy 函数实现
FlashErase() - 用 memset 函数实现
最终 RAM 型通用 flashloader 源码工程地址如下:
https://github.com/JayHeng/imxrt-tool-flash-algo/tree/master/boards/nxp_evkmimxrt1060_rev.a1/ram_algo/IAR
我们把新生成的 SDRAM flashloader 相关的所有文件(.out/.flash/.mac)放到对应 IAR 系统目录下,并且修改原来的 FlashIMXRT1060_EVK_FlexSPI.board 文件,加入 SDRAM 相关的部分:
1. FlashIMXRT1060_SEMC.mac 文件基本沿用 evkmimxrt1060_sdram_init.mac 文件,只是 setup 宏函数从 execUserPreload 换到 execUserFlashInit
2. FlashIMXRT1060_SEMC.flash 文件内容按 FlashIMXRT1060_FlexSPI1.flash 写即可,注意文件后缀一定要是 .flash, IAR 只认这个后缀。
现在再去下载调试,就一切正常了,说明双 Flashloader 解决方案生效了。本例是以 IAR flashloader 为例的,如果用 J-Link flashloader 也是可以的,一样的原理制作两个 Flashloader 即可。
至此,IAR环境下无法直接下载调试i.MXRT分散链接工程的解决方案痞子衡便介绍完毕了,掌声在哪里~~~