ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植

一、选择合适的官方原版 uboot

1、官方原版 uboot 的版本

(1) 版本号。刚开始是 1.3.4 样式,后来变成 2009.08 样式。

(2) 新版和旧版的差别。uboot 的架构很早就定下来了,然后里面普遍公用的东西(common 目录下、drivers 目录下、fs 目录下等···)在各个版本之间几乎是完全一样的。

差别最大的是 board 和 cpu 目录,这两个目录正是单板(开发板)相关的。越新的 uboot 版本支持越多的开发板(CPU),所以越新的 uboot 越庞大。

(3) 并不是越新的版本就越好。越新的 uboot 中,会多出更多的开发板的支持代码,如果我们的开发板并不是很新,就没必要去用很新版本的 uboot。因为多出来的代码自己也用不到,而且还会成为累赘。


2、官方原版 uboot 的来源

(1) 从 uboot 官方网站 ftp 下载。

(2) 从一些镜像网站下载。


3、新版 uboot 配置体系的改变

(1) 在最新的 uboot 版本(准确的说是 2013.10 到 2014.10 中的某个版本)中,uboot 的文件体系发生了一个很大的变化。这个变化就是,uboot 引入了 linux kernel 的配置体系(Kbuild、Kconfig、menuconfig),从而让我们可以在图形界面下,像配置内核一样配置 uboot。

(2) 所以新版本的 uboot 配置时,和我们之前的课程讲的就不同了。我们移植时不能选择这种配置方式更改之后的 uboot 版本。我们要选择更改之前的。

(3) 新版本的配置方式,本质上和 linux kernel 一样的,所以在学完 linux kernel 移植后自己就能看懂,因此不用担心。


4、结论:选择合适的官方原版 uboot 进行移植

(1) 结合以上,选择 2013.10 版本进行实验移植是比较合理的。


5、注意:实践工作中,一般是从SoC厂商的 uboot 出发移植的

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


二、先初步浏览官方原版 uboot

1、文件夹结构浏览

(1) 文件夹结构分析、主要文件检视

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

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第1张图片

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第2张图片

总的来说,2013.10 版本的 uboot 在结构上和 1.3.4 版本的 uboot 还是有所不同的。


(2) 参照物开发板的选择

我们开发板使用的 CPU 是 S5PV210,所以要找 uboot 中针对 S5PV210 或者 S5PC110 进行移植的作为参考。

根据规律,我们应该参考 include/configs/s5p_goni.h,对应的 board 在 uboot/board/samsung/goni 这个目录。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第3张图片


ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第4张图片


(3) 删除无关文件和文件夹
其实不删除也可以,但是删除更好。

arch 目录:

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第5张图片


ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第6张图片


ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第7张图片


ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第8张图片


ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第9张图片


ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第10张图片


board 目录:

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第11张图片


ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第12张图片


2、建立工程

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第13张图片


3、主 Makefile 浏览及 boards.cfg 文件

(1) 2013.10 版本的 uboot 的 Makefile 中使用了 boards.cfg 文件,因此在配置 uboot 时 make xxx_config,这个 xxx 要到 boards.cfg 文件中查找。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第14张图片
ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第15张图片


(2) 其实就相当于把以前的版本的 uboot 中,各种开发板的配置部分规则抽离出来,写到了 Makefile 中,然后把配置信息部分写到了一个独立文件 boards.cfg。


4、mkconfig 脚本浏览及符号连接的分析

(1) 下节课详细分析,给出结论。


5、结论:

(1) 参照物开发板为:55p_goni

(2) 配置对应的 cpu、board 文件夹分别为:
cpu: u-boot-2013.10\arch\arm\cpu\armv7
board: u-boot-2013.10\board\samsung\goni


三、mkconfig 脚本分析

1、脚本功能浏览

(1) 首先我们在命令行配置 uboot 时,是:make s5p_goni_config,对应 Makefile 中的一个目标。

(2) 新版本的 Makefile 中:
ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第16张图片

从这里分析得出结论,实际配置时,是调用 mkconfig 脚本,然后传参 2 个:-A 和 s5p_goni。

(3) 到了 mkconfig 脚本中了。在 24 到 35 行中,使用 awk 正则表达式将 boards.cfg 中与刚才 $1(s5p_goni)能够匹配上的那一行截取出来,赋值给变量 line,然后将 line 的内容以空格为间隔依次分开,分别赋值给 $1、$2···$8。
ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第17张图片


根据上面的 awk 脚本和 board.cfg,其实是将传进来的 $2 参数:s5p_goni,和 board.cfg 的第 7 列:s5p_goni,做匹配。

所以我们要配置,就是用命令:make s5p_goni_config,才能配置成功。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第18张图片


(4) 注意在解析完 boards.cfg 之后,$1 到 $8 就有了新的值。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第19张图片

Active  arm         armv7          s5pc1xx     samsung         goni                s5p_goni                             -                                                                                                                                 Minkyu Kang <[email protected]>

$1 = Active
$2 = arm
$3 = armv7
$4 = s5pc1xx
$5 = samsung
$6 = goni
$7 = s5p_goni
$8 = -


2、几个传参和其含义

(1) 处理了 board.cfg 文件后,mkconfig 脚本中将参数赋值给变量。

几个很重要的变量:

arch=arm
cpu=armv7
vendor=samsung
soc=s5pc1xx

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第20张图片


3、创建符号链接

(1) include/asm -> arch/arm/include/asm
(2) include/asm/arch -> include/asm/arch-s5pc1xx
(3) include/asm/proc -> include/asm/proc-armv
ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第21张图片


最后创建了 include/config.h 文件。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第22张图片


4、Makefile 中添加交叉编译工具链

(1) 官方原版的 uboot 中,CROSS_COMPLIE 是没有定义的,需要自己去定义。如果没定义就直接去编译,就会用 gcc 编译。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第23张图片


(2) 添加一行:

# 添加我们本地的交叉编译环境
ifeq ($(ARCH),arm)
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
endif

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第24张图片

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第25张图片


5、配置编译测试

(1) 编译过程:

make distclean
make s5p_goni_config
make

(2) 结果:得到 u-boot.bin 即可。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第26张图片


四、先解决官方版本uboot的烧录运行

1、如何烧录 uboot

(1) 烧录 u-boot.bin 到 SD 卡中有 2 种方法:

windows 下用烧录软件;

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第27张图片


linux 下用 dd 命令烧录脚本来烧录;

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第28张图片


因为 windows 下的工具不开源,出了问题没法调试,所以不推荐。推荐 linux 下用烧录脚本来烧录(实质是 dd 命令进行 sd 卡扇区写入)


(2) 移植原来的版本(u-boot-samsung-dev)的 uboot 中的 sd_fusing 文件夹,到官方 uboot 版本中,使用这个文件夹中的 sd_fusing.sh 脚本来进行烧录。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第29张图片


2、分析:为什么烧录运行不正确?

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第30张图片

(1) 串口接串口 2,串口有输出。但是这个串口输出不是 uboot 输出的,而是内部 iROM 中的 BL0 运行时输出的。

(2) 输出错误信息分析:
第一个 SD checksum Error:是第一顺序启动设备(SD0(iNand))启动时,校验和 checksum 失败打印出来的;

第二个 SD checksum Error:是第二顺序启动设备(SD2(外部SD卡))启动时,校验和 checksum 失败打印出来的;

剩下的是串口启动和 usb 启动的东西,可以不管。

总结:从两个 SD checksum Error,可以看出:外部 SD 卡校验和失败了。
分析:SD 卡烧录出错了,导致 SD 卡校验和 checksum 过程失败。


3、解决方案分析

(1) 为什么 SD 卡烧录会出错?可能原因:烧录方法错误、烧录原材料错误。

(2) 经过分析,sd_fusing 这个文件夹下的 mkbl1 这个程序肯定没错,uboot 顶层目录下的 u-boot.bin 是存在的,校验和 checksum 失败不失败,和 u-boot.bin 无关。

(3) 经过分析和查找,发现是 mkbl1 程序和 start.S 中,前 16 个字节校验和的处理上面不匹配造成的,解决方法是在 start.S 最前面加上 16 个字节的占位。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第31张图片

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第32张图片


修改

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第33张图片


4、代码实践

(1) 重新编译烧录运行,发现结果只显示一个 SD checksum Error。这一个就是内部 SD0 通道的 inand 启动校验和 checksum 失败打印出来的。

剩下的没有了,说明外部 SD 卡校验和 checksum 成功了,只是 SD 卡上的 uboot 是错误的,没有串口输出内容,所以没有输出了。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第34张图片


五、start.S 文件分析与移植1

1、start.S 流程分析

(1)

 #define CONFIG_SYS_TEXT_BASE		0x34800000  

可以看出,我们的 uboot 的连接地址是在 0x34800000 位置。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第35张图片


(2) save_boot_params 是个空函数,里面直接返回的。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第36张图片


(3) cpu_init_cp15 这个函数功能是,设置 MMU、cache 等。这个版本的 uboot 中未使用虚拟地址,因此 MMU 在这里直接关掉。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第37张图片


(4) cpu_init_crit,这个函数里只有一句跳转指令,短跳转到 lowlevel_init 函数。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第38张图片

注意:uboot 中有 2 个 lowlevel_init.S 文件(文件中还都有 lowlevel_init 函数),凭一般分析,无法断定 2 个中哪个才是我们想要的。通过分析两个文件所在文件夹下面的 Makefile 可以判定,board/samsung/goni 目录下的才是真正包含进来的,arch/arm/cpu/armv7 目录下的并没有被包含进来。

还可以通过实践验证的方法来辅助判断。通过查看之前已经编译过的 uboot 源码目录,看哪个被编程为 .o文件了,就知道哪个是真正被使用的了。

board/samsung/goni/lowlevel_init.S
ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第39张图片


(5) lowlevel_init 函数在 board/samsung/goni 目录下,主要作用是时钟设置、串口设置、复位状态判断···这个函数是 S5PC100 和 S5PC110 两个 CPU 共用的。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第40张图片


(6) 经过浏览,发现 lowlevel_init 函数中做的有意义的事情有:关看门狗、调用uart_asm_init 来初始化串口、并没有做时钟初始化(下面有时钟初始化的函数,但是实际没调用。如果 uboot 中没有初始化时钟,那么时钟就是 iROM 中初始化的那种配置)。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第41张图片


2、添加开发板置锁和串口打印字符 “O”

(1) 我们为了调试 uboot 的第一阶段,就要看到现象。为了看到现象,我们向lowlevel_init 函数中添加 2 个代码,一个是开发板置锁,一个是串口打印 “O”。

(2) 这两段代码可以直接从 ARM 裸机全集课程中的代码中来。其实也可以从三星移植版本的 uboot 中来,但是因为三星移植版本中用到了很多寄存器定义,涉及到头文件的,所以移植过来不方便。

(3) 实践添加。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第42张图片

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第43张图片


3、实践结果及分析

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第44张图片

(1) 实验结果是:没看到开发板制锁,串口也没有输出任何东西。实验失败。

(2) 结论:因为开发板置锁没有成功,所以我们判定,在开发板置锁代码运行之前,uboot 就已经挂掉了。下面就是去跟踪代码运行,然后判定问题点再去解决。


六、start.S 文件分析与移植2

1、添加 LED 点亮代码跟踪程序运行

(1) 在基础代码阶段,串口还没有运行,串口调试工具还无法使用时,使用 LED 点亮的方式来调试程序就是一个有力的手段。

(2) 有些情况下可以用 Jlink 等调试工具来调试这种基础代码。

(3) 从程序的基本运行路径端出发,隔一段给他添加一个 LED 点亮代码,然后运行时根据现象来观察,判定哪里执行了哪里没执行。从而去定位问题。

(4)从以前的裸机代码中组织出一个标准的 LED 点亮然后延时一段的一个标准代码段:

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

        //第二步:全部点亮
        ldr r0, =((0 << 3) | (0 << 4) | (0 << 5))
        ldr r1, =0xE0200244
        str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮

        //延时函数
delay:
        ldr r2, =10000000
        ldr r3, =0x0
delay_loop:
        sub r2, r2, #1          // r2 = r2 -1
        cmp r2, r3              // cmp 会影响 z 标志位,如果 r2 等于 r3 则 z =1,下一句中 eq 就会成立
        bne delay_loop

(5) 之前做实验时发现一个现象:我们的 uboot 运行时,按住电源开关时所有 4 颗 LED 都是亮的。所以我们做实验时,给 LED 点亮是看不到现象的,所以我们的代码关键是要熄灭某些 LED 来判断。

(6) 我们将熄灭 LED 的函数在 start.S 中隔一段的关键部位放上 1 个,然后运行时通过观察 LED 的点亮熄灭状态,就知道程序运行到哪里了。

/*************** startup.S **********************/
reset:
	bl	save_boot_params
	/*
	 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
	 * except if in HYP mode already
	 */
	mrs	r0, cpsr
	and	r1, r0, #0x1f		@ mask mode bits
	teq	r1, #0x1a		@ test for HYP mode
	bicne	r0, r0, #0x1f		@ clear all mode bits
	orrne	r0, r0, #0x13		@ set SVC mode
	orr	r0, r0, #0xc0		@ disable FIQ and IRQ
	msr	cpsr,r0

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

        //第二步:
        ldr r0, =((1 << 3) | (0 << 4) | (0 << 5)) //熄灭左边的 LED
        ldr r1, =0xE0200244
        str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮

        //延时函数
delay1:
        ldr r2, =10000000
        ldr r3, =0x0
delay_loop1:
        sub r2, r2, #1          // r2 = r2 -1
        cmp r2, r3              // cmp 会影响 z 标志位,如果 r2 等于 r3 则 z =1,下一句中 eq 就会成立
        bne delay_loop1

/*
 * 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

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

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

        //第二步:
        ldr r0, =((0 << 3) | (1 << 4) | (0 << 5)) //熄灭中间的 LED
        ldr r1, =0xE0200244
        str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮

        //延时函数
delay2:
        ldr r2, =10000000
        ldr r3, =0x0
delay_loop2:
        sub r2, r2, #1          // r2 = r2 -1
        cmp r2, r3              // cmp 会影响 z 标志位,如果 r2 等于 r3 则 z =1,下一句中 eq 就会成立
        bne delay_loop2


	bl	cpu_init_crit

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

        //第二步:
        ldr r0, =((0 << 3) | (0 << 4) | (1 << 5)) //熄灭右边的 LED
        ldr r1, =0xE0200244
        str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮

        //延时函数
delay3:
        ldr r2, =10000000
        ldr r3, =0x0
delay_loop3:
        sub r2, r2, #1          // r2 = r2 -1
        cmp r2, r3              // cmp 会影响 z 标志位,如果 r2 等于 r3 则 z =1,下一句中 eq 就会成立
        bne delay_loop3
#endif

	bl	_main

(7) 经过判断我们发现:start.S 中工作一切正常,但是函数一旦放到 lowlevel_init.S 中就完全不工作了。通过分析得出结论:b lowlevel_init 这句代码出了问题。


2、修改 u-boot.lds 将 lowlevel_init.S 放到前部

(1) 问题分析:跳转代码出了问题。分析问题出在代码的链接上。

(2) 三星 S5PV210 要求 BL1 大小为 8KB,因此 uboot 第一阶段代码,必须在整个 uboot 镜像的前 8KB 内,否则跳转不到。

(3) 对比三星移植版本的 uboot 的 u-boot.lds 和官方版本 uboot 的链接脚本 u-boot.lds(注意这两个版本的uboot的链接脚本的位置是不同的),就发现 lowlevel_init.S 的代码段没有被放在前面。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第45张图片


(4) 在 u-boot.lds 中 start.o 后面添加 board/samsung/goni/lowlevel_init.o (.text*),这个就保证了 lowlevel_init 函数被连接到前面 8kb 中去。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第46张图片


(5) 报错,lowlevel_init 重复定义了。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第47张图片


3、修改 board/samsung/goni/Makefile 解决编译问题

(1) 问题分析:为什么会重复定义。因为 lowlevel_init 这个函数被链接时连接了 2 次。

一次是 board/samsung/goni 这个目录下生成 libgoni.o 时链接了 1 次,第 2 次是链接脚本最终在连接生成 u-boot 时又链接了一次,所以重复定义了。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第48张图片


(2) 这个错误如何解决?思路是在 libgoni.o 中,不要让他链接进 lowlevel_init,让他只在最终链接 u-boo t时用 1 次,就可以避免重复定义。

(3) 参考当前版本的 uboot 的 start.S 文件的处理技巧,解决了这个问题。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第49张图片

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第50张图片


4、实践验证。

结果是开发板置锁和串口输出 ‘O’ 都成功了。

ARM uboot 的移植3 -从 uboot 官方标准uboot开始移植_第51张图片


源自朱有鹏老师.

你可能感兴趣的:(ARM,S5PV210,朱有鹏,arm开发,linux,ARM,s5pv210)