本帖最后由 翠花儿 于 2021-8-5 12:06 编辑
I.MX6ULL支持多种启动方式,我们可以通过设置不同的启动方式来选择不同的启动设备,比如从SD卡,EMMC,NAND Flash等启动。本章我们就来学习下I.MX6ULL的启动方式和镜像格式。
1 i.MX6ULL启动方式详解I.MX6ULL支持多种启动方式,可以从EMMC、SD卡、NAND FLASH、USB、QSPI等设备启动。通过查看《IMX6ULL参考手册.pdf》手册的第8章System Boot,我们可以了解到i.MX6ULL在上电以后,芯片ROM里面固化的程序会根据BOOT_MODE[1:0]寄存器的值来选择启动方式。 BOOT_MODE[1:0]寄存器的值有两种方式可以修改:一种是采用eFuses(熔丝)的方式;另一种是通过设置BOOT_MODE[1:0]对应的GPIO的状态。我们在平常使用的时候基本都是采用设置BOOT_MODE[1:0]对应的GPIO状态来选择启动方式的。 BOOT_MODE[1:0]对应的GPIO引脚是BOOT_MODE1和BOOT_MODE0,如下图所示: BOOT_MODE1和BOOT_MODE0这两个引脚在芯片内部默认有100K的下拉,所以这两个引脚在悬空状态下默认是低电平(0)。我们把这两个引脚引到了底板上,并连接到了拨码开关上,拨码开关对应的另一端通过10K电阻接到了3.3V电源上,这样我们可以通过拨码开关来设置这两个IO的状态了(高电平或低电平)。如下图所示: 从上面的截图,可以看到如果我们要设置BOOT1和BOOT0为高电平我们需要将对应的拨码开关设置到“ON”位置,这样就通过10K电阻上拉到3.3V,芯片内部有100K的下拉,所以此时这两个IO的电平是:100/(10+100)*3.3V=3V,属于高电平。 从参考手册我们看到BOOT_MODE[1:0]两个IO可以设置四种状态,对应着四种启动方式,如下图所示: 从上图可以看到BOOT_MODE[1:0]:设置为00的时候从Fuses启动,设置为01的时候串行下载,设置为10的时候内部Boot模式,设置为11作为保留使用。我们用到的是“串行下载”和“内部Boot”模式这两种。 首先我们来看下“串行下载”,它是指可以通过USB接口(板子的OTG接口)将代码下载到板子上然后运行。通过此方式我们可以实现开发板镜像的烧写(我们将在后面章节介绍具体的烧写方法)。 然后我们看下“内部Boot模式”,它是指在此种模式下,芯片会执行内部ROM里面固化的boot程序。这段程序会进行硬件的初始化,然后从Boot设备(EMMC、SD卡、NAND FLASH)中读取出镜像到内存中。如果在“内部Boot模式”运行过程中发生错误,则会进入“串行下载模式”。 当BOOT_MODE设置为“内部Boot模式”后,i.MX6ULL可以从SPI,EIM,NOR,SD,MMC,QSPI等设备启动,同时也可以配置相应的参数(例如SD的总线宽度,速度等等)。启动设备和对应的参数我们可以使用eFUSEs的方式设置,也可以通过GPIO来配置(我们主要使用GPIO的方式)。下面我们来看一下如何通过GPIO设置启动设备。 从参考手册我们可以看到启动设备主要通过三组IO来设置,他们分别是BOOT_CFG1[7:0],BOOT_CFG2[7:0],BOOT_CFG4[7:0]这三组IO。如下图所示: 从上图我们可以看到这三组决定启动设备的IO正好用到的RGB的24根数据IO,在i.MX6ULL芯片刚开始上电的时候这24个IO先作为“选择启动设备”的功能来使用,在ROM中的固化程序把启动镜像从启动设备加载到内存,并开始从启动镜像(Uboot)开始运行的时候,这24个IO就可以当做RGB来使用了。 现在我们来总结一下,i.MX6ULL启动流程:首先根据BOOT_MODE的两个IO(BOOT_MODE1,BOOT_MODE0)先确定启动方式,如果是串行下载模式,就会进入USB启动模式。如果是“内部Boot”启动方式,然后就根据BOOT_CFG1,BOOT_CFG2,BOOT_CFG4这三组IO的配置来决定从哪种设备启动(读取启动镜像到内存,并开始启动)。 接下来我们看下BOOT_CFG1,BOOT_CFG2,BOOT_CFG4这三组IO是怎么配置启动方式的,在我们的底板原理图上有如下图: 上图中最左侧一列是支持的启动设备,后面的每一列分别对应BOOT_CFG的IO,我们可以看到决定启动方式的是由BOOT_CFG1和BOOT_CFG2这两组IO决定。大家也许觉得这么多IO需要配置,操作起来太麻烦了,实际上这些IO中大部分都是默认下拉的,需要配置的只有6个,从我们的原理图可以看到(因为我们用了一个8位的拨码开关,其中有两位是BOOT_MOD的),如下图所示: 从上面的原理图我们可以看到8位的拨码开关一端上拉到3.3V,另一端分别接到了BOOT_MODE1,BOOT_MODE0,BOOTCFG2[3],BOOTCFG1[3],BOOTCFG1[4],BOOTCFG1[5],BOOTCFG1[6],BOOTCFG1[7]。所以我们设置启动方式,只需要修改这8位(有两位是BOOT_MODE)IO的状态,我们看下负责启动方式的这6个IO的含义,如下表所示: | | | 为0时从SDHC1上的SD/EMMC启动,为1时从SDHC2上的SD/EMMC启动。 | | 当从SD/EMMC启动的时候设置启动速度,当从NAND启动的话设置NAND数量。 | | BOOT_CFG1[7:4]: 0000 NOR/OneNAND(EIM)启动。 0001 QSPI启动。 0011 SPI 启动。 010x SD/eSD/SDXC启动。 011x MMC/eMMC启动。 1xxx NAND Flash启动。 | | | |
根据上面的表格,我们设置开发板分别从串行下载,EMMC,SD卡,NAND FLASH启动的时候,可以按照下面的表格设置,如下表: 下面是i.MX6ULL终结者底板上的拨码开关输入图片,如下图所示: 上图中的8位拨码,拨到上面“ON”位置就是1,拨到下面就是“0”,8位拨码按照表2.1中的设置就可以按照对应的方式启动。 2 镜像格式I.MX6ULL的镜像文件由下面几部分组成: 1.Image vector table简称IVT。 2.Boot data,启动数据。 3.Device configuration data,简称DCD,主要是芯片的配置信息 4.用户代码生成的执行文件。比如uboot等。 所以i.MX6ULL的镜像组成为:IVT+Boot Data+DCD+用户程序。 我们首先看下IVT部分:它里面包含了程序的入口点,一个指向设备配置数据(DCD)的指针,以及其他在启动过程中被ROM固化程序用到的指针。IVT被存储在启动设备固定的位置(但是不同的启动设备保存的位置可能会不一样)。IVT在镜像文件的最前面,如下图是不同启动设备,IVT所在的位置偏移(相对于设备首地址): 这里我们以EMMC为例,从上表可以看出他的偏移是1Kbyte(1024字节),假设EMMC的每个扇区是512字节,那么我们的IVT要从第三个扇区开始保存。我们从《IMX6ULL参考手册.pdf》的8.7.1.1章节可以看到IVT的数据格式如下图所示: 上图中第一个存放的是header(头),header头的格式如下图所示: 其中的Tag是一个字节设置为0xD1,length是2个字节,按照大端模式存储,表示IVT的长度,最后一个Version是版本信息,占用一个字节,通常为0x40或0x41。由于《IMX6ULL参考手册.pdf》文档只有header的描述其它几个字节的描述没有涉及。我们可以打开编译生成的Uboot镜像uboot.imx查看下它里面的内容。 接下来是Boot data,它的数据格式如下图所示: 从上图可以看出Boot data一共是三个字段,每个字段32位。 然后是DCD,DCD紧跟在Boot Data后面,IVT里面也定义了DCD的位置。DCD主要是初始化片内的寄存器。DCD的数据格式如下图所示: 第一个是Header包头信息,他是一个32位的包头,具体定义如下图所示: Tag占一个字节,被设置为0xD2;Length占两个字节,按照大端模式存储,表示DCD的区域大小(包含包头的长度),Version占一个字节,设置为0x40或0x41。 Header后面的是CMD配置寄存器的字节,每个CMD的结构如下图所示: CMD域的第一个是Tag,占一个字节,设置为0xcc;然后是Lehgth占两个字节,按照大端模式存储,表示该CMD的长度(包含CMD的包头),Parameter占一个字节,这个字的每个为含义如下图所示: 上图中的bytes表示目标位置宽度,单位为byte,可以设置为1,2,4字节。Flags是命令控制标志位。 然后CMD域后面的是Address和Value/Mask。这两个分别是要初始化的寄存器地址,设置的值。 紧接着DCD的就是我们的程序生成的.bin文件。至此关于i.MX6ULL镜像的组成格式我们就分析完成了。只是看上面的介绍大家也许感觉到会很枯燥,接下来我们结合uboot.imx镜像,来一起看看这些数据的组织格式。我们使用winhex软件(在光盘资料的“i.MX6UL终结者光盘资料\02_开发所需软件”目录下面)打开光盘资料的“i.MX6UL终结者光盘资料\08_开发板系统镜像\uboot\emmc\u-boot.imx”文件。 从上图可以看出是按照小端方式显示的,我们首先把前面44个字节按照4个字节一组的方式组合在一起就是:0x402000D1,0x87800000,0x00000000,0x877FF42C,0x877FF420,0x877FF400,0x00000000, 0x00000000。这8组数据是IVT的数据,我们整理成下表: IVT数据信息 | | | | | | 根据上面header 的格式,第一个字节Tag为0XD1,二,三两个字节为IVT大小,为大端模式,所以IVT大小为 0X20=32 字节。第四个字节为 0X40 | | | 入口地址,也就是镜像第一行指令所在的位置。0X87800000 就是uboot的链接地址,后面裸机开发我们也使用这个地址。 | | | | | | | | | boot地址,header里面已经设置了IVT大小是32个字节 , 所以我们可以推出IVT的地址就是 0X877FF420-32=0X877FF400 | | | | | | | | | |
然后是Boot Data域,我们把接下来的12个字节按照4个字节一组的方式组合在一起就是0x877FF000,0x0007E000,0x00000000。我们整理成下表所示: Boot Data数据信息 | | | | | | 整个Uboot.imx的其实地址,包括前面1KByte的地址偏移 | | | | | | |
然后紧接着就是DCD的数据,如下表所示: DCD数据信息 | | | | | | 根据header格式,第一个字节Tag为0XD2,二和三这两个字节为DCD大小,为大端模式,所以DCD大小为0X01E8=488 字节。第四个字节为0X40。 | | | 第一个为Tag,固定为0XCC,第二和第三个字节是大端模式的命令总长度,为0X01E4=484个字节。第四个字节是Parameter,为0X04,表示目标位置宽度为4个字节。 | | | | | | 要写入寄存器CCGR0的值,表示打开CCGR0控制的所有外设时钟。 | | | | | | | | | 要写入寄存器CCGR6的值,表示打开CCGR6控制的所有外设时钟。 | | | 寄存器 IOMUXC_SW_PAD_CTL_GRP_DDR_TYPE 寄存器地址。 | | | | | | 寄存器 IOMUXC_SW_PAD_CTL_GRP_DDRPKE 地址 | | | | | | 寄存器 IOMUXC_SW_PAD_CTL_PAD_DRAM_SDCLK0_P | | | | | | | | | 寄存器 IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM1 | | | | | | | | | | | | |
从上面的表格我们可以看到DCD主要做了下面的配置: 1. 使能所有外设的时钟。 2. 配置DDR3所用的所有IO。 3. 配置MMDC控制器,初始化DDR3。 关于i.MX6ULL的启动流程和启动镜像文件格式我们就介绍到这里。通过本章的讲解我们可以掌握i.MX6ULL的启动设置了。我们编译出的二进制.bin文件,不能直接烧写到EMMC中,需要在它前面加上IVT,Boot Data和DCD这三部分的数据域。我们之中烧写到EMMC的镜像文件时:IVT+Boot Data+CDC+用户的.bin文件。 3 启动模式设置(拨码开关)我们设置开发板分别从串行下载,EMMC,SD卡,NAND FLASH启动的时候,可以按照下面的表格设置,如下表: 表1 下面是i.MX6ULL终结者底板上的拨码开关输入图片,如下图所示: 上图中的8位拨码,拨到上面“ON”位置就是1,拨到下面就是“0”,8位拨码按照表2.1中的设置就可以按照对应的方式启动。 4 uboot模式在确认电源、串口等连接好以后,按下开发板上的SWITCH按键,启动开发板,这时开发板上 POWER 旁
边的 Led 灯会点亮,是 u-boot启动信息。 读秒倒计时的过程中如果输入任何值,将进入 uboot 模式,
在 PC 的串口上可以看到类似下图所示的系统启动的信息。在文档的后面,我们统一将“在超级终端的命
令行里面输入命令” 的过程叫做“进入超级终端的 uboot 模式, 输入命令” 或者“进入 uboot 模式” 。 我们在 uboot 模式下, 可以设置相关的环境变量,如屏幕类型的设置等。 5文件系统模式启动开发板以后,在开发板启动过程中,如果用户不做任何操作和控制,等系统启动成功,然后多次输入回车,最后超级终端会出现如下图所示的界面。 就表明超级终端进入了文件系统模式。在文件系统模式下,我们可以像Ubuntu 控制台那样输入命令来操作开发板,如 ls, pwd 命令等。 6 系统开关机(必看)开发板使用关机命令如poweroff,shutdown等,要长按开发板的ON/OFF按键才可以开机,否则开机没有打印并且烧写器不识别,这里建议大家直接硬关机,使用开发板的自锁开关进行开关机,硬关机不会对开发板使用寿命有影响。
|