月黑风高夜,金陵城外的芯片码头。
你,一个代号为“裸机程序”的神秘刀客,怀揣着绝密任务,来到了这片由硅晶与电流构成的江湖。眼前这座巍峨的i.MX6ULL城堡,城门紧锁,唯有顶楼的ROM Code密室,还亮着一盏永不熄灭的灯。
你知道规矩。要想让城堡为你开门,启动那沉睡的千军万马(DDR、外设、内核),你必须先通过楼上那位“ROM老爷”的考验。他不见来人,只听暗号——一段必须严丝合缝、分毫不差的数据结构。
这不是普通的暗号。它有一个冰冷的技术名字:Image Vector Table,简称IVT。但在懂行的人眼里,它就是一张写着“天王盖地虎,宝塔镇河妖”的江湖拜帖,是进入i.MX6ULL世界的唯一通行证。
本篇属于第一部分 万事开头难之入门篇
今晚,我们就来破译这份密令。
截图来自Reference Manual Chapter 8 System Boot
先前在真真没啥技术的我学Linux驱动 | 新芯片的官方文档(下)
中已经阅读过boot mode,启动方式如下图所示。



但我的板子引出boot相关引脚如下,拨码开关除了BOOT_MODE0\1外,仅有LCD_DATA几个引脚。

其余引导引脚都接地了。


SD卡 - SDHC1
EMMC - SDHC2






引出引脚与BOOT CFG映射关系:
BOOT_CFG2[3] | ||
BOOT_CFG1[3] | ||
BOOT_CFG1[4] | BOOT_CFG1[7:4] | |
BOOT_CFG1[5] | ||
BOOT_CFG1[6] | ||
BOOT_CFG1[7] |
拨码开关拨码定义:
“暗号”要从SD卡来,那么就要“100xx010”这样一串拨码数字来设置。
如何铸造“信物”:亲手打造IVT这张拜帖,每一个字段,都将是与ROM老爷对话的关键密语。

程序要想真正运行,还要在前面加上IVT、BootData、DCD信息,最后才是用户程序。
设备从SD/EMMC启动,IVT offset为1Kb=0x400,加载大小为4Kb,那么也就是说最后实际大小只有4Kb-1Kb=3Kb大小用来表示IVT+Boot data+DCD。
IVT格式:


IVT header包含Tag\Length\Version,
Tag:单字节,固定0xD1,
Length:两个字节,采用大端格式,包含IVT的总长度(以字节为单位),包括头部。该长度是固定的,必须为32字节。
Version:单字节0x40或0x41。

芯片复位后,寄存器值变为默认值,默认值并不是理想情况下能发挥最大性能的值。这个时候就需要通过DCD这块区域来配置一些参数来初始化。说白了,DCD就是一系列寄存器的地址和配置的值。
当然,这块区域也不是无限大的,最大1768Byte。


DCD格式包含Header和一系列CMD组合。
Header:由Tag\Length\Version组成,
Tag:单字节,固定值0xD2。
Length:两个字节,大端格式,包含整个DCD的长度(包括Header)。
Version:单字节,固定值0x41。


Address:寄存器地址。
Value/Mask:寄存器值。

写入数据的时候还要注意数据宽度、地址范围,否则,会写入失败。

对于不支持的boot引导设备如以太网,Boot ROM 自身并不知道如何读取这些设备上的数据。这时候就需要一个“插件”——即 Plugin Image 来实现。
既然知道了IVT格式内容,那么接下来怎么做呢?
寄存器这么多,又是头一次接触。
还记得当时下载的
SDK_2.2_MCIM6ULL_RFP_Linux.run
么?
如何完成“交接”:当暗号对上,信物验明,你将亲眼目睹城堡大门轰然开启,你的代码从此在这片疆域内,生杀予夺,令行禁止。
SDK目录如下图示,

这里面一定有怎么让板子跑起来的demo程序,遇事不决看文档。
打开docs,可以看到有个start的pdf。


文档介绍了SDK支持NXP的官方评估板,以及SDK的文件目录相关内容,还有run a demo的手把手教学(好吧,居然是windows的
)。

另外关键的是,在mfg下载地方提到了怎么生成image,这是我们最需要的。

编译hello world后,得到了bin文件,按上述步骤到tools/imgutil下生成image。


在文件夹下除了mkimage.sh外,还有个dcd.config文件,dcd多么熟悉的名字。执行mkimage.sh,生成了一个img文件,发现文件除了生成一个img外还多了一个dcd.bin,并且还细心的打印了image info。


基地址是0x80000000 对应DDR起始地址
IVT offset 0x00000400与RM要求一致
程序镜像地址0x80002000
.....
以16进制形式,打开文件sdk20-app.bin和sdk20-app.img
bin文件是程序文件开头是18F0...



img文件前面1Kb全是FF,数据从0x400开始D1 00 20 41.....

好了,再根据RM手册的IVT table结合img文件分析一下数据对应关系

Command Sequence File地址,未使用 | ||
Write Data Command | ||
Enable all clocks | ||
寄存器地址 CCM_CCGR0 | ||
Config IOMUX for DDR | ||
TAG固定为0xCC。 Length两个字节,大端格式,0x000C。 Params目标数据宽度4字节。 | ||
设置DDR的IOMUXC, IOMUXC_SW_PAD_CTL_GRP_DDR_TYPE | ||
0x000C0000 | ||
Config DDR Controller Registers | ||
TAG固定为0xCC。 Length两个字节,大端格式,0x000C。 Params目标数据宽度4字节。 | ||
MMC寄存器 MMDC_MDSCR | ||
0x00008000 | ||
Config MMDC init | ||
以上就是img整个镜像的头部IVT 相关信息,
包含初始化时钟、配置DDR IO、配置DDR控制器、配置MMC控制器。
那这个到底怎么生成的呢?


还记得dcd.config吗?内容如上图所示,描述了外设时钟、DDR IO口、DDR、MMC相关配置寄存器的配置信息。
当mkimage脚本执行时,会通过dcdgen提取dcd.config文件来生成对应dcd配置信息,然后按照手册描述规则,把IVT+BootData+DCD一起并添加到程序头部,形成镜像文件。
通往i.MX6ULL内核世界的密钥已交予ROM Code老爷手中。
当IVT的第一个字节的电流震颤被ROM Code准确捕获——那扇厚重的城门开始缓缓打开。
