# IMX6ULL镜像文件分析 正常情况下我们编译出来的固件后缀`.bin`,但是针对imx6ull需要对裸机程序`.bin`前面加上一部分数据,该部分数据用来初始化ddr相关等内容。例如最大的uboot程序,最终下载到芯片里面的是加过头部数据的`u-boot.imx`,而不是`u-boot.bin`。 ![](media/1627116391920.png) 在裸机编程中,烧录过程如下: 1. 裸机生成的`.bin`文件前面通过工具打上头部信息,生成`.imx`文件; 2. 通过`dd`命令烧录到sd卡; 3. 让板子通过sd卡启动,来运行裸机程序。 ## 内部BootRom imx6ull内部有一段iROM(96kb)用来存放NXP厂家固化好的boot,假如我们自己编译的程序`led.bin`链接地址为`0x80100000`,那么bootRom主要功能如下: 1. bootRom会把EMMC或TF卡的前4K数据读入到芯片内部RAM运行 2. bootRom根据DCD进行初始化DDR。 3. bootRom根据IVT,从EMMC或TF卡中将led.bin读到DDR的0x80100000地址 4. 跳转到DDR的0x80100000地址执行 ![](media/image-20210802145856706.png) ![](media/1627121222753.png) ## 头部组成数据分析 Imx6ull镜像组成结构如下图所示: ![](media/1627121993538.png) **解释如下**: 1. 空偏移:芯片厂商设定下图说明了在不同的存储介质中空偏移的大小,如下图所示: ![](media/1627120966004.png) 以SD卡/EMMC为例,空偏移1k+3k(ivt+BootData+DCD),构成整个固件头部(4k),所以实际在4k开始地址处才是真正的用户`bin`文件,如下图所示: ![](media/image-20210727085257234.png) 2. Image vector table 简称IVT:组成如下 ![](media/image-20210727085407296.png) 1. Tag-[1 byte]:固定为0xD1 2. Length-[2 byte]:IVT表的长度,大端格式 3. Version-[1 byte]:0x40 或者 0x41 3. Boot Data 启动数据:镜像的加载地址和大小 ![](media/image-20210727085747065.png) 4. Device configuration data 简称DCD: DCD其实就是 I.MX6U寄存器地址和对应的配置信息集合, Boot ROM会使用这些寄存器地址和配置集合来初始化相应的寄存器,比如开启某些外设的时钟、初始化 DDR等等。 DCD区域不能超过 1768Byte,DCD数据组成如下: ![](media/image-20210727090109090.png) 1. DCD的Header和IVT的Header类型结构一致 ![](media/image-20210727090153192.png) - Tag-[1 byte]:固定为0xD2 - Length-[2 byte]:DCD区域的大小,大端格式 - Version-[1 byte]:0x40 或者 0x41 2. DCD的CMD格式如下: ![](media/image-20210727090435690.png) - Tag-[1 byte]:固定为0xCC - Length-[2 byte]:写入命令的长度,大端格式 - Parameter-[1 byte]:结构如下: ![](media/image-20210727090550457.png) - bytes:表示目标位置宽度,单位为byte,可以选择1、2和4字节 - flags:命令控制位 - Address和 Vlalue/Mask就是要初始化的寄存器地址和相应的寄存器值,注 意采用的是大端模式! 5. Bin文件:即编译的真正的应用程序`.bin` ## 实际imx镜像分析 ![](media/image-20210727091229017.png) 从0地址我们将前44个字节的数据按照4个字节一组组合在一起如下所示,这 44个字节的数据就是 IVT和 Boot Data数据。 ``` 0X402000D1、0X87800000、 0X00000000、 0X877FF42C、 0X877FF420、 0X877FF400、 0X00000000、 0X00000000、 0X877FF000、 0X00200000、 0X00000000。 ``` ### IVT表分析 | | IVT表 | | | --------- | ---------- | ------------------------------------------------------------ | | IVT结构 | 数据 | 描述 | | header | 0X402000D1 | 第一个字节 Tag为0XD1,
第二三两个字节为 IVT大小,所以 IVT大小为 0X0020=32字节。
第四个字节固定为 0X40 | | entry | 0X87800000 | 入口地址,也就是镜像第一行指令所在的位置。就是我们的链接地址。 | | reserved1 | 0X00000000 | 未使用,保留 | | dcd | 0X877FF42C | DCD起始地址:镜像地址为 0X87800000 IVT+Boot Data+DCD整个大小为 3KByte。因此 load.imx的起始地址就是 0X87800000-0XC00=0X877FF400。因此 DCD起始地址相对于 load.imx起始地址的偏移就是 0X877FF42C-0X877FF400=0X2C,也就是说load.imx的 0X2C这个地址开始就是 DCD数据了。 | | boot data | 0X877FF420 | boot data起始地址, header里面已经设置了 IVT大小是 32个字节,所以 boot data的地址就是0X877FF400+32=0X877FF420 | | self | 0X877FF400 | IVT复制到 DDR中以后的起始地址。 | | csf | 0X00000000 | CSF地址。 | | reserved2 | 0X00000000 | 保留,未使用。 | ### BootData分析 | | Boot Data | | | -------------- | ---------- | --------------------------------------------------- | | Boot Data 结构 | 数据 | 描述 | | start | 0X877FF000 | 整个load.imx的起始地址,包括前面 1KByte的地址偏移。 | | length | 0X00200000 | 镜像大小,这里设置2MByte。镜像大小不能超过2MByte | | plugin | 0X00000000 | 插件。 | ### DCD分析 前面提到,DCD数据是从0X2C地址开始的,从0X2C处我们得到4个字节为一组的数据组合为 ``` 0X40E801D2、0X04E401CC、0X020C4068、0XFFFFFFFF、……、0X020C4080、0XFFFFFFFF、0X020E04B4、0X000C0000、0X020E04AC、0X00000000、0X020E027C、0X00000030、……0X020E0248等 ``` | DCD结构 | 数据 | 描述 | | ------------------ | ---------- | ------------------------------------------------------------ | | header | 0X40E801D2 | 第一个字节 Tag为 0XD2
第二和三这两个字节为 DCD大小, 0X01E8=488字节
第四个字节为 0X40 | | Write Data Command | 0X04E401CC | 第一个为 Tag,固定为 0XCC,
第二和三这两个字节命令总长度,为 0X01E4=484个字节。
第四个字节是 Parameter,为 0X04,表示目标位置宽度为 4个字节。 | | Address | 0X020C4068 | 寄存器 CCGR0地址 | | Value | 0XFFFFFFFF | 要写入寄存器 CCGR0的值,表示打开 CCGR0控制的所有外设时钟。 | | …… | …… | CCGR1~CCGR5这些寄存器的地址和值。 | | Address | 0X020C4080 | 寄存器 CCGR6地址 | | Value | 0XFFFFFFFF | 要写入寄存器 CCGR6的值,表示打开 CCGR6控制的所有外设时钟。 | | Address | 0X020E04B4 | 寄存器 IOMUXC_SW_PAD_CTL_GRP_DDR_TYPE寄存器地址。 | | Value | 0X000C0000 | 设置 DDR的所有 IO为 DDR3模式。 | | Address | 0X020E04AC | 寄存器 IOMUXC_SW_PAD_CTL_GRP_DDRPKE地址。 | | Value | 0X00000000 | 所有 DDR引脚关闭 Pull/Keeper功能。 | | Address | 0X020E027C | 寄存器 IOMUXC_SW_PAD_CTL_PAD_DRAM_SDCLK0_P | | Value | 0X00000030 | DRAM_SDCLK0_P引脚为 R0/6。 | | …… | …… | 全部是 DDR引脚设置 | | Address | 0X020E0248 | 寄存器 IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM1 | | Value | 0X00000030 | DRAM_DQM1引脚驱动能力为 R0/6 | | Address | 0X021B001C | MMDC_MDSCR寄存器 | | Value | 0X00008000 | MMDC_MDSCR寄存器值 | | …… | …… | MMDC相关寄存器地址及其寄存器值。 | | Address | 0X021B0404 | MMDC_MAPSR寄存器 | | Value | 0X00011006 | MMDC_MAPSR寄存器配置值 | | Address | 0X021B001C | MMDC_MDSCR寄存器 | | Value | 0X00000000 | MMDC_MDSCR寄存器清零 | DCD里面的初始化配置主要包括三方面: 1. 设置 CCGR0~CCGR6这 7个外设时钟使能寄存器,默认打开所有的外设时钟。 2. 配置 DDR3所用的所有 IO。 3. 配置 MMDC控制器,初始化 DDR3 ## 启动方式 | 编号 | 名称 | NAND FLASH | eMMC | SD | USB | | ---- | ------ | ---------- | ---- | ---- | ---- | | 1 | MODE0 | 0 | 0 | 0 | 1 | | 2 | MODE1 | 1 | 1 | 1 | 0 | | 3 | CFG1-4 | 1 | 0 | 0 | X | | 4 | CFG1-5 | 0 | 1 | 0 | X | | 5 | CFG1-6 | 0 | 1 | 1 | X | | 6 | CFG1-7 | 1 | 0 | 0 | X | | 7 | CFG2-3 | 0 | 1 | 0 | X | | 8 | CFG2-5 | 0 | 0 | 1 | X | **注意**:其中的USB启动模式主要用来配合NXP官方的`mfgtool`工具烧录镜像。 总结一下,我们编译出来的 .bin文件不能直接烧写到 SD卡中,需要在 **.bin**文件前面加上 IVT、 Boot Data和 DCD这三个数据块。这三个数据块是有指定格式的,我们必须按照格式填写,然后将其放到 .bin文件前面,最终合成的才是可以直接烧写到 SD卡中的文件。 ## 如何添加头部信息 上文介绍镜像文件的组成方式,其中IVT表中Entry、dcd等数值意义,如下图:链接地址是用户自己指定的。所以当用户指定完链接地址paddr后,我们就可以推算出,图中A、B、C、D的地址。 ![](media/image-20210727150442715.png) 我们只需把这些4kb的头部数据给添加到用户自己编译的bin文件上,就可以得到最后的`.imx`文件。 使用软件源码如下: ```c /* * IMX6ULL * imx6ull download tool(refer to from ALIENTEK) * * Change Logs: * Date Author Notes * 2021-01-11 Lyons first version * 2021-01-28 Lyons add notes throw if sd not exited */ #include #include #include #include #define PARAM_DDR_SIZE_256M (0) #define PARAM_DDR_SIZE_512M (1) /* source bin data offset in imx file */ #define IMX_FILE_BIN_OFFSET (3072) #define IMX_FILE_NAME "load.imx" #define ADDR_ENTRY (0x80100000) /* 链接地址 */ #define ADDR_SELF (ADDR_ENTRY - 3*1024) #define ADDR_START (ADDR_ENTRY - 4*1024) #define ADDR_DCD (ADDR_SELF + 0x2C) #define ADDR_BOOT (ADDR_SELF + 0x20) #define IMAGE_SIZE (3*1024*1024) #define LOG_D(...) \ do \ { \ printf(__VA_ARGS__); \ printf("\r\n"); \ } while(0); const int _k_imx6_256mb_ivt_dcd_tbl[256]; const int _k_imx6_512mb_ivt_dcd_tbl[256]; int main( int argc, char *argv[] ) { FILE *fp = NULL; unsigned char *dummy_buf = NULL; unsigned char cmd_buf[1024]; int file_len; int ddr_size; LOG_D("--------------------------"); LOG_D("imx6ul image download tool"); LOG_D("build %s %s", __DATE__, __TIME__); LOG_D("--------------------------"); switch (argc) { case 3: ddr_size = PARAM_DDR_SIZE_256M; break; case 4: if (0 == strcmp(argv[3], "-256m")) { ddr_size = PARAM_DDR_SIZE_256M; break; } if (0 == strcmp(argv[3], "-512m")) { ddr_size = PARAM_DDR_SIZE_512M; break; } break; default: LOG_D("Help Menu:"); LOG_D(" [<-512m or -256m>]"); return -1; } char *source_bin_name = argv[1]; char *sd_device_name = argv[2]; if (0 != access(sd_device_name, F_OK)) { LOG_D("can not open sd device %s!", sd_device_name); return -1; } fp = fopen(source_bin_name, "rb"); if (NULL == fp) { LOG_D("can not open file %s!", source_bin_name); return -1; } fseek(fp, 0L, SEEK_END); file_len = ftell(fp); fclose(fp); LOG_D("file %s size = %dBytes", source_bin_name, file_len); dummy_buf = calloc(1, file_len + IMX_FILE_BIN_OFFSET); if (NULL == dummy_buf) { LOG_D("memory malloc failed!"); goto _main_exit; } fp = fopen(source_bin_name, "rb"); fread(&dummy_buf[IMX_FILE_BIN_OFFSET], 1, file_len, fp); fclose(fp); /* add DCD data to head of imx file */ switch (ddr_size) { case PARAM_DDR_SIZE_256M: LOG_D("DDR size: 256MB"); memcpy(dummy_buf, _k_imx6_256mb_ivt_dcd_tbl, sizeof(_k_imx6_256mb_ivt_dcd_tbl)); break; case PARAM_DDR_SIZE_512M: LOG_D("DDR size: 512MB"); memcpy(dummy_buf, _k_imx6_512mb_ivt_dcd_tbl, sizeof(_k_imx6_512mb_ivt_dcd_tbl)); break; default: break; } /* remove old imx file and create a new one */ LOG_D("delete old %s ...", IMX_FILE_NAME); sprintf(cmd_buf, "rm -rf %s", IMX_FILE_NAME); system(cmd_buf); LOG_D("create new %s ...", IMX_FILE_NAME); sprintf(cmd_buf, "touch %s", IMX_FILE_NAME); system(cmd_buf); fp = fopen(IMX_FILE_NAME, "wb"); if (NULL == fp) { LOG_D("can not open file %s!", IMX_FILE_NAME); goto _main_exit; } file_len += IMX_FILE_BIN_OFFSET; if (file_len != fwrite(dummy_buf, 1, file_len, fp)) { LOG_D("file write failed!"); fclose(fp); goto _main_exit; } fclose(fp); //save and start download! sprintf(cmd_buf, "sudo dd iflag=dsync oflag=dsync if=%s of=%s bs=512 seek=2", IMX_FILE_NAME, sd_device_name); LOG_D("download %s to %s ...", IMX_FILE_NAME, sd_device_name); system(cmd_buf); _main_exit: if (dummy_buf) free(dummy_buf); return 0; } const int _k_imx6_512mb_ivt_dcd_tbl[256] = { 0X402000D1,ADDR_ENTRY,0X00000000,ADDR_DCD, ADDR_BOOT, ADDR_SELF, 0X00000000,0X00000000, ADDR_START,IMAGE_SIZE,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02, 0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02, 0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02, 0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02, 0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02, 0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02, 0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02, 0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X0B000300,0X3C081B02, 0X44014801,0X48081B02,0X302C4040,0X50081B02,0X343E4040,0X1C081B02,0X33333333,0X20081B02, 0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02, 0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF3526B67,0X10001B02, 0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02, 0XD2260000,0X30001B02,0X23106B00,0X40001B02,0X4F000000,0X00001B02,0X00001884,0X90081B02, 0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02, 0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02, 0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000 }; const int _k_imx6_256mb_ivt_dcd_tbl[256] = { 0X402000D1,ADDR_ENTRY,0X00000000,ADDR_DCD, ADDR_BOOT, ADDR_SELF, 0X00000000,0X00000000, ADDR_START,IMAGE_SIZE,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02, 0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02, 0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02, 0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02, 0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02, 0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02, 0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02, 0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X04000000,0X3C081B02, 0X3C013C01,0X48081B02,0X38324040,0X50081B02,0X28304040,0X1C081B02,0X33333333,0X20081B02, 0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02, 0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF352433F,0X10001B02, 0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02, 0XD2260000,0X30001B02,0X23104300,0X40001B02,0X47000000,0X00001B02,0X00001883,0X90081B02, 0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02, 0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02, 0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, }; ``` 1. 保存源码为**imxdown.c** ; 2. gcc编译`imxdown.c`为可执行文件**imxdown**; ```bash gcc imxdown.c -o imxdown ``` 3. 用户**bin**和**imxdown**可执行文件放同一个目录下,插入sd卡,添加头部信息,并把生成的**imx**固件烧录到sd卡中; ```bash sudo chmod +x imxdown sudo ./imxdown led.bin /dev/sdb -512m ``` 4. 插入sd卡到板子,从sd卡启动,即可执行用户的bin文件。