国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
u-boot總結(jié)
1. 下面代碼是系統(tǒng)啟動(dòng)后U-boot上電后運(yùn)行的第一段代碼,他是什么意思?
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq

_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
他們是系統(tǒng)定義的異常,一上電程序跳轉(zhuǎn)到reset異常處執(zhí)行相應(yīng)的匯編指令,下面定義出的都是不同的異常,比如軟件發(fā)生軟中斷時(shí),CPU就會(huì)去執(zhí)行軟中斷的指令,這些異常中斷在CUP中地址是從0開始,每個(gè)異常占4個(gè)字節(jié)。
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
操作系統(tǒng)先注冊(cè)一個(gè)總的中斷,然后去查是由哪個(gè)中斷源產(chǎn)生的中斷,再去查用戶注冊(cè)的中斷表,查出來后就去執(zhí)行用戶定義的用戶中斷處理函數(shù)。

ldr pc, _undefined_instruction表示把_undefined_instruction存放的數(shù)值存放到pc指針上,_undefined_instruction: .word undefined_instruction表示未定義的這個(gè)異常是由.word來定義的,它表示定義一個(gè)字,一個(gè)32位的數(shù),.word后面的數(shù)表示把該標(biāo)識(shí)的編譯地址寫入當(dāng)前地址,標(biāo)識(shí)是不占用任何指令的。把標(biāo)識(shí)存放的數(shù)值copy到指針pc上面,那么標(biāo)識(shí)上存放的值是什么?是由.word undefined_instruction來指定的,pc就代表你運(yùn)行代碼的地址,她就實(shí)現(xiàn)了CPU要做一次跳轉(zhuǎn)時(shí)的工作。

什么是編譯地址?什么是運(yùn)行地址?
32位的處理器,它的每一條指令是4個(gè)字節(jié),以4個(gè)字節(jié)存儲(chǔ)順序,進(jìn)行順序執(zhí)行,CPU是順序執(zhí)行的,只要沒發(fā)生什么跳轉(zhuǎn),它會(huì)順序進(jìn)行執(zhí)行,編譯器會(huì)對(duì)每一條指令分配一個(gè)編譯地址,這是編譯器分配的,在編譯過程中分配的地址,我們稱之為編譯地址。
運(yùn)行地址是指,程序指令真正運(yùn)行的地址,是由用戶指定的,用戶將運(yùn)行地址燒錄到哪里,哪里就是運(yùn)行的地址。比如有一個(gè)指令的編譯地址是0x5,實(shí)際運(yùn)行的地址是0x200,如果用戶將指令燒到0x200上,那么這條指令的運(yùn)行地址就是0x200,當(dāng)編譯地址和運(yùn)行地址不同的時(shí)候會(huì)出現(xiàn)什么結(jié)果?結(jié)果是不能跳轉(zhuǎn),編譯后會(huì)產(chǎn)生跳轉(zhuǎn)地址,如果實(shí)際地址和編譯后產(chǎn)生的地址不相等,那么就不能跳轉(zhuǎn)。C語言編譯地址都希望把編譯地址和實(shí)際運(yùn)行地址放在一起的,但是匯編代碼因?yàn)椴恍枰鯟語言到匯編的轉(zhuǎn)換,可以認(rèn)為的去寫地址,所以直接寫的就是他的運(yùn)行地址,這就是為什么任何bootloader剛開始會(huì)有一段匯編代碼,因?yàn)槠鹗即a編譯地址和實(shí)際地址不相等,這段代碼和匯編無關(guān),跳轉(zhuǎn)用的運(yùn)行地址。編譯地址和運(yùn)行地址如何來算呢?假如有兩個(gè)編譯地址a=0x10,b=0x7,b的運(yùn)行地址是0x300,那么a的運(yùn)行地址就是b的運(yùn)行地址加上兩者編譯地址的差值,a-b=0x10-0x7=0x3,a的運(yùn)行地址就是0x300+0x3=0x303。
假設(shè)uboot上兩條指令的編譯地址為a=0x33000007和b=0x33000001,這兩條指令都落在bank6上,現(xiàn)在要計(jì)算出他們對(duì)應(yīng)的運(yùn)行地址,要找出運(yùn)行地址的始地址,這個(gè)是由用戶燒錄進(jìn)去的,假設(shè)運(yùn)行地址的首地址是0x0,則a的運(yùn)行地址
為0x7,b為0x1,就是這樣算出來的。

為什么要分配編譯地址?這樣做有什么好處,有什么作用?
比如在函數(shù)a中定義了函數(shù)b,當(dāng)執(zhí)行到函數(shù)b時(shí)要進(jìn)行指令跳轉(zhuǎn),要跳轉(zhuǎn)到b函數(shù)所對(duì)應(yīng)的起始地址上去,編譯時(shí),編譯器給每條指令都分配了編譯地址,如果編譯器已經(jīng)給分配了地址就可以直接進(jìn)行跳轉(zhuǎn),查找b函數(shù)跳轉(zhuǎn)指令所對(duì)應(yīng)的表,進(jìn)行直接跳轉(zhuǎn),因?yàn)橛袀€(gè)編譯地址和指令對(duì)應(yīng)的一個(gè)表,如果沒有分配,編譯器就查找不到這個(gè)跳轉(zhuǎn)地址,要進(jìn)行計(jì)算,非常麻煩。

什么是相對(duì)地址?
以NOR Flash為例,NOR Falsh是映射到bank0上面,SDRAM是映射到bank6上面,uboot和內(nèi)核最終是在SDRAM上面運(yùn)行,最開始我們是從Nor Flash的零地址開始往后燒錄,uboot中至少有一段代碼編譯地址和運(yùn)行地址是不一樣的,編譯uboot或內(nèi)核時(shí),都會(huì)將編譯地址放入到SDRAM中,他們最終都會(huì)在SDRAM中執(zhí)行,剛開始uboot在Nor Flash中運(yùn)行,運(yùn)行地址是一個(gè)低端地址,是bank0中的一個(gè)地址,但編譯地址是bank6中的地址,這樣就會(huì)導(dǎo)致絕對(duì)跳轉(zhuǎn)指令執(zhí)行的失敗,所以就引出了相對(duì)地址的概念。那么什么是相對(duì)地址呢?至少在bank0中uboot這段代碼要知道不能用b+編譯地址這樣的方法去跳轉(zhuǎn)指令,因?yàn)檫@段代碼的編譯地址和運(yùn)行地址不一樣,那如何去做呢?要去計(jì)算這個(gè)指令運(yùn)行的真實(shí)地址,計(jì)算出來后再做跳轉(zhuǎn),應(yīng)該是b+運(yùn)行地址,不能出現(xiàn)b+編譯地址,而是b+運(yùn)行地址,而運(yùn)行地址是算出來的。

_TEXT_BASE:
  .word TEXT_BASE //0x33F80000,在board/config.mk中
這段話表示,用戶告訴編譯器編譯地址的起始地址

Uboot啟動(dòng)流程
1. 設(shè)置CPU的啟動(dòng)模式
reset:
//設(shè)置CPU進(jìn)入管理模式 即設(shè)置相應(yīng)的CPSR程序狀態(tài)字
/* * set the cpu to SVC32 mode*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
2. 關(guān)閉看門狗,關(guān)閉中斷,所謂的喂狗是每隔一段時(shí)間給某個(gè)寄存器置位而已,在實(shí)際中會(huì)專門啟動(dòng)一個(gè)線程或進(jìn)程會(huì)專門喂狗,當(dāng)上層軟件出現(xiàn)故障時(shí)就會(huì)停止喂狗,停止喂狗之后,cpu會(huì)自動(dòng)復(fù)位,一般都在外部專門有一個(gè)看門狗,做一個(gè)外部的電路,不在cpu內(nèi)部使用看門狗,cpu內(nèi)部的看門狗是復(fù)位的cpu,當(dāng)開發(fā)板很復(fù)雜時(shí),有好幾個(gè)cpu時(shí),就不能完全讓板子復(fù)位,但我們通常都讓整個(gè)板子復(fù)位。看門狗每隔短時(shí)間就會(huì)喂狗,問題是在兩次喂狗之間的時(shí)間間隔內(nèi),運(yùn)行的代碼的時(shí)間是否夠用,兩次喂狗之間的代碼是否在兩次喂狗的時(shí)間延遲之內(nèi),如果在延遲之外的話,代碼還沒改完就又進(jìn)行喂狗,代碼永遠(yuǎn)也改不完。
//關(guān)閉看門狗的實(shí)際代碼
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON //將pwtcon寄存器地址賦給R0
mov r1, #0x0 //r1的內(nèi)容為0
str r1, [r0] //將R1的內(nèi)容送到Ro寄存器中去

3. 屏蔽所有中斷,為什么要關(guān)中斷?中斷處理中l(wèi)dr pc是將代碼的編譯地址放在了指針上,而這段時(shí)間還沒有搬移代碼,所以編譯地址上面沒有這個(gè)代碼,如果進(jìn)行跳轉(zhuǎn)就會(huì)跳轉(zhuǎn)到空指針上面
/* * mask all IRQs by setting all bits in the INTMR - default*/
mov r1, #0xffffffff //寄存器中的值全為11111111111111111111111111111111
ldr r0, =INTMSK //將管理中斷的寄存器地址賦給ro
str r1, [r0] //將全1的值賦給ro地址中的內(nèi)容
#if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif

3. 設(shè)置時(shí)鐘分頻,為什么要設(shè)置時(shí)鐘?起始可以不設(shè),系統(tǒng)能不能跑起來和頻率沒有任何關(guān)系,頻率的設(shè)置是要讓外圍的設(shè)備能承受所設(shè)置的頻率,如果頻率過高則會(huì)導(dǎo)致cpu操作外圍設(shè)備失敗
//設(shè)置CPU的頻率
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]

4. 做bank的設(shè)置
cpu_init_crit:

/* flush v4 I/D caches,關(guān)閉catch*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB *///協(xié)處理器 

//禁止MMU
/** disable MMU stuff and caches*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0 //關(guān)閉
為什么要關(guān)閉catch和MMU呢?catch和MMU是做什么用的?
Catch是cpu內(nèi)部的一個(gè)2級(jí)緩存,她的作用是將常用的數(shù)據(jù)和指令放在cpu內(nèi)部,MMU是用來做虛實(shí)地址轉(zhuǎn)換用的,我們的目的是設(shè)置控制的寄存器,寄存器都是實(shí)地址,如果既要開啟MMU又要做虛實(shí)地址轉(zhuǎn)換的話,中間還多一步,
先要把實(shí)地址轉(zhuǎn)換成虛地址,然后再做設(shè)置,但對(duì)uboot而言就是起到一個(gè)簡(jiǎn)單的初始化的作用和引導(dǎo)操作系統(tǒng),如果開啟MMU的話,很麻煩,也沒必要,所以關(guān)閉MMU.
  說道catch就必須提到一個(gè)關(guān)鍵字Volatile,以后在設(shè)置寄存器時(shí)會(huì)經(jīng)常遇到,他的本質(zhì)是告訴編譯器不要對(duì)我的代碼進(jìn)行優(yōu)化,優(yōu)化的過程是將常用的代碼取出來放到catch中,它沒有從實(shí)際的物理地址去取,它直接從cpu的緩存中去取,但常用的代碼就是為了感知一些常用變量的變化,如果正在取數(shù)據(jù)的時(shí)候發(fā)生跳變,那么就感覺不到變量的變化了,所以在這種情況下要用Volatile關(guān)鍵字告訴編譯器不要做優(yōu)化,每次從實(shí)際的物理地址中去取指令,這就是為什么關(guān)閉catch關(guān)閉MMU。但在C語言中是不會(huì)關(guān)閉catch和MMU的,會(huì)打開,如果編寫者要感知外界變化,或變化太快,從catch中取數(shù)據(jù)會(huì)有誤差,就加一個(gè)關(guān)鍵字Volatile。 

5. bl lowlevel_init下來初始化各個(gè)bank,把各個(gè)bank設(shè)置必須搞清楚,對(duì)以后移植復(fù)雜的uboot有很大幫助
6.設(shè)置完畢后拷貝uboot代碼到4k空間,拷貝完畢后執(zhí)行內(nèi)存中的uboot代碼
以上流程基本上適用于所有的bootloader,這就是step1階段
7. 問題:如果換一塊開發(fā)板有可能改哪些東西?
  首先,cpu的運(yùn)行模式,如果需要對(duì)cpu進(jìn)行設(shè)置那就設(shè)置,管看門狗,關(guān)中斷不用改,時(shí)鐘有可能要改,如果能正常使用則不用改,關(guān)閉catch和MMU不用改,設(shè)置bank有可能要改。最后一步拷貝時(shí)看地址會(huì)不會(huì)變,如果變化也要改,執(zhí)行內(nèi)存中代碼,地址有可能要改。
8. Nor Flash和Nand Flash本質(zhì)區(qū)別就在于是否進(jìn)行代碼拷貝,也就是下面代碼所表述:無論是Nor Flash還是Nand Flash,核心思想就是將uboot代碼搬運(yùn)到內(nèi)存中去運(yùn)行,但是沒有拷貝bss后面這段代碼,只拷貝bss前面的代碼,bss代碼是放置全局變量的。Bss段代碼是為了清零,拷貝過去再清零重復(fù)操作
//uboot代碼搬運(yùn)到RAM中去
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup

ldr r2, _armboot_start //flash中armboot_start的起始地址
ldr r3, _bss_start //uboot_bss的起始地址
sub r2, r3, r2 /* r2 <- size of armboot//uboot實(shí)際程序代碼的大小 */
add r2, r0, r2 /* r2 <- source end address */

copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

9. 看一下uboot.lds文件,在board/smdk2410目錄下面,uboot.lds是告訴編譯器這些段改怎么劃分,GUN編譯過的段,最基本的三個(gè)段是RO,RW,ZI,RO表示只讀,對(duì)應(yīng)于具體的指代碼段,RW是數(shù)據(jù)段,ZI是歸零段,就是全局變量的那段。Uboot代碼這么多,如何保證start.s會(huì)第一個(gè)執(zhí)行,編譯在最開始呢?就是通過uboot.lds鏈接文件進(jìn)行

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000; //起始地址

. = ALIGN(4); //4字節(jié)對(duì)齊
.text : //test指代碼段,上面3行標(biāo)識(shí)是不占用任何空間的
{
cpu/arm920t/start.o (.text) //這里把start.o放在第一位就表示把start.s編
譯時(shí)放到最開始,這就是為什么把uboot燒到起始地址上它肯定運(yùn)行的是start.s
*(.text)
}

. = ALIGN(4); //前面的 “.” 代表當(dāng)前值,是計(jì)算一個(gè)當(dāng)前的值,是計(jì)算上
面占用的整個(gè)空間,再加一個(gè)單元就表示它現(xiàn)在的位置
.rodata : { *(.rodata) }

. = ALIGN(4);
.data : { *(.data) }

. = ALIGN(4);
.got : { *(.got) }

. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

. = ALIGN(4);
__bss_start = .; //bss表示歸零段
.bss : { *(.bss) }
_end = .;

回憶一下GUN在編譯代碼時(shí)的四個(gè)步驟,1.預(yù)處理,2.編譯,3.匯編,4.鏈接,鏈接就做的是這個(gè)文件的動(dòng)作,就是將這些文件重新map一下分配地址。
最后運(yùn)行的是_start_armboot: .word start_armboot函數(shù)跳轉(zhuǎn)到step2的階段,這個(gè)函數(shù)是uboot中第一個(gè)C代碼,也是第一個(gè)在內(nèi)存中運(yùn)行的代碼
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
S3C6410之uboot回爐再造(1)start.S
s3c6410 uboot代碼分析《一》
BOOT閱讀筆記【轉(zhuǎn)貼】
P4080上電啟動(dòng)及uboot流程(轉(zhuǎn))
ARM上電后都干了哪些事uboot啟動(dòng)代碼詳解(uboot的入口是start鏈接地址0x00000000)
淺析嵌入式Linux系統(tǒng)的構(gòu)成和啟動(dòng)過程
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服