×
嵌入式 > 嵌入式开发 > 详情

S3C2410启动代码详解(2)

发布时间:2020-08-26 发布时间:
|
;========================================================================================

//在这里用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 entryof 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.等待自刷新生效
0subs r1,r1,#1
bne %B0//表示不相等则往回跳转到标号为0的位置,在此为上一句。

ldr r0,=CLKCON;enter STOP mode.
str r2,[r0]

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 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.
0subs r1,r1,#1
bne %B0

ldr r1,=MISCCR
ldrr0,[r1]
orrr0,r0,#(7<<17) ;Make sure that SCLK0:SCLK->0, SCLK1:SCLK->0, SCKE=L during boot-up
strr0,[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等相关的时钟周期等
ldrr0,[r1]
bicr0,r0,#(7<<17)//SCLK0:0->SCLK, SCLK1:0->SCLK, SCKE:L->H

strr0,[r1]//通过将MISCCR[19:17]设置为000b,释放SDRAM信号保护。

;Set memory control registers配置内存控制寄存器。
ldrr0,=SMRDATA//在程序的后面LTORGSMRDATA DATA中定义了

//一个数据缓冲池就是用来配置相关的内存控制寄存器的
ldrr1,=BWSCON ;BWSCON Address
addr2, r0, #52 ;End address of SMRDATA
0
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

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了,跳转成功!



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
Recogni:将高端AI芯片推向自动驾驶边缘