s3c2410启动代码分析
ARM 2010-05-12 19:45:47 阅读11 评论0 字号:大中小
通常,启动代码是指CPU复位后到进入C语言的main函数之前需要执行的那段汇编代码.这是由于C语言程序的运行需要具备一定的条件,比如:分配好外部数据空闿堆栈空间和中断入口等筿另外汇编代码可以更直接的对硬件进行操使效率更高. 通常启动代码是放圿410init.s汇编文件;特殊功能寄存器定义在2410addr.s;Memory Bank 配置在mencfg.s;还有系统的选项等在option.s文件;2410init.s不仅包括复位后执行的代码,还包括CPU进入掉电模式,产生中断等和处理器直接相关的,用汇编实现的代码.
;=========================================
; NAME: 2410INIT.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,POWER_OFF mode
; 2003.05.19:jcs:Configure UPLL in init.s not usbmain.c
;=========================================
//首先,启动代码定义了一些常量 ,相当于C中的INCLUDE
GET option.inc
GET memcfg.inc
GET 2410addr.inc
BIT_SELFREFRESH EQU (1<<22) //自刷新常量
//;;处理器模式常量
USERMODE EQU 0x10
FIQMODE EQU 0x11
IRQMODE EQU 0x12
SVCMODE EQU 0x13
ABORTMODE EQU 0x17
UNDEFMODE EQU 0x1b
MODEMASK EQU 0x1f //系统模式
NOINT EQU 0xc0 //屏蔽所有的中断,即置位I,F位
//;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 ~
;check if tasm.exe is used.
;arm处理器有两种工作状态 1.arm:32位 这种工作状态下执行字对准的arm指令 2.Thumb:16位 这种工作状态执行半字对准的Thumb指令
;因为处理器分为16位 32位两种工作状态程序的编译器也是分16位和32两种编译方式 所以下面的程序用于根据处理器工作状态确定编译器编译方式
;code16伪指令指示汇编编译器后面的指令为16位的thumb指令
;code32伪指令指示汇编编译器后面的指令为32位的arm指令
;这段是为了统一目前的处理器工作状态和软件编译方式(16位编译环境使用tasm.exe编译)
GBLL THUMBCODE ;设置一个全局逻辑变量
[ {CONFIG} = 16 ;if config==16 这里表示你的目前处于领先地16位编译方式,{CONFIG}为汇编器内置变量
THUMBCODE SETL {TRUE} ;设置THUMBCODE 为 true
CODE32 ;转入32位编译模式
| ;else
THUMBCODE SETL {FALSE} ;设置THUMBCODE 为 false
]
[ THUMBCODE ;if THUMBCODE==TRUE
CODE32 ;for start-up code for Thumb mode;转入32位编译方式
]
;注意下面这段程序是个宏定义 很多人对这段程序不理解 我再次强调这是一个宏定义 所以大家要注意了下面包含的HandlerXXX HANDLER HandleXXX将都被下面这段程序展开。
;这段程序用于把中断服务程序的首地址装载到pc中,有人称之为“加载程序”。其大致作用是把宏的第一个参数$HandlerLabel 转变为一个标号,然后让程序跳转到第二个参数 $HandleLabel (第二个参数应该为一个地址)对应的值的地址去。可以分析出,sp和r0在执行前后都没有变化,程序就实现了跳转
;本初始化程序定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首地址。每个字空间都有一个标号,以Handle***命名。
;在向量中断模式下使用“加载程序”来执行中断服务程序。
;这里就必须讲一下向量中断模式和非向量中断模式的概念
;向量中断模式是当cpu读取位于0x18处的IRQ中断指令的时候,系统自动读取对应于该中断源确定地址上的指令取代0x18处的指令,通过跳转指令系统就直接跳转到对应地址
;函数中 节省了中断处理时间提高了中断处理速度标 例如 ADC中断的向量地址为0xC0,则在0xC0处放如下代码:ldr PC,=HandlerADC 当ADC中断产生的时候系统会
;自动跳转到HandlerADC函数中
;非向量中断模式处理方式是一种传统的中断处理方法,当系统产生中断的时候,系统将interrupt pending寄存器中对应标志位置位 然后跳转到位于0x18处的统一中断
;函数中 该函数通过读取interrupt pending寄存器中对应标志位 来判断中断源 并根据优先级关系再跳到对应中断源的处理代码中
MACRO
$HandlerLabel HANDLER $HandleLabel //
$HandlerLabel
sub sp,sp,#4 ;减少sp(预留一个字,用于存放转跳地址)
stmfd sp!,{r0} ;把工作寄存器压入栈(lr does not push because it return to original address)
ldr r0,=$HandleLabel ;将HandleXXX的址址放入r0
ldr r0,[r0] ;把HandleXXX所指向的内容(也就是中断程序的入口)放入r0
str r0,[sp,#4] ;把中断服务程序(ISR)压入栈,保存在高一个地址预留的空间中,但SP没变。
ldmfd sp!,{r0,pc} ;用出栈的方式恢复r0的原值和为pc设定新值(也就完成了到ISR的转跳) ;
ADS仅支持FD(满递减)型堆栈,故只能用stmfd和ldmfd
MEND
//在这里用IMPORT伪指令(和c语言的extren一样)引入|Image$$RO$$Base|,|Image$$RO$$Limit|...
//这些变量是通过ADS的工程设置里面设定的RO Base和RW Base设定的,最终由编译脚本和连接程序导入程序.
//那为什么要引入这玩意呢,最简单的用处是可以根据它们拷贝自已 ,从把RW和ZI变量从加载域中复制到运行域中
//一个arm由RO,RW,ZI三个断组成 其中RO为代码段,RW是已经初始化的全局变量,ZI是未初始化的全局变量(对于GNU工具 对应的概念是TEXT ,DATA,BSS)。
;========================================================================================
IMPORT |Image$$RO$$Base| ; ROM code(也就是代码)的开始地址
IMPORT |Image$$RO$$Limit| ; ROM code的结束地址 (也就是RW在加载域中的起始地址)
IMPORT |Image$$RW$$Base| ; 在运行域中要初始化的RAM的开始地址
IMPORT |Image$$ZI$$Base| ; area(需要清零的RAM区域)的开始地址
IMPORT |Image$$ZI$$Limit| ; area的结束地址
;这里引入一些在其它文件中实现在函数,包括为我们所熟知的main函数
IMPORT Main ; The main entry of mon program
;从这里开始就是正真的代码入口了!
AREA Init,CODE,READONLY ;这表明下面的是一个名为Init的代码段
ENTRY ;定义程序的入口(调试用) 其中关键字ENTRY是指定编译器保留这段代码,因为
编译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码
被链接在0地址处,并且作为整个程序的入口
;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_CHANGE ;下面是大小端的一个判断,在Option.inc里已经设为FALSE
ASSERT :DEF:ENTRY_BUS_WIDTH
[ ENTRY_BUS_WIDTH=32 //‘[’=IF
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,所以系统复位后就来到这了,转跳到复位程序入口
]
//=====================================================================================
;ARM要求中断向量表必须放置在仿地址开始,连续8X4字节的空间内.每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理
//=====================================================================================
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 //通过设置CP15中的C1的位7来设置存储格式为大端模式。
;@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
;Function for entering power down mode,下面这段程序为进入掉电模式及从掉电模式中唤醒的相关设置和处理
; 1. SDRAM should be in self-refresh mode. SDRAm应该设置为自刷新的模式
; 2. All interrupt should be maksked for SDRAM/DRAM self-refresh. 所有中断必须屏蔽 for SDRAM/DRAM self-ref
; 3. LCD controller should be disabled for SDRAM/DRAM self-refresh. LCD 控制器关闭
; 4. The I-cache may have to be turned on.
; 5. The location of the following code may have not to be changed.
//;void EnterPWDN(int CLKCON); //PWDN:powerdown
EnterPWDN
mov r2,r0 ;r2=rCLKCON //rCLKCONr [3;2]位为电源模式标置位。若[3]为1,表示转为了掉电模式
tst r0,#0x8 ;POWER_OFF mode? //按位与判断,若[3]为1则跳转到ENTER_POWER_OFF
bne ENTER_POWER_OFF
ENTER_STOP //进入停止模式相关处理
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 //表示不相等则往回跳转到标号为0的位置,在此为上一句。
ldr r0,=CLKCON ;enter STOP mode.
str r2,[r0]
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 POWER_OFF mode, only the reset by wake-up is available.
//进入掉电 模式后,仅唤醒中断有效
ldr r0,=REFRESH ;exit from SDRAM self refresh mode.
str r3,[r0]
MOV_PC_LR //开始处定义的返回跳转宏
ENTER_POWER_OFF
;NOTE.注意在rGSTATUS3寄存器中应该保存掉电模式唤醒的返回地址,rGSTATUS3,4可在掉电下保存信息
;1) rGSTATUS3 should have the return address after wake-up from POWER_OFF mode.
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
ldr r1,=MISCCR
ldr r0,[r1]
orr r0,r0,#(7<<17) ;Make sure that SCLK0:SCLK->0, SCLK1:SCLK->0, SCKE=L during boot-up
str r0,[r1]
ldr r0,=CLKCON
str r2,[r0]
b . ;CPU will die here.
;=================================================================================
从掉电模式唤醒的过程
1、 某个唤醒源生效将产生一个内部复位信号。复位时间由一个内部16位计数器决定,此计数器的时钟是tRST=(65535/XTAL_frequency)。
2、 查询GSTATUS[2]位看从掉电模式唤醒是否产生了一个POWER-UP。
3、 通过将MISCCR[19:17]设置为000b,释放SDRAM信号保护。
4、 配置SDRAM控制器。
5、 等待SDRAM自我刷新完毕。大部分SDRAM需要refresh cycle of all SDRAM row。
6、 GSTATUS3,4的信息可以被用户使用,因为GSTATUS3,4的值已经在掉电模式下被保存了。
7、 对于EINT[3:0],检查SRCPND寄存器;对于EINT[15:4],检查EINTPND寄存器;对于RTC报警唤醒,检查RTC时间,因为在唤醒时SRCPND寄存器的RTC位不被置位;如果在掉电模式期间有nBATT-FLT assertion,SRCPND寄存器的相关位被置位。
;==================================================================================
WAKEUP_POWER_OFF
;Release SCLKn after wake-up from the POWER_OFF mode.
ldr r1,=MISCCR //MISCCR寄存器用来设置一些USB等相关的时钟周期等
ldr r0,[r1]
bic r0,r0,#(7<<17) //SCLK0:0->SCLK, SCLK1:0->SCLK, SCKE:L->H
str r0,[r1] //通过将MISCCR[19:17]设置为000b,释放SDRAM信号保护。
;Set memory control registers配置内存控制寄存器。
ldr r0,=SMRDATA //在程序的后面 LTORG SMRDATA DATA中定义了
//一个数据缓冲池就是用来配置相关的内存控制寄存器的
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
ldr r1,=GSTATUS3 ; GSTATUS3 has the start address just after POWER_OFF wake-up
ldr r0,[r1]
mov pc,r0 //从掉电模式下唤醒后,将保存在GSTATUS3 返回地址传给PC
如上所说,这里采用HANDLER宏去建立Hander***和Handle***之间的联系
LTORG ;声明文字池,因为我们用了ldr伪指令
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
;===================================================================================
;呵呵,来了来了.好戏来了,这一段程序就是用来进行第二次查表的过程了.
;如果说第一次查表是由硬件来完成的,那这一次查表就是由软件来实现的了.
;为什么要查两次表??
;没有办法,ARM把所有的中断都归纳成一个IRQ中断异常和一个FIRQ中断异常
;第一次查表主要是查出是什么异常,可我们总要知道是这个中断异常中的什么中断呀!
;没办法了,再查一次表呗!
;===================================================================================
IsrIRQ //第二次中断查表,因为ARM把所有的中断都归为一个IRQ异常,通过此处查表可知道具体中断
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
;===================================================================================
;哈哈,这查表方法够好了吧,r8(入口)+index*4(别望了一条指令是4 bytes的喔),
;这不就是我们要找的那一项了吗.找到了表项,下一步做什么?肯定先装入了!
;==================================================================================
add r8,r8,r9,lsl #2
ldr r8,[r8] ;装入中断服务程序的入口
str r8,[sp,#8] ;把入口也入栈,准备用旧招
ldmfd sp!,{r8-r9,pc} ;施招,弹出栈,哈哈,顺便把r8弹出到PC,O了,跳转成功!
;================================================================================
; ENTRY 系统上电后经过一个b ResetHandler就跳转到此处来。在此完成一些相关的软硬件配置工作
1. 屏蔽所有中断,关看门狗。
2. 根据工作频率设置PLL寄存器
3. 初始化存储控制相关寄存器
4. 初始化各模式下的栈指针
5. 设置缺省中断处理函数
6. 将数据段拷贝到RAM中,将零初始化数据段清零
7. 跳转到C 语言Main入口函数中
;================================================================================
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,=0x3ff ;all sub interrupt disable屏蔽所有子中断
str r1,[r0]
[ {FALSE} //;是得有些表示了,该点点LED灯了,不过被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]
]
;To reduce PLL lock time, adjust the LOCKTIME register. 为了减少PLL的lock time, 调整LOCKTIME寄存器.
ldr r0,=LOCKTIME //LOCKTIME为锁定计数定时器,即设置PLL稳定过渡时间,一般大于150uS
ldr r1,=0xffffff
str r1,[r0]
;=================================================================================
上电复位时的时钟行为。晶振在几毫秒内开始振荡。当OSC时钟稳定后,PLL根据默认PLL设置开始生效,但是通常这个时候是不稳定的,因此在软件重新配置PLLCON寄存器之前FCLK直接使用Fin而不是MPLL,即使用户不希望改变PLLCON的默认值,用户也应该执行一边写PLLCON操作。
;这里介绍一下计算公式
;Fpllo=(m*Fin)/(p*2^s)
;m=MDIV+8,p=PDIV+2,s=SDIV
;Fpllo必须大于20Mhz小于66Mhz
;Fpllo*2^s必须小于170Mhz
;如下面的PLLCON设定中的M_DIV P_DIV S_DIV是取自option.h中M_DIV=0x5c=92 P_DIV=0x4 S_DIV=0x2
所以Fpllo=(m*Fin)/(p*2^s)=(92+8)*12M/(4+2)*2^2=50M
;==================================================================================
[ PLL_ON_START
;Configure MPLL
ldr r0,=MPLLCON //M_DIV=0x5c=92 P_DIV=0x4 S_DIV=0x2
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;Fin=12MHz,Fout=50MHz
str r1,[r0]
;Configure UPLL
ldr r0,=UPLLCON
ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV) ;Fin=12MHz,UPLLout=48MHz
str r1,[r0]
]
;Check if the boot is caused by the wake-up from POWER_OFF mode.
ldr r1,=GSTATUS2
ldr r0,[r1]
tst r0,#0x2
;In case of the wake-up from POWER_OFF mode, go to POWER_OFF_WAKEUP handler.
bne WAKEUP_POWER_OFF
EXPORT StartPointAfterPowerOffWakeUp
StartPointAfterPowerOffWakeUp
;====================================================================================
//设置内存控制器等寄存器的值,因为这些寄存器是连续排列的,所以采用如下办法对这些 ;寄存器进行连续设置.其中用到了SMRDATA的数据,这在代码后面有定义 ;Set memory control registers
;=====================================================================================
ldr r0,=SMRDATA
ldr r1,=BWSCON ;BWSCON Address
add r2, r0, #52 ;End address of SMRDATA,共13个DCD,52个字节
0
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne %B0 //上面这段小程序将后面定义的数据复制到相关内存控制寄存器
;Initialize stacks
bl InitStacks //调用堆栈初始化子程序
;==================================================================================
关于异常中断系统:异常中断矢量表(每个表项占4个字节) 下面是中断向量表 一旦系统运行时有中断发生 即使移植了操作系统 如linux 处理器已经把控制权交给了操作系统 一旦发生中断 处理器还是会跳转到从0x0开始
;中断向量表中某个中断表项(依据中断类型)开始执行;具体中断向量布局请参考s3c44b0 spec 例如 adc中断向量为 0x000000c0下面对应表中第49项位置 向量地址0x0+4*(49-1)=0x000000c0
2410异常中断系统中有两张中断转移表,经过二重转移才跳到中断处理程序。第一张中断向量表由硬件决定,所在区域为ROM(flash),地址空间从0X00开始,其中0X00-0X1C为异常向量入口地址。另一张中断向量表在RAM 中,可以随便改,其位置在程序连接后才定。ARM7的内核实际上只有8个(1个保留)异常向量,对于其他所有众多的中断源,ARM7 的内核是通过IRQ或FRQ 的软件查询中断状态寄存器的位来获得ISR的起始地址。
;===================================================================================
; Setup IRQ handler //;设置缺省中断处理函数
ldr r0,=HandleIRQ //;This routine is needed;//使用ldr伪指令装载HandleIRQ的地址到r0中
ldr r1,=IsrIRQ //If there isn't 'subs pc,lr,#4' at 0x18, 0x1c;//使用ldr伪指令装载IsrIRQ的地址到r1中
str r1,[r0] //把r1的值写到r0指向的存储地址中,把IsrIRQ这个函数的地址写入到HandleIRQ存储单元里面
;======================================================================================
;Copy and paste RW data/zero initialized data
//以下程序段将加载哉中的数据段RW拷贝到运行域的ram中 将ZI段中的零初始化数据段清零
//跳入C语言的main函数执行到这步结束bootloader初步引导结束
程序先把 ROM 里|Image$$RO$$Limt|开始的 RW 初始数据拷贝到 RAM 里面|Image$$RW$$Base|开始的地址,当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地址|Image$$ZI$$Limit|。
;======================================================================================
ldr r0, =|Image$$RO$$Limit| //Get pointer to ROM data,rom中的RW数据源的起始地址
ldr r1, =|Image$$RW$$Base| //and RAM copyRW区在RAM里的执行区起始地址
ldr r3, =|Image$$ZI$$Base| //ZI区在RAM里面的起始地址
;Zero init base => top of initialised data检查装载地址和执行地址是否相同
cmp r0, r1 ; Check that they are different
beq %F2 ;//若相等则跳转到2,相同,则不拷贝该区间,初始化零数据区
1
cmp r1, r3 ; Copy init data;//如果r0不等于r1,r1和r3比较,Copy init data,不相同,将装载区拷贝到执行区
ldrcc r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4 当无符号数r1<r3时,读取r0地址处的内容
strcc r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4
bcc %B1 ;//若相等则跳转到1,相同,则不拷贝该区间,初始化零数据区
2
ldr r1, =|Image$$ZI$$Limit| ; Top of zero init segment,ZI区在RAM里面的结束地址后面的一个地址
mov r2, #0
3
cmp r3, r1 ; Zero init
strcc r2, [r3], #4 //当ZI区的起始地址未达等于结束地址时,继续清0
bcc %B3 ;//当无符号数r3<r1时,跳转到3处,这样做就完成了zi区的初始化清0
[ :LNOT:THUMBCODE
bl Main ;Don't use main() because ......跳到Main()主函数,注意大小写
b .
]
[ THUMBCODE ;for start-up code for Thumb mode
orr lr,pc,#1
bx lr
CODE16
bl Main ;Don't use main() because ......
b .
CODE32
]
;function initializing stacks
InitStacks
;Don't use DRAM,such as stmfd,ldmfd......
;SVCstack is initialized before
;Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'
mrs r0,cpsr ;//读取CPSR的值,R0=CPSR,CPSR为当前程序状态寄存器
bic r0,r0,#MODEMASK ;//R0=R0&(~MODEMASK),MODEMASK=0X1F,也就是低五位清0
orr r1,r0,#UNDEFMODE|NOINT ;//R1=R0|(MODEMASK|NONINT),R1为未定义模式,也就是低八位为11X11011
msr cpsr_cxsf,r1 ;UndefMode ;//写把R1的值写到状态寄存器cpsr_cxsf(也就是CPSR),UndefMode
ldr sp,=UndefStack ;//设置未定义模式下的堆栈指针
orr r1,r0,#ABORTMODE|NOINT
msr cpsr_cxsf,r1 ;AbortMode
ldr sp,=AbortStack
orr r1,r0,#IRQMODE|NOINT
msr cpsr_cxsf,r1 ;IRQMode
ldr sp,=IRQStack
orr r1,r0,#FIQMODE|NOINT
msr cpsr_cxsf,r1 ;FIQMode
ldr sp,=FIQStack
bic r0,r0,#MODEMASK|NOINT
orr r1,r0,#SVCMODE
msr cpsr_cxsf,r1 ;SVCMode
ldr sp,=SVCStack
//USER mode has not be initialized.注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就
//不能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响
mov pc,lr //堆栈初始化完成返回
;The LR register won't be valid if the current mode is not SVC mode.
;以下是上面提到的对存储寄存器初始化的数据map,共13个DCD
LTORG
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)) //BAN1,6,7为32位数据完,其他除0外为16
DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0;BANK0的相关访问模式及各访问周期设置, 各位定义可参考memcfg.inc文件
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)+(Trc<<18)+(Tchr<<16)+REFCNT) //;设置刷新周期
DCD 0x32 ; ;SCLK power saving mode, ARM core burst disable, BANKSIZE 128M/128M
DCD 0x30 ;MRSR6 CL=3clk CL:CAS latency
DCD 0x30 ;MRSR7
; DCD 0x20 ;MRSR6 CL=2clk
; DCD 0x20 ;MRSR7
//下面是对ram区域map的定义;这里定义了处理器工作于各模式的堆栈区在ram中map.
ALIGN //字对齐
AREA RamData, DATA, READWRITE
^ _ISR_STARTADDRESS //^=map
HandleReset # 4 //#=field
HandleUndef # 4
HandleSWI # 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ # 4
HandleFIQ # 4
;Don't use the label 'IntVectorTable',
;The value of IntVectorTable is different with the address you think it may be.
;IntVectorTable
HandleEINT0 # 4
HandleEINT1 # 4
HandleEINT2 # 4
HandleEINT3 # 4
HandleEINT4_7 # 4
HandleEINT8_23 # 4
HandleRSV6 # 4
HandleBATFLT # 4
HandleTICK # 4
HandleWDT # 4
HandleTIMER0 # 4
HandleTIMER1 # 4
HandleTIMER2 # 4
HandleTIMER3 # 4
HandleTIMER4 # 4
HandleUART2 # 4
HandleLCD # 4
HandleDMA0 # 4
HandleDMA1 # 4
HandleDMA2 # 4
HandleDMA3 # 4
HandleMMC # 4
HandleSPI0 # 4
HandleUART1 # 4
HandleRSV24 # 4
HandleUSBD # 4
HandleUSBH # 4
HandleIIC # 4
HandleUART0 # 4
HandleSPI1 # 4
HandleRTC # 4
HandleADC # 4
END