/*文件名:2440init.s
;*功能:2440启动代码,实现硬件初始化及存储空间的映射等
;*设计者:enjoymylinux
;*设计时间:2010.2.10
;*版本:V1.0
;*参考程序:友善之臂启动代码及sumsang的bootloader源代码vivi
;*/
;//注意点:由于ARM是32位的,一般都按照32位的字对齐
;//64M SDRAM 内存空间分配状况
;//0x3000_0000 内存起始地址
;// > supervivi下载程序的缓存空间 共16M
;//0x3100_0000
;// > 程序的运行空间
;//0x31ff_0000
;// > 堆栈空间
;//0x33ff_8000
;// 预保留空间
;//0x33ff_ff00
;// > 中断向量表
;//0x3400_0000 内存结束地址
;//系统引导及启动过程
;//(1)硬件初始化 关闭看门狗,屏蔽所有中断
;//(2)时钟初始化 调整PLL锁相环到合适频率
;//(3)开启总线状态指示灯
;//(4)检测启动状态 boot启动还是外界唤醒恢复
;//(5)初始化内存 设置SDRAM寄存器,擦除内存
;//(6)初始化FLASH 设置FLASH寄存器,检测启动物理位置
;//(7)拷贝代码到内存 NOR FLASH可以直接运行;NAND FLASH必须拷贝到RAM中才行
;//(8)跳转到Main去执行
;//包含头文件,注意:在汇编程序中头文件后缀名为.inc,而在C程序中后缀名为.h
;// option.inc 定义配置PLL及总线宽度寄存器位
;// memcfg.inc 定义存储器的寄存器的位定义
;// 2440addr.inc 2440各寄存器的地址
;//获取头文件,其中GET可用include来代替,两者可以互换
GET option.inc
GET memcfg.inc
GET 2440addr.inc
BIT_SELFREFRESH EQU (1<<22)
;//定义一些常量,EQU相当#define语句
;//处理器各工作模式定义,即CPSR中后五位CPSR[4:0]
USERMODE EQU 0x10
FIQMODE EQU 0x11
IRQMODE EQU 0x12
SVCMODE EQU 0x13
ABORTMODE EQU 0x17
UNDEFMODE EQU 0x1b
MODEMASK EQU 0x1f
;//CPSR[7:5]
I_BIT EQU 0x80 ;//中断位
F_BIT EQU 0x40 ;//快速中断位
T_BIT EQU 0x20 ;//指令模式状态位
NOINT EQU 0xc0 ;//屏蔽中断位
;//定义stack的地址,其中_STACK_BASEADDRESS代表堆栈基地址
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 ~
;//检测是thumb指令集还是arm指令集
GBLL THUMBCODE ;//定义一个全局变量
[ {CONFIG} = 16 ;// #if(CONFIG=16)
THUMBCODE SETL {TRUE} ;// THUMBCODE=TRUE;
CODE32 ;//说明此后代码为arm指令
| ;// #else
THUMBCODE SETL {FALSE} ;// THUMBCODE=FALSE;
]
;//宏定义
MACRO ;//宏定义开始
MOV_PC_LR ;//宏名称
[ THUMBCODE
bx lr
|
mov pc,lr
]
MEND ;//宏定义结束
;//宏定义
MACRO
MOVEQ_PC_LR
[ THUMBCODE
bxeq lr
|
moveq pc,lr
]
MEND
;//带参数的宏定义
;//宏定义,作用是当发生中断时,将中断服务程序的首地址装载到PC中
;//$HandlerLabel HANDLER(宏名称) $HandleLabel(宏的参数)
;//*********************************************************
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;//SP减4,为了存储跳转地址
stmfd sp!,{r0} ;//将工作寄存器压入堆栈,
ldr r0,=$HandleLabel ;//装载HandleLable的地址到r0
ldr r0,[r0] ;//装载HandleLable的内容到r0,即服务程序的地址
str r0,[sp,#4] ;//存储HandleLable的内容到堆栈
ldmfd sp!,{r0,pc} ;//恢复r0的内容,并使PC跳转到HandleLable,即中断服务的首地址
MEND
;//*********************************************************
;//声明、通知编译器标号在其他源文件中定义,但要在本文件中调用
;//一个ARM程序是由RO、RW、ZI三段组成(具体可见编译器生成的空间分配list文件)
;//RO代码段(只读) RW 已经初始化的全局变量(读写) ZI未初始化的全局变量(零初始化)
;//这些地址是通过编译器的设定来确定的
IMPORT |Image$$RO$$Base| ;//RO段起始地址
IMPORT |Image$$RO$$Limit| ;//RO段结束地址加1
IMPORT |Image$$RW$$Base| ;//RW段起始地址
IMPORT |Image$$RW$$Limit| ;//RW段结束地址加1
IMPORT |Image$$ZI$$Base| ;//ZI段起始地址
IMPORT |Image$$ZI$$Limit| ;//ZI段结束地址加1
IMPORT MMU_SetAsyncBusMode
IMPORT MMU_SetFastBusMode ;
IMPORT Main ;//C代码的入口点
;//**********************************************************8
;//定义一个代码段,段名为Init,属性:代码,只读
AREA Init,CODE,READONLY
;//声明入口点,整个镜像从这个地方开始执行,一般来说一个文件里只能有一个
ENTRY
;//声明一个全局标号,可以在其他文件中被引用
EXPORT __ENTRY
__ENTRY ;//__ENTRY标号
ResetEntry ;//ResetEntry标号
;//指令 b :跳转指令,后加代码段标号,程序执行到此处时将会跳转到相应的代码段执行
;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.
ASSERT :DEF:ENDIAN_CHANGE ;;//断言伪定义,假定后面的ENDIAN标识已经定义
[ ENDIAN_CHANGE
ASSERT :DEF:ENTRY_BUS_WIDTH
[ ENTRY_BUS_WIDTH=32
b ChangeBigEndian ;DCD 0xea000007
]
[ ENTRY_BUS_WIDTH=16
andeq r14,r7,r0,lsl #20 ;DCD 0x0007ea00
]
[ ENTRY_BUS_WIDTH=8
streq r0,[r0,-r10,ror #1] ;DCD 0x070000ea
]
|
b ResetHandler
]
;/**********************************************************/
;//中断向量表vertor table
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
b EnterPWDN ; Must be @0x20.
;/***********************************************************/
;//ChangeBigEndian标号
ChangeBigEndian
;// 符号[]相当于编译指令#if····#else ····
;//通过编译指令,不同的总线宽度采取不同的存储单元分配措施
;//伪汇编指令DCD 用于分配一片连续的字节存储单元
[ ENTRY_BUS_WIDTH=32
DCD 0xee110f10 ;0xee110f10 => mrc p15,0,r0,c1,c0,0
DCD 0xe3800080 ;0xe3800080 => orr r0,r0,#0x80; //Big-endian
DCD 0xee010f10 ;0xee010f10 => mcr p15,0,r0,c1,c0,0
]
[ ENTRY_BUS_WIDTH=16
DCD 0x0f10ee11
DCD 0x0080e380
DCD 0x0f10ee01
]
[ ENTRY_BUS_WIDTH=8
DCD 0x100f11ee
DCD 0x800080e3
DCD 0x100f01ee
]
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
;//宏展开,正确理解代码内容,代码要按规则编写,不要引起误解
;//代码标号相当于C语言中的函数,标号后的代码内容为函数的内容,即函数的具体实现过程
HandlerFIQ
HANDLER HandleFIQ
HandlerIRQ
HANDLER HandleIRQ
HandlerUndef
HANDLER HandleUndef
HandlerSWI
HANDLER HandleSWI
HandlerDabort
HANDLER HandleDabort
HandlerPabort
HANDLER HandlePabort
;//IsrIRQ代码段,实现中断服务程序的跳转
IsrIRQ
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
ldr r9,=INTOFFSET
ldr r9,[r9]
ldr r8,=HandleEINT0
add r8,r8,r9,lsl #2
ldr r8,[r8]
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc}
LTORG
;/*************************************************************************************/
;//声明一个标号ResetHandler,当其他处引用此标号时,程序跳转到此处执行
;//ResetHandler代码段
ResetHandler
;//禁止看门狗
ldr r0,=WTCON ;//把寄存器WTCON的地址装入R0,watch dog disable
ldr r1,=0x0 ;//把0装入R1
str r1,[r0] ;//把R1内的值赋给地址为R0的存储区域,即WTCON,这三条语句相当于 WTCON=0x0;
;//禁止所有中断
ldr r0,=INTMSK
ldr r1,=0xffffffff ;all interrupt disable
str r1,[r0] ;//这三条语句相当于 INTMSK=0xffffffff;
;//禁止所有次级中断
ldr r0,=INTSUBMSK
ldr r1,=0x7fff ;all sub interrupt disable
str r1,[r0] ;//这三条语句相当于 INTSUBMSK=0x7ffff;
//打开状态指示灯LED
[ {TRUE}
;rGPFDAT = (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);
; Led_Display
ldr r0,=GPBCON
ldr r1,=0x00555555
str r1,[r0] ;//即GPCON=0x00555555;
ldr r0,=GPBDAT
ldr r1,=0x07fe
str r1,[r0] ;//即GPBDAT=0x07fe;
]
;//To reduce PLL lock time, adjust the LOCKTIME register.
ldr r0,=LOCKTIME
ldr r1,=0xffffff
str r1,[r0] ;//即LOCKTIME=0xffffff;
;/************************************************************************************/
[ PLL_ON_START
; Added for confirm clock divide. for 2440.
; Setting value 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] ;//即CLKDIVN=CLKDIV_VAL;
;//协处理器操作,MMU和CACHE属于P15协处理器,ARM处理器共支持多达16个协处理器
[ CLKDIV_VAL>1 ; means Fclk:Hclk is not 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
],
;//Configure UPLL
ldr r0,=UPLLCON
ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)
str r1,[r0] ;//即UPLLCON=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)
;//nop指令,不进行任何操作,空指令
nop ; Caution: After UPLL setting, at least 7-clocks delay must be inserted for setting hardware be completed.
nop
nop
nop
nop
nop
nop
;//Configure MPLL
ldr r0,=MPLLCON
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;Fin=16.9344MHz
str r1,[r0] ;//即MPLLCON=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)
]
;/************************************************************************************/
;Check if the boot is caused by the wake-up from SLEEP mode.
ldr r1,=GSTATUS2
ldr r0,[r1]
tst r0,#0x2 ;//TST位测试指令,逻辑与操作,更新CPSR中相应的标志位
;In case of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler.
bne WAKEUP_SLEEP ;//条件跳转指令,bne(b if not equal),beq(b if equal)
EXPORT StartPointAfterSleepWakeUp ;//声明外部可以引用
StartPointAfterSleepWakeUp
;//内存控制初始化
;//adrl 地址读取伪指令,谨慎使用,编译器将其分解成两条指令,若不能成功分解会报错
;//这段代码实现了8个BANK寄存器值的设定
adrl r0, SMRDATA
ldr r1,=BWSCON ;BWSCON Address
add r2, r0, #52 ;End address of SMRDATA
0
ldr r3, [r0], #4 ;//将地址为r0中的内容装载到r3,然后r0中的值加4,即指向下一个字
str r3, [r1], #4 ;//注意8个BANK控制寄存器的地址是靠着的
cmp r2, r0 ;//CMP指令会更新CPSR中的相应标志位,后面的指令根据此标志位来决定是否执行相应的操作
bne %B0 ;//如果不相等就向后跳转到标号0,类似一个循环,直到r0=r2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;; When EINT0 is pressed, Clear SDRAM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;//check if EIN0 button is pressed
;//当EIN0发生时先清除SDRAM再初始化堆栈,若没有发生则跳过SDRAM的清除阶段,
;//直接去初始化堆栈
ldr r0,=GPFCON
ldr r1,=0x0
str r1,[r0]
ldr r0,=GPFUP
ldr r1,=0xff
str r1,[r0]
ldr r1,=GPFDAT
ldr r0,[r1]
bic r0,r0,#(0x1e<<1) ;//bit clear 在值为1时清零
tst r0,#0x1 ;//按位与
bne %F1 ;//如果not equal 向前跳转到标号1
;// Clear SDRAM Start
ldr r0,=GPFCON
ldr r1,=0x55aa
str r1,[r0]
ldr r0,=GPFDAT
ldr r1,=0x0
str r1,[r0] ;LED=****
;//所有的寄存器都清零
mov r1,#0
mov r2,#0
mov r3,#0