一般而言,一個程式包括唯讀程式碼片段和可讀寫的資料區段。在ARM的整合式開發環境中,唯讀程式碼片段和常量被稱作RO段(ReadOnly);可讀寫的全域變數和靜態變數被稱作RW段(ReadWrite);RW段中要被初始化為零的變數被稱為ZI段(ZeroInit)。對於嵌入式系統而言,程式映象都是儲存在Flash儲存空間等一些非易失性器件中的,而在運行時,程式中的RW段必須重新裝載到可讀寫的RAM中。這就涉及到程式的載入時域和運行時域。簡單來說,程式的載入時域就是指程式燒入Flash中的狀態,運行時域是指程式執行時的狀態。對於比較簡單的情況,可以在ADS整合式開發環境的ARM LINKER選項中指定RO BASE和RW BASE,告知連接器RO和RW的串連基地址。對於複雜情況,如RO段被分成幾部分並映射到儲存空間的多個地方時,需要建立一個稱為“分布裝載描述檔案”的文字檔,通知連接器把程式的某一部分串連在儲存空間的某個地址空間。需要指出的是,分布裝載描述檔案中的定義要按照系統重新導向後的儲存空間分布情況進行。在引導程式完成初始化的任務後,應該把主程式轉移到RAM中去運行,以加快系統的運行速度。
什麼是arm的映像檔案,arm映像檔案其實就是可執行檔,包括bin或hex兩種格式,可以直接燒到rom裡執行。在axd調試過程中,我們調試的是axf檔案,其實這也是一種映像檔案,它只是在bin檔案中加了一個檔案頭和一些調試資訊。映像檔案一般由域組成,域最多由三個輸出段組成(RO,RW,ZI)組成,輸出段又由輸入段組成。所謂域,指的就是整個bin映像檔案所處在的地區,它又分為載入域和運行域。載入域就是映像檔案被靜態存放的工作區域,一般來說Flash裡的 整個bin檔案所在的地址空間就是載入域,當然在程式一般都不會放在 Flash裡執行,一般都會搬到SDRAM裡運行工作,它們在被搬到SDRAM裡工作所處的地址空間就是運行域。我們輸入的代碼,一般有代碼部分和資料部分,這就是所謂的輸入段,經過編譯後就變成了bin檔案中RO段和RW段,還有所謂的ZI段,這就是輸出段。對於載入域中的輸出段,一般來說RO段後面緊跟著RW段,RW段後面緊跟著ZI段。在運行域中這些輸出段並不連續,但RW和ZI一定是連著的。ZI段和RW段中的資料其實可以是RW屬性。
| Image$$RO$$Base| |Image$$RO$$Limit| |Image$$RW$$Base| |Image$$ZI$$Base| |Image$$ZI$$Limit|這幾個變數是編譯器通知的,我們在 makefile檔案中可以看到它們的值。它們指示了在運行域中各個輸出段所處的地址空間,| Image$$RO$$Base| 就是ro段在運行域中的起始地址,|Image$$RO$$Limit| 是ro段在運行域中的截止地址,其它依次類推。我們可以在linker的output中指定,在 simple模式中,ro base對應的就是| Image$$RO$$Base|,RW Base 對應的是|Image$$RW$$Base|,由於rw和zi相連,|Image$$ZI$$Base| 就等於|Image$$RW$$limit|。其它的值都是編譯器自動計算出來的。
下面是2410啟動代碼的搬運部分,我給出注釋:
BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|
adr r0, ResetEntry; ResetEntry是複位運行時域的起始地址,在boot nand中一般是0
ldr r2, BaseOfROM;
cmp r0, r2
ldreq r0, TopOfROM;TopOfROM=0x30001de0,程式碼片段地址的結束
beq InitRam
ldr r3, TopOfROM
;part 1,通過比較,將ro搬到sdram裡,搬到的目的地址從 | Image$$RO$$Base| 開始,到|Image$$RO$$Limit|結束
0
ldmia r0!, {r4-r7} ;將r0值作為地址處(ResetEntry)連續的4個32位元依次轉入r4,r5,r6,r7;同時r0增加。
stmia r2!, {r4-r7};將r4,r5,r6,r7的值依次存入|Image$$RO$$Base|地址處;同時r2增加。
cmp r2, r3
bcc %B0;
;part 2,搬rw段到sdram,目的地址從|Image$$RW$$Base| 開始,到|Image$$ZI$$Base|結束
sub r2, r2, r3;r2=0 ;上面拷貝時每次拷貝4個雙字(32位)大小,但是RO段大小不一定是4的整數倍,所以可能多拷貝了幾個雙字大小,r2-r3得到多拷貝的個數
sub r0, r0, r2 ;r0-(r2-r3)可以使r0指向在boot nand中RO的結束位址
InitRam ;carry rw to baseofBSS
ldr r2, BaseOfBSS ;TopOfROM=0x30001de0,baseofrw
ldr r3, BaseOfZero ;BaseOfZero=0x30001de0
0
cmp r2, r3
ldrcc r1, [r0], #4
strcc r1, [r2], #4
bcc %B0
;part 3,將sdram zi初始化為0,地址從|Image$$ZI$$Base|到|Image$$ZI$$Limit|
mov r0, #0;init 0
ldr r3, EndOfBSS;EndOfBSS=30001e40
1
cmp r2, r3
strcc r0, [r2], #4
bcc %B1
至此三個輸出段組成(RO,RW,ZI)拷貝和初始化結束。
************************************************************
RO段、RW段和ZI段 --Image$$??$$Limit 含義
ARM程式的組成
此處所說的“ARM程式”是指在ARM系統中正在執行的程式,而非儲存在ROM中的bin映像(image)檔案,這一點清注意區別。
一個ARM程式包含3部分:RO,RW和ZI
RO是程式中的指令和常量
RW是程式中的已初始設定變數
ZI是程式中的未初始化的變數
由以上3點說明可以理解為:
RO就是readonly,
RW就是read/write,
ZI就是zero
ARM映像檔案的組成
所謂ARM映像檔案就是指燒錄到ROM中的bin檔案,也成為image檔案。以下用Image檔案來稱呼它。
Image檔案包含了RO和RW資料。
之所以Image檔案不包含ZI資料,是因為ZI資料都是0,沒必要包含,只要程式運行之前將ZI資料所在的地區一律清零即可。包含進去反而浪費儲存空間。
Q:為什麼Image中必須包含RO和RW?
A:因為RO中的指令和常量以及RW中初始化過的變數是不能像ZI那樣“無中生有”的。
ARM程式的執行過程
從以上兩點可以知道,燒錄到ROM中的image檔案與實際運行時的ARM程式之間並不是完全一樣的。因此就有必要瞭解ARM程式是如何從ROM中的image到達實際運行狀態的。
實際上,ROM中的指令至少應該有這樣的功能:
1. 將RW從ROM中搬到RAM中,因為RW是變數,變數不能存在ROM中。
2. 將ZI所在的RAM地區全部清零,因為ZI地區並不在Image中,所以需要程式根據編譯器給出的ZI地址及大小來將相應得RAM地區清零。ZI中也是變數,同理:變數不能存在ROM中。在程式啟動並執行最初階段,RO中的指令完成了這兩項工作後C程式才能正常訪問變數。否則只能運行不含變數的代碼。
注意:如果一個變數被初始化為0,則該變數的處理方法與未初始化華變數一樣放在ZI地區。即:ARM C程式中,所有的未初始設定變數都會被自動初始化為0。RO包含了 Code和RO Data兩類資料。
總結:
1; C中的指令以及常量被編譯後是RO類型資料。
2; C中的未被初始化或初始化為0的變數編譯後是ZI類型資料。
3; C中的已被初始化成非0值的變數編譯後是RW類型資料。