專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

ARM2440ddr.h文件解讀

作者:未知   來源:不詳   點(diǎn)擊數(shù):  更新時(shí)間:2014年06月16日   【字體:

 

 
 
板子上電后就會從這里開始執(zhí)行,主要完成基本初始化,還有判斷是從nor還是nand啟動,再實(shí)現(xiàn)把程序搬到SDRAM當(dāng)中,在搬運(yùn)成功后再跳到main函數(shù)里面執(zhí)行。
 
我們現(xiàn)在開始來看看它的具體代碼吧!
 
GET和INCLUDE的功能是相同的,功能都是引進(jìn)一些編譯過的文件。
 
 GET option.inc
 GET memcfg.inc
 GET 2440addr.inc
 
定義SDRAM工作在Reflesh模式下,SDRAM有兩種刷新模式:selfreflesh,autoreflesh。后者是在其使用過程當(dāng)中設(shè)置的。
 
 BIT_SELFREFRESH EQU (1<<22)
 
下面是對arm處理器模式寄存器對應(yīng)的常數(shù)進(jìn)行賦值,arm處理器有一個(gè)CPSR寄存器,它的后五位決定了處理器處于哪個(gè)模式下?梢钥闯龀(shù)的定義都不會超過后5位的。
 
USERMODE    EQU  0x10
FIQMODE     EQU  0x11
IRQMODE     EQU  0x12
SVCMODE     EQU  0x13
ABORTMODE   EQU  0x17
UNDEFMODE   EQU  0x1b
MODEMASK    EQU  0x1f
NOINT       EQU  0xc0
各個(gè)異常模式的堆棧
 
UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~
SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~
UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~
AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~
IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~
FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~
這一段是統(tǒng)一arm的工作狀態(tài)和對應(yīng)的軟件編譯方式(16位編譯環(huán)境使用tasm.exe編譯)。arm處理器的工作狀態(tài)分為兩種:32位,arm執(zhí)行字對齊的arm指令集;16位,arm執(zhí)行半字對齊的Thumb指令集。不同的工作狀態(tài),編譯方式也不一樣。所以下面的程序就是判斷arm的工作方式來確定它的編譯方式。
 
 GBLL    THUMBCODE//定義THUMBCODE 這個(gè)變量GBLL 聲明一個(gè)全局邏輯變量并初始化為{FALSE}
 [ {CONFIG} = 16//"["表示"if","|"表示"else","]"表示"endif",對于CONFIG是在ADS編譯中定義的內(nèi)部變量。
THUMBCODE SETL  {TRUE}
     CODE32
   |
THUMBCODE SETL  {FALSE}
    ]//如果ARM是在16位的工作狀態(tài)的話,就使全局變量THUMBCODE設(shè)置為ture。
 
   MACRO//這個(gè)是宏定義的關(guān)鍵字
 MOV_PC_LR//作用是子程序返回
   [ THUMBCODE
     bx lr//當(dāng)目標(biāo)程序是Thumb時(shí),就要使用BX跳轉(zhuǎn)返回,并轉(zhuǎn)換模式。
   |
     mov pc,lr//目標(biāo)程序是ARM指令集,直接把lr賦給pc就可以了。
   ]
 MEND//宏定義的結(jié)束標(biāo)志。
 
   MACRO
 MOVEQ_PC_LR//這個(gè)是帶“相等”條件的子程序返回。和上面說的類似。
   [ THUMBCODE
        bxeq lr
   |
     moveq pc,lr
   ]
 MEND
 
在宏定義下面的handlexxx HANDLER handlexxx都會展成以下的程序段,這段程序主要把中斷服務(wù)程序的入口地址傳送給pc,在程序的用34字空間來存放中斷服務(wù)程序的入口地址,每個(gè)字空間都會有一個(gè)標(biāo)號,以handlerxxx開頭的。
 
 MACRO
$HandlerLabel HANDLER $HandleLabel
 
$HandlerLabel
 sub sp,sp,#4 //先預(yù)留空間,為了存儲跳轉(zhuǎn)地址。
 
 stmfd sp!,{r0} //把工作寄存器按入堆棧。
 ldr     r0,=$HandleLabel
 ldr     r0,[r0] //這兩句的功能是把中斷程序的入口地址先放在中間變量r0處。
 
 str     r0,[sp,#4]//把中斷服務(wù)程序的入口地址按入堆棧。     
 ldmfd   sp!,{r0,pc}//最后把堆棧中的中斷程序入口地址彈給pc寄存器,這樣就可以執(zhí)行相應(yīng)的中斷服務(wù)程序了。    
 MEND
 
S3C2440有兩種中斷模式:一種有中斷向量表的,一種則沒有。有表的話實(shí)時(shí)性比較好。當(dāng)一個(gè)外部中斷0發(fā)生后,程序自動跳轉(zhuǎn)到地址0x20處,0x20地址單元的指令為“ldr pc, = HandlerEINT0”,因此程序跳轉(zhuǎn)到HandlerEINT0處執(zhí)行這個(gè)宏操作,就是把外部中斷地址賦給PC。
 
一個(gè)arm程序是由R0,RW,ZI三個(gè)段組成。其中R0為代碼段,RW是已經(jīng)初始化的全局變量,ZI是未初始化的全局變量,BOOTLOADER要將RW段復(fù)制到RAM中并將ZI段清零。
 
編譯器使用下列段來記錄各段的起始地址和結(jié)束地址
|Image$$RO$$Base| ; RO 段起始地址|Image$$RO$$Limit| ; RO 段結(jié)束地址加1|Image$$RW$$Base| ; RW 段起始地址
 
|Image$$RW$$Limit| ; RW 段結(jié)束地址加1|Image$$ZI$$Base| ; ZI 段起始地址|Image$$ZI$$Limit| ; ZI 段結(jié)束地址加1
 
這些標(biāo)號的值是通過編譯器的設(shè)定來確定的如編譯軟件中對ro-base 和rw-base 的設(shè)定,例如ro-base=0xc000000 rw-base=0xc5f0000,在這里用IMPORT 偽指令( 和c 語言的extren 一樣) 引入|Image$$RO$$Base|,|Image$$RO$$Limit|...等比較古怪的變量是編譯器生成的。RO, RW, ZI 這三個(gè)段都保存在Flash 中,但RW,ZI 在Flash 中的地址肯定不是程序運(yùn)行時(shí)變量所存儲的位置,因此我們的程序在初始化時(shí)應(yīng)該把Flash 中的RW,ZI 拷貝到RAM 的對應(yīng)位置。這些變量是通過ADS 的工程設(shè)置里面設(shè)定的RO Base 和RW Base 設(shè)定的,最終由編譯腳本和連接程序?qū)氤绦?
 
IMPORT |Image$$RO$$Base|
 
IMPORT |Image$$RO$$Limit|
 
IMPORT |Image$$RW$$Base|
 
IMPORT |Image$$ZI$$Base|
 
IMPORT |Image$$ZI$$Limit|
 
引入外部變量mmu的快速總線模式和同步總線模式兩個(gè)變量
 
IMPORT MMU_SetAsyncBusMode
IMPORT MMU_SetFastBusMode
 
我們所熟知的main函數(shù)
 
IMPORT  Main
 
把鏡像從Nandflash拷貝到SDRAM的函數(shù)
 
IMPORT  RdNF2SDRAM
 
定義arm匯編程序段,段名叫init段,為只讀段
 
       AREA    Init,CODE,READONLY
 
       ENTRY
 
       EXPORT __ENTRY//導(dǎo)出__ENTRY標(biāo)號
__ENTRY
ResetEntry
 
ASSERT :DEF:ENDIAN_CHANGE//判斷模式改變是否定義過(ASSERT是偽指令,:DEF:lable判斷l(xiāng)able是否定義過了)
 
[ ENDIAN_CHANGE
  ASSERT  :DEF:ENTRY_BUS_WIDTH//判斷是否定義了總線寬度
 
  [ ENTRY_BUS_WIDTH=32//如果存儲器是32位的總線寬度
   b ChangeBigEndian     ;DCD 0xea000007
  ]
 
  [ ENTRY_BUS_WIDTH=16//如果存儲器是16位的總線寬度
   andeq r14,r7,r0,lsl #20   ;DCD 0x0007ea00
  ]
 
  [ ENTRY_BUS_WIDTH=8//如果是存儲器是8位總線寬度
   streq r0,[r0,-r10,ror #1] ;DCD 0x070000ea
  ]
 
|//如果總線寬度沒有定義的話,就直接跳轉(zhuǎn)到復(fù)位中斷
  b ResetHandler//程序執(zhí)行的地跳跳轉(zhuǎn)指令
 
]
 
 b HandlerUndef ;handler for Undefined mode
 b HandlerSWI ;handler for SWI interrupt
 b HandlerPabort ;handler for PAbort
 b HandlerDabort ;handler for DAbort
 b .  ;reserved
 b HandlerIRQ ;handler for IRQ interrupt
 b HandlerFIQ ;handler for FIQ interrupt
 
;@0x20
 b EnterPWDN ; Must be @0x20.//進(jìn)入powerdown模式
 
以上8條跳轉(zhuǎn)指令,是8個(gè)異常中斷處理向量,一定要按照順序排好,據(jù)我了解,每次出現(xiàn)異常的話,是由硬件自行查表的。
 
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
 
下面這段程序很重要,他是實(shí)現(xiàn)第二次查表的程序。arm把所有中斷都?xì)w為一個(gè)IRQ和一個(gè)FIRQ中斷異常,我們?yōu)榱艘谰唧w的中斷,從而才可以跳到中斷對應(yīng)的中斷服務(wù)程序。
 
IsrIRQ
 sub sp,sp,#4       //保留pc寄存器的值
 stmfd sp!,{r8-r9}//把r8 r9按入堆棧
 
 ldr r9,=INTOFFSET//把中斷偏移INTOFFSET的地址裝入r9里面
 ldr r9,[r9]//取出INTOFFSET單元里面的值給r9
 ldr r8,=HandleEINT0//向量表的入口地址賦給r8
 add r8,r8,r9,lsl #2//求出具體中斷向量的地址
 ldr r8,[r8]//中斷向量里面存儲的中斷服務(wù)程序的入口地址賦給r8
 str r8,[sp,#8]//按入堆棧
 ldmfd sp!,{r8-r9,pc}//堆棧彈出,跳轉(zhuǎn)到相應(yīng)的中斷服務(wù)程序
 
 
 
 LTORG//聲明文字池
 
板子上電后就,程序就執(zhí)行0x00處的b ResetHandler
 
ResetHandler
 ldr r0,=WTCON     //關(guān)閉看門狗  
 ldr r1,=0x0
 str r1,[r0]
 
 
 
 ldr r0,=INTMSK
 ldr r1,=0xffffffff  //關(guān)閉所有中斷
 str r1,[r0]
 
 ldr r0,=INTSUBMSK
 ldr r1,=0x7fff  //關(guān)閉所有子中斷
 
 str r1,[r0]
 
 
 
 [ {FALSE}
  ;rGPFDAT = (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);
  ; Led_Display
  ldr r0,=GPBCON
  ldr r1,=0x155500
  str r1,[r0]//使GPB10~GPB4為輸出口,GPB3~GPB0為輸入口
  ldr r0,=GPBDAT
  ldr r1,=0x0
  str r1,[r0]//使GPB10~GPB4輸出為低電平,GPB3~GPB0輸入為低電平
 ]
 
通過數(shù)據(jù)手冊可以發(fā)現(xiàn),當(dāng)輸出為1時(shí),LED滅,反之亦然。
 
LOCKTIME是pll的lock time計(jì)數(shù)器。為了減少pll的lock time,調(diào)整LOCKTIME寄存器。
 
 ldr r0,=LOCKTIME
 ldr r1,=0xffffff//賦給這個(gè)值后,UPLL和MPLL的locktime的值都會設(shè)定好了。具體為什么是設(shè)定這個(gè)值,你就去問問三星公司吧,我也不太懂。
 str r1,[r0]
說到這里,大家可能不太懂。我就在這里細(xì)說一下吧。這個(gè)涉及到arm9的時(shí)鐘模塊的知識。arm9有個(gè)時(shí)鐘控制邏輯,它可以產(chǎn)生cpu的FCLK時(shí)鐘、AHB總線外圍接口器件的HCLK時(shí)鐘以及APB總線外圍接口器件的PCLK時(shí)鐘。arm9有兩個(gè)鎖相環(huán)PLL,一個(gè)用于FCLK、HCLK、HCLK。一個(gè)用于USB模塊。這兩個(gè)PLL我們分別稱之為MPLL和UPLL。在系統(tǒng)復(fù)位之后,PLL按照默認(rèn)的配置進(jìn)行操作,由于認(rèn)為它這時(shí)是一個(gè)不穩(wěn)定的狀態(tài),所以這時(shí)用外部時(shí)鐘作為FCLK時(shí)鐘的輸出。只有當(dāng)向PLLCON寄存器設(shè)置相應(yīng)的值后,PLL就會按照軟件設(shè)置的頻率運(yùn)行了。這時(shí)就換成使用PLL的輸出作為FCLK了。對于FCLK先后不是有兩次不同時(shí)鐘作為輸入,這樣就余姚一個(gè)適應(yīng)的時(shí)間,這個(gè)時(shí)間的設(shè)定就是我們這里在LOCKTIME寄存器里面設(shè)置的常數(shù)啦。
 
[ PLL_ON_START//設(shè)置CLKDIVN的值在PLL鎖存時(shí)間之后有效。
 
  ldr r0,=CLKDIVN
 
  ldr r1,=CLKDIV_VAL  ; 0=1:1:1, 1=1:1:2, 2=1:2:2, 3=1:2:4, 4=1:4:4, 5=1:4:8, 6=1:3:3, 7=1:3:6.
  str r1,[r0]
 
可以看出是對FCLK、PCLK以及HCLK三者的比率設(shè)置。只要通過對CLKDIVN執(zhí)行操作就可以得到相應(yīng)需要的比率了。
 
  [ CLKDIV_VAL>1   //如果 Fclk:Hclk不是1:1的話執(zhí)行下面
 
    mrc p15,0,r0,c1,c0,0
    orr r0,r0,#0xc0000000;R1_nF:OR:R1_iA
    mcr p15,0,r0,c1,c0,0
   |
    mrc p15,0,r0,c1,c0,0
    bic r0,r0,#0xc0000000;R1_iA:OR:R1_nF
    mcr p15,0,r0,c1,c0,0
  ] 
 
這里可以看出,如果FCLK:HCLK不是1:1的關(guān)系的話,就要轉(zhuǎn)成異步總線模式。反之,如果是這個(gè)比例關(guān)系的話,就轉(zhuǎn)成快速總線模式。
 
  ldr r0,=UPLLCON//對UPLL進(jìn)行配置
  ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)//這里就是非常熟悉的PMS啦,F(xiàn)in = 12.0MHz, UCLK = 48MHz
  str r1,[r0]
  nop ; Caution: After UPLL setting, at least 7-clocks delay must be inserted for setting hardware be completed.
  nop
  nop
  nop
  nop
  nop
  nop
  ldr r0,=MPLLCON//對MPLL進(jìn)行配置
  ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)    ;Fin = 12.0MHz, FCLK = 400MHz
  str r1,[r0]
 ]
 
 ldr r1,=GSTATUS2
 ldr r0,[r1]
 tst r0,#0x2
 
判斷是否是從休眠模式喚醒的,對GSTATUS2[2]的檢測就可以判斷出是否從休眠模式喚醒的。
 
bne WAKEUP_SLEEP//如果是的話就跳轉(zhuǎn)。
 
EXPORT StartPointAfterSleepWakeUp//定義一個(gè)外部的StartPointAfterSleepWakeUp
 
StartPointAfterSleepWakeUp
 
    adrl r0, SMRDATA 
    ldr r1,=BWSCON 
    add r2, r0, #52 
 
0
    ldr r3, [r0], #4
    str r3, [r1], #4
    cmp r2, r0
    bne %B0
 
這段代碼的作用就是設(shè)置存儲控制器。在代碼的后面有一個(gè)SMRDATA的數(shù)據(jù)區(qū),用r0來定義它的起始地址,用r2來定義它的結(jié)束地址。r3是代表那13個(gè)存儲控制器.代碼很明顯,就是把內(nèi)存的數(shù)據(jù)賦給這13個(gè)存儲控制器里面的。
 
 ldr r0,=GPFCON
 ldr r1,=0x0
 str r1,[r0]//對GPF設(shè)置為輸入的功能
 ldr r0,=GPFUP
 ldr r1,=0xff
 str r1,[r0]//禁止上拉電阻
 
 ldr r1,=GPFDAT
 ldr r0,[r1]
 bic r0,r0,#(0x1e<<1)//bic是r0與#(0x1e<<1)的反碼按位相與。
 tst r0,#0x1//這里就是測試最后一位是否為0,為0時(shí)說明是有按鍵按下了。
 bne %F1//當(dāng)按鍵0沒有被按下的時(shí)候,就跳轉(zhuǎn)啦。
這段代碼是檢測EINT0是否被按下了。
 
 ldr r0,=GPFCON
 ldr r1,=0x55aa
 str r1,[r0]//GPF7~GPF4設(shè)置為輸出,GPF3~GPF0設(shè)置為EINT0~EINT3
 
 ldr r0,=GPFDAT
 ldr r1,=0x0
 str r1,[r0] //很明顯,GPF7~GPF4設(shè)置為LED燈的控制,低電平全部亮了。起到指示的用途。
 
 mov r1,#0
 mov r2,#0
 mov r3,#0
 mov r4,#0
 mov r5,#0
 mov r6,#0
 mov r7,#0
 mov r8,#0
 
 
 
 ldr r9,=0x4000000   ;64MB
 ldr r0,=0x30000000
 
 
 
 stmia r0!,{r1-r8}
 subs r9,r9,#32
 bne %B0
 
很明顯可以看出,程序利用r1~r8這幾個(gè)寄存器把0x30000000到0x34000000的內(nèi)存全部清零了。
 
1
 
 bl InitStacks//初始化堆棧
 
 
 ldr r0, =BWSCON
 ldr r0, [r0]
 ands r0, r0, #6//OM[1:0] != 0, 從NOR FLash或者內(nèi)存啟動,不用讀取NAND FLASH
 bne copy_proc_beg//不需要從NAND FLASH啟動就在這里跳轉(zhuǎn)啦
 
 adr r0, ResetEntry//OM[1:0] == 0,就從NAND FLash啟動 
 cmp r0, #0//在進(jìn)行比較,是否入口地址是在0處,如果不是則是用仿真器   
 bne copy_proc_beg//仿真器也不需要在NAND FLASH啟動
 
nand_boot_beg
 [ {TRUE}
  bl RdNF2SDRAM
 ]
 
  ldr pc, =copy_proc_beg
 
我們來看下RdNF2SDRAM具體是怎么工作的,這段代碼的作用就是把NAND的程序讀到RAM里面。
 
 void RdNF2SDRAM( )
{
  U32 i;
  U32 start_addr = 0x0;
  unsigned char * to = (unsigned char *)0x30000000;
  U32 size = 0x100000;//可以算出是8M的大小。
  rNF_Init();//我們來仔細(xì)看看這個(gè)函數(shù)吧。
 
  如下:
 
         static void rNF_Init(void)
{
 rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);//TACLS=1,TWRPH0=4,TWRPH1=0初始化ECC,CLE&ALE持續(xù)時(shí)間的設(shè)置,TWRPH0和TWRPH1持續(xù)時(shí)間的設(shè)置。
 rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);//在讀寫NANDFLASH之前,對6,5,4位的設(shè)置是確?梢允褂肊CC;對13位清零,使得可以寫,擦除還有讀0x4E000038~0x4E00003C區(qū)域的內(nèi)容;由于對于這范圍區(qū)域的讀寫我們不加任何限制,所以我們就不用設(shè)置中斷來通知系統(tǒng)這個(gè)范圍的區(qū)域被讀寫了,也就是10位清零了;RnB是表示存儲器現(xiàn)在是否處于忙碌狀態(tài),9位的設(shè)置為1時(shí),表示可以用中斷來通知CPU現(xiàn)在存儲器的狀態(tài),而8位的設(shè)置是用來說明是上升沿觸發(fā)還是下降沿觸發(fā)。
 
 rNFSTAT = 0;
 rNF_Reset();
}
 
我們來看下rNF_Reset()它的具體代碼吧,代碼如下:
 
static void rNF_Reset()
{
 NF_CE_L();
 NF_CLEAR_RB();
 NF_CMD(CMD_RESET); 
 NF_DETECT_RB();
 NF_CE_H();
}
 
代碼看上去很煩人,其實(shí)不是的,就是一堆宏定義,我直接翻譯一下吧,翻譯如下:
 
rNFCONT &= ~(1<<1); //位1清零,表示片選使能,這樣片子就可以工作了。
 
rNFSTAT |= (1<<2);//清零2位,這里不需要判斷片子是否忙碌。
 
rNFCMD  = (CMD_RESET);//其中CMD_RESET=0xff。
 
while(!(rNFSTAT&(1<<2)));//當(dāng)RnB從低電平變換到高電平的時(shí)候,就會跳出這個(gè)循環(huán)。就是在等待NANDFLASH操作完畢。
 
rNFCONT |= (1<<1);//使片子停止工作。
 
這樣NANDFLASH的初始化工作終于完成了。我們現(xiàn)在回到RdNF2SDRAM里面來,接著往下分析。
 
switch(rNF_ReadID())我們來分析一下里面這個(gè)函數(shù)吧,代碼如下:
 
static char rNF_ReadID()
{
 char pMID;
 char pDID;
 char nBuff;
 char n4thcycle;
 int i;
 
 
 NF_nFCE_L();//又是使能片子工作   
 NF_CLEAR_RB();//清除NFSTAT的2位,為以后判斷片子是否工作完畢。
 NF_CMD(CMD_READID); //往NFCMD送讀ID指令。
 NF_ADDR(0x0);//往NFADDR送地址
 for ( i = 0; i < 100; i++ );
 
 pMID = NF_RDDATA8();
 pDID = NF_RDDATA8();
 
 nBuff     = NF_RDDATA8();
 n4thcycle = NF_RDDATA8();
 NF_nFCE_H();
 
 
 return (pDID);
}//最后返回pDID為什么會有其它值,我就不太理解了。我們再返回到主程序里面看看。
 
switch(rNF_ReadID())
 {
  case 0x76:
   for(i = (start_addr >> 9); size > 0; )//在這種情況下,認(rèn)為一頁的大小為512字節(jié)
   {
    rSB_ReadPage(i, to);
    size -= 512;
    to += 512;
    i ++;
   }
   break;
  case 0xf1:
  case 0xda:
  case 0xdc:
  case 0xd3:
   for(i = (start_addr >> 11); size > 0; )//在這種情況下,認(rèn)為是2048字節(jié)為一頁
   {
    rLB_ReadPage(i, to);
    size -= 2048;
    to += 2048;
    i ++;
   }
   break;
 }
}  
 
其實(shí)都是把NANDFLASH的開始第二頁的內(nèi)容存放在一個(gè)指針數(shù)組里面,這個(gè)指針數(shù)組的起始地址在0x30000000。就是我們等下在下面看到的to[i]數(shù)組了。下面兩個(gè)函數(shù)完成的功能是一樣的,只是區(qū)別在于一頁是多大,512或者是2048。
 
static void rSB_ReadPage(U32 addr, unsigned char * to)
{
 U32 i;
 
 rNF_Reset();
 
 //  Enable the chip
 NF_nFCE_L();
 NF_CLEAR_RB();
 
 // Issue Read command
 NF_CMD(CMD_READ);
 
 //  Set up address
 NF_ADDR(0x00);
 NF_ADDR((addr) & 0xff);
 NF_ADDR((addr >> 8) & 0xff);
 NF_ADDR((addr >> 16) & 0xff);
 
 
 NF_DETECT_RB();  // wait tR(max 12us)
 
 for (i = 0; i < 512; i++)
 {
  to[i] =  NF_RDDATA8();
 }
 
 NF_nFCE_H();
 
}
static void rLB_ReadPage(U32 addr, unsigned char * to)
{
 U32 i;
 
 rNF_Reset();
 
 //  Enable the chip
 NF_nFCE_L();  
 NF_CLEAR_RB();
 
 // Issue Read command
 NF_CMD(CMD_READ);
 
 //  Set up address
 NF_ADDR(0x00);
 NF_ADDR(0x00);
 NF_ADDR((addr) & 0xff);
 NF_ADDR((addr >> 8) & 0xff);
 NF_ADDR((addr >> 16) & 0xff);
 
 NF_CMD(CMD_READ3);
 
 NF_DETECT_RB();  // wait tR(max 12us)
 
 for (i = 0; i < 2048; i++)
 {
  to[i] =  NF_RDDATA8();
 }
 
 NF_nFCE_H();
 
}
 
可以看出剛開始的時(shí)候都是先復(fù)位一下的,不同的地方在于每次是怎樣把傳進(jìn)來的地址經(jīng)過轉(zhuǎn)換再付給NFADDR寄存器的,具體怎么樣要看NAND的數(shù)據(jù)手冊。
 
 我們接著回到2440init.s的程序來,接著就有以下一句:
 
ldr pc, =copy_proc_beg
 
在前面也看到copy_proc_beg這個(gè)標(biāo)號出現(xiàn)很多次,這個(gè)標(biāo)號下面的代碼完成的功能就是把nand flash的內(nèi)容拷貝到ram當(dāng)中。
 
copy_proc_beg
 adr r0, ResetEntry
 ldr r2, BaseOfROM
 cmp r0, r2//兩個(gè)進(jìn)行比較
 ldreq r0, TopOfROM//如果相同的話,為r0賦上R0的結(jié)束位置,也是RW的起始位置。
 beq InitRam //如果相同的話,就跳到這個(gè)標(biāo)號的位置。
 
 ldr r3, TopOfROM//以下代碼是針對代碼在NOR FLASH時(shí)的拷貝方法。
 ldmia r0!, {r4-r7}
 stmia r2!, {r4-r7}
 cmp r2, r3
 bcc %B0//這幾段代碼的功能就是把ResetEntry的內(nèi)容搬到BaseOfROM(R0的起始位置,后面有聲明的)。
 
 sub r2, r2, r3
 sub r0, r0, r2 //這里使 ResetEntry的位置往下移,為了后面的數(shù)據(jù)拷貝做準(zhǔn)備。  
  
InitRam 
 ldr r2, BaseOfBSS
 ldr r3, BaseOfZero 
0
 cmp r2, r3
 ldrcc r1, [r0], #4
 strcc r1, [r2], #4
 bcc %B0 //可以看出這一段是對ResetEntry里面定義好的數(shù)據(jù)拷貝到RW段。
 
 mov r0, #0
 ldr r3, EndOfBSS
 cmp r2, r3
 strcc r0, [r2], #4
 bcc %B1//如果拷貝完數(shù)據(jù)后還剩下多余的空間的話,就往里面填充0
 
 ldr pc, =%F2  ;goto compiler address
2
 
 ldr r0,=HandleIRQ 
 ldr r1,=IsrIRQ 
 str r1,[r0]//這三條語句很明顯就是說明了,HandleIRQ這個(gè)中斷向量的存儲單元被賦上了IsrIRQ標(biāo)號的地址,這樣發(fā)生IRQ中斷后就會直接去到二級表,去確認(rèn)具體發(fā)生哪個(gè)中斷。
 
 
 
    [ :LNOT:THUMBCODE
   bl Main //到這里,我們就看到了進(jìn)入MAIN函數(shù)了。
   b .
    ]
 
    [ THUMBCODE  ;for start-up code for Thumb mode
   orr lr,pc,#1
   bx lr
   CODE16
   bl Main //可以看到以上代碼表示如果arm是在THUMBCODE指令模式下的話,就進(jìn)行模式轉(zhuǎn)換。
 
   b .
  CODE32
    ]
 
到這里,我們已經(jīng)把2440init.s的啟動代碼分析了一遍了。如有任何錯(cuò)誤的話,請大家指出!謝謝!
 
關(guān)閉窗口

相關(guān)文章