s3c2440啓動文件start.s分析

 啓動文件就是引導ARM啓動,並進入我們熟悉的C語言程序。它主要完成了ARM最基本的硬件初始化工作。雖然啓動文件的內容大同小異(就是設置系統時鐘、內存、中斷向量表、棧等內容),而且只要有一個現成的啓動文件,即使不用詳細瞭解該文件的內容,直接進入C語言編程工作也可以對ARM進行操作,但我認爲熟悉啓動文件的內容,還是有必要的,它對我們熟悉ARM的體系結構,編寫出更高效的程序是大有益處的。因此我花了一些時間詳細分析了s3c2440啓動文件的內容,讓它作爲我進入ARM領域研究的開端,希望能有一個好的起點,爲以後的研究打下基礎。

        下面就是我對s3c2440啓動文件的分析,標註了較詳細的註解,不僅有我對啓動文件的理解,同時也查閱其他網友的相關文章。理解不對的地方還望大家指正!

 

;=========================================

; NAME: 2440INIT.S

; DESC: C start up codes

;       Configure memory, ISR ,stacks

;      Initialize C-variables

;=========================================

 

;GET類似於C語言的include,option.inc文件內定義了一些全局變量,memcfg.inc文件內定義了關於內存bank的符號和數字常量,2440addr.inc文件內定義了用於彙編的s3c2440寄存器變量和地址

       GET option.inc

       GET memcfg.inc

       GET 2440addr.inc

 

;SDRAM自刷新位,把寄存器REFRESH的第22位處置1

BIT_SELFREFRESH   EQU    (1<<22)

 

;CPSR中的低5位定義了處理器的七種工作模式,爲以後切換模式時使用

;Pre-defined constants

USERMODE    EQU     0x10

FIQMODE      EQU                0x11

IRQMODE      EQU               0x12

SVCMODE     EQU      0x13

ABORTMODE   EQU        0x17

UNDEFMODE   EQU        0x1b

MODEMASK    EQU        0x1f

;CPSR中的I位和F位置1,表示禁止任何中斷

NOINT       EQU      0xc0

 

;定義了7種處理器模式下的棧的起始地址,其中用戶模式和系統模式共有一個棧空間

;_STACK_BASEADDRESS在option.inc文件內定義,值爲0x33ff8000

;The location of stacks

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位和32位

;編譯器有相對應的用16位和32位兩種編譯方式

;這段的目的是統一目前的處理器工作狀態和軟件編譯方式

;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.

       GBLL    THUMBCODE                   ;聲明一個全局邏輯變量

       [ {CONFIG} = 16                                 ;if CONFIG == 16

THUMBCODE SETL  {TRUE}               ;THUMBCODE = TRUE

           CODE32                                        ;指示編譯器爲ARM指令

             |                                                ;else

THUMBCODE SETL  {FALSE}                    ;THUMBCODE = FALSE

    ]

 

;宏定義,在後面出現MOV_PC_LR時,這個宏會被自動展開

;該宏的作用是跳出子程序,返回被調用處

             MACRO

       MOV_PC_LR

             [ THUMBCODE              ;if THUMBCODE == TRUE

           bx lr

             |                                  ;else 即THUMBCODE == FALSE

           mov      pc,lr

             ]

       MEND

;該宏定義的作用是有條件地(當Z=1時)跳出子程序,返回被調用處

             MACRO

       MOVEQ_PC_LR

             [ THUMBCODE

        bxeq lr

             |

           moveq pc,lr

             ]

       MEND

 

;該宏定義是把中斷服務程序的首地址裝載到pc中

;在後面當遇到HandlerXXX HANDLER HandleXXX時,該宏被展開

;注意:HANDLER前的符號HandlerXXX比其後的符號HandleXXX多了一個r

;HandlerXXX爲ARM體系中統一定義的幾種異常中斷

;HandleXXX爲每個ARM處理器各自定義的中斷,見該文件最後部分的中斷向量表

             MACRO

$HandlerLabel HANDLER $HandleLabel

 

$HandlerLabel          

       sub  sp,sp,#4 ;ATPCS規定數據棧爲FD類型

 ;即棧指針指向棧頂元素,數據棧向內存地址減小的方向增長

 ;該語句是使棧地址減小4個字節,以留出空間裝載中斷服務函數首地址

       stmfd     sp!,{r0} ;由於要利用r0寄存器來傳遞數據,所以要保存r0數據,使其入棧

       ldr     r0,=$HandleLabel     ;把HandleXXX的地址裝到r0

       ldr     r0,[r0]    ;裝載中斷服務函數的起始地址

       str     r0,[sp,#4]               ;中斷函數首地址入棧

       ldmfd   sp!,{r0,pc} ;將事先保存的r0數據和中斷函數首地址出棧

                      ;並使系統跳轉到相應的中斷處理函數

       MEND

 

;導入連接器事先定義好的運行域中三個段變量

;ARM的可執行映像文件由RO、RW、ZI三個段組成

;RO爲代碼段,RW爲已初始化的全局變量,ZI爲未初始化的全局變量

       IMPORT  |Image$$RO$$Base|    ;RO段起始地址

       IMPORT  |Image$$RO$$Limit|   ;RO段結束地址加1,等於RW段起始地址

       IMPORT  |Image$$RW$$Base|   ;RW段起始地址

       IMPORT  |Image$$ZI$$Base|      ;ZI段起始地址

       IMPORT  |Image$$ZI$$Limit|     ;ZI段結束地址加1

 

;導入兩個關於MMU的函數,用於設置時鐘模式爲異步模式和快速總線模式

       IMPORT      MMU_SetAsyncBusMode

       IMPORT      MMU_SetFastBusMode  ;

 

;導入Main,它爲C語言程序入口函數

       IMPORT  Main        ; The main entry of mon program

;導入用於複製從Nand Flash中的映像文件到SDRAM中的函數

       IMPORT  RdNF2SDRAM   ; Copy Image from Nand Flash to SDRAM

 

;定義代碼段,名爲Init

       AREA    Init,CODE,READONLY

 

;在入口處(0x0)開始的8個字單元空間內,存放的是ARM異常中斷向量表,每個字單元空間都是一條跳轉指令,當異常發生時,ARM會自動跳轉到相應的中斷向量處,並由該處的跳轉指令再跳轉到相應的執行函數處

       ENTRY        ;程序入口處

       EXPORT     __ENTRY           ;導出__ENTRY,即導出代碼段入口地址

__ENTRY                                ;主要用於MMU

ResetEntry

       ;1)The code, which converts to Big-endian, should be in little endian code.

       ;2)The following little endian code will be compiled in Big-Endian mode.

       ;  The code byte order should be changed as the memory bus width.

       ;3)The pseudo instruction,DCD can not be used here because the linker generates error.

;在0x0處的異常中斷是復位異常中斷,是上電後執行的第一條指令

;變量ENDIAN_CHANGE用於標記是否要從小端模式改變爲大端模式,因爲編譯器初始模式是小端模式,如果要用大端模式,就要事先把該變量設置爲TRUE,否則爲FLASE

;變量ENTRY_BUS_WIDTH用於設置總線的寬度,因爲用16位和8位寬度來表示32位數據時,在大端模式下,數據的含義是不同的

;由於要考慮到大端和小端模式,以及總線的寬度,因此該處看似較複雜,其實只是一條跳轉指令:當爲大端模式時,跳轉到ChangeBigEndian函數處,否則跳轉到ResetHandler函數處

       ASSERT      :DEF:ENDIAN_CHANGE    ;判斷是否定義了ENDIAN_CHANGE

                                ;如果沒有定義,則報告該處錯誤信息

       [ ENDIAN_CHANGE                   ;if ENDIAN_CHANGE ==TRUE

              ASSERT  :DEF:ENTRY_BUS_WIDTH  ;判斷是否定義了ENTRY_BUS_WIDTH

                                       ;如果沒有定義,則報告該處錯誤信息

 

              [ ENTRY_BUS_WIDTH=32         ;if ENTRY_BUS_WIDTH ==32

;跳轉到ChangeBigEndian(ChangeBigEndian在0x24),因此該條指令的機器碼爲0xea000007

;所以該語句與在該處(即0x0處)直接放入0xea000007數據(即DCD 0xea000007)作用相同

                     b     ChangeBigEndian     

              ]

 

              [ ENTRY_BUS_WIDTH=16         ;if ENTRY_BUS_WIDTH ==16

;在小端模式下,用16位或8位數據總線寬度表示32位數據,與用32位總線寬度表示32位數據,格式完全一致。但在大端模式下,格式就會發生變化

;在復位時,系統默認的是小端模式,所以就要人爲地改變數據格式,使得用16位大端數據表示的32位數據也能被小端模式的系統識別

;該語句的目的也是跳轉到ChangeBigEndian,即機器碼也應該是0xea000007,但爲了讓小端模式系統識別,就要把機器碼的順序做一下調整,改爲0x0007ea00,那麼我們就可以用DCD 0x0007ea00把機器碼裝載進去了,但由於該處不能使用DCD僞指令,因此我們就要用一條真實的指令來代替DCD 0x0007ea00,即該指令編譯後的機器碼也爲0x0007ea00,而andeq  r14,r7,r0,lsl #20就是一條編譯後機器碼爲0x0007ea00的指令,所以我們在該處寫上該條指令

                     andeq    r14,r7,r0,lsl #20   ;DCD 0x0007ea00

              ]

 

              [ ENTRY_BUS_WIDTH=8           ;if ENTRY_BUS_WIDTH ==8

;該語句的分析與上一段代碼的分析相似

;streq  r0,[r0,-r10,ror #1]編譯後的機器碼爲0x070000ea

                     streq      r0,[r0,-r10,ror #1] ;DCD 0x070000ea

              ]

              |             ;else 即ENDIAN_CHANGE ==FALSE

              b     ResetHandler             ;跳轉到ResetHandler處,復位

       ]

       b     HandlerUndef     ;未定義

       b     HandlerSWI ;軟件中斷

       b     HandlerPabort     ;指令預取中止

       b     HandlerDabort    ;數據訪問中止

       b     .             ;保留,跳轉到自身地址處,即進入死循環

       b     HandlerIRQ ;外部中斷請求

       b     HandlerFIQ ;快速中斷請求

;以上爲異常中斷向量表

 

;跳轉到EnterPWDN,處理電源管理的其他非正常模式,在C語言程序段中被調用

;該處地址爲0x20,至於爲什麼要在該處執行,我認爲可能是該處離異常中斷向量表最近吧

       b     EnterPWDN       ; Must be @0x20.

 

;由0x0跳轉至此,目的是把小端模式改爲大端模式,即把CP15中的寄存器C1中的第7位置1

ChangeBigEndian

;@0x24

       [ ENTRY_BUS_WIDTH=32         ;if ENTRY_BUS_WIDTH == 32

       ;執行mrc p15,0,r0,c1,c0,0,得到CP15中的寄存器C1,放入r0中

       ;由於mrc p15,0,r0,c1,c0,0的機器碼爲0xee110f10

       ;因此DCD      0xee110f10的意思就是mrc p15,0,r0,c1,c0,0。下同

              DCD     0xee110f10  ;0xee110f10 => mrc p15,0,r0,c1,c0,0

       ;執行orr r0,r0,#0x80,置r0中的第7位爲1,表示選擇大端模式

              DCD     0xe3800080 ;0xe3800080 => orr r0,r0,#0x80;  //Big-endian

       ;執行mcr p15,0,r0,c1,c0,0,把r0寫入CP15中的寄存器C1

              DCD     0xee010f10  ;0xee010f10 => mcr p15,0,r0,c1,c0,0

       ]

       [ ENTRY_BUS_WIDTH=16         ;if ENTRY_BUS_WIDTH == 16

       ;由於此時系統還不能識別16位或8位大端模式下表示的32爲數據

       ;因此還需人爲地進行數據調整,即把0xee110f10變爲0x0f10ee11

       ;然後用DCD指令存入該數據。下同

              DCD 0x0f10ee11

              DCD 0x0080e380

              DCD 0x0f10ee01

       ]

       [ ENTRY_BUS_WIDTH=8           ;if ENTRY_BUS_WIDTH == 8

              DCD 0x100f11ee

              DCD 0x800080e3

              DCD 0x100f01ee

       ]

    ;相當於NOP指令

    ;作用是等待系統從小端模式向大端模式轉換

    ;此後系統就能夠自動識別出不同總線寬度下的大端模式,因此以後就無需再人爲調整指令了

       DCD 0xffffffff  ;swinv 0xffffff is similar with NOP and run well in both endian mode.

       DCD 0xffffffff

       DCD 0xffffffff

       DCD 0xffffffff

       DCD 0xffffffff

       b ResetHandler       ;跳轉到ResetHandler

   

;當系統進入異常中斷後,由存放在0x0~0x1C處的中斷向量地址中的跳轉指令,跳轉到此處相應的位置,並由事先定義好的宏定義再次跳轉到相應的中斷服務程序中

HandlerFIQ HANDLER HandleFIQ

HandlerIRQ HANDLER HandleIRQ

HandlerUndef     HANDLER HandleUndef

HandlerSWI HANDLER HandleSWI

HandlerDabort    HANDLER HandleDabort

HandlerPabort     HANDLER HandlePabort

 

;下面這段代碼是用於處理非向量中斷,即由軟件程序來判斷到底發生了哪種中斷,然後跳轉到相應地中斷服務程序中

;具體地說就是,當發生中斷時,會置INTOFFSET寄存器相應的位爲1,然後通過查表(見該程序末端部分的中斷向量表),找到相對應的中斷入口地址

;觀察中斷向量表,會發現它與INTOFFSET寄存器中的中斷源正好相對應,即向量表的順序與INTOFFSET寄存器中的中斷源的由小到大的順序一致,因此我們可以用基址加變址的方式很容易找到相對應的中斷入口地址。其中基址爲向量表的首箇中斷源地址,變址爲INTOFFSET寄存器的值乘以4(因爲系統是用4個字節單元來存放一箇中斷向量)

IsrIRQ

       sub  sp,sp,#4        ;在棧中留出4個字節空間,以便保存中斷入口地址

       stmfd     sp!,{r8-r9}    ;由於要用到r8和r9,因此保存這兩個寄存器內的值

 

       ldr   r9,=INTOFFSET       ;把INTOFFSET寄存器地址裝入r9內

       ldr   r9,[r9]           ;讀取INTOFFSET寄存器內容

       ldr   r8,=HandleEINT0      ;得到中斷向量表的基址

       add r8,r8,r9,lsl #2              ;用基址加變址的方式得到中斷向量表的地址

       ldr   r8,[r8]                  ;得到中斷服務程序入口地址

       str   r8,[sp,#8]                    ;使中斷服務程序入口地址入棧

       ldmfd    sp!,{r8-r9,pc}      ;使r8,r9和入口地址出棧,並跳到中斷服務程序中

 

 

;定義一個數據緩衝池,供ldr僞指令使用

       LTORG

 

;=======

; ENTRY

;=======

;系統上電或復位後,由0x0處的跳轉指令,跳轉到該處開始真正執行系統的初始化工作

ResetHandler

    ;在系統初始化過程中,不需要看門狗,因此關閉看門狗功能

       ldr   r0,=WTCON       ;watch dog disable

       ldr   r1,=0x0

       str   r1,[r0]

 

    ;同樣,此時也不應該響應任何中斷,因此屏蔽所有中斷,以及子中斷

       ldr   r0,=INTMSK

       ldr   r1,=0xffffffff  ;all interrupt disable

       str   r1,[r0]

 

       ldr   r0,=INTSUBMSK

       ldr   r1,=0x7fff           ;all sub interrupt disable

       str   r1,[r0]

 

;由於啓動文件是無法仿真的,因此爲了判斷該文件中語句的正確與否,往往在需要觀察的地方加上一段點亮LED的程序,這樣就可以知道程序是否已經執行到此處

;下面方括號內的程序就是點亮LED的小程序

       [ {FALSE}

              ;rGPFDAT = (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);

              ; Led_Display

              ldr   r0,=GPBCON

              ldr   r1,=0x155500

              str   r1,[r0]

              ldr   r0,=GPBDAT

              ldr   r1,=0x0

              str   r1,[r0]

       ]

 

;下列程序是用於設置系統時鐘頻率

;設置PLL的鎖定時間常數,以得到一定時間的延時

       ;To reduce PLL lock time, adjust the LOCKTIME register.

       ldr   r0,=LOCKTIME

       ldr   r1,=0xffffff

       str   r1,[r0]

 

       [ PLL_ON_START

              ; Added for confirm clock divide. for 2440.

              ; Setting value Fclk:Hclk:Pclk

       ;設置系統的三個時鐘頻率FCLK、HCLK、PCLK

              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]

 

       ;program has not been copied, so use these directly

              [ CLKDIV_VAL>1               ;if 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

                     |                                  ;else

           ;設置時鐘模式爲快速總線模式

                     mrc p15,0,r0,c1,c0,0

                     bic r0,r0,#0xc0000000;R1_iA:OR:R1_nF

                     mcr p15,0,r0,c1,c0,0

              ]

 

       ;配置UPLL

       ;按照手冊中的計算公式,確定MDIV、PDIV和SDIV

       ;得到當系統輸入時鐘頻率爲12MHz的情況下,UCLK輸出頻率爲48MHz

              ;Configure UPLL

              ldr   r0,=UPLLCON

              ldr   r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)                          ;Fin = 12.0MHz, UCLK = 48MHz

              str   r1,[r0]

       ;等待至少7個時鐘週期,以保證系統的正確配置

              nop ; Caution: After UPLL setting, at least 7-clocks delay must be inserted for setting hardware be completed.

              nop

              nop

              nop

              nop

              nop

              nop

       ;配置MPLL,同UPLL

              ;Configure MPLL

              ldr   r0,=MPLLCON

              ldr   r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)                         ;Fin = 12.0MHz, FCLK = 400MHz

              str   r1,[r0]

       ]

 

    ;從SLEEP模式下被喚醒,類似於RESET引腳被觸發,因此它也要從0x0處開始執行

    ;在此處要判斷是否是由SLEEP模式喚醒引起的復位

       ;Check if the boot is caused by the wake-up from SLEEP mode.

       ldr   r1,=GSTATUS2

       ldr   r0,[r1]

       tst   r0,#0x2         ;檢查GSTATUS2寄存器的第1位

       ;In case of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler.

       bne WAKEUP_SLEEP           ;是被喚醒的,則跳轉

 

;設置一個被喚醒復位後的起始點地址標號,可以把它保存到GSTATUS3中

;導出該地址標號,以便在C語言程序中使用

       EXPORT StartPointAfterSleepWakeUp

StartPointAfterSleepWakeUp

 

;設置內存控制寄存器

;關於內存控制寄存器一共有以BWSCON爲開始的連續放置的13個寄存器,我們要一次性批量完成這13個寄存器的配置

;因此開闢一段以SMRDATA爲地址起始點的13個字單元空間,按順序放入要寫入的13個寄存器內容

       ;Set memory control registers

      ;ldr  r0,=SMRDATA

      adrl r0, SMRDATA   ;得到SMRDATA空間的首地址

       ldr   r1,=BWSCON           ;得到BWSCON的地址

       add r2, r0, #52            ;得到SMRDATA空間的末地址

 

;完成13個字數據的複製

0

       ldr   r3, [r0], #4

       str   r3, [r1], #4

       cmp       r2, r0

       bne %B0

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;       When EINT0 is pressed,  Clear SDRAM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; check if EIN0 button is pressed

;檢查EIN0按鈕是否被按下

       ldr   r0,=GPFCON

       ldr   r1,=0x0

       str   r1,[r0]           ;GPFCON=0,F口爲輸入

       ldr   r0,=GPFUP

       ldr   r1,=0xff

       str   r1,[r0]           ;GPFUP=0xff,上拉功能無效

 

       ldr   r1,=GPFDAT

       ldr   r0,[r1]           ;讀取F口數據

       bic  r0,r0,#(0x1e<<1)        ;僅保留第1位數據,其他清0

       tst   r0,#0x1                ;判斷第1位

       bne %F1                     ;不爲0表示按鈕沒有被按下,則向前跳轉,不執行清空SDRAM

   

   

 

; Clear SDRAM Start

;清空SDRAM

       ldr   r0,=GPFCON

       ldr   r1,=0x55aa

       str   r1,[r0]           ;GPF7~4爲輸出,GPF3~0爲中斷

;      ldr   r0,=GPFUP

;      ldr   r1,=0xff

;      str   r1,[r0]          ;上拉功能無效

       ldr   r0,=GPFDAT

       ldr   r1,=0x0

       str   r1,[r0]           ;GPFDAT = 0

 

       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 RAM

       ldr   r0,=0x30000000         ;RAM首地址

;清空64MB的RAM

0    

       stmia      r0!,{r1-r8}

       subs       r9,r9,#32

       bne %B0

 

;Clear SDRAM End

 

1

;初始化各種處理器模式下的堆棧

       ;Initialize stacks

       bl    InitStacks                   ;跳轉到InitStacks

 

;===========================================================

   

;下面的代碼爲把ROM中的數據複製到RAM中

       ldr   r0, =BWSCON

       ldr   r0, [r0]

       ands       r0, r0, #6              ;讀取OM[1:0]引腳狀態

 ;爲0表示從NAND Flash啓動,不爲0則從NOR Flash啓動

       bne copy_proc_beg    ;跳轉,不用讀取NAND Flash

       adr  r0, ResetEntry            ;OM[1:0] == 0,從NAND Flash啓動

       cmp       r0, #0                          ;if use Multi-ice,

       bne copy_proc_beg           ;do not read nand flash for boot

       ;nop

;===========================================================

nand_boot_beg

       [ {TRUE}

              bl RdNF2SDRAM           ;複製NAND Flash到SDRAM

       ]

 

       ldr   pc, =copy_proc_beg

;===========================================================

copy_proc_beg

       adr  r0, ResetEntry

       ldr   r2, BaseOfROM

       cmp       r0, r2             ;比較程序入口地址與連接器定義的RO基地址

       ldreq      r0, TopOfROM   ;如果相等,把RO尾地址讀取到r0中

       beq InitRam        ;如果相等,則跳轉

       ldr r3, TopOfROM                  ;否則,把RO尾地址讀取到r3中

;下列循環體爲在程序入口地址與連接器定義的RO基地址不相等的情況下,把程序複製到RAM中

0    

       ldmia     r0!, {r4-r7}

       stmia      r2!, {r4-r7}

       cmp       r2, r3

       bcc  %B0

;修正非字對齊的情況

       sub  r2, r2, r3

       sub  r0, r0, r2                           

      

InitRam

       ldr   r2, BaseOfBSS

       ldr   r3, BaseOfZero  

;下面循環體爲複製已初始化的全局變量

0

       cmp       r2, r3

       ldrcc      r1, [r0], #4

       strcc       r1, [r2], #4

       bcc  %B0     

 

;下面循環體是爲未初始化的全局變量賦值爲0

       mov       r0,   #0

       ldr   r3,   EndOfBSS

1    

       cmp       r2,   r3

       strcc       r0, [r2], #4

       bcc  %B1

   

       ldr   pc, =%F2             ;goto compiler address

2

   

;      [ CLKDIV_VAL>1               ;if FCLK:HCLK≠1:1

;      bl    MMU_SetAsyncBusMode      ;設置時鐘模式爲異步模式

;      |

;      bl MMU_SetFastBusMode     ;設置時鐘模式爲快速總線模式

;      ]

   

 

;===========================================================

;普通中斷處理

;當普通中斷髮生時,執行的是IsrIRQ

     ; Setup IRQ handler

       ldr   r0,=HandleIRQ   ;This routine is needed

       ldr   r1,=IsrIRQ   ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c

       str   r1,[r0]

 

 

;完成最基本的初始化任務,跳轉到由C語言撰寫的Main()函數內繼續執行其他程序

;這裏不能寫main,因爲寫了main,系統會自動爲我們完成一些初始化工作,而這些工作在這段程序中是由我們顯式地人爲完成的。

    [ :LNOT:THUMBCODE

             bl    Main      ;Do not use main() because ......

             b     .

    ]

 

    [ THUMBCODE      ;for start-up code for Thumb mode

             orr   lr,pc,#1

             bx   lr

             CODE16

             bl    Main      ;Do not use main() because ......

             b     .

              CODE32

    ]

 

;初始化堆棧函數

;function initializing stacks

InitStacks

       ;Do not use DRAM,such as stmfd,ldmfd......

       ;Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'

;改變CPSR中M控制位,切換到相應的處理器模式下

;爲各自模式下的SP賦值

       mrs r0,cpsr

       bic  r0,r0,#MODEMASK

       orr   r1,r0,#UNDEFMODE|NOINT

       msr cpsr_cxsf,r1         ;UndefMode

       ldr   sp,=UndefStack         ; UndefStack=0x33FF_5C00

 

       orr   r1,r0,#ABORTMODE|NOINT

       msr cpsr_cxsf,r1         ;AbortMode

       ldr   sp,=AbortStack          ; AbortStack=0x33FF_6000

 

       orr   r1,r0,#IRQMODE|NOINT

       msr cpsr_cxsf,r1         ;IRQMode

       ldr   sp,=IRQStack            ; IRQStack=0x33FF_7000

 

       orr   r1,r0,#FIQMODE|NOINT

       msr cpsr_cxsf,r1         ;FIQMode

       ldr   sp,=FIQStack             ; FIQStack=0x33FF_8000

 

       bic  r0,r0,#MODEMASK|NOINT

       orr   r1,r0,#SVCMODE

       msr cpsr_cxsf,r1         ;SVCMode

       ldr   sp,=SVCStack           ; SVCStack=0x33FF_5800

 

;系統模式和用戶模式共用一個棧空間,因此不用再重複設置用戶模式堆棧

;系統復位後進入的是SVC模式,而且各種模式下的lr不同,因此要想從該函數內返回,要首先切換到SVC模式,再使用lr,這樣可以正確返回了

       mov       pc,lr

       ;The LR register will not be valid if the current mode is not SVC mode.

   

;定義一個數據緩衝池

       LTORG

 

;連續13個內存控制寄存器的定義空間

SMRDATA DATA

; Memory configuration should be optimized for best performance

; The following parameter is not optimized.

; Memory access cycle parameter strategy

; 1) The memory settings is  safe parameters even at HCLK=75Mhz.

; 2) SDRAM refresh period is for HCLK<=75Mhz.

 

       DCD (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))

       DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))   ;GCS0

       DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))   ;GCS1

       DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))   ;GCS2

       DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))   ;GCS3

       DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))   ;GCS4

       DCD ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))   ;GCS5

       DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))    ;GCS6

       DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))    ;GCS7

       DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Tsrc<<18)+(Tchr<<16)+REFCNT)

 

       DCD 0x32       ;SCLK power saving mode, BANKSIZE 128M/128M

 

       DCD 0x30       ;MRSR6 CL=3clk

       DCD 0x30       ;MRSR7 CL=3clk

   

;運行域定義

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|

 

;重新使數據字對齊

       ALIGN

   

;Function for entering power down mode

; 1. SDRAM should be in self-refresh mode.

; 2. All interrupt should be maksked for SDRAM/DRAM self-refresh.

; 3. LCD controller should be disabled for SDRAM/DRAM self-refresh.

; 4. The I-cache may have to be turned on.

; 5. The location of the following code may have not to be changed.

 

;掉電模式函數

;在C語言中定義爲:#define EnterPWDN(clkcon) ((void (*)(int))0x20)(clkcon)

;void EnterPWDN(int clkcon);

EnterPWDN

       mov r2,r0             ;r0爲該函數輸入參數clkcon

       tst r0,#0x8           ;判斷clkcon中的第3位,是否要切換到SLEEP模式

       bne ENTER_SLEEP ;切換到SLEEP模式

 

ENTER_STOP          ;IDLE模式

    ;設置SDRAM爲自刷新方式

       ldr r0,=REFRESH

       ldr r3,[r0]             ;r3=rREFRESH

       mov r1, r3

       orr r1, r1, #BIT_SELFREFRESH

       str r1, [r0]            ;Enable SDRAM self-refresh

 

    ;等待一段時間

       mov r1,#16                 ;wait until self-refresh is issued. may not be needed.

0     subs r1,r1,#1

       bne %B0

 

       ldr r0,=CLKCON            

       str r2,[r0]                    ;置第2位,進入IDLE模式

 

    ;等待一段時間

       mov r1,#32

0     subs r1,r1,#1 ;1) wait until the STOP mode is in effect.

       bne %B0             ;2) Or wait here until the CPU&Peripherals will be turned-off

                     ;   Entering SLEEP mode, only the reset by wake-up is available.

 

    ;從IDLE模式下被喚醒,系統從該處繼續執行

 

    ;取消SDRAM自刷新方式

       ldr r0,=REFRESH ;exit from SDRAM self refresh mode.

       str r3,[r0]

 

       MOV_PC_LR           ;返回,該語句爲一個宏定義

 

ENTER_SLEEP        ;SLEEP模式

       ;NOTE.

       ;1) rGSTATUS3 should have the return address after wake-up from SLEEP mode.

 

    ;設置SDRAM爲自刷新方式

       ldr r0,=REFRESH

       ldr r1,[r0]             ;r1=rREFRESH

       orr r1, r1, #BIT_SELFREFRESH

       str r1, [r0]            ;Enable SDRAM self-refresh

 

    ;等待一段時間

       mov r1,#16                 ;Wait until self-refresh is issued,which may not be needed.

0     subs r1,r1,#1

       bne %B0

 

    ;在進入SLEEP模式之前,配置必要的時鐘和OFFREFRESH

       ldr   r1,=MISCCR

       ldr   r0,[r1]

       orr   r0,r0,#(7<<17)  ;Set SCLK0=0, SCLK1=0, SCKE=0.

       str   r0,[r1]

 

       ldr r0,=CLKCON            

       str r2,[r0]                    ;置第3位,進入SLEEP模式

 

       b .                 ;CPU will die here.

 

 

;從SLEEP模式下被喚醒函數

WAKEUP_SLEEP

       ;Release SCLKn after wake-up from the SLEEP mode.

    ;設置時鐘和OFFREFRESH

       ldr   r1,=MISCCR

       ldr   r0,[r1]

       bic  r0,r0,#(7<<17)  ;SCLK0:0->SCLK, SCLK1:0->SCLK, SCKE:0->=SCKE.

       str   r0,[r1]

 

       ;Set memory control registers

    ;配置內存控制寄存器

      ldr   r0,=SMRDATA  ;be careful!

       ldr   r1,=BWSCON    ;BWSCON Address

       add r2, r0, #52     ;End address of SMRDATA

0

       ldr   r3, [r0], #4

       str   r3, [r1], #4

       cmp       r2, r0

       bne %B0

 

    ;等待一段時間

       mov r1,#256

0     subs r1,r1,#1 ;1) wait until the SelfRefresh is released.

       bne %B0

 

    ;GSTATUS3存放着想要從SLEEP模式喚醒後的執行地址

       ldr r1,=GSTATUS3 ;GSTATUS3 has the start address just after SLEEP wake-up

       ldr r0,[r1]

 

       mov pc,r0            ;跳轉到GSTATUS3存放的地址處

   

;=====================================================================

; Clock division test

; Assemble code, because VSYNC time is very short

;=====================================================================

       EXPORT CLKDIV124

       EXPORT CLKDIV144

   

CLKDIV124

   

       ldr     r0, = CLKDIVN

       ldr     r1, = 0x3             ; 0x3 = 1:2:4

       str     r1, [r0]

;      wait until clock is stable

       nop

       nop

       nop

       nop

       nop

 

       ldr     r0, = REFRESH

       ldr     r1, [r0]

       bic         r1, r1, #0xff

       bic         r1, r1, #(0x7<<8)

       orr          r1, r1, #0x470      ; REFCNT135

       str     r1, [r0]

       nop

       nop

       nop

       nop

       nop

       mov     pc, lr

 

CLKDIV144

       ldr     r0, = CLKDIVN

       ldr     r1, = 0x4             ; 0x4 = 1:4:4

       str     r1, [r0]

;      wait until clock is stable

       nop

       nop

       nop

       nop

       nop

 

       ldr     r0, = REFRESH

       ldr     r1, [r0]

       bic         r1, r1, #0xff

       bic         r1, r1, #(0x7<<8)

       orr          r1, r1, #0x630      ; REFCNT675 - 1520

       str     r1, [r0]

       nop

       nop

       nop

       nop

       nop

       mov     pc, lr

 

 

       ALIGN

 

       AREA RamData, DATA, READWRITE

 

;在0x33FF_FF00處定義中斷向量表

;^是MAP的同義詞,#是FIELD的同義詞

       ^   _ISR_STARTADDRESS             ; _ISR_STARTADDRESS=0x33FF_FF00

HandleReset      #   4

HandleUndef            #   4

HandleSWI         #   4

HandlePabort         #   4

HandleDabort        #   4

HandleReserved     #   4

HandleIRQ         #   4

HandleFIQ          #   4

 

;Do not use the label 'IntVectorTable',

;The value of IntVectorTable is different with the address you think it may be.

;IntVectorTable

;@0x33FF_FF20

HandleEINT0            #   4

HandleEINT1            #   4

HandleEINT2            #   4

HandleEINT3            #   4

HandleEINT4_7 #   4

HandleEINT8_23      #   4

HandleCAM              #   4           ; Added for 2440.

HandleBATFLT #   4

HandleTICK              #   4

HandleWDT              #   4

HandleTIMER0        #   4

HandleTIMER1        #   4

HandleTIMER2        #   4

HandleTIMER3        #   4

HandleTIMER4        #   4

HandleUART2        #   4

;@0x33FF_FF60

HandleLCD              #   4

HandleDMA0            #   4

HandleDMA1            #   4

HandleDMA2            #   4

HandleDMA3            #   4

HandleMMC              #   4

HandleSPI0        #   4

HandleUART1          #   4

HandleNFCON         #   4           ; Added for 2440.

HandleUSBD            #   4

HandleUSBH            #   4

HandleIIC           #   4

HandleUART0 #   4

HandleSPI1              #   4

HandleRTC               #   4

HandleADC              #   4

;@0x33FF_FFA0

       END                    ;程序結尾

發佈了56 篇原創文章 · 獲贊 12 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章