IMX6ULL镜像文件分析

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

../../_images/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地址执行

../../_images/image-20210802145856706.png

../../_images/1627121222753.png

头部组成数据分析

Imx6ull镜像组成结构如下图所示:

../../_images/1627121993538.png

解释如下

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

    ../../_images/1627120966004.png

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

    ../../_images/image-20210727085257234.png

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

    ../../_images/image-20210727085407296.png

    1. Tag-[1 byte]:固定为0xD1

    2. Length-[2 byte]:IVT表的长度,大端格式

    3. Version-[1 byte]:0x40 或者 0x41

  3. Boot Data 启动数据:镜像的加载地址和大小

    ../../_images/image-20210727085747065.png

  4. Device configuration data 简称DCD:

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

    ../../_images/image-20210727090109090.png

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

      ../../_images/image-20210727090153192.png

      • Tag-[1 byte]:固定为0xD2

      • Length-[2 byte]:DCD区域的大小,大端格式

      • Version-[1 byte]:0x40 或者 0x41

    2. DCD的CMD格式如下:

      ../../_images/image-20210727090435690.png

      • Tag-[1 byte]:固定为0xCC

      • Length-[2 byte]:写入命令的长度,大端格式

      • Parameter-[1 byte]:结构如下:

        ../../_images/image-20210727090550457.png

        • bytes:表示目标位置宽度,单位为byte,可以选择1、2和4字节

        • flags:命令控制位

      • Address和 Vlalue/Mask就是要初始化的寄存器地址和相应的寄存器值,注 意采用的是大端模式!

  5. Bin文件:即编译的真正的应用程序.bin

实际imx镜像分析

../../_images/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的地址。

../../_images/image-20210727150442715.png

我们只需把这些4kb的头部数据给添加到用户自己编译的bin文件上,就可以得到最后的.imx文件。

使用软件源码如下:

/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#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("<toolname> <source_bin> <sd_device> [<-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

    gcc imxdown.c -o imxdown
    
  3. 用户binimxdown可执行文件放同一个目录下,插入sd卡,添加头部信息,并把生成的imx固件烧录到sd卡中;

    sudo chmod +x imxdown
    sudo ./imxdown led.bin /dev/sdb -512m
    
  4. 插入sd卡到板子,从sd卡启动,即可执行用户的bin文件。