最近跑完裸机之后,便开始跑系统,但想着裸机与系统之间隔着个Bootloader,反正以前也没怎么深入研究,便说花一到两周时间来搞搞U-BOOT。
参考了
fzb
和赵春江两位大牛的,也研究了
2010.06
版本的
和
2011.06
版本两个经典版本,也对比了
TQ
(我买的板是天嵌的)自己写的
U-BOOT
,学到了不少,也发现了很多东西,以下便记录以下自己的心得吧,以便以后可以自己参考下。
U-BOOT的两个阶段启动过程:(2010.06经典版来说)
第一阶段:start.S的路径位于arch\arm\cpu\arm920t\这段汇编代码一般被称作第一阶段初始化代码。主要作用是初始化运行环境;初始化内存;重新放置UBOOT代码到内存中;跳入到内存中执行第二段初始化代码
1、 关闭开门狗,屏蔽所有中断
2、 设置分频比
3、 bl cpu_init_crit() 关MMU,初始化内存
bl lowlevel_init()
配置内存,修改内存刷新率参数等
4、
relocate
判断当前代码是在NORFLASH还是RAM
copy_loop
循环 将FLASH代码复制至RAM中
5、
stack_setup
栈设置
clear_bss _bss_start
到_bss_end之间的数据清0
6、
ldr pc , start_armboot
跳转到第二阶段
//=====================================================================
第二阶段:board.c的路径位于arch/arm/lib/board.c,这段代码为U-BOOT的第二阶段初始化代码。主要作用是初始化两个重要数据结构,对SDRAM的内存分配设置,对各种需要用到的外设进行初始化,最后循环跳入main_loop()函数
二阶段start_armboot分为board_init_f 和 board_init_r两部分
先执行的
board_init_f部分:
1、为gd数据结构分配地址,并清零
2、执行init_fnc_ptr函数指针数组中的各个初始化函数,如下
board_early_init_f ,
timer_init , env_init init_baudrate serial_init
console_init_f display_banner dram_init
3、A
、 分配SDRAM高64KB为TLB,用于U-BOOT
B
、分配SDRAM下一单元为U-BOOT代码段,数据段,BSS段
(这里插一句,原来BSS段是用来存放未初始化的全局变量与静态变量)
C
、接着开辟malloc空间,存bd , gd , 3个字大小的异常堆空间
4
、将relorate的地址值赋给gd结构体相应变量(2011.06版本的,用于返回start.S)
后执行的
board_init_r部分:
1、对gd , bd 数据结构赋值初始化
2、各种外设初始化:
初始化
NORFLASH, NANDFLASH,
初始化
ONENAND FLASH
初始化环境变量
初始化
PCI
设置
IP
地址
初始化各类外设
:IIC
、
LCD
、键盘、
USB
初始化控制台
建立
IRQ
中断堆栈
初始化以太网
初始化跳转表(定义了
U-Boot
中基本的常用函数库)。。这不算外设
3、一个死循环执行 main_loop()函数
/**********************************
两个版本的U-BOOT启动对比:
************************************/
其实在总体上都差不多,只不过相对于经典版(2010.06版),新版之后都变恶心了
主要有这样的区别:
1、原版本第一阶段的第5步栈设置被放到第4步relorate前(这个没什么)
2、原版第二阶段的
board_init_f被放到第一阶段第4步relorate前,就是说执行完
stack_setup
栈设置后变进入了第二阶段的部分初始化,然后通过
4
、将relorate的地址值赋给gd结构体相应变量(2011.06版本的,用于返回start.S)
又返回来第一阶段。。。感觉新版改后很乱,很没条理(开源的每年改,就是烦呀,哈哈)
//=================================================
以下列出两个阶段可能要用到的函数的路径,方便以后找:(按2011.06版本)
一阶段:
lowlevel_init
函数,它是在
board/samsung/smdk2410
目录下的
lowlevel_init.s
文件中定义
二阶段:
gd
是一个保存在
ARM
的
r8
寄存器中的
gd_t
结构体的指针,它是在
/include/asm
目录下的
global_data.h
文件内被定义的
bd
结构体的数据原型为
bd_t
数据结构,它表示的是
“
板级信息
”
结构体,它是在
/include/asm
目录下的
u-boot.h
文件中定义的。
init_fnc_ptr函数指针数组中的各个初始化函数:
board_early_init_f
函数在
board/samsung/smdk2410
目录下的
smdk2410.c
文件内
timer_init
函数在
arch/arm/cpu/arm920t/s3c24x0
目录下的
timer.c
文件内
env_init
函数在
common
目录下的
env_flash.c
文件内
init_baudrate
函数在
arch/arm/lib
目录下的
board.c
文件内
serial_init
函数在
drivers/serial
目录下的
serial_s3c24x0.c
文件内,在
include/configs/smdk2410.h
中定义了
CONFIG_S3C24X0_SERIAL
console_init_f
函数在
common
目录下的
console.c
文件内
display_banner
函数在
arch/arm/lib
目录下的
board.c
文件内
dram_init
函数在
board/samsung/smdk2410
目录下的
smdk2410.c
文件内
各种外设的初始化:
flash_init
函数是在
drivers/mtd
目录下的
cfi_flash.c
文件内(因为
include/configs/smdk2410.h
中定义了
CONFIG_FLASH_CFI_DRIVER
)
nand_init
函数是在
divers/mtd/nand
目录下的
nand.c
文件内定义的
env_relocate
函数是在
common
目录下的
env_common.c
文件中定义的
stdio_init ()
在
common
目录下的
stdio.c
文件中定义的
jumptable_init ()
在
common
目录下的
exports.c
文件中定义的
console_init_r ()
是在
common
目录下的
console.c
文件中定义的
interrupt_init ()
enable_interrupts ()
都是在
arch/arm/lib
目录下的
interrupts.c
文件中定义
eth_initialize()
函数是在
net
目录下的
eth.c
文件的第
209
行至第
298
行定义的
main_loop()
在
common
目录下的
main.c
文件内定义的
//===================================================================
天嵌与自己移植的U-BOOT的差别分析和领悟
先列出天嵌公司里研发人员写的
和
我们自己移植(小移植)的最大不同:
对比了一下,发现最大的不同在于
common/main.c
文件中,即在两阶段启动过程基本一样
不同点:(行数按天嵌版本的)
abortboot()
函数(在
main_loop()
中被调用)
Ln239: printf ( “ Press Space key to Download Mode ! ” ) ;
Ln303 :
在检测是否
a key press
时
,
加入了显示
LOGO
程序:
embedsky_tq_logo();
main_loop()
函数
Ln 381: LCD
初始化程序
Ln481 :
分支选择
下载
OR
加载
模式
:
if (
BootFrmNORFLASH()
)
run_command (“
menu
”,0 );
else
{
Printf (“ Booting Linux \n ”);
run_command (“
boot_zImage
”,0 );
}
解析一下:
前面几点都是关于
LCD
和
LOGO
显示的不多说(因为自己移植是没弄到
LCD
的移植)
说一下
main_loop()
函数
中
Ln481 :
分支选择
下载
OR
加载
模式
首先,
run_command
这个是执行命令函数,一看名字就知道,也是在
/common/main.c
中定义的
说说最重要的
“
menu
”
和
“
boot_zImage
”
吧
1、 If
从
NORFLASH
进行启动
,则为下载模式,则执行
menu()
这个函数,在
/common/cmd_menu.c
中定义
打开
cmd_menu.c
文件会发现,里面都是一些串口选项列表,我们打开
2440
电源发现的下载列表都是从
main_menu_usage()
函数中打印出来的,而选择的项又通过
menu_shell()
通过控制台执行各种我们的选项,每个选项的如何执行过程都列得很清楚,感觉就像跑裸机时,自己按照
fzb
的串口控制台弄出来一样
2、
Else
从
NANDFLASH
进行启动
,
则为加载模式,进行
LINUX
系统的配置和启动。
在
lib_arm /boot_zImage.c
文件:里的
boot_zImage( )
函数
函数执行的内容大概如下:
1
、
copy kernel image
2
、
setup linux parameters
3
、
get machine type
4
、
GO -> call-linux
对比后的一些感悟:
虽说自己也跟着移植过
U-BOOT
,也能建立自己的板级支持包,能实现基本的串口控制台,
NAND OR NOR FLASH
,
DM9000
网络,
JFFS2
文件系统等基本功能,但比起天嵌这个,能下载
和
加载
模式,还是有很多不足
所以说,自己移植只是感觉其中的方法,领悟之后还是在天嵌的基础上再加进一步移植吧,感觉没必要从头到尾都自己搞一遍,方法懂了,框架熟悉了就好
//===================================================================
移植过程的一个简单举例:
因为移植很多都是基于
smdk2410
来改的,首先要对
2410
和
2440
的区别有一定了解,再者就是自己在裸机上是编写过改外设的驱动的,这样移植起来就比较舒服,不会说什么都跟着做,感觉很虚,学不到东西。
就举让
U-BOOT
支持
NANDFLASH
的读写
1、
先是在总的宏定义头文件中加上你外设所需的宏定义
总的宏定义路径为
/include /configs / XX.h/ (
最后这个
.h
文件一般是以板的名称命名
)
添加宏定义,如:
#define CONFIG_NAND_BASE 0
。。。
等等
那怎么知道添加什么宏定义呢?一般来说看对外设初始化函数,和
U-BOOT
二阶段启动函数要用到哪些就定义哪些。。。
2、
改相应的初始化函数
:
如
board_nand_init
函数和
s3c2440_hwcontrol
函数
因为
U-BOOT
里初始化函数基本基于
2410
的,而
2440
的
NAND
配置参数和它不同,需要改部分地方
3、
添加初始化函数到第二阶段
board_init_r处,一般来说基本外设都已添加了,看你是否定义宏来让其编译这函数
移植一些规律总结:
其实多移植几次就会发现,UBOOT的移植修改还是遵循着一定的规律。即是先在配置头文件中打开相关宏定义支持,在到板级初始化(一般是第二阶段初始化过程)代码中添加需要支持功能的初始化函数。
如果初始化函数对应的板版本不兼容或不存在,就要自己编写了。
//===================================================================
最后,说说U-BOOT的编译吧
说到编译,建议去看《从庖丁解牛说
uboot
如何编译》,说得很好。
而说到编译的执行过程,建议看看
《
详细分析make uboot 最后的编译链接的具体执行过程》
最后谈谈编译不通过的问题,如果是内部自己程序出错,可以通过提示信息查出
如果是出现ERROR一百多个,或者什么arm-linux-ld的问题,那应该是编译器版本不兼容问题,建议换换新的版本或更旧的版本再试试。
好,到这里算是可以继续下一步系统的移植和驱动的编写了,而最近503的肥仔说想了个有关投影仪与摄像头的项目想做,刚好我对图像处理这方面比较感兴趣,而且觉得他创意很好
可能去做了哦,GO
头很晕,歇几天啦~~