久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術學習與研究
當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

ARM2440ddr.h文件解讀

作者:未知   來源:不詳   點擊數:  更新時間:2014年06月16日   【字體:

 

 
 
板子上電后就會從這里開始執行,主要完成基本初始化,還有判斷是從nor還是nand啟動,再實現把程序搬到SDRAM當中,在搬運成功后再跳到main函數里面執行。
 
我們現在開始來看看它的具體代碼吧!
 
GET和INCLUDE的功能是相同的,功能都是引進一些編譯過的文件。
 
 GET option.inc
 GET memcfg.inc
 GET 2440addr.inc
 
定義SDRAM工作在Reflesh模式下,SDRAM有兩種刷新模式:selfreflesh,autoreflesh。后者是在其使用過程當中設置的。
 
 BIT_SELFREFRESH EQU (1<<22)
 
下面是對arm處理器模式寄存器對應的常數進行賦值,arm處理器有一個CPSR寄存器,它的后五位決定了處理器處于哪個模式下。可以看出常數的定義都不會超過后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
各個異常模式的堆棧
 
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 ~
這一段是統一arm的工作狀態和對應的軟件編譯方式(16位編譯環境使用tasm.exe編譯)。arm處理器的工作狀態分為兩種:32位,arm執行字對齊的arm指令集;16位,arm執行半字對齊的Thumb指令集。不同的工作狀態,編譯方式也不一樣。所以下面的程序就是判斷arm的工作方式來確定它的編譯方式。
 
 GBLL    THUMBCODE//定義THUMBCODE 這個變量GBLL 聲明一個全局邏輯變量并初始化為{FALSE}
 [ {CONFIG} = 16//"["表示"if","|"表示"else","]"表示"endif",對于CONFIG是在ADS編譯中定義的內部變量。
THUMBCODE SETL  {TRUE}
     CODE32
   |
THUMBCODE SETL  {FALSE}
    ]//如果ARM是在16位的工作狀態的話,就使全局變量THUMBCODE設置為ture。
 
   MACRO//這個是宏定義的關鍵字
 MOV_PC_LR//作用是子程序返回
   [ THUMBCODE
     bx lr//當目標程序是Thumb時,就要使用BX跳轉返回,并轉換模式。
   |
     mov pc,lr//目標程序是ARM指令集,直接把lr賦給pc就可以了。
   ]
 MEND//宏定義的結束標志。
 
   MACRO
 MOVEQ_PC_LR//這個是帶“相等”條件的子程序返回。和上面說的類似。
   [ THUMBCODE
        bxeq lr
   |
     moveq pc,lr
   ]
 MEND
 
在宏定義下面的handlexxx HANDLER handlexxx都會展成以下的程序段,這段程序主要把中斷服務程序的入口地址傳送給pc,在程序的用34字空間來存放中斷服務程序的入口地址,每個字空間都會有一個標號,以handlerxxx開頭的。
 
 MACRO
$HandlerLabel HANDLER $HandleLabel
 
$HandlerLabel
 sub sp,sp,#4 //先預留空間,為了存儲跳轉地址。
 
 stmfd sp!,{r0} //把工作寄存器按入堆棧。
 ldr     r0,=$HandleLabel
 ldr     r0,[r0] //這兩句的功能是把中斷程序的入口地址先放在中間變量r0處。
 
 str     r0,[sp,#4]//把中斷服務程序的入口地址按入堆棧。     
 ldmfd   sp!,{r0,pc}//最后把堆棧中的中斷程序入口地址彈給pc寄存器,這樣就可以執行相應的中斷服務程序了。    
 MEND
 
S3C2440有兩種中斷模式:一種有中斷向量表的,一種則沒有。有表的話實時性比較好。當一個外部中斷0發生后,程序自動跳轉到地址0x20處,0x20地址單元的指令為“ldr pc, = HandlerEINT0”,因此程序跳轉到HandlerEINT0處執行這個宏操作,就是把外部中斷地址賦給PC。
 
一個arm程序是由R0,RW,ZI三個段組成。其中R0為代碼段,RW是已經初始化的全局變量,ZI是未初始化的全局變量,BOOTLOADER要將RW段復制到RAM中并將ZI段清零。
 
編譯器使用下列段來記錄各段的起始地址和結束地址
|Image$$RO$$Base| ; RO 段起始地址|Image$$RO$$Limit| ; RO 段結束地址加1|Image$$RW$$Base| ; RW 段起始地址
 
|Image$$RW$$Limit| ; RW 段結束地址加1|Image$$ZI$$Base| ; ZI 段起始地址|Image$$ZI$$Limit| ; ZI 段結束地址加1
 
這些標號的值是通過編譯器的設定來確定的如編譯軟件中對ro-base 和rw-base 的設定,例如ro-base=0xc000000 rw-base=0xc5f0000,在這里用IMPORT 偽指令( 和c 語言的extren 一樣) 引入|Image$$RO$$Base|,|Image$$RO$$Limit|...等比較古怪的變量是編譯器生成的。RO, RW, ZI 這三個段都保存在Flash 中,但RW,ZI 在Flash 中的地址肯定不是程序運行時變量所存儲的位置,因此我們的程序在初始化時應該把Flash 中的RW,ZI 拷貝到RAM 的對應位置。這些變量是通過ADS 的工程設置里面設定的RO Base 和RW Base 設定的,最終由編譯腳本和連接程序導入程序.
 
IMPORT |Image$$RO$$Base|
 
IMPORT |Image$$RO$$Limit|
 
IMPORT |Image$$RW$$Base|
 
IMPORT |Image$$ZI$$Base|
 
IMPORT |Image$$ZI$$Limit|
 
引入外部變量mmu的快速總線模式和同步總線模式兩個變量
 
IMPORT MMU_SetAsyncBusMode
IMPORT MMU_SetFastBusMode
 
我們所熟知的main函數
 
IMPORT  Main
 
把鏡像從Nandflash拷貝到SDRAM的函數
 
IMPORT  RdNF2SDRAM
 
定義arm匯編程序段,段名叫init段,為只讀段
 
       AREA    Init,CODE,READONLY
 
       ENTRY
 
       EXPORT __ENTRY//導出__ENTRY標號
__ENTRY
ResetEntry
 
ASSERT :DEF:ENDIAN_CHANGE//判斷模式改變是否定義過(ASSERT是偽指令,:DEF:lable判斷lable是否定義過了)
 
[ 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
  ]
 
|//如果總線寬度沒有定義的話,就直接跳轉到復位中斷
  b ResetHandler//程序執行的地跳跳轉指令
 
]
 
 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.//進入powerdown模式
 
以上8條跳轉指令,是8個異常中斷處理向量,一定要按照順序排好,據我了解,每次出現異常的話,是由硬件自行查表的。
 
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
 
下面這段程序很重要,他是實現第二次查表的程序。arm把所有中斷都歸為一個IRQ和一個FIRQ中斷異常,我們為了要知道具體的中斷,從而才可以跳到中斷對應的中斷服務程序。
 
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]//中斷向量里面存儲的中斷服務程序的入口地址賦給r8
 str r8,[sp,#8]//按入堆棧
 ldmfd sp!,{r8-r9,pc}//堆棧彈出,跳轉到相應的中斷服務程序
 
 
 
 LTORG//聲明文字池
 
板子上電后就,程序就執行0x00處的b ResetHandler
 
ResetHandler
 ldr r0,=WTCON     //關閉看門狗  
 ldr r1,=0x0
 str r1,[r0]
 
 
 
 ldr r0,=INTMSK
 ldr r1,=0xffffffff  //關閉所有中斷
 str r1,[r0]
 
 ldr r0,=INTSUBMSK
 ldr r1,=0x7fff  //關閉所有子中斷
 
 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輸入為低電平
 ]
 
通過數據手冊可以發現,當輸出為1時,LED滅,反之亦然。
 
LOCKTIME是pll的lock time計數器。為了減少pll的lock time,調整LOCKTIME寄存器。
 
 ldr r0,=LOCKTIME
 ldr r1,=0xffffff//賦給這個值后,UPLL和MPLL的locktime的值都會設定好了。具體為什么是設定這個值,你就去問問三星公司吧,我也不太懂。
 str r1,[r0]
說到這里,大家可能不太懂。我就在這里細說一下吧。這個涉及到arm9的時鐘模塊的知識。arm9有個時鐘控制邏輯,它可以產生cpu的FCLK時鐘、AHB總線外圍接口器件的HCLK時鐘以及APB總線外圍接口器件的PCLK時鐘。arm9有兩個鎖相環PLL,一個用于FCLK、HCLK、HCLK。一個用于USB模塊。這兩個PLL我們分別稱之為MPLL和UPLL。在系統復位之后,PLL按照默認的配置進行操作,由于認為它這時是一個不穩定的狀態,所以這時用外部時鐘作為FCLK時鐘的輸出。只有當向PLLCON寄存器設置相應的值后,PLL就會按照軟件設置的頻率運行了。這時就換成使用PLL的輸出作為FCLK了。對于FCLK先后不是有兩次不同時鐘作為輸入,這樣就余姚一個適應的時間,這個時間的設定就是我們這里在LOCKTIME寄存器里面設置的常數啦。
 
[ PLL_ON_START//設置CLKDIVN的值在PLL鎖存時間之后有效。
 
  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三者的比率設置。只要通過對CLKDIVN執行操作就可以得到相應需要的比率了。
 
  [ CLKDIV_VAL>1   //如果 Fclk:Hclk不是1:1的話執行下面
 
    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的關系的話,就要轉成異步總線模式。反之,如果是這個比例關系的話,就轉成快速總線模式。
 
  ldr r0,=UPLLCON//對UPLL進行配置
  ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)//這里就是非常熟悉的PMS啦,Fin = 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進行配置
  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//如果是的話就跳轉。
 
EXPORT StartPointAfterSleepWakeUp//定義一個外部的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
 
這段代碼的作用就是設置存儲控制器。在代碼的后面有一個SMRDATA的數據區,用r0來定義它的起始地址,用r2來定義它的結束地址。r3是代表那13個存儲控制器.代碼很明顯,就是把內存的數據賦給這13個存儲控制器里面的。
 
 ldr r0,=GPFCON
 ldr r1,=0x0
 str r1,[r0]//對GPF設置為輸入的功能
 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時說明是有按鍵按下了。
 bne %F1//當按鍵0沒有被按下的時候,就跳轉啦。
這段代碼是檢測EINT0是否被按下了。
 
 ldr r0,=GPFCON
 ldr r1,=0x55aa
 str r1,[r0]//GPF7~GPF4設置為輸出,GPF3~GPF0設置為EINT0~EINT3
 
 ldr r0,=GPFDAT
 ldr r1,=0x0
 str r1,[r0] //很明顯,GPF7~GPF4設置為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這幾個寄存器把0x30000000到0x34000000的內存全部清零了。
 
1
 
 bl InitStacks//初始化堆棧
 
 
 ldr r0, =BWSCON
 ldr r0, [r0]
 ands r0, r0, #6//OM[1:0] != 0, 從NOR FLash或者內存啟動,不用讀取NAND FLASH
 bne copy_proc_beg//不需要從NAND FLASH啟動就在這里跳轉啦
 
 adr r0, ResetEntry//OM[1:0] == 0,就從NAND FLash啟動 
 cmp r0, #0//在進行比較,是否入口地址是在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();//我們來仔細看看這個函數吧。
 
  如下:
 
         static void rNF_Init(void)
{
 rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);//TACLS=1,TWRPH0=4,TWRPH1=0初始化ECC,CLE&ALE持續時間的設置,TWRPH0和TWRPH1持續時間的設置。
 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位的設置是確保可以使用ECC;對13位清零,使得可以寫,擦除還有讀0x4E000038~0x4E00003C區域的內容;由于對于這范圍區域的讀寫我們不加任何限制,所以我們就不用設置中斷來通知系統這個范圍的區域被讀寫了,也就是10位清零了;RnB是表示存儲器現在是否處于忙碌狀態,9位的設置為1時,表示可以用中斷來通知CPU現在存儲器的狀態,而8位的設置是用來說明是上升沿觸發還是下降沿觸發。
 
 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();
}
 
代碼看上去很煩人,其實不是的,就是一堆宏定義,我直接翻譯一下吧,翻譯如下:
 
rNFCONT &= ~(1<<1); //位1清零,表示片選使能,這樣片子就可以工作了。
 
rNFSTAT |= (1<<2);//清零2位,這里不需要判斷片子是否忙碌。
 
rNFCMD  = (CMD_RESET);//其中CMD_RESET=0xff。
 
while(!(rNFSTAT&(1<<2)));//當RnB從低電平變換到高電平的時候,就會跳出這個循環。就是在等待NANDFLASH操作完畢。
 
rNFCONT |= (1<<1);//使片子停止工作。
 
這樣NANDFLASH的初始化工作終于完成了。我們現在回到RdNF2SDRAM里面來,接著往下分析。
 
switch(rNF_ReadID())我們來分析一下里面這個函數吧,代碼如下:
 
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; )//在這種情況下,認為一頁的大小為512字節
   {
    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; )//在這種情況下,認為是2048字節為一頁
   {
    rLB_ReadPage(i, to);
    size -= 2048;
    to += 2048;
    i ++;
   }
   break;
 }
}  
 
其實都是把NANDFLASH的開始第二頁的內容存放在一個指針數組里面,這個指針數組的起始地址在0x30000000。就是我們等下在下面看到的to[i]數組了。下面兩個函數完成的功能是一樣的,只是區別在于一頁是多大,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();
 
}
 
可以看出剛開始的時候都是先復位一下的,不同的地方在于每次是怎樣把傳進來的地址經過轉換再付給NFADDR寄存器的,具體怎么樣要看NAND的數據手冊。
 
 我們接著回到2440init.s的程序來,接著就有以下一句:
 
ldr pc, =copy_proc_beg
 
在前面也看到copy_proc_beg這個標號出現很多次,這個標號下面的代碼完成的功能就是把nand flash的內容拷貝到ram當中。
 
copy_proc_beg
 adr r0, ResetEntry
 ldr r2, BaseOfROM
 cmp r0, r2//兩個進行比較
 ldreq r0, TopOfROM//如果相同的話,為r0賦上R0的結束位置,也是RW的起始位置。
 beq InitRam //如果相同的話,就跳到這個標號的位置。
 
 ldr r3, TopOfROM//以下代碼是針對代碼在NOR FLASH時的拷貝方法。
 ldmia r0!, {r4-r7}
 stmia r2!, {r4-r7}
 cmp r2, r3
 bcc %B0//這幾段代碼的功能就是把ResetEntry的內容搬到BaseOfROM(R0的起始位置,后面有聲明的)。
 
 sub r2, r2, r3
 sub r0, r0, r2 //這里使 ResetEntry的位置往下移,為了后面的數據拷貝做準備。  
  
InitRam 
 ldr r2, BaseOfBSS
 ldr r3, BaseOfZero 
0
 cmp r2, r3
 ldrcc r1, [r0], #4
 strcc r1, [r2], #4
 bcc %B0 //可以看出這一段是對ResetEntry里面定義好的數據拷貝到RW段。
 
 mov r0, #0
 ldr r3, EndOfBSS
 cmp r2, r3
 strcc r0, [r2], #4
 bcc %B1//如果拷貝完數據后還剩下多余的空間的話,就往里面填充0
 
 ldr pc, =%F2  ;goto compiler address
2
 
 ldr r0,=HandleIRQ 
 ldr r1,=IsrIRQ 
 str r1,[r0]//這三條語句很明顯就是說明了,HandleIRQ這個中斷向量的存儲單元被賦上了IsrIRQ標號的地址,這樣發生IRQ中斷后就會直接去到二級表,去確認具體發生哪個中斷。
 
 
 
    [ :LNOT:THUMBCODE
   bl Main //到這里,我們就看到了進入MAIN函數了。
   b .
    ]
 
    [ THUMBCODE  ;for start-up code for Thumb mode
   orr lr,pc,#1
   bx lr
   CODE16
   bl Main //可以看到以上代碼表示如果arm是在THUMBCODE指令模式下的話,就進行模式轉換。
 
   b .
  CODE32
    ]
 
到這里,我們已經把2440init.s的啟動代碼分析了一遍了。如有任何錯誤的話,請大家指出!謝謝!
 
關閉窗口

相關文章

主站蜘蛛池模板: 国产精品久久久久久久久免费丝袜 | 国产精品伦一区二区三级视频 | 男女网站在线观看 | 精品粉嫩aⅴ一区二区三区四区 | 国产精品免费看 | 欧美精品一区二区三区四区 | 2019天天操| 日韩中文一区二区 | 日韩欧美国产一区二区 | 久久久国产精品入口麻豆 | 午夜视频免费在线观看 | 日日操视频 | 欧美精品网站 | 狠狠av| 免费黄色片在线观看 | 国产精品久久久久久久久久久久 | 久久久国产一区二区三区 | 国产一区二区在线免费观看 | 午夜激情在线 | 成人水多啪啪片 | 欧美国产日韩一区 | 精品欧美一区二区三区久久久 | 午夜ww| 免费观看的av | 91视频免费在观看 | 日韩中文在线观看 | 午夜性色a√在线视频观看9 | 精品一区二区不卡 | 久久青草av | 欧美日韩大片 | 久久精品久久久久久 | 美女福利视频一区 | 久久精品国产99国产精品亚洲 | 天天曰夜夜操 | 婷婷午夜天 | 亚洲精品在线91 | 岛国午夜| 欧美二区在线 | 精品国偷自产在线 | 91美女视频 | 亚洲日本国产 |