在之前的話 新年過去了,那麼久沒有好好學習,感覺好頹廢,現在就uboot的一些基礎問題做一些筆記,順便分享給大家,不過由於見識有限,如果有不足之處請多多指教。 位置無關?什麼意思?我們先瞭解一些基礎知識。。。。。 我們都知道我們寫的代碼最後是運行在記憶體(SDRAM或者SRAM,通常是SDRAM)中的 ...
在之前的話
新年過去了,那麼久沒有好好學習,感覺好頹廢,現在就uboot的一些基礎問題做一些筆記,順便分享給大家,不過由於見識有限,如果有不足之處請多多指教。
位置無關?什麼意思?我們先瞭解一些基礎知識。。。。。
我們都知道我們寫的代碼最後是運行在記憶體(SDRAM或者SRAM,通常是SDRAM)中的,但是在運行之前他們是保存在諸如nand、flash等非易失存儲設備中的,而這些存儲設備的地址要映射到CPU能夠尋找的地址上(一般映射在0X0地址上,這個後面詳細解釋),這樣才能得到要運行的代碼。而代碼要運行的記憶體(這裡就假設是SDRAM)也要映射到CPU上(肯定是和nand那些存儲器不一樣的地址,例如三星的2440的就映射到了0x30000000上),所以說存儲在nand或者flash的代碼最終得複製到記憶體上運行的。這裡就記住代碼存儲的地方(載入地址)和代碼要運行的地方(運行地址)是不一樣的。
現在說一下地址映射的問題。在學習ARM時,我們一般會遇到兩種啟動方式,一個是nand啟動,一個是nor啟動。這兩種啟動方式都是映射到0x0地址的,但是映射方式稍有不同。所以先瞭解一下這兩種介質的硬體特性:
nand:由於nand存儲器的硬體特性,代碼是無法在他上面運行的,他只有存儲代碼的功能。
nor:他不僅可以存儲代碼,還可以讓代碼運行,但是他只能進行讀,不能進行寫的操作,但畢竟是可以運行代碼啦。
所以nand啟動的話,如果沒有進行特殊手段,代碼是無法得到的,在ARM中,他是通過硬體自動把nand前面的4K代碼複製到一塊映射到0x0地址上的SRAM中的,記住這是硬體自動完成的。這樣4K代碼就在SRAM上了,我們知道SRAM是能運行代碼的(可讀可寫)。但是這個SRAM只有4K大小,要是存儲在nand的代碼不止4K,只在SRAM上面運行是無法完成代碼設定的所有功能的。為瞭解決這個問題,於是就在這4K的代碼中完成一個可以將nand的代碼複製到其他更大的記憶體上(這裡是SDRAM),這樣就可以運行所有代碼了。
nor啟動的話,由於nor可以運行代碼,所以就不用什麼SRAM了,直接把nor映射到0x0位置上就可了,如果nor空間夠大,甚至可以不用重定位代碼,只是nor無法進行寫功能,讀取的效率又沒SDRAM高,加上為了相容nand啟動(不用寫兩套代碼),也就跟nand啟動一樣只運行前面4K,剩下複製到SDRAM上再繼續運行。
問題又來了,我們知道代碼經過編譯,鏈接後才可以得到可以運行的代碼,而這代碼只有在鏈接時指定的位置上才能運行。對於2440,鏈接文件指定的位置一般是在SDRAM上面的,可是nand啟動時,前面的4K代碼卻在SRAM中運行了。
這會有什麼問題?我們知道編寫好的代碼的地址在鏈接時就已經安排好了。例如2440鏈接文件指定鏈接地址是0x30008000,那麼所有代碼地址就是從0x30008000開始的。而在啟動時,代碼卻是在0x0開始運行的,如果不進行特殊處理,肯定會有問題,比如我們調用一個函數,而這個函數地址是在SDRAM上的(鏈接時已經確定),並且這時代碼還沒複製到SDRAM上,也就是說代碼還在nand或者在4K的SRAM裡面,在SDRAM是找不到要調用的函數的,那樣就會無法執行這個函數。
如果在還在nand裡面肯定是沒辦法了,這個代碼是無法運行的必須廢棄,如果在4K的SRAM就有辦法了。於是就出現了位置無關指令:
位置無關/相關指令:
B BL ADR MOV ADD等,這些指令都是位置無關指令
LDR STR等指令是位置有關指令
位置無關什麼意思呢?
既然這個時候SDRAM還沒有代碼,那就不去那裡找了嘛,我們去別的地方找。別的地方是哪裡呢?這個時候代碼是運行在SRAM上的,處理器的PC寄存器也是指向這個地方的,有4K代碼也是在這個地方的。那我就不用鏈接指定的地址來尋找想要的代碼,我設計一些是與PC寄存器指向的位置有聯繫的定址指令來定址,這樣PC寄存器指向哪裡,我就在哪裡找我想要的代碼,而不是去鏈接指定的地址去找。
位置無關指令就是這樣的指令,他不去鏈接指定位置尋找代碼,而是在PC寄存器指向的位置相對它前後32M的範圍尋找代碼,也就是說只要我想要調用的代碼在PC指向位置前後的32M範圍內,就能找到(這是硬體完成的,我們知道位置無關指令就是這個意思就行)。當然,我們的SRAM只有4K,所以尋找的範圍肯定就只能在這4K裡面才行了,超過了也是找不到的。所以要求我們的重定位代碼必須在這4K裡面,必要的硬體初始化代碼,記憶體初始化代碼,nand初始化代碼等等,必須在4K以內完成,否則就會出問題。所以位置無關指令的定址是基於當前PC寄存器位置來定址的。
下麵根據uboot的代碼進行講解
1.記憶體控制器初始化代碼
_TEXT_BASE:
.word TEXT_BASE /*根據鏈接地址得知這是0x30000000*/
.globl lowlevel_init /*定義lowlevel_init為全局函數*/
lowlevel_init:
ldr r0, =SMRDATA /*鏈接後,SMRDATA是在SDRAM上某個位置的,但是這時候SDRAM還沒任何代碼,代碼現在還在SRAM上,也就說ldr指令是位置相關指令*/
ldr r1, _TEXT_BASE /*0x30000000*/
sub r0, r0, r1 /* SMRDATA減 _TEXT_BASE就是13個寄存器在SRAM上的地址 */
ldr r1, =BWSCON /* R1指向記憶體控制器的寄存器地址上 */
add r2, r0, #13*4 /*有13個寄存器,每個32位寬度*/
0:
ldr r3, [r0], #4 /*將13個寄存器的值逐一賦值給對應的寄存器*/
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA: /* 下麵是13個寄存器的值 */
.word … …
.word … …
… …
2.nor啟動的重定位代碼
adr r0, _start /*adr是位置無關指令,因為nor啟動,這時第一條指令被映射到nor上的0x0位置,所以R0=0x0,如果是RAM啟動,那_start就是存在0x30000000了 */
ldr r1, _TEXT_BASE /* 位置相關指令,R1=0x30000000 */
/* 判斷U-Boot是否是下載到RAM中運行,若是,則不用 再複製到RAM中了,這種情況通常在調試U-Boot時才發生 */
cmp r0, r1 /*_start等於_TEXT_BASE說明是下載到RAM中運行 */
beq stack_setup
/* 以下直到nand_boot標號前都是NOR Flash啟動的代碼 */
ldr r2, _armboot_start /*這裡其實就是_start的地址*/
ldr r3, _bss_start
sub r2, r3, r2 /* 得到代碼大小 */
add r2, r0, r2 /* 得到代碼結束地址 */
/* 搬運U-Boot自身到RAM中*/
copy_loop:
ldmia r0!, {r3-r10} /* 從地址為[r0]的NOR Flash中讀入8個字的數據 */
stmia r1!, {r3-r10} /* 將r3至r10寄存器的數據複製給地址為[r1]的記憶體 */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
b stack_setup /* 跳過NAND Flash啟動的代碼 */
以上就是位置無關代碼的解析了,uboot在運行第二階段之前,絕大部分的指令都是位置無關指令,這樣才能保證代碼能夠運行。