研究arm也有2個(gè)月了,現(xiàn)在才感覺理解了arm在Nand flash模式下的啟動(dòng)過程,現(xiàn)在來這里記錄下來以表達(dá)我無比喜悅的心情。閑話少說,趁著還沒有忘記學(xué)習(xí)過程中的感受,直接進(jìn)入正題。
大家都知道,arm在Nand flash啟動(dòng)模式下啟動(dòng)時(shí)系統(tǒng)會(huì)將Nand flash中的前4KB代碼拷貝到SRAM(也就是Steppingstone中),由SRAM配置中斷向量表和完成Nand flash訪問的必要初始化,然后將Nand flash中的全部程序代碼拷貝到SDRAM中,最后由SRAM跳轉(zhuǎn)到SDRAM,然后程序就正常執(zhí)行了,這一過程看上去很簡單,但是真正理解這一過程還是不簡單的,盡管這樣,還是想告訴大家仔細(xì)理解還是比較容易理解這個(gè)過程的。如果您是ADS用戶,你省去了很多麻煩,但我不確定你省去的這些麻煩是否值得,這里介紹的是一種麻煩的方式,linux下的led程序。 代碼Head.s - .extern main
- .text
- .global _start
- _start:
- b reset
- reset:
- ldr sp,=4096
- bl disable_watch_dog
- bl clock_init
- bl memsetup
- bl copy_steppingstone_to_sdram
- ldr pc,=on_sdram
- on_sdram:
- msr cpsr_c,#0xdf
- ldr sp,=0x34000000
- ldr lr,=halt_loop
- ldr pc,=Main
- halt_loop:
- b halt_loop
我認(rèn)為,最需要理解的就是這段代碼了。先簡單的解釋下這段代碼。 (1)由于arm執(zhí)行reset之后pc會(huì)被清零,也就是reset中斷的中斷入口地址,因此,第一條指令就是b reset,跳轉(zhuǎn)到reset中斷處理函數(shù)。 (2)由于這里硬件配置都是C語言來完成的,而且我們的初始代碼比較小,完全不會(huì)超出4KB,因此可以在SRAM使用堆棧,故將SP設(shè)置為4096,以提供C運(yùn)行環(huán)境 (3)接下來的3個(gè)bl分別完成了關(guān)閉看門夠定時(shí)器,配置時(shí)鐘信號和存儲器配置的工作,第四個(gè)bl是將SRAM的4KB空間內(nèi)的代碼拷貝到了SDRAM中。 (4)接下來的ldr句將pc賦值為on_sdram的地址,實(shí)現(xiàn)了從SRAM到SDRAM的跳轉(zhuǎn)(下面會(huì)講為什么) (5)on_sdram中切換到了了系統(tǒng)模式然后分配了系統(tǒng)模式堆棧,將鏈接寄存器設(shè)置為halt_loop然后就跳轉(zhuǎn)到了SDRAM中的Main 上面的解釋只是大體上說明了代碼的意思,但是初學(xué)者總會(huì)有個(gè)疑問就是為什么ldr pc,=on_sdram就實(shí)現(xiàn)了從SRAM到SDRAM的跳轉(zhuǎn)呢?我被這個(gè)問題困擾了很長時(shí)間,到今天才想明白了這個(gè)問題,問題的關(guān)鍵就是相對跳轉(zhuǎn)和絕對跳轉(zhuǎn)的問題。為了說明這個(gè)問題我先解釋一下bl指令跟ldr指令在執(zhí)行過程中的區(qū)別。 B指令是相對跳轉(zhuǎn)指令,B 指令是最簡單的跳轉(zhuǎn)指令。一旦遇到一個(gè) B 指令,ARM 處理器將立即跳轉(zhuǎn)到給定的目標(biāo)地址,從那里繼續(xù)執(zhí)行。注意存儲在跳轉(zhuǎn)指令中的實(shí)際值是相對當(dāng)前PC 值的一個(gè)偏移量,而不是一個(gè)絕對地址,它的值由匯編器來計(jì)算(參考尋址方式中的相對尋址)。它是 24 位有符號數(shù),左移兩位后有符號擴(kuò)展為 32 位,表示的有效偏移為 26 位(前后32MB 的地址空間),同樣的,BL、BX都是相對跳轉(zhuǎn)。 LDR偽指令是將第二操作直接賦值給第一操作數(shù),當(dāng)執(zhí)行l(wèi)dr pc,=Main時(shí)是將Main的絕對地址賦值給了PC。 好了,知道這兩個(gè)指令的區(qū)別之后我們來看代碼是如何實(shí)現(xiàn)的從SRAM到SDRAM的跳轉(zhuǎn),首先需要指出,2440的開發(fā)板有SRAM和SDRAM,SRAM是從地址0x00000000開始的4KB內(nèi)存空間,SDRAM是從0x30000000開始的64M空間。 無論用ADS編譯還是用arm-linux-gcc編譯都會(huì)將代碼鏈接到0x30000000以后(也就是SDRAM中),ADS用戶可以通過查看ADS的工程配置,其中有項(xiàng)配置是RO起始地址是0x30000000,linux用戶在鏈接時(shí)需要用-T指定代碼的其實(shí)地址為0x30000000。 根據(jù)編譯原理,在鏈接階段程序中函數(shù)的地址就已經(jīng)確定了,也就是說函數(shù)的實(shí)際地址都在0x30000000之后,而程序的入口函數(shù)也就是這里的_start的地址就是0x300000000,其他函數(shù)都會(huì)大于這個(gè)數(shù)。 但是由于arm上電后系統(tǒng)會(huì)將Nand flash的前4KB代碼拷貝到SRAM中,也就是_start函數(shù)開始的4KB指令將被拷貝到SRAM中執(zhí)行,根據(jù)上例,在0x00000000處執(zhí)行的指令就是“b reset”,由于b是相對跳轉(zhuǎn),是在當(dāng)前pc值的基礎(chǔ)上加減某個(gè)數(shù)而跳轉(zhuǎn)到將要執(zhí)行的代碼處,因此,pc加減該數(shù)之后將到達(dá)reset函數(shù)的位置,故reset函數(shù)不能寫到4KB之外的空間中,否則arm的啟動(dòng)將會(huì)失敗,同樣的,接下來的幾個(gè)bl都是執(zhí)行的相對跳轉(zhuǎn),所以都相對當(dāng)前pc進(jìn)行的跳轉(zhuǎn),由于Nand flash總共只有64M的空間,所以相對跳轉(zhuǎn)是不可能會(huì)跳轉(zhuǎn)到SDRAM的,因?yàn)樘D(zhuǎn)到SDRAM至少要發(fā)生0x30000000的跳轉(zhuǎn),而這個(gè)相對位移遠(yuǎn)遠(yuǎn)大于64M。 而ldr pc,=Main是將Main函數(shù)的實(shí)際地址賦值給pc,而Main的實(shí)際地址是在0x30000000之后,這樣,就從SRAM跳轉(zhuǎn)到了SDRAM。 由于這個(gè)過程設(shè)計(jì)到了硬件格局和編譯原理,所以對一般人來講,理解起來確實(shí)比較困難,而且受本人水平限制,很多地方只能說是只可意會(huì)不可言傳,如果誤導(dǎo)了大家請大家諒解。當(dāng)然如果看到這里還不能理解arm的啟動(dòng)過程可以聯(lián)系QQ630905224來討論這個(gè)問題。下面是相關(guān)的其他代碼,我附在這里,2440addr.h沒有貼出,由于我也是使用arm自帶示例程序中的代碼,而且代碼有四千多行,多數(shù)地址是沒有用到的,如果有人需要就聯(lián)系我的QQ吧。其他的代碼如下 代碼Init.s- #include "2440addr.h"
- void disable_watch_dog(void);
- void clock_init(void);
- void memsetup(void);
- void copy_steppingstone_to_sdram(void);
- void inituart(void);
- void disable_watch_dog(void)
- {
- rWTCON = 0;
- }
- void clock_init(void)
- {
- rCLKDIVN = 0x03;
- /*
- *如果HDIVN非0,CPU的總線模式應(yīng)該從
- *“fast bus mode”變?yōu)椤癮synchronous
- *bus mode”
- */
- __asm__(
- "mrc p15, 0, r1, c1, c0, 0\n"
- "orr r1, r1, #0xc0000000\n"
- "mcr p15, 0, r1, c1, c0, 0\n"
- );
- rMPLLCON = (92<<12)|(1<<4)|(2);
- //rMPLLCON = ((0x5c<<12)|(0x01<<4)|(0x02));
- }
- void memsetup(void)
- {
- volatile unsigned long *p = (volatile unsigned long *)0x48000000;
- /* 這個(gè)函數(shù)之所以這樣賦值,而不是像前面的實(shí)驗(yàn)(比如mmu實(shí)驗(yàn))那樣將配置值
- * 寫在數(shù)組中,是因?yàn)橐伞蔽恢脽o關(guān)的代碼”,使得這個(gè)函數(shù)可以在被復(fù)制到
- * SDRAM之前就可以在steppingstone中運(yùn)行
- */
- /* 存儲控制器13個(gè)寄存器的值 */
- p[0] = 0x22011110; //BWSCON
- p[1] = 0x00000700; //BANKCON0
- p[2] = 0x00000700; //BANKCON1
- p[3] = 0x00000700; //BANKCON2
- p[4] = 0x00000700; //BANKCON3
- p[5] = 0x00000700; //BANKCON4
- p[6] = 0x00000700; //BANKCON5
- p[7] = 0x00018005; //BANKCON6
- p[8] = 0x00018005; //BANKCON7
- /* REFRESH,
- * HCLK=12MHz: 0x008C07A3,
- * HCLK=100MHz: 0x008C04F4
- */
- p[9] = 0x008C04F4;
- p[10] = 0x000000B1; //BANKSIZE
- p[11] = 0x00000030; //MRSRB6
- p[12] = 0x00000030; //MRSRB7
- }
- void copy_steppingstone_to_sdram(void)
- {
- unsigned int *pdwSrc = (unsigned int *)0;
- unsigned int *pdwDest = (unsigned int *)0x30000000;
- while (pdwSrc < (unsigned int *)4096)
- {
- *pdwDest = *pdwSrc;
- pdwDest++;
- pdwSrc++;
- }
- }
=======================================================================
一般而言,一個(gè)程序包括只讀的代碼段和可讀寫的數(shù)據(jù)段。在ARM的集成開發(fā)環(huán)境中,只讀的代碼段和常量被稱作RO段(ReadOnly);可讀寫的全局變量和靜態(tài)變量被稱作RW段(ReadWrite);RW段中要被初始化為零的變量被稱為ZI段(ZeroInit)。對于嵌入式系統(tǒng)而言,程序映象都是存儲在Flash存儲器等一些非易失性器件中的,而在運(yùn)行時(shí),程序中的RW段必須重新裝載到可讀寫的RAM中。這就涉及到程序的加載時(shí)域和運(yùn)行時(shí)域。簡單來說,程序的加載時(shí)域就是指程序燒入Flash中的狀態(tài),運(yùn)行時(shí)域是指程序執(zhí)行時(shí)的狀態(tài)。對于比較簡單的情況,可以在ADS集成開發(fā)環(huán)境的ARM LINKER選項(xiàng)中指定RO BASE和RW BASE,告知連接器RO和RW的連接基地址。對于復(fù)雜情況,如RO段被分成幾部分并映射到存儲空間的多個(gè)地方時(shí),需要?jiǎng)?chuàng)建一個(gè)稱為“分布裝載描述文件”的文本文件,通知連接器把程序的某一部分連接在存儲器的某個(gè)地址空間。需要指出的是,分布裝載描述文件中的定義要按照系統(tǒng)重定向后的存儲器分布情況進(jìn)行。在引導(dǎo)程序完成初始化的任務(wù)后,應(yīng)該把主程序轉(zhuǎn)移到RAM中去運(yùn)行,以加快系統(tǒng)的運(yùn)行速度。
什么是arm的映像文件,arm映像文件其實(shí)就是可執(zhí)行文件,包括bin或hex兩種格式,可以直接燒到rom里執(zhí)行。在axd調(diào)試過程中,我們調(diào)試的是axf文件,其實(shí)這也是一種映像文件,它只是在bin文件中加了一個(gè)文件頭和一些調(diào)試信息。映像文件一般由域組成,域最多由三個(gè)輸出段組成(RO,RW,ZI)組成,輸出段又由輸入段組成。所謂域,指的就是整個(gè)bin映像文件所處在的區(qū)域,它又分為加載域和運(yùn)行域。加載域就是映像文件被靜態(tài)存放的工作區(qū)域,一般來說flash里的 整個(gè)bin文件所在的地址空間就是加載域,當(dāng)然在程序一般都不會(huì)放在 flash里執(zhí)行,一般都會(huì)搬到sdram里運(yùn)行工作,它們在被搬到sdram里工作所處的地址空間就是運(yùn)行域。
我們輸入的代碼,一般有代碼部分和數(shù)據(jù)部分,這就是所謂的輸入段,經(jīng)過編譯后就變成了bin文件中ro段和rw段,還有所謂的zi段,這就是輸出段。對于加載域中的輸出段,一般來說ro段后面緊跟著rw段,rw段后面緊跟著zi段。在運(yùn)行域中這些輸出段并不連續(xù),但rw和zi一定是連著的。zi段和rw段中的數(shù)據(jù)其實(shí)可以是rw屬性。
| Image
Base| |Image
Limit| |Image
Base| |Image
Base| |Image
Limit|這幾個(gè)變量是編譯器通知的,我們在 makefile文件中可以看到它們的值。它們指示了在運(yùn)行域中各個(gè)輸出段所處的地址空間。| Image
Base| 就是ro段在運(yùn)行域中的起始地址,|Image
Limit| 是ro段在運(yùn)行域中的截止地址。其它依次類推。我們可以在linker的output中指定,在 simple模式中,ro base對應(yīng)的就是| Image
Base|,rw base 對應(yīng)的是|Image
Base|,由于rw和zi相連,|Image
Base| 就等于|Image
limit| .其它的值都是編譯器自動(dòng)計(jì)算出來的。
下面是2410啟動(dòng)代碼的搬運(yùn)部分,我給出注釋
BaseOfROM DCD |Image
Base|
TopOfROM DCD |Image
Limit|
BaseOfBSS DCD |Image
Base|
BaseOfZero DCD |Image
Base|
EndOfBSS DCD |Image
Limit|
adr r0, ResetEntry; ResetEntry是復(fù)位運(yùn)行時(shí)域的起始地址,在boot nand中一般是0
ldr r2, BaseOfROM;
cmp r0, r2
ldreq r0, TopOfROM;TopOfROM=0x30001de0,代碼段地址的結(jié)束
beq InitRam
ldr r3, TopOfROM
;part 1,通過比較,將ro搬到sdram里,搬到的目的地址從 | Image
Base| 開始,到|Image
Limit|結(jié)束
0
ldmia r0!, {r4-r7}
stmia r2!, {r4-r7}
cmp r2, r3
bcc %B0;
;part 2,搬rw段到sdram,目的地址從|Image
Base| 開始,到|Image
Base|結(jié)束
sub r2, r2, r3;r2=0
sub r0, r0, r2
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
Base|到|Image
Limit|
mov r0, #0;init 0
ldr r3, EndOfBSS;EndOfBSS=30001e40
1
cmp r2, r3
strcc r0, [r2], #4
bcc %B1
------------------------------------------------------------
一 概述
Scatter file (分散加載描述文件)用于armlink的輸入?yún)?shù),他指定映像文件內(nèi)部各區(qū)域的download與運(yùn)行時(shí)位置。Armlink將會(huì)根據(jù)scatter file生成一些區(qū)域相關(guān)的符號,他們是全局的供用戶建立運(yùn)行時(shí)環(huán)境時(shí)使用。(注意:當(dāng)使用了scatter file 時(shí)將不會(huì)生成以下符號 Image
Base, Image
Limit, Image
Base, Image
Limit, Image
Base, and Image
Limit)
二 什么時(shí)候使用scatter file
當(dāng)然首要的條件是你在利用ADS進(jìn)行項(xiàng)目開發(fā),下面我們看看更具體的一些情況。
1 存在復(fù)雜的地址映射:例如代碼和數(shù)據(jù)需要分開放在在多個(gè)區(qū)域。
2 存在多種存儲器類型:例如包含 Flash,ROM,SDRAM,快速SRAM。我們根據(jù)代碼與數(shù)據(jù)的特性把他們放在不同的存儲器中,比如中斷處理部分放在快速SRAM內(nèi)部來提高響應(yīng)速度,而把不常用到的代碼放到速度比較慢的Flash內(nèi)。
3 函數(shù)的地址固定定位:可以利用Scatter file實(shí)現(xiàn)把某個(gè)函數(shù)放在固定地址,而不管其應(yīng)用程序是否已經(jīng)改變或重新編譯。
4 利用符號確定堆與堆棧:
5 內(nèi)存映射的IO:采用scatter file可以實(shí)現(xiàn)把某個(gè)數(shù)據(jù)段放在精確的地指處。
因此對于嵌入式系統(tǒng)來說scatter file是必不可少的,因?yàn)榍度胧较到y(tǒng)采用了ROM,RAM,和內(nèi)存映射的IO。
|