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

news/2024/12/28 17:02:19/

一、选择合适的官方原版 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 架构有关的东西。

在这里插入图片描述

在这里插入图片描述

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


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

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

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

在这里插入图片描述


在这里插入图片描述


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

arch 目录:

在这里插入图片描述


在这里插入图片描述



在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


board 目录:

在这里插入图片描述


在这里插入图片描述


2、建立工程

在这里插入图片描述


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

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

在这里插入图片描述
在这里插入图片描述


(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 中:
在这里插入图片描述

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

(3) 到了 mkconfig 脚本中了。在 24 到 35 行中,使用 awk 正则表达式将 boards.cfg 中与刚才 $1(s5p_goni)能够匹配上的那一行截取出来,赋值给变量 line,然后将 line 的内容以空格为间隔依次分开,分别赋值给 $1、$2···$8。
在这里插入图片描述


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

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

在这里插入图片描述


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

在这里插入图片描述

Active  arm         armv7          s5pc1xx     samsung         goni                s5p_goni                             -                                                                                                                                 Minkyu Kang <mk7.kang@samsung.com>

$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

在这里插入图片描述


3、创建符号链接

(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、Makefile 中添加交叉编译工具链

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

在这里插入图片描述


(2) 添加一行:

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

在这里插入图片描述

在这里插入图片描述


5、配置编译测试

(1) 编译过程:

make distclean
make s5p_goni_config
make

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

在这里插入图片描述


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

1、如何烧录 uboot

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

windows 下用烧录软件;

在这里插入图片描述


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

在这里插入图片描述


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


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

在这里插入图片描述


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

在这里插入图片描述

(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 个字节的占位。

在这里插入图片描述

在这里插入图片描述


修改

在这里插入图片描述


4、代码实践

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

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

在这里插入图片描述


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

1、start.S 流程分析

(1)

 #define CONFIG_SYS_TEXT_BASE		0x34800000  

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

在这里插入图片描述


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

在这里插入图片描述


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

在这里插入图片描述


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

在这里插入图片描述

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

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

board/samsung/goni/lowlevel_init.S
在这里插入图片描述


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

在这里插入图片描述


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

在这里插入图片描述


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

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

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

(3) 实践添加。

在这里插入图片描述

在这里插入图片描述


3、实践结果及分析

在这里插入图片描述

(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, =0xE0200244str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮//延时函数
delay:ldr r2, =10000000ldr r3, =0x0
delay_loop:sub r2, r2, #1          // r2 = r2 -1cmp 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, cpsrand	r1, r0, #0x1f		@ mask mode bitsteq	r1, #0x1a		@ test for HYP modebicne	r0, r0, #0x1f		@ clear all mode bitsorrne	r0, r0, #0x13		@ set SVC modeorr	r0, r0, #0xc0		@ disable FIQ and IRQmsr	cpsr,r0//第一步: 把所有引脚设置成输出模式ldr r0, =0x11111111     //从后面的 = 可以看出用的是 ldr 伪指令,因为需要编译器来判断这个数ldr r1, =0xE0200240     //是合法立即数还是非法立即数,一般写代码都用 ldr 伪指令str r0, [r1]            // 寄存器间接寻址。功能是把 r0 中的数写入到 r1 中的数为地址的内存中去//第二步:ldr r0, =((1 << 3) | (0 << 4) | (0 << 5)) //熄灭左边的 LEDldr r1, =0xE0200244str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮//延时函数
delay1:ldr r2, =10000000ldr r3, =0x0
delay_loop1:sub r2, r2, #1          // r2 = r2 -1cmp 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 Registerbic	r0, #CR_V		@ V = 0mcr	p15, 0, r0, c1, c0, 0	@ Write CP15 SCTRL Register/* Set vector address in CP15 VBAR register */ldr	r0, =_startmcr	p15, 0, r0, c12, c0, 0	@Set VBAR
#endif/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INITbl	cpu_init_cp15//第一步: 把所有引脚设置成输出模式ldr r0, =0x11111111     //从后面的 = 可以看出用的是 ldr 伪指令,因为需要编译器来判断这个数ldr r1, =0xE0200240     //是合法立即数还是非法立即数,一般写代码都用 ldr 伪指令str r0, [r1]            // 寄存器间接寻址。功能是把 r0 中的数写入到 r1 中的数为地址的内存中去//第二步:ldr r0, =((0 << 3) | (1 << 4) | (0 << 5)) //熄灭中间的 LEDldr r1, =0xE0200244str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮//延时函数
delay2:ldr r2, =10000000ldr r3, =0x0
delay_loop2:sub r2, r2, #1          // r2 = r2 -1cmp r2, r3              // cmp 会影响 z 标志位,如果 r2 等于 r3 则 z =1,下一句中 eq 就会成立bne delay_loop2bl	cpu_init_crit//第一步: 把所有引脚设置成输出模式ldr r0, =0x11111111     //从后面的 = 可以看出用的是 ldr 伪指令,因为需要编译器来判断这个数ldr r1, =0xE0200240     //是合法立即数还是非法立即数,一般写代码都用 ldr 伪指令str r0, [r1]            // 寄存器间接寻址。功能是把 r0 中的数写入到 r1 中的数为地址的内存中去//第二步:ldr r0, =((0 << 3) | (0 << 4) | (1 << 5)) //熄灭右边的 LEDldr r1, =0xE0200244str r0, [r1]            //把 0 写入到 GPJ0DAT 寄存器中,引脚即输出低电平,LED 点亮//延时函数
delay3:ldr r2, =10000000ldr r3, =0x0
delay_loop3:sub r2, r2, #1          // r2 = r2 -1cmp r2, r3              // cmp 会影响 z 标志位,如果 r2 等于 r3 则 z =1,下一句中 eq 就会成立bne delay_loop3
#endifbl	_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 的代码段没有被放在前面。

在这里插入图片描述


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

在这里插入图片描述


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

在这里插入图片描述


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

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

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

在这里插入图片描述


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

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

在这里插入图片描述

在这里插入图片描述


4、实践验证。

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

在这里插入图片描述


源自朱有鹏老师.


http://www.ppmy.cn/news/31197.html

相关文章

如何使用码匠连接 CouchDB

目录 在码匠中集成 CouchDB 在码匠中使用 CouchDB 关于码匠 CouchDB 是一种开源的 NoSQL 数据库服务&#xff0c;它使用基于文档的数据模型来存储数据。CouchDB 的数据源提供了高度可扩展性、高可用性和分布式性质。它支持跨多个节点的数据同步和复制&#xff0c;可以在多个…

【YOLOv8/YOLOv7/YOLOv5/YOLOv4/Faster-rcnn系列算法改进NO.57】引入可形变卷积

文章目录前言一、解决问题二、基本原理三、​添加方法四、总结前言 作为当前先进的深度学习目标检测算法YOLOv8&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系列…

Mysql问题:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause

1 问题描述 使用Navicat连接到MySQL(版本&#xff1a;8.0.18)&#xff0c;执行查询&#xff1a; select * from t_user WHERE user_name admin查询结果没有问题&#xff0c;但是报错&#xff1a; [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY claus…

(枚举)(模拟)(前缀和)(数组模拟哈希)(可二分)1236. 递增三元组

目录 题目链接 一些话 流程 套路 ac代码 题目链接 1236. 递增三元组 - AcWing题库 一些话 int f[N]; memset(f,0,sizeof f)影响不到f[N] 所以尽量不要对f[N]赋值&#xff0c;不要用f[N]操作 流程 //由三重暴力i,j,k因为三重暴力底下是分别用i和j&#xff0c;j和k作比较…

SpringBoot - 什么是跨域?如何解决跨域?

什么是跨域&#xff1f; 在浏览器上当前访问的网站&#xff0c;向另一个网站发送请求&#xff0c;用于获取数据的过程就是跨域请求。 跨域&#xff0c;是浏览器的同源策略决定的&#xff0c;是一个重要的浏览器安全策略&#xff0c;用于限制一个 origin 的文档或者它加载的脚本…

python带你成功复刻热门手机游戏——飞翔的小鸟

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 飞翔的小鸟&#xff08;游戏英文名&#xff1a;Flappy Bird&#xff09; 一款由越南独立开发者开发的手机游戏&#xff0c;是之前非常流行的一款手机游戏 小游戏目标&#xff1a;让小鸟穿过管子&#xff0c;不要碰到任何物体…

23种设计模式-迭代器模式(安卓应用场景介绍)

迭代器模式是一种行为型设计模式&#xff0c;它允许你在不暴露集合对象内部结构的情况下遍历集合中所有元素。在本文中&#xff0c;我们将介绍迭代器模式的概念和原理&#xff0c;提供一个基于Java的示例&#xff0c;并探讨在Android应用程序开发中的实际应用。 迭代器模式的概…

[深入理解SSD系列 闪存2.1.5] NAND FLASH基本读操作及原理_NAND FLASH Read Operation源码实现

前言 上面是我使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义, 下面先来个热身: 问1. 原理图上NAND FLASH只有数据线,怎么传输地址? 答1.在DATA0~DATA7上既传输数据,又传输地址 当ALE为高电平时传输的是地址, 问2. 从NAND FLASH芯片手册可知,要…