软硬件环境
宿主机系统:Fedora10
U-boot版本:U-boot-2012-10
板子芯片:S5PV210(Contex-A8),512M DDR2,512M SLC Nand
交叉编译器:arm-linux-gcc-4.5.1-v6-vfp-20120301(支持armv7指令系统)
1.写在前面的话
Tiny210买回来好几个星期了,一直没有很认真的弄他,偶尔看看友善之臂提供的裸机程序。本来买板子回来是要跑android系统的,还没有真正跑到那一步。本来以为友善之臂会和mini2440一样提供U-boot源码,买回来发现啥都木有,只有一个superBoot。superBoot确实很美,可是不是我们自己的,总觉得有点遗憾。所以不想用superBoot来做Bootloader,想自己移植U-boot。搜索了网上的一些帖子和博客。对s5pv210的u-boot移植有两个人是连篇写的,写得也不错。一个是在别人移植到mini210的u-boot基础上改的,一个是移植到qt210的。第一个是在别人修改的源码上修改的,没有了在原始代码上移植的滋味,并且我试过,我这边没法编译,悲催!第二个是在QT210上的,虽然芯片是一样的,可是按照他的步骤第一步我的程序就跑不起来。哎!算了吧,还是靠自己吧!!!
移植之前首先要准备几样东西,下面列个清单:
①U-boot-2012-10的源代码,可以自行去http://ftp.denx.de/pub/u-boot/下载
②arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz交叉编译器,友善之臂的光盘有提供
③S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf,这个是S5PV210的启动 流程文档,友善的光盘有提供在光盘的\No OS(裸机程序)\reference下
④S5PV210_UM_REV1.1.pdf,S5PV210的datasheet,友善光盘有提供
⑤s5pv210.h和mkv210_image.c,s5pv210.h是定义了s5pv210芯片地址的头文件, mkv210_image.c到我们制作spl的时候需要使用到。这两个文件可以在友善之臂提
供的光盘中找到,具体位置在\No OS(裸机程序)\src\6.sdram\BL1中就有这两个文件
⑥准备一张4G的可用的SD卡和一块tiny210开发板。
在这一篇中不会涉及到U-boot的移植,只是做U-boot移植之前的一些基本前提知识的介绍,这样子我们进入U-boot的移植才会明白每一步是怎么走的,要不你总是拷贝别人的修改的代码来编译,就算能跑起来也不是你的。
2.S5PV210介绍
S5PV210是三星的Contex-A8系列的一块芯片,属于ARMV7架构,如果你想了解Context-A8是啥,那你就得来了解ARM的发展历程。说起ARM那是一个大名鼎鼎的家伙,可能你们觉得手里拿着Iphone和IPad有多高级,可是别忘了,你拿着的这些东西的处理器都是ARM架构的,就算是Iphone5使用的自己的不标准的ARM架构的A6,可是还是ARM沾边的,可以说是盗版的ARM,至于三星,HTC,那就更加不用说了,都是用高通的ARM架构芯片比较多。2011年的时候ARM在全球的移动设备市场上占据了90%以上的份额,简直就比Intel牛多了!不过两个不同的市场,没什么可比性。听说最近Intel也在向移动设备靠近,不过被Android搞个半死,因为Android开始设计就没把Intel纳入它使用的芯片范畴,对Intel是各种不支持,并且Intel使用CISC指令,功耗可想而知。对于具体的ARM发展的历史和知识,请自行查看网络。
S5PV210的datasheet中有对它的基本架构的一些介绍,里面有这么一幅图
从这张图中我们可以看出,对于S5PV210来说,除了内部的运算单元,两级高速缓存之外其他的部分都是属于核外部件,这跟S3C2440不一样,当然啦,要是一样,那就没必要换个名字了。并且ContextA8具有32KB的一级缓存(Cache)和512K的二级缓存,这是S3C2440没法比的。如果你学过《计算机组成原理》,那么你就知道Cache对CPU来说是多么的重要的。
我们看到ContextA8有一个96K的iRam和一个64K的iRom。这两个东西是干嘛用的呢,iRom是拿来存放ContextA8的启动BL0的,在出厂的时候由生产商固化在里面,iRam是ContextA8的内部RAM,这个才是真正意义上的内存,BL0会把BL1拷贝到iRam中运行,至于什么是BL0,BL1,下面会仔细介绍,这里不罗嗦。然后剩下的就是memory管理接口,多媒体管理接口,电源管理接口等。我们的芯片中有4个串口,3个IIC接口,2个SPI接口,有一个DSP和一个MFC还有2D,3D加速器。有四个PLL升频锁相环路,用于为芯片提供CPU时钟和外设时钟。支持MLC/SLC NAND接口和DDR2/LPDDR2接口。
再来看看ContextA8的Memory Map,datasheet有张图很清楚的介绍了
我们看到IROM和IRAM是映射到了0x0000 0000 ~0x1FFF FFFF地址空间,如果你认真点看你会发现在0xD000 0000的位置还有一个IROM&IRAM,这是为啥呢?如果你看下面你就知道其实我们的S5PV210的IROM是挂在了0x0000 0000这里,而IRAM呢是挂在了0xD000 0000这里的。从0x2000 0000开始到0x7FFF FFFF是两个DRAM,1个512M一个1G。具体其他的自己可以看datasheet,可以从这里看出我们的基本的一些外设是挂在哪个位置的。接下来我们开始接触三星给我们提供的英文的S5PV210介绍文档,这个文档给我们介绍了iRom的基本程序和S5PV210的基本启动流程。
3. S5PV210启动流程
这里首先假设你知道计算机的基本知识对系统和bootloader一点认识也没有。这里讲的都是以S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf(以下简称SIAP)这个文档以及S5PV210的S5PV210_UM_REV1.1.pdf这个版本的datasheet作为根据的。
3.1板子是怎么启动起来的?
平时我们开电脑的时候就是一按开关就开起来了,就这么简单!是的,就这么简单!可是作为一个嵌入式开发人员来说,你要是也是看到这么个表象的话,那肯定不行的,这些表面现象本来就不是做给你看的,是你要做给别人看的。所以我们必须先要知道从我们给我们的板子一上电的瞬间开始,我们的程序是怎么跑起来的。首先我们先来看看SIAN里面的一个图:
对这个图,在SIAP里面有很详细的解析,在这个图中我们看以看出我们的S5PV210支持Nor Flash启动,eSSD启动,MMC启动,OneNand启动和Nand启动。这里的启动代码分为三个部分,第一个就是BL0,第二个就是BL1,第三个就是BL2,这里的BL就是Bootloader的意思。这些标着①②③的就是三星给我们的一个启动流程建议步骤,为啥说是建议的步骤呢?因为我们的启动有时候不一定按照它的建议来,不过流程是不变的,只是代码存放的地方要做一些调整。我们先来看看这几个流程是怎么样子的,如果你喜欢自己看,请自己自行去看SIAP,里面说得很清楚,并且还有流程图。
第一步,开始运行IROM里面的BL0,这些代码是三星给我们固化好的,你想改或者想看,那么没门了,可是三星告诉了你它做了什么东西,我们看看三星是怎么说的:
2.2 iROM(BL0) boot-up sequence (Refer 2.3 V210 boot-up diagram)
1. Disable the Watch-Dog Timer
2. Initialize the instruction cache
3. Initialize the stack region (see “memory map” on chap 2.5)
4. Initialize the heap region. (see “memory map” on chap 2.5)
5. Initialize the Block Device Copy Function. (see “Device Copy Function” on chap 2.7)
6. Initialize the PLL and Set system clock. (see “clock configuration” on chap 2.11)
7. Copy the BL1 to the internal SRAM region (see “Device Copy Function” on chap 2.7)
8. Verify the checksum of BL1.
If checksum fails, iROM will try the second boot up. (SD/MMC channel 2)
9. Check if it is secure-boot mode or not.
If the security key value is written in S5PV210, It’s secure-boot mode.
If it is secure-boot mode, verify the integrity of BL1.
10. Jump to the start address of BL1
从这段英文介绍中我们可以看出BL0做的东西挺多了:关闭看门狗,初始化cache,初始化堆栈,初始化系统时钟,拷贝BL1到IRAM中运行等并且我们知道它为我们提供了好几个拷贝函数,这些函数我们在后面会使用到,既然三星提供给我们了,我们就不用自己写的,何乐而不为呢是吧。可是这里有一个很重要的信息我们没有读取到,三星也没有明显提及,就是我们的BL1该有多大呢?总不可能无限大的,因为我们的IRAM就只有96K,撑死让你全用了,也就是96K。在网络上也存在两种说法,一个说是16K,一个说是8K。到底是多少k呢?这个问题我也说不准,不过我觉得应该是8K,为什么呢,我们来看SIAN里面的一个memory map的图
这个是我们上面说的那个全部memory map的一部分,就是我们IROM和IRAM的那两个部分,IROM我们就不看的,我们来看IRAM的,我们可以看到从0xD002_0000开始到0xD003_5400这8K的空间中包含了BL1和Reserved,那么我觉得BL1最大只能是8K。但是这也不一定,如果BL0就是拷贝16K过来的呢?这个我没做过实验,如果有星期自己可以做过实验验证一下是不是真的是拷贝16K,而且我们使用友善的那个生成BL1的工具是把它认为是16K的。
既然说到了这个图,那么我们继续对这个图挖掘一下信息。我们可以看到在BL1的前面16个字节是一些关于BL1的基本信息的,其中包含了BL1 size和BL1的cksum值,而在BL0中,貌似只做cksum校验,那么这个cksum我们必须填写正确。具体的自己去看友善的代码。
下面我们要去找拷贝BL1的那个函数,因为我们打算在BL1中拿它来拷贝BL2。
因为我使用的是从SDMMC启动,所以我们会使用到CopySDMMCtoMem这个函数,这个函数被定义在0xD0037F98这个位置,这个函数的原型是:
/**
* This Function copy MMC(MoviNAND/iNand) Card Data to memory.
* Always use EPLL source clock.
* This function works at 20Mhz.
* @param u32 StartBlkAddress : Source card(MoviNAND/iNand MMC)) Address.(It must block address.)
* @param u16 blockSize : Number of blocks to copy.
* @param u32* memoryPtr : Buffer to copy from.
* @param bool with_init : determined card initialization.
* @return bool(u8) - Success or failure.
*/
#define CopySDMMCtoMem(z,a,b,c,e)(((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned int *)0xD0037F98)))(z,a,b,c,e))
现在拷贝的函数接口我们知道了,还有一点我们要说明白的就是,那么我们的BL1和BL2在我们的SD卡上面应该怎么摆放呢,BL2的话还好,因为拷贝是我们自己的BL1拷贝的,可是BL1就不能乱放了,因为这是BL0拷贝的,BL0我们是不可控制它的。在SIAN中也有这部分的介绍,我们就只看SD的那部分。
从这里我们可以知道,我们SD的第一个Block必须被预留下来,不能使用,BL1存放在Block1中,那么这个N取值多少呢?友善给我们的值是49,所以BL2边放着编号为49的Block中。