------------恢复内容开始------------
在上一章我们通过点亮LED实现了第一个小程序。在烧录程序完成插入SD卡后有个选择启动拨码开关的步骤,那么这个启动方式是如何实现的,今天我们就来大致了解下I.MX6U的启动方式
一.启动模式选择
按照I.MX6UL的开发手册上所说,整个BOOT的处理过程就是芯片上电后,芯片首先会根据BOOT_MODE[]1:0]的设置来选择启动方式
00为通过可编程熔断丝启动,这种方式只能修改一次,后面就不能修改了,我们不会使用
01为串行下载器模式,就是通过USB或者UART将代码下载到板子的外置存储设备中。针对阿尔法开发板,可以使用支持OTG的USB接口向板子上的SD/EMMC、NAND等设备下载代码。这个下载需要NXP(恩智浦)提供的一个软件,在量产的时候使用。
10为内部BOOT模式,在此模式下芯片会执行bootROM代码,这段代码会进行硬件初始化,然后从boot设备中将代码拷贝出来复制到指定的RAM中,一般就是DDR。
结合硬件原理图来看(下面拨码开关9那里标注不对,应该是LCD_DATA7。并且这个图我修改过,原图拨码开关标注左边是off)
拨码开关1、2位就是启动方式的选择,我们常用的就是01(USB)或10(内部).
二.启动设备选择
如果要选择启动设备,前提是MODE1=1和MODE0=0,即内部BOOT模式。开发手册上说明了这个模式下支持的设备
我们最常用的就是NANDflash,SD,EMMC,QSPIFlash(轻量级,便宜)
设备的选择是通过BOOT_CFG1,2,4几个寄存器,每个8为,通过LCD1_DATA00-23y一共24个值决定的。
而在开发板上,大部分都是通过47K的下拉电阻接地(BT_CFG4全接地,CFG2上面两个上拉是DNP也就是未焊接,也是全部接地的)
往前翻一下那个拨码开关,可以看一下旁边画的草图,当开关on的时候,LCD_DATA点通过47K的下拉电阻可以得到近似3.3V的电压,可以判定为1。
然后除过1、2两个开关剩下6个拨码分别控制了LCD_DATA3/4/5/6/11脚,对应的BOOT_CFG1[3:7]和BOOT_CFG2[3]
下面的表是讲到BOOT_CFG2的定义
3和4一同决定了板子首冲SDHC1还是SDHC2启动。6ULL支持两个SD卡接口,是通过拨码开关3选择,为1的时候是从SDHC2启动
重要的是BOOT_CFG1,还是结合图表来看
首先,0,1,2都是定死的,3-7被引到了拨码开关。
由于前三种设备我们都未使用,3同2一同决定了是选择SD或MMC/eMMC的速度(能选的只有3,一般都是0),对于NANDFlash是定义了设备的数量
剩下的就直接看SD,eMMC和NAND的设置就行了
对于SD来说,[7:5]是订好的010,[4]是快速启动,我们一般都是0
eMMC时和SD一样,[7:4]是0110
NAND模式时,[7]固定为1,而[6:4]查手册可以发现
[6]是NAND的协议模式,一般我们都用0
[5:4]决定了块的页大小,这个要看NANDFlash的手册了。
三.启动头文件
我们在led的裸机试验里讲过,我们在生成的led.bin文件前要添加一些头文件,在我们选择了从内部BOOT启动时,BOOTROM就会从我们选择的设备里读取加了头部信息的bin文件。
那么BootRom要做哪些事情呢?参考下开发手册8.4设备初始化的部分可以得到
设置内核时钟:396MHz(8.4.3ARM PLL)。
使能MMU和caches(8.4.4),包含L1cache,L2cache,MMU,目的是加速启动。
从BOOT_CFG24个设置外置存储中读取image,然后做相应的处理,在读取底image文件上是包含了头部信息的,那么这里的头部信息又包含什么呢?
bin文件的头部信息
开发手册8.7里讲到下面的内容
IVT镜像向量表 ,包含了镜像那个的入口,DCD的地址等一系列数据信息,这个表在不同启动设备的存储地址及大小是不同的,对于不同设备的IVT起始地址是有规定的,其相应的便宜地址及初始化加载大小如下。BootRom只要知道IVT的地址,其他的组成部分在IVT中的内容就能查到。
对于我们常用的SD/MMC什么的,偏移量是1K,因为对于SD卡等存储设备,前512个字节包含了分区表等信息,肯定是不能用的。所以前面给了个偏移量。而整个IVT+BootData+DCD还有前面被偏移的量一共是4K,那么剩下IVT+BootData+DCD的大小就剩了3K了,所以我们的实际bin地址就是3*1024,也就是0xC00。
这样就能知道我们烧写到SD卡中的load.imx文件是在SD卡中的起始地址为0x400,既然IVT是一个表(Table),那么每个地址的格式都是定死的。IVT具体结构如下表,每个成员都是32bit(4个字节)
我们用hexedit打开那个 load.imx文件,截取从0x00000000到0x0000025F一共608个字节的数据,注意打印出来的内容,左边是低字节,右边是高字节
关注下前44个字节,我们把前44个字节的数据按照4个字节一起的方式组合下数据,分别就是
- 0x402000D1
- 0x87800000
- 0x00000000
- 0x877FF42C
- 0x877FF420
- 0x877FF400
- 0x00000000
- 0x00000000
- 0x877FF000
- 0x00200000
- 0x00000000
这11个数据就是IVT和BootData的数据。我们对照给定的IVT的格式来看下IVT的内容
header:
Tag,固定的为1个字节,固定为0xD1;Length为2个字节,保存IVT长度,注意为大端格式,即高字节保存在低地址中;最后的Version也是1个字节,为0x40或0x41
这里有个很奇怪的点,按照这个图表上画的Tag是在前边,但是对照我们的load.imx文件的前4个字节,0x402000D1,0xD1是低字节的数据,0x40是高字节的数据,那么Tag应该就是对应的低字节。中间两个字节是长度,big endian模式,2000对应的长度应该是0x20,也就是32个字节,这个长度是包含header的。因为每条数据格式都是固定的,都是4个字节,所以IVT就有32/4=8条信息,刚好跟IVT的Format一致。
entry
入口地址,image那个第一行指令的位置,也就是编译时候指定的链接那个地址,我们在编译的时候-Ttext后面指定的链接地址是0x87800000,和第二条数据一致。
resvered1
保留,未使用,数据为0x00000000
dcd
dcd地址,镜像地址为0x87800000(bin的起始地址),IVT+BootData+DCD大小为3Kb(0xC00),因此load.imx的起始地址就是0x87800000-0xC00=0x877FF400,这个地址也就是IVT的起始地址。这个起始地址加上IVT(0x20,32字节),还有BootData的地址就是DCD的地址。BootData的大小在后面讲BootData的时候会讲到,一共12个字节(0xC),所以dcd的起始地址就是(0x877FF400+0x20+0xC=0x877FF42C),和实际数据一样。
真实数据为0x877FF42C,后面时候会再讲到
bootdata
BootData的为地址,因为IVT的起始地址为0x877FF400,长度为0x20,所以Boot的起始地址为0x0x877FF420,和实际数据一致。
self
IVT复制到DDR中的首地址,就是前面计算的IVT的起始地址0x877FF400,和实际数据相同。
csf
未使用,数据为0x00000000
resvered2
未使用,数据为0x00000000
Bootdata数据结构
BootData的数据结构有也是固定的,每条信息4个字节,共3条信息。
先看一下这三条数据也就是上面说的11条数据的最后三条:
- 0x877FF000
- 0x00200000
- 0x00000000
start
第一条就是img的绝对地址,也就是load.imx的绝对地址,即0x87800000-4*1024 = 0x877FF000,和实际相同。注意这个地址是包含了1K的偏移量的(所以是4*1024,包含1K的偏移量和3K的Initial Load Region)。
length
镜像的大小,这里封顶为0x200000,换算为10进制后除以1024就是2048,即2MB。也就是镜像大小不能超过2MB。这个是提供的烧录软件定死的。
plugin
插件,这里没用。
上面的IVT和BootData是教程里从恩智浦给定的u-boot.imx文件提取出来的。整个头部的结构应该是这样的
DCD数据
I.MX6U在每次上电或复位以后,片内所有的寄存器都会被复位为默认值,但是这些值通常不是我们需要的,并且有些外设在使用的时候必须初始化。这就是DCD(Device Config Data)的作用。和IVT、BootData一样,DCD也是添加到load.imx里的,位置就在BootData后面,IVT里也指定了DCD的地址。DCD其实就是I.MX6U寄存器地址和对应的配置信息的集合。BootRom会根据这些信息去初始化相应的寄存器。要注意的是,DCD的区域不能超过1768Byte。其结构如下图所示
和IVT一样,我们也截取一段程序数据看一下。由于在IVT里已经计算过了,DCD的起始是在IVT和Bootdata后即0x20+0xC=0x2C,
内容可以读一下,还是注意方向
- 0x40E801D2
- 0x04E401CC
- 0x68400C02
- 0xFFFFFFF
- 0xB4040E02
- 0xFFFFFFFF
DCD的header和IVT的header基本一样,一个固定的单字节tag,数据为0xD2,第二第三字节表明DCD大小,为0x01E8=488字节,后面为版本号为固定的0x41(实际为0x40,估计版本有出入)
header后面跟的就是命令,也是有固定格式的
第一条是指定的,对应数据为0x04E401CC,0xCC对应Tag,长度为0x1E4=484个字节,是除去header的长度;后一个字节Parameter为0x04,,其结构如下表
0x04可以按照上表理解为目标位置宽度为4个字节, 前面的3、4bit对应的操作为写入。
从第三个数据开始依次就是寄存器地址/写入的值,我们看下第三个值:0x68400C02(注意是大端模式,反转为就是0x020C4068),看下我们的LED.s文件
一开始我们先对时钟使能,对应的寄存器CCGR0的地址就是0x020C4068,写入的数据就是0xFFFFFFFF,是不是就是DCD里的内容了。
下面的0x020C406C,可以直接在手册里查一下(搜索关键字应为20C_406C),可以看出来对应的寄存器是CCM_CCGR1
就是对各种要操作都DDR进行初始化(CCGR1在代码里并没有初始化,查了烧录软件的源代码里的头文件,imxdownload.h,里面对DCD进行了初始化)
其他的数据
后面其实还有检查数据命令、nop命令、解锁命令等我们暂时还没用到,但是全属于DCD的,但是我们都还没用到,看下具体的数据,都是0x00000000。
整个启动的过程以及加载在bin文件前的头部信息的过程,就是上面的内容了,虽然有些枯燥,但还是要搞清楚的!