痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计


  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT1060,1010上串行NOR Flash冗余程序启动设计

  工业产品设计里经常会有冗余程序/备份程序设计的需求,因为在工业环境里要求设备能够持续稳定运行,不能轻易宕机,但现实环境中常常有各种意外发生,其中一个常见的意外就是设备主控 MCU 程序被破坏。为了降低程序损坏这种意外带来的影响,一个很典型的做法就是增加 MCU 程序的份数,第一份被破坏,就启动第二份...,用程序数量的增加来降低启动失败率,这属于概率学手段范畴。

  我们知道恩智浦 i.MXRT 系列 MCU 内部没有非易失性存储器,它需要搭配外部存储器来工作,可选存储器类型非常多: NOR Flash, NAND Flash, eMMC, SD卡都行。其中对于 NAND 型存储器,冗余程序设计是必备的,因为 NAND 本身允许坏块现象存在,所以不得不做冗余设计。而对于 NOR 型存储器,冗余设计则不是必需的,偏偏串行 NOR Flash 是 i.MXRT 最常用的搭档,那该怎么办?别急,今天痞子衡介绍的就是 i.MXRT1010/1060 上串行 NOR Flash 冗余程序设计,恩智浦已经都给你考虑到了。

  • Note:本文所涉及的串行 NOR Flash 冗余程序设计在早期的 i.MXRT1050/1020/1015 型号上不被支持。

一、初识冗余程序启动

  在 i.MXRT 上可以挂载串行 NOR Flash 去启动程序的外设有两个,分别是 FlexSPI 和 LPSPI,如下图所示。其中连接在 FlexSPI 上的 Flash A 属于主启动设备,连接在 LPSPI 上的 Flash B 属于备份启动设备(关于备份设备启动详见痞子衡旧文 《从Serial(1-bit SPI) EEPROM/NOR恢复启动》)。

  • Note: 并不是所有 i.MXRT 型号都支持 LPSPI 外设挂载存储器去启动。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第1张图片

  用两片 NOR Flash 去完成冗余程序设计当然是没问题,但也相应增加了硬件成本,而本文今天要讨论得不是这种冗余程序设计,我们要聊的是在一片挂载在 FlexSPI 上的串行 NOR Flash 里做冗余程序设计,就是图中的 image 0 和 image 1。

  冗余程序启动流程其实特别简单,上电永远先启动物理地址靠前的 image 0,如果 image 0 被破坏了,则启动 image 1。原则上 image 0 和 image 1 应该是完全一样的(链接地址也一样,并且中途不需要搬移这两份 image 数据,这个是靠芯片里的一个黑科技做到的,下文会细讲),毕竟是冗余设计,不过你要执意放两份不完全一样的 image,倒也没问题。

二、实测冗余程序启动

  我们知道 i.MXRT 芯片上电总是先运行厂商固化好的 BootROM,所以冗余程序启动设计是做在 BootROM 代码里的。痞子衡之前有一篇旧文 《了解i.MXRT1060系列ROM中串行NOR Flash启动初始化流程优化点》 中 2.1 节其实已经简单地提到了冗余程序设计,其主要借助了芯片系统 FlexSPI 地址重映射(Remap)功能,这个功能是在恩智浦后期推出的 i.MXRT1060/1010 等型号上才有的,这也是上文所说的黑科技。关于这个黑科技,痞子衡也有旧文 《利用i.MXRT1060,1010上新增的FlexSPI地址重映射(Remap)功能可安全OTA》。

  有了 FlexSPI Remap 黑科技存在,我们就可以将同一份 image binary 放在 Flash 中两个不同位置,并且不用做擦除编程操作来交换其位置,就可以实现各自的启动执行。如果没有这个黑科技,我们只能老老实实做数据搬移,这会增加擦除次数从而影响 Flash 使用寿命。

  原理搞清楚了,现在我们在板子上实测一下这个功能,看看如何正确地放两份 image 进 Flash,哪些情况会导致 image 0 启动失败从而去启动 image 1。我们就以恩智浦官方 MIMXRT1060-EVK 开发板为例,这个板子 FlexSPI1 上挂了两片 Flash,默认连接的 8MB QuadSPI Flash,还有一片 64MB HyperFlash(需要做板子改动才能使能):

2.1 使能冗余程序启动

  使能冗余程序启动特性很简单,就是将 fuse 0x6E0[23:16] - FLEXSPI_NOR_SEC_IMAGE_OFFSET 烧录为非 0 值即可,根据下面定义,第二份 image 偏移地址最大可以设到 Flash 中 0x3FC0000(63.75MB)处:

                                                  Remap功能的ADDR_START寄存器固定设为 Flash 起始映射地址。
fuse 0x6e0[15:13] - xSPI_FLASH_IMAGE_SIZE,       App的最大长度,标识了第一份App的结束地址,该值加上ADDR_START后被填入Remap功能的ADDR_END寄存器。
fuse 0x6e0[23:16] - FLEXSPI_NOR_SEC_IMAGE_OFFSET,标识了第二份App的起始地址(在Flash中偏移位置),即填入Remap功能的ADDR_OFFSET寄存器的值。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第2张图片

  本次测试,我们就将 FLEXSPI_NOR_SEC_IMAGE_OFFSET 烧录为 0x10,xSPI_FLASH_IMAGE_SIZE 保持默认 0,即第二份 image 偏移地址在 Flash 0x400000(4MB)处,最大 image 长度也是 4MB,可借助 MCUBootUtility 工具完成 Fuse 烧录:

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第3张图片

2.2 下载两份无签名 image 进 Flash

  现在开始准备 image,我们就直接用 \SDK_2.10.1_EVK-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky 例程,简单直观。为了便于肉眼分辨效果,我们生成两个稍微不一样的 image,闪灯间隔时间一个是 200ms(image 0 - iled_blinky_delay200ms.bin),另一个是 2s(image 1 - iled_blinky_delay2s.bin):

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第4张图片

  然后还是借助 MCUBootUtility 工具,先使用 All-In-One 操作将 image 0 下载进 Flash,因为是 bin 文件,所以我们要填入正确的链接起始地址(对于 i.MXRT1060 就是 0x60000000;对于 i.MXRT1010 应该是 0x60000400,具体查看工程链接文件便知)。此外我们使用的是完整的可启动镜像文件(包含了全部所需启动头),也可以直接在软件通用编程器界面做下载。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第5张图片

  image 0 下载进 Flash 后,继续下载 image 1,这时候只能在软件通用编程器界面操作。这里主要是设置好下载起始地址,前面我们使能冗余程序启动时在 Fuse 里设置的偏移地址是 0x400000,那么此时下载起始地址就应该是 0x400000(对于 i.MXRT1060 是 0x400000;对于 i.MXRT1010 应该是 0x400400)。至此两份 image 下载就完成了。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第6张图片

2.3 快速验证两份 image 正确性

  现在 Flash 里有了两份 image,我们来做一个快速验证,看看 image 是不是放得符合冗余程序启动的要求。对于 image 0,没什么好说的,芯片启动模式设为 2'b10 后断电复位应该可以看到 image 0 在执行,最重要的是验证 image 1 是不是合法。

  这里开始涉及到芯片冗余程序启动流程核心了,当 image 0 启动失败后,芯片 BootROM 不是立刻去执行 image 1 的,它用了一个取巧的方式,在一个软复位不置位的寄存器里(SRC_GPR10)标记了当前状态,然后调用 NVIC_SystemReset() 重新进入 BootROM 执行,第二次 BootROM 执行时才会去启动 image 1。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第7张图片

  所以这也给了我们快速验证 image 1 执行的可能性,我们在板子上挂上 J-Link 仿真器,然后打开 J-Link Commander,直接将 SRC_GPR10 寄存器改写为 0x40000000,再依次执行 reset 和 go 命令,这时你应该可以看到 image 1 正在执行了。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第8张图片

2.4 测试无签名 image 0 损坏条件

  痞子衡之前在 《i.MXRT Bootable image格式》 一文里介绍过一个可启动 image 包含哪些组成部分,其中最简单的无签名 image 应该至少包含 FDCB, IVT, BD, App 四部分,芯片 BootROM 也是按序读取这四部分数据完成程序启动的。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第9张图片

  现在我们尝试分别破坏这几个组成部分来看看何种程度的 image 0 损坏能被 BootROM 识别出从而去启动 image 1,下面是测试结果。从测试结果来看,除了 Image 0 - App 的损坏无法检测外,image 0 其余启动头的损坏都可以被 BootROM 识别到。

测试项 损坏程度 测试结果 备注
Image 0 - FDCB 全无或关键配置丢失导致Flash无法访问 无签名image 0启动失败
无签名image 1启动成功
如启用Auto Probe功能,则FDCB无所谓
Image 0 - IVT,BD 全无或关键配置丢失导致App信息无法找到 无签名image 0启动失败
无签名image 1启动成功
N/A
Image 0 - App 任意破坏App区域 无签名image 0被启动,但执行情况视破坏程度而定
无签名image 1未被启动
N/A

2.5 能检测 image 0 app 损坏的方法

  如果不能检测 image app 部分的损坏,那这个冗余程序启动功能就比较鸡肋了,毕竟 app 部分的数据才是整个 image 核心所在。如果要加上 app 损坏检测,需要使能 i.MXRT 签名启动,还是可以借助 MCUBootUtility 工具完成全部流程,下载两份含签名的 image 进 Flash 的过程跟 2.2 节基本差不多,只是 Secure Boot Type 里需要选择 "HAB Signed Image Boot",含签名的 image 0 下载没什么好说的,All-In-One 操作全搞定,含签名的 image 1 数据可以通过在通用编程器界面里将含签名的 image 0 数据全部读回得到,这个具体操作就不详细展开了。

  含签名 image 0 相比无签名 image 0 多了 HAB data(csf,cert,signature) 数据段,BootROM 在跳转 image 之前会根据 HAB data 数据对 image 进行验签,验签通过才做跳转。

痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计_第10张图片

  此时再做一次破坏实验,结果如下,显然加了签名的 image 0 其完整性就有保证了,这时的冗余程序启动设计才能发挥出最大效果。

测试项 损坏程度 测试结果 备注
Image 0 - FDCB 全无或关键配置丢失导致Flash无法访问 含签名image 0启动失败
含签名image 1启动成功
如启用Auto Probe功能,则FDCB无所谓
Image 0 - IVT,BD 全无或关键配置丢失导致App信息无法找到 含签名image 0启动失败
含签名image 1启动成功
N/A
Image 0 - App,HAB data 任意破坏App,HAB Data区域 含签名image 0启动失败
含签名image 1启动成功
N/A

  至此,i.MXRT1060,1010上串行NOR Flash冗余程序启动设计痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

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

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

你可能感兴趣的:(痞子衡嵌入式:揭秘i.MXRT1060,1010上串行NOR Flash冗余程序启动设计)