嵌入式uboot移植之从uboot官方移植

本文详细介绍了如何从uboot官方源码进行移植,选择合适的版本,理解uboot配置体系的变化,并确定移植的board模板。文章通过分析文件夹结构和主要文件,删除无关内容,配置开发板,修改主Makefile,以及对Uboot启动第一、第二阶段的实现进行深入探讨。在移植过程中,重点讲述了DDR初始化、重定位和网卡移植的步骤,最后总结了移植过程中的关键知识点和实践经验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:以下内容来自朱老师物联网大讲堂课件

1. 选择合适的官方原版uboot

1.1 官方原版uboot的版本

(1)版本号。刚开始是1.3.4,后来变成2009.08
(2)新版和旧版的差别。uboot的架构很早就定下来了,然后里面普遍公用的东西(common目录下、drivers目录下、fs目录下等···)在各个版本之间几乎是完全一样的。差别最大的是board和cpu目录,这两个目录正是单板(开发板)相关的。越新的uboot版本支持越多的开发板(CPU),所以越新的uboot越庞大。
(3)并不是越新的版本就越好。越新的uboot中会多出更多的开发板的支持代码,如果我们的开发板并不是很新,就没必要去用很新版本的uboot。因为多出来的代码自己也用不到而且还会成为累赘。
为了方便学习我们这里也使用2013.10的版本

1.2 官方原版uboot的来源

uboot可以有3种获取途径:uboot官方、SoC官方如(三星)、具体开发板的官方(如:九鼎)。
我们选择从官方下载(ftp://ftp.denx.de/pub/u-boot/)

1.3 新版uboot配置体系的改变

(1)在最新的uboot版本(准确的说是2013.10到2014.10中的某个版本)中,uboot的文件体系发生了一个很大的变化。这个变化就是uboot引入了linux kernel的配置体系(Kbuild、Kconfig、menuconfig),从而让我们可以在图形界面下,像配置内核一样配置uboot。
(2)所以新版本的uboot配置时和我们之前的课程讲的就不同了。我们移植时不能选择这种配置方式更改之后的uboot版本。我们要选择更改之前的。
(3)新版本的配置方式本质上和linux kernel一样的,所以在学完linux kernel移植后自己就能看懂,因此不用担心。

使用2013.10对比最新的2020.10 文件结构区别如下(左边是2013.10文件,右边是2020.10文件)
在这里插入图片描述
使用2013.10对比最新的2020.10 文件夹结构区别如下(左边是2013.10文件夹,右边是2020.10文件夹)
在这里插入图片描述
可以发现有80%的文件夹结构是相同的,但外面的文件的差异就比较大了,这里我们先不深入探究。

因为我们的S5PV210是比较早的产品,我们选择2013.10版本进行实验移植。

注意:实践工作中一般是从SoC厂商的uboot出发移植的
在工作中一般是不需要从uboot官方版本出发去做移植的,而是从SoC厂商提供的开发板配套的uboot去做移植的。

文档后续我们提到的都是2013.10版的uboot

2. 确定我们要移植的board模板

2.1 文件夹结构分析、主要文件检视

总的来说,文件夹结构和以前基本一样。不同的主要是lib,以前是lib_arm和lib_generic,现在是arch和lib。arch目录下放的是和cpu架构有关的东西。
总的来说,2013.10版本的uboot在结构上和1.3.4版本的uboot还是有所不同的。

2.2 参照物开发板的选择

我们开发板使用的CPU是S5PV210,所以要找uboot中针对S5PV210或者S5PC110进行移植的作为参考。
我们先在board里面找,但是并没有明显的发现,所以我们有一个想法就是将全部文件导入到SI,然后查找关键字S5PV210、S5PC110,主要看configs目录下和board目录下
关键字S5PC110搜索结果如下:
S5p_goni.h (u-boot-2013.10\include\configs)
lowlevel_init.S (u-boot-2013.10\board\samsung\goni)
关键字S5PV210没有搜索结果

根据规律,我们应该参考include/configs/s5p_goni.h,对应的board在uboot/board/samsung/goni这个目录。
结论:
参照物开发板为:s5p_goni
配置对应的cpu、board文件夹分别为:
cpu: u-boot-2013.10\arch\arm\cpu\armv7
board: u-boot-2013.10\board\samsung\goni

2.3 删除无关文件和文件夹

其实不删除也可以,但是删除更好。
我们已经在board中选择了goni,所以其他的文件都可以删除了。
通过S5PV210的数据手册可知我们的架构是ARMv7,所以arch->arm->cpu中除过ARMv7其他的架构都可以删除了,arm层除过arm也都可以删除了。
在这里插入图片描述
注意: 对于不确定能不能删的都先不要删除。

3. 开发板的配置

3.1 建立SI工程并预解析

这个工程我们已经在上一步建立了,所以这一步就只需要Project->sync file 即可。
验证方式就是我们搜索start.s我们发现只能看到一个start文件在armv7中。

对照之前三星自带的uboot,我们发现:
(1)2013.10版本的uboot的Makefile中使用了boards.cfg文件,因此在配置uboot时make xxx_config,这个xxx要到boards.cfg文件中查找。
(2)其实就相当于把以前的版本的uboot中各种开发板的配置部分规则抽离出来写到了Makefile中,然后把配置信息部分写到了一个独立文件boards.cfg。

以前的版本(三星的为例):

smdkv210single_config :	unconfig
	@$(MKCONFIG) $(@:_config=) arm s5pc11x smdkc110 samsung s5pc110
	@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/smdkc110/config.mk

MKCONFIG	:= $(SRCTREE)/mkconfig

现在的版本(2013.10)

%_config::	unconfig
	@$(MKCONFIG) -A $(@:_config=)

MKCONFIG	:= $(SRCTREE)/mkconfig

总结:以前是直接在Makefile中进行传参的,这样导致程序会越来越大,所以后面的版本选择了将配置信息写到了一个独立文件boards.cfg,我们实际使用时直接根据去匹配调取。
在这里插入图片描述

3.2 make s5p_goni_config

问题: 那么现在的配置信息是如何工作的呢?
我们在主Makefile中发现了如下的程序


MKCONFIG	:= $(SRCTREE)/mkconfig
export MKCONFIG

%_config::	unconfig
	@$(MKCONFIG) -A $(@:_config=) //:_config=表示使用空格替换_config

举例说明:
第一步、当我们make s5p_goni_config 时,会执行如下的指令,也就是给mkconfig传参

(SRCTREE)/mkconfig -A s5p_goni 

在这里插入图片描述

第二步、到了mkconfig脚本中了。在24到35行中使用awk正则表达式将boards.cfg中与刚才$1(s5p_goni)能够匹配上的那一行截取出来赋值给变量line,然后将line的内容以空格为间隔依次分开,分别赋值给$1、$2···$8。
注意在解析完boards.cfg之后,$1到$8就有了新的值。
$1 = Active
$2 = arm
$3 = armv7
$4 = s5pc1xx
$5 = samsung
$6 = goni
$7 = s5p_goni
$8 = -
第三步、对几个重要的变量进行传参
arch="$2"=arm
cpu=armv7
soc="$4"=s5pc1xx
vendor="$5"=samsung
board="$6"=goni
这样就将配置信息传给了mkconfig,方可进行后续的操作。

4.主Makefile的修改

4.1 符号链接

这部分和我们之前的是一模一样的------>嵌入式之uboot的配置和编译过程学习笔记
(1)include/asm -> arch/arm/include/asm
(2)include/asm/arch -> include/asm/arch-s5pc1xx
(3)include/asm/proc -> include/asm/proc-armv
最后创建了include/config.h文件。

4.2 Makefile中添加交叉编译工具链

官方原版的uboot中CROSS_COMPLIE是没有定义的,需要自己去定义。如果没定义就直接去编译,就会用gcc编译。
我们将我们之前定义的地址复制到主Makefile的对应位置就好了,注意这里的位置必须和你实际的是对应的。
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
官方的uboot解压后没有烧录程序,所以我们将我们之前使用的sd_fusing复制到当前的目录
这个时候就可以编译生成烧录文件了。

5.Uboot2013.10的架构大体分析

5.1 对start.S进行框架分析

1.头文件包含
2.代码开始,先reset进入SVC管理模式
3.构建异常向量表
4.读取链接地址
5.关中断(IRQ、FIQ)
6.进入cpu_init_cp15(刷新L1 cache的icache和dcache,关MMU)
7.进入cpu_init_crit(执行lowlevel_init函数关看门狗、设置SRAM、设置中断、uart_asm_init、internal_ram_init)
8.跳转到 _main(board_init_f、relocate_code、board_init_r)

说明
(1)cpu_init_crit函数成功初始化串口后,转入_main函数,函数在arch/arm/lib/crt0.S文件中。
(2)在crt0.S中首先设置栈,将sp指向DDR中的栈地址;然后调用board_init_f函数进行板级初始化。函数在arch/arm/lib/board.c中。
(3)以前三星提供的uboot板级初始化是在start_armboot中,我们目前移植的这个版本uboot中,把以前uboot的第二阶段start_armboot函数分成了2部分:board_init_r + board_init_f
(4)board_init_f中肯定是做了板级初始化,board_init_r中进入了uboot的命令行。

5.2 uboot2013.10版本中移植思路

在uboot2013.10版本中思路:uboot的第二阶段就在crt0.S文件中,第二阶段的入口就是_main函数。第一阶段工作主要就是cpu_init_crit函数,所以我们要在cpu_init_crit函数中添加DDR初始化和uboot的重定位。
那我们移植uboot启动第一阶段的流程为:

  1. 先在cpu_init_crit函数中添加DDR初始化
  2. 在start.S中bl _main之前添加uboot的重定位
  3. 长跳转到DDR开始第二阶段,将bl _main改成ldr pc, __main(__main: .word _main)长跳转

6. Uboot启动第一阶段的实现

6.1 程序进入lowlevel_init并打印出debug字符“OK”

6.1.1 解决SD卡检验和不能通过

实验现象:出现了两次SD checksum Error,uboot不能启动
现象分析:出现两次就说明我们的SD卡的校验失败(正常应该是一次,也就是1st启动被破坏)
解决思路:要不就是uboot.bin原材料的问题,要不就是sd_fusing 这个程序的问题
解决办法:先对比uboot.bin的头信息,我们发现正常的uboot比出错的uboot多出了16个字节的头,对照我们的sd_fusing中的镜像制作程序,我们知道SD卡的镜像制作是跳过16个字节开始计算checksum的,但是出错的uboot显然在最开始并没有预留16字节的头,所以我们添加上即可。

	.word 0x2000
	.word 0x0
	.word 0x0
	.word 0x0

在这里插入图片描述

6.1.2 串口无输出
发现问题

实验现象:按下按键出现了一次SD checksum Error ,后续无任何输出
现象分析:说明我们的SD卡第二次启动正常了,就是串口现在无输出
解决思路:设置断点,确定故障区域,因为现在串口不工作,我们无法通过串口观察,所以我们可以设置一些硬件来观察,如led的点亮。
为了方便观察我们先设置开发板置锁。
如下为我们添加的位置

//add power lock
	ldr r0, =0xE010E81C
	ldr r1, =0x301
	str r1, [r0]


#define GPJ0CON	0xE0200240
#define GPJ0DAT	0xE0200244


// 第一步:把所有引脚都设置为输出模式,代码不变
	ldr r0, =0x11111111 		// 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数
	ldr r1, =GPJ0CON			// 是合法立即数还是非法立即数。一般写代码都用ldr伪指令
	str r0, [r1]				// 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去

//熄灭第一个led
	ldr r0, =((1<<3) | (0<<4) | (0<<5))	// 清清楚楚的看到哪个灭,哪个是亮
	ldr r1, =GPJ0DAT
	str r0, [r1]				// 把1写入到GPJ0DAT寄存器中,引脚即输出高电平,LED熄灭

	
/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
	
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
	/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
	mrc	p15, 0, r0, c1, c0, 0	@ Read CP15 SCTRL Register
	bic	r0, #CR_V		@ V = 0
	mcr	p15, 0, r0, c1, c0, 0	@ Write CP15 SCTRL Register

	/* Set vector address in CP15 VBAR register */
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR
#endif


	// 第2步:熄灭第二个led
		ldr r0, =((1<<3) | (1<<4) | (0<<5))	// 清清楚楚的看到哪个灭,哪个是亮
		ldr r1, =GPJ0DAT
		str r0, [r1]				// 把1写入到GPJ0DAT寄存器中,引脚即输出高电平,LED熄灭

	/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	bl	cpu_init_cp15
	bl	cpu_init_crit
#endif

	// 第3步:熄灭第三个led
		ldr r0, =((1<<3) | (1<<4) | (1<<5)) // 清清楚楚的看到哪个灭,哪个是亮
		ldr r1, =GPJ0DAT
		str r0, [r1]				// 把1写入到GPJ0DAT寄存器中,引脚即输出高电平,LED熄灭

	bl	_main

编译运行
实验现象:开发板可以置锁,第一个和第二个LED熄灭了,第三个并没有熄灭
现象分析:说明问题出现在LED2之后LED3之前,也就是如下的代码:

	/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	bl	cpu_init_cp15
	bl	cpu_init_crit
#endif

逐步继续缩小范围,最后发现在cpu_init_crit这个函数里,b lowlevel_init不能跳转成功

解决问题

现象分析

  • 三星S5PV210要求BL1大小为8KB,因此uboot第一阶段代码必须在整个uboot镜像的前8KB内,否则跳转不到。
  • 对比三星移植版本的uboot的u-boot.lds和官方版本uboot的连接脚本u-boot.lds(注意这两个版本的uboot的连接脚本的位置是不同的),就发现lowlevel_init.S的代码段没有被放在前面。

解决方法:在u-boot.lds中start.o后面添加board/samsung/goni/lowlevel_init.o (.text*),这个就保证了lowlevel_init函数被连接到前面8kb中去。也就是人为控制其优先处理。

编译报错,lowlevel_init重复定义了。
在这里插入图片描述
为什么会重复定义?
因为lowlevel_init这个函数被连接时连接了2次。一次是board/samsung/goni这个目录下生成时连接了1次,第2次是连接脚本最终在连接生成u-boot时又连接了一次,所以重复定义了。
解决思路,让board/samsung/goni中的只编译不连接,最后在链接脚本中链接。
如何解决:在第一次链接的Makefile中,我们使用LOWL变量替换原来的SOBJS,然后在最后链接时不要添加LOWL就好了。

include $(TOPDIR)/config.mk

LIB	
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值