;=========================================
; NAME: 2440INIT.S
; DESC: C start up codes
; Configure memory, ISR ,stacks
; Initialize C-variables
; HISTORY:
; 2002.02.25:kwtark: ver 0.0
; 2002.03.20:purnnamu: Add some functions for testing STOP,Sleep mode
; 2003.03.14:DonGo: Modified for 2440.
;=========================================
;首先,启动代码定义了一些常量
;//GET类似于C语言的include,option.inc文件内定义了一些全局变量,memcfg.inc文件内定义了关于内存bank的符号和数字常量,2440addr.inc文件内定义了用于汇编的s3c2440寄存器变量和地址
GET option.inc
GET memcfg.inc
GET 2440addr.inc
BIT_SELFREFRESH EQU (1<<22) ;//#define BIT_SELFREFRESH (1<<22)
;//对arm处理器模式寄存器对应的常数进行赋值,arm处理器有一个CPSR寄存器,它的后五位决定了处理器处于哪个模式下
USERMODE EQU 0x10
FIQMODE EQU 0x11
IRQMODE EQU 0x12
SVCMODE EQU 0x13
ABORTMODE EQU 0x17
UNDEFMODE EQU 0x1b
MODEMASK EQU 0x1f
NOINT EQU 0xc0 ;//禁止IRQ中断,FIQ中断
;//定义了7种处理器模式下的栈的起始地址,其中用户模式和系统模式共有一个栈空间
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 ~
;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.
;//这一段是统一arm的工作状态和对应的软件编译方式(16位编译环境使用tasm.exe编译)。
;//arm处理器的工作状态分为两种:32位,arm执行字对齐的arm指令集;16位,arm执行半字对齐的Thumb指令集。
;//不同的工作状态,编译方式也不一样。所以下面的程序就是判断arm的工作方式来确定它的编译方式。
GBLL THUMBCODE ;//定义THUMBCODE全局变量
[ {CONFIG} = 16 ;//如果发现时采用16位代码的话
THUMBCODE SETL {TRUE} ;//把THUMBCODE设置为TRUE
CODE32 ;//把处理器重新设置成ARM模式
| ;//如果处理器现在就是ARM模式
THUMBCODE SETL {FALSE} ;//把THUMBCODE设置为FALSE
]
MACRO ;定义一个宏,一个根据THUMBCODE把PC寄存器的值保存到LR的宏
MOV_PC_LR
[ THUMBCODE
bx lr ;在ARM模式中要使用BX指令跳转到THUMB指令,并转换模式
|
mov pc,lr ;如果目标地址也是Arm指令的话就采用这种方式
]
MEND
MACRO
MOVEQ_PC_LR
[ THUMBCODE
bxeq lr ;//r14
|
moveq pc,lr
]
MEND
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel //
sub sp,sp,#4 ;decrement sp(to store jump address)减少sp(用于存放跳转地址)
stmfd sp!,{r0} ;把工作寄存器压入栈
ldr r0,=$HandleLabel ;把Handlexxx的地址放入r0
ldr r0,[r0] ;把Handlexxx所指向的内容(也就是中断程序的入口)放入r0
str r0,[sp,#4] ;把中断服务程序(ISR)压入栈
ldmfd sp!,{r0,pc} ;用出栈的方式恢复r0的原值和为pc设定新值(也就完成了到ISR的跳转)
MEND
IMPORT |Image$$RO$$Base| ;代码的开始地址
IMPORT |Image$$RO$$Limit| ; ROM code的结束地址(=ROM data 的开始地址)
IMPORT |Image$$RW$$Base| ; 要初始化的RAM的开始地址
IMPORT |Image$$ZI$$Base| ; area(需要清零的RAM区域)的开始地址
IMPORT |Image$$ZI$$Limit| ; area的结束地址
IMPORT MMU_SetAsyncBusMode
IMPORT MMU_SetFastBusMode
IMPORT Main ; The main entry of mon program
AREA Init,CODE,READONLY ;这表明下面是一个名为init的代码段
ENTRY ;定义程序的入口
EXPORT __ENTRY
__ENTRY
;========
;复位
;========
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 t be used here because the linker generates error.
ASSERT :DEF:ENDIAN_CHANGE
[ ENDIAN_CHANGE ;下面是大小端的一个判断,在option.inc里已经设为FALSE
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 ;设成FALSE的话就来到这了,跳转到复位程序入口
]
b HandlerUndef ;跳转到Undefined mode程序入口
b HandlerSWI ;跳转到SWI中断程序入口
b HandlerPabort ;跳转到PAbort(指令异常)程序入口
b HandlerDabort ;跳转到DAbort(数据异常)程序入口
b . ;保留
b HandlerIRQ ;跳转到IRQ中断程序入口
b HandlerFIQ ;跳转到FIQ中断程序入口
;@0x20
b EnterPWDN ; Must be @0x20.
;==================================================================================
;下面是改变大小端的程序,这里采用直接定义机器码的方式,至说为什么这么做就得问三星了
;反正我们程序里这段代码也不会去执行,不用去管它
;==================================================================================
ChangeBigEndian
;@0x24
[ 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
;根据上述宏定义,Handler与Handle之间的联系
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
;===================================================================================
;第二次查表的,不懂
;===================================================================================
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] ;;装入中断服务程序的入口
str r8,[sp,#8] ;str不是入栈,只是把r0值放到sp+8这个地址上面
ldmfd sp!,{r8-r9,pc} ;一个个出栈,跳转
LTORG ;//声明文字池,因为用了ldr伪指令 ldr存储器到寄存器的数据传输指令
;=======
; ENTRY
;=======
ResetHandler
ldr r0,=WTCON ;1watch dog disable关开门狗
ldr r1,=0x0
str r1,[r0]
ldr r0,=INTMSK
ldr r1,=0xffffffff ;2all interrupt disable关中断
str r1,[r0]
ldr r0,=INTSUBMSK
ldr r1,=0x7fff ;3all sub interrupt disable关子中断
str r1,[r0]
;4led显示
[ {FALSE}
; rGPFDAT = (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);
; Led_Display
ldr r0,=GPFCON
ldr r1,=0x5500
str r1,[r0]
ldr r0,=GPFDAT
ldr r1,=0x10
str r1,[r0]
]
;5.为了减少PLL的lock time, 调整LOCKTIME寄存器.
ldr r0,=LOCKTIME
ldr r1,=0xffffff
str r1,[r0]
[ PLL_ON_START ;6.下面就来设置PLL了,你的板快不快就看这了!!
; Added for confirm clock divide. for 2440.
; 设定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,
str r1,[r0] ; 4=1:4:4, 5=1:4:8, 6=1:3:3, 7=1:3:6.
[ 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]
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]
]
;检查是否从SLEEP模式中恢复
ldr r1,=GSTATUS2
ldr r0,[r1]
tst r0,#0x2
;如果是从SLEEP模式中恢复, 转跳到SLEEP_WAKEUP.
bne WAKEUP_SLEEP
EXPORT StartPointAfterSleepWakeUp ;导出符号StartPointAfterSleepWakeUp
StartPointAfterSleepWakeUp
;===============================================================================
;设置内存控制器等寄存器的值,因为这些寄存器是连续排列的,所以采用如下办法对这些
;寄存器进行连续设置.其中用到了SMRDATA的数据,这在代码后面有定义
;===============================================================================
;ldr r0,=SMRDATA
adrl r0, SMRDATA ;be careful!, hzh
ldr r1,=BWSCON ;BWSCON 地址
add r2, r0, #52 ;SMRDATA数据的结束地址,共有52字节的数据
0
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne %B0
;================================================================================
;如果 EINT0 产生(这中断就是我们按键产生的), 就清除SDRAM ,不过好像没人会在这个时候按
;================================================================================
; check if EIN0 button is pressed
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
tst r0,#0x1
bne %F1 ;如果没有按,就跳到后面的1标号处
; 这就是清零内存的代码
ldr r0,=GPFCON
ldr r1,=0x55aa
str r1,[r0]
; ldr r0,=GPFUP
; ldr r1,=0xff
; str r1,[r0]
ldr r0,=GPFDAT
ldr r1,=0x0
str r1,[r0] ;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
0
stmia r0!,{r1-r8}
subs r9,r9,#32
bne %B0
;到这就结束了.
1
bl InitStacks ;初始化堆栈
;bl Led_Test ;又是LED,注掉了
;===========================================================
;// 判断是从nor启动还是从nand启动
;===========================================================
ldr r0, =BWSCON
ldr r0, [r0]
ands r0, r0, #6 ;OM[1:0] != 0, NOR FLash boot从NOR Flash启动或直接在内存运行
bne NORRoCopy ;don t read nand flash不读取NAND FLASH
adr r0, ResetEntry ;OM[1:0] == 0,否则,为从NAND FLASH启动
cmp r0, #0 ;再比较入口是否为0地址处,如果不是,表示主板设置了NAND启动,但这个程序由于其他原因,并没有从NAND启动,这种情况最有可能用了仿真器
bne InitRamZero ;don t read nand flash for boot
;nop
;===========================================================
;//将程序从nandflash拷贝到sdram
;===========================================================
nand_boot_beg
bl ClearSdram
mov r5, #NFCONF ;首先设定NAND的一些控制寄存器
ldr r0, =(7<<12)|(7<<8)|(7<<4)
str r0, [r5]
;enable control
ldr r0, =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)
str r0, [r5, #4]
bl ReadNandID ;接着读取NAND的ID号,结果保存在r5里
mov r6, #0 ;r6设初值0
ldr r0, =0xecF1 ;期望的NAND ID号
cmp r5, r0 ;进行比较
beq %F1 相等的话跳到下一个1标号处
; ldr r0, =0xecda
; cmp r5, r0
mov r6, #1 ;Nandaddr(寻址周期 0:4 1:5)不相等,设置r6为1
1
bl ReadNandStatus ;读取NAND状态,结果放在r1里
mov r8, #0 ;r8设初值0,意义为页号
ldr r9, =ResetEntry ;r9设初值为初始化程序入口地址
mov r10,#32 ;+081010 feiling
;=========================================================================
; 注意,在这里使用的是ldr伪指令,而不是上面用的adr伪指令,它加载的是ResetEntry
; 的决对地址,也就是我们期望的RAM中的地址,在这里,它和|Image$$RO$$Base|一样
; 也就是说,我如我们编译程序时RO BASE指定的地址在RAM里,而把生成的文件拷到
; NAND里运行,由ldr加载的r9的值还是定位在内存.
;=========================================================================
2
ands r0, r8, #0x1f ;凡r8为0x1f(32)的整数倍-1,eq有效,ne无效
bne %F3 ;这句的意思是对每个块(32页)进行检错
mov r0, r8 ;r8->r0
bl CheckBadBlk ;检查NAND的坏区
cmp r0, #0 ;比较r0和0
addne r8, r8, #32 ;存在坏块的话就跳过这个坏块
bne %F4 ;没有的话就跳到标号4处
3
mov r0, r8 ;当前页号->r0
mov r1, r9 ;当前目标地址->r1
bl ReadNandPage ;读取该页的NAND数据到RAM
add r9, r9, #512 ;每一页的大小是512Bytes
add r8, r8, #1 ;r8指向下一页
4
cmp r8, #256 ;比较是否读完256页即128KBytes
bcc %B2 ;如果r8小于256(没读完),就返回前面的标号2处
mov r5, #NFCONF ;DsNandFlash
ldr r0, [r5, #4]
bic r0, r0, #1
str r0, [r5, #4]
ldr pc, =copy_proc_beg ;调用copy_proc_beg
;===========================================================
copy_proc_beg
adr r0, ResetEntry ;ResetEntry值->r0
ldr r2, BaseOfROM ;BaseOfROM值(后面有定义)->r2
cmp r0, r2 ;比较r0和r2
ldreq r0, TopOfROM ; 如果相等的话(在内存运行),TopOfROM->r0
beq InitRam ;同时跳到InitRam
;=========================================================
;下面这个是针对代码在NOR FLASH时的拷贝方法
;功能为把从ResetEntry起,TopOfROM-BaseOfROM大小的数据拷到BaseOfROM
;TopOfROM和BaseOfROM为|Image$$RO$$Limit|和|Image$$RO$$Base|
;|Image$$RO$$Limit|和|Image$$RO$$Base|由连接器生成
;为生成的代码的代码段运行时的起启和终止地址
;BaseOfBSS和BaseOfZero为|Image$$RW$$Base|和|Image$$ZI$$Base|
;|Image$$RW$$Base|和|Image$$ZI$$Base|也是由连接器生成
;两者之间就是初始化数据的存放地放
;=======================================================
ldr r3, TopOfROM
0
ldmia r0!, {r4-r7}
stmia r2!, {r4-r7}
cmp r2, r3
bcc %B0
sub r2, r2, r3 ;r2=BaseOfROM-TopOfROM=(-)代码长度
sub r0, r0, r2 ;r0=ResetEntry-(-)代码长度=ResetEntry+代码长度
InitRam
ldr r2, BaseOfBSS ;BaseOfBSS->r2
ldr r3, BaseOfZero ;BaseOfZero->r3
0
cmp r2, r3 ;比较BaseOfBSS和BaseOfZero
ldrcc r1, [r0], #4 ;要是r21 ; means Fclk:Hclk is not 1:1.
; bl MMU_SetAsyncBusMode
; |
; bl MMU_SetFastBusMode ; default value.
; ]
;bl Led_Test
;===========================================================
; 进入C语言前的最后一步了,就是把我们用说查二级向量表
; 的中断例程安装到一级向量表(异常向量表)里.
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函数处了.
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;*****************************************************************************
[ :LNOT:THUMBCODE
bl Main ;Do not use main() because ......
;ldr pc, =Main ;hzh
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::
InitStacks
ReadNandID
ReadNandStatus
WaitNandBusy
CheckBadBlk
WaitNandBusy
ReadNandPage
Led_Test
1把需要的文件加载进来
2定义处理器模式常数
3定义处理器各模式下堆栈地址常数
4判断THUMB和ARM模式 宏THUMBCODE
5第一次查表,实现中断向量的重定向 $HandlerLabel HANDLER $HandleLabel
6
IMPORT引入|Image$$RO$$Base|,|Image$$RO$$Limit|,|RW Base|,|ZI Base|,|ZI Limit|
ROM code开始与结束地址,要初始化的RAM开始地址,需要清零的RAM区域的开始地址与结束地址
7 IMPORT引入其他文件中实现的函数,MMU_SetAsyncBusMode MMU_SetFastBusMode, Main
8 AREA Init,CODE,READONLY //init的代码段
9ENTRY
ResetEntry
10判断大小端
11跳转程序入口
12改变大小端的程序
13HandlerFIQ HANDLER HandleFIQ
14第二次查表 判断是什么中断
15 ENTRY复位
ResetHandler
i关看门狗
ii关中断
iii关子中断
iiii点灯
vPLL
vi MMU_SetAsyncBusMode
MMU_SetFastBusMode
viiUPLL
viiiMPLL
iv 检查是否从SLEEP模式中恢复
WAKEUP_SLEEP
16设置内存控制器等寄存器的值
SMRDATA
17清零内存的代码
18初始化堆栈InitStacks
19 从NOR NAND拷贝的依据
20 从NAND读代码到RAM
设定NAND控制寄存器
ReadNandID
ReadNandStatus
CheckBadBlk
ReadNandPage
21NOR FLASH拷贝
22 用二级向量表的中断例程安装到一级向量表里
23 跳到main函数处
WaitNandBusy
24时钟测试
25可读写数据段
AREA RamData ,DATA,READWRITE
参考:http://www.cnblogs.com/dekun_1986/archive/2010/08/07/1790601.html
http://hi.baidu.com/blue_belief/blog/item/ddee3b7c9a222d340dd7daca.html
《ARM体系结构与编程》
ARM汇编设计http://www.cnblogs.com/hoys/archive/2010/12/01/1892578.html
伪指令MARCO http://www.360doc.com/content/10/1101/14/3038654_65704006.shtml
bootloader启动流程http://hi.baidu.com/blue_belief/blog/item/db375aca04b1811b7e3e6f27.html