小猫爪:i.MX RT1050学习笔记9-镜像文件分解

1 前言

我们在《小猫爪:i.MX RT1050学习笔记1-启动》中介绍了一下RT系列芯片的启动过程与一般MCU的启动机制不一样,提到RT的bin文件是经过加工之后的镜像文件,接下就让我们看看RT的镜像文件进行了怎么的加工,才能让RT正常启动。

2 RT启动过程

之前在介绍RT的启动,就简单介绍了RT的正常启动流程,接下来再简单介绍一下:
首先BootROM根据Boot Mode引脚选择从哪里启动,再根据具体的CFG引脚或者eFUSE的相关配置决定从什么存储设备开始启动,随后BootROM才会从该指定的存储设备读取镜像文件,然后再开始分析镜像并执行镜像中的APP。

我们这一章的重点在于BootROM读取镜像的过程。

3 头信息组成

在介绍读取镜像的过程前,我们首先得了解一下镜像的组成。为了让RT正常启动,我们需要在传统意义上的bin文件(也就是APP主体)增加一个头,那这个头具体内容是什么。

这个头信息包含了以下部分:

①FCB(存储器接口配置数据)
②IVT(Image Vector Table)
③Boot Data
④DCD(Device configuration data)

下面我们根据具体的实例来对每一个部分进行分解。我打开了NXP的官方的demo Hello World生成的镜像来进行详细的介绍。(环境:IAR;链接文件为:MIMXRT1052xxxxx_flexspi_nor_sdram.icf;使能SDRAM)

3.1 FCB

该部分是可选的,也是整个镜像最开始部分,其本身没有统一的结构,具体结构根据启动FLASH的接口类型而定,其一般是用来存储RT存储器接口配置数据以及当前连接的FLASH的具体特性参数,BootROM首先会使用通用且可靠的FLASH接口控制器配置(即BootROM中默认参数配置、CFG引脚以及eFUSE的配置,一般是比较低速通用的配置)去访问外接FLASH并获取这部分数据,然后根其参数去重新配置FLASH接口控制器再去进一步访问FLASH。占的空间大小随着外部存储器类型而改变,下表为不同类型存储器对应的所占空间大小:

启动设备类型大小
FlexSPI NOR/SEMC NOR4 Kbyte
SD/MMC/eSD/eMMC/SDXC1 Kbyte
SPI NOR/EEPROM/SEMC NAND/ FlexSPI NAND1 Kbyte

这一部分数据对于不同的存储器类型是不一样的,我们知道RT有FlexSPI,SEMC,uSDHC 三种外部存储器接口,所以这一部分数据是根据不同的存储器接口来进行划分的。

①对于FlexSPI,存储器接口配置数据则包括两个部分,分别是FlexSPI初始化配置数据和存储器配置数据。这部分数据的具体定义大家可参考官方参考指南《i.MX RT1050 Processor Reference Manual》的Serial NAND Flash Boot over FlexSPI

②对于SEMC,存储器接口配置数据则包括两个部分,分别是SEMC初始化配置数据和存储器配置数据。这部分数据的具体定义大家可参考官方参考指南《i.MX RT1050 Processor Reference Manual》的Parallel NOR and NAND configuration based on SEMC interface

③对于uSDHC ,这一部分则是不需要了。因为对于SD协议来说,有相关的信息来区别不同的存储器。

(对于NAND而言,这一部分还包括关于坏块处理的FCB和DBBT数据,后续将具体介绍。)

以通过FlexSPI NOR为启动存储设备为例,在NXP官方demo程序中,是通过一个结构体来实现这部分数据,代码如下:

/*
 *  Serial NOR configuration block
 */
typedef struct _flexspi_nor_config
{
    flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI
    uint32_t pageSize;              //!< Page size of Serial NOR
    uint32_t sectorSize;            //!< Sector size of Serial NOR
    uint8_t ipcmdSerialClkFreq;     //!< Clock frequency for IP command
    uint8_t isUniformBlockSize;     //!< Sector/Block size is the same
    uint8_t reserved0[2];           //!< Reserved for future use
    uint8_t serialNorType;          //!< Serial NOR Flash type: 0/1/2/3
    uint8_t needExitNoCmdMode;      //!< Need to exit NoCmd mode before other IP command
    uint8_t halfClkForNonReadCmd;   //!< Half the Serial Clock for non-read command: true/false
    uint8_t needRestoreNoCmdMode;   //!< Need to Restore NoCmd mode after IP commmand execution
    uint32_t blockSize;             //!< Block size
    uint32_t reserve2[11];          //!< Reserved for future use
} flexspi_nor_config_t;

3.2 IVT

IVT里面包含了一系列的地址信息,这些地址信息按照固定的序列存放着,对于RT启动至关重要, 大小为32个字节。

启动设备类型IVT偏移
FlexSPI NOR/SEMC NOR4 Kbyte = 0x1000 bytes
SD/MMC/eSD/eMMC/SDXC1 Kbyte = 0x400 bytes
SPI NOR/EEPROM/SEMC NAND/ FlexSPI NAND1 Kbyte = 0x400 bytes
IVT的具体组成如下:

在这里插入图片描述

打开Hello World.bin,因为链接文件MIMXRT1052xxxxx_flexspi_nor_sdram.icf决定了程序从FlexSPI NOR启动,我们可以知道IVT的偏移地址为0x10000。我们找到Bin文件的0x10000处:
在这里插入图片描述再对应IVT结构我们可以得到:

IVT结构数据描述大小
header0X402000D1第一个字节Tag为0XD1,第二三这两个字节为 IVT大小0X0020=32字节。第四个字节为 0X404(Byte)
entry0X60002000APP的入口链接地址4(Byte)
reserved10X00000000未使用,保留4(Byte)
dcd0X60001030DCD链接地址4(Byte)
boot data0X60001020boot data链接地址4(Byte)
self0X60001000IVT链接地址4(Byte)
csf0X00000000CSF链接地址,不使用时为04(Byte)
reserved20X00000000保留,未使用。4(Byte)
(注:链接地址就是当前数据被存储到RT的映射地址位置, 可以理解成其在RT的memory map中所在的绝对地址,通过查询可得到FlexSPI NOR的映射地址为0x60000000。)

在NXP官方demo程序中,是通过一个结构体来实现这部分数据,代码如下:

/************************************* 
 *  IVT Data 
 *************************************/
typedef struct _ivt_ {
    /** @ref hdr with tag #HAB_TAG_IVT, length and HAB version fields
     *  (see @ref data)
     */
    uint32_t hdr;
    /** Absolute address of the first instruction to execute from the
     *  image
     */
    uint32_t entry;
    /** Reserved in this version of HAB: should be NULL. */
    uint32_t reserved1;
    /** Absolute address of the image DCD: may be NULL. */
    uint32_t dcd;
    /** Absolute address of the Boot Data: may be NULL, but not interpreted
     *  any further by HAB
     */
    uint32_t boot_data;
    /** Absolute address of the IVT.*/
    uint32_t self;
    /** Absolute address of the image CSF.*/
    uint32_t csf;
    /** Reserved in this version of HAB: should be zero. */
    uint32_t reserved2;
} ivt;


3.3 Boot Data

Boot Data即启动数据,包含了镜像要拷贝到哪个地址,拷贝的大小是多少,其必须紧跟在IVT的后面,位置不能随意变动。所占空间大小为12个字节,其结构如下图。
在这里插入图片描述
我们从IVT中可以得到Boot Data的链接地址为0X60001020,所以我们在bin文件中找到0x1020处:
在这里插入图片描述再对应Boot Data结构我们可以得到:

名称数据描述大小
start0x60000000整个镜像的链接地址,包括头信息4(byte)
length0x04000000整个镜像的大小,这里设置64M4(byte)
plugin0x00000000插件标志,imx6原生支持的Boot Device是有限的,如果我们想使用其他的Boot源(如Ethernet、CDROM、USB等),则需要提供对应的驱动程序,来完成Boot过程。具体的驱动程序路径则在DCD配置文件设置4(byte)

在NXP官方demo程序中,是通过一个结构体来实现这部分数据,代码如下:

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;

最后一项placeholder不是必须的,只是用来凑数,让数据对齐用的,可省略。

3.4 DCD

DCD即设备配置信息,其实就是RT启动前初始化,目的就是为了初始化SEMC,所以能通过DCD初始化的寄存器都是和SEMC有关的寄存器。原理就是里面存储着寄存器地址和数据对,读取地址和数据后,再讲相应的数据写到对应的地址。(注:我们在《小猫爪:i.MX RT1050学习笔记8-SEMC》就曾经介绍过可以使用DCD来提前初始化SEMC。)

DCD的结构如下图所示:
在这里插入图片描述
其中Header的格式如下:
在这里插入图片描述Tag为0xD2,长度为整个DCD的长度,Version为0x41。

我们从IVT中可以得到DCD的链接地址为0X60001030,所以我们在bin文件中找到0x1030处:
在这里插入图片描述
从图中,0x1030处的数据0x411004D2, 可以得到DCD大小为0x0410 = 1040Byte,我们找到DCD结束的位置如下:
在这里插入图片描述所以DCD的大小为0x1440-0x1030=0x0410,和上面对应正确,所以这个推断没有错。

CMD即为指令,一般有三种:写指令,检查指令和空指令。每一种指令都有自己的格式,具体的格式大家可以参考文章:《IMX头部详细解析之一 头部组成》,在这里我就不展开说了。

在NXP官方demo程序中,是通过一个数组来实现DCD数据,我们可以直接通过修改这个数组来更改DCD数据,根据所使用的SDRAM、FLASH特性来初始化SEMC,具体代码如下:

const uint8_t dcd_data[] = {
	/* HEADER */
	/* Tag */
	0xD2,
	/* Image Length */
	0x04, 0x10,
	/* Version */
	0x41,

	/* COMMANDS */

	/* group: 'Imported Commands' */
	/* #1.1-113, command header bytes for merged 'Write - value' command */
	0xCC, 0x03, 0x8C, 0x04,
	/* #1.1, command: write_value, address: CCM_CCGR0, value: 0xFFFFFFFF, size: 4 */
	0x40, 0x0F, 0xC0, 0x68, 0xFF, 0xFF, 0xFF, 0xFF,
	/* #1.2, command: write_value, address: CCM_CCGR1, value: 0xFFFFFFFF, size: 4 */
	0x40, 0x0F, 0xC0, 0x6C, 0xFF, 0xFF, 0xFF, 0xFF,
	
    .........
    
	/* #9, command: write_value, address: SEMC_SDRAMCR3, value: 0x50210A09, size: 4 */
	0xCC, 0x00, 0x0C, 0x04, 0x40, 0x2F, 0x00, 0x4C, 0x50, 0x21, 0x0A, 0x09
	};

到这里,我们给镜像添加的头文件分解完毕。我们只要把这个头信息和APP合二为一就是一个最简单的镜像文件了,这样可以确保RT的正常运行。

除了上面介绍的头信息部分和APP部分,一个镜像文件还可以有CSF(Command Sequence File)和 KeyBlob两个部分,这两个部分主要用于安全启动的认证相关特性。后面我们接触到再对其进行深入学习。

3 镜像文件的生成

对于镜像文件的生成可以通过NXP的elftosb工具来生成,实例命令如下:

elftosb.exe -f imx -V -c APP.bd -o APP.bin APP.out

APP.bin就是我们最终的镜像文件;APP.out就是你的APP工程编译链接生成的ELF文件;APP.bd是用户配置文件,该文件主要是指示elftosb工具如何在Application binary基础上添加头信息等其他信息数据从而构成镜像文件,bd文件有专门语法格式,在\Flashloader_i.MXRT1050_GA\Flashloader_RT1050_1.1\Tools\bd_file\imx10xx目录下给了很多bd文件示例。

不过这样太过麻烦。现在NXP官方在例程中都添加了XIP文件,在文件中,我们可以非常方便的修改这些信息,在修改相关的链接文件,就可以按照我们的想法做出相关镜像文件。最后生成的bin文件即为最终的镜像文件,每一个部分在代码中怎么去实现,前面已经介绍的非常清楚。

END

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小猫爪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值