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

it2024-11-19  42

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

1 前言2 RT启动过程3 头信息组成3.1 FCB3.2 IVT3.3 Boot Data3.4 DCD 3 镜像文件的生成

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 KbyteSD/MMC/eSD/eMMC/SDXC1 KbyteSPI 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 bytesSD/MMC/eSD/eMMC/SDXC1 Kbyte = 0x400 bytesSPI 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中可以得到Boot Data的链接地址为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

最新回复(0)