對 S3C2410 啓動代碼內數據複製過程的分析

對 S3C2410 啓動代碼內數據複製過程的分析     
本文若有錯誤之處,歡迎來信指正。
S3C2410 啓動後先進行一些必要的設置,如關 WatchDog,設置 PLL 與時鐘,配置 SDRAM,初始化堆棧等,
網上有很多分析啓動代碼的文章,本文不打算再進行說明。 
本文分從 NOR Flash 與 NAND Flash 啓動兩種情況分別進行分析。首先要知道 NOR Flash 是 XIP 的,如
果從 NOR 啓動,代碼首先在 NOR Flash 內執行,考慮到運行速度,我們要把代碼要複製到 SDRAM 內去執行。
當系統被設置成從 NAND FALSH 啓動(使用 OM[1:0]引腳的電平來控制)時,由於其自身的特點,NAND 
Flash 不具備運行程序的功能,程序不能在 NAND Flash 內執行,CPU 會自動把 NAND 內頭 4K 代碼加載到 CPU
的內部 SRAM(StepingStone 墊腳石)中,然後把 StepingStone 映射到 CPU 地址空間的 0 地址處(BANK0),因
此 CPU 會從 StepingStone 的 0 地址處取指令並執行。因爲程序一般都會大於 4K,我們必須把代碼複製到
SDRAM 中去執行。綜上所述,不管是從 NOR 的啓動還是從 NAND 啓動,都要把代碼複製到 SDRAM 中去執行,
這個複製過程要在頭 4K 代碼中完成。此後這 4KB 的 SRAM 還可以用作其它用途。 
另外必須注意到的是 NOR 的啓動與 NAND 啓動的程序入口點 ResetEntry 都爲 0,CPU 在加電後會去 0x0
處取指執行,如下圖所示。 
一、我們先看從 NOR Flash 啓動的情況: 
下面這段代碼進行復制前的判斷,因爲從 NAND Flash 與從 NOR Flash 啓動的複製過程是大不相同的。 
;/************************************************************************************** 
;BWSCON 的[2:1]反映了外部引腳 OM[1:0]:若 OM[1:0] != 00, 從 NOR FLash 啓動或直接在內存運行; 
;若 OM[1:0]==00,則爲從 Nand Flash 啓動 
;**************************************************************************************/ 
 ldr r0, =BWSCON  ldr r0, [r0] 
 ands r0, r0, #6   
 bne copy_proc_beg  ;若從 NOR 啓動,跳轉到標號 copy_proc_beg
 adr r0, ResetEntry  ;從 NOR 或 NAND 啓動,ResetEntry 都爲 0, 但上面已經排除了 NOR 啓動 
  cmp  r0,  #0        ;所以入口是 0 地址表示是從 NAND 的 StepingStone 啓動 
 bne copy_proc_beg  ;若從 NOR 啓動,跳轉到標號 copy_proc_beg。否則往下執行 
若判斷是從 NOR Flash 啓動,程序直接跳轉到標號 copy_proc_beg 處執行: 
copy_proc_beg 
 adr r0, ResetEntry  ;r0= ResetEntry=0
 ldr r2, BaseOfROM  ;r2= |Image$$RO$$Base| = 0x30000000
 cmp r0, r2     ;//如果從 norflash 啓動,那麼 ResetEntry=0 BaseOfROM=0x3000000,顯然不相同 
 ldreq r0, TopOfROM  
 beq InitRam     ;同時跳到 InitRam 
因爲現在程序仍然在 NOR Flash 中執行,代碼的執行基址還是 0,所以“adr r0, ResetEntry”取得的
ResetEntry 相對地址是 0。則 r0≠r2,先執行下面的代碼從 NOR 複製 RO 數據段到 SDRAM,不直接跳轉到
InitRam 進行 RW 段複製。 
 ldr r3, TopOfROM   
0              ;循環複製 Code 數據 
 ldmia r0!, {r4-r7} ;r0 的初始值 ResetEntry=0
 stmia r2!, {r4-r7} ;r2 的初始值 RO Base(0x30000000)
              ;上面的 2 行代碼將從 0 開始的地址內的數據保存到 SDRAM 內以 RO Base 開始的地址內 
 cmp r2, r3     ;複製的終止條件:複製了(RO Limit - RO Base) 大小,即整個 RO 數據段 
  bcc  %B0          ;若 r2<r3,往回跳轉到標號 0
 sub r2, r2, r3    ;r0+16,r2+16 every time,if r2>r3
 sub r0, r0, r2    ;將可能多加的部分減回來,讓 r0 對準 ROM 內 RO 段的結束點,也是 RW 數據段的開始點 
複製過程及結果如下圖:
代碼段複製完緊接着就是複製 RW 段: 
RW Data 
RO Data 
Total ROM Size 
ROM 
ResetEntry 
0x00000000 
|Image$$RO$$Base|(0x30000000)
RO Data
SDRAM 加載域
|Image$$RO$$Limit| 
從NOR Flash的0地址開始複製長度爲(RO Limit – RO Base)的數據到SDRAM的RO Base處
r0(複製完後) 
r0(複製前) InitRam            ;從 NOR Flash 複製 RW 段到 SDRM 中預設的 RW BASE 位置 
 ldr r2, BaseOfBSS   ;r2= |Image$$RW$$Base|=|Image$$RO$$Limit|
 ldr r3, BaseOfZero   ;r3= |Image$$ZI$$Base|              
0              ;複製 RW 數據到 BaseOfBSS
 cmp r2, r3     ;比較 r2,r3;看 RW 數據段有沒有複製完
 ldrcc r1, [r0], #4 ;R2<R3, [r0]->r1,然後 r0=r0+4, r0 是 RW 數據段在 ROM 內的起始地址
 strcc r1, [r2], #4 ;r2 爲|Image$$RW$$Base|地址,由編譯器指定 
  bcc  %B0          ;若 r2<r3,RW 段還沒有複製完 
從 NOR Flash 的 RO 段結束點開始複製 RW 數據到 SDRAM 的 RW Base 處,數據長度爲 RW Limit - RW Base 
還是有必要交代一下 ADS 中預定義的幾個變量以及它們的作用。|Image$$RO$$Base|表示連接完成後程序映
像 RO 段的起始地址;|Image$$RO$$Limit|表示連接完成後程序映像 RO 段的結束地址;|Image$$RW$$Base|
表示連接完成後程序映像 RW 段的起始地址;|Image$$ZI$$Base|表示連接完成後程序映像 ZI 段的起始地址,
注意由於 RW 段與 ZI 段在運行時是緊密挨着的哦,所以這個值也就代表了 RW 段的結束地址;
|Image$$ZI$$Limit|表示連接完成後程序映像 ZI 段的結束地址。爲了書寫和閱讀的方便,我們分別把上面
幾個值賦給變量 BaseOfROM,TopOfROM,BaseOfBSS,BaseOfZero,EndOfBSS 這樣看起來就很簡結了: 
BaseOfROM DCD |Image$$RO$$Base|  ;BaseOfROM 地址內保存|Image$$RO$$Base|的值 
TopOfROM DCD |Image$$RO$$Limit|  ;  
BaseOfBSS DCD |Image$$RW$$Base| 
BaseOfZero DCD |Image$$ZI$$Base|  ;ZI Base = RW Limit  
EndOfBSS DCD |Image$$ZI$$Limit| 
這樣“ldr r2, BaseOfBSS”實際是加載了|Image$$RW$$Base|到 r2。 
實際上也可以直接用 ldr r2,= |Image$$RW$$Base| 。 
複製完 RW 數據段,需要將 ZI 段清 0。ZI 段在 SDRAM 內是緊跟着 RW 段的,所以其起始地址|Image$$ZI$$Base|
同時也是 RW 段的結束地址。 
下面的代碼很簡單明瞭: 
  mov  r0,  #0        ;用 0 初始化 ZI 區 
 ldr r3, EndOfBSS  ;r3 = |Image$$ZI$$Limit| ; ZI 段的結束地址 
1  
  cmp  r2,  r3        ;r2 是 RW  limit,也就是  ZI 段的起始地址 
 strcc r0, [r2], #4 ;ZI 數據段清 0 
 bcc %B1  
RW Data 
RO Data 
Total ROM Size 
ROM 
ResetEntry 
0x00000000 
|Image$$RO$$Base|(0x30000000)
RO Data
SDRAM 加載域
|Image$$RO$$Limit| 
從NOR Flash的(RO Limit – RO Base)  地址開始複製(RW Limit – RW Base)的數據到SDRAM的RW Base處
|Image$$RW$$Base|(0x30100000) 
RO Limit-RO Base 
|Image$$RW$$Limit| 
RW Data
r0(複製後) 
r0(複製前) 
r2(複製後) 
r2(複製前) 此後,程序就可以跳轉到 Main 主函數處執行了。 
    [ :LNOT:THUMBCODE 
     ldr pc, GotoMain ;//跳轉到 Main()主函數; 它把$main_entry 的絕對地址賦給 PC,由於程序
已經複製到 SDRAM 中,而且$main_entry 的絕對地址是在 SDRAM 範圍內,所以從這句代碼以後,程序就開
始從 ROM 跳到 SDRAM 內執行了。 
     b .        ;就是仍跳回這一指令...不做任何事情的死循環                   
    ] 
關於 GotoMain 標號,是在文件的最後定義的: 
 GBLS main_entry 
main_entry SETS "Main"  
 IMPORT $main_entry 
GotoMain DCD $main_entry   
因爲“ldr pc, GotoMain”把$main_entry 的絕對地址(即 Main 函數的入口地址)賦給 PC,由於程序已經復
制到 SDRAM 中,而且$main_entry 的絕對地址是在 SDRAM 範圍內,所以從這句代碼以後,程序就開始從 ROM
跳到 SDRAM 內執行了。 
二、從 NAND 啓動的分析 
從 NAND 啓動,一樣是用下面這段代碼做判斷:  
;/************************************************************************************** 
;BWSCON 的[2:1]反映了外部引腳 OM[1:0]:若 OM[1:0] != 00, 從 NOR FLash 啓動或直接在內存運行; 
;若 OM[1:0]==00,則爲從 Nand Flash 啓動 
;**************************************************************************************/ 
 ldr r0, =BWSCON 
 ldr r0, [r0] 
 ands r0, r0, #6   
 bne copy_proc_beg  ;若從 NOR 啓動,跳轉到標號 copy_proc_beg
 adr r0, ResetEntry  ;從 NOR 或 NAND 啓動,ResetEntry 都爲 0, 但上面已經排除了 NOR 啓動 
RW Data 
RO Data 
SDRAM 
|Image$$RW$$Base|(0x30100000) 
|Image$$RO$$Base|(0x30000000) 
ZI Data 
|Image$$RO$$Limit| 
|Image$$ZI$$Base|(|Image$$RW$$Limit|) 
|Image$$ZI$$Limit| 
ZI數據段清0   cmp  r0,  #0        ;所以入口是 0 地址表示是從 NAND 的 StepingStone 啓動 
 bne copy_proc_beg  ;若從 NOR 啓動,跳轉到標號 copy_proc_beg。否則往下執行 
若判斷是從 NAND 啓動,不會跳轉到 copy_proc_beg,而是緊接着執行從 NAND 複製數據到 SDRAM 的代碼,從
NAND 的開始處(第一個 Block 的頭一頁)複製總共 256 頁即 128Kbytes 數據。過程與結果如下圖所示,代碼
如下。 
nand_boot_beg 
 mov r5, #NFCONF   ;首先設定 NAND 的控制寄存器;NFCONF 配置寄存器 地址 0x4E000000
 ldr r0, =(1<<15)|(1<<12)|(1<<11)|(7<<8)|(7<<4)|(7) 
 str r0, [r5]  ; 
 bl ReadNandID  ;接着讀取 NAND 的 ID 號,結果保存在 r5 裏
 mov r6, #0   ;r6 設初值 0.
 ldr r0, =0xec73  ;期望的 NAND ID 號
 cmp r5, r0   ;這裏進行比較,r5 是讀取到的 ID
  beq  %F1        ;相等的話就跳到下一個 1 標號處
 ldr r0, =0xec75  ;這是另一個期望值
 cmp r5, r0   ;這裏進行比較
  beq  %F1        ;相等的話就跳到下一個 1 標號處
 mov r6, #1   ;若 ID 不是 0xec73 或 0xec75, 設置 r6=1,否則 r6=0
1  
 bl ReadNandStatus ;讀取 NAND 狀態,結果放在 r1 裏  
  mov  r8,  #0        ;r8 設初值 0,意義爲頁號(start page address) 
 ldr r9, =ResetEntry  ;r9 設初值爲 ResetEntry 的絕對地址 |Image$$RO$$Base|=0x30000000,
2             
 ands r0, r8, #0x1f ;若 r8 爲 32 的整數倍(0,32,64...),eq 有效,ne 無效 
  bne    %F3        ;若不是 32 的整數倍,不是當前塊的頭一頁跳轉到標號 3 處,不用檢查壞塊。 
 mov  r0, r8   ;若 ands 結果所有位爲 0,即 r8 是 32 的整數倍或爲 0,是每個 Block 的頭一頁 
 bl  CheckBadBlk  ;檢查 NAND 的壞區,結果保存到 r0,r0 是 0 說明當前塊不是壞塊 
 cmp  r0, #0   ;比較 r0 和 0, r0==0, not bad block 
 addne r8, r8, #32  ;存在壞塊的話就跳過這個壞塊: +32 得到下一塊.(1 block=32 page);       
|Image$$RO$$Base|(0x30000000) 
RW Data 
RO Data 
SDRAM 
|Image$$RO$$Limit| 
對於從NAND Flash啓動,首先程序在4KB的StepingStone中運行,會複製128K的數據到SDRAM的RO 
Base處,其中包含了RO Data與RW Data。
RW Data 
RO Data 
NAND Flash   bne    %F4        ;若有壞塊就跳到 4 進行循環條件判斷。沒有壞塊的話就跳到標號 3 處 copy 當前頁 
3  
  mov  r0,  r8        ;加載當前頁號到 r0  
  mov  r1,  r9        ;加載複製數據的目標地址到 r1 (0x30000000)
 bl ReadNandPage  ;讀取該頁的 NAND 數據到 SDRAM 中由 r1 指定的地址
 add r9, r9, #512  ;目標地址+512(每一頁的大小是 512Bytes) 用於複製下一頁數據
 add r8, r8, #1   ;r8 指向下一頁
4  
  cmp  r8,  #256      ;比較是否讀完 256 頁即 128KBytes  共複製 8blocks 128KB 到 SDRAM
  bcc  %B2          ;如果 r8 小於 256(沒讀完 256 頁),就返回前面的標號 2 處讀取下一頁 
 ;複製完成後要關閉 NAND 控制器 
 mov r5, #NFCONF   ;Disable NandFlash
 ldr r0, [r5] 
 and r0, r0, #~0x8000 ;clear bit15, Disable NAND Controller
 str r0, [r5] 
 ldr pc, =copy_proc_beg 
執行完 NAND 到 SDRAM 的複製,最後一句“ldr  pc, =copy_proc_beg”,是重點。它把 copy_proc_beg
的絕對地址賦給 PC,由於程序的 RO Base 是 SDRAM 的起始地址空間,copy_proc_beg 的絕對地址肯定是在
SDRAM 範圍內,所以從這句代碼以後,程序就開始從 StepingStone 跳到 SDRAM 內執行了。程序中所有標號
的地址也相應要加上運行域的基址 RO Base(0x30000000)。另外在這句之前的程序大小要控制在 4K 以內。 
然後執行 copy_proc_beg 子程序: 
copy_proc_beg 
 adr r0, ResetEntry  ;從這裏開始 ResetEntry 變成了 0x30000000
 ldr r2, BaseOfROM   ;加載 BaseOfROM 內的數據即|Image$$RO$$Base|=0x30000000 
  cmp  r0,  r2        ;//如果從 norflash 啓動,那麼 ResetEntry=0 BaseOfROM=0x3000000,顯然不相同
 ldreq r0, TopOfROM ;如果相等的話,加載|Image$$RO$$Limit|到 r0,作爲複製 RW 數據段的起始地址
  beq  InitRam        ;同時跳到 InitRam 進行 RW 數據段的複製
對於“adr r0, ResetEntry”要特別注意, 這句代碼大致會被編譯成如下格式: 
sub r0,PC, #offset to ResetEntry 
其中“offset to ResetEntry”指編譯後 adr r0, ResetEntry 語句與 ResetEntry 標號之間的地址偏移,
是固定不變的。 
在執行“ldr pc, =copy_proc_beg”之前,程序還在 StepingStone 內運行,也就是從 0x0 地址開始運
行的,而 ResetEntry 本來就是程序入口,地址偏移值就爲當前 PC( 假設爲 0x00001000),計算結果顯然爲
0,則取到的 ResetEntry 值爲 0x0。而在這裏因爲程序已經跳轉到 SDRAM 內運行,起始地址爲 RO 
Base(0x30000000),當前語句的執行地址與 PC 值相應增加了 0x30000000(變爲 0x30001000)而偏移量是固
定不變的,則取到的 ResetEntry 值爲 0x30000000。 
其實不用想那麼複雜,只要知道因爲已經完成了代碼的複製,程序已經在 SDRAM 中執行,代碼的執行
基址是 RO Base(0x30000000),所以“adr r0, ResetEntry”取得的 ResetEntry 相對地址是 0x30000000
就行了。 
好了,下面該跳轉到 InitRam 了: 
InitRam             
 ldr r2, BaseOfBSS  ;這裏的 ldr 是 ARM 指令,加載 BaseOfBSS 內的數據|Image$$RW$$Base|到 r2 
                ;也可以用 ldr r2,= |Image$$RW$$Base| ,但不太方便閱讀           
 ldr r3, BaseOfZero  ; r3= |Image$$ZI$$Base|
0  ;//複製 RW 數據到 BaseOfBSS 
  cmp  r2,  r3        ;比較 r2,r3  ldrcc r1, [r0], #4 ;R2<R3, [r0]->r1,然後 r0=r0+4, r0 初始值是 TopOfROM(|Image$$RO$$Limit|),
              ;也是 RW 段的起始地址,此處即 0x30000000+RO Size 
 strcc r1, [r2], #4 ;r2 爲|Image$$RW$$Base|地址,由編譯器指定
  bcc  %B0          ;若 r2<r3,RW 段還沒有複製完
前面分析過從 NOR 啓動的過程,是從 NOR 複製 RW 數據段到 SDRAM 的 RW Base 處,而對於從 NAND 啓動,是
從 SDRAM 的 RO Limit 處複製 RW 數據段到 SDRAM 的 RW Base 處,因爲 RO 段與 RW 段都已經從 NAND 複製到了
SDRAM。 
後面將 ZI 段清 0 以及跳轉到 Main 主函數的過程與前面所分析的從 NOR 啓動的是一樣的,不再贅述。 
最後要強調的是,一定要清楚一旦程序跳轉到 SDRAM 中運行,程序所有語句的運行地址都變了,要加上基
址 RO Base(本例設定爲 0x30000000)。 
RW Data
RO Data
SDRAM 
|Image$$RW$$Base|(0x30100000)
|Image$$RO$$Base|(0x30000000) 
RW Data 
RO Data 
SDRAM 
|Image$$RO$$Limit| 
從SDRAM的|Image$$RO$$Limit|處開始複製長度爲RW Limit-RW Base的數據段到|Image$$RW$$Base|(0x30100000)處。
|Image$$ZI$$Base|(|Image$$RW$$Limit|)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章