在上一篇文章“i.MX RT1170上串行NOR Flash双程序可交替启动设计”里,详细介绍了i.MX RT11xx 系列上的双程序启动设计,本质上就是在双备份程序启动基础上增加了 image版本控制,所以两份image可以按版本优先级来灵活选择启动,而不是死板地靠物理地址高低来定启动顺序。
i.MX RTxxx 系列上(RT500/600)也有双程序可交替启动特性,其主体设计逻辑基本上跟i.MX RT1170是差不多的,只是一些细节处略有差异(比如可启动image 结构不同、otp 配置地址不同、签名实现不同、非易失性寄存器暂存状态设计不同、image 版本判断逻辑略有不同等),除此之外i.MX RTxxx上在验证image完整性方面除了签名外,还有一种相对平民化的CRC32校验可供选择,这也是今天本文要介绍的重点:
一、与i.MX RT11xx系列双程序启动细节差异
本文不打算从头开始完整介绍 i.MX RTxxx 上双程序可交替启动特性,这里只讲和 i.MX RT11xx 上的差异点,其余流程直接参考“i.MX RT1170上串行NOR Flash双程序可交替启动设计”一文。
1.1 恢复启动的接口外设不同
第一点不同其实与本文要讨论的 FlexSPI 双程序启动特性无关,因为我们要聊的还是在一片挂载在 FlexSPI 上的串行 NOR Flash 里做双程序设计,就是下图中的 image 0 和 image 1,不涉及 Flexcomm SPI 接口 Flash B 里的 image 2(在 i.MX RT1170 上这个外设是 LPSPI)。
在介绍 i.MX RT1170 双程序启动一文里我们用了 image L/H 来表示 image 0/1,这里还是恢复使用 image 0/1 来表示,因为后面我们要看的i.MX RT500/600 参考手册启动流程图里就是用 image 0/1来表达的,避免表达混乱。
1.2 可启动 image 结构不同
i.MX RT1170上最简易可启动image结构比较复杂(包含 FDCB、img_ver、IVT、BD、App),而 i.MX RTxxx上就比较简单了(仅需 FDCB、img_ver、App),但是好在两者关于 image version 头结构定义以及偏移位置是完全一致的(0x600)。
typedef struct
{
uint16_t version; // 版本值
uint16_t inversion; // version值的取反(~version)
} img_ver_t;
此外i.MX RT1170上第一个FlexSPI 的 AHB 映射地址是 0x3000_0000,而 i.MX RTxxx 上第一个 FlexSPI 的 AHB 映射地址是0x0800_0000,这是系统设计差异,需要注意。
注:下图中示意地址均是 Flash 偏移地址,没有包含 AHB 映射地址,另外这里假设第二份 image 偏移地址在 0x400000(具体是由 otp 配置值来决定的)。
1.3 使能双程序启动的otp配置地址不同
i.MX RTxxx 上关于使能双程序启动的 otp 配置定义与i.MX RT1170 上是一致的,只是因为两者 otp 空间设计不同,所以具体配置地址不同。i.MX RTxxx 上具体配置在 otp BOOT_CFG2/3 上面:
Remap功能的ADDR_START寄存器固定设为Flash 起始映射地址。
otp 0x188[31:28] - FlexSPI remap size, App的最大长度,标识了第一份App的结束地址,该值加上ADDR_START后被填入Remap功能的ADDR_END寄存器。
otp 0x18C[31:22] - Second image offset,标识了第二份App的起始地址(在Flash中偏移位置),即填入Remap功能的ADDR_OFFSET寄存器的值。
这次我们要在 MIMXRT595-EVK 板卡上实测,这个板子 FlexSPI0 上挂了两片 Flash,默认连接64MB OctalFlash,还有一片 8MB QuadSPI Flash(需要做板子改动才能使能)。为了跟之前测试保持一致,还是借助 MCUBootUtility 工具将 Second image offset 烧录为 0x10,FlexSPI remap size 保持默认 0,即第二份 image 偏移地址在Flash 0x400000(4MB)处,最大 image 长度也是 4MB。
1.4 暂存状态的非易失寄存器有差异
i.MX RT1170 是用非易失寄存器 SRC_GPR10 其中 2bit 来记录当前启动状态的,而 i.MX RTxxx 上则复杂得多,它采用了 SYSCTL0 外设里的一个非易失寄存器的全部 32bit 来暂存启动状态。
// Load redundant boot options stored in specific register
#define LOAD_REDUNDANT_BOOT_OPTIONS() (*(volatile uint32_t *)(SYSCTL0_BASE + 0x384))
// Store redundant boot options in specific register before system reset
#define SET_REDUNDANT_BOOT_OPTIONS(val) ((*(volatile uint32_t *)(SYSCTL0_BASE + 0x384)) = val
这个32bit寄存器功能原型如下,它不单纯是用做双程序启动的状态记录了,还糅合了 ROM API 功能。具体用法可以在芯片参考手册 ROM API 小节找到,这里不具体展开了,不是本文重点。
typedef struct _user_app_boot_invoke_option
{
union
{
struct
{
uint32_t reserved : 8;
uint32_t boot_image_index : 4;
uint32_t instance : 4;
uint32_t boot_interface : 4;
uint32_t mode : 4;
uint32_t tag : 8;
} B;
uint32_t U;
} option;
} user_app_boot_invoke_option_t;
1.5 image 版本判断逻辑不同
在 i.MX RT1170 上 image version 头有效的条件一定是其高低16bit符合取反关系,而 i.MX RTxxx 上除了这个条件外,其认定 0xFFFFFFFF 也是一个有效版本(被定为最低版本)。
芯片参考手册里有比较详细的 version 判断逻辑如下,这个逻辑跟 i.MX RT1170 上差异还是比较大的,i.MX RTxxx 上 BootROM 只会启动包含有效版本号的 image,版本有效性是 image 能被启动的一个必要条件,不像 i.MX RT1170 上版本信息只是单纯用来判断启动顺序,不作为 image 是否有效的标准。
在MIMXRT595-EVK开发板上对 image 版本设置情况也做了比较全面的实测,测试结果如下:
二、测试CRC32校验双程序启动
现在来到本文的重头戏了,如何使能 image 的 CRC32 检验启动?这个设计其实最早可追溯到 Kinetis 系列,我有一篇旧文 "Kinetis BOOT特性(完整性检测)",文章很详细地介绍了 Kinetis 系列 BootROM 里是如何支持 CRC32 校验的。
2.1 启动头CRC32参数存储位置
i.MX RTxxx BootROM 关于 CRC32 校验的设计与 Kinetis 非常类似,最大的区别就在于存储 CRC32 三大参数(起始地址,校验长度,校验值)的位置。i.MX RTxxx 上也是放在了 App 默认中断向量表里的保留空间里(offset 0x20, 0x28, 0x34),共 12 个字节。
offset 0x34 - imageLoadAddress: App加载后中断向量表首地址,也决定CRC校验起始地址
- 对于XIP image,一般固定为0x08001000(App无需加载)
- 对于Non-XIP image,App加载前存储起始地址是0x08001000,加载后到指定链接的RAM 地址,CRC计算和校验是发生在App加载后。
offset 0x20 - imageLength: 决定CRC校验总长度,一般是App 的长度(从中断向量表首地址开始到代码体结束)
offset 0x28 - crcChecksum: CRC校验值,[imageLoadAddress : imageLoadAddress + imageLength] 范围内数据的正确CRC32 结果
2.2 使能CRC32校验的条件
当App 默认中断向量表里 offset 0x24 处的imageType[7:0] 类型为 0x02 或者 0x05,且 offset 0x20 处的 imageLength 不为 0 时,CRC32 校验的功能就会被使能。BootROM 在做 CRC32 计算时主要有如下两个注意事项:
Note1: 指定的CRC计算范围如果包含crcChecksum这4bytes的话,在计算CRC时会自动跳过这4bytes。
Note2: 指定的CRC计算长度如果不是4字节对齐,CRC数据计算到最后会自动补0对齐。
2.3 具体CRC32算法选项
关于CRC32 算法的具体实现有很多分支,BootROM 中使用的比较主流的 MPEG2 分支,其在计算 image 具体 CRC 时主要借助了芯片内部的 CRC 模块(这个模块也常见于恩智浦 LPC 系列芯片上),这个 CRC 模块支持三种固定的 CRC 算法多项式(多项式系数不是可自由配置的),BootROM 用得就是最后一个模式选项 CRC-32:
BootROM中对 CRC 模块的配置代码如下:
#include "fsl_crc.h"
void crc32_init(void)
{
crc_config_t crcUserConfigPtr;
CRC_GetDefaultConfig(&crcUserConfigPtr);
crcUserConfigPtr.seed = 0xffffffffU;
crcUserConfigPtr.polynomial = kCRC_Polynomial_CRC_32;
crcUserConfigPtr.reverseIn = false;
crcUserConfigPtr.reverseOut = false;
crcUserConfigPtr.complementIn = false;
crcUserConfigPtr.complementOut = false;
}
2.4 利用工具自动添加CRC校验参数
对CRC32 校验启动的原理了解差不多了,我们现在在 MIMXRT595-EVK 开发板上实测一下,跟前面测试一样,先使用\SDK_2.10.1_EVK-MIMXRT595\boards\evkmimxrt595\driver_examples\gpio\led_output\iar\flash_debug例程生成两个闪灯间隔时间不同的程序镜像文件:image 0 -gpio_led_output_delay200ms.bin 和 image 1 -gpio_led_output_delay2s.bin。
然后借助MCUBootUtility 工具(需要 v3.5.0 版本及以上),在 Secure Boot Type 里选择 Plain CRC ImageBoot,点击 All-In-One 下载按钮(两个文件分别做两次同样的下载流程),工具会自动在 image 相应地方填充进所需的 CRC32 参数并下载进 Flash。
这时候在工具通用编程器模式(Boot Device Memory)里我们再读回 image 保存就可以得到两个含 CRC32 校验的程序镜像文件 image 0 -gpio_led_output_delay200ms_crc.bin 和 image 1 -gpio_led_output_delay2s_crc.bin。
以image 0 为例,根据 0x08001020 处的imageLength 信息显示,image 0 App 本身长度为 0x36e8 字节,而 App 起始偏移是 0x1000,所以我们直接是从偏移 0 地址处开始读回 0x46e8 字节作为gpio_led_output_delay200ms_crc.bin 文件数据。此外 image 0 的 CRC32 校验值已经填好了,是 0x4d8957d8。
2.5 手动验证CRC32校验值的方法
在使用image 0 - gpio_led_output_delay200ms_crc.bin 和 image 1- gpio_led_output_delay2s_crc.bin 做双程序启动前,我们可以先手动地验证下其中的 CRC32 校验值是否正确,痞子衡找到一个在线计算 CRC 的网站:
CRC在线校验网站:http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
在这个网站里把模式选好,然后从 gpio_led_output_delay200ms_crc.bin 文件里仅拷贝出App 部分的数据放到网站 CRC Input Data 框(注意要手动删除 crcChecksum 四个字节,另外还要检查总数据字节长度是否按 4 对齐,如果不对齐,要在数据末尾按格式补上相应的 00),最后点击网站上的 Calculate CRC!按钮可以得到结果,这里我们看到两个结果是一致的:
2.6 含CRC32校验的双程序启动测试
现在可以利用 image 0 - gpio_led_output_delay200ms_crc.bin 和 image 1 - gpio_led_output_delay2s_crc.bin 测试双程序启动了,继续借助 MCUBootUtility 工具的通用编程器模式将其分别下载进 0x0 和 0x400000 地址处,必要时还可以手动调整两个 image 里的版本号,测试过程中也可以稍微修改一下 image 数据再下载或者下载后再擦除一些 image 区域(故意让CRC32校验失败),最终测试结果如下:
三、一些关于image的注意事项
-
Note1:虽然文中所有的测试均是针对 XIP image,但这个双程序可交替启动特性对于 Non-XIP image 也同样适用。
-
Note2:如果是 XIP image,其链接地址要求固定在 Flash 偏移 0x1000 处(如果 Flash 挂在第一个 FlexSPI 上,其 AHB 地址就是 0x08001000)。
-
Note3:如果是 Non-XIP image,在 SDK 包里无法直接生成含启动头的 Non-XIP image binary,这时候可以先使用 MCUBootUtility 主界面的 All-In-One 操作下载一次 image,再通过通用编程器界面 Read 操作读回来便是含启动头的 Non-XIP image binary。
-
Note4:使能 CRC32 校验的双程序可交替启动,也是同时支持 XIP image 和 Non-XIP image 的。
相关阅读
《i.MX RT1060/1010上串行NOR Flash冗余程序启动设计》
《i.MX RT1170上串行NOR Flash双程序可交替启动设计》
来源:恩智浦MCU加油站
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。