天天看點

tq2440 啟動代碼分析

一直畏懼啟動代碼,和彙編,今天搜資料的時候碰見我學習用的arm 開發闆的啟動代碼的分析,看着還不錯,是以在這和大家分享一下,共同參考學習。

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

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

; 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;#include "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 [email protected] 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三個段組成,可以了解為text、data、bss

;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的函數,用于設定時鐘模式為異步模式和快速總線模式

IMPORTMMU_SetAsyncBusMode

IMPORTMMU_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,而andeqr14,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

;是以DCD0xee110f10的意思就是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

HandlerUndefHANDLER HandleUndef

HandlerSWI HANDLER HandleSWI

HandlerDabortHANDLER HandleDabort

HandlerPabortHANDLER HandlePabort

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

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

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

Isr IRQ

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

ldrr0,=UPLLCON

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

strr1,[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

ldrr0,=MPLLCON

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

strr1,[r0]

]

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

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

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

ldrr1,=GSTATUS2

ldrr0,[r1]

tstr0,#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

 ;ldrr0,=SMRDATA

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

ldrr1,=BWSCON;得到BWSCON的位址

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

;完成13個字資料的複制

ldrr3, [r0], #4

strr3, [r1], #4

cmpr2, r0

bne %B0

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

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

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

; check if EIN0 button is pressed

;檢查EIN0按鈕是否被按下

       ldrr0,=GPFCON

ldrr1,=0x0

strr1,[r0];GPFCON=0,F口為輸入

ldrr0,=GPFUP

ldrr1,=0xff

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

ldrr1,=GPFDAT

ldrr0,[r1];讀取F口資料

bicr0,r0,#(0x1e<<1);僅保留第1位資料,其他清0

tstr0,#0x1;判斷第1位

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

; Clear SDRAM Start

;清空SDRAM

ldrr0,=GPFCON

ldrr1,=0x55aa

strr1,[r0];GPF7~4為輸出,GPF3~0為中斷

;ldrr0,=GPFUP

;ldrr1,=0xff 

;strr1,[r0] ;上拉功能無效

ldrr0,=GPFDAT

ldrr1,=0x0

strr1,[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

ldrr9,=0x4000000;64MB RAM

ldrr0,=0x30000000;RAM首位址

;清空64MB的RAM

stmiar0!,{r1-r8}

subsr9,r9,#32 

bne %B0

;Clear SDRAM End

1

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

;Initialize stacks

blInitStacks;跳轉到InitStacks

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

;下面的代碼為把ROM中的資料複制到RAM中

ldrr0, =BWSCON

ldrr0, [r0]

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

 ;為0表示從NAND Flash啟動,不為0則從NOR Flash啟動

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

adrr0, ResetEntry;OM[1:0] == 0,從NAND Flash啟動

cmpr0, #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

]

ldrpc, =copy_proc_beg

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

copy_proc_beg

adrr0, ResetEntry

ldrr2, BaseOfROM

cmpr0, r2;比較程式入口位址與連接配接器定義的RO基位址

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

beq InitRam;如果相等,則跳轉

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

;下列循環體為在程式入口位址與連接配接器定義的RO基位址不相等的情況下,把程式複制到RAM中

ldmiar0!, {r4-r7}

stmiar2!, {r4-r7}

cmpr2, r3

bcc%B0

;修正非字對齊的情況

subr2, r2, r3

subr0, r0, r2

InitRam 

ldrr2, BaseOfBSS

ldrr3, BaseOfZero

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

cmpr2, r3

ldrccr1, [r0], #4

strccr1, [r2], #4

bcc%B0

;下面循環體是為未初始化的全局變量指派為0

movr0,#0

ldrr3,EndOfBSS

1

cmpr2,r3

strccr0, [r2], #4

bcc%B1

ldrpc, =%F2;goto compiler address

2

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

;blMMU_SetAsyncBusMode;設定時鐘模式為異步模式

;|

;bl MMU_SetFastBusMode;設定時鐘模式為快速總線模式

;]

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

;普通中斷處理

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

  ; Setup IRQ handler

ldrr0,=HandleIRQ;This routine is needed

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

strr1,[r0]

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

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

    [ :LNOT:THUMBCODE

 blMain;Do not use main() because ......

 b.

    ]

    [ THUMBCODE;for start-up code for Thumb mode

 orrlr,pc,#1

 bxlr

 CODE16

 blMain;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

bicr0,r0,#MODEMASK

orrr1,r0,#UNDEFMODE|NOINT

msr cpsr_cxsf,r1;UndefMode

ldrsp,=UndefStack; UndefStack=0x33FF_5C00

orrr1,r0,#ABORTMODE|NOINT

msr cpsr_cxsf,r1;AbortMode

ldrsp,=AbortStack; AbortStack=0x33FF_6000

orrr1,r0,#IRQMODE|NOINT

msr cpsr_cxsf,r1;IRQMode

ldrsp,=IRQStack; IRQStack=0x33FF_7000

orrr1,r0,#FIQMODE|NOINT

msr cpsr_cxsf,r1;FIQMode

ldrsp,=FIQStack; FIQStack=0x33FF_8000

bicr0,r0,#MODEMASK|NOINT

orrr1,r0,#SVCMODE

msr cpsr_cxsf,r1;SVCMode

ldrsp,=SVCStack; SVCStack=0x33FF_5800

;系統模式和使用者模式共用一個棧空間,是以不用再重複設定使用者模式堆棧

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

movpc,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

;運作域定義

BaseOfROMDCD|Image$$RO$$Base|

TopOfROM DCD|Image$$RO$$Limit|

BaseOfBSSDCD|Image$$RW$$Base|

BaseOfZero DCD|Image$$ZI$$Base|

EndOfBSSDCD|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.

0subs r1,r1,#1

bne %B0

ldr r0,=CLKCON

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

;等待一段時間

mov r1,#32

0subs 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.

0subs r1,r1,#1

bne %B0

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

ldrr1,=MISCCR

ldrr0,[r1]

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

strr0,[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

ldrr1,=MISCCR

ldrr0,[r1]

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

strr0,[r1]

;Set memory control registers

;配置記憶體控制寄存器

 ldrr0,=SMRDATA;be careful! 

ldrr1,=BWSCON;BWSCON Address

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

ldrr3, [r0], #4

strr3, [r1], #4

cmpr2, r0

bne %B0

;等待一段時間

mov r1,#256

0subs 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]

bicr1, r1, #0xff

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

orrr1, 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]

bicr1, r1, #0xff

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

orrr1, 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;程式結尾