一.u-boot目录介绍
主要在这三个文件夹做修改。
目 录 | 特 性 | 解 释 说 明 |
board | 平台相关 | 存放开发板相关的目录文件,如 RPXlite(mpc8xx)、 smdk2410(arm920t)、sc520_cdp(x86) 等目录 |
arch | 构架相关 | 存放了各种芯片构架相关的文件 |
api | 通用 | 存放 U-Boot 提供的接口函数 |
common | 通用 | 通用的代码,涵盖各个方面,以命令行处理为主 |
disk | 通用 | 磁盘分区相关代码 |
lib | 通用 | 存放 u-boot 源码中使用到的库函数 |
nand_spi | 通用 | NAND 存储器启动相关代码 |
include | 平台相关 | 头文件和开发板配置文件,所有开发板的配置文件都在 configs 目录下 |
common | 通用 | 通用的多功能函数实现 |
net | 通用 | 存放网络相关程序 |
fs | 通用 | 存放文件系统相关程序 |
post | 通用 | 存放上电自检程序 |
drivers | 通用 | 通用的设备驱动程序,主要有以太网接口的驱动 |
disk | 通用 | 硬盘接口程序 |
examples | 应用例程 | 一些独立运行的应用程序的例子,如 helloworld |
tools | 工具 | 存放制作 S-Record 或者 U-Boot 格式的镜像等工具,如 mkimage |
doc | 文档 | 开发使用文档 |
boards.cfg:存放着开发板的一些信息
config.mk:是一个Makefile文件,将来在某个Makefile中会去调用它
COPYING:版权申声明
CREDITS:记录对u-boot有贡献的人
MAINTAINERS:记录当前在参与维护u-boot源码的社区工作者
MAKEALL:一个脚本文件,帮助编译u-boot
Makefile:负责u-boot的编译
mkconfig:负责u-boot的配置,一个shell脚本
README:u-boot使用说明书
rules.mk:u-boot中Makefile使用的规则
二.u-boot启动流程分析
1.u-boot启动流程
1)汇编阶段:
设置CPU为SVC模式
关闭中断、MMU、看门狗、Cache
部分硬件初始化(时钟、串口、DRAM)
uboot自搬运到DDR(14K引导)
设置好栈区(准备运行C语言)
2)C语言阶段:
大部分硬件初始化
搬运Linux内核或者进入交互模式
运行LInux内核
2.uboot代码分析
u-boot程序入口在arch/arm/cpu/armv7/start.S文件下。并且读取的第一条指令在_start标号所指向的程序。
1)异常向量表定义
arch/arm/cpu/armv7/start.S
异常:当硬件发生故障的时候CPU会强制PC指针指向对应的异常入口执行代码(异常入口一般为异常处理函数)
ARM处理器有七种异常:分别是复位,未定义指令异常,软件中断异常,预取指异常,数据中止,普通中断异常,快速中断异常
异常向量表只与CPU架构有关
2)设置SVC模式并禁止中断
:arch/arm/cpu/armv7/start.S
CPSR是ARM内核的程序状态寄存器,操作该寄存器可以设置ARM内核的状态。
注:此代码属于架构级别,只要SOC芯片是ARM内核,代码不需要改变
3)关闭MMU和Cache
arch/arm/cpu/armv7/start.S
所有 CPU 初始化工作都在cpu_init_crit这里完成,该函数在第282行:
注:以上代码用于让L1的I/D cache失效(I-cache用来存指令 D-cache用来存数据)以及关闭MMU和Cache。这部分代码一般也不需要修改的,属于架构级的。只要使用ARM内核都不用修改。
4)部分硬件初始化
arch/arm/cpu/armv7/start.S
lowlevel_init函数用户基本硬件初始化,该函数在board/samsung/tiny4412/lowlevel_init.S文件中。
通过分析lowlevel_init代码,我们可以得知在lowlevel_init代码中完成了以下功能:
1.设置栈指针
2.设置电源管理
3.LED等初始化并且点亮LED灯
4.判断OM引脚状态,得到启动方式
5.时钟初始化:system_clock_init
6.DDR初始化:mem_ctrl_asm_init
7.串口初始化:uart_asm_init
8.u-boot自搬移:load_uboot
注:以上大部分代码属于芯片级的,如果所使用的芯片相同,则也不需要修改!
5)uboot自搬运
load_uboot代码在在board/samsung/tiny4412/lowlevel_init.S文件中
CPU会根据前面读取的启动方式,到指定的启动设备复制u-boot到DDR内存中
6)设置好栈
当复制完成后,便执行
在after_copy代码中,有如下代码:
上面代码表示跳转到board_init_f去执行。而board_init_f是有C语言来写的。至此u-boot的汇编阶段便结束了。
7)大部分硬件初始化
在board_init_f函数(arch/arm/lib/board.c),有如下代码:
这是一个for循环,通过函数指针init_fnc_ptr去循环调用init_sequence指针数组中的函数。
init_sequence[] 数组中存放的是用户需要进行初始化各的种设备函数,即实现了大部分硬件初始化。如果函数执行失败,则u-boot会死循环。这部分代码就需要根据具体的硬件开发平台来进行相应的增删改。
8)引导内核
在board_init_f中最后执行的是relocate_code函数。relocate_code函数最终又会调用board_init_r函数,board_init_r函数在arch/arm/lib/board.c文件中。,这部分代码不仅实现了引导加载内核,而且还实现了命令交互的功能。
注:这部分代码都是通用的。
reset:
设置SVC模式,
屏蔽中断
cpu_init_crit:
Invalidate L1 I/D
disable MMU stuff and caches
lowlevel_init:基本硬件初始化
设置sp
read_om:
led (GPM4_0~3) on LED1_ON
system_clock_init
mem_ctrl_asm_init
uart_asm_init
load_uboot:
mmcsd_boot
movi_uboot_copy:
after_copy:
LED1_ON LED2_ON
board_init_f:(arch/arm/lib/board.c)
/* 大部分硬件初始化 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang();
}
}
relocate_code(start.S)
board_init_r:(arch/arm/lib/board.c)
//要不搬运内核,要不交互模式
main_loop();
三.注意
因为针对SOC芯片的电路设计比较复杂,所以为了节省项目开发周期,很多公司做嵌入式Linux项目直接采购现成的开发板进行项目开发。那么在这个时候开发板厂商一般会有移植好的u-boot供我们使用。我们直接拿来用即可,不要进行u-boot移植。
当然,也有很多公司做项目时会自己设计电路板,一般情况下,都会参考该芯片厂商发布的demo板进行硬件设计。核心板设计不会做太多的改动,比如demo板中使用哪种型号的内存芯片,哪种型号的FLASH芯片,那么公司设计的开发板也会采取这种方案。但是底板就需要根据项目进行特定的电路设计。这个时候,芯片厂商同样会提供demo板的u-boot,我们需要在这个u-boot的基础上进行修改,大部分是对硬件初始化进行修改,比如网卡驱动,串口驱动等。
最后,同学们面临最多的情况是,当你进入某家公司时,该公司的项目板对应的u-boot已经被移植好了,所以同学们直接u-boot即可。
总结:大部分工程师进行u-boot移植工作的概率很低,所以,对u-boot移植现在只需要掌握u-boot启动流程以及基本思想即可。其次,切忌不要在u-boot上浪费太多时间,因此即使u-boot有多完善,在整个Linux项目开发仅仅起到引导加载内核的作用,项目的功能的多样性还得靠系统编程及网络应用。