1 基础知识

(1)BootROM
BootROM(ROM Bootloader)是芯片在出厂前固化在ROM里的一段Bootloader程序。每次上电的时候都将执行里面的代码,它完成了对于特定引脚上的FlexSPI、SEMC和SDHC接口的初始化,并从这些接口连接到的Flash和RAM中引导程序启动。
当然,对于BootROM来说,它还可以通过IVT中相关字段的配置完成程序的加密和解密等操作,这个将在后续介绍。
(2)Flashloader
Flashloader是一个RT1170的工程。对于BootROM来说,它主要是完成对于程序的启动功能。而对于Flashloader来说,它实现了一些Flash的擦除和烧写函数,当然还有基于熔丝的读取和烧录、加解密等功能。这些功能之所以没有在BootROM中实现,我想大概率是基于成本考虑而决定的。

  • 用户可以通过USB1或LPUART1与BootROM和Flashloaer进行通信

(3)eFuse
eFUSE是I.MXRT1170内嵌的一块OTP(One time Programmable) memory,初始状态下所有bit均为0,仅能被烧写1次。RT1176的eFUSE有8Kbit,分为32个Bank,每个Bank有8个word(1个word为4字节),即32字节。下图0~0x8F为eFUSE的bank word索引地址(index地址)。
在这里插入图片描述
这里不详细对eFuse进行介绍,简单地说一下:

  • eFuse中部分位的定义在参考手册26.2 Boot Fusemap中有介绍,对于所有位的介绍,比如加密启动的一些字段,需要参考Secure Reference Manual,这个手册需要找FAE要,自己是下载不了的。
  • eFuse除了有OTP特性外,还有Lock特性,它包含三层:写保护、覆盖保护和访问保护
  • eFuse空间有两类:一类受冗余保护(牺牲一半的空间保存数据的备份以防止数据损坏),一类受ECC保护(可纠正损坏位)
  • eFuse空间的读写由RT1170中的OCOTP控制器来实现,如果想要修改eFuse,用户可以参考SDK中OCOTP相关代码自己来实现。也可以通过集成了eFuse烧写的工具:blhostsdphostMCUBootUtility(通过Flashloader)来修改。

(4)从外部存储器启动

(1)NOR Flash:可以XIP执行,但也需要BootROM引导完成Nor的初始化及XIP相关配置

(2)Nand Flash:无法XIP执行,由BootROM从Nand拷贝代码到RAM中执行

(3)SDRAM与SRAM:SRAM挂在系统BUS上,无需初始化;而SDRAM挂载存SEMC控制器上,需要初始化。在BootROM中,可以通过DCD字段对SDRAM进行初始化。

2 BOOT配置

2.1 BOOT_CFG配置

前面我们知道,BootROM可以帮助我们从外部存储器中启动程序。但是BootROM需要知道一些信息:启动设备连接在从哪个FlexSPI接口,这个接口上接的是Nand还是NOR或者是EMMC等设备,还有是否需要加密启动等信息。这些信息就BOOT_CFG引脚来告诉BootROM,这些引脚在硬件上也是固定的,如下图所示。
在这里插入图片描述

  • 引脚的具体含义,参考手册10.6 Boot Device章节。对于不同的外部存储器,引脚的定义都不相同。这个章节对于BOOT_CFG的介绍实际是eFuse BOOT_CFG相关位的定义,但这和GPIO BOOT_CFG的定义是相同的。这是因为eFuse中有和GPIO的BOOT_CFG相同的定义的位,在eFuse中的BT_FUSE_SEL位置1时,将使用eFuse中的BOOT_CFG配置。

2.2 BOOT_MODE

对于I.MXRT1XXX系列单片机来说,上电永远是从BootROM开始启动,而用户可以通过BOOT_MODE[1:0]决定BootROM程序的不同行为模式。这两个字段由硬件进行配置,它们所对应的引脚是固定的:
在这里插入图片描述
总共有四种不同的启动方式,如下图所示:
在这里插入图片描述
1、Boot From Fuses模式
在这个模式下,将根据Fuse里的Boot配置来决定从哪个外部存储器Boot,如下图所示。
在这里插入图片描述
由上图可以看出:

Internal BootBoot from Fuses
BT_FUSE_SEL=0BOOT_CFG从GPIO相关引脚中获取从USB的Serial Loader启动(通过USB传输程序)
BT_FUSE_SEL=1BOOT_CFG从eFuse相关字段中获取BOOT_CFG从eFuse相关字段中获取

2、Serial Downlaoder模式
在此模式下,用户可以与参考手册中指定引脚连接的UART或USB与BootROM进行通信,从而接收程序并在SRAM中运行。该模式主要用来从SRAM中启动Flashloader,用户可以将Flashloader程序烧录,然后与Flashloader进行通信,通过Flashloader里面的函数来对Flash、eFuse等内容进行修改。

  • Serial Downloader模式的通讯遵从一定的协议,NXP官方提供了几个相关的工具可用于此通信:sdphost.exemfgtoolMCUBootUtility

3、Internal Boot模式(Serial NOR/NAND、Parallel NOR/NAND、SD/eMMC、1bit Recovery SPI NOR)
该模式支持从串行/并行的NOR/NAND Flash、SDCard、EMMC和带1位恢复位的Recovery SPI NOR中启动,这是最常使用的一种启动模式。
在这个模式下,也会参考eFuse中的BT_FUSE_SEL位来使用不同的配置:
BT_FUSE_SEL=0:BootROM将使用BOOT_CFG[x:0] pins 的配置
BT_FUSE_SEL=1:BootROM将使用Fuse中的配置

  • 我们可以在确定了启动设备后,将BT_FUSE_SEL烧录为1,然后将GPIO已经测试过没有问题的BOOT_CFG位烧录到eFuse中对应的BOOT_CFG位中,这样启动设备就不可能被硬件上更改了。
  • 如果Internal Boot失败,比如用户的BOOT_CFG配置有错,系统将进入Serial Downlaoder模式。

3 Bootable image

前面BootROM帮我们初始化了外部的存储器,可以从里面获取到我们写的程序。但是程序的大小是多少、需要链接到哪里运行呢?这就需要我们在生成的程序最前面加一个头来让BootROM知道这些信息,从而完成程序的拷贝和跳转。

3.1 文件格式

对于不同的IDE来说,编译后生成的程序的文件格式都不太一样,常见的有以下几种:

  • AXF:用于基于ARM的微控制器。它包含可执行代码、数据和调试信息。AXF文件通常在开发和调试过程中使用
  • HEX:由十六进制数及其对应的内存地址组成,可以将程序解析和编程到目标设备的内存中
  • S19:以特定格式的ASCII字符表示二进制数据。S19文件包含数据和内存地址,常用于编程旧的微控制器和EEPROM
  • ELF:包含可执行代码、数据和其他加载和执行程序所需的信息,可用于调试、分析和部署到目标设备
  • SREC:类似于S19的文件格式。它以ASCII字符表示二进制数据,但遵循不同的格式
  • BIN:BIN文件是直接包含可执行机器代码的二进制文件。它们通常用于以原始二进制格式存储最终编译的代码。

不管什么格式,都是为不同下载器或者调试而服务的,经过解析后下载进MCU内部FLASH的数据还是bin格式。

3.2 Bootable image头的组成

以下偏移均以NOR Flash为例,对于Nand Flash来说,有略微的不同,但大致相同。
1、FCB(FlexSPI Configuration Block):偏移0x400
可选,用于Serial/Parallel NOR FLASH。其数据结构随着Flash的接口的不同而不同,用来存储FLASH的特性参数。因为BootROM需要兼容不同的Flash,所以BootROM上电会使用比较通用的、低速的配置来初始化FLASH。那如果我们希望Flash能工作在更高的频率下,就可以通过此字段保存配置信息,BootROM会根据此字段重新配置Flash。

2、IVT(Image Vector Table):偏移0x1000
必选的与FLASH无关的structure,IVT中记录了Application、DCD、BD、CSF的位置信息,与BootROM加载启动有关。IVT大小固定为32byte,数据结构如下:

typedef struct _ivt_
{
    uint32_t hdr;
    uint32_t entry;
    uint32_t reserved1;
    uint32_t dcd;
    uint32_t boot_data;
    uint32_t self;
    uint32_t csf;
    uint32_t reserved2;
} ivt;

具体每个字段的含义后面实例分析时会解释,有实例也更好理解。
3、BD(Boot Data):偏移0x1020

必选的与FLASH无关的structure,BD中记录了Bootable image的起始地址与总长度,其大小固定为16byte,数据结构如下:

typedef struct _boot_data_
{
    uint32_t start;       /* boot start location */
    uint32_t size;        /* size */
    uint32_t plugin;      /* plugin flag - 1 if downloaded application is plugin */
    uint32_t placeholder; /* placehoder to make even 0x10 size */
} BOOT_DATA_T;

4、DCD(Device Configuration Data)

可选的用于初始化SEMC接口的配置信息,主要用于SDRAM的初始化,它由寄存器地址和数据对组成,有三种指令类型:写指令、检查指令和空指令。可以在MCUXPresso IDE中配置DCD字段:
在这里插入图片描述

  • 可以看到是可以初始化任意寄存器的值的,所以还可以用来初始化HyperRAM
  • Bootable image中还有一个XMCD字段可以用来对HyperRAM进行初始化,具体参考reference manual

5、Application Binary:偏移0x2000
即用户编译出的程序,偏移位置固定为0x2000。
6、CSF(Command Sequence File)
主要用于安全启动的认证相关特性。
7、KeyBlob
主要用于安全启动的加密相关特性。

  • 对于安全启动加密相关内容,将在后续博客中介绍

3.3 Bootable image的生成

(1)IDE:MCUXPresso IDE、IAR、Keil
通过在链接脚本中声明IVTBoot DataDCD等段,然后在C文件中声明其数据结构对应的结构体,再用attribute关键字将对应结构体变量链接到指定段中。

  • 注意需要打开一些宏定义,如使用DCD,则需要声明XIP_BOOT_HEADER_ENABLE = 1

(2)elftosb.exe
elftosb.exe是NXP提供的一个软件,可以用来生成Bootable image,当然还有一些其它的功能,比如生成可以与Flashloader能识别的指定协议的镜像文件,可以实现修改eFuse、烧录Flash、AES固件加密等功能。
对于生成Bootable image,其命令格式固定如下:

elftosb.exe -f imx -V -c config_application.bd -o ivt_application.bin application.out
  • ivt_application.bin:最终生成的Bootable image

  • application.out:编译链接生成的ELF文件

  • config_application.bd:用户配置文件

.bd指示elftosb如何在binary基础上添加IVTBD等其他信息数据从而形成Bootable image,在Flashloader目录下给了很多bd文件示例,只需在某一个bd文件基础上修改即可
(3)MCUBootUtility
自动检测有没有IVT、BD头,没有的话将根据镜像文件自动填充该字段。

3.4 例:BootROM之non-XIP加载过程

假设程序保存在Nand Flash,然后要在ITCM(0x00000000~0x20000000)运行,下图显示了BootROM加载image的四个阶段:
(1)Bootable image存储在Nand Flash中
(2)BootROM从Nand Flash读取Bootable image前4KB(包含IVTBD)数据到OCRAM(0x20240000~0x202C0000)
(3)为了避免重复读取,BootROM会把读取的4KB数据先复制到ITCM
(4)BootROM根据IVTBD的字段,获得程序的偏移和大小,将剩下的image从Nand Flash中读到ITCM
在这里插入图片描述

3.5 例:bin文件分析

以NOR Flash,程序XIP执行为例,对SDK中的evkmimxrt1170_freertos_hello_cm7编译生成的bin文件进行分析:
1、FCB(FlexSPI Configuration Block):偏移0x400
可选的,格式参考手册10.6.3 Serial NOR and NAND Configuration based on FlexSPI Interface,主要是Flash一些参数的声明、时钟频率的初始化还有保存Flash读写时序的FlexSPI LUT(Look Up Table)
在这里插入图片描述

2、IVT(Image Vector Table):偏移0x1000
在这里插入图片描述
其中TAG为d1,IVT Length为0x0020(这个字段为大端表示),version为0x41,entrypoint Address为0x300024e9(Reset_ISR的地址),DCD的绝对地址为0x30001030,Boot Data链接地址为0x30001020,IVT链接地址为0x30001000

3、BD(Boot Data):偏移0x1020
在这里插入图片描述
其中镜像的绝对起始地址为0x30000000,程序镜像的大小为0x1000000(实际保存的是Flash的大小)。
4、DCD:偏移0x1030
在这里插入图片描述
前面在IVT中指定了DCD的地址为0x30001030(绝对地址),BootROM就会从这个地址读取DCD。其中Tag为0xd2;整个DCD的长度为0x4b8(这个大小为大端存储),即结束地址为0x14E8;版本为0x41。也就是说从0x1040~0x14E8就是DCD配置字段。

  • 前面有提到,XMCD可以用来hyperRAM,实际上是将其FlexSPI配置块FCB填充到0x1040处,也就是说BootROM不能同时初始化DCD和XMCD字段。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐