参考:Linux之ARM(IMX6U)裸机之I.MX6ULL镜像烧写以及启动头文件的详解
作者:一只青木呀
发布时间: 2020-08-09 17:10:00
网址:
BOOT ROM做的事情
上一节讲到,当 BOOT_MODE1 为 1, BOOT_MODE0 为 0 的时候此模式使能,在此模式下, 芯片会执行内部的 boot ROM 代码 ,这段 boot ROM 代码会进行 硬件初始化 (一部分外设), 然后从 boot 设备(就是存放代码的设备、比如 SD/EMMC、 NAND)中将代码拷贝出来复制到指定的 RAM 中 (这个代码也就是前面点灯讲的leds.bin 文件前面添加了一些数据头后生成的 load.imx 文件),一般是 DDR。
简单看下内存映射:
这个 boot ROM 代码都会做什么处理呢?首先肯定是初始化时钟, boot ROM 设置的系统时钟如图:
内部 boot ROM 为了加快执行速度会打开 MMU 和 Cache , 下载镜像 的时候 L1 ICache 会打开,验证镜像的时候 L1 DCache、 L2 Cache 和 MMU 都会打开。一旦镜像验证完成, boot ROM就会关闭 L1 DCache、 L2 Cache 和 MMU。
中断向量偏移 会被设置到 boot ROM 的起始位置,当 boot ROM 启动了用户代码以后就可以重新设置中断向量偏移了。一般是重新设置到我们用户代码的开始地方。
BOOT ROM和后面讲的头部信息进行配合,完成初始化的。
详见参考手册第八章,如下。
烧写工具imxdownload在led.bin前面添加的头部信息
IVT 和 Boot Data 数据(地址信息和启动数据)
前面我们设置好 BOOT 以后就能从指定的设备启动了,但是你的设备里面得有代码啊, 在LED灯实验中我们使用 imxdownload这个软件将 led.bin 烧写到了 SD 卡中 。imxdownload 会在 led.bin前面添加一些头信息,重新生成一个叫做 load.imx 的文件,最终实际烧写的是 laod.imx。 那么imxdownload 究竟做了什么 ? load.imx 和 led.bin 究竟是什么关系?
①、 Image vector table ,简称 IVT, IVT 里面包含了一系列的 地址信息 ,这些地址信息在ROM中按照固定的地址存放着。
②、 Boot data , 启动数据 ,包含了镜像要拷贝到哪个地址,拷贝的大小是多少等等。
③、 Device configuration data ,简称 DCD,设备配置信息,重点是 DDR3 的初始化配置。
④、 用户代码可执行文件 ,比如 led.bin。 可以看出最终烧写到 I.MX6U 中的程序其组成为: IVT+Boot data+DCD+.bin。所以LED灯实验中的 imxdownload 所生成的 load.imx 就是在 led.bin 前面加上 IVT+Boot data+DCD。内部 BootROM 会将 load.imx 拷贝到 DDR 中, 用户代码是要一定要从 0X87800000 这个地方开始的,因为链接地址为0X87800000 , load.imx 在用户代码前面又有 3KByte 的 IVT+Boot Data+DCD 数据,下面会讲为什么是3KByte,因此 load.imx 在 DDR 中的起始地址就是 0X87800000-3072=0X877FF400
总结:我们编译出来的.bin 文件不能直接烧写到 SD 卡中,需要在.bin 文件前面加上 IVT、 Boot Data 和 DCD 这三个数据块。这三个数据块是有指定格式的,我们必须按照格式填写,然后将其放到.bin 文件前面,最终合成的才是可以直接烧写到 SD卡中的文件。
load.imx 最前面的就是 IVT 和 Boot Data
, IVT 包含了镜像程序的入口点、指向 DCD 的指针和一些用作其它用途的指针。
内部 Boot ROM 要求 IVT 应该放到指定的位置,不同的启动设备位置不同
,而 IVT 在整个 load.imx 的最前面,其实就相当于要求 load.imx 在烧写的时候应该烧写到存储设备的指定位置去。整个位置都是
相对于存储设备的起始地址的偏移
,如图
以 SD/EMMC 为例(上表第四行所示) , IVT 偏移为 1Kbyte, IVT+Boot data+DCD 的总大小为 3KByte,load.imx 从第 3KByte 开始才是真正的.bin 文件。
假如 SD/EMMC 每个扇区为 512 字节,那么 load.imx 应该从第三个扇区开始烧写,前两个扇区要留出来。
SD卡为什么偏移1Kbyte(1K字节)?
SD卡前512字节保存了分区表分区信息,如果把SD卡前512字节写入数据的话,那么这个SD卡就废了。这里偏移了1K。
IVT(8个地址信息)
那么 IVT 里面究竟存放着什么东西呢? IVT 里面存放的内容如图(一共八个,每个占4字节):
第一个存放的就是 header(头), header 格式如图
- Tag 为一个字节长度,固定为 0XD1
- Length 是两个字节, 保存着 IVT 长度 ,为大端格式,也就是高字节保存在低内存中
- Version 是一个字节,为 0X40 或者0X41
实际情况是不是这样的呢? 我们用winhex 软件打开load.imx (前面点亮LED灯生成的)一看便知,winhex 可以直接查看一个文件的二进制格式数据。用winhex 打开以后的load.imxd 如图所示:
图是我们截取的load.imx 的一部分内容,从地址0X00000000~0X000025F,共608个字节的数据。我们将前44 个字节的数据按照4 个字节一组组合在一起就是:
8个IVT :0X402000D1(左上角第一个,winhex软件低字节在前显示)、0X87800000、0X00000000、0X877FF42C、0X877FF420、0X877FF400、0X00000000、0X00000000
3个Boot Data :0X877FF000、0X00200000、0X00000000
IVT 和Boot Data 所示的格式对应起来如表所示:
| IVT地址信息 | 数据 | 描述 |
|---|---|---|
| header | 0X402000D1 | 根据 header 格式,第一个字节 Tag 为0XD1,第二三这两个字节为 IVT 大小,为大端模式,所以 IVT 大小为 0X20=32 字节。第四个字节为 0X40 |
| entry | 0X87800000 | 入口地址 ,也就镜像第一行指令所在的位置。0X87800000 就是我们链接到RAM地址。 |
| reserved1 | 0X00000000 | 未使用,保留。 |
| dcd | 0X877FF42C | DCD 地址 ,镜像地址为 0X87800000, IVT+BootData+DCD 整个大小为 3KByte。因此 load.imx 的起始地址就是 0X87800000-0XC00=0X877FF400。因此 DCD 起始地址相对于 load.imx 起始地址的偏移就是0X877FF42C-0X877FF400=0X2C,也就是说 0X2C 这个地址开始就是 DCD 数据了。 |
| boot data | 0X877FF420 | boot 地址 , header 里面已经设置了 IVT 大小是 32个 字 节 , 所 以 boot data 的 地 址 就 是0X877FF400+ 32 =0X877FF420。 |
| self | 0X877FF400 | IVT自己的地址 ,就是IVT 复制到 DDR 中以后的首地址。 |
| csf | 0X00000000 | CSF 地址。 |
| reserved2 | 0X00000000 | 保留,未使用 |
Boot Data(3个启动数据信息)
Boot Data 的数据格式如图,一个三个,每个占4字节
| BOOT DATA结构 | 数据 | 描述 |
|---|---|---|
| start | 0X877FF000 | 整个 load.imx 的 起始地址 ,包括前面 1KByte 的地址偏移。 |
| length | 0X00200000 | 镜像大小 ,这里设置 2MByte。镜像大小不能超过2MByte。 |
| plugin | 0X00000000 | 插件 |
我们详细的列出了 load.imx 的 IVT+Boot Data 每 32 位数据所代表的意义。这些数据都是由 imxdownload 这个软件添加进去的(由正点原子的左中凯老师在NXP官方u-boot.imx文件里面提取出来的,反推出的,上面的解释是一种猜测,详见视频讲解:。
DCD数据(配置内部寄存器初始化外设,成对出现——地址+数据)
配置内部寄存器,这个寄存器是非内核寄存器
复位以后, I.MX6U 片内的所有寄存器都会复位为默认值 ,但是这些默认值往往不是我们想要的值,而且有些外设我们必须在使用之前初始化它。 为此 I.MX6U 提出了一个 DCD(DeviceConfig Data)的概念来配置寄存器 ,和 IVT、 Boot Data 一样, DCD 也是添加到 load.imx 里面的, 紧跟在 IVT和 Boot Data 后面 , IVT 里面也指定了 DCD 的位置 。
DCD 其实就是 I.MX6U
寄存器地址和对应的配置信息集合
,
Boot ROM 会使用这些寄存器地址和配置集合来初始化相应的寄存器
,比如开启某些外设的时钟、初始化 DDR 等等。 DCD 区域不能超过 1768Byte, DCD 区域结构如图:
DCD 的 header 和 IVT 的 header 类似,结构如图:
- Tag 是单字节,固定为 0XD2
- Length 为两个字节,表示 DCD 区域的大小,包含 header,同样是大端模式
- Version 是单字节,固定为 0X40 或者 0X41
DCD区域结构图中的 CMD 就是要初始化的
寄存器地址
和相应的
寄存器值
, 结构如图
- Tag 为一个字节,固定为 0XCC
- Length 是两个字节,包含写入的命令数据长度,包含 header,同样是大端模式
-
Parameter 为一个字节,这个字节的每个位含义如图
bytes 表示是目标位置宽度,单位为 byte,可以选择 1、 2、和 4 字节。 flags是命令控制标志位。
Address 和 Vlalue/Mask 就是要初始化的寄存器地址和相应的寄存器值,注意采用的是大端模式(高字节的数据在低位)! 在分析 IVT 的时候我们就已经说过了, DCD数据是从 0X2C 地址开始的。根据我们分析的 DCD 结构可以得到 load.imx 的 DCD数据如表
| DCD结构 | 数据 | 描述 |
|---|---|---|
| header | 0X40E801D2 | 根据的 header 格式,第一个字节 Tag 为 0XD2,第二和三这两个字节为 DCD 大小,为大端模式,所以 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 里面的初始化配置主要包括三方面:
- ①、设置 CCGR0~CCGR6 这 7 个外设时钟使能寄存器,默认 打开所有的外设时钟 。
- ②、配置 DDR3 所用的所有 IO。
- ③、配置 MMDC 控制器 ,初始化 DDR3


发布评论