參考了mobilefzb和趙春江兩位大牛的,也研究了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
、將relocate的地址值賦給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的問題,那應該是編譯器版本不兼容問題,建議換換新的版本或更舊的版本再試試。