痞子衡嵌入式:如果i.MXRT离线无法启动,试着分析ROM启动日志


大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是恩智浦i.MXRT系列MCU的ROM启动日志

关于 i.MX RT 启动问题解决的文章,痞子衡写过非常多,其中大部分都是具体到某一类启动设备下的具体问题分析,比较依赖经验,这些经验当然是非常有用的。此外也有一篇 《启动失败先查看SRC_SBMRx寄存器》,这篇内容通用于全部启动设备,算是葵花宝典系列了。一直以来我们都当芯片启动 ROM 程序运行状态完全是个黑盒子,如果遇到异常,我们通常是去猜其可能遇到的问题,那么能不能不全靠猜呢?答案是可以的!这便是痞子衡今天要聊 ROM 启动日志:

一、ROM启动日志原理

我们知道 i.MX RT 系列上电都是片内固化的 ROM 程序代码先运行,由 ROM 来加载启动设备里的用户程序去执行,所以如果用户程序不能正常启动,一定是 ROM 程序执行过程中遇到了异常。

i.MX RT 初始 ROM 代码来源于它的老大哥 i.MX 处理器,翻看 i.MX 参考手册其中有关于 ROM Log Event 功能的描述,所谓 ROM Log Event 就是 ROM 程序在执行过程中将其重要节点事件(状态)按时间轴记录到一个日志缓冲区里(日志缓冲区通常在片内 RAM 固定地址处),这个日志内容显然对于分析 ROM 执行过程非常有用,那么 i.MX RT 系列 ROM 代码里有没有保留这个功能呢?很高兴,它还在!

下表记录了 i.MX RT 全系列型号的 ROM 启动日志缓冲区地址:

芯片 启动LOG地址 最大启动LOG长度(Bytes)
i.MXRT118x 0x3048a000 264
i.MXRT117x 0x2024ad78 264
i.MXRT116x 0x2024ad78 264
i.MXRT106x 0x2020523c 256
i.MXRT105x 0x202051c8 256
i.MXRT104x 0x2020523c 256
i.MXRT102x 0x2020515c 256
i.MXRT1015 0x2020515c 256
i.MXRT1011 0x20203d38 256
i.MXRT6xx 0x10017f00 264
i.MXRT5xx 0x10017ef8 264

二、获取ROM启动日志数据

上一节我们知道了 ROM 启动日志缓冲区存储地址,获取其数据的方法就简单了,可以直接连接上仿真器去读取。不过这里有需要注意的地方:如果是在 ROM 跳转用户程序之前发生的异常(日志体现为启动失败),那么内核 PC 应该还停留在 ROM 空间,这时候 RAM 区数据是完整的,无人破坏。如果是在 ROM 跳转到用户程序之后发生的异常(日志体现为启动成功),这时候用户程序已经开始执行了,这可能会破坏 RAM 区数据(如果用户程序链接文件里用到了存储启动日志的 RAM 区域),这时候启动日志内容就不一定有效了。

以 i.MXRT1170 为例,痞子衡找了一块开发板,上电后挂上 J-Link 调试器,使用 J-Link 命令行工具里的 SaveBin 命令从 0x2024ad78 地址处读取最大的日志数据存储到 bootlog.bin 文件中。这里需要注意在用 J-Link 连接目标设备时尽量不要选 MCU 型号,而用内核 CORTEX-M7 代替,这样可以防止选了 MCU 型号而自动加载执行相应配套初始化脚本(万一脚本里有片内 RAM 相关操作破坏日志数据)。

text 复制代码
命令格式:SaveBin <filename>, <addr>, <NumBytes>
命令解释:Save target memory range into binary file.

除了借助调试器,我们也可以借助芯片串行下载模式下配套的 MCUBoot 工具链(Flashloader+blhost)来获取 ROM 日志数据,具体可见 《MCUBootUtility v6.3发布,支持获取与解析启动日志》 一文 2.3 小节里的途径二。

三、解析ROM启动日志

i.MXRT 全系列 ROM 启动日志缓冲区数据结构并不是完全一样的,主要分为两个版本。其中 i.MXRT10xx 系列的日志结构如下,跟 i.MX 处理器差不多,每条日志内容压缩存储在一个 uint32_t 型变量里,最大支持 64 条日志(当实际日志超出 64 条时,后面的日志直接被忽略不记)。

C 复制代码
uint32_t pu_irom_log_buffer[64];

而 i.MXRT11xx 系列以及 i.MXRTxxx 系列的日志结构相比前一代有一些改进,其结构如下,首先增加了 entryIndex 用于记录有效的日志个数,同时也增加了 checkSum 用于校验全部日志的完整性(但实际意义并不大,ROM 异常运行时计算 checkSum 时机难以确定)。

C 复制代码
typedef struct _log_context
{
    uint32_t entryIndex;
    uint32_t logEntries[64];
    uint32_t checkSum;
} log_context_t;

此时最大日志个数依旧是 64(当实际日志超出 64 条时,会找到日志缓存区里排在最后的状态为 Fail 或者 Fatal 的日志,然后只保存其后面的正常日志并继续向下记录)。

text 复制代码
举例说明:当前记录到了第 65 条日志
 - 如果 logEntries[63:0] 里没有 Fail 或者 Fatal 状态的日志,那么清空数组,entryIndex 从 0 开始记录。  
 - 如果 logEntries[31] 是排在最后的 Fail 或者 Fatal 状态的日志,那么将 logEntries[63:32] 拷贝到 logEntries[31:0],entryIndex 重置为 32 再开始记录。

除了以上启动日志缓冲区数据结构差别之外,i.MXRT10xx 与 i.MXRT11xx/i.MXRTxxx 在单条日志值定义上也是完全不同的,不过具体如何解析每条日志内容,用户无需过多关注,这在痞子衡开发的 MCUBootUtility v6.3 软件里已经全部搞定了,用户可以直接查看解析后的日志结果。

还是继续以 i.MXRT1170 为例查看解析后的日志结果,我们在 FlexSPI1 连接的串行 NOR Flash 里下载一个能正常启动的 XIP 裸用户程序(非签名非加密),将芯片启动模式设为 2'b10,当看到程序正常执行后,挂上调试器读出启动日志数据(确保日志未被用户程序),并用 MCUBootUtility 软件解析如下,还是能够清晰地看到 ROM 执行过程信息的。

至此,恩智浦i.MXRT系列MCU的ROM启动日志痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页CSDN主页微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。