在上一篇文章<a href="http://mcu.eetrend.com/content/2022/100559903.html">“i.MX RT1170上串行NOR Flash双程序可交替启动设计”</a>里,详细介绍了i.MX RT11xx 系列上的双程序启动设计,本质上就是在双备份程序启动基础上增加了 image版本控制,所以两份image可以按版本优先级来灵活选择启动,而不是死板地靠物理地址高低来定启动顺序。
i.MX RTxxx 系列上(RT500/600)也有双程序可交替启动特性,其主体设计逻辑基本上跟i.MX RT1170是差不多的,只是一些细节处略有差异(比如可启动image 结构不同、otp 配置地址不同、签名实现不同、非易失性寄存器暂存状态设计不同、image 版本判断逻辑略有不同等),除此之外i.MX RTxxx上在验证image完整性方面除了签名外,还有一种相对平民化的CRC32校验可供选择,这也是今天本文要介绍的重点:
<strong><font color="#38447a">一、与i.MX RT11xx系列双程序启动细节差异</font> </strong>
本文不打算从头开始完整介绍 i.MX RTxxx 上双程序可交替启动特性,这里只讲和 i.MX RT11xx 上的差异点,其余流程直接参考<a href="http://mcu.eetrend.com/content/2022/100559903.html">“i.MX RT1170上串行NOR Flash双程序可交替启动设计”</a>一文。
<strong>1.1 恢复启动的接口外设不同</strong>
第一点不同其实与本文要讨论的 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来表达的,避免表达混乱。
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252469-1.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong>1.2 可启动 image 结构不同</strong>
i.MX RT1170上最简易可启动image结构比较复杂(包含 FDCB、img_ver、IVT、BD、App),而 i.MX RTxxx上就比较简单了(仅需 FDCB、img_ver、App),但是好在两者关于 image version 头结构定义以及偏移位置是完全一致的(0x600)。
<pre style="overflow-x:auto; background-color:#e9e9e9;">typedef struct
{
uint16_t version; // 版本值
uint16_t inversion; // version值的取反(~version)
} img_ver_t;</pre>
此外i.MX RT1170上第一个FlexSPI 的 AHB 映射地址是 0x3000_0000,而 i.MX RTxxx 上第一个 FlexSPI 的 AHB 映射地址是0x0800_0000,这是系统设计差异,需要注意。
注:下图中示意地址均是 Flash 偏移地址,没有包含 AHB 映射地址,另外这里假设第二份 image 偏移地址在 0x400000(具体是由 otp 配置值来决定的)。
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252470-2.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong>1.3 使能双程序启动的otp配置地址不同</strong>
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寄存器的值。
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252471-3.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
这次我们要在 MIMXRT595-EVK 板卡上实测,这个板子 FlexSPI0 上挂了两片 Flash,默认连接64MB OctalFlash,还有一片 8MB QuadSPI Flash(需要做板子改动才能使能)。为了跟之前测试保持一致,还是借助 MCUBootUtility 工具将 Second image offset 烧录为 0x10,FlexSPI remap size 保持默认 0,即第二份 image 偏移地址在Flash 0x400000(4MB)处,最大 image 长度也是 4MB。
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252472-4.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong>1.4 暂存状态的非易失寄存器有差异</strong>
i.MX RT1170 是用非易失寄存器 SRC_GPR10 其中 2bit 来记录当前启动状态的,而 i.MX RTxxx 上则复杂得多,它采用了 SYSCTL0 外设里的一个非易失寄存器的全部 32bit 来暂存启动状态。
<pre style="overflow-x:auto; background-color:#e9e9e9;">// 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</pre>
这个32bit寄存器功能原型如下,它不单纯是用做双程序启动的状态记录了,还糅合了 ROM API 功能。具体用法可以在芯片参考手册 ROM API 小节找到,这里不具体展开了,不是本文重点。
<pre style="overflow-x:auto; background-color:#e9e9e9;">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;</pre>
<strong>1.5 image 版本判断逻辑不同</strong>
在 i.MX RT1170 上 image version 头有效的条件一定是其高低16bit符合取反关系,而 i.MX RTxxx 上除了这个条件外,其认定 0xFFFFFFFF 也是一个有效版本(被定为最低版本)。
芯片参考手册里有比较详细的 version 判断逻辑如下,这个逻辑跟 i.MX RT1170 上差异还是比较大的,i.MX RTxxx 上 BootROM 只会启动包含有效版本号的 image,版本有效性是 image 能被启动的一个必要条件,不像 i.MX RT1170 上版本信息只是单纯用来判断启动顺序,不作为 image 是否有效的标准。
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252473-5.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
在MIMXRT595-EVK开发板上对 image 版本设置情况也做了比较全面的实测,测试结果如下:
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252474-6.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong><font color="#38447a">二、测试CRC32校验双程序启动</font> </strong>
现在来到本文的重头戏了,如何使能 image 的 CRC32 检验启动?这个设计其实最早可追溯到 Kinetis 系列,我有一篇旧文 "Kinetis BOOT特性(完整性检测)",文章很详细地介绍了 Kinetis 系列 BootROM 里是如何支持 CRC32 校验的。
<strong>2.1 启动头CRC32参数存储位置</strong>
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 结果
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252475-7.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong>2.2 使能CRC32校验的条件</strong>
当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对齐。
<strong>2.3 具体CRC32算法选项</strong>
关于CRC32 算法的具体实现有很多分支,BootROM 中使用的比较主流的 MPEG2 分支,其在计算 image 具体 CRC 时主要借助了芯片内部的 CRC 模块(这个模块也常见于恩智浦 LPC 系列芯片上),这个 CRC 模块支持三种固定的 CRC 算法多项式(多项式系数不是可自由配置的),BootROM 用得就是最后一个模式选项 CRC-32:
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252476-8.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
BootROM中对 CRC 模块的配置代码如下:
<pre style="overflow-x:auto; background-color:#e9e9e9;">#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;
}</pre>
<strong>2.4 利用工具自动添加CRC校验参数</strong>
对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。
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252477-9.png&…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
这时候在工具通用编程器模式(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。
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252478-10.png…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong>2.5 手动验证CRC32校验值的方法</strong>
在使用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!按钮可以得到结果,这里我们看到两个结果是一致的:
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252479-11.png…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong>2.6 含CRC32校验的双程序启动测试</strong>
现在可以利用 image 0 - gpio_led_output_delay200ms_crc.bin 和 image 1 - gpio_led_output_delay2s_crc.bin 测试双程序启动了,继续借助 MCUBootUtility 工具的通用编程器模式将其分别下载进 0x0 和 0x400000 地址处,必要时还可以手动调整两个 image 里的版本号,测试过程中也可以稍微修改一下 image 数据再下载或者下载后再擦除一些 image 区域(故意让CRC32校验失败),最终测试结果如下:
<center><img src="http://mcu.eetrend.com/files/2022-05/wen_zhang_/100560055-252480-12.png…; alt=“i.MX RT500/600系列上串行NOR Flash双程序可交替启动设计"></center>
<strong><font color="#38447a">三、一些关于image的注意事项</font> </strong>
<ul>
<li>
<p><em>Note1:虽然文中所有的测试均是针对 XIP image,但这个双程序可交替启动特性对于 Non-XIP image 也同样适用。</em></p>
</li>
<li>
<p><em>Note2:如果是 XIP image,其链接地址要求固定在 Flash 偏移 0x1000 处(如果 Flash 挂在第一个 FlexSPI 上,其 AHB 地址就是 0x08001000)。</em></p>
</li>
<li>
<p><em>Note3:如果是 Non-XIP image,在 SDK 包里无法直接生成含启动头的 Non-XIP image binary,这时候可以先使用 MCUBootUtility 主界面的 All-In-One 操作下载一次 image,再通过通用编程器界面 Read 操作读回来便是含启动头的 Non-XIP image binary。</em></p>
</li>
<li>
<p><em>Note4:使能 CRC32 校验的双程序可交替启动,也是同时支持 XIP image 和 Non-XIP image 的。</em></p>
</li>
</ul>
<strong>相关阅读</strong>
<a href="http://mcu.eetrend.com/content/2022/100558432.html"> 《i.MX RT1060/1010上串行NOR Flash冗余程序启动设计》</a>
<a href="http://mcu.eetrend.com/content/2022/100559903.html"> 《i.MX RT1170上串行NOR Flash双程序可交替启动设计》</a>
来源:<a href="https://mp.weixin.qq.com/s/LTRuxJrSBKHOCwaZexaHNA">恩智浦MCU加油站</a>
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。