日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

S5PV210-uboot源码分析-第二阶段

發(fā)布時(shí)間:2023/12/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 S5PV210-uboot源码分析-第二阶段 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


由第一階段,已經(jīng)將在SD卡的整個(gè)uboot重定位拷貝到了DDR的鏈接地址中去后,又回到了調(diào)用這個(gè)函數(shù)的位置

。接著往下執(zhí)行遇到了這個(gè)start_armboot函數(shù)(uboot啟動(dòng)的第二階段)。


uboot啟動(dòng)的第二階段分析:


6、1、start_armboot函數(shù)

(1)一個(gè)很長(zhǎng)的函數(shù),從444行到908行。這個(gè)函數(shù)不僅僅只有這么長(zhǎng),這個(gè)函數(shù)的內(nèi)部還調(diào)用了一些函數(shù),所以說(shuō)這個(gè)函數(shù)是很龐大的。這個(gè)函數(shù)構(gòu)成了整個(gè)uboot啟動(dòng)的第二個(gè)階段。

(2)宏觀上分析,uboot啟動(dòng)的第二階段應(yīng)該做什么?

(3)uboot的第一階段主要完成的任務(wù)就是,初始化SOC內(nèi)部的一些部件(關(guān)看門(mén)狗,初始化時(shí)鐘等),也初始化了DDR并且將整個(gè)uboot重定位到了DDR中去。

(4)所以u(píng)boot的第二階段應(yīng)該要做的就是,去初始化一些在uboot第一階段沒(méi)有初始化的硬件,主要是初始化SOC外部的硬件(,如iNand、網(wǎng)卡芯片····),還有uboot本身的東西(uboot的命令,環(huán)境變量等·····),然后最終完成這些任務(wù)時(shí),進(jìn)入到uboot的命令行,準(zhǔn)備接受命令,人機(jī)開(kāi)始交互。

(5)uboot啟動(dòng)時(shí)是開(kāi)機(jī)自動(dòng)啟動(dòng)的,在啟動(dòng)的過(guò)程中,會(huì)打印出來(lái)很多的信息(這些信息就是uboot在第一階段和第二階段不斷進(jìn)行初始化時(shí),打印出來(lái)的信息,我通過(guò)這些信息也可以跟蹤uboot的軌跡,看uboot執(zhí)行是否成功運(yùn)行。)然后uboot開(kāi)始bootdelay倒數(shù),如果在倒數(shù)的過(guò)程中,我們敲了回車(chē),則會(huì)進(jìn)入到uboot的命令行下,如果我們沒(méi)有干涉bootdelay月的倒數(shù),倒數(shù)結(jié)束后,將直接執(zhí)行bootcmd對(duì)應(yīng)的命令(一般是啟動(dòng)內(nèi)核命令),uboot就會(huì)死掉了。不管內(nèi)核這次是否啟動(dòng)成功,uboot這一生都已經(jīng)死掉了。

(6)uboot的命令行就是一個(gè)死循環(huán),這個(gè)循環(huán)體內(nèi),不斷的接受命令,解析命令,執(zhí)行命令,直到接收解析執(zhí)行了啟動(dòng)內(nèi)核命令,uboot就會(huì)死掉。


6、2、start_armboot函數(shù)解析1

1、init_fnc_t 通過(guò)搜索可以知道這是一個(gè)函數(shù)類(lèi)型。

(1)typedef int (init_fnc_t) (void); 這是將一個(gè)返回值為int的,參數(shù)為void的函數(shù)類(lèi)型,重命名為init_fnc_t,所以這個(gè)init_fuc_t是一個(gè)函數(shù)類(lèi)型。

(2)init_fnc_t **init_fnc_ptr; 所以這句話的意思就是定義了一個(gè)二重的函數(shù)指針。

init_fnc_ptr就是一個(gè)二重函數(shù)指針。回顧學(xué)過(guò)的高級(jí)C語(yǔ)言,二重指針有兩個(gè)作用,一個(gè)是用來(lái)指向一重指針,一個(gè)是用來(lái)指向指針數(shù)組(數(shù)組中的沒(méi)個(gè)元素都是指針)。

所以這個(gè)init_fnc_ptr這個(gè)二重函數(shù)指針,可以用來(lái)指向一個(gè)函數(shù)指針數(shù)組(數(shù)組里面的元素全是函數(shù)指針)


2、board.c文件中,開(kāi)始的內(nèi)容有個(gè)下面的東西

(1)DECLARE_GLOBAL_DATA_PTR;

//#define DECLARE_GLOBAL_DATA_PTR ? ? register volatile gd_t *gd asm ("r8")

(2)這個(gè)宏的意思就是,定義了一個(gè)全局變量gd,這個(gè)全局變量gd是一個(gè)指針,用volatile修飾,表示這個(gè)變量是可變的,在編譯之外的情況下,這個(gè)變量的值是可以改變的,不讓編譯對(duì)這個(gè)變量做優(yōu)化,用register修飾,表示讓這個(gè)變量盡量放在寄存器中,提高速度,asm ("r8")意思是,讓這個(gè)變量放在寄存器的r8中,就是不僅讓這個(gè)變量放在了寄存器中,還要讓這個(gè)變量放到的寄存器是寄存器r8中。

(3)綜合分析:DECLARE_GLOBAL_DATA_PTR 這個(gè)宏的意思就是,定義了一個(gè)放在寄存器r8中,并且值是可以在編譯之外改變的全局變量,名字叫g(shù)d,并且這個(gè)全局變量是一個(gè)指針類(lèi)型,指向gd_t類(lèi)型變量的指針。

(4)為什么要用register修飾這個(gè)全局變量呢,因?yàn)檫@個(gè)全局變量是一個(gè)gd_t * 類(lèi)型的,是一個(gè)指向結(jié)構(gòu)體的指針。同時(shí)這個(gè)全局變量gd(global data)指向的那個(gè)結(jié)構(gòu)體中放了很多內(nèi)容,組成了這個(gè)結(jié)構(gòu)體,這些內(nèi)容就是uboot中常用的全局變量。

也就是說(shuō),這個(gè)gd全局變量所指向的那個(gè)結(jié)構(gòu)體中放的內(nèi)容是uboot常用的全局變量。也就是說(shuō),那個(gè)把那個(gè)放了uboot常用的全局變量的結(jié)構(gòu)體的地址放再了gd這個(gè)全局變量指針中。gd這個(gè)指針本身占4個(gè)字節(jié)。

所以這個(gè)gd全局變量是會(huì)經(jīng)常被訪問(wèn)的,所以為了提升程序的效率,用register來(lái)修飾

(5)gd_t這個(gè)結(jié)構(gòu)體類(lèi)型定義在include/asm-arm/global_data.h中,這個(gè)結(jié)構(gòu)體類(lèi)型中,第一個(gè)又是定義了一個(gè)結(jié)構(gòu)體類(lèi)型的指針變量,這個(gè)結(jié)構(gòu)體類(lèi)型bd_t定義在include/asm-arm/U-boot.h中,找到bd_t這個(gè)結(jié)構(gòu)體類(lèi)型的封裝,可以看著個(gè)封裝的名字bd_info,知道這個(gè)結(jié)構(gòu)體封裝的是開(kāi)發(fā)板相關(guān)的硬件信息。

(6)typedef struct global_data {

bd_t *bd; //結(jié)構(gòu)體類(lèi)型指針變量,指向一個(gè)struct bd_info結(jié)構(gòu)體,里面封裝了開(kāi)發(fā)板的

//硬件相關(guān)信息

unsigned long flags; //標(biāo)志位,干嘛的現(xiàn)在不清楚

unsigned long baudrate; //波特率 因?yàn)閡boot要用串口發(fā)送到人機(jī)界面上,實(shí)現(xiàn)人機(jī)交互,所以需要波特率

//都是全局的,因?yàn)槎际怯蒰d這個(gè)全局變量指針開(kāi)始指向的。控制臺(tái)的波特率

unsigned long have_console; /* serial_init() was called */ //bool類(lèi)型的變量,雖然定義是四字節(jié),但

//是后面會(huì)知道只用了一個(gè)位,可以看出應(yīng)該是代表有無(wú)控制臺(tái)的,就是能否進(jìn)行printf scanf ,在控制臺(tái)建立起來(lái)以后就可以printf scanf了,不然只能用串口的putc等

unsigned long reloc_off; /* Relocation Offset */ //重定位時(shí)候的偏移量

unsigned long env_addr; /* Address ?of Environment struct */ //封裝環(huán)境變量那個(gè)結(jié)構(gòu)體的偏移量

//地址

unsigned long env_valid; /* Checksum of Environment valid? */ ?//在內(nèi)存里的環(huán)境變量當(dāng)前是可以使用的

//還是不可以使用的。也是一個(gè)bool類(lèi)型的變量,如果是1就表示

//內(nèi)存中的那一份環(huán)境變量已經(jīng)可以使用了。

//為什么要檢查內(nèi)存中那一份環(huán)境變量是否可以使用呢,因?yàn)殚_(kāi)始的時(shí)候我們的

//環(huán)境變量是放在SD卡中,當(dāng)我們把在SD卡中的環(huán)境變量讀取到內(nèi)存中的時(shí)候,這些個(gè)環(huán)境

//變量是不能馬上建立好的,因?yàn)閮?nèi)存中開(kāi)始沒(méi)有的,所以要檢查,等所有的環(huán)境變量從SD卡

//讀取到內(nèi)存中,建立好后,我們才可以使用內(nèi)存中那一份環(huán)境變量

unsigned long fb_base; /* base address of frame buffer */ //顯存空間的起始地址,因?yàn)樵诼銠C(jī)中的LCD

//那一章我們知道顯存空間中的數(shù)據(jù)圖像,會(huì)通過(guò)lcd控制器自動(dòng)硬件刷到lcd液晶屏上顯示,

//所以需要一個(gè)顯存空間的起始地址

#ifdef CONFIG_VFD //這個(gè)宏沒(méi)有定義

unsigned char vfd_type; /* display type */

#endif

#if 0 //用0注釋掉了

unsigned long cpu_clk; /* CPU clock in Hz! */

unsigned long bus_clk;

phys_size_t ram_size; /* RAM size */

unsigned long reset_status; /* reset status register at boot */

#endif

void **jt; /* jump table */ //調(diào)轉(zhuǎn)表,在uboot中應(yīng)該是沒(méi)有用到

} gd_t;


/***************************************************************************/


typedef struct bd_info {

? ? int bi_baudrate; /* serial console baudrate */ //開(kāi)發(fā)板上的波特率,這個(gè)波特率和上面的那

//個(gè)波特率設(shè)置為一模一樣的,可以能是重復(fù)了

//bi表示是在bd_info這個(gè)結(jié)構(gòu)體中的。也有

//告訴我們這些是跟開(kāi)發(fā)板板上有關(guān)的

? ? unsigned long bi_ip_addr; /* IP Address */ //開(kāi)發(fā)板的IP地址

? ? unsigned char bi_enetaddr[6]; /* Ethernet adress */ //開(kāi)發(fā)板的MAC地址

? ? struct environment_s ? ? ? *bi_env; //封裝環(huán)境變量的結(jié)構(gòu)體指針,跟上面那個(gè)好像也重復(fù)

? ? ulong ? ? ? ?bi_arch_number; /* unique id for this board */ //開(kāi)發(fā)板的機(jī)器碼

? ? ulong ? ? ? ?bi_boot_params; /* where this board expects params */ //啟動(dòng)參數(shù)地址

? ? struct /* RAM configuration */

? ? {

ulong start;

ulong size;

? ? } bi_dram[CONFIG_NR_DRAM_BANKS]; //開(kāi)發(fā)板上DDR的信息,定義了一個(gè)結(jié)構(gòu)體數(shù)組變量

//這個(gè)結(jié)構(gòu)體數(shù)組bi_dram有兩個(gè)元素,CONFIG_NR_DRAM_BANKS值

//是看代碼知道的,為什么是2呢,因?yàn)槲覀冇袃蓚€(gè)DDR,每一個(gè)

//元素是一個(gè)結(jié)構(gòu)體,里面有兩個(gè)信息一個(gè)是在哪里個(gè)地址開(kāi)始,

//一個(gè)是有多大。一個(gè)元素表示一個(gè)DDR

#ifdef CONFIG_HAS_ETH1 //這個(gè)宏我們有定義

? ? /* second onboard ethernet port */ //兩個(gè)網(wǎng)卡的意思,但是上面的宏沒(méi)有定義

? ? unsigned char ? bi_enet1addr[6];

#endif

} bd_t; //將這封裝了很多東西的結(jié)構(gòu)體類(lèi)型重命名為bd_t





6、3、內(nèi)存使用排布

1、為什么要分配內(nèi)存給gd和bd

(1)#define DECLARE_GLOBAL_DATA_PTR ? ? register volatile gd_t *gd asm ("r8")

gd這個(gè)只是一個(gè)指針變量,而且他不在占內(nèi)存,被分配到寄存器中去了,也就是說(shuō)gd所指向的那個(gè)結(jié)構(gòu)體中環(huán)境變量并沒(méi)有被分配內(nèi)存。所以我們?cè)谑褂胓d之前,要給他分配內(nèi)存,否則gd也只是一個(gè)野指針而已,但是現(xiàn)在是在裸機(jī)程序下,不是在操作系統(tǒng)下,所以沒(méi)有辦法使用malloc去給gd分配內(nèi)存。裸機(jī)程序下沒(méi)有malloc管理器,沒(méi)有操作系統(tǒng),無(wú)法用malloc分配內(nèi)存。

(2)gd和bd需要內(nèi)存,但是當(dāng)前沒(méi)有操作系統(tǒng)去管理內(nèi)存,不能malloc去申請(qǐng)內(nèi)存。在DDR中現(xiàn)在有大片的內(nèi)存可以使用,我們只需要直接去用內(nèi)存地址訪問(wèn)內(nèi)存即可。但是DDR中的內(nèi)存我們?cè)趗boot中不能隨意的去使用,因?yàn)閡boot會(huì)被反復(fù)使用的,比如如果我們?cè)趗boot中用fastboot命令將操作系統(tǒng)的內(nèi)核下載到DDR的指定的內(nèi)存中去,如果我們?cè)趗boot中隨意使用了這段內(nèi)存,那么結(jié)果可想而知,所以我們?cè)趗boot中使用DDR的內(nèi)存的時(shí)候,不能隨意的去使用。uboot的后續(xù)操作中,會(huì)需要用到緊湊排布的內(nèi)存塊空間,所以這里我們使用內(nèi)存的時(shí)候,要盡量的本著我們申請(qǐng)的內(nèi)存夠用就好,而且要緊湊排布,不能這里一塊那里一塊,避免以后的uboot的操作和內(nèi)存相關(guān)的造成不便。

(3)所以我們uboot中,使用內(nèi)存的時(shí)候要有一個(gè)整體的規(guī)劃。

在DDR中,共有512M,DDR1和DDR2兩個(gè)DDR分別是256M,一共512M,DDR的起始地址是0X30000000,uboot的被重定位到了DDR的0X33E00000地址中去了,我在重定位到uboot的時(shí)候,我們給了uboot的大小是2M,也就是說(shuō)從0X33E00000開(kāi)始向上的2M空間是我們整個(gè)uboot所擁有的,在這空間中,實(shí)際上我們的uboot用不了那么大的空間,在這2M的空間內(nèi),緊挨著實(shí)際uboot的大小上面的地方放的是我們?cè)贒DR中設(shè)置的棧。

(4)gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);

gd_base就是這個(gè)gd的內(nèi)存起始地址。

6、4、內(nèi)存排布

1、uboot區(qū): 從CFG_UBOOT_BASE起始地址開(kāi)始,到一個(gè)地方。CFG_UBOOT_BASE+XX(長(zhǎng)度為實(shí)際uboot的大小)

CFG_UBOOT_BASE是uboot在DDR中的起始地址0X33E00000

CFG_UBOOT_SIZE是uboot的在DDR中的整個(gè)大小

2、堆區(qū):CFG_MALLOC_LEN 堆區(qū)的長(zhǎng)度

在uboot中,我們也想建立一個(gè)堆去供我們使用,所以我們也建造了一個(gè)堆區(qū),這個(gè)堆區(qū)的長(zhǎng)度是CFG_MALLOC_LEN,

#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 896*1024)

#define CFG_ENV_SIZE 0x4000

所以可以知道堆區(qū)的長(zhǎng)度大小是CFG_MALLOC_LEN = 0X4000 + 896*1024 = 16KB + 896KB = 912KB

3、棧區(qū):#define CFG_STACK_SIZE 512*1024 //512KB

4、gd :sizeof(gd_t) 就是gd所指向的那個(gè)結(jié)構(gòu)體的大小,可以知道是36B

5、bd :sizeof(bd) 就是bd所指向的那個(gè)結(jié)構(gòu)體的大小,大概是44B.

6、所以大概可以知道gd內(nèi)存起始地址的位置了,gd大概是在uboot在DDR中起始地址0X33E00000開(kāi)始的位置往上走了2M的長(zhǎng)度,又減掉了堆區(qū)912KB的長(zhǎng)度,又減去了棧區(qū)512KB的長(zhǎng)度,又減去了gd那個(gè)結(jié)構(gòu)體的大小36B的位置。

uboot實(shí)際的大小很小,只有200或者300多KB,而gd的位置應(yīng)該在uboot實(shí)際大小的上面200或者300KB的位置,不會(huì)踩到uboot,如果uboot寫(xiě)的很大,怕會(huì)踩到gd的內(nèi)存部分,我們可以改變CFG_UBOOT_SIZE這個(gè)值,把2M改的更大,這樣就不會(huì)踩到了gd了。

(1)gd = (gd_t*)gd_base;//將算好規(guī)劃好的gd應(yīng)該在DDR中內(nèi)存的起始地址位置賦值給gd,此時(shí)gd就已經(jīng)有了好的安身

//也可叫將gd實(shí)例化,由單純的指針,變成了有指向?qū)嶋H內(nèi)存的指針

(2)memset ((void*)gd, 0, sizeof (gd_t));//怕這段gd所用的內(nèi)存不干凈,全部清零

(3)gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));//因?yàn)閎d也是指針,此時(shí)也沒(méi)有確切的身體,所以我們也要將bd實(shí)例化,給他一個(gè)內(nèi)存,因?yàn)镈DR內(nèi)存是向上生長(zhǎng)的,所以我們讓gd這個(gè)內(nèi)存起始的地址位置,減去了一個(gè)bd大小的空間,讓這個(gè)空間的起始地址作為bd的內(nèi)存起始位置即可。

總結(jié):所以總的來(lái)說(shuō)就是為了給gd和bd原先沒(méi)有指向確切內(nèi)存,讓他指向一個(gè)確切的可用安全的內(nèi)存空間的起始位置去。這樣gd和bd就被實(shí)例化了,有了身體了。可以使用了。

7、內(nèi)存間隔: __asm__ __volatile__("": : :"memory");

__asm__ 是在C語(yǔ)言中內(nèi)嵌匯編的意思,就是在C語(yǔ)言中內(nèi)嵌了匯編,讓C語(yǔ)言和匯編可以交疊工作。

知道這句代碼的意思就行,就是為了不讓GCC過(guò)度優(yōu)化造成錯(cuò)誤。為了防止高版本的gcc優(yōu)化造成錯(cuò)誤,一般在我們的gcc交叉編譯工具大于3.4版本的時(shí)候就要加上這句話,因?yàn)槲覀僄CC已經(jīng)是4.4以上的了,所以一定要加。


6、4 start_armboot函數(shù)的解析2

1.board.c中483行的for循環(huán)

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

if ((*init_fnc_ptr)() != 0) {

hang ();

}

}

(1)init_fnc_ptr 可以知道這是一個(gè)二重函數(shù)指針

(2)init_sequence 是一個(gè)函數(shù)指針數(shù)組,數(shù)組中有很多函數(shù)指針,指針指向的函數(shù)都是init_fnc_t類(lèi)型的(typedef (3)int (init_fnc_t) (void); )返回值是int類(lèi)型的,參數(shù)是void

(4)*init_fnc_ptr for循環(huán)的終止條件, 解引用這個(gè)二重函數(shù)指針指向的數(shù)組元素的地址,也就是解引用后,得到數(shù)組偏移地址所對(duì)應(yīng)的那個(gè)函數(shù)指針

(5)++init_fnc_ptr for循環(huán)的增量 指針遍歷函數(shù)指針數(shù)組的元素地址,+1表示指向下一個(gè)元素的地址

(6)init_sequence 這個(gè)數(shù)組在board.c的416行,定義的同時(shí)初始化了,初始化的內(nèi)容都是函數(shù)名(函數(shù)名就是函數(shù)指針,代碼段的首地址)。

(7)我們?cè)趺幢闅v一個(gè)函數(shù)指針數(shù)組呢?一般由兩種方法,一個(gè)是將終止條件設(shè)置為數(shù)組的大小,當(dāng)不滿足時(shí),則循環(huán)結(jié)束。另一種是在數(shù)組的內(nèi)部弄一個(gè)元素讓其為終止的條件,如果遇到了這個(gè)元素則其終止,這里我們用到的就是第二種方法。

init_sequence數(shù)組中的函數(shù)指針,這些函數(shù)在運(yùn)行成功的時(shí)候都會(huì)返回一個(gè)0,返回0表示運(yùn)行成功了。如果在遍歷這個(gè)函數(shù)指針數(shù)組時(shí),里面有一個(gè)函數(shù)運(yùn)行不成功,不返回0,則會(huì)執(zhí)行hang();掛起,就是證明啟動(dòng)過(guò)程執(zhí)行失敗了

(8)init_sequence里面的函數(shù)初始化,都是board級(jí)別的,板級(jí)的硬件相關(guān)的初始化。cpu外面的,開(kāi)發(fā)板里面的。

2、cpu_init 里面是空的,直接返回了0,代表確實(shí)這個(gè)init_sequence里面的函數(shù)是初始化CPU外面的,開(kāi)發(fā)板里面相關(guān)的硬件的東西,并且cpu初始化在前面的start.S中已經(jīng)完成了

3、board_init 函數(shù)體在board/samsung/X210/X210.c中

(1)DECLARE_GLOBAL_DATA_PTR; 在board_init函數(shù)中的一句話,意思是聲明gd的意思,因?yàn)橄旅嬉玫絞d。定義的同時(shí)不初始化就是聲明。

宏定義的變量也是放在頭文件中的,在調(diào)用時(shí)也是需要包含頭文件的。只是說(shuō)這個(gè)宏如果不使用,這個(gè)變量就不會(huì)聲明。

(2)網(wǎng)卡初始化

接著這個(gè)宏CONFIG_DRIVER_DM9000

這個(gè)宏是在x210.h中定義的,定義了這個(gè)宏就表示我們的開(kāi)發(fā)板要配置這個(gè)DM9000這個(gè)網(wǎng)卡,下面就是這個(gè)網(wǎng)卡的初始化了。dm9000_pre_init();這個(gè)函數(shù)就是對(duì)應(yīng)的DM9000這個(gè)網(wǎng)卡的初始化函數(shù),如果你用的網(wǎng)卡在uboot中沒(méi)有,那么就需要你自己去移植了,怎么移植呢,也是用一個(gè)宏來(lái)進(jìn)行條件編譯的選擇,完了將你對(duì)應(yīng)的網(wǎng)卡的初始化函數(shù)放在這個(gè)宏下,可以看到,這些個(gè)函數(shù)都是放在,x210.c中的。

dm9000_pre_init();這個(gè)函數(shù)主要是GPIO和端口的配置,而不是驅(qū)動(dòng),因?yàn)榫W(wǎng)卡驅(qū)動(dòng)是現(xiàn)成的,移植的時(shí)候驅(qū)動(dòng)是不需要改動(dòng)的,關(guān)鍵就是這個(gè)函數(shù)中的基本的初始化,因?yàn)檫@些基本初始化是和硬件相關(guān)的。

?

6、5 start_armboot函數(shù)解析3

背景:

(1)初始化了DDR

注意:這里初始化DDR和我們?cè)趨R編階段在lowlevel_init函數(shù)中初始化DDR是不同的。

當(dāng)時(shí)DDR的初始化是DDR硬件下的初始化,為了讓DDR可以工作,而現(xiàn)在的初始化是和軟件層面相關(guān)的,一些DDR的屬性配置、地址設(shè)置的初始化。

為什么要在軟件層次上初始化DDR,因?yàn)閷?duì)于uboot來(lái)說(shuō),我們要知道開(kāi)發(fā)板上有幾片DDR,每一片DDR的起始地址和長(zhǎng)度是多大這些信息。uboot采用了一個(gè)簡(jiǎn)單有效的方式:程序員在移植uboot到一個(gè)開(kāi)發(fā)板上時(shí),程序員在x210_sd.h這個(gè)文件中,自己用宏定義的方式配置出板子上DDR的相關(guān)信息,然后uboot只要讀取這些信息即可。(實(shí)際上還有另外一種方式,就是uboot通過(guò)代碼的方式直接讀取硬件中的硬件相關(guān)的信息來(lái)知道DDR的配置,但是uboot沒(méi)有這么做,在我們PC機(jī)上,BIOS用的是這種方式,比如我們的電腦是4G的內(nèi)存,當(dāng)我加了一條內(nèi)存條的時(shí)候,電腦就識(shí)別到了內(nèi)存變成了8G了,這就是直接讀取硬件相關(guān)的信息,來(lái)知道DDR相關(guān)的信息)

(2)在x210_sd.h中的499行到505行,用標(biāo)準(zhǔn)宏定義的方式配置了我們開(kāi)發(fā)板上DDR的相關(guān)的信息,為什么說(shuō)是標(biāo)準(zhǔn)的宏定義呢,因?yàn)檫@些宏定義的名字是不可以被改變的,我們只能改變這些宏定義的值。所以叫做標(biāo)準(zhǔn)的宏定義。

#define CONFIG_NR_DRAM_BANKS ? ?2 ? ? ? ? ?/* we have 2 bank of DRAM */

#define SDRAM_BANK_SIZE ? ? ? ? 0x10000000 ? ?//實(shí)際256MB ?/* 512 MB lqm 這個(gè)512MB的注釋純屬瞎寫(xiě) */

//#define SDRAM_BANK_SIZE ? ? ? ? 0x20000000 ? ?/* 1GB lqm*/ //這個(gè)可能是以前的,但實(shí)際應(yīng)該是512MB

#define PHYS_SDRAM_1 ? ? ? ? ? ?MEMORY_BASE_ADDRESS /* SDRAM Bank #1 base address */

#define PHYS_SDRAM_1_SIZE ? ? ? SDRAM_BANK_SIZE //我們用的DDR一片是256MB

#define PHYS_SDRAM_2 ? ? ? ? ? ?MEMORY_BASE_ADDRESS2 /* SDRAM Bank #2 */

#define PHYS_SDRAM_2_SIZE ? ? ? SDRAM_BANK_SIZE //我們用的DDR第二片也是256MB


1、gd->bd->bi_arch_number = MACH_TYPE;

(1)bi_arch_number是bd_info結(jié)構(gòu)體中的一個(gè)成員,他的意思就是開(kāi)發(fā)板的機(jī)器碼,所謂機(jī)器碼就是uboot給我們的這個(gè)開(kāi)發(fā)板定義的唯一編號(hào)。

(2)機(jī)器碼的作用,就是uboot和linux內(nèi)核之間進(jìn)行比對(duì)和適配。

因?yàn)榍度胧皆O(shè)備的硬件都是定制化的,每一個(gè)開(kāi)發(fā)板的機(jī)器碼都不同,在這個(gè)開(kāi)發(fā)板上跑起來(lái)的內(nèi)核,同樣的內(nèi)核鏡像在另一個(gè)開(kāi)發(fā)板上就跑不起來(lái),不能通用,這就是嵌入式設(shè)備和PC機(jī)的不同。

(3)因此linux做了個(gè)設(shè)置:就是給每個(gè)開(kāi)發(fā)板做了一個(gè)唯一的編號(hào)(機(jī)器碼),然后在uboot、linux內(nèi)核中都有一個(gè)軟件維護(hù)機(jī)器碼的編號(hào),然后開(kāi)發(fā)板、uboot、linux三者比較適配機(jī)器碼,如果機(jī)器碼對(duì)上了就啟動(dòng),對(duì)不上就不啟動(dòng)(因?yàn)檐浖J(rèn)為,我和這個(gè)硬件不匹配,不能啟動(dòng)了把他給坑了,因?yàn)槿绻麤](méi)有這個(gè)機(jī)器碼和軟件去維護(hù),那么隨意的去的啟動(dòng),可能啟動(dòng)不起來(lái),如果啟動(dòng)起來(lái)了,就可能帶來(lái)無(wú)法預(yù)料的損失,金錢(qián),生命都有可能)。

(4)MACH_TYPE 在x210_sd.h中123行定義,值是2456,沒(méi)有特殊的含義,只是當(dāng)前開(kāi)發(fā)板對(duì)應(yīng)的編號(hào)。這個(gè)編號(hào)就代表x210這個(gè)開(kāi)發(fā)板的機(jī)器碼,將來(lái)這個(gè)開(kāi)發(fā)板上一直的linux內(nèi)核的機(jī)器碼也必須是這個(gè)2456的值。不然比對(duì)適配不上啟動(dòng)不起來(lái)。

(5)uboot中配置的這個(gè)機(jī)器碼,將來(lái)會(huì)在啟動(dòng)linux內(nèi)核的時(shí)候,作為傳參的一部分傳參給linux內(nèi)核,這樣linux內(nèi)核就知道uboot的機(jī)器碼了。內(nèi)核啟動(dòng)過(guò)程中,會(huì)將這個(gè)傳參過(guò)來(lái)的機(jī)器碼和自己的機(jī)器碼進(jìn)行想比對(duì)。

(6)在國(guó)內(nèi),這個(gè)機(jī)器碼我們可以隨便給,但是如果你是在公司中做產(chǎn)品的話,就要慎重了,盡量不要和別人的重了

2、gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);

(1)bi_boot_params,也是bd_info中的一個(gè)成員,含義是uboot給linuxkernel啟動(dòng)時(shí)傳參的內(nèi)存地址。

也就是說(shuō),uboot在給linux內(nèi)核傳遞參數(shù)的時(shí)候是這么傳的:uboot事先將準(zhǔn)備好的傳參(字符串。bootargs)放在內(nèi)存的一個(gè)地址處(bi_boot_params),然后uboot就啟動(dòng)了內(nèi)核(uboot在啟動(dòng)內(nèi)核時(shí)真正是通過(guò)r0、r1、r2、寄存器來(lái)直接傳遞參數(shù)的,這三個(gè)寄存器中,其中有一個(gè)就是這個(gè)bd_boot_params參數(shù),這個(gè)參數(shù)就是內(nèi)存的地址),然后內(nèi)核在啟動(dòng)的時(shí)候,通過(guò)讀取這個(gè)寄存器中的值就可以知道uboot傳遞過(guò)來(lái)的參數(shù)在內(nèi)存中哪里。然后自己去內(nèi)存的那個(gè)地方去找bootargs

(2)經(jīng)過(guò)計(jì)算知道這個(gè)bi_boot_params的值是0x30000100,所以可以知道uboot將這個(gè)內(nèi)存地址連續(xù)的一定空間內(nèi),作為給內(nèi)核傳參的地方了,所以我們?cè)趗boot的其他代碼中,就不去這個(gè)地址附近玩耍了,將來(lái)linux內(nèi)核啟動(dòng)的時(shí)候,也會(huì)通過(guò)讀取到那三個(gè)寄存器中的某一個(gè)后,知道了參數(shù)在內(nèi)存的哪個(gè)位置了,完了去那個(gè)內(nèi)存的位置去讀取相應(yīng)的uboot傳給linux內(nèi)核的參數(shù)。



6、6 start_armboot函數(shù)解析4

1、interrupt_init;

(1)這個(gè)函數(shù)指針指向的函數(shù),跳到這個(gè)函數(shù),發(fā)現(xiàn)代表和函數(shù)的名字含義不相關(guān)。實(shí)際上這個(gè)函數(shù)是用來(lái)初始化定時(shí)器的(使用的是timer4定時(shí)器)。

(2)在裸機(jī)中,我們知道,我們x210開(kāi)發(fā)板,一共有五個(gè)PWM定時(shí)器,中timer0-timer3這個(gè)四個(gè)PWM定時(shí)器有信號(hào)的輸出引腳,和timer4沒(méi)有信號(hào)的輸出引腳,無(wú)法輸出PWM波形。timer4在設(shè)計(jì)的時(shí)候就不是用來(lái)輸出PWM波形的(第一。沒(méi)有輸出引腳,第二、沒(méi)有TCMPB這個(gè)用來(lái)計(jì)算PWM波的寄存器),所以這個(gè)定時(shí)器在設(shè)計(jì)的時(shí)候是用來(lái)做計(jì)時(shí)用的(因?yàn)橛蠺CNTB這個(gè)用來(lái)存放計(jì)數(shù)的寄存器)。

(3)timer4用來(lái)做計(jì)時(shí)時(shí),需要用到2個(gè)寄存器,一個(gè)是TCNTB、一個(gè)是TCNTO。

TCNTB寄存器中,存的是一個(gè)數(shù),這個(gè)數(shù)代表著定時(shí)次數(shù)(每一次的時(shí)間是由時(shí)鐘決定的,實(shí)際上是由PCLKPSYS過(guò)來(lái)的時(shí)鐘頻率,經(jīng)過(guò)預(yù)分頻器和分頻器得到的最終給了這個(gè)定時(shí)器的一個(gè)時(shí)鐘頻率,這個(gè)寄存中的數(shù)乘以過(guò)來(lái)的時(shí)鐘頻率就是定時(shí)的時(shí)間長(zhǎng)度,每一個(gè)時(shí)鐘頻率過(guò)來(lái),這里面的數(shù)就會(huì)減1,當(dāng)減到0的時(shí)候,證明定時(shí)時(shí)間到了)。我們定時(shí)的時(shí)候只需要將我們要定的時(shí)間/這個(gè)定時(shí)器的時(shí)鐘記一次數(shù)的時(shí)間=要放在這個(gè)寄存中的數(shù)。將這個(gè)數(shù)放入這個(gè)TCNTB寄存器中即可。

TCNTO寄存器,這個(gè)寄存器是用來(lái)觀察TCNTB寄存器中的數(shù)的(實(shí)際上是TCNTB背后那個(gè)寄存器的數(shù)),我們通過(guò)TCNTO寄存器可以知道TCNTB中的數(shù)有沒(méi)有減到0,當(dāng)TCNTO讀取到TCNTB寄存器中數(shù)減到0后,就知道定時(shí)間到了。(因?yàn)橥ㄟ^(guò)TCNTB這個(gè)寄存器,我們把數(shù)放到里面后,是看不到這個(gè)寄存器中數(shù)在減的,這個(gè)寄存器相當(dāng)于一個(gè)buff。將數(shù)傳到這個(gè)寄存器的背后里面那個(gè)寄存中,那個(gè)寄存器中的數(shù)是會(huì)減的,但是我們看不到,那個(gè)寄存器是看不見(jiàn)的,所以要通過(guò)TCNTO這個(gè)寄存器去觀察那個(gè)寄存器中的數(shù)有沒(méi)有減到0,定時(shí)時(shí)間有沒(méi)有到)

(4)使用timer4定時(shí)器,因?yàn)檫@個(gè)timer4沒(méi)有中斷支持,不支持中斷的,所以當(dāng)時(shí)間到了以后,不會(huì)有中斷標(biāo)志位的改變,我們無(wú)法知道時(shí)間是否定時(shí)到了,所以要用TCNTO這個(gè)寄存器,通過(guò)讀,取里面的值,來(lái)知道timer4定時(shí)器的定時(shí)時(shí)間是否到了,并且我們的CPU不能在做其實(shí)事情的后,來(lái)知道定時(shí)器時(shí)間到了,因?yàn)椴恢С种袛?#xff0c;不會(huì)有中斷通知我們時(shí)間到了,所以我們只能用查詢的方式來(lái)讀取TCNTO寄存器中的值來(lái)知道定時(shí)時(shí)間是否到了,因?yàn)閠imer4的定時(shí)是無(wú)法實(shí)現(xiàn)微觀上的并行的,用它來(lái)定時(shí),只能不斷的輪詢查看這個(gè)寄存器,來(lái)知道時(shí)間是否到了。

(5)uboot中是用這個(gè)timer4來(lái)實(shí)現(xiàn)定時(shí)的,所以u(píng)boot在使用這個(gè)定時(shí)器定時(shí)的時(shí)候,是無(wú)法做其他的事情的。只能看著這個(gè)定時(shí)器的定時(shí)時(shí)間到(這不就是bootdelay的原理嘛,為什么uboot在啟動(dòng)的時(shí)候,bootdelay在倒計(jì)時(shí),在進(jìn)行等到,程序并沒(méi)有執(zhí)行其他的事情,因?yàn)檫@個(gè)定時(shí)器實(shí)現(xiàn)不了并且,定時(shí)時(shí)程序只能停留在那個(gè)位置,查看定時(shí)時(shí)間是否到了,所以bootdelay在實(shí)現(xiàn)定時(shí)的時(shí)候是用的輪詢的方式,檢查用戶有沒(méi)有輸入回車(chē)鍵,如果沒(méi)有繼續(xù)定時(shí))。

(6)interrupt_init;函數(shù)就是將timer4設(shè)置為定時(shí)10ms,用get_PCLK函數(shù)來(lái)獲取系統(tǒng)設(shè)置的PCLK_PSYS的時(shí)鐘頻率。然后進(jìn)行TCFG0,TCFG1分頻和預(yù)分頻,然后計(jì)算10ms應(yīng)該向TCNTB寄存器中寫(xiě)入的值。最后寫(xiě)入,完了設(shè)置是自動(dòng)刷新數(shù)到TCNTB背后的寄存器中,還是手動(dòng)刷新。最后在開(kāi)啟timer4。

總結(jié):學(xué)習(xí)這個(gè)函數(shù)的時(shí)候,重點(diǎn)要知道這種標(biāo)準(zhǔn)代碼和之前裸機(jī)代碼的區(qū)別,并且要學(xué)會(huì)怎么用結(jié)構(gòu)體封裝寄存器的方式,用結(jié)構(gòu)體的首地址經(jīng)過(guò)偏移去訪問(wèn)一類(lèi)的寄存器,因?yàn)橐活?lèi)寄存器的內(nèi)存地址是4字節(jié)連續(xù)向上生長(zhǎng)的,所以可以將某一類(lèi)寄存器的基地址給結(jié)構(gòu)體的指針,用結(jié)構(gòu)體指針的方式去訪問(wèn),因?yàn)榻Y(jié)構(gòu)體的是內(nèi)存對(duì)齊的,如果成員如果都是4個(gè)字節(jié)的話,那么成員和成員之前就相差了四個(gè)字節(jié)的內(nèi)存,正好一類(lèi)寄存器的內(nèi)存地址也是4字節(jié)4字節(jié)相連續(xù)的,還要學(xué)會(huì)用函數(shù)的方式來(lái)獲取設(shè)置值的方式。這樣可以讓代碼具有可移植性。


2、env_init

(1)看名字就知道是和環(huán)境變量的初始化有關(guān)。

(2)為什么有很多env_init函數(shù)呢?因?yàn)槲覀僽boot支持很多種啟動(dòng)介質(zhì)(比如,從iNand、norflash、nandflash、sd卡啟動(dòng)····很多種啟動(dòng)),當(dāng)我們用不同的啟動(dòng)介質(zhì)啟動(dòng)uboot的時(shí)候,從啟動(dòng)介質(zhì)操作env環(huán)境變量到內(nèi)存中,或者從內(nèi)存中存到啟動(dòng)介質(zhì)中的的方法都是不一樣的,操作方法都是不一樣的。因此uboot支持各種啟動(dòng)介質(zhì)中,env的存取操作方法,所以有很多env_xxx名字的.c文件。(當(dāng)我們編譯鏈接的時(shí)候,只會(huì)有一個(gè)env_xxx.c文件中的env_init函數(shù)被鏈接,因?yàn)樵趚210_sd.h頭文件中,已經(jīng)用宏定義的方式來(lái)配置了,有條件編譯的控制,所以只會(huì)有一個(gè)會(huì)起作用),對(duì)于我們x210iNand版本的開(kāi)發(fā)板來(lái)說(shuō),我們應(yīng)該看是env_movi.c這個(gè)文件中的env_init函數(shù)。

(3)這個(gè)env_init函數(shù)做的操作就是對(duì)我們內(nèi)存中維護(hù)的那一份uboot的env環(huán)境變量進(jìn)行了判定(判定內(nèi)存中有沒(méi)有環(huán)境變量),當(dāng)前因?yàn)槲覀冞€沒(méi)進(jìn)行環(huán)境從SD卡到DDR的relocate,因此當(dāng)前的環(huán)境變量是不能用。

(4)在start_armboot這個(gè)函數(shù)的776行調(diào)用env_relocate才將環(huán)境變量從SD卡中relocate到DDR中,那時(shí)才可以用。重定位之后,在需要環(huán)境變量的時(shí)候,才能從DDR中去使用環(huán)境變量,在重定位之前,我們只能從SD卡中去讀取環(huán)境變量。內(nèi)存中并沒(méi)有。



6、6 start_armboot函數(shù)解析5

1、init_baudrate

(1)看名字就知道是初始化串口通信波特率的,uboot在啟動(dòng)時(shí),就是通過(guò)串口和我們主機(jī)進(jìn)行通信的。通過(guò)串口形成的控制臺(tái)來(lái)控制我們的uboot。

(2)getenv_r函數(shù),讀取環(huán)境變量的值的。因?yàn)槲覀僽boot希望不改變?cè)创a的情況下,直接在uboot的命令行下,通過(guò)更改baudrate這個(gè)環(huán)境變量的值來(lái)設(shè)置uboot的串口波特率,所以我們將把baudrate弄成了環(huán)境變量。

int i = getenv_r ("baudrate", tmp, sizeof (tmp)); 這個(gè)函數(shù)的目的就是將baudrate這個(gè)字符串代表的環(huán)境變量的值讀取到tmp數(shù)組中,注意,這個(gè)環(huán)境變量的值是一個(gè)字符串,因?yàn)閠mp是一個(gè)字符數(shù)組,并且uboot界面中的東西都是字符串。函數(shù)執(zhí)行成功后返回一個(gè)大于1的數(shù)給i。

(3)int) simple_strtoul (tmp, NULL, 10) 這句話的意思就是tmp數(shù)組中放的把baudrate的值(字符串)轉(zhuǎn)換成數(shù)字。

(4)baudrate初始化的規(guī)則:首先先從環(huán)境變量中讀取這個(gè)環(huán)境變量的值放在tmp字符數(shù)組中,如果讀取成功,則將這個(gè)值轉(zhuǎn)換成數(shù)字給我們gd->baudrate和gd->bd->baudrate,如果讀取失敗了,就用x210_sd.h中的CONFIG_BAUDRATE這個(gè)宏的值(115200)給這兩個(gè)成員,作為波特率的值。從這里可以看出來(lái),環(huán)境變量的優(yōu)先級(jí)是很高的,環(huán)境變量如果有則用環(huán)境變量的,環(huán)境變量中沒(méi)有則在考慮用別的。

2、serial_init

(1)看名字知道是串口的初始化,疑問(wèn)(我們start.S中的lowlevel_init中,已經(jīng)初始化串口了,為什么這里還要初始化呢?和之前在匯編階段的串口初始化有什么不同嗎)。

(2)和我們這個(gè)開(kāi)發(fā)板有關(guān)系的串口初始化serial_init函數(shù)是在uboot/uboot_jiuding/cpu/s5pc11x/serial.c中。

(3)看代碼知道,我們這個(gè)函數(shù)什么都沒(méi)有做,緊緊就是用for循環(huán)延時(shí)了一小小下,所以就說(shuō)明我們?cè)趨R編階段初始化了串口,這里就沒(méi)有在進(jìn)行硬件寄存器的初始化了。



6、6 start_armboot函數(shù)解析6

1、console_init_f函數(shù)

(1)初始化控制臺(tái)的函數(shù),_f表示這個(gè)初始化是第一階段的初始化,_r表示是第二階段的初始化,因?yàn)橛械某跏蓟粋€(gè)階段初始化不完,所以會(huì)有第二階段的初始化。有時(shí)候初始化函數(shù)不能一次一起完成初始化,中間還會(huì)夾雜這一些代碼,所以需要將一個(gè)完成的初始化模塊的代碼分成了2個(gè)階段(我們uboot中的,start_armboot函數(shù)的第826行進(jìn)行了console_init_r第二階段的初始化)。

(2)console_init_f函數(shù)在uboot/common/console.c中,common表示這個(gè)文件中放的是與硬件無(wú)關(guān)的普遍使用的代碼。

(3)這個(gè)函數(shù)的功能僅僅是將我們gd->have_console設(shè)置為1了。


2、display_banner函數(shù)?

(1)這個(gè)函數(shù)是用來(lái)串口輸出顯示uboot的logo的。在uboot剛開(kāi)始啟動(dòng)的時(shí)候,OK是在lowlevel_init函數(shù)中初始化完串口后,執(zhí)行完畢lowlevel_init函數(shù)后打印的OK字樣,U-Boot 1.3.4 (Aug ?7 2013 - 14:53:08) for x210是uboot的logo,是在uboot啟動(dòng)的第二階段,start_armboot函數(shù)中調(diào)用disolay_banner函數(shù)顯示的。

(2)在這個(gè)函數(shù)中開(kāi)始用到了printf,但是我們的控制臺(tái)還沒(méi)有初始化好呢,上面的那個(gè)console_init_f初始化函數(shù)中,相當(dāng)于什么都沒(méi)有,那這里為什么可以進(jìn)行printf呢?

(3)我們追到printf這函,發(fā)現(xiàn)這個(gè)函數(shù)實(shí)際上是通過(guò)調(diào)用puts函數(shù),接著在puts函數(shù)中判斷我們控制臺(tái)是否初始化好,如果初始化好了則調(diào)用fputs函數(shù)(這樣發(fā)送才是控制臺(tái)的路),如果控制臺(tái)console沒(méi)有初始化則調(diào)用了serial_puts函數(shù),我們此時(shí)的控制臺(tái)console還沒(méi)有初始化成功,所以我們走到了這個(gè)函數(shù)serial_puts中,這個(gè)函數(shù)中又調(diào)用serial_putc函數(shù),最后在serial_putc中是通過(guò),將串口寄存器組的基地址給了封裝串口寄存器的結(jié)構(gòu)體的指針變量,通過(guò)這個(gè)指針變量將要通過(guò)串口發(fā)送的數(shù)據(jù)放到了串口的發(fā)送寄存器中。所以是跟控制臺(tái)無(wú)關(guān)的。

(4)我們的控制臺(tái)是用串口輸出,控制臺(tái)沒(méi)有初始化好,我們用的也是串口輸出,那么控制臺(tái)究竟有什么好處呢?什么是控制臺(tái)呢?實(shí)際上如果通過(guò)代碼的分析,會(huì)發(fā)現(xiàn),控制臺(tái)實(shí)際上是通過(guò)軟件虛擬出來(lái)的設(shè)備,這個(gè)設(shè)備有一套專(zhuān)用的通信函數(shù)(發(fā)送。接收···),控制臺(tái)的通信函數(shù),最終會(huì)映射到硬件的通信函數(shù)中來(lái)實(shí)現(xiàn)。也就是說(shuō)uboot的控制臺(tái)的通信函數(shù)實(shí)際上是映射到硬件的串口通信函數(shù)總的,也就是說(shuō)uboot用沒(méi)用控制臺(tái),其實(shí)并沒(méi)有本質(zhì)的差別。但是在別的體系中,控制臺(tái)的通信函數(shù)映射到硬件的通信函數(shù)中,可以用軟件來(lái)做一些優(yōu)化,比如說(shuō)是緩沖機(jī)制。(在操作系統(tǒng)中,用的就是這樣控制臺(tái)的方式,這樣可以將我們要發(fā)送的很多字節(jié)放到console的buff中進(jìn)行緩沖,如果不這樣的話,因?yàn)橛布拇诒旧硎且粋€(gè)字節(jié)一個(gè)字節(jié)的發(fā)送的,會(huì)很慢,而CPU的速度很快,這樣去將發(fā)送的東西放到控制臺(tái)的buff中去緩沖就會(huì)提供通信的速度。尤其是輸出到lcd的輸出設(shè)備上,如果沒(méi)有這個(gè)console的緩沖機(jī)制,那么輸出一個(gè)字節(jié)就要去刷新一次屏幕,如果有這個(gè)console的緩沖機(jī)制就可以將發(fā)送的東西放到console的緩沖中,一次刷到lcd屏幕上)

(5)printf函數(shù)要發(fā)送的是version_string這個(gè)字符數(shù)組。

const char version_string[] =

U_BOOT_VERSION" (" __DATE__ " - " __TIME__ ")"CONFIG_IDENT_STRING;

數(shù)組中的U_BOOT_VERSION在源代碼中是找不到定義的,這個(gè)變量數(shù)組中的U_BOOT_VERSION在源代碼中是找不到定義的,這個(gè)變量(字符串)實(shí)際上是在makefile中定義的makefile的365行,在配置編譯的時(shí)候才能生成,在include/version_autogenerated.h中的一個(gè)宏。CONFIG_IDENT_STRING是在x210_sd.h中的宏,配置的信息。DATE是編譯的日期,TIME是時(shí)間


3、print_cpuinfo函數(shù)

(1)打印CPU的信息。uboot啟動(dòng)過(guò)程中,

CPU: ?S5PV210@1000MHz(OK)

? ? ? ? APLL = 1000MHz, HclkMsys = 200MHz, PclkMsys = 100MHz

? ? ? ? MPLL = 667MHz, EPLL = 96MHz

? ? ? ? ? ? ? ? ? ? ? ?HclkDsys = 166MHz, PclkDsys = 83MHz

? ? ? ? ? ? ? ? ? ? ? ?HclkPsys = 133MHz, PclkPsys = 66MHz

? ? ? ? ? ? ? ? ? ? ? ?SCLKA2M ?= 200MHz

Serial = CLKUART?

這些信息就是print_cpuinfo打印出來(lái)的


6、6、start_armboot函數(shù)解析7

1、checkboard函數(shù)

(1)檢查當(dāng)前開(kāi)發(fā)板是哪一個(gè)開(kāi)發(fā)板,并且打印出來(lái)開(kāi)發(fā)板的名字

2、#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

(1)CONFIG_HARD_I2C是硬件的I2C(就是SOC中有I2C控制器),這個(gè)如果定義了,或者CONFIG_SOFT_I2C軟件I2C(就是兩個(gè)GPIO來(lái)模擬I2C的),兩個(gè)中一個(gè)成立就會(huì)執(zhí)行init_func_i2c函數(shù),看似我們的硬件的I2C的宏是被定義的,但是追蹤發(fā)現(xiàn)我們這個(gè)硬件I2C的宏沒(méi)有定義,被條件編譯給攔住了。

3、init_func_i2c函數(shù)

所以可以知道這個(gè)函數(shù)實(shí)際沒(méi)有被執(zhí)行的,因?yàn)橛布蛙浖腎2C宏都沒(méi)有定義。所以串口中也沒(méi)有打印I2C的信息。


4、uboot學(xué)習(xí)實(shí)踐

(1)對(duì)uboot源碼進(jìn)行修改(按照我們的需要進(jìn)行修改)、然后make distclean將之前編譯生成的東西刪除掉、然后在make x210_sd_config進(jìn)行配置、然后在make編譯

(2)之后編譯完成后生成的u-boot.bin,在linux下用dd命令的方法進(jìn)行燒寫(xiě)。

(3)燒寫(xiě)的過(guò)程:

第一步:進(jìn)入到uboot根目錄下的sd_fusing文件夾中,這里面有一個(gè)燒寫(xiě)腳本,我們當(dāng)前是不可以用的,因?yàn)橹熬哦κ窃?4位的Ubuntu中用的,而我們現(xiàn)在的Ubuntu是32位,file mkbll可以看出來(lái)

第二部:make clean 將原先的跟燒寫(xiě)腳本相關(guān)的東西刪掉,在make,因?yàn)槲覀兊腢buntu是32位的,所以這個(gè)時(shí)候make的時(shí)候就是我們能用的了。因?yàn)槭窃?2位Ubuntu中make的,在file mkbll可以看出來(lái)。

第三步:看sd_fusing.sh腳本中的燒錄uboot的路徑和燒錄到SD卡的扇區(qū)(和我們從SD卡重定位到DDR中的那個(gè)SD卡起始扇區(qū)位置一致,因?yàn)槲以创a中的扇區(qū)是哪里,我們?cè)跓龑?xiě)uboot到SD卡的時(shí)候就應(yīng)該燒寫(xiě)到哪里,不然將來(lái)從SD卡重定位到DDR中,就不能運(yùn)行了,因?yàn)樵创a我們是那么寫(xiě)的)對(duì)不對(duì)。

第四步:插上SD卡,Ubuntu中識(shí)別到之后執(zhí)行 ./sd_fusing.sh /dev/sdb 燒錄到SD卡中。


注意:破壞linux平臺(tái)下的SD0通道所連啟動(dòng)介質(zhì)中的bootloader:在linux平臺(tái)下輸入

busybox dd if=/dev/zero of=/dev/mmcblk0 bs=512 seek=1 count=1 conv=sync

在執(zhí)行sync確保破壞的前一塊數(shù)據(jù)有效。


6、6 start_armboot函數(shù)解析8

1、dram_init函數(shù)

(1)在匯編階段我們已經(jīng)初始化DDR了,不然也不能relocate到這里運(yùn)行了,這里初始化DDR是初始化DDR軟件相關(guān)的,就是初始化DDR的信息,將DDR相關(guān)的信息,比如DDR起始地址,DDR的大小的放到gd全局變量下的DDR相關(guān)的成員中。將DDR的相關(guān)信息記錄到這兩個(gè)成員中,以便于以后用到。這些相關(guān)的信息在x210_sd.h中可以進(jìn)行配置。

(2)從代碼來(lái)看,這個(gè)函數(shù)的目的就是初始化了gd->bd->bi_dram這個(gè)結(jié)構(gòu)體數(shù)組,內(nèi)容就是我們開(kāi)發(fā)板用的DDR的相關(guān)信息,起始地址和大小。

2、display_dram_config函數(shù)

(1)打印DDR的信息。打印顯示DDR的配置信息。

(2)啟動(dòng)過(guò)程中的 DRAM: ? ?512 MB 就是在這個(gè)函數(shù)中被打印顯示出來(lái)的。

(3)如何在uboot運(yùn)行的時(shí)候,知道DDR的配置信息呢,有一個(gè)命令 bdinfo,這個(gè)命令可以打印出來(lái)gd->bd->bi_dram成員中的配置信息,就是DDR的配置信息。并且這個(gè)bdinfo命令還可以打印出來(lái)所有硬件相關(guān)的全局變量的值


3、init_sequence函數(shù)指針數(shù)組中做的事情的總結(jié):

總結(jié):

(1)都是板級(jí)的初始化,gd全局變量中的成員的初始化,cpu_init是沒(méi)有的,網(wǎng)卡的初始化、機(jī)器碼的初始化(gd->bd->bi_arch_number)、內(nèi)存?zhèn)鲄⒌刂返某跏蓟?gd->bd->bi_boot_params)。

(2)定時(shí)器的初始化,使用的是定時(shí)器timer4,這個(gè)定時(shí)器沒(méi)有PWM輸出引腳,不支持中斷,所以在用這個(gè)定時(shí)器進(jìn)行定時(shí)的時(shí)候,我們CPU要不斷的進(jìn)行輪詢?nèi)ゲ檎夷莻€(gè)TCNTO寄存器中的值去觀察定時(shí)時(shí)間是否到了。bootdelay的延時(shí)就是由這個(gè)實(shí)現(xiàn)的,所以在bootdelay倒計(jì)時(shí)時(shí),uboot不干別的,只會(huì)看時(shí)間是否到了,和是否有回車(chē)鍵按下。

(3)看內(nèi)存中有無(wú)環(huán)境變量,有無(wú)的信息記錄在gd->env_valid成員中

(4)波特率的初始化,串口的初始在uboot匯編階段(uboot第一階段已經(jīng)初始化好了,否則OK也不會(huì)打印出來(lái)),串口初始化中沒(méi)有實(shí)質(zhì)的內(nèi)容。

(5)控制臺(tái)初始化的第一部分,將gd->console成員賦值我1

(6)顯示uboot的logo

(7)打印顯示CPU相關(guān)的信息

(8)檢查打印是哪個(gè)開(kāi)發(fā)板

(9)硬件和軟件的i2c都沒(méi)有進(jìn)行。

(10)將DDR的相關(guān)配置信息放在gd->bd->bi_dram的數(shù)據(jù)結(jié)構(gòu)中。

(11)打印DDR的大小


總結(jié):至此我們到了start_armboot的487行。


6、6 start_armboot函數(shù)解析9

1、CFG_NO_FLASH

(1)Nandflash和norflash都是flash,但是一般的情況下,flash指的norflash,nandflash簡(jiǎn)稱(chēng)為nand。

(2)491行,執(zhí)行的flash_init是norflash的初始化的,display_flash_config打印的是,norflash的大小。uboot啟動(dòng)信息中的flash 8M 就是在這里打印的。


2、CONFIG_VFD和CONFIG_LCD lcd顯示相關(guān)的,uboot中自帶的lcd顯示架構(gòu),我們沒(méi)有用這個(gè),我們?cè)诤竺嬷?#xff0c;自己添加了一個(gè)LCD

顯示的部分


3、mem_malloc_init函數(shù)

(1)用來(lái)初始化uboot的堆管理器的。

(2)在uboot中,開(kāi)始是沒(méi)有堆管理器的,但是uboot在這里自己維護(hù)了一段堆內(nèi)存,肯定有自己的一套代碼來(lái)管理這個(gè)堆內(nèi)存,有了這些東西以后,我們就可以使用malloc、free這套機(jī)制來(lái)申請(qǐng)內(nèi)存,釋放內(nèi)存了。

(3)我們?cè)贒DR中,建立的堆內(nèi)存大小是912KB,我們用代碼寫(xiě)出來(lái)的堆管理器,來(lái)管理DDR中的這段堆內(nèi)存,當(dāng)我們malloc申請(qǐng)堆內(nèi)存的時(shí)候,堆管理器就會(huì)去這個(gè)堆內(nèi)存中分配給我們。并且把那段申請(qǐng)的堆內(nèi)存的起始地址給我們。這個(gè)函數(shù)就是將堆內(nèi)存的部分內(nèi)容請(qǐng)0。


6、6 start_armboot函數(shù)解析10

1、接著的在start_armboot的599行-632行是我們x210相關(guān)的代碼。530-769行是開(kāi)發(fā)板獨(dú)有的初始化代碼

(1)mmc_initialize,開(kāi)發(fā)板MMC相關(guān)的初始化,初始化SOC內(nèi)部SD/MMC控制器的。函數(shù)在uboot/drivers/mmc/mmc.c中。

(2)uboot對(duì)硬件的操作,如網(wǎng)卡、SD卡···都是借用的linux內(nèi)核中的驅(qū)動(dòng)來(lái)實(shí)現(xiàn)的,uboot根目錄下的drivers文件夾,這里面放的就全是linux內(nèi)核中移植過(guò)來(lái)的各種驅(qū)動(dòng)文件

(3)mmc_initialize,所有使用MMC架構(gòu)的MMC卡,都是使用了這個(gè)函數(shù)來(lái)初始化MMC的。

(4)board_mmc_init這個(gè)函數(shù)在mmc_initialize返回了一個(gè) -1(這個(gè)函數(shù)開(kāi)發(fā)板級(jí)別的MMC初始化,意思就是說(shuō),如果我們SOC內(nèi)部沒(méi)MMC控制器的話,是開(kāi)發(fā)板上提供的MMC控制器,外部擴(kuò)展的,所用就用這個(gè)函數(shù),所以這個(gè)函數(shù)的優(yōu)先級(jí)要高于cpu_mmc_init),實(shí)際干活的函數(shù)是cpu_mmc_init(如果我們的SOC內(nèi)部有MMC控制器的話,就可以執(zhí)行這個(gè)函數(shù),顯然我們x210開(kāi)發(fā)板,內(nèi)部SOC上有MMC控制器,所以我們可以跳過(guò)board_mmc_init函數(shù),執(zhí)行cpu_mmc_init函數(shù))

(5)cpu_mmc_init函數(shù)在uboot/cpu/s5pc11x/cpu.c中,這里面又間接的調(diào)用了uboot/driver/mmc/s3c_mmcxxx.c的驅(qū)動(dòng)代碼來(lái)實(shí)現(xiàn)mmc控制器的初始化。


6、6 start_armboot函數(shù)解析11

1、start_armboot函數(shù)中到了770行

2、776行 env_relocate函數(shù)

(1)環(huán)境變量的重定位,將SD卡中的環(huán)境變量重定位到DDR中。

(2)環(huán)境變量到底重哪里來(lái)?雖然SD卡中我們?cè)趧澐值臅r(shí)候有些扇區(qū)是放我們的env的,但是我們?cè)跓浀臅r(shí)候,不曾燒錄env分區(qū),只燒錄了uboot分區(qū)、kernel分區(qū)、rootfs分區(qū),在SD卡的那些放env扇區(qū)的位置是沒(méi)有env的,因?yàn)闆](méi)有燒錄env過(guò)去,在第一次啟動(dòng)的時(shí)候。所以第一次uboot啟動(dòng)的時(shí)候,去SD卡的env分區(qū)讀取環(huán)境變量的時(shí)候是失敗(讀取回來(lái)后,做CRC校驗(yàn)時(shí)是失敗的),env分區(qū)是空的,所以第一次啟動(dòng)的時(shí)候,我們uboot選擇從uboot內(nèi)部代碼中設(shè)置的一套默認(rèn)的環(huán)境變量出發(fā)來(lái)使用(這套代碼的ENV,在第一次啟動(dòng)的時(shí)候,被弄到了DDR中,當(dāng)我們將內(nèi)存中的環(huán)境變量改變后,在saveenv后會(huì)被寫(xiě)入到sd卡的env分區(qū)中,當(dāng)我們下次啟動(dòng)的時(shí)候,在sd卡的env分區(qū)中就有了環(huán)境變量了,所以下次讀取的時(shí)候就成功了。也可能是uboot在第一次讀取內(nèi)部默認(rèn)的環(huán)境變量后,就直接寫(xiě)入到SD卡的env分區(qū)中了)。這就是為什么我們的SD卡燒錄好后,雖然沒(méi)有燒錄環(huán)境變量分區(qū),但是在第一次啟動(dòng)的時(shí)候,uboot界面中是可以看到環(huán)境變量的,并且在本次我們將改動(dòng)的環(huán)境變量改動(dòng)好,保存起來(lái)后,我們重新燒錄SD卡,但是還是沒(méi)有燒錄env分區(qū),但是下一次啟動(dòng)的時(shí)候,發(fā)現(xiàn)環(huán)境變量的值確是我們上一次更改后的環(huán)境變量的值。


總結(jié):第一次啟動(dòng)的時(shí)候,SD卡的env分區(qū)是沒(méi)有環(huán)境變量的,uboot代碼中的gd->env_valid 的值是等于0的,所以這個(gè)時(shí)候uboot讀取的是uboot代碼內(nèi)置的一套環(huán)境變量到DDR中,將這套環(huán)境變量通過(guò)代碼寫(xiě)入到SD卡的env分區(qū)中(或者我們改變這套環(huán)境變量的值后,saveenv保存到SD卡的env分區(qū)中),并且計(jì)算出CRC校驗(yàn),將gd->env_valid 值賦值為1,這樣在下次啟動(dòng)的時(shí)候,uboot重SD卡的env分區(qū)讀取環(huán)境變量的時(shí)候,CRC校驗(yàn)就可以通過(guò)了,gd->env_valid 值就不為0 了, 就可以成功的重SD卡的env分區(qū)中將環(huán)境變量讀取到DDR中了。

(3)真正的環(huán)境變量從SD卡重定位到DDR中的代碼是在env_relocate_spec函數(shù)中的movi_read_env函數(shù)



6、6 start_armboot函數(shù)解析12

1、IP地址。MAC地址的獲取

(1)IP地址的獲取,優(yōu)先從環(huán)境變量中獲取,來(lái)源于ipaddr這個(gè)環(huán)境變量,存放在gd->bd->bi_ipaddr中

(2)getenv_IPaddr ("ipaddr")函數(shù)獲取ipaddr這個(gè)環(huán)境變量的值,這個(gè)函數(shù)中的getenv用來(lái)獲取ip地址的值(注意是字符串格式的),string_to_ip函數(shù)用來(lái)將這個(gè)字符串格式的IP地址值轉(zhuǎn)換為正常的數(shù)字的IP地址值(點(diǎn)分的十進(jìn)制的數(shù)字),然后最后返回這個(gè)數(shù),存放在那個(gè)gd中的ip地址成員中。

(3)IP地址是由4個(gè)0-255的數(shù)字組成的,所以最簡(jiǎn)單的存儲(chǔ)格式,就是用一個(gè)unsigned int的大小32位。一個(gè)存儲(chǔ)單元。但是我們?nèi)四芸炊腎P地址是點(diǎn)分十進(jìn)制格式的(192.168.1.10),這兩種類(lèi)型可以進(jìn)行互相的轉(zhuǎn)換,一般給我們?nèi)丝吹脑捑褪寝D(zhuǎn)換成點(diǎn)分十進(jìn)制類(lèi)型的,給機(jī)器用就是unsigned int類(lèi)型的。所以get_env函數(shù)得到的IP地址應(yīng)該是unsigned int類(lèi)型的,因?yàn)檫@樣的在內(nèi)存中存儲(chǔ)是比較省內(nèi)存空間的,機(jī)器也好用。


2、MAC地址(網(wǎng)卡的硬件的地址)

(1)ethaddr這個(gè)環(huán)境變量中放,放的就是MAC地址,MAC地址是由6為0-255的數(shù)字組成的。

(2)環(huán)境變量的值,在第一次啟動(dòng)的時(shí)候,我們可以通過(guò)x210_sd.h中的宏配置來(lái)決定。在我們更改后,下一次啟動(dòng),環(huán)境變量的值以我們改動(dòng)過(guò)的在SD卡env分區(qū)中那一份環(huán)境變量為準(zhǔn)。


3、devices_init函數(shù)

(1)設(shè)備的初始化,指的是,開(kāi)發(fā)板上的硬件設(shè)備的初始化。這里初始化的是前面沒(méi)有初始化的一部分的設(shè)備初始化,只是把名字弄成了設(shè)備初始化的名字,放在這個(gè)函數(shù)中初始化的設(shè)備,都是驅(qū)動(dòng)設(shè)備。這個(gè)函數(shù)本來(lái)就是從驅(qū)動(dòng)框架中延生出來(lái)的。uboot中的很多設(shè)配的驅(qū)動(dòng)是直接移植linux內(nèi)核中的驅(qū)動(dòng)的。如網(wǎng)卡,SD卡··。

(2)這個(gè)函數(shù)在linux內(nèi)核中,就是集中的將一些設(shè)備進(jìn)行初始化。在uboot中也是如此,但是在uboot中,這個(gè)函數(shù)從linux內(nèi)核中拿過(guò)來(lái)后,有好多設(shè)備的初始化都沒(méi)有,幾乎一個(gè)都沒(méi)有用,因?yàn)樵谇懊娴碾A段,我們能用到的設(shè)備基本都初始化了,uboot畢竟用的少。


4、jumptable_init函數(shù)

(1)jumptable跳轉(zhuǎn)表,在uboot中,這個(gè)函數(shù)中的相關(guān)內(nèi)容,只是最為了左值,在整個(gè)uboot中,并沒(méi)有做右值被使用,所以也可以說(shuō)是空有起名的。本身是一個(gè)函數(shù)指針數(shù)組,里面記錄了很多函數(shù)的函數(shù)名(函數(shù)指針),看這個(gè)陣勢(shì),是要實(shí)現(xiàn)一個(gè)函數(shù)指針到真正函數(shù)的一個(gè)映射。將來(lái)通過(guò)跳轉(zhuǎn)表中的函數(shù)指針就可以執(zhí)行具體的函數(shù)。這個(gè)其實(shí)就是在用C語(yǔ)言實(shí)現(xiàn)面向?qū)ο蟮木幊?#xff0c;在linux內(nèi)核中有很多這種技巧,所以我們以前有一個(gè)說(shuō)法,C語(yǔ)言不是面向?qū)ο蟮?#xff0c;但是linux內(nèi)核是面向?qū)ο蟮摹?/p>

(2)通過(guò)分析返現(xiàn)這個(gè)跳轉(zhuǎn)表只是被賦值,但是從來(lái)沒(méi)有被引用,因此這個(gè)跳轉(zhuǎn)表,在uboot中,根本就沒(méi)有使用他


6、6 start_armboot函數(shù)解析13

1、console_init_r函數(shù)

(1)console_init_f函數(shù)是我們console第一階段的初始化,console_init_r函數(shù)是我們第二階段的初始化,實(shí)際上我們的console_init_f第一階段的初始化并沒(méi)有做什么,只是將gd->have_console賦值為了1.第二階段的初始化才進(jìn)行了實(shí)質(zhì)性的工作。

(2)uboot中有很多同名的函數(shù),我們SI進(jìn)行索引的時(shí)候,經(jīng)常搜到的是一些不對(duì)的函數(shù)。有是自動(dòng)索引找到的是錯(cuò)的,真正卻沒(méi)有找到,一般情況下是一搜有很多個(gè),一定要判斷是哪個(gè)。

(3)console函數(shù),其實(shí)是純軟件架構(gòu)方面的初始化(說(shuō)白了其實(shí)就是給我們console相關(guān)的數(shù)據(jù)結(jié)構(gòu)體中,填充一個(gè)些相應(yīng)的值),比如標(biāo)準(zhǔn)輸入配置成什么,標(biāo)準(zhǔn)錯(cuò)誤,標(biāo)準(zhǔn)輸出。

(4)但是實(shí)際上,我們uboot中console實(shí)際上并沒(méi)有干有意義的初始化,他就是直接調(diào)用的串口通信的函數(shù)進(jìn)行通信的,所以用不用consoleinit實(shí)際上并沒(méi)有什么分別。但是在linux內(nèi)核中console就可以提供緩沖機(jī)制,不用console就不能實(shí)現(xiàn)的東西。


2、enable_interrupts函數(shù)

(1)這個(gè)函數(shù)如果定義了那個(gè)USE_IRQ的宏,就是值的CPSR中的總中斷使能,但是我們因?yàn)槲覀兊膗boot中是用不到中斷的,所以我們沒(méi)有定義這個(gè)宏,看代碼知道,如果我們有定義這個(gè)宏那么這個(gè)函數(shù)就是一個(gè)空函數(shù),所以在這里我們的這個(gè)函數(shù)是一個(gè)空函數(shù)。

(2)因?yàn)槲覀兊膗boot中沒(méi)有使用中斷,因此沒(méi)有定義這個(gè)USE_IIRQ宏,因此我們這個(gè)函數(shù)是一個(gè)函數(shù),為的是在編譯的時(shí)候防止報(bào)錯(cuò),因?yàn)槟銢](méi)有定義這個(gè)宏,你還調(diào)用了這個(gè)函數(shù),那個(gè)宏既然沒(méi)有包含,這個(gè)函數(shù)的函數(shù)體就不會(huì)存在,所以u(píng)boot中為我們提供了一個(gè)空函數(shù),在沒(méi)有定義那個(gè)宏的時(shí)候,這個(gè)函數(shù)被調(diào)用的時(shí)候,執(zhí)行的就是那個(gè)空函數(shù)。

總結(jié):uboot中經(jīng)常出現(xiàn)一個(gè)情況,就是根據(jù)一個(gè)宏是否定義了來(lái)?xiàng)l件編譯決定是否調(diào)用這個(gè)函數(shù),這種情況有兩種解決方案來(lái)處理這種情況,方案一就是在調(diào)用函數(shù)處使用條用編譯,函數(shù)體實(shí)際完全提供代碼。方案二就是我們?cè)诤瘮?shù)處直接調(diào)用函數(shù),然后在函數(shù)體處,提供兩個(gè)函數(shù)體,一個(gè)是有實(shí)體的函數(shù),一個(gè)是空函數(shù),用宏定義的條件編譯來(lái)決定實(shí)際編譯時(shí)編譯哪個(gè)函數(shù)進(jìn)去。


3、loadaddr、bootfile兩個(gè)環(huán)境變量

(1)這兩個(gè)環(huán)境變量都是內(nèi)核啟動(dòng)有關(guān)的,在啟動(dòng)linux內(nèi)核時(shí)會(huì)參考這兩個(gè)環(huán)境變量的值,這里猜測(cè)應(yīng)該是用在tftpboot命令下,下載內(nèi)核鏡像到內(nèi)存地址中的兩個(gè)參數(shù),一個(gè)是要下載到內(nèi)存地址中的地址,一個(gè)是從tftp服務(wù)器中下載的內(nèi)核的名字,下載到內(nèi)存地址的內(nèi)核鏡像的名字。


4、board_late_init函數(shù)

(1)實(shí)際是空的,看名字是開(kāi)發(fā)板的初始化里比較晚期的初始化,就是前面該初始化的基本都初始化完了,剩下的一些必須放在后面初始化的就放在這里了,這也側(cè)面說(shuō)明了,開(kāi)發(fā)板級(jí)別的硬件軟件初始化告一段落了,基本結(jié)束了。對(duì)于x210來(lái)說(shuō),這個(gè)函數(shù)是空的,說(shuō)明都沒(méi)有干。


6、6 start_armboot函數(shù)解析14

1、eth_initialize函數(shù)

(1)看名字是網(wǎng)卡相關(guān)的初始化,在這之前的那個(gè)函數(shù)指針數(shù)組中的boar_init中,已經(jīng)初始化了網(wǎng)卡,那這里的這個(gè)是什么呢。

(2)實(shí)際上這個(gè)函數(shù)應(yīng)該是對(duì)網(wǎng)卡芯片本身的初始化,這里不是SOC與網(wǎng)卡芯片鏈接時(shí),我們SOC這邊的初始化。而是網(wǎng)卡芯片本身的初始化,

(3)對(duì)于x210來(lái)說(shuō),我們使用的是DM9000網(wǎng)卡來(lái)說(shuō),這個(gè)函數(shù)是空的,我們x210開(kāi)發(fā)板的網(wǎng)卡DM9000的初始化在前面的那個(gè)函數(shù)指針數(shù)組中的board_init中初始化的,網(wǎng)卡芯片的初始化在驅(qū)動(dòng)中,所以這個(gè)函數(shù)對(duì)于我們來(lái)說(shuō)是空的。


2、x210_preboot_init

(1)x210開(kāi)發(fā)板在啟動(dòng)起來(lái)之前的一些初始化,也就是說(shuō)開(kāi)發(fā)板在運(yùn)行uboot時(shí),進(jìn)入到uboot的命令行之前的初始化

(2)這個(gè)函數(shù)中調(diào)用了mpadfb_init函數(shù)

(3)mpadfb_init函數(shù),看名字是和LCD相關(guān)的,以及LCD屏幕上的LOGO顯示


3、check_menu_update_from_sd函數(shù)

(1)uboot啟動(dòng)的最后階段設(shè)計(jì)了一個(gè)自動(dòng)更新的功能,就是將我們要升級(jí)的鏡像放到SD卡的固定目錄中,然后開(kāi)機(jī)時(shí),在uboot啟動(dòng)的最后階段,檢查升級(jí)標(biāo)志(是一個(gè)按鍵,按鍵中標(biāo)志為"LEFT"的那個(gè)按鍵,這個(gè)按鍵如果按鍵按下,則表示Uupdate mode 如果沒(méi)有按下,則表示boot mode),如果是update mode,則uboot會(huì)自動(dòng)從SD卡中的那個(gè)固定目錄下讀取鏡像文件燒錄到iNand中,如果進(jìn)入到了boot mode中,則uboot不執(zhí)行update 直接啟動(dòng),正常運(yùn)行

(2)這種機(jī)制,能夠幫助我們快速的燒錄系統(tǒng),常用于量產(chǎn)時(shí),用SD卡對(duì)系統(tǒng)的燒錄和部署。


4、死循環(huán)

(1)start_armboot進(jìn)入了死循環(huán),去main_loop函數(shù)執(zhí)行了。

(2)解析器

(3)開(kāi)機(jī)倒數(shù)自動(dòng)執(zhí)行

(4)命令的補(bǔ)全



















轉(zhuǎn)載于:https://blog.51cto.com/whylinux/1898785

總結(jié)

以上是生活随笔為你收集整理的S5PV210-uboot源码分析-第二阶段的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。