基于S5PV210的uboot移植

1. 获取主线uboot源码


1.1 进入uboot wiki

通过链接uboot-wiki进入wiki页面,在其Obtaining the source中给出了获取uboot的方法,如下图所示。

通过git命令从官网服务器或github上拉取源码,如果git拉取速度较慢,则可以直接进入uboot链接地址,通过迅雷或者其他下载工具下载。
拉取完成后,通过git tag来查看各个版本情况,如下所示。

然后使用git checkout 来切换版本,例如这里使用git checkout v2019.01来切换到2019版本,如下所示。

当然,这里为了演示方便,在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安全启动项):

  1. 芯片上电后,S5PV210运行BL0代码并根据OM来选择启动介质;
  2. 从启动介质中拷贝BL1(最大16KB)到片内SRAM中,同时对BL1进行校验;
  3. 如果校验成功,则并运行BL1代码,否则跳到2nd boot阶段;
  4. 验证成功后,在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:

  1. 芯片上电后,S5PV210运行BL0代码并根据OM来选择启动介质;
  2. 从启动介质中拷贝BL1(最大16KB)到片内SRAM中,同时对BL1进行校验;
  3. 如果校验成功,则并运行BL1代码,BL1中负责初始化串口,DDR,初始化栈指针SP;
  4. 将uboot拷贝只DDR中,并跳转到DDR中运行uboot,由uboot来引导启动kernel。

2.2 启动镜像内存布局

通过查看之前的手册,可以看到S5PV210中SD卡启动方式的各镜像的内存布局如下:

从上面可以看到BL1的镜像需要放到SD卡的第一扇区,且BL1中包16B的校验头,如果使用安全启动模式,则在末尾需要添加512B的签名。

3. 制作BL1启动镜像


 在制作BL1启动镜像前首先需要了解下BL0镜像的主要工作,根据官方数据手册,BL0的主要工作如下:

  1. 关闭看门狗;
  2. 初始化I-Cache;
  3. 初始化stack栈指针SP;
  4. 初始化heap堆;
  5. 初始化设备块拷贝功能;
  6. 初始化PLL并设置sys-clk系统时钟(设置成400MHz);
  7. 拷贝BL1到片内SRAM;
  8. 检查BL1的校验和,如果失败则到2nd boot阶段;
  9. 进行安全启动,若没有启动该模式,则跳过该步;
  10. 跳到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移植完毕。

posted @ 2023-02-12 00:11  凌空破天  阅读(391)  评论(0编辑  收藏  举报