;=========================================
; NAME: 2440INIT.S
; DESC: C start up codes
; Configure memory, ISR ,stacks
; Initialize C-variables
;=========================================
启动代码主要是在主程序运行之前初始化系统硬件及软件的运行环境,它的主要功能包括以下的几个方面:
1、建立中断向量表
2、初始化系统堆栈
3、应用程序执行环境初始化
4 跳转至主函数
接下来对各个部分进行分析
;GET 是包含头文件的意思,相当于C语言中的include,是将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理
GET option.inc ;option.inc文件包含了开发板的配置信息—堆栈、时钟等
GET memcfg.inc ;存储控制文件
GET 2440addr.inc ;寄存器地址地址定义
BIT_SELFREFRESH EQU (1<<22) ;SDRAM自刷新标志位
;预定义的工作模式
;ARM 有7 种模式,用户模式,快速中断模式,中断模式,管理模式,中止模式,未定
;义模式和系统模式。系统堆栈的初始化主要是给各个处理器模式分配堆栈空间。堆栈是为
;中断或程序跳转服务的,当发生中断或程序跳转时,需要将当前处理器的状态及一些参数
;保持在堆栈中,当中断处理完毕以后或程序执行完后返回时,再将堆栈保存的现场数据进
;行恢复,以保证原来的程序正确运行
;Pre-defined constants
USERMODE EQU 0x10 ;用户模式
FIQMODE EQU 0x11 ;快速中断模式
IRQMODE EQU 0x12 ;中断模式
SVCMODE EQU 0x13 ;管理模式
ABORTMODE EQU 0x17 ;中止模式
UNDEFMODE EQU 0x1b ;未定义模式
MODEMASK EQU 0x1f ;模式掩码
NOINT EQU 0xc0 ;无中断模式
;The location of stacks
;定义各种模式下使用的堆栈起始地址,_STACK_BASEADDRESS是由option.inc定义的
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.
; 判断是否THUMB指令
; [ 代表IF,| 指的是ELSE,] 相当于ENDIF
GBLL THUMBCODE ;定义一个局部变量THUMBCODE
[ {CONFIG} = 16 ;如果是16位代码,则将THUMBCODE设为真
THUMBCODE SETL {TRUE} ;
CODE32 ;否则是ARM指令
|
THUMBCODE SETL {FALSE}
]
; 宏定义,用于子程序的返回
MACRO
MOV_PC_LR
[ THUMBCODE ;如果目标地址是THUMB的指令,则跳到THUMB地址
bx lr ;在ARM模式中,要用BX指令跳转到THUMB指令,并转换模式
|
mov pc,lr ;如果目标地址是ARM指令,则直接把函数返回地址给PC
]
MEND
MACRO
MOVEQ_PC_LR ;带条件的函数返回,与MOV_PC_LR类似
[ THUMBCODE
bxeq lr
|
moveq pc,lr
]
MEND
PS:下面这一段是网上找的,我觉得它已经介绍的非常好了,所以就直接拿过来用来,呵呵
;===============================================================
;下面这个宏是用于第一次查表过程的实现中断向量的重定向,你会发现
;在_ISR_STARTADDRESS=0x33FF_FF00里定义的第一级中断向量表
;是采用型如Handle***的方式的. 而在程序的ENTRY处(程序开始处)采用的是
;b Handler***的方式.
;在这里Handler***就是通过HANDLER这个宏和Handle***进立联系的.
;这种方式的优点就是正真定义的向量数据在内存空间里,而不是在ENTRY处
;的ROM(FLASH)空间里, 这样,我们就可以在程序里灵活的改动向量的数据了.
;其中HANDLER是一个宏,用于查找中断处理程序的入口地址。这些地址存放在
;由HandleXXX指向的表项中,该表定位在RAM高端,基地址为_ISR_STARTADDRESS。
;假如_ISR_STARTADDRESS为 0x800000000,当IRQ中断时,根据b HandlerFIQ,先跳转
;再根据^ _ISR_STARTADDRESS基地址+HandleIRQ 的偏移地址(4*6)得到的中断地址
;0x80000000+0x00000024=0x80000024[page]
;===============================================================
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address)
ldr r0,=$HandleLabel;load the address of HandleXXX to r0
ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
;IMPORT指令类似于C语言的extern关键字,下面这些古怪的变量是由编译器生成的
IMPORT |Image$$RO$$Base| ; Base of ROM code,即代码开始的地方
IMPORT |Image$$RO$$Limit| ; End of ROM code (=start of ROM data)
IMPORT |Image$$RW$$Base| ; Base of RAM to initialise,RAM起始地址
IMPORT |Image$$ZI$$Base| ; Base and limit of area初始化的起始地址
IMPORT |Image$$ZI$$Limit| ; to zero initialise初始化的结束地址
;===============================================================
107 ;在这里用IMPORT伪指令(和c语言的extren一样)引入外部变量MMU的快速总线
108 ;模式和异步总线模式两个变量
109 ;===============================================================
IMPORT MMU_SetAsyncBusMode
IMPORT MMU_SetFastBusMode ;
IMPORT Main ; The main entry of mon program
IMPORT RdNF2SDRAM ; Copy Image from Nand Flash to SDRAM
初始化程序中必须指明入口地址,因为处理器复位(仿真时,装载image)后PC 要找到入口开始执行代码,当各种异常或是中断产生的时候也要找到各个异常的入口开始执行代
码。从这里开始就是真正的代码入口了!
AREA Init,CODE,READONLY;声明一个名为Init的代码段,
ENTRY 程序的入口点,(调试用)
;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 not be used here because the linker generates error.
ASSERT :DEF:ENDIAN_CHANGE
[ ENDIAN_CHANGE
ASSERT :DEF:ENTRY_BUS_WIDTH
;如果没有定义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;复位异常,开发板上电或复位时进入0x00
]
中断向量表一般位于启动代码的开始部分,它是用户程序与启动代码之间以及启动代码
的各部分之间联系的纽带。它由一个一个的跳转函数组成,它就象一个普通的散转函数,只
不过散转的过程中有硬件机制参与,当系统发生异常时,ARM 处理器会通过硬件机制强制
将PC 指针指向中断向量表中对应的异常跳转函数存储的地址,然后程序会跳转到相应的中
断服务程序去执行。
b HandlerUndef ;handler for Undefined mode 未定义异常,遇到无法识别的指令时0x04
b HandlerSWI ;handler for SWI interrupt,软中断异常0x08
b HandlerPabort ;handler for PAbort指令预取错误时进入0x0c
b HandlerDabort ;handler for DAbort数据访问不能完成时进入0x10
b . ;reserved, 保留 0x14
b HandlerIRQ ;handler for IRQ interrupt发生IRQ 中断时进入0x18
b HandlerFIQ ;handler for FIQ interrupt发生FIQ 中断时进入0x1c
;@0x20
b EnterPWDN ; Must be @0x20.
158 ;===============================================================
159 ;下面是改变大小端的程序,这里采用直接定义机器码的方式,至说为什么这么做
160 ;就得问三星了反正我们程序里这段代码也不会去执行,不用去管它
161 ;===============================================================
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宏去建立Hander***和Handle***之间的联系
HandlerFIQ HANDLER HandleFIQ
Ha ndlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
[page]
IsrIRQ
sub sp,sp,#4 ;reserved for PC,给PC寄存器保留
stmfd sp!,{r8-r9};工作寄存器入栈保护
ldr r9,=INTOFFSET;INTOFFSET在2440addr.inc中定义
ldr r9,[r9]
ldr r8,=HandleEINT0;HandleEINT0 的地址就是中断的入口地址
add r8,r8,r9,lsl #2;逻辑左移就相当于乘以4
ldr r8,[r8]
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc},将地址从堆栈中弹出给PC
LTORG
;用于声明一个数据缓冲池
;=======
上电和复位后,程序开始从位于0x0 执行b ResetHandler 程序跳转到这里执行,将看门
狗,中断之类的程序关掉,以免打扰初始化程序的进行。
; ENTRY
;=======
ResetHandler
ldr r0,=WTCON ;watch dog disable
ldr r1,=0x0
str r1,[r0]
WTCON 为看门狗控制寄存器,此处将其写入0x0,就是禁止它的所有功能,包括定时
器定时,溢出中断及溢出复位。
ldr r0,=INTMSK
ldr r1,=0xffffffff ;all interrupt disable
str r1,[r0]
INTMSK 为中断屏蔽寄存器,写入0xffffffff,就是禁止所有的中断产生,因为中断
向量表还未初始化,如果此时产生中断会使程序进入未知的状态而跑飞。因为外设的中断太
多,INTMSK 不够用,还需要INTSUBMSK 来将剩余的中断源也禁止掉。
ldr r0,=INTSUBMSK
ldr r1,=0x7fff ;all sub interrupt disable
str r1,[r0]
[ {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]
]
;To reduce PLL lock time, adjust the LOCKTIME register.
ldr r0,=LOCKTIME;设置pll 锁定时间
ldr r1,=0xffffff
str r1,[r0]
LOCKTIME 为PLL 锁定时间计数寄存器,重新设定分频值时,PLL 进入锁定,输出稳
定频率的时钟需要一定的时间。这里设置成默认的值,以满足锁定的要求。
[ PLL_ON_START
; Added for confirm clock divide. for 2440.
; Setting value Fclk:Hclk:Pclk
ldr r0,=CLKDIVN;用于设定FCLK,HCLK和PCLK的比例
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.,在option.inc中有定义
str r1,[r0]
;program has not been copied, so use these directly
[ 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) ;Fin = 12.0MHz, UCLK = 48MHz
str r1,[r0]
nop ; Caution: After UPLL setting, at least 7-clocks delay must be inserted for setting hardware be completed. ;在UPP 设定之后,必须等待7 个时钟的延迟,设定才会有效
nop
nop
nop
nop
nop
nop
;Configure MPLL,设置MPLL
ldr r0,=MPLLCON
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;Fin = 12.0MHz, FCLK = 400MHz
str r1,[r0]
]
;查看是否是由睡眠状态启动,如果是则跳转到WAKEUP_SLEEP状态
;Check if the boot is caused by the wake-up from SLEEP mode.
ldr r1,=GSTATUS2
ldr r0,[r1]
tst r0,#0x2
;In case of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler.
bne WAKEUP_SLEEP
EXPORT StartPointAfterSleepWakeUp
StartPointAfterSleepWakeUp
初始化内存控制器其实就是对S3C2440 的memory bank 进行设置,使其扩展的存储器
或外部设备能够被处理器通过内存控制器正确读写。由于S3C2440 的最终应用程序是在
SDRAM(bank6)中运行,并与C 语言变量等的用户数据,各种模式的堆栈,中断向量表,
都被定位在SDRAM 的空间,所以它必须在涉及这些处理之前完成初始化工作。
;Set memory control registers
;ldr r0,=SMRDATA
adrl 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
这段是功能寄存器初始化,把13 个存储控制器的内容批量的读取到了对应的特殊功能
寄存器中,首先是下面有一个数据区SMRDATA,在程序的后面有定义,这个数据区给13 个寄存器分配52 字节的地址空间。在上面的代码中,r0 是这个数据区的起始地址,r2 是数据区的结束地址,r1 是寄存器的起始地址。这样,用一个判断语句就可以把内存中的数据赋给这13 个存储控制寄存器了。
上一篇:S3C2440对Nand Flash操作和电路原理(基于K9F2G08U0A)
下一篇:TQ2440启动代码分析(二)
推荐阅读最新更新时间:2024-03-16 14:35
设计资源 培训 开发板 精华推荐
- 有奖直播|保护嵌入式设备与系统的完整性和可靠性——英飞凌 OPTIGA™ TPM 安全解决方案
- 抢楼有礼:看直播,深入了解ST最新 MEMS气压计原理、操作、防水结构设计
- 提交物联网应用创意,赢万用表、蓝牙耳机、小米排插、京东卡等好礼!
- 在线直播 意法半导体工业峰会2022
- 瑞萨电子颠覆传统理念的嵌入式开发平台 Renesas Synergy™ 评估套件震撼来袭!答题即可免费申请!
- TI带你一起解剖共享单车智能锁!看视频涨知识赢好礼喽!
- 【EE征集令】LaunchPad学习全体验
- DIY 个浪漫光影棒,报名请跟帖!
- 看视频赢京东卡 PI汽车专用解决方案学习中心正式上线
- 中秋“国宴“ :拍摄板卡上的国产元器件,抽开心小礼品