正点原子 发表于 2020-5-26 16:57:15

【正点原子Linux连载】第九章I.MX6U启动方式详解--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南

本帖最后由 正点原子 于 2020-10-24 15:54 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
4)对正点原子Linux感兴趣的同学可以加群讨论:876919289
5)关注正点原子公众号,获取最新资料更新




第九章I.MX6U启动方式详解

      I.MX6U支持多种启动方式以及启动设备,比如可以从SD/EMMC、NAND Flash、QSPI Flash等启动。用户可以根据实际情况,选择合适的启动设备。不同的启动方式其启动方式和启动要求也不一样,比如上一章中的从SD卡启动就需要在bin文件前面添加一个数据头,其它的启动设备也是需要这个数据头的。本章我们就来学习一下I.MX6U的启动方式,以及不同设备启动的要求。

9.1启动方式选择
      BOOT的处理过程是发生在I.MX6U芯片上电以后,芯片会根据BOOT_MODE的设置来选择BOOT方式。BOOT_MODE的值是可以改变的,有两种方式,一种是改写eFUSE(熔丝),一种是修改相应的GPIO高低电平。第一种修改eFUSE的方式只能修改一次,后面就不能在修改了,所以我们不使用。我们使用的是通过修改BOOT_MODE对应的GPIO高低电平来选择启动方式,所有的开发板都使用的这种方式,I.MX6U有一个BOOT_MODE1引脚和BOOT_MODE0引脚,这两个引脚对应这BOOT_MODE。I.MX6U-ALPHA开发板的这两个引脚原理图如图9.1.1所示:




图9.1.1 BOOT_MODE原理图
      其中BOOT_MODE1和BOOT_MODE0在芯片内部是有100KΩ下拉电阻的,所以默认是0。BOOT_MODE1和BOOT_MODE0这两个引脚我们也接到了底板的拨码开关上,这样我们就可以通过拨码开关来控制BOOT_MODE1和BOOT_MODE0的高低电平。以BOOT_MODE1为例,当我们把BOOT_CFG的第一个开关拨到“ON”的时候,就相当于BOOT_MODE1引脚通过R88这个10K电阻接到了3.3V电源,芯片内部的BOOT_MODE1又是100K下拉电阻接地,因此此时BOOT_MODE1的电压就是100/(10+100)*3.3V= 3V,这是个高电平,BOOT_CFG这个拨码开关的不管哪个位拨到“ON”就是高电平,拨到“OFF”就是低电平。
      而I.MX6U有四个BOOT模式,这四个BOOT模式由BOOT_MODE来控制,也就是BOOT_MODE1和BOOT_MODE0这两IO,BOOT模式配置如表9.1.1所示:


表9.1.1 BOOT类型
      在表9.1.1中,我们用到的只有第二和第三种BOOT方式。
9.1.1串行下载
当BOOT_MODE1为0,BOOT_MODE0为1的时候此模式使能,串行下载的意思就是可以通过USB或者UART将代码下载到板子上的外置存储设备中,我们可以使用OTG1这个USB口向开发板上的SD/EMMC、NAND等存储设备下载代码。我们需要将BOOT_MODE1拨到“OFF”,将BOOT_MODE0拨到“ON”。这个下载是需要用到NXP提供的一个软件,一般用来最终量产的时候将代码烧写到外置存储设备中的,我们后面讲解如何使用。
9.1.2 内部BOOT模式
      当BOOT_MODE1为1,BOOT_MODE0为0的时候此模式使能,在此模式下,芯片会执行内部的bootROM代码,这段boot ROM代码会进行硬件初始化(一部分外设),然后从boot设备(就是存放代码的设备、比如SD/EMMC、NAND)中将代码拷贝出来复制到指定的RAM中,一般是DDR。
9.2 BOOT ROM初始化内容
      当我们设置BOOT模式为“内部BOOT模式”以后,I.MX6U内部的boot ROM代码就会执行,这个boot ROM代码都会做什么处理呢?首先肯定是初始化时钟,boot ROM设置的系统时钟如图9.2.1所示:


图9.2.1boot ROM系统时钟设置
      在图9.2.1中BT_FREQ模式为0,可以看到,boot ROM会将I.MX6U的内核时钟设置为396MHz,也就是主频为396Mhz。System PLL=528Mhz,USB PLL=480MHz,AHB=132MHz,IPG=66MHz。关于I.MX6U的系统时钟,我们后面会详细讲解。
      内部boot ROM为了加快执行速度会打开MMU和Cache,下载镜像的时候L1ICache会打开,验证镜像的时候L1DCache、L2 Cache和MMU都会打开。一旦镜像验证完成,boot ROM就会关闭L1 DCache、L2 Cache和MMU。
      中断向量偏移会被设置到boot ROM的起始位置,当boot ROM启动了用户代码以后就可以重新设置中断向量偏移了。一般是重新设置到我们用户代码的开始地方,关于中断的内容后面会详细讲解。
9.3启动设备
      当BOOT_MODE设置为内部BOOT模式以后,可以从一下设备中启动:
      、接到EIM接口的CS0上的16位NOR Flash。
      、接到EIM接口的CS0上的OneNAND Flash。
      、接到GPMI接口上的MLC/SLC NAND Flash,NAND Flash页大小支持2KByte、4KByte和8KByte,8位宽。
      、Quad SPI Flash。
      、接到USDHC接口上的SD/MMC/eSD/SDXC/eMMC等设备。
      、SPI接口的EEPROM。
      这些启动设备如何选择呢?I.MX6U同样提供了eFUSE和GPIO配置两种,eFUSE就不讲解了。我们重点看如何通过GPIO来选择启动设备,因为所有的I.MX6U开发板都是通过GPIO来配置启动设备的。正如启动模式由BOOT_MODE来选择一样,启动设备是通过BOOT_CFG1、BOOT_CFG2和BOOT_CFG4这24个配置IO,这24个配置IO刚好对应着LCD的24根数据线LCD_DATA0~LCDDATA23,当启动完成以后这24个IO就可以作为LCD的数据线使用。这24根线和BOOT_MODE1、BOOT_MODE0共同组成了I.MX6U的启动选择引脚,如图9.3.1所示:


图9.3.1启动引脚
      通过图9.3.1中的26个启动IO即可实现I.MX6U从不同的设备启动,BOOT_MODE1和BOOT_MODE0已经讲过了。看到这24个IO是不是头大?调整这24个IO的高低电平得多复杂啊?其实不然,虽然有24个IO,但是实际需要调整的只有那几个IO,其它的IO全部下拉接地即可,也就是设置为0。打开I.MX6U-ALPHA开发板的核心板原理图,这24个IO的默认设置如图9.3.1所示:

图9.3.1 BOOT_CFG默认设置

      可以看出在图9.3.1中大部分的IO都接地了,只有几个IO接高,尤其是BOOT_CFG4这8个IO都10K电阻下拉接地,所以我们压根就不需要去关注BOOT_CFG4。我们需要重点关注的就只剩下了BOOT_CFG2和BOOT_CFG1这16个IO。这16个配置IO含义在原理图的左侧已经贴出来了,如图9.3.2所示:


图9.3.2 BOOT_CFG引脚含义
      图9.3.2看着是不是也很头大,BOOT_CFG1和BOOT_CFG2这16个IO还能不能在减少呢?可以!打开I.MX6U-ALPHA开发板的底板原理图,底板上启动设备选择拨码开关原理图如图9.3.3所示:


图9.3.3 BOOT选择拨码开关
      在图9.3.3中,除了BOOT_MODE1和BOOT_MODE0必须引出来,LCD_DATA3~LCDDATA7、LCD_DATA11这6个IO也被引出来了,可以通过拨码开关来设置其对应的高低电平,拨码开关拨到“ON”就是1,拨到“OFF”就是0。其中LCD_DATA11就是BOOT_CFG2,LCD_DATA3~LCD_DATA7就是BOOT_CFG1~BOOT_CFG1,这6个IO的配置含义如表9.3.1所示:



表9.3.1 BOOT IO含义
      根据表9.3.1中的BOOT IO含义,I.MX6U-ALPHA开发板从SD卡、EMMC、NAND启动的时候拨码开关各个位设置方式如表9.3.2所示:


表9.3.2 I.MX6U-ALPHA开发板启动设置
      我们在“第八章汇编LED灯试验”中,最终的可执行文件led.bin烧写到了SD卡里面,然后开发板从SD卡启动,其拨码开关就是根据表9.3.2来设置的,通过上面的讲解就知道为什么拨码开关要这么设置了。
9.4镜像烧写
注意!本小节会分析bin文件添加的头部信息,但是在笔者写本教程的时候关于I.MX系列SOC头部信息的资料很少,基本只能参考NXP官方资料,而官方资料有些地方讲解的又不是很详细。所以本节有部分内容是笔者根据NXP官方u-boo.imx文件的头部信息反推出来的,因此难免有错误的地方,还望大家谅解!如有发现错误之处,欢迎大家在www.openedv.com论坛上留言。
      前面我们设置好BOOT以后就能从指定的设备启动了,但是你的设备里面得有代码啊,在第八章中我们使用imxdownload这个软件将led.bin烧写到了SD卡中。imxdownload会在led.bin前面添加一些头信息,重新生成一个叫做load.imx的文件,最终实际烧写的是laod.imx。那么肯定就有人问:imxdownload究竟做了什么?load.imx和led.bin究竟是什么关系?本节我们就来详细的讲解一下imxdownload是如何将led.bin打包成load.imx的。
      学习STM32的时候我们可以直接将编译生成的.bin文件烧写到STM32内部flash里面,但是I.MX6U不能直接烧写编译生成的.bin文件,我们需要在.bin文件前面添加一些头信息构成满足I.MX6U需求的最终可烧写文件,I.MX6U的最终可烧写文件组成如下:
      、Image vectortable,简称IVT,IVT里面包含了一系列的地址信息,这些地址信息在ROM中按照固定的地址存放着。
      、Bootdata,启动数据,包含了镜像要拷贝到哪个地址,拷贝的大小是多少等等。
      、Deviceconfigurationdata,简称DCD,设备配置信息,重点是DDR3的初始化配置。
      、用户代码可执行文件,比如led.bin。
      可以看出最终烧写到I.MX6U中的程序其组成为:IVT+Bootdata+DCD+.bin。所以第八章中的imxdownload所生成的load.imx就是在led.bin前面加上IVT+Bootdata+DCD。内部Boot ROM会将load.imx拷贝到DDR中,用户代码是要一定要从0X87800000这个地方开始的,因为链接地址为0X87800000,load.imx在用户代码前面又有3KByte的IVT+Boot Data+DCD数据,下面会讲为什么是3KByte,因此load.imx在DDR中的起始地址就是0X87800000-3072=0X877FF400。 
9.4.1 IVT和Boot Data数据
      load.imx最前面的就是IVT和Boot Data,IVT包含了镜像程序的入口点、指向DCD的指针和一些用作其它用途的指针。内部Boot ROM要求IVT应该放到指定的位置,不同的启动设备位置不同,而IVT在整个load.imx的最前面,其实就相当于要求load.imx在烧写的时候应该烧写到存储设备的指定位置去。整个位置都是相对于存储设备的起始地址的偏移,如图9.4.1.1所示:



图9.4.1.1IVT偏移
      以SD/EMMC为例,IVT偏移为1Kbyte,IVT+Bootdata+DCD的总大小为4KByte-1KByte=3KByte。假如SD/EMMC每个扇区为512字节,那么load.imx应该从第三个扇区开始烧写,前两个扇区要留出来。load.imx从第3KByte开始才是真正的.bin文件。那么IVT里面究竟存放着什么东西呢?IVT里面存放的内容如图9.4.1.2所示:



图9.4.1.2 IVT格式
      从图9.4.1.2可以看到,第一个存放的就是header(头),header格式如图9.4.1.3所示:



图9.4.1.3 IVT header格式
      在图9.4.1.3中,Tag为一个字节长度,固定为0XD1,Length是两个字节,保存着IVT长度,为大端格式,也就是高字节保存在低内存中。最后的Version是一个字节,为0X40或者0X41。
      Boot Data的数据格式如图9.4.1.4所示:




图9.4.1.4 Boot Data数据格式
实际情况是不是这样的呢?我们用winhex软件打开load.imx一看便知,winhex可以直接查看一个文件的二进制格式数据,winhex软件我们已经放到了开发板光盘中,路径为:开发板光盘->3、软件->winhexv19.7.zip,大家自行安装。用winhex打开以后的load.imxd如图9.4.1.4所示:


图9.4.1.4load.imx部分内容
      图9.4.1.4是我们截取的load.imx的一部分内容,从地址0X00000000~0X000025F,共608个字节的数据。我们将前44个字节的数据按照4个字节一组组合在一起就是:0X402000D1、0X87800000、0X00000000、0X877FF42C、0X877FF420、0X877FF400、0X00000000、0X00000000、0X877FF000、0X00200000、0X00000000。这44个字节的数据就是IVT和Boot Data数据,按照图9.4.1.2和图9.4.1.4所示的IVT和Boot Data所示的格式对应起来如表9.4.1.1所示:




表9.4.1.1load.imx结构分析
      在表9.4.1.1中,我们详细的列出了load.imx的IVT+Boot Data每32位数据所代表的意义。这些数据都是由imxdownload这个软件添加进去的。
9.4.2 DCD数据
      复位以后,I.MX6U片内的所有寄存器都会复位为默认值,但是这些默认值往往不是我们想要的值,而且有些外设我们必须在使用之前初始化它。为此I.MX6U提出了一个DCD(Device Config Data)的概念,和IVT、Boot Data一样,DCD也是添加到load.imx里面的,紧跟在IVT和Boot Data后面,IVT里面也指定了DCD的位置。DCD其实就是I.MX6U寄存器地址和对应的配置信息集合,Boot ROM会使用这些寄存器地址和配置集合来初始化相应的寄存器,比如开启某些外设的时钟、初始化DDR等等。DCD区域不能超过1768Byte,DCD区域结构如图9.4.2.1所示:


图9.4.2.1 DCD区域结构
      DCD的header和IVT的header类似,结构如图9.4.2.2所示:


图9.4.2.2 DCD的header结构
      其中Tag是单字节,固定为0XD2,Length为两个字节,表示DCD区域的大小,包含header,同样是大端模式,Version是单字节,固定为0X40或者0X41。
      图9.4.2.1中的CMD就是要初始化的寄存器地址和相应的寄存器值,结构如图9.4.2.3所示:


图9.4.2.3 DCD CMD命令格式
      图9.4.2.3中Tag为一个字节,固定为0XCC。Length是两个字节,包含写入的命令数据长度,包含header,同样是大端模式。Parameter为一个字节,这个字节的每个位含义如图9.4.2.4所示:


图9.4.2.4 Parameter结构
      图9.4.2.4中的bytes表示是目标位置宽度,单位为byte,可以选择1、2、和4字节。flags是命令控制标志位。
      图9.4.2.3中的Address和Vlalue/Mask就是要初始化的寄存器地址和相应的寄存器值,注意采用的是大端模式!DCD结构就分析到这里,在分析IVT的时候我们就已经说过了,DCD数据是从图9.4.1.4的0X2C地址开始的。根据我们分析的DCD结构可以得到load.imx的DCD数据如表9.4.2.1所示:






表9.4.2.1 DCD数据结构
      从图9.4.2.1中可以看出,DCD里面的初始化配置主要包括三方面:
      ①、设置CCGR0~CCGR6这7个外设时钟使能寄存器,默认打开所有的外设时钟。
      ②、配置DDR3所用的所有IO。
      ③、配置MMDC控制器,初始化DDR3。
      I.MX6U的启动过程我们就讲解到这里,本章我们详细的讲解了I.MX6U的启动模式、启动设备类型和镜像烧写过程。总结一下,我们编译出来的.bin文件不能直接烧写到SD卡中,需要在.bin文件前面加上IVT、Boot Data和DCD这三个数据块。这三个数据块是有指定格式的,我们必须按照格式填写,然后将其放到.bin文件前面,最终合成的才是可以直接烧写到SD卡中的文件。











lonny_chen 发表于 2021-5-13 08:55:55

买了imx6u开发板
页: [1]
查看完整版本: 【正点原子Linux连载】第九章I.MX6U启动方式详解--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南