版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、<p><b> 嵌</b></p><p><b> 入</b></p><p><b> 式</b></p><p><b> Linux</b></p><p><b> 課</b></p>&
2、lt;p><b> 程</b></p><p><b> 設(shè)</b></p><p><b> 計(jì)</b></p><p><b> 報(bào)</b></p><p><b> 告</b></p><p&
3、gt; 課題:嵌入式bootloader的移植分析</p><p><b> 一 概述:</b></p><p> BootLoader介紹</p><p> 系統(tǒng)上電之后,需要一段程序來(lái)進(jìn)行初始化:關(guān)閉WATCHDOG、改變系統(tǒng)時(shí)鐘、初始化存儲(chǔ)控制器、將更多的代碼復(fù)制到內(nèi)存中等。如果它能將操作系統(tǒng)內(nèi)核復(fù)制到內(nèi)存中運(yùn)行,無(wú)論從本地,比如
4、Flash;還是從遠(yuǎn)端,比如網(wǎng)絡(luò),就稱這段程序?yàn)锽ootloader。</p><p> Bootloader 是可以添加功能的,比如網(wǎng)絡(luò)功能。通過(guò)串口或網(wǎng)絡(luò)從PC下載燒寫文件、將存儲(chǔ)在Flash 上壓縮的文件解壓后再運(yùn)行等,這樣的Bootloader是比較強(qiáng)大的,也稱為Monitor。實(shí)際上,在最終產(chǎn)品中用戶并不需要使用這些功能,它們只是為了方便開(kāi)發(fā)。</p><p> Bootlo
5、ader 的實(shí)現(xiàn)非常依賴于具體硬件,在嵌入式系統(tǒng)中硬件配置千差萬(wàn)別,即使是相同的CPU,它的外設(shè)也可能不同,比如Flash不同,所以不可能有一個(gè)Bootloader 支持所有的CPU、所有的電路板。即使是支持CPU 架構(gòu)比較多的U-Boot,也不是一拿來(lái)就可以使用的,需要進(jìn)行一些移植。</p><p> 引導(dǎo)加載程序是系統(tǒng)加電后運(yùn)行的第一段軟件代碼。PC機(jī)中的引導(dǎo)加載程序由BIOS(其本質(zhì)就是一段固件程序)和位
6、于硬盤MBR中的OS BootLoader(比如,LILO和GRUB等)一起組成。BIOS在完成硬件檢測(cè)和資源分配后,將硬盤MBR中的BootLoader讀到系統(tǒng)的 RAM中,然后將控制權(quán)交給OS BootLoader。BootLoader的主要運(yùn)行任務(wù)就是將內(nèi)核映象從硬盤上讀到 RAM 中,然后跳轉(zhuǎn)到內(nèi)核的入口點(diǎn)去運(yùn)行,也即開(kāi)始啟動(dòng)操作系統(tǒng)。</p><p> 而在嵌入式系統(tǒng)中,通常并沒(méi)有像BIOS那樣的固件
7、程序(注,有的嵌入式CPU也會(huì)內(nèi)嵌一段短小的啟動(dòng)程序),因此整個(gè)系統(tǒng)的加載啟動(dòng)任務(wù)就完全由BootLoader來(lái)完成。比如在一個(gè)基于ARM7TDMI core的嵌入式系統(tǒng)中,系統(tǒng)在上電或復(fù)位時(shí)通常都從地址0x00000000處開(kāi)始執(zhí)行,而在這個(gè)地址處安排的通常就是系統(tǒng)的BootLoader程序。</p><p> 簡(jiǎn)單地說(shuō),Boot Loader 就是在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行的一段小程序。通過(guò)這段小程序,我們
8、可以初始化硬件設(shè)備、建立內(nèi)存空間的映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適的狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好正確的環(huán)境。在嵌入式系統(tǒng)中,通常并沒(méi)有像BIOS那樣的固件程序,因此整個(gè)系統(tǒng)的加載啟動(dòng)任務(wù)就完全由BootLoader來(lái)完成。</p><p> 二:系統(tǒng)總體設(shè)計(jì)和模塊結(jié)構(gòu)知識(shí):</p><p> (一) 嵌入式Linux軟件結(jié)構(gòu)與分布 在一般情況下嵌入式Linux系
9、統(tǒng)中的軟件主要分為以下及部分:(1)引導(dǎo)加載程序:其中包括內(nèi)部ROM中的固化啟動(dòng)代碼和Boot Loader兩部分。而這個(gè)內(nèi)部固化ROM是廠家在芯片生產(chǎn)時(shí)候固化的,作用基本上是引導(dǎo)Boot Loader。有的芯片比較復(fù)雜,比如Omap3,他在flash中沒(méi)有代碼的時(shí)候有許多啟動(dòng)方式:USB、UART或以太網(wǎng)等等。而S3C24x0則很簡(jiǎn)單,只有Norboot和Nandboot。(2)Linux kernel 和drivers。(3
10、)文件系統(tǒng)。包括根文件系統(tǒng)和建立于Flash內(nèi)存設(shè)備之上的文件系統(tǒng)(EXT4、UBI、CRAMFS等等)。它是提供管理系統(tǒng)的各種配置文件以及系統(tǒng)執(zhí)行用戶應(yīng)用程序的良好運(yùn)行環(huán)境的載體。(4)應(yīng)用程序。用戶自定義的應(yīng)用程序,存放于文件系統(tǒng)之中。在Flash 存儲(chǔ)器中,他們的 一般分布如下:</p><p> ?。ǘ︰-Boot主要目錄結(jié)構(gòu)- board 目標(biāo)板相關(guān)文件,主要包含SDRAM、FLASH驅(qū)動(dòng);-
11、 common 獨(dú)立于處理器體系結(jié)構(gòu)的通用代碼,如內(nèi)存大小探測(cè)與故障檢測(cè);- cpu 與處理器相關(guān)的文件。如mpc8xx子目錄下含串口、網(wǎng)口、LCD驅(qū)動(dòng)及中斷初始化等文件;- driver 通用設(shè)備驅(qū)動(dòng),如CFI FLASH驅(qū)動(dòng)(目前對(duì)INTEL FLASH支持較好)- doc U-Boot的說(shuō)明文檔;- examples可在U-Boot下運(yùn)行的示例程序;如hello_world.c,timer.c;- include U-B
12、oot頭文件;尤其configs子目錄下與目標(biāo)板相關(guān)的配置頭文件是移植過(guò)程中經(jīng)常要修改的文件;- lib_xxx 處理器體系相關(guān)的文件,如lib_ppc, lib_arm目錄分別包含與PowerPC、ARM體系結(jié)構(gòu)相關(guān)的文件;- net 與網(wǎng)絡(luò)功能相關(guān)的文件目錄,如bootp,nfs,tftp;- post 上電自檢文件目錄。尚有待于進(jìn)一步完善;- rtc RTC驅(qū)動(dòng)程序;- tools 用于創(chuàng)建U-Boot S-RECORD
13、和BIN鏡像文件</p><p> (三)U-Boot啟動(dòng)過(guò)程:</p><p> Stage1工作流程:</p><p> Stage1的代碼都是與平臺(tái)相關(guān)的,使用匯編語(yǔ)言編寫占用空間小而且執(zhí)行速度快。以ARM920為例,Stage1階段主要是設(shè)置各模式程序異常向量表,初始化處理器相關(guān)的關(guān)鍵寄存器以及系統(tǒng)內(nèi)存。Stage1負(fù)責(zé)建立Stage1階段使用的堆棧和
14、代碼段,然后復(fù)制Stage2階段的代碼到內(nèi)存。</p><p> Stage2工作流程:</p><p> Stage2階段一般包括:初始化Flash器件、檢測(cè)系統(tǒng)內(nèi)存映射、初始化網(wǎng)絡(luò)設(shè)備、進(jìn)入命令循環(huán),接收用戶從串口發(fā)送的命令然后進(jìn)行相應(yīng)的處理。Stage2使用C語(yǔ)言編寫,用于加載操作系統(tǒng)內(nèi)核,該階段主要是board.c中的start_armboot()函數(shù)實(shí)現(xiàn)。</p>
15、<p> 三 原理介紹與系統(tǒng)實(shí)現(xiàn):</p><p> (一)工作原理及代碼分析:</p><p> 1 第一階段(Stage 1) </p><p><b> 中斷向量表的設(shè)置 </b></p><p> .globl _start </p><p> _start
16、:b reset </p><p> ldr pc, _undefined_instruction </p><p> ldr pc, _software_interrupt </p><p> ldr pc, _prefetch_abort </p><p> ldr pc, _data_abort </p&g
17、t;<p> ldr pc, _not_used </p><p> ldr pc, _irq </p><p> ldr pc, _fiq </p><p> _undefined_instruction: .word undefined_instruction </p><p> _software_interru
18、pt: .word software_interrupt </p><p> _prefetch_abort: .word prefetch_abort </p><p> _data_abort: .word data_abort </p><p> _not_used: .word not_used </p>
19、<p> _irq: .word irq </p><p> _fiq: .word fiq </p><p> .balignl 16,0xdeadbeef </p><p> Start.s 文件一開(kāi)始,就定義了_start 的全局變量。也即,在別的文件,照 </p><p>
20、樣能引用這個(gè)_start 變量。這段代碼驗(yàn)證了我們之前學(xué)過(guò)的 arm 體系的理論知 </p><p> 識(shí):中斷向量表放在從 0x0 開(kāi)始的地方。其中,每個(gè)異常中斷的擺放次序,是 </p><p> 事先規(guī)定的。比如第一個(gè)必須是 reset 異常,第二個(gè)必須是未定義的指令異常等等。需要注意的是,在這里,我們也可以理解:為何系統(tǒng)一上電,會(huì)自動(dòng)運(yùn)行代碼。因?yàn)橄到y(tǒng)上電后,會(huì)從 0x0 地方取
21、指令,而 0x0 處放置的是 reset 標(biāo)簽,直接就跳去 reset 標(biāo)簽處去啟動(dòng)系統(tǒng)了。另外,這里使用了 ldr 指令。而 ldr 指令中的 label,分別用一個(gè).word偽操 作來(lái)定義。比如: </p><p> _undefined_instruction: .word undefined_instruction </p><p> 我們用 source insight 跟蹤
22、代碼后,發(fā)現(xiàn),undefined_instruction 在 </p><p> start.s 的后面給出了具體的操作,如下: </p><p> undefined_instruction: </p><p> get_bad_stack </p><p> bad_save_user_regs </p><p
23、> bl do_undefined_instruction </p><p> 在跳轉(zhuǎn)到中斷服務(wù)子程序之前,先有兩個(gè)宏代碼,一個(gè)是對(duì)stack的操作, </p><p> 一個(gè)是用戶regs的保存。然后才能跳轉(zhuǎn)如中斷服務(wù)子程序中執(zhí)行。讀者若不理 </p><p> 解進(jìn)入中斷之前做的“現(xiàn)場(chǎng)保護(hù)”,請(qǐng)參考《ARM體系結(jié)構(gòu)與編程》等相關(guān)書籍, </p
24、><p> 自然能獲得詳細(xì)的答案。 </p><p> 值得一提的是,當(dāng)發(fā)生異常時(shí),都將執(zhí)行 u-boot-1.2.0\cpu\arm920t\ </p><p> interrupts.c 中定義的中斷函數(shù)。也就是說(shuō),start.s 中要跳轉(zhuǎn)的這些中斷子程 </p><p> 序的代碼,均在 u-boot-1.2.0\cpu\arm92
25、0t\ interrupts.c 中定義。 </p><p> U-Boot存儲(chǔ)器映射定義 </p><p> 該代碼段主要是定義u-boot 需要使用的一些映射區(qū)的label,比如用戶堆區(qū)、用戶棧區(qū)、全局?jǐn)?shù)據(jù)結(jié)構(gòu)區(qū)等。</p><p> _TEXT_BASE: </p><p> .word TEXT_BASE </p&g
26、t;<p> .globl _armboot_start </p><p> _armboot_start: </p><p> .word _start </p><p> * These are defined in the board-specific linker script. </p><p><b&g
27、t; */ </b></p><p> .globl _bss_start </p><p> _bss_start: </p><p> .word __bss_start </p><p> .globl _bss_end </p><p> _bss_end: </p>&l
28、t;p> .word _end </p><p> #ifdef CONFIG_USE_IRQ </p><p> /* IRQ stack memory (calculated at run-time) */ </p><p> .globl IRQ_STACK_START </p><p> IRQ_STACK_START
29、: </p><p> .word 0x0badc0de </p><p> /* IRQ stack memory (calculated at run-time) */ </p><p> .globl FIQ_STACK_START </p><p> FIQ_STACK_START: </p><p&g
30、t; .word 0x0badc0de </p><p><b> #endif </b></p><p> 從上圖也可以清晰地發(fā)現(xiàn),堆和棧是有區(qū)別的。而且可以看到,用戶棧區(qū)是 </p><p> 向下遞減的,即地址減少的方向生長(zhǎng)。 </p><p> 上電后CPU為SVC模式 </p><
31、p><b> reset: </b></p><p><b> /* </b></p><p> * set the cpu to SVC32 mode </p><p><b> */ </b></p><p> mrs r0,cpsr </p&
32、gt;<p> bic r0,r0,#0x1f </p><p> orr r0,r0,#0xd3 </p><p> msr cpsr,r0 </p><p> 這是系統(tǒng)復(fù)位后執(zhí)行的“第一個(gè)代碼段”(嚴(yán)格來(lái)說(shuō)不是)。CPU 復(fù)位后,系 </p><p> 統(tǒng)會(huì)立即被設(shè)置成 SVC 模式。記得之前有網(wǎng)友發(fā)帖咨詢
33、這個(gè)問(wèn)題,問(wèn)系統(tǒng)復(fù)位 </p><p> 后,cpu 處于哪個(gè)處理器模式。這個(gè)代碼,就回答了這個(gè)問(wèn)題。 </p><p> 從這個(gè)代碼中,我們也可以得到一個(gè)對(duì)寄存器操作的經(jīng)驗(yàn):讀—修改--寫。 </p><p> 這里先把 cpsr 的值讀到r0 中,清除掉我們想修改的bit 位,然后用 orr 指令來(lái) </p><p> 保證其他
34、bit 位不被改動(dòng),并達(dá)到修改寄存器低 5 位值的目的。最后用 msr 指 </p><p> 令把 r0 的值給cpsr 寄存器達(dá)到我們的修改目的。 </p><p><b> 關(guān)閉看門狗 </b></p><p> #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) </
35、p><p> ldr r0, =pWTCON </p><p> mov r1, #0x0 </p><p> str r1, [r0] </p><p> 根據(jù) S3C2440 的 datasheet 文檔,系統(tǒng)啟動(dòng)后,看門狗寄存器是被使能 </p><p> 的,所以,如果不在預(yù)計(jì)的時(shí)間
36、內(nèi)“喂狗”,就有“被狗咬”的可能。別說(shuō)啥了, </p><p> 趕緊先喂狗。上面這段代碼即為喂狗代碼。u-boot 代碼編寫者把它放在 CPU </p><p> 上電修改 SVC 模式后的第一個(gè)代碼,是可以理解的。 </p><p> 這個(gè)代碼,也是修改寄存器的代碼,它的思路依舊是:讀—修改—寫。 </p><p> 實(shí)際上,u-
37、boot-1.2.0 代碼在喂狗代碼之前,還有一段代碼,如下: </p><p> #if defined(CONFIG_S3C2400) </p><p> # define pWTCON 0x15300000 </p><p> # define INTMSK 0x14400008 /* Interu
38、pt-Controller base </p><p> addresses */ </p><p> # define CLKDIVN 0x14800014 /* clock divisor register */ </p><p> #elif defined(CONFIG_S3C2410) </p&
39、gt;<p> # define pWTCON 0x53000000 /* 喂狗寄存器*/ </p><p> # define INTMSK 0x4A000008 /* Interupt-Controller base </p><p> addresses */ </p>
40、<p> # define INTSUBMSK 0x4A00001C </p><p> # define CLKDIVN 0x4C000014 /* clock divisor register */ </p><p><b> #endif </b></p><p>
41、 這是定義寄存器用的。比如根據(jù) S3C2440 的 datasheet 文檔,喂狗寄存 </p><p> 器 pWTCON 的寄存器地址是0x15300000,需要定義后才能使用。同理,這 </p><p> 里還定義了時(shí)鐘除數(shù)寄存器 CLKDIVN 和中斷掩碼的 INTMSK 寄存器的地址。 </p><p> 在后續(xù)代碼中會(huì)陸續(xù)用到。 </p&g
42、t;<p><b> 關(guān)掉中斷 </b></p><p> * mask all IRQs by setting all bits in the INTMR - default </p><p><b> */ </b></p><p> mov r1, #0xffffffff </p&
43、gt;<p> ldr r0, =INTMSK </p><p> str r1, [r0] </p><p> # if defined(CONFIG_S3C2410) </p><p> ldr r1, =0x3ff </p><p> ldr r0, =INTSUBMSK </p><p>
44、; str r1, [r0] </p><p><b> # endif </b></p><p> 從注釋可以看出此段代碼的作用:屏蔽掉所有的 irq 中斷。為了屏蔽這些中斷,我們只要把INTMSK 的所有的bit 位都置 1 即可。INTMSK 寄存器共 32bit 位,每個(gè) bit 對(duì)應(yīng)著不同的中斷源。事實(shí)上,筆者認(rèn)為這個(gè)代碼是多余的,只是 為了“心里更踏
45、實(shí)”而已。因?yàn)镾3C2440 的datasheet 文檔里明確指出,cpu 在復(fù)位的時(shí)候,這個(gè)寄存器的值就是 0XFFFFFFFF,以防止發(fā)生異常中斷。 </p><p> 3.2.6 修改時(shí)鐘除數(shù)寄存器 </p><p> /* FCLK:HCLK:PCLK = 1:2:4 */ </p><p> /* default FCLK is 120 MHz !
46、 */ </p><p> ldr r0, =CLKDIVN </p><p> mov r1, #0 /* 原先的值是 3 ,現(xiàn)在是 1:1:1*/ </p><p> str r1, [r0] </p><p> 在 u-boot-1.2.0 源碼中,給 CLKDIVN 寄存器賦值的是#0x3,表示 </p
47、><p> FCLK:HCLK:PCLK = 1:2:4,這里筆者將其比例改為 1:1:1,沒(méi)啥特殊的目的, </p><p> 調(diào)試代碼的時(shí)候試驗(yàn)用的,后來(lái)調(diào)試完畢,就沒(méi)有再修改了。 </p><p> 調(diào)用cpu_init_crit </p><p> #ifndef CONFIG_SKIP_LOWLEVEL_INIT </p
48、><p> bl cpu_init_crit </p><p><b> #endif </b></p><p> 此段代碼指明:若未定義 CONFIG_SKIP_LOWLEVEL_INIT ,就執(zhí)行 </p><p> cpu_init_crit。我們當(dāng)然不會(huì)跳過(guò)底層的初始化。因?yàn)長(zhǎng)OWLEVEL_INIT 會(huì)對(duì)
49、我 </p><p> 們的 SDRAM 進(jìn)行初始化,這對(duì)我們的cpu 是必要的。根據(jù)source insight 的索引, </p><p> 我們轉(zhuǎn)到了cpu_init_crit 的代碼中: </p><p> #ifndef CONFIG_SKIP_LOWLEVEL_INIT </p><p> cpu_init_crit: &
50、lt;/p><p><b> /* </b></p><p> * flush v4 I/D caches </p><p><b> */ </b></p><p> mov r0, #0 </p><p> mcr p15, 0, r0, c7, c
51、7, 0 /* flush v3/v4 cache */ </p><p> mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ </p><p><b> /* </b></p><p> * disable MMU stuff and caches </p><p>
52、;<b> */ </b></p><p> mrc p15, 0, r0, c1, c0, 0 </p><p> bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) </p><p> bic r0, r0, #0x00000087 @
53、 clear bits 7, 2:0 (B--- -CAM) </p><p> orr r0, r0, #0x00000002 @ set bit 2 (A) Align </p><p> orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache </p><p> mcr p15
54、, 0, r0, c1, c0, 0 </p><p><b> /* </b></p><p> * before relocating, we have to setup RAM timing </p><p> * because memory timing is board-dependend, you will </p&g
55、t;<p> * find a lowlevel_init.S in your board directory. </p><p><b> */ </b></p><p> mov ip, lr </p><p> bl lowlevel_init </p><p> mov
56、lr, ip </p><p> mov pc, lr </p><p> #endif /* CONFIG_SKIP_LOWLEVEL_INIT */ </p><p> 非常符合我們的思維,我們無(wú)效掉了指令cache和數(shù)據(jù)cache,并禁止MMU </p><p> 與 cache。為什么會(huì)有這一步呢?筆者曾經(jīng)深受cach
57、e 的傷害。在調(diào)試代碼的 </p><p> 時(shí)候,下載完修改的bin 文件后,如果只按復(fù)位鍵,而不關(guān)掉板子重新上電,就 </p><p> 會(huì)造成 cache 中可能殘留之前對(duì) cache 操作的數(shù)據(jù)。我們稱之為“臟數(shù)據(jù)”, </p><p> 它會(huì)映像我們的調(diào)試結(jié)果,造成假象。 </p><p> 當(dāng)然,在這里無(wú)效cache 和
58、MMU 肯定還有別的原因。比如在初始化階段, </p><p> 可以認(rèn)為我們只有一個(gè)任務(wù)在跑,沒(méi)有必要,也不允許使用地址變換。因此最好 </p><p> 應(yīng)該無(wú)效掉 MMU。 </p><p> 由于在cpu_init_cri 子程序中又一次調(diào)用子程序 lowlevel_init,因此,需 </p><p> 要事先保護(hù)好 lr
59、寄存器的內(nèi)容。當(dāng)返回時(shí)候,再恢復(fù)它。在進(jìn)入 lowlevel_init </p><p> 之前,有必要詳細(xì)說(shuō)一下 mov ip, lr,這個(gè)語(yǔ)句的ip。 </p><p> 為了使單獨(dú)編譯的C 語(yǔ)言程序和匯編程序之間能相互調(diào)用,必須為子程序間 </p><p> 的調(diào)用規(guī)定一定的規(guī)則。這就是ATPCS 規(guī)則。它規(guī)定了一些子程序間調(diào)用的基 </p&g
60、t;<p> 本規(guī)則。在寄存器的使用規(guī)則里,寄存器 R12 作用子程序間的 scratch 寄存器, </p><p> 記做 ip。mov ip, lr 語(yǔ)句的 ip 由此而來(lái)。筆者認(rèn)為,這里使用別的通用寄存器 </p><p> 來(lái)代替 ip,實(shí)現(xiàn)的功能也是一樣的。詳情請(qǐng)參考《ARM 體系結(jié)構(gòu)與編程》第 6 </p><p> 章 AT
61、PCS 介紹。 </p><p> 調(diào)用lowlevel_init </p><p> 這個(gè)函數(shù)在 u-boot-1.2.0\board\smdk2410\lowlevel_init.S 文件中。 </p><p> 這是對(duì) SDRAM 的初始化。 </p><p> _TEXT_BASE: </p><p>
62、 .word TEXT_BASE </p><p> .globl lowlevel_init </p><p> lowlevel_init: </p><p> /* memory control configuration */ </p><p> /* make r0 relative the current locat
63、ion so that it */ </p><p> /* reads SMRDATA out of FLASH rather than memory ! */ </p><p> ldr r0, =SMRDATA </p><p> ldr r1, _TEXT_BASE </p><p> sub r0, r0, r
64、1 </p><p> ldr r1, =BWSCON /* Bus Width Status Controller */ </p><p> add r2, r0, #13*4 </p><p><b> 0: </b></p><p> ldr r3, [r0], #4 <
65、;/p><p> str r3, [r1], #4 </p><p> cmp r2, r0 </p><p> bne 0b </p><p> /* everything is fine now */ </p><p> mov pc, lr </p><
66、;p><b> .ltorg </b></p><p> /* the literal pools origin */ </p><p><b> SMRDATA: </b></p><p><b> .word </b></p><p> (0+(B1_BWS
67、CON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+( </p><p> B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B </p><p> 7_BWSCON<<28)) </p><p><b
68、> .word </b></p><p> ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+ </p><p> (B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) </p><p><
69、b> .word </b></p><p> ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+ </p><p> (B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) </p><p><
70、;b> .word </b></p><p> ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+ </p><p> (B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) </p><p>&l
71、t;b> .word </b></p><p> ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+ </p><p> (B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) </p><p>&
72、lt;b> .word </b></p><p> ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+ </p><p> (B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) </p><p>
73、<b> .word </b></p><p> ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+ </p><p> (B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) </p><p>
74、; .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) </p><p> .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) </p><p><b> .word </b></p><p> ((REFEN<&l
75、t;23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<1 </p><p> 6)+REFCNT) </p><p> .word 0x32 </p><p> .word 0x30 </p><p> .word 0x30 </p>
76、<p> 該段代碼是對(duì) SDRAM 控制器相關(guān)的寄存器賦值,賦值過(guò)程中,采用了一個(gè) 巧妙的做法,把SDRAM 控制器初始化需要用到的 13個(gè)寄存器的值先保存在文 字池(literal pools)中,然后通過(guò) LDR 偽指令以及.ltorg 來(lái)訪問(wèn)這個(gè)文字池, 獲取寄存器的值賦值到對(duì)應(yīng)的寄存器地址中去。很多同學(xué)對(duì)此代碼的兩個(gè)地址不理解:SMRDATA 與_TEXT_BASE。不理 解這兩個(gè)地址相減之后,到底是一個(gè)什么值。為什
77、么要相減呢? 其實(shí)編譯器進(jìn)行編譯,是按照鏈接文件進(jìn)行的。也就是說(shuō),編譯的時(shí)候所有 的地址都是相對(duì)于這個(gè)TEXT_BASE 計(jì)算出來(lái)的。而我們的程序是存放在 Flash 中的,ARM 上電后,假設(shè)從 nandflash 模式啟動(dòng),那么它會(huì)把 Nandflash 的前 4K 加載到內(nèi)存中開(kāi)始運(yùn)行,當(dāng)然是從 0x0 這個(gè)地址開(kāi)始運(yùn)行,所以要求我們的 代碼在還沒(méi)有搬移到 TEXT_BASE (0x38f00000)這個(gè)位置以前是不能使用這 些
78、label 的,只能找到一個(gè)相對(duì)于0x0 的地址出來(lái),才能得到真正的數(shù)據(jù)。而且 這時(shí)候,我們編譯出來(lái)的 bin 文件是存放在 0x00000</p><p><b> 碼的搬移 </b></p><p> #ifndef CONFIG_SKIP_RELOCATE_UBOOT </p><p> relocate:
79、 /* relocate U-Boot to RAM */ </p><p> adr r0, _start /* r0 <- current position of code */ </p><p> ldr r1, _TEXT_BASE /* test if we run from
80、flash or RAM */ </p><p> cmp r0, r1 /* don't reloc during debug */ </p><p> beq stack_setup </p><p> ldr r2, _armboot_start </p><p&
81、gt; ldr r3, _bss_start </p><p> sub r2, r3, r2 /* r2 <- size of armboot */ </p><p> add r2, r0, r2 /* r2 <- source end address */ <
82、;/p><p> copy_loop: </p><p> ldmia r0!, {r3-r10} /* copy from source address [r0] */ </p><p> stmia r1!, {r3-r10} /* copy to target address [r1] */ &
83、lt;/p><p> cmp r0, r2 /* until source end addreee [r2] */ </p><p> ble copy_loop </p><p> #endif /* CONFIG_SKIP_RELOCATE_UBOOT */ </p><p> 在 SD
84、RAM 初始化完畢后,我們開(kāi)始搬移代碼,把代碼從原先的 0x0 開(kāi)始 </p><p> 的位置搬移到內(nèi)存中的適當(dāng)?shù)奈恢美^續(xù)執(zhí)行。為啥要搬移代碼?原因可能如下: </p><p> 1、運(yùn)行速度的考慮。 </p><p> flash 的讀寫速度遠(yuǎn)小于SDRAM 的讀寫速度,搬移到SDRAM 后,可提高 </p><p><b&g
85、t; 運(yùn)行效率。 </b></p><p><b> 2、空間的考慮。 </b></p><p> 如果是 nandflash 啟動(dòng)模式,那么只有4KB 的空間供用戶使用,實(shí)際的代 </p><p> 碼是永遠(yuǎn)大于 4KB 的,因此需要重新開(kāi)辟空間來(lái)進(jìn)行代碼的運(yùn)行工作。 </p><p> 一時(shí)間只
86、能想起這兩個(gè)原因。 有些版本的 u-boot 的代碼搬移用C 語(yǔ)言來(lái)實(shí)現(xiàn):bl CopyCode2Ram, </p><p> 也是可以的。因?yàn)檫@時(shí)候,我們完全搭建好了 C 環(huán)境。 在這段代碼中,還有一個(gè)子程序:beq stack_setup,用來(lái)設(shè)置棧空 </p><p> 間的,我們?cè)?/p>
87、下節(jié)中講解。 </p><p><b> ??臻g的設(shè)置 </b></p><p> stack_setup: </p><p> ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ </p><p> sub r0
88、, r0, #CFG_MALLOC_LEN /* malloc area */ </p><p> sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ </p><p> #ifdef CONFIG_USE_IRQ </p><p> sub
89、 r0, r0, </p><p> #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) </p><p><b> #endif </b></p><p> sub s
90、p, r0, #12 /* leave 3 words for abort-stack */ </p><p> 這段代碼是用來(lái)分配各個(gè)??臻g的。包括分配動(dòng)態(tài)內(nèi)存區(qū),全局?jǐn)?shù)據(jù)區(qū),IRQ </p><p> 和 FIQ 的??臻g等。 </p><p><b> BSS段的清零 </b></p>
91、<p> clear_bss: </p><p> ldr r0, _bss_start /* find start of bss segment */ </p><p> ldr r1, _bss_end /* stop here */ </
92、p><p> mov r2, #0x00000000 /* clear */ </p><p><b> clbss_l: </b></p><p> str r2, [r0] /* clear loop... */ </p><p
93、> add r0, r0, #4 </p><p> cmp r0, r1 </p><p> ble clbss_l </p><p> 本段代碼先設(shè)置了 BSS 段的起始地址與結(jié)束地址,然后循環(huán)清楚所有的 </p><p> BSS 段。至此,所有的 cpu 初始化工作(stage1 階段)已經(jīng)全部結(jié)束了。
94、后 </p><p> 面的代碼,將通過(guò) ldr pc, _start_armboot,進(jìn)入C 代碼執(zhí)行。這個(gè) C 入口 </p><p> 的函數(shù),是在 u-boot-1.1.6\lib_arm\board.c 文件中。它標(biāo)志著后續(xù)將全面 </p><p> 啟動(dòng)C 語(yǔ)言程序,同時(shí)它也是整個(gè) u-boot 的主函數(shù)。 </p&g
95、t;<p> 2 第二階段 stage2:C代碼分析 </p><p> 上節(jié)提到,start_armboot 函數(shù)不僅標(biāo)志著后續(xù)將全面啟動(dòng) C 語(yǔ)言程序, </p><p> 同時(shí)它也是整個(gè)u-boot 的主函數(shù)。那么該函數(shù)完成什么操作呢? </p><p> 為gd與bd分配空間 </p><p> /* Poin
96、ter is writable since we allocated a register for it */ </p><p> gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - </p><p> sizeof(gd_t)); </p><p> /* comp
97、iler optimization barrier needed for GCC >= 3.4 */ </p><p> __asm__ __volatile__("": : :"memory"); </p><p> memset ((void*)gd, 0, sizeof (gd_t)); </p><p>
98、gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); </p><p> memset (gd->bd, 0, sizeof (bd_t)); </p><p> 如同使用變量之前,需要聲明定義一樣,這里使用全局變量 gd 和 bd 之前, </p><p> 我們需要先設(shè)置它的地址,并用 memset 函數(shù)為它分
99、配合適的空間。u-boot </p><p> 的注釋告知我們,gd 和 bd 是一個(gè)可寫的指針,實(shí)際上不過(guò)是一個(gè)地址而已。 </p><p> 代碼中的這句話:__asm__ __volatile__("": : :"memory"); 目的就是 </p><p> 告訴編譯器內(nèi)存被修改過(guò)了 </p>&
100、lt;p> 執(zhí)行初始化列表函數(shù) </p><p> for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { </p><p> if ((*init_fnc_ptr)() != 0) { </p><p><b> hang (); </b></
101、p><p><b> } </b></p><p><b> } </b></p><p> 這是一個(gè)for 語(yǔ)句,卻完成了板子初始化列表函數(shù)的功能。我們先來(lái)看一下 </p><p> for 語(yǔ)句的初始值:init_sequence。用source insight 跟蹤后發(fā)現(xiàn),它是一個(gè) <
102、;/p><p><b> 指針數(shù)組: </b></p><p> init_fnc_t *init_sequence[] = { </p><p> cpu_init, /* basic cpu dependent setup */ </p><p> board_init, /* basic b
103、oard dependent setup */ </p><p> interrupt_init, /* set up exceptions */ </p><p> env_init, /* initialize environment */ </p><p> init_baudrate, /* initialze baudra
104、te settings */ </p><p> serial_init, /* serial communications setup */ </p><p> console_init_f, /* stage 1 init of console */ </p><p> display_banner, /* say tha
105、t we are here */ </p><p> #if defined(CONFIG_DISPLAY_CPUINFO) </p><p> print_cpuinfo, /* display cpu info (and speed) */ </p><p><b> #endif </b></p><p&
106、gt; #if defined(CONFIG_DISPLAY_BOARDINFO) </p><p> checkboard, /* display board info */ </p><p><b> #endif </b></p><p> dram_init, /* configure available R
107、AM banks */ </p><p> display_dram_config, </p><p><b> NULL, </b></p><p><b> }; </b></p><p> 指針數(shù)組的每個(gè)成員都對(duì)應(yīng)著一個(gè)函數(shù)名(函數(shù)指針),指向的是 init_fnc_t </p&
108、gt;<p> 類型的函數(shù)。For 語(yǔ)句每次都會(huì)判斷當(dāng)前的函數(shù)是不是 NULL,如果是,則跳出 </p><p> for 語(yǔ)句,完成當(dāng)前的板子初始化列表函數(shù)的功能。 </p><p> 可能大家都注意到了一個(gè)類型:init_fnc_t,它表示什么意思呢?我們看到 </p><p> 了在初始化列表函數(shù)之前,有一個(gè)新的數(shù)據(jù)類型,它是個(gè)typed
109、ef 語(yǔ)句: </p><p> typedef int (init_fnc_t) (void); </p><p> 可能有的同學(xué)對(duì)此不太理解,為啥非得用一個(gè) typedef 呢?筆者認(rèn)為,可以不 </p><p> 用 typedef,但是用了 init_fnc_t 后,團(tuán)隊(duì)中別的成員來(lái)看代碼的時(shí)候,會(huì)很 </p><p> 輕松
110、地知道,這是一個(gè)初始化(init)的函數(shù)(fnc),增加了代碼的可讀性。 </p><p> Cpu_init 函數(shù),并沒(méi)有做實(shí)質(zhì)性的工作,而且我們現(xiàn)在暫時(shí)沒(méi)有定義 </p><p> CONFIG_USE_IRQ,因此,代碼執(zhí)行到這里,直接就return 0; </p><p> Board_init 函數(shù),是初始化與硬件平臺(tái)有關(guān)的函數(shù)。它的工作很明顯:時(shí)
111、 </p><p> 鐘的設(shè)置,引腳 IO 口的設(shè)置,并且在這里把數(shù)據(jù)cache 和指令 cache 也打開(kāi) </p><p> 了。以上工作的完成,標(biāo)志著板子已經(jīng)準(zhǔn)備好工作了。當(dāng)然,考慮到可能系統(tǒng)會(huì) </p><p> 發(fā)生意外中斷,所以我們還需要初始化中斷,讓中斷也準(zhǔn)備好工作,因此 u-boot </p><p> 代碼中下一步就
112、開(kāi)始了中斷的初始化。 </p><p> interrupt_init 函數(shù),這實(shí)際上是定時(shí)器的中斷初始化。和我們之前的培 </p><p> 訓(xùn)課程相符的是,我們看到了中斷初始化中的那幾個(gè)熟悉的寄存器,首先是兩個(gè) </p><p> 配置寄存器 TCFG0 和 TCFG1。暈倒,怎么代碼中只有TCFG0 的設(shè)置,沒(méi)有 </p><p>
113、; TCFG1 的設(shè)置?很明顯,TCFG1 采用的是默認(rèn)值。然后配置寄存器的下載值, </p><p> 最后打開(kāi)啟動(dòng)開(kāi)關(guān),啟動(dòng)定時(shí)器工作。 </p><p> env_init 函數(shù),這是對(duì)我們板子的環(huán)境做出初始化配置。順便提一下,我 </p><p> 們修改的配置文件里,用的是 nand flash 來(lái)存放環(huán)境變量的值。 </p><
114、;p> #define CFG_ENV_IS_IN_NAND 1 </p><p> #define CFG_ENV_OFFSET 0x40000 </p><p> #define CFG_ENV_SIZE64 0xc000 /* Total Size of
115、 </p><p> Environment Sector */ </p><p> #define CFG_ENV_SIZE 0x20000 /* Total Size of </p><p> Environment Sector */ </p><p> 因此,我們?cè)谶M(jìn)入
116、u-boot 命令行之后,運(yùn)行的關(guān)于環(huán)境變量的操作,只要 </p><p> 它被保存,saveenv,肯定是save 在 nandflash 中的某個(gè)位置。 </p><p> init_baudrate 函數(shù),初始化波特率。我們心里要很明確,初始化波特率, </p><p> 目的只有一個(gè):讓串口打印調(diào)試信息。因此,下一個(gè)函數(shù),肯定是串口的初始化 <
117、/p><p> 函數(shù)。所以,我們可以在調(diào)試的時(shí)候,先算好波特率的值,直接賦值給 </p><p> gd->bd->bi_baudrate,注釋掉該函數(shù)中的其他代碼。調(diào)試完畢,再恢復(fù)出原 </p><p> 先的代碼。這樣,我們可以不用考慮別的因素導(dǎo)致串口打印不出信息。 </p><p> serial_init 函數(shù),串口的
118、初始化函數(shù)。這里調(diào)用了另一個(gè)函數(shù)來(lái)配置串口 </p><p> 寄存器:serial_setbrg();在這個(gè)函數(shù)中,我們看到了關(guān)于串口的 5 個(gè)寄存器的 </p><p> 配置。關(guān)于每個(gè)寄存器的更詳細(xì)的配置信息,請(qǐng)參考ARM 技術(shù)交流網(wǎng)推出的串 </p><p><b> 口課程講解部分。 </b></p><p&
119、gt; console_init_f 函數(shù),這個(gè)函數(shù)的功能只有一個(gè),就是指出我們目前是使 </p><p> 用串口的,因此有此句:gd->have_console = 1;然后直接返回 0。 </p><p> display_banner 函數(shù)。OK,現(xiàn)在串口初始化完畢,我們可以打印信息 </p><p> 了。這是u-boot 代碼中第一次打印
120、信息。我們可以在這里加入我們自己的代碼, </p><p> 比如筆者移植的u-boot 代碼中,就加入了如下“歡迎”的代碼信息: </p><p> printf ("\n\n"); </p><p> printf("********************************************* </p>
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫(kù)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 嵌入式課程設(shè)計(jì)--linux系統(tǒng)的裁剪與移植
- 基于嵌入式Linux的BootLoader的設(shè)計(jì)與應(yīng)用.pdf
- 嵌入式課程設(shè)計(jì)報(bào)告--qtopia移植
- 嵌入式課程設(shè)計(jì)
- 嵌入式課程設(shè)計(jì)
- 嵌入式Arm Linux系統(tǒng)的Bootloader的研究與實(shí)現(xiàn).pdf
- 嵌入式Linux實(shí)時(shí)性研究及Bootloader的設(shè)計(jì)與實(shí)現(xiàn).pdf
- 嵌入式課程設(shè)計(jì)
- 嵌入式課程設(shè)計(jì)
- 嵌入式課程設(shè)計(jì)
- 嵌入式課程設(shè)計(jì)
- MiniGUI在嵌入式Linux的移植.pdf
- 基于arm7的uclinux移植嵌入式課程設(shè)計(jì)
- LINUX嵌入式系統(tǒng)移植與應(yīng)用.pdf
- 嵌入式Linux系統(tǒng)移植與實(shí)現(xiàn).pdf
- 嵌入式Linux操作系統(tǒng)移植.pdf
- 嵌入式Linux的移植與實(shí)時(shí)優(yōu)化.pdf
- 嵌入式課程設(shè)計(jì)---嵌入式網(wǎng)絡(luò)視頻監(jiān)控系統(tǒng)
- 嵌入式課程設(shè)計(jì)報(bào)告---嵌入式文件鎖應(yīng)用
- 嵌入式系統(tǒng)BOOTLOADER的設(shè)計(jì)與實(shí)現(xiàn).pdf
評(píng)論
0/150
提交評(píng)論