IMX6ULL深入理解镜像文件的制作(人话版)
IMX6ULL的镜像制作
本文章内容完全可以从IMX6ULL的用户手册的systemBoot章节找到,强烈建议看源文章,是非常不错的学习资料,文章有点长知道一些内存,储存的同学,直接第2节看
前言:是非专业的嵌入式专业的一名学生,喜欢嵌入式Linux的开发,并且希望这个也能成为我以后的职业,(虽然说这个难度不是一点一点)我也是菜鸟,写博客的目的主要是学习笔记,如果有人也遇见和我一样的问题并且可以从我踩过的坑里找到一些解决方法我也非常高兴,能力有限,如果有错误的地方欢迎一起讨论,共同进步。我也看过许多文章,但是总是感觉很有点故弄玄虚,看着云里雾里的,我并不习惯那样,所以我尽力用最接地气的语言解释这些。噼里啪啦说这么多现在开始进入正题。。。。。
0.芯片想怎么加载程序镜像
这个问题一直困扰了我好久好久,我就喜欢想得很多,一直有一个问题我一直不解,芯片从上电到运行程序这一段过程芯片干了啥!!?,做过裸机开发深入了解过寄存器的同学都知道,说白了,我这点灯工程师,点亮了一个LED灯,是因为我在程序里写了先使能了系统的时钟,再配置了GPIO的时钟寄存器,在配置了GPIO的控制寄存器,然后往数据寄存器灌数据,寄存器的位就改变了,寄存器的改变本质上就是改变电路,结果表现在LED灯上,在这个部分是我们清楚的,但是程序是如何被执行到的?程序能不能在SD卡上执行?,nandflash?,eMMC?,norflash?,除norflash外(可以自行百度一下),都不能直接执行。就拿SD卡来说,我们用arm-linux-gcc编译好一个elf格式的可执行文件,再用arm-Linux-objcopy,将elf格式的文件剥离掉调试信息等,得到一个干净的二进制文件,在加一点小处理,用dd命令烧到SD卡上,再把SD卡插到开发板上,然后选择SD卡启动,程序就可以运行了。既然SD卡不能运行程序,那么芯片会怎么加载程序来运行勒?,答案是加载到内存来运行,所以芯片想把SD卡中的程序镜像加载到内存后然后再在内存中运行程序。
1.芯片的IROM.IRAM.DDR
芯片想把SD卡中的程序加载到内存,但是看一下imx6ull-emmc版本芯片的memory map:
由芯片手册可以看出在我们这个开发板芯片上有三个地方的存储空间,因为我们芯片是片上SOC所以 :
在芯片上有 BIOS(boot ROM/IROM),硬盘(flash),内存(OCRAM/IRAM),这个ROM区的内容我们用户一般是不能更改的,内部有NXP公司做好的固件程序,所以这个ROM是不能直接编程存程序的,那它能干啥?它的主要目的就是将用户程序拷贝到内存中运行的,完成芯片上电到运行程序这之间的事情,是拷贝到片上内存(OCRAM)运行?,还是DDR内存中运行?,如果是运行Linux操作系统的话,拷贝到OCRAM运行程序,芯片感觉要裂开,如果你的用户代码很小可以尝试拷贝到OCRAM运行,如果代码量特别大的话,OCRAM也有用,可以把程序先拷贝一部独立的小体积的程序到OCRAM中运行,目标是初始化好DDR内存,然后再把SD卡的大部分内容拷贝到DDR中运行,但是ROM程序为啥要拷贝SD卡的内容,不拷贝开发板上eMMC的内容?我们的ROM程序能直接将SD卡中的程序拷贝到DDR吗?
为啥拷贝SD卡的内容?这个问题主要是通过板子上的启动模式选择来进行选择的,因为芯片厂商的芯片想做得兼容性好一点,支持的启动介质多一点,所以做了一个BOOTMODE的选择,可以使用eFUSE(熔断丝),和一组GPIO两种方式来选择启动的模式,因为熔断丝只能写一次,以后就不能改变了,所以板子厂商就选择的是一组GPIO的选择来进行选择启动的地方,换句话说就是,IROM中的代码检测一组GPIO的状态来选择执行,到底是初始化eMMC呀,还是SD卡,还是nandflash,还是norflash呀,
选择好启动介质后,芯片就会将介质中的内容进行拷贝了?,那芯片真会那么智能蛮,作为老点灯工程师了,应该有个盐井的逻辑(要把机器当傻瓜,它啥也不知道),拷贝到内存,拷贝多少?从SD卡的那个位置拷,拷贝到DDR还是OCRAM,拷贝到DDR的那个地址?拷贝到OCRAM的那个地址?,所以说在拷贝程序的时候还是需要一个,简单的描述一下这个程序有啥特点,告诉芯片,再较真一下,芯片由谁来看这些信息,默默的想起了上电到执行程序之间的苦逼的IROM,IROM中的代码初始化好SD卡(后面的启动介质都以SD卡启动为例),然后看SD卡中的内容,就给女朋友看你写给她的信一样,如果你开头连句亲爱的,都不写的话,可能又得问你是不是不爱她了,哈哈开个玩笑,就是说你的程序需要按照一定的格式来写,不然IROM就不会认你的程序,那需要啥格式呢?
2.程序镜像
那给IROM的一封情书应该怎么写呢?
如下:
你写的程序的全部内容在绿色的区域中,Application应用程序位置,前面的这一大堆东西就是你程序镜像的格式头(格式内容下面节详细说),在SD卡启动中,IROM代码满意了你代码格式后,拷多少?拷贝到哪里?
看这个的用户手册说明:
所以在这块板子上是IROM是先拷贝了4KB的SD卡的内容到OCRAM运行,然后获取了BootData,DCD信息后,初始化好DDR后,重新拷贝到DDR,在DDR中在BootData和IVT信息指明的数据结构和entry地址进行跳转到你的程序运行。(还在值得注意的通过镜像格式图可以看到虽然Initial Load Region Size初始加载区域大小是4KB,但是有1KB的镜像地址偏移,啥意思呢,就是说虽然是拷贝4KB的内容,但是前1KB都是空白的(值为0),实际内容大小就3KB。) 拷贝到那里则通过IVT表的内容决定
3.程序镜像的组织结构(IVT-BootData-DCD)
3.0-IVT表:程序镜像的特征描述
由用户手册我们可以知道:每一个表项都是32位字长
分别解释一下各项的意思和作用:
1.是IVT表的header它有固定格式,是给我IROM代码做识别用的,格式如下:
是这个一个字的前1个字节是8位的识别码,是固定的0xD1,接下来是IVT表的长度(大端格式的->就是看的时候低地址的内容在前高地址在后),最后一个是版本信息一般为0x40
2.entry我们自己写的程序的绝对地址(物理地址),给IROM记录方便重新拷贝到DDR中直接进行程序的跳转
3.空32位空间,.保留,清零
4.BootData的绝对地址,当IROM获取处理完信息后就可以重新拷贝,它指明了重新拷贝地址和地址长度BootData内容下面说
5.DCD的绝对地址,在这块板子上用了DCD(驱动配置数据)数据,DCD数据可以直接执行,只要按照DCD格式写数据就能初始化芯片的外设 DCD内容下面说
6.Self:IVT表的绝对地址,IROM保存识别
7.csfl:安全启动模式使用,非安全模式启动设置位0
8.保留,清零
IVT过后紧跟着的就是BootData在内存的物理地址,也是紧跟着,上面IVT的BootData的绝对地址就好定位了,并且DCD内存地址也是紧跟在BootData的后面的那IVT表的DCD的绝对地址就好定位了
3.1-BootData
1.start:整个镜像加格式头(4KB)的绝对地址-确定储存在SD卡中的内容拷贝到内存时,拷贝到哪里!!
2.length:整个镜像加格式头(4KB)的大小
3.这个是特殊的启动方式的插件位,比如用网络启动什么的,一般没有用,写0
3.2-DCD
格式如下:由一个头部信息+CMD指令组成
1.头部信息:0xD2+length+0x41
2.CMD信息-在这块板子上我参考了原子的启动数据,只用了写CMD的格式
需要一个头信息0xCC+length+value/mask的选择
4.SD卡程序镜像实例
我用二进制文件编辑器打开了,这个sdcard.img的文件,这个是完整的imx6ull的sd卡镜像,有bootloard+Linuxkernel+rootfile。这个完整的程序,也有着NXP公司规定的镜像格式,如图,红色的关键信息,0x0000400这个刚好是1KB,也就是前面说的镜像偏移量,其他红色的就是上面说的IVT,BootData,DCD头,DCD写指令的头信信息
绿色的就是IVT表中的entry的内容,也是我们编写的程序在内存中的绝对地址,也是我们做编译链接时的地址, (因为链接默认是用0x00000000做为基地址,程序中的地址操作都是通过基地址+多少偏移量进行链接的,比如说用默认地址做为链接地址,我程序中有个绝对地址访问操作,但是链接器链接时是默认地址+偏移量,假设这个操作的地址偏移量为1,那么我这个操作就是访问绝对地址为1的地方,如果我用默认链接好的程序,烧写到内存为0x808700000的地方,这个操作原本要访问的地址为0x808700000+偏移量1=0x808700001,但是你链接时链接的默认地址,导致执行这条程序的时候访问的地址变为了0x1)
黄色的就是整个程序镜像(加4KB头信息),拷贝到内存的绝对地址,也就是1KB的镜像偏移的首地址,
蓝色就是上面说的整镜像大小,这个正常情况是要计算的,如果你不想算你编译好的应用程序大小有多大+4KB,你随便给一个比你大的长度就可以了,大不了RIOM多帮你拷贝一点SD的内容。
上面说过前4KB的是Initial Load Region初始加载区域,你可以查一下DCD内容中的地址,其实就是初始化时钟,和DDR内存,并且你看我们程序的入口点设置在了0x800000000以后,其实就是DDR的地址空间,说明IVT+BootDate+DCD完成了基本信息的录入,并且初始化好了DDR内存,IROM通过BootDate提供的信息又重新将SD的内容拷贝到了BootDate的start地址,拷贝了BootDate的length那么多,又因为我们运行的地方从OCRAM变为了DDR所以IROM判断成功后,就会直接将PC指针装入IVT变中的entry地址,芯片就将从你编写的程序开始运行你,如果你写的是bootloard就运行你的bootloard,如果你写的是点亮一个led灯,那么它就点亮一个led了。在橙色的地址0x0001000就是 4KB。那么我们的SD卡镜像内容和那个程序镜像格式就是一模一样的。 如果是裸机开发,先不初始堆栈指针时,我们的镜像可以在?0x80000000开始+1KB空白+(IVT+BootDate+DCD+补齐)(4KB)+我们自己的程序。
5.IMX6ULL镜像的头信息的制作思路
得到xx.bin文件后,可以用文件操作读出文件大下,再申请一个4KB+文件大小的一个buff空间,在把下面IMX的头信息加到buff+1024的空间,再在buff+4096后加入你的xx.bin内容,然后得到img文件,现在就可以烧录了,注意:下面的entry的内容地址
const int IMX_Header[256] = {
0X402000D1,0X87800000,0X00000000,0X877FF42C,0X877FF420,0X877FF400,0X00000000,0X00000000,
0X877FF000,0X00200000,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02,
0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02,
0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02,
0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02,
0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02,
0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02,
0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02,
0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X0B000300,0X3C081B02,
0X44014801,0X48081B02,0X302C4040,0X50081B02,0X343E4040,0X1C081B02,0X33333333,0X20081B02,
0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02,
0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF3526B67,0X10001B02,
0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02,
0XD2260000,0X30001B02,0X23106B00,0X40001B02,0X4F000000,0X00001B02,0X00001884,0X90081B02,
0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02,
0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02,
0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000
};
本文地址:https://blog.csdn.net/qq_44886611/article/details/109590011
上一篇: QT5 代码创建控件示例
下一篇: 两条腿的蛤蟆不好找