查看: 1991|回复: 0

i.MX RT系列MCU启动那些事(6)—Bootable image格式与加载

[复制链接]
  • TA的每日心情
    开心
    2021-12-31 14:33
  • 签到天数: 45 天

    [LV.5]常住居民I

    48

    主题

    131

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    1129
    最后登录
    2024-3-29
    发表于 2019-6-18 09:29:16 | 显示全部楼层 |阅读模式
    本帖最后由 z1278064213 于 2019-6-18 09:29 编辑

    i.MX RT没有内置Flash,只有一块ROM区用于启动系统,并运行放在片外存储器中的代码。本站将连载痞子衡撰写的,关于i.MX RT产品启动相关的一系列文章,下面是系列中的第六篇。点击下面链接阅读前五篇:
    i.MX RT系列MCU启动那些事(4) - Flashloader初体验i.MX RT系列MCU启动那些事(5) - 再聊eFUSE及其烧写方法

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是恩智浦i.MX RT系列MCU的Bootable image格式与加载过程。

    在i.MX RT启动系列第三篇文章Serial Downloader模式(sdphost, mfgtool)里,痞子衡在介绍使用sdphost引导启动Flashloader时,使用过一个名叫ivt_flashloader.bin的image文件,其实这个image文件就是Bootable image的一种,虽然痞子衡简单分析过ivt_flashloader的组成,但介绍得并不详尽,今天痞子衡会为大家系统地讲解i.MXRT Bootable image。

    一、什么是Bootable image?

    如果你是一个有经验的嵌入式开发者,肯定对image格式有所了解,我们通常开发的应用程序,都是针对含内部FLASH的MCU而言的,比如Kinetis、LPC等MCU,其内部集成了一块Parallel NOR FLASH,且FLASH地址是映射在ARM 4GB系统空间内的(一般从0x0地址开始)。

    FLASH里存储的,直接就是我们编译链接后,生成的原始应用程序代码(.bin),没有任何多余的数据组成。或许你会说还有.hex, .srec等其他image格式,是的,但这些带地址信息的image格式,是为编程器或下载器服务的,这些image格式经过编程器或者下载器解析后,真正下载进MCU内部FLASH的数据,还是原始应用程序代码。这类MCU上电后,CPU能直接从内部FLASH获取应用程序代码并原地执行(XIP),所以对这类MCU而言,Bootable image就是存储在内部FLASH的应用程序代码(.bin)。

    但是以上经验在开发i.MX RT时遇到了问题,i.MX RT没有内部FLASH,需要外接FLASH存储器以存储image。

    众所周知,FLASH从结构上分为NOR和NAND,i.MX RT启动同时支持这两种FLASH,NOR FLASH可以实现XIP,NAND FLASH不可以XIP,为了兼容所有FLASH,在设计i.MX RT bootable image格式时,必须以非XIP这种情况为基准。


    既然是非XIP执行,即意味着i.MX RT上电时,会将image从外接FLASH拷贝到内部SRAM中去执行,在拷贝时必不可免要知道两个重要的数据:image链接起始地址(决定image被拷贝到SRAM哪个地址)、image总长度(决定要从外部FLASH拷贝多长的image数据进SRAM)。实际上除了这两个最基本的数据外,还有其他更高级的数据(配置、安全等特性),因此存储在外接FLASH的i.MX RT Bootable image,除了含有应用程序代码数据之外,还必须含有额外的信息,这些额外的信息数据与应用程序代码共同组成i.MX RT的Bootable image。

    至于这些额外的信息在Bootable image里是如何组织的,痞子衡在后面会继续聊。


    二、Bootable image链接空间

    一个image的链接空间分两种,一种是只读段(readonly code,data)的链接空间,另一种是读写段(readwrite data, STACK)的链接空间,这两种链接空间要求的存储介质特性不一样,痞子衡逐一讲解:

    前面讲了i.MX RT同时支持外接NOR和NAND FLASH,其中NAND FLASH无法XIP,那么存储在NAND FLASH中的image只读段,必须要链接在SRAM里。

    i.MX RT内部有三种SRAM,分别是ITCM, DTCM, OCRAM,是不是这三种SRAM都可以被随意链接呢?答案并不是!因为在Boot期间,BootROM也需要占用SRAM,用于存放BootROM的读写段,所以被BootROM占用的SRAM,无法用于链接image的只读段,如果强行链接,会导致BootROM在拷贝image只读段时,破坏自身读写段,从而发生不可预料的行为。

    下图是RT1050 BootROM的memory map,从图中可以得知BootROM占用的是0x20200000开始的OCRAM,并且看起来是整块OCRAM都被占用了,所以不推荐使用OCRAM去链接image只读段。
    黑科技:如果有朋友表示不服,RT1060 / RT1050 / RT1020的OCRAM是1MB / 512KB / 256KB,BootROM读写段不可能有这么大。是的,痞子衡告诉你,其实BootROM数据段只要32KB(0x20200000 - 0x20207FFF),另外还需要4KB用加载initial non-XIP image(0x20208000 - 0x20208FFF),所以对于存储在non-XIP FLASH的image,你可以从0x20209000之后的空间里链接image只读段,而对于存储在XIP FLASH的image,你可以从0x20208000之后的空间里链接image只读段,这个秘密一般人痞子衡是不会告诉他的。
    。0.png

    前面讲了存储在NAND FLASH中的image只读段链接注意事项,而对于可以XIP的NOR FLASH,除了跟NAND一样可以将只读段链接在SRAM外,还可以链接在i.MX RT分配给外接存储器的XIP映射空间里,下表给出了Serial NOR(QSPI)和Parallel NOR(SEMC)各自的映射起始地址,需要注意的是Serial NOR支持的最大XIP空间为504MB,但是Parallel NOR支持的最大XIP空间只有16MB,别问痞子衡是怎么知道的,痞子衡无所不知。
    。1.png

    至于image的读写段,在链接时就不用区别Non-XIP/XIP FLASH了,都只能放在SRAM里,并且不用考虑BootROM对SRAM的占用问题(因为不在一个时间域里被使用),只要注意不和image自身只读段冲突就行。
    黑科技:有朋友注意到了SDRAM,是的i.MX RT也支持SDRAM,通过SEMC接口去实现SDRAM读写,所以如果外接了SDRAM并且使能的话,也可以将image只读段/读写段放入SDRAM,关于SDRAM的使用,痞子衡会在后面文章里介绍。

    三、Bootable image七大组成


    Bootable image是由一些额外的信息数据,与应用程序代码共同组成的,那些额外的信息数据按功能分有6类,但这6类信息数据并不都是必须的,其中有4类是可选的,因此一个Bootable image最多由7部分组成,最少由3部分组成。下面痞子衡按在FLASH里存储位置从低到高的顺序,逐一介绍组成Bootable image的7大部分:

    3.1 偏移0x0000: FDCB(Flash Device Configuration Block)
    第一个组成部分叫FDCB,是个可选组成,目前只用于Serial/Parallel NOR FLASH。FDCB是从FLASH的起始地址处开始存放的,也是Bootable image最开始部分。

    FDCB最大4KB,其本身没有统一的与FLASH无关的structure,具体structure根据启动FLASH的接口类型(Serial / Parallel)而定,其一般是用来存储当前连接的FLASH的具体特性参数,BootROM上电会使用通用且可靠的FLASH接口控制器配置(即BootROM中默认参数配置,一般是比较低速的配置)去访问外接FLASH并获取FDCB,然后根据FDCB存储的参数去重新配置FLASH接口控制器,再去进一步访问FLASH。

    下面的结构体是Serial NOR的FDCB原型,此处痞子衡不会展开介绍这个结构体,留到后续介绍Serial NOR启动再详细介绍。

    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 偏移0x0400/0x1000: IVT(Image Vector Table)
    第二个组成部分叫IVT,是个必备组成,也是6类信息数据里的最核心数据,IVT是一个统一的与FLASH无关的structure,其原型如下面结构体所示,从结构体定义我们得知,IVT中记录了应用程序、DCD、BD、CSF的位置信息,这些信息对BootROM加载启动至关重要。IVT大小固定为32byte,其在Bootable image中的偏移位置也是固定的(对于XIP FLASH而言偏移是0x1000,对于Non-XIP FLASH而言偏移是0x400)。

    有朋友会疑问为何IVT偏移地址是固定的?其实答案很简单,因为BootROM必须要首先获取IVT,才能进一步找到其他信息数据,而IVT本身的位置信息没有在其他地方被标明,所以只能在BootROM里用一个常量来记录。

    #define HAB_TAG_IVT0 0xd1     /**< Image Vector Table V0 *//** @ref hab_header structure */typedef struct hab_hdr {
        uint8_t tag;              /**< Tag field */
        uint8_t len[2];           /**< Length field in bytes (big-endian) */
        uint8_t par;              /**< Parameters field */} hab_hdr_t;/** @ref ivt structure */struct hab_ivt_v0 {
        /** @ref hdr with tag #HAB_TAG_IVT0, length and HAB version fields */
        hab_hdr_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;
    };
    3.3 偏移0x0420/0x1020: BD(Boot Data)
    第三个组成部分叫BD,是个必备组成,是仅次于IVT的核心数据,BD也是一个统一的与FLASH无关的structure,其原型如下面结构体所示,BD中记录了Bootable image的起始地址与总长度。

    BD大小固定为16字节,BD信息虽然记录在了IVT中,但其在Bootable image中的偏移位置并不是任意的,BD是紧挨着IVT的。


    /** @ref boot_data structure */typedef struct boot_data{
        uint32_t start;           /* Start address of the image */
        uint32_t size;            /* Size of the image */
        uint32_t plugin;          /* Plugin flag */
        uint32_t placeholder;     /* placehoder to make even 0x10 size */} BOOT_DATA_T;
    3.4 DCD(Device Configuration Data)
    第四个组成部分叫DCD,是个可选组成,目前主要用于SDRAM接口控制器(SEMC)的配置。由于i.MX RT内部SRAM的大小通常是够用的,且访问速度也很快,所以SDRAM并不一定要被使能,Bootable image常常不会包含DCD,所以痞子衡在这里先不做展开,后续有必要会再介绍。下面是SDK_2.3.1_EVKB-IMXRT1050包里hello_world工程(flexspi_nor)所使用DCD示例:

    #define DCD_TAG_HEADER (0xD2)const uint8_t dcd_data[] = {    /*0000*/ DCD_TAG_HEADER,    0x04,0x30,0x41,0xCC,0x03,0xAC,0x04,0x40,0x0F,0xC0,0x68,0xFF,0xFF,0xFF,0xFF,    /*0010*/ 0x40,    0x0F,0xC0,0x6C,0xFF,0xFF,0xFF,0xFF,0x40,0x0F,0xC0,0x70,0xFF,0xFF,0xFF,0xFF,


        ...    /*0420*/ 0x00,    0x00,0x00,0x01,0xCC,0x00,0x0C,0x04,0x40,0x2F,0x00,0x4C,0x50,0x21,0x0A,0x09,
    };
    3.5 偏移0x2000: Application Binary
    第五个组成部分是你最熟悉的应用程序代码,当然是个必备组成,其在Bootable image中的偏移位置是固定的(0x2000),关于应用本身这里就不再赘述了。只特别提一点,那就是i.MX RT的应用程序只读段(主要指ARM中断向量表),并不可以从任意地址开始链接,有一个小小的限制,必须从选定的存储器地址空间偏移0x2000之后开始链接(如选中ITCM,则必须要链接在0x00002000之后;如选中DTCM,则必须链接在0x20002000之后...),因为要预留至少8KB空间给IVT、BD、DCD等数据,这个限制是BootROM自身决定的,务必要注意。

    3.6 CSF(Command Sequence File)
    第六个组成部分叫CSF,是个特性组成,主要用于安全启动的认证相关特性,痞子衡会在安全启动里进一步介绍。

    3.7 KeyBlob
    第七个组成部分叫KeyBlob,是个特性组成,主要用于安全启动的加密相关特性,痞子衡会在安全启动里进一步介绍。
    .3.png

    上图是包含IVT、BD、DCD、Application、CSF的Bootable image的layout,这张图很好地诠释了IVT的作用。

    四、Bootable image三种分类

    前面介绍了Bootable image最多有7大组成,有些是必备,有些是可选,有的是特性。而在实际应用中,主要是必备+特性的组合形成如下三种常用分类:

    Unsigned Image: 这是最简单的image类型,由IVT+BD+Application组成,主要用于产品开发阶段。

    Signed Image: 这是较复杂的image类型,由IVT+BD+Application+CSF组成,一般用于产品发布阶段。

    Encrypted Image: 这是最复杂的image类型,由IVT+BD+Application+CSF+KeyBlob组成,主要用于对安全要求较高的产品中。


    五、使用elftosb生成Bootable image

    恩智浦官方提供了一个用于生成Bootable image的工具,名叫elftosb,这个工具在\Flashloader_i.MXRT1050_GA\Flashloader_RT1050_1.1\Tools\elftosb目录下,它可以用来生成所有类型的Bootable image,命令格式固定如下:

    elftosb.exe -f imx -V -c config_application.bd -o ivt_application.bin application.out

    其中ivt_application.bin就是最终生成的Bootable image,命令所需要的2个输入文件分别是application.out、config_application.bd。application.out就是你的应用程序工程编译链接生成的ELF文件,config_application.bd是用户配置文件,这个.bd文件主要是指示elftosb工具,如何在应用程序代码基础上添加IVT、BD等其他信息数据,从而形成Bootable image,所以编写.bd文件是关键步骤,bd文件有专门语法格式。在\Flashloader_i.MXRT1050_GA\Flashloader_RT1050_1.1\Tools\bd_file\imx10xx目录下给了很多bd文件示例,我们只需要在某一个bd文件基础上修改即可


    如果你关注了“恩智浦MCU加油站”发表过的文章,你应该知道痞子衡曾经实测过RT1052的coremark性能:i.MX RT105x微控制器CoreMark性能实测,coremark工程已经上传到痞子衡的github http://github.com/JayHeng/cortex-m_app,工程路径在\cortex-m_app\apps\coremark_imxrt1052\bsp\build\coremark.eww,编译此工程可得到coremark_a000.out和coremark_a000.bin文件,coremark程序只读段链接在ITCM地址(0x0000a000),我们来试着使用elftosb将coremark程序转换成bootable image,bd文件可参考imx-itcm-unsigned.bd,打开这个参考bd文件:

    options {
        flags = 0x00;
        # Note: This is an example address, it can be any non-zero address in ITCM region
        startAddress = 0x8000;
        ivtOffset = 0x400;
        initialLoadSize = 0x2000;
        # Note: This is required if the default entrypoint is not the Reset_Handler
        #       Please set the entryPointAddress to Reset_Handler address
        // entryPointAddress = 0x60002411;
    }


    sources {
        elfFile = extern(0);
    }


    section (0)
    {
    }
    ivtOffset和initialLoadSize不用改,分别代表IVT和Application在Bootable image中的偏移地址,startAddress即BOOT_DATA_T.start,这个是可以修改的,牢记下面公式:

    startAddress + initialLoadSize = Application只读段起始链接地址

    coremark_a000.out是链接在0xa000地址处的,0x8000 + 0x2000 = 0xa000,所以此处startAddress也无需改,唯一需要确认的是entryPointAddress,保险起见统一将entryPointAddress设成Application的复位中断地址,即entryPointAddress = 0x0000ecd1。bd文件修改完成之后另存为config_coremark_a000.bd,让我们试着执行下面命令:

    elftosb.exe -f imx -V -c config_coremark_a000.bd -o ivt_coremark_a000.bin coremark_a000.out
    分别打开coremark_a000.bin和ivt_coremark_a000.bin,可以看到ivt_coremark_a000.bin比coremark_a000.bin多了前8KB的数据,这前8KB里包含了有效的IVT(偏移0x400)和BD(偏移0x420)。
    .4.png

    六、Bootable image的加载过程


    知道了Bootable image的构成,痞子衡最后再简要为大家介绍一下i.MX RT的BootROM,是如何从外部存储器中加载Bootable image进SRAM内存的。以non-XIP image加载为例(image链接在ITCM里),以下概括了i.MXRT加载image的四个阶段:

    ■  第一个阶段即加载前,此时Bootable image完全存储在外部Flash中,SRAM中没有任何image数据;

    ■  第二阶段即初始加载,BootROM首先会从外部Flash读取Bootable image前4KB数据进SRAM临时缓存区(OCRAM:0x20208000 - 0x20208FFF),我们知道这4KB数据里包含了IVT和BD,BootROM从IVT和BD里获取到Bootable image的目标地址(BOOT_DATA_T.start)以及总长度(BOOT_DATA_T.size),此时便可以开始做进一步加载;

    ■  第三阶段即内部转移,由于BootROM已经从外部Flash读取了4KB进SRAM临时缓存区,为了避免重复读取,BootROM会把这4KB数据首先复制到Bootable image的目标地址(ITCM);

    ■  第四阶段即加载完成,BootROM会接着将剩下的Bootable image(BOOT_DATA_T.size - 4KB),从外部Flash中全部读取出来,存到目标区域(ITCM)完成全部加载。
    .5.png

    至此,恩智浦i.MX RT系列MCU的Bootable image格式与加载过程痞子衡便介绍完毕了,掌声在哪里~~~




    作者:痞子衡  文章出处:恩智浦MCU加油站

    11111签到
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

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

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

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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