查看: 1469|回复: 0

i.MXRT Bootable image格式与加载过程

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

    [LV.8]以坛为家I

    3298

    主题

    6545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32003
    最后登录
    2024-4-9
    发表于 2020-1-22 17:46:14 | 显示全部楼层 |阅读模式
       
            今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Bootable image格式与加载过程。


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


    一、什么是Bootable image?
      如果你是一个有经验的嵌入式开发者,肯定对image格式有所了解,我们通常开发的Application都是针对含内部FLASH的MCU而言的,比如Kinetis、LPC、STM32等MCU,其内部集成了一块Parallel NOR FLASH,且FLASH地址是映射在ARM 4GB system address内的(一般从0x0地址开始),FLASH里存储的直接就是我们编译链接后生成的原始Application binary(.bin),没有任何多余的数据组成。或许你会说还有.hex, .srec等其他image格式,是的,但这些带地址信息的image格式是为编程器或下载器服务的,这些image格式经过编程器或者下载器解析后真正下载进MCU内部FLASH的数据还是原始Application binary。这类MCU上电后CPU能直接从内部FLASH获取Application代码并原地执行(XIP),所以对这类MCU而言,Bootable image就是存储在内部FLASH的Application binary(.bin)。
      但是以上经验在开发i.MXRT时遇到了问题,i.MXRT没有内部FLASH,需要外接FLASH存储器以存储image。众所周知,FLASH从结构上分为NOR和NAND,i.MXRT启动同时支持这两种FLASH,NOR FLASH可以实现XIP,NAND FLASH不可以XIP,为了兼容所有FLASH,在设计i.MXRT bootable image格式时必须以非XIP这种情况为基准。既然是非XIP执行,即意味着i.MXRT上电时会将image从外接FLASH拷贝到内部SRAM中去执行,在拷贝时必不可免要知道两个重要的数据:image链接起始地址(决定image被拷贝到SRAM哪个地址)、image总长度(决定要从外部FLASH拷贝多长的image数据进SRAM),实际上除了这两个最基本的数据外还有其他更高级的数据(配置、安全等特性),因此存储在外接FLASH的i.MXRT Bootable image除了含有Application binary数据之外还必须含有额外的信息,这些额外的信息数据与Application binary共同组成i.MXRT Bootable image。至于这些额外的信息在Bootable image里是如何组织的,痞子衡在后面会继续聊。


    二、Bootable image链接空间
      一个image的链接空间分两种,一种是只读段(readonly code,data)的链接空间,另一种是读写段(readwrite data, STACK)的链接空间,这两种链接空间要求的存储介质特性不一样,痞子衡逐一讲解:
      前面讲了i.MXRT同时支持外接NOR和NAND FLASH,其中NAND FLASH无法XIP,那么存储在NAND FLASH中的image只读段必须要链接在SRAM里。i.MXRT内部有三种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只读段,这个秘密一般人痞子衡是不会告诉他的。
    41.png
    前面讲了存储在NAND FLASH中的image只读段链接注意事项,而对于可以XIP的NOR FLASH,除了跟NAND一样可以将只读段链接在SRAM外,还可以链接在i.MXRT分配给外接存储器的XIP映射空间里,下表给出了Serial NOR(QSPI)和Parallel NOR(SEMC)各自的映射起始地址,需要注意的是Serial NOR支持的最大XIP空间为504MB,但是Parallel NOR支持的最大XIP空间只有16MB,别问痞子衡是怎么知道的,痞子衡无所不知。
    42.png
    至于image的读写段,在链接时就不用区别Non-XIP/XIP FLASH了,都只能放在SRAM里,并且不用考虑BootROM对SRAM的占用问题(因为不在一个时间域里被使用),只要注意不和image自身只读段冲突就行。
      黑科技:有朋友注意到了SDRAM,是的i.MXRT也支持SDRAM,通过SEMC接口去实现SDRAM读写,所以如果外接了SDRAM并且使能的话,也可以将image只读段/读写段放入SDRAM,关于SDRAM的使用,痞子衡会在后面文章里介绍。


    三、Bootable image七大组成
      Bootable image是由一些额外的信息数据与Application binary共同组成的,那些额外的信息数据按功能分有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启动再详细介绍。


    1. <font size="3" face="微软雅黑">typedef struct _flexspi_nor_config
    2. {
    3.     flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI
    4.     uint32_t pageSize;              //!< Page size of Serial NOR
    5.     uint32_t sectorSize;            //!< Sector size of Serial NOR
    6.     uint8_t ipcmdSerialClkFreq;     //!< Clock frequency for IP command
    7.     uint8_t isUniformBlockSize;     //!< Sector/Block size is the same
    8.     uint8_t reserved0[2];           //!< Reserved for future use
    9.     uint8_t serialNorType;          //!< Serial NOR Flash type: 0/1/2/3
    10.     uint8_t needExitNoCmdMode;      //!< Need to exit NoCmd mode before other IP command
    11.     uint8_t halfClkForNonReadCmd;   //!< Half the Serial Clock for non-read command: true/false
    12.     uint8_t needRestoreNoCmdMode;   //!< Need to Restore NoCmd mode after IP commmand execution
    13.     uint32_t blockSize;             //!< Block size
    14.     uint32_t reserve2[11];          //!< Reserved for future use} flexspi_nor_config_t;</font>
    复制代码
    3.2 偏移0x0400/0x1000: IVT(Image Vector Table)
      第二个组成部分叫IVT,是个必备组成,也是6类信息数据里的最核心数据,IVT是一个统一的与FLASH无关的structure,其原型如下面结构体所示,从结构体定义我们得知,IVT中记录了Application、DCD、BD、CSF的位置信息,这些信息对BootROM加载启动至关重要。IVT大小固定为32byte,其在Bootable image中的偏移位置也是固定的(对于XIP FLASH而言偏移是0x1000,对于Non-XIP FLASH而言偏移是0x400)。有朋友会疑问为何IVT偏移地址是固定的?其实答案很简单,因为BootROM必须要首先获取IVT才能进一步找到其他信息数据,而IVT本身的位置信息没有在其他地方被标明,所以只能在BootROM里用一个常量来记录。


    1. <font size="3" face="微软雅黑">#define HAB_TAG_IVT0 0xd1     /**< Image Vector Table V0 */
    2. /** @ref hab_header structure */
    3. typedef struct hab_hdr {
    4.     uint8_t tag;              /**< Tag field */
    5.     uint8_t len[2];           /**< Length field in bytes (big-endian) */
    6.     uint8_t par;              /**< Parameters field */
    7. } hab_hdr_t;

    8. /** @ref ivt structure */
    9. struct hab_ivt_v0 {
    10.     /** @ref hdr with tag #HAB_TAG_IVT0, length and HAB version fields */
    11.     hab_hdr_t hdr;
    12.     /** Absolute address of the first instruction to execute from the image */
    13.     uint32_t entry;
    14.     /** Reserved in this version of HAB: should be NULL. */
    15.     uint32_t reserved1;
    16.     /** Absolute address of the image DCD: may be NULL. */
    17.     uint32_t dcd;
    18.     /** Absolute address of the Boot Data: may be NULL, but not interpreted any further by HAB */
    19.     uint32_t boot_data;
    20.     /** Absolute address of the IVT.*/
    21.     uint32_t self;
    22.     /** Absolute address of the image CSF.*/
    23.     uint32_t csf;
    24.     /** Reserved in this version of HAB: should be zero. */
    25.     uint32_t reserved2;
    26. };</font>
    复制代码
    3.3 偏移0x0420/0x1020: BD(Boot Data)
      第三个组成部分叫BD,是个必备组成,是仅次于IVT的核心数据,BD也是一个统一的与FLASH无关的structure,其原型如下面结构体所示,BD中记录了Bootable image的起始地址与总长度。BD大小固定为16byte,BD信息虽然记录在了IVT中,但其在Bootable image中的偏移位置并不是任意的,BD是紧挨着IVT的。
    1. <font size="3" face="微软雅黑">/** @ref boot_data structure */
    2. typedef struct boot_data{
    3.     uint32_t start;           /* Start address of the image */
    4.     uint32_t size;            /* Size of the image */
    5.     uint32_t plugin;          /* Plugin flag */
    6.     uint32_t placeholder;     /* placehoder to make even 0x10 size */} BOOT_DATA_T;</font>
    复制代码
    3.4 DCD(Device Configuration Data)
      第四个组成部分叫DCD,是个可选组成,目前主要用于SDRAM接口控制器(SEMC)的配置。由于i.MXRT内部SRAM size通常是够用的,且访问速度也很快,所以SDRAM并不一定要被使能,Bootable image常常不会包含DCD,所以痞子衡在这里先不做展开,后续有必要会再介绍。下面是SDK_2.3.1_EVKB-IMXRT1050包里hello_world工程(flexspi_nor)所使用DCD示例:
    1. <font size="3" face="微软雅黑">#define DCD_TAG_HEADER (0xD2)

    2. const uint8_t dcd_data[] = {
    3. /*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,

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

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


    3.7 KeyBlob
      第七个组成部分叫KeyBlob,是个特性组成,主要用于安全启动的加密相关特性,痞子衡会在安全启动里进一步介绍。
    43.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就是你的Application工程编译链接生成的ELF文件,config_application.bd是用户配置文件,这个.bd文件主要是指示elftosb工具如何在Application binary基础上添加IVT、BD等其他信息数据从而形成Bootable image,所以编写.bd文件是关键步骤,bd文件有专门语法格式,但\Flashloader_i.MXRT1050_GA\Flashloader_RT1050_1.1\Tools\bd_file\imx10xx目录下给了很多bd文件示例,我们只需要在某一个bd文件基础上修改即可
    44.png
    如果你追过痞子衡博客文章,你应该知道痞子衡曾经实测过RT1052的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)。
    45.png
    六、Bootable image的加载过程
      知道了Bootable image的构成,痞子衡最后再简要为大家介绍一下i.MXRT 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)完成全部加载。
    46.png




    作者:痞子衡

    签到签到
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-20 05:57 , Processed in 0.118861 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

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