# IMX6ULL镜像文件分析
正常情况下我们编译出来的固件后缀`.bin`,但是针对imx6ull需要对裸机程序`.bin`前面加上一部分数据,该部分数据用来初始化ddr相关等内容。例如最大的uboot程序,最终下载到芯片里面的是加过头部数据的`u-boot.imx`,而不是`u-boot.bin`。

在裸机编程中,烧录过程如下:
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地址执行


## 头部组成数据分析
Imx6ull镜像组成结构如下图所示:

**解释如下**:
1. 空偏移:芯片厂商设定下图说明了在不同的存储介质中空偏移的大小,如下图所示:

以SD卡/EMMC为例,空偏移1k+3k(ivt+BootData+DCD),构成整个固件头部(4k),所以实际在4k开始地址处才是真正的用户`bin`文件,如下图所示:

2. Image vector table 简称IVT:组成如下

1. Tag-[1 byte]:固定为0xD1
2. Length-[2 byte]:IVT表的长度,大端格式
3. Version-[1 byte]:0x40 或者 0x41
3. Boot Data 启动数据:镜像的加载地址和大小

4. Device configuration data 简称DCD:
DCD其实就是 I.MX6U寄存器地址和对应的配置信息集合, Boot ROM会使用这些寄存器地址和配置集合来初始化相应的寄存器,比如开启某些外设的时钟、初始化 DDR等等。 DCD区域不能超过 1768Byte,DCD数据组成如下:

1. DCD的Header和IVT的Header类型结构一致

- Tag-[1 byte]:固定为0xD2
- Length-[2 byte]:DCD区域的大小,大端格式
- Version-[1 byte]:0x40 或者 0x41
2. DCD的CMD格式如下:

- Tag-[1 byte]:固定为0xCC
- Length-[2 byte]:写入命令的长度,大端格式
- Parameter-[1 byte]:结构如下:

- bytes:表示目标位置宽度,单位为byte,可以选择1、2和4字节
- flags:命令控制位
- Address和 Vlalue/Mask就是要初始化的寄存器地址和相应的寄存器值,注
意采用的是大端模式!
5. Bin文件:即编译的真正的应用程序`.bin`
## 实际imx镜像分析

从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的地址。

我们只需把这些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文件。