基于S5PV210的uboot移植
1. 获取主线uboot源码
1.1 进入uboot wiki
通过链接uboot-wiki进入wiki页面,在其Obtaining the source中给出了获取uboot的方法,如下图所示。
通过git命令从官网服务器或github上拉取源码,如果git拉取速度较慢,则可以直接进入uboot链接地址,通过迅雷或者其他下载工具下载。
拉取完成后,通过git tag来查看各个版本情况,如下所示。
然后使用git checkout
当然,这里为了演示方便,在windows下操作git的,最好是直接在linux环境下操作。
1.2 尝试编译uboot源码
下载好交叉工具链后,进入到uboot的顶层目录,然后再进入configs目录中,通过ls或ll来查看当前所支持的板级配置,如下所示。
图中的s5p_goni_defconfig即为s5pv210的配置文件,该配置文件是有三星官方提供的goni评估板,通过修改该评估板来适配自己的s5pv210板子。
2.了解S5PV210启动过程
2.1 分析大致启动过程
S5PV210的启动过程说明在S5PV210_iROM_ApplicationNote_Preliminary.pdf文档中有详细说明,下面简要分析下大致启动过程。
启动过程大致如下(略过secure boot安全启动项):
- 芯片上电后,S5PV210运行BL0代码并根据OM来选择启动介质;
- 从启动介质中拷贝BL1(最大16KB)到片内SRAM中,同时对BL1进行校验;
- 如果校验成功,则并运行BL1代码,否则跳到2nd boot阶段;
- 验证成功后,在BL1中拷贝BL2代码到片内SRAM中,然后运行BL2代码,BL2代码应该做好DDR初始化相关操作,最后将OS加载至DDR中,并启动OS。
以上是三星官方推荐的启动步骤,主要分为3步,首先是运行BL0,负责拷贝BL1到SRAM中,然后运行BL1并负责拷贝BL2到SRAM中,最后运行BL2初始化DDR并将OS加载到DDR中。由于整个uboot镜像较大,无法直接存到片内SRAM中,需要将uboot分为两个部分,即SPL部分和uboot主体部分。uboot-spl镜像主要负责DDR初始化,uboot主体部分则负责kernel的引导和启动。
这里为了简化启动流程,笔者将按照如下方式来启动uboot并引导kernel:
- 芯片上电后,S5PV210运行BL0代码并根据OM来选择启动介质;
- 从启动介质中拷贝BL1(最大16KB)到片内SRAM中,同时对BL1进行校验;
- 如果校验成功,则并运行BL1代码,BL1中负责初始化串口,DDR,初始化栈指针SP;
- 将uboot拷贝只DDR中,并跳转到DDR中运行uboot,由uboot来引导启动kernel。
2.2 启动镜像内存布局
通过查看之前的手册,可以看到S5PV210中SD卡启动方式的各镜像的内存布局如下:
从上面可以看到BL1的镜像需要放到SD卡的第一扇区,且BL1中包16B的校验头,如果使用安全启动模式,则在末尾需要添加512B的签名。
3. 制作BL1启动镜像
在制作BL1启动镜像前首先需要了解下BL0镜像的主要工作,根据官方数据手册,BL0的主要工作如下:
- 关闭看门狗;
- 初始化I-Cache;
- 初始化stack栈指针SP;
- 初始化heap堆;
- 初始化设备块拷贝功能;
- 初始化PLL并设置sys-clk系统时钟(设置成400MHz);
- 拷贝BL1到片内SRAM;
- 检查BL1的校验和,如果失败则到2nd boot阶段;
- 进行安全启动,若没有启动该模式,则跳过该步;
- 跳到BL1并运行之。
从上面可知,BL1的主要目标是完成时钟初始化(将系统时钟设置为1GHz),串口初始化、DDR初始化和uboot镜像的拷贝。
这里为了简洁起见,直接使用天嵌QT210提供的裸机代码,如下所示:
这里只需要BL1的部分代码,并在main.c文件中修改数据拷贝位置和大小,如下所示:
因为uboot的镜像大小差不多400KB左右,这里直接从SD卡第20扇区开始拷贝500KB的数据到DDR。然后再修改start.S文件,指定在DDR中的栈指针SP,如下所示:
这里QT210的DDR为1GB,从0x2000_0000 ~ 0x6000_0000,可以将SP设置为最顶位置。
当然,如果板载的DDR为512MB,则需要将其设置为0x4000_0000,如果板载的DDR内存空间不连续(典型的如飞凌OK210)则可将其设置为任意一块的最高地址(OK210可设置为0x3000_0000或0x5000_0000)。
最后是修改Makefile文件,指定交叉工具链,如下所示:
笔者的交叉工具链为arm-linux-,故无需修改,若为其他的名称的交叉工具链,则修改指定的arm-linux-即可。最后在BL1目录中直接make即可生成相应的bl1.bin文件。
由于S5PV210在运行BL前需要进行校验,故需要在bl1.bin文件前添加校验和,为了间接起见,这里直接使用QT210提供校验和计算代码,当然也可参考三星提供的代码,QT210提供的代码如下所示:
/* mkimg.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define IMG_SIZE 16 * 1024
#define HEADER_SIZE 16
int main (int argc, char *argv[])
{
FILE *fp;
unsigned char *buffer;
int bufferLen;
int nbytes, fileLen;
unsigned int checksum, count;
int i;
if (argc != 3)
{
printf("Usage: %s <source file> <destination file>\n", argv[0]);
return -1;
}
/* 分配16KByte的buffer,BL1最大为16KByte,并初始化为0 */
buffer = calloc(1, IMG_SIZE);
if (!buffer)
{
perror("Alloc buffer failed!");
return -1;
}
/* 打开源bin文件 */
fp = fopen(argv[1], "rb");
if( fp == NULL)
{
perror("source file open error");
free(buffer);
return -1;
}
/* 获取源bin文件的长度 */
fseek(fp, 0L, SEEK_END);
fileLen = ftell(fp);
fseek(fp, 0L, SEEK_SET);
/* 源bin文件不得超过(16K-16)Byte */
if (fileLen > (IMG_SIZE - HEADER_SIZE))
{
fprintf(stderr, "Source file is too big(> 16KByte)\n");
free(buffer);
fclose(fp);
}
/* 计算校验和 */
i = 0;
checksum = 0;
while (fread(buffer + HEADER_SIZE + i, 1, 1, fp))
{
checksum += buffer[HEADER_SIZE + i++];
}
fclose(fp);
/* 计算BL1的大小(BL1的大小包括BL1的头信息),并保存到buffer[0~3]中 */
fileLen += HEADER_SIZE;
memcpy(buffer, &fileLen, 4);
// 将校验和保存在buffer[8~15]
memcpy(buffer + 8, &checksum, 4);
/* 打开目标文件 */
fp = fopen(argv[2], "wb");
if (fp == NULL)
{
perror("destination file open error");
free(buffer);
return -1;
}
// 将buffer拷贝到目标bin文件中
nbytes = fwrite(buffer, 1, fileLen, fp);
if (nbytes != fileLen)
{
perror("destination file write error");
free(buffer);
fclose(fp);
return -1;
}
free(buffer);
fclose(fp);
return 0;
}
在终端中输入gcc mkimg.c -o mkimg.exe,后生成mkimg.exe可执行文件,最后使用mkimg.exe bl1.bin bl1_img.bin后即可生成完整的bl1镜像文件。
为了方便查看BL1的运行情况,可以在bl1代码中添加一些串口打印信息。将制作好的bl1_img.bin文件烧录到SD卡中,然后选择从SD卡启动,如果BL1工作正常,则会打印出一些信息。这里可以使用dd命令来将bl1_img.bin烧录到SD卡的第1扇区(参考官方文档)。
4. 移植uboot
移植uboot相对较为简单,这里使用主线uboot中提供的s5p_goni_defconfig配置文件,同时修改dts和相应文件中的设备信息。
4.1 修改s5pc1xx-goni.dts文件
在arch/arm/dts中修改s5pc1xx-goni.dts文件,将其串口改为uart0,如下所示:
4.2 修改include/configs/s5p_goni.h文件
此文件定义了一系诶板级信息,这里需要按照自己的开发板进行修改,如下所示:
这里按照飞凌OK210的DDR来配置的,简要说明下,OK210的有两块DDR bank,分别使用DMC0和DMC1,其中一块的地址是0x2000_0000,大小为256MB,另一块的地址为0x4000_0000,大小为256MB,总共512MB内存。
当然,如果使用的是友善之臂的tiny210,则只需使用一个bank即可,因为tiny210只使用了DMC0,地址从0x2000_0000 ~ 0x4000_0000,即只需要PHYS_SDRAM_1,其他的可以直接删除。
4.3 修改board/samsung/goni/goni.c 文件
因为修改了s5p_goni.h文件,相应的源文件也需要修改,这里主要修改DDR的bank个数和大小,如下所示:
这里根据自己的板子进行配置,由这里OK210有块BANK,而友善之臂的tiny210有1块bank,则只需要使用PHYS_SDRAM_1_SIZE和gd->bd->bi_dram[0]即可。
4.4 defconfig & menuconfig
先在uboot顶层目录中make ARCH=arm s5p_goni_defconfig来载入默认配置信息。
然后在uboot顶层目录中make ARCH=arm menuconfig后,出现配置窗口,如下所示:
(1)修改General setup --->Number of DRAM banks为2;
(2)设置Boot images --->(0x20000000) Text Base为0x2000_0000,即为DRAM的起始地址。
(3)关闭Command line interface --->Device access commands --->[ ] onenand - access to onenand device选项,因为uboot中onenand的驱动暂时不完整。
最后编译make ARCH=arm CROSS_COMPILE=arm-linux- -j4,将生成的u-boot.bin文件使用
sudo dd if=u-boot.bin of=/dev/sdb bs=512 seek=20 iflag=dsync oflag=dsync烧录到SD卡的第20扇区。至此,基于S5PV210的uboot移植完毕。