查看: 1664|回复: 0

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

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3299

    主题

    6546

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32024
    最后登录
    2024-4-25
    发表于 2021-4-1 14:24:55 | 显示全部楼层 |阅读模式
    i.MX RT1050学习笔记9-镜像文件分解

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

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

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

    这个头信息包含了以下部分:
    ①FCB(存储器接口配置数据)
    ②IVT(Image VectorTable)
    ③Boot Data
    ④DCD(Deviceconfiguration 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。占的空间大小随着外部存储器类型而改变,下表为不同类型存储器对应的所占空间大小:
    4.1.png

    这一部分数据对于不同的存储器类型是不一样的,我们知道RT有FlexSPI,SEMC,uSDHC 三种外部存储器接口,所以这一部分数据是根据不同的存储器接口来进行划分的。
    ①对于FlexSPI,存储器接口配置数据则包括两个部分,分别是FlexSPI初始化配置数据和存储器配置数据。这部分数据的具体定义大家可参考官方参考指南《i.MX RT1050 Processor Reference Manual》的SerialNAND Flash Boot over FlexSPI。
    ②对于SEMC,存储器接口配置数据则包括两个部分,分别是SEMC初始化配置数据和存储器配置数据。这部分数据的具体定义大家可参考官方参考指南《i.MXRT1050 Processor Reference Manual》的Parallel NOR andNAND configuration based on SEMC interface。
    ③对于uSDHC ,这一部分则是不需要了。因为对于SD协议来说,有相关的信息来区别不同的存储器。
    (对于NAND而言,这一部分还包括关于坏块处理的FCB和DBBT数据,后续将具体介绍。)

    以通过FlexSPI NOR为启动存储设备为例,在NXP官方demo程序中,是通过一个结构体来实现这部分数据,代码如下:
    1. /*
    2. *  Serial NOR configuration block
    3. */
    4. typedef struct _flexspi_nor_config
    5. {
    6.     flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI
    7.     uint32_t pageSize;              //!< Page size of Serial NOR
    8.     uint32_t sectorSize;            //!< Sector size of Serial NOR
    9.     uint8_t ipcmdSerialClkFreq;     //!< Clock frequency for IP command
    10.     uint8_t isUniformBlockSize;     //!< Sector/Block size is the same
    11.     uint8_t reserved0[2];           //!< Reserved for future use
    12.     uint8_t serialNorType;          //!< Serial NOR Flash type: 0/1/2/3
    13.     uint8_t needExitNoCmdMode;      //!< Need to exit NoCmd mode before other IP command
    14.     uint8_t halfClkForNonReadCmd;   //!< Half the Serial Clock for non-read command: true/false
    15.     uint8_t needRestoreNoCmdMode;   //!< Need to Restore NoCmd mode after IP commmand execution
    16.     uint32_t blockSize;             //!< Block size
    17.     uint32_t reserve2[11];          //!< Reserved for future use
    18. } flexspi_nor_config_t;
    复制代码

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

    IVT的具体组成如下:
    4.3.png

    打开Hello World.bin,因为链接文件MIMXRT1052xxxxx_flexspi_nor_sdram.icf决定了程序从FlexSPINOR启动,我们可以知道IVT的偏移地址为0x10000。我们找到Bin文件的0x10000处:
    4.4.png

    再对应IVT结构我们可以得到:
    4.5.png

    (注:链接地址就是当前数据被存储到RT的映射地址位置, 可以理解成其在RT的memorymap中所在的绝对地址,通过查询可得到FlexSPI NOR的映射地址为0x60000000。)

    在NXP官方demo程序中,是通过一个结构体来实现这部分数据,代码如下:
    1. /*************************************
    2. *  IVT Data
    3. *************************************/
    4. typedef struct _ivt_ {
    5.     /** @ref hdr with tag #HAB_TAG_IVT, length and HAB version fields
    6.      *  (see @ref data)
    7.      */
    8.     uint32_t hdr;
    9.     /** Absolute address of the first instruction to execute from the
    10.      *  image
    11.      */
    12.     uint32_t entry;
    13.     /** Reserved in this version of HAB: should be NULL. */
    14.     uint32_t reserved1;
    15.     /** Absolute address of the image DCD: may be NULL. */
    16.     uint32_t dcd;
    17.     /** Absolute address of the Boot Data: may be NULL, but not interpreted
    18.      *  any further by HAB
    19.      */
    20.     uint32_t boot_data;
    21.     /** Absolute address of the IVT.*/
    22.     uint32_t self;
    23.     /** Absolute address of the image CSF.*/
    24.     uint32_t csf;
    25.     /** Reserved in this version of HAB: should be zero. */
    26.     uint32_t reserved2;
    27. } ivt;
    复制代码

    3.3 Boot Data
    Boot Data即启动数据,包含了镜像要拷贝到哪个地址,拷贝的大小是多少,其必须紧跟在IVT的后面,位置不能随意变动。所占空间大小为12个字节,其结构如下图。
    4.6.png

    我们从IVT中可以得到BootData的链接地址为0X60001020,所以我们在bin文件中找到0x1020处:
    4.7.png

    再对应Boot Data结构我们可以得到:
    4.8.png

    在NXP官方demo程序中,是通过一个结构体来实现这部分数据,代码如下:
    1. typedef struct _boot_data_ {
    2.   uint32_t start;           /* boot start location */
    3.   uint32_t size;            /* size */
    4.   uint32_t plugin;          /* plugin flag - 1 if downloaded application is plugin */
    5.   uint32_t placeholder;                /* placehoder to make even 0x10 size */
    6. }BOOT_DATA_T;
    复制代码

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

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

    DCD的结构如下图所示:
    4.9.png

    其中Header的格式如下:
    4.10.png

    Tag为0xD2,长度为整个DCD的长度,Version为0x41。
    我们从IVT中可以得到BootData的链接地址为0X60001030,所以我们在bin文件中找到0x1030处:
    4.11.png

    从图中,0x1030处的数据0x411004D2,可以得到DCD大小为0x0410 = 1040Byte,我们找到DCD结束的位置如下:
    4.12.png

    所以DCD的大小为0x1440-0x1030=0x0410,和上面对应正确,所以这个推断没有错。
    CMD即为指令,一般有三种:写指令,检查指令和空指令。每一种指令都有自己的格式,在这里我就不展开说了。

    在NXP官方demo程序中,是通过一个数组来实现DCD数据,我们可以直接通过修改这个数组来更改DCD数据,根据所使用的SDRAM、FLASH特性来初始化SEMC,具体代码如下:
    1. const uint8_t dcd_data[] = {
    2.         /* HEADER */
    3.         /* Tag */
    4.         0xD2,
    5.         /* Image Length */
    6.         0x04, 0x10,
    7.         /* Version */
    8.         0x41,

    9.         /* COMMANDS */

    10.         /* group: 'Imported Commands' */
    11.         /* #1.1-113, command header bytes for merged 'Write - value' command */
    12.         0xCC, 0x03, 0x8C, 0x04,
    13.         /* #1.1, command: write_value, address: CCM_CCGR0, value: 0xFFFFFFFF, size: 4 */
    14.         0x40, 0x0F, 0xC0, 0x68, 0xFF, 0xFF, 0xFF, 0xFF,
    15.         /* #1.2, command: write_value, address: CCM_CCGR1, value: 0xFFFFFFFF, size: 4 */
    16.         0x40, 0x0F, 0xC0, 0x6C, 0xFF, 0xFF, 0xFF, 0xFF,
    17.         
    18.     .........
    19.    
    20.         /* #9, command: write_value, address: SEMC_SDRAMCR3, value: 0x50210A09, size: 4 */
    21.         0xCC, 0x00, 0x0C, 0x04, 0x40, 0x2F, 0x00, 0x4C, 0x50, 0x21, 0x0A, 0x09
    22.         };
    复制代码

    到这里,我们给镜像添加的头文件分解完毕。我们只要把这个头信息和APP合二为一就是一个最简单的镜像文件了,这样可以确保RT的正常运行。
    除了上面介绍的头信息部分和APP部分,一个镜像文件还可以有CSF(Command Sequence File)和 KeyBlob两个部分,这两个部分主要用于安全启动的认证相关特性。后面我们接触到再对其进行深入学习。

    4 镜像文件的生成
    对于镜像文件的生成可以通过NXP的elftosb工具来生成,实例命令如下:
    1. 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文件即为最终的镜像文件,每一个部分在代码中怎么去实现,前面已经介绍的非常清楚。

    ————————————————
    版权声明:本文为「小猫爪」的原创文章

    签到签到
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-25 20:54 , Processed in 0.111091 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表