一、实验目的:
练习ARM汇编程序的编写,对提供的程序的启动代码进行分析,了解S3C2410初始化过程, 初始化代码主要是包含在start.s中。
二、实验原理:
启动程序要完成的任务包括:硬件初始化,系统存储系统的配置,复制二级中断向量表。
启动程序过程
● 系统硬件初始化
系统上电或复位后,程序从位于地址0x0的Reset Exception Vector处开始执行,因此需要在这里放置Bootloader的第一条指令:b ResetHandler,跳转到标号为ResetHandler处进行第一阶段的硬件初始化,执行完,系统进行堆栈和存储器的初始化。使用了外设,则需要设置相关的寄存器,确定其刷新频率、总线宽度等信息。
● 代码段复制到RAM中运行
需要把系统的代码复制到RAM中运行。映像文件内部共有三种输出段:RO段、RW段和ZI段。ARMLink同时还产生了这三种输出段的起始和终止定位信息:Image$$RO$$Base、Image$$RO$$Limit、Image$$RW$$Base、Image$RW$Limit、Image$ZI$Base和Image$$ZI$$Limit。可以在程序中使用这些定位信息。将ROM中的代码和数据搬移到RAM中。
● 建立二级中断向量表
在ARM系统中,中断向量表位于0X0开始的地址处,意味着无论运行什么样的上层软件,一旦发生中断,程序就得到Flash存储器中的中断向量表里去,降低系统的运行效率。因此在RAM中建立自己的二级中断向量表,当中断发生后,程序直接从RAM中取中断向量进入中断子程序。尤其是在中断频繁发生的系统里,这种方法可以大大提高系统的运行效率。
三、实验内容:
1.运行一个简单的串口程序,单步执行初始化代码,观察寄存器变化。 2.分析系统上电后的初始化工作包括哪些内容。
3.分析中断的处理过程,包括中断向量表的建立、中断源的识别及中断IRQ服务程序是如何进入的。
4.分析应用程序的结构。
四、实验代码:
在系统上电后,初始化部分的工作注意包含以下部分: 1、 关看门狗定时器
2、 屏蔽所有中断
3、 设置CPU时钟频率。
4、 设置存储器控制寄存器,对外部存储器的参数进行设置。 5、 初始化各模式下的堆栈
6、 建立IRQ中断的总的入口地址 7、 初始化应用程序的执行环境 8、 跳入Main函数,进入C程序
;========================================= ; 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 ;=========================================
GET option.s ;GET相当INCLUDE 将一个源文件包含到当前源文件,这里表示包含
option.s,并在当前位置进行汇编
BIT_SELFREFRESH EQU (1<<22) ;定义了一些符号常量
;ARM异常模式的定义
;SDRAM/DRAM 刷新控制器 bit22 REFMD位 0:CBR/AUTO REFRESH 1:SHIF REFRESH ;下面是对arm处理器模式寄存器对应值的常数定义,arm处理器中有一个CPSR程序状 ;态寄存器,CPSR后五位决定目前的处理器模式。 ;Pre-defined constants USERMODE FIQMODE IRQMODE SVCMODE
EQU EQU EQU EQU
0x10 ;用户模式
0x11 ;FIQ快速中断模式 0x12 ;中断模式 0x13 ;管理模式
GET memcfg.s GET 2410addr.s
ABORTMODE EQU UNDEFMODE EQU MODEMASK EQU NOINT
EQU
0x17 ;中止模式 0x1b ;未定义指令模式 0x1f ;系统模式
0xc0 ;禁止IRQ和FIQ中断
;ARM各异常模式堆栈,定义各模式堆栈地址 ;_STACK_BASEADDRESS在option.s中, ;The location of stacks
_STACK_BASEADDRESS EQU (SDRAM_END-0x8000) UserStack 堆栈
SVCStack EQU (_STACK_BASEADDRESS-0x2800) 管理模式堆栈 4k UndefStack
EQU (_STACK_BASEADDRESS-0x2400)
;0x33ff5c00 ~0x3ff57ff ; ;0x33ff5800 ~0x3ff47ff ;
;0x33ff8000
;0x33ff4800 ~ ; 用户模式
EQU (_STACK_BASEADDRESS-0x3800)
未定义指令模式堆栈 1k AbortStack
EQU (_STACK_BASEADDRESS-0x2000)
;0x33ff6000 ~0x3ff5bff ;
中止模式堆栈 1k
IRQStack EQU (_STACK_BASEADDRESS-0x1000) 中断模式堆栈 4k
FIQStack EQU (_STACK_BASEADDRESS-0x0) 快速中断模式堆栈 4k ;arm处理器有两种工作状态
;1.arm:32位 这种工作状态下执行字对齐的arm指令 ;2.Thumb:16位 这种工作状;态执行半字对齐的Thumb指令
;因为处理器分为16位 32位两种工作状态 程序的编译器也是分16位和32两种编译方式 ;所以下面的程序用于根据处理器工作状态确定编译器编译方式 ;code16伪指令指示汇编编译器,后面的指令为16位的thumb指令 ;code32伪指令指示汇编编译器,后面的指令为32位的arm指令
;这段是为了统一目前的处理器工作状态和软件编译方式(16位编译环境使用tasm.exe编译) ;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used. 检查是否是用tasm.exe进行16位编译
GBLL THUMBCODE ;声明一个全局变量 并初始化为{FALSE}
;0x33ff8000 ~0x33ff6fff ; ;0x33ff7000 ~0x3ff5fff ;
[ {CONFIG} = 16 ;if config=16这里表示用16位编译方式
THUMBCODE SETL {TRUE} ;SETL 给全局变量赋值,设置THUMBCODE 为 true 式
| ; |=ELSE | 等同 ELSE
;设置THUMBCODE 为 false,THUMBCODE=FALSE ;endif ] 等同 ENDIF
CODE32
;CODE32表明一下操作都在ARM状态,转入32位编译模
THUMBCODE SETL {FALSE}
]
;宏定义MOV_PC_LR
MACRO ;MACRO 定义宏,宏定义开始
MOV_PC_LR ;宏将被下面定义部分展开,宏名 MOV_PC_LR
[ THUMBCODE
;if THUMBCODE=true
;THUMBCODE 模式上返回ARM状态 ;else
bx lr
|
mov pc,lr ;ARM状态下返回
MACRO
;宏定义开始
;宏名为MOVEQ_PC_LR,与上面部分相同 ;if THUMBCODE=true
;EQ 相等 则跳转回ARM状态 ;else
]
;end if ;宏结束
MEND
MOVEQ_PC_LR
[ THUMBCODE
bxeq lr |
moveq pc,lr
;宏定义-进入异常流程 ;HANDLER-宏的名称 ;$HandleLabel-宏的参数
]
;同上位置相同部分,不同的是如果相等才跳回ARM状态
;end if ;宏结束
MEND
;这个宏的作用是把各个中断程序的地址装入当前的PC,2410有两种装断模式:一种是没有
;中断向量表,一种是使用中断向量表的
;使用中断向量表只能是IRQ方式,当使用中断向量表的时候,中断发生时由2410的中断控 ;制器自动跳转到 ;相应的位置。
;注意下面这段宏定义程序
;下面包含的HandlerXXX HANDLER HandleXXX将都被下面这段程序展开
;本初始化程序定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首 ;地址。每个字,即4字节
;空间都有一个标号,以Handle***命名。
;在向量中断模式下使用“加载程序”来执行中断服务程序。
;向量中断模式和非向量中断模式的区别是: 向量中断模式是当cpu读取位于0x18处的IR ;Q中断指令的时候,系统自动读取对应于该中断源确定地址上的指令取代0x18处的指令, ;通过跳转指令系统就直接跳转到对应地址
;函数中 节省了中断处理时间提高了中断处理速度标 例如 ADC中断的向量地址为0xC0, ;在0xC0处放如下代码:
;ldr PC,=HandlerADC 当ADC中断产生的时候系统会自动跳转到HandlerADC函数中 ;非向量中断模式处理方式是一种传统的中断处理方法,当系统产生中断的时候,系统将int ;errupt pending寄存器中对应标志位置位 ;然后跳转到位于0x18处的统一中断;函数中
;该函数通过读取interrupt pending寄存器中对应标志位来判断中断源,并根据优先级关系 ;再跳到;对应中断源的处理代码中
;大致作用是把宏的第一个参数$HandlerLabel转变为一个标号,然后让程序跳转到第二个参 ;数$HandleLabel(为一个地址)对应的值的地址去。
;所以,通过上面的分析可以看出,$HandlerLabel HANDLER $HandleLabel是让PC跳转到$H ;andleLabel中存放的地址执行。
MACRO
;宏定义
$HandlerLabel HANDLER $HandleLabel ;宏名字叫HANDLER,$HandleLabel是
;行参,定义了一个标号$HandlerLabel,展开时可替换成相应的符号 $HandlerLabel
sub sp,sp,#4 stmfd
;sp-sp-4,空出的空间用于存放跳转地址
sp!,{r0} ;sp=sp-4,并将r0入栈
;获取存放跳转地址的标号HandleLabel到r0 ;将HandleLabel中存放的跳转地址送给r0 ;将r0存放到sp+4的地方,
;弹出栈顶2个字,分别保存到r0和pc中,系统将跳
ldr r0,=$HandleLabel ldr r0,[r0] str r0,[sp,#4] ldmfd sp!,{r0,pc}
;转到对应中断处理函数,由上r0和pc都没变,但程序已跳转 MEND ;宏定义结束 ;连接器生成的输出段相关的符号
;引入连接器生成的映象文件的各个部分地址。
;OR-只读区域、RW-读写区域、ZI-初始化为0的区域。
IMPORT |Image$$RO$$Limit|
; End of ROM code (=start of ROM data) ,RO结束
;地址+1
IMPORT |Image$$RW$$Base| IMPORT |Image$$ZI$$Base| IMPORT |Image$$ZI$$Limit|
; Base of RAM to initialise,RW起始 ; Base and limit of area,RW段结束+1 ; to zero initialise,ZI段起始地址
; 引入外部函数Main,进入C程序。
IMPORT Main ; The main entry of mon program ;IMPORT LEDTEST
;定义ARM汇编程序段,段名为SelfBoot,程序段为只读的代码段。 AREA SelfBoot,CODE,READONLY
;Selfboot初始化程序
;板子上电和复位后程序开始从位于0x0处开始执行,硬件刚刚上电复位后程序从这里开始 ;执行跳转到标为ResetHandler处执行
;DCD用于分配一段字内存单片,并用后面的伪指令初始化,分配字节由expr 个数决定 ;程序入口地址
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.
ASSERT :DEF:ENDIAN_CHANGE ;ASSERT 断言错误伪指令,这里表示是否定;3)The pseudo instruction,DCD can't be used here because the linker generates error.
义过ENDIAN_CHANGE
[ ENDIAN_CHANGE ;如果定义了ENDIAN_CHANGE
ASSERT :DEF:ENTRY_BUS_WIDTH ;这里表示是否定义过ENTRY_BUS_WIDTH
[ ENTRY_BUS_WIDTH=32 ;if ENTRY_BUS_WIDTH=32
b
ChangeBigEndian ;DCD 0xea000007,跳转到ChangeBigEndian,
;执行DCD 0xea000007 改变大小端数据模式
]
;end if
[ ENTRY_BUS_WIDTH=16 ;if ENTRY_BUS_WIDTH=16
andeq
r14,r7,r0,lsl #20
; DCD 0x0007ea00,标志状态寄存器CPSR的Z
;=1时,r14=r7+r0逻辑左移20位,执行DCD 0x0007ea00改变大小端模式
]
;end if
;if ENTRY_BUS_WIDTH=8
[ ENTRY_BUS_WIDTH=8
streq
r0,[r0,-r10,ror #1] ; DCD 0x070000e,当标志状态寄存器CPSR的Z
;=1时„,执行DCD 0x070000ea 改变大小端模式 ]
;end if
| ;ELSE 即如果没定义 ENDIAN_CHANGE b
]
ResetHandler
;复位处理模式
式
b b b b b
HandlerUndef ;handler for Undefined mode 处理为定义模式
处理软中断模式
处理终止程序访问终止模
HandlerSWI ;handler for SWI interrupt HandlerPabort
;handler for PAbort
HandlerDabort .
;handler for DAbort
处理数据访问终止模式 保留,\".\"代表指令的地
;reserved
址 ,即表示进行死循环
;@0x20
b
\"@\" 存储区位置计数器的当前值 ;进入掉电模式,见下面的标号
b b
HandlerIRQ HandlerFIQ
;handler for IRQ interrupt 处理中断模式 ;handler for FIQ interrupt 处理快速中断模式
EnterPWDN
ChangeBigEndian ;上面提到的ChangeBigEndian,改变大小端数据模式 ;@0x24
[ ENTRY_BUS_WIDTH=32 DCD 0xee110f10
;if ENTRY_BUS_WIDTH=32
;0xee110f10 => mrc p15,0,r0,c1,c0,0
DCD 0xe3800080 ;0xe3800080 => orr r0,r0,#0x80; //Big-endian DCD 0xee010f10 ]
[ ENTRY_BUS_WIDTH=16 DCD 0x0f10ee11 DCD 0x0080e380 DCD 0x0f10ee01 ]
[ ENTRY_BUS_WIDTH=8 DCD 0x100f11ee
;if ENTRY_BUS_WIDTH=8
;if ENTRY_BUS_WIDTH=16 ;0xee010f10 => mcr p15,0,r0,c1,c0,0
DCD 0x800080e3 DCD 0x100f01ee
]
;swinv 0xffffff is similar with NOP and run well in both endian
DCD 0xffffffff
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.
; 2. All interrupt should be maksked for SDRAM/DRAM self-refresh.
; 3. LCD controller should be disabled for SDRAM/DRAM self-refresh. ; 4. The I-cache may have to be turned on.
; 5. The location of the following code may have not to be changed. ;进入掉电模式功能
; 1. SDRAM 必须在自刷新模式.
; 2. 所有中断必须屏蔽 for SDRAM/DRAM self-refresh. ; 3. LCD 关闭for SDRAM/DRAM self-refresh. ; 4. The I-cache 可能需要开启.
; 5. The location of the following code may have not to be changed. ;void EnterPWDN(int CLKCON); ;进入掉电模式 EnterPWDN mov r2,r0
tst r0,#0x8
;r2=rCLKCON CLKCON[3]掉电模式控制位 0:关闭 1:进入掉电模式
; POWER_OFF mode?判断POWER_OFF 是否为0 。r0和
#0X8相与,更新CPSR位
ENTER_STOP ;不进入掉电模式
ldr r0,=REFRESH
;0x48000024 ;DRAM/SDRAM refresh DRAM/SDRAM 刷新控
bne ENTER_POWER_OFF ;NE 不相等(则表示power_off不为O) 标志位Z=0
制器,刷新DRAM/SDRAM
ldr r3,[r0]
;r3=rREFRESH
;r1=r3
mov r1, r3
orr r1, r1, #BIT_SELFREFRESH ;BIT_SELFREFRESH EQU (1<<22) bit22 TREFMD位
0:CBR/AUTO REFRESH 1:SHIF REFRESH
str r1, [r0]
;R1->[R0] Enable SDRAM self-refresh
;%B是向后搜索局部标号, %F是向前搜索局部标号。
mov r1,#16
;延时 wait until self-refresh is issued. may not be
needed. 0
subs r1,r1,#1 bne %B0
;enter STOP mode. CLKCON ;Clock generator control
;0为局部标号(延时)
ldr r0,=CLKCON
0
str r2,[r0]
mov r1,#32
subs r1,r1,#1 bne %B0
;1)延时 wait until the STOP mode is in effect.
;2) Or wait here until the CPU&Peripherals will be turned-off
; Entering POWER_OFF mode, only the reset by wake-up is available.
ENTER_POWER_OFF
;NOTE. 0
ldr r1,=MISCCR ; ;Miscellaneous control,混杂控制模式 ldr r0,[r1]
orr r0,r0,#(7<<17) ;Make sure that SCLK0:SCLK->0, SCLK1:SCLK->0, SCKE=L during mov r1,#16
;Wait until self-refresh is issued,which may not be needed.
;延时
;1) rGSTATUS3 should have the return address after wake-up from POWER_OFF mode.
ldr r0,=REFRESH ldr r1,[r0]
;r1=rREFRESH
;进入掉电模式
ldr r0,=REFRESH str r3,[r0]
MOV_PC_LR ;调用宏MOV_PC_LR
;exit from SDRAM self refresh mode.
orr r1, r1, #BIT_SELFREFRESH str r1, [r0]
;Enable SDRAM self-refresh
subs r1,r1,#1 bne %B0
;boot-up
str r0,[r1]
ldr r0,=CLKCON str r2,[r0]
;CPU will die here. \".\"当前指令的地址 CPU死在这
b .
WAKEUP_POWER_OFF ;掉电唤醒
;Release SCLKn after wake-up from the POWER_OFF mode. ldr r1,=MISCCR ;Miscellaneous control,混杂模式 ldr r0,[r1]
bic r0,r0,#(7<<17) ;SCLK0:0->SCLK, SCLK1:0->SCLK, SCKE:L->H str r0,[r1]
;Set memory control registers
ldr r0,=SMRDATA
;BWSCON Address 起始位置 依次为BANKCON0 BANKCON1
ldr r1,=BWSCON
BANKCON2 ........
add r2, r0, #52
;End address of SMRDATA
0
ldr r3, [r0], #4
;读取R0地址的数据到R3 R0=R0+4 读取SMRDATA 数组
(BWSCON BANKCON0 ....)的值
str r3, [r1], #4
;读取R3的数据到 R1地址内存中 R1=R1+4 赋给BWSON
BANKCON0 ....寄存器 0
mov r1,#256 subs r1,r1,#1
;1) wait until the SelfRefresh is released. ;延时
cmp r2, r0
;判断是否赋完值
bne %B0
bne %B0
ldr r1,=GSTATUS3 ;GSTATUS3 has the start address just after POWER_OFF
wake-up
ldr r0,[r1]
;Saved data0(32-bit) before entering POWER_OFF mode,在进
;入掉电模式之前(POWER=OFFMODE),保存data0
LTORG ;文字池, 声明一个数据缓冲池的开始 mov pc,r0
;下面是具体的中断处理函数跳转的宏,通过上面的$HandlerLabel的宏定义展开后跳转到对 ;应的中断处理;函数(对于向量中断)
HandlerFIQ HANDLER HandleFIQ ;调用宏HANDLER
HandlerIRQ HANDLER HandleIRQ ;HandlerIRQ 行号 HANDLER 宏名 ;HandleIRQ 导入的形参
HandlerUndef HANDLER HandleUndef HandlerSWI HANDLER HandleSWI HandlerDabort HANDLER HandleDabort HandlerPabort HANDLER HandlePabort
;下面这段程序是用来处理非向量中断,具体判断I_ISPR中各位是否置1 ,置1表示目前此 ;中断等待响应(每次只能有一位置1),
;从最高优先级中断位开始判断,检测到等待服务中断就将pc置为中断服务函数首地址
IsrIRQ ;IRQ模式中断(片内外设中断 引脚中断)
sub sp,sp,#4
; reserved for PC,中断分发例程入口地址预留栈空间,预留
;pc返回指针的存储位置
stmfd
sp!,{r8-r9}
;将R8~R9 放入堆栈
;INTOFFSET寄存器各位表明发生了应该调用那个中断子程序。 ;不过这个寄存器只能用在IRQ中断模式。
ldr r9,=INTOFFSET ;中断请求源偏移寄存器 ldr r9,[r9] ;r9中存放INTOFFSET的内容
ldr r8,=HandleEINT0 ; 取EINT0的地址,即IRQ 中断首址 add r8,r8,r9,lsl #2
;r8=r8+ r9<<2 r9<<2(乘4),4字节对齐
ldr r8,[r8] str r8,[sp,#8]
;取[r8]地址的数据放入R8,加载中断服务函数地址到r8 ;将R8中的数据放入[sp+8]的地址中,保存到sp,作为新的
;PC
;=================================== ; ENTRY
;===================================
ldmfd
sp!,{r8-r9,pc} ;将堆栈内空恢复到 R8 R9 PC,跳转到新的PC运行
;板子上电和复位后 程序开始从位于0x0执行,b ResetHandler 程序从跳转到这 ;里执行
;板子上电复位后 执行几个步骤。 ;1.禁止看门狗 屏蔽所有中断
;初始化程序入口指令
ResetHandler ;复位处理程式
ldr r0,=WTCON ; watch dog disable 关闭看门狗 ldr r1,=0x0
str r1,[r0] ;r1->[r0] 禁止看门狗
ldr r0, =0x300 ldr r1, [r0] b
%B1
;1 ; ;
ldr r0,=INTMSK ldr r1,=0xffffffff
;all interrupt disable,关中断,全部置1
str r1,[r0] ;r1->[r0] 禁止所有中断
ldr r0,=INTSUBMSK ldr r1,=0x7ff str r1,[r0]
;all sub interrupt disable, 关所有子中断
;测试外部LED等
[ {FALSE}
;if
; rGPFDAT = (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);
; Led_Display
ldr r0,=GPFCON ;F口控制寄存器 ldr r1,=0x5500 str r1,[r0]
ldr r0,=GPFDAT ;F口数据寄存器 ldr r1,=0x10 str r1,[r0] ]
;2.根据工作频率设置pll ,设置ARM主时钟频率 ;计算公式
;Fpllo=(m*Fin)/(p*2^s) ;m=MDIV+8,p=PDIV+2,s=SDIV ;Fpllo必须大于20Mhz小于66Mhz ;Fpllo*2^s必须小于170Mhz
;To reduce PLL lock time, adjust the LOCKTIME register.
ldr r0,=LOCKTIME ldr r1,=0xffffff str r1,[r0]
;Check if the boot is caused by the wake-up from POWER_OFF mode. ldr r1,=GSTATUS2 ; GSTATUS2 复位状态寄存器 ldr r0,[r1] tst r0,#0x2
;判断R0 的第二位是否为0
[ PLL_ON_START
;Configure MPLL
ldr r0,=MPLLCON
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;Fin=12MHz,Fout=50MHz str r1,[r0] ]
;In case of the wake-up from POWER_OFF mode, go to POWER_OFF_WAKEUP ;handler.
EXPORT StartPointAfterPowerOffWakeUp ;声明全局函数 nop;bne WAKEUP_POWER_OFF
StartPointAfterPowerOffWakeUp
;3.置存储相关寄存器的程序
;设置存储器控制寄存器。
;是设置SDRAM,flash ROM 存储器连接和工作时序的程序,片选定义的程序 ;SMRDATA map在下面的程序中定义
;Set memory control registers
ldr r0,=SMRDATA
ldr r1,=BWSCON ;BWSCON Address ;BWSCON Address 起始位置依次为BANKCON0
;BANKCON1 BANKCON2 ........
add r2, r0, #52
;End address of SMRDATA 一共13个寄存器
0
ldr r3, [r0], #4 ;读取R0地址的数据到R3,R0=R0+4 读取SMRDATA 数组 (BWSCON
BANKCON0 ....)的值
str r3, [r1], #4 ;读取R3的数据到 R1地址内存中 R1=R1+4 赋给BWSON
BANKCON0 ....寄存器
cmp r2, r0
bne %B0 ;禁止Icache和Dcache,禁止MMU ;IMPORT MMU_DisableICache
;bl MMU_DisableICache ;
;IMPORT MMU_DisableDCache
;bl MMU_DisableDCache ; ;IMPORT MMU_InvalidateICache ;bl MMU_InvalidateICache ;
;IMPORT MMU_DisableMMU
;bl MMU_DisableMMU ;
;初始化堆栈 ;Initialize stacks
bl
InitStacks
;4.建立IRQ中断 ; Setup IRQ handler
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c str r1,[r0]
;5.复制和初始化RW和ZI区域
adr r0, ResetEntry ;adr也是取值,但这时程序在哪运行就取那时的地址 ldr r2, BaseOfROM cmp r0, r2 ldreq
r0, TopOfROM
beq InitRam ldr r3, TopOfROM
;将RO区域的代码copy到RW域中并且将ZI区域初始化为0。
;6.将数据段拷贝到ram中 将零初始化数据段清零,跳入C语言的main函数执行到这步结束bootloader初步引导结束
0
ldmia stmia
r0!, {r4-r7} r2!, {r4-r7}
cmp r2, r3 bcc %B0
sub r2, r2, r3 sub r0, r0, r2
InitRam ;将RW内容从FLASH拷贝到SDRAM 0
cmp r2, r3
;copy 初始化代码
ldr r2, BaseOfBSS ldr r3, BaseOfZero
ldrcc r1, [r0], #4 ;没有设置C标志位的时候执行,即无符号数小于 strcc r1, [r2], #4 bcc %B0
;ZI段全部清零 1
[ :LNOT:THUMBCODE
bl
Main
;将THUMBCODE 做逻辑非操作
mov r0, #0 ;初始化ZI区域为0 ldr r3, EndOfBSS
cmp r2, r3 strcc r0, [r2], #4 bcc %B1
; Don't use main() because ...... 转入C程序
;bl LEDTEST ]
[ THUMBCODE ;for start-up code for Thumb mode ]
orr lr,pc,#1 bx lr CODE16 bl b
Main .
; Don't use main() because ...... 转入C程序
b
. ;死循环
CODE32
;堆栈初始化
;这是上面提到的对存储寄存器初始化的数据map ;function initializing stacks InitStacks
orr r1,r0,#IRQMODE|NOINT msr cpsr_cxsf,r1 ldr sp,=IRQStack
;IRQMode
;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 ;r0<-cpsr
bic r0,r0,#MODEMASK ;MODEMASK=0x1f 清除CPSR中的PSR位(5bit) orr r1,r0,#UNDEFMODE|NOINT ;NOINT=0XC0 禁止IRQ和FIQ中断 msr cpsr_cxsf,r1
;UndefMode cpsr_cxsf<-r1
ldr sp,=UndefStack
orr r1,r0,#ABORTMODE|NOINT msr cpsr_cxsf,r1
;AbortMode
ldr sp,=AbortStack
bic r0,r0,#MODEMASK|NOINT orr r1,r0,#SVCMODE msr cpsr_cxsf,r1
;SVCMode
orr r1,r0,#FIQMODE|NOINT msr cpsr_cxsf,r1 ldr sp,=FIQStack
;FIQMode
ldr sp,=SVCStack
;USER mode has not be initialized. 用户模式不初始化堆栈
mov pc,lr
;The LR register won't be valid if the current mode is not SVC mode.
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)) ; BWSCON=0x2211D110定义BWSCON
DCD
((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0 BANK0CON=0x0700定义BANKCON0,下同
DCD
((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) ;GCS1 BANKCON1=0x7FFC
DCD
((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) ;GCS2 BANKCON2=0x0700
DCD
((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) ;GCS3 BANKCON3=0x0700
DCD
((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) ;GCS4 BANKCON4=0x0700
DCD
((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) ;GCS5 BANKCON5=0x0700 ; ;
;这里将中断异常向量建立在sdram中 ;可读写的数据段
AREA RamData, DATA, READWRITE ;异常中断向量(矢量)
ALIGN ;通过添加补丁字节使当前位置满足一定的对齐方式,声明字对齐 DCD 0x30 ;MRSR6 CL=3clk ;Mode register set for SDRAM DCD 0x30 ;MRSR7 ;Mode register set for SDRAM DCD 0x20 ;MRSR6 CL=2clk DCD 0x20 ;MRSR7
DCD 0x32 ;SCLK power saving mode, BANKSIZE 128M/128M
DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) ;GCS6 BANKCON6=0x18005 DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) ;GCS7 BANKCON7=0x18005 DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) ;REFR
ESH ;REFRESH=0x008E0459 DRAM/SDRAM refresh 设置刷新周期
^ _ISR_STARTADDRESS
^=MAP:定义一个结构化的内存表(storage map)的首地址,地址为0x33ff8000
HandleReset # 4 ; #--Field:定义一个结构化内存表中的数据域,该域为4个
;字节
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 HandleWDT HandleTIMER0 HandleTIMER1 HandleTIMER2 HandleTIMER3 HandleTIMER4 HandleUART2 HandleLCD
# 4 # 4 # 4 # 4 # 4 # 4 # 4 # 4 # 4
HandleDMA0 # 4 HandleDMA1 HandleDMA2
# 4 # 4
HandleDMA3 HandleMMC HandleSPI0
# 4 # 4 # 4 # 4 # 4 # 4 # 4
HandleUART1 HandleRSV24 HandleUSBD HandleUSBH
HandleIIC # 4 HandleUART0 HandleSPI1 HandleRTC HandleADC
END
IRQ中断执行步骤:当出现一个IRQ中断时,首先执行b HandlerIRQ ,这是中断的入口指令,紧接着跳到标号为HandlerIRQ的语句上,即HandlerIRQ HANDLER HandleIRQ,根据上面的HANDLER宏定义,程序被这样执行 HandlerIRQ
sub sp,sp,#4 ;decrement sp(to store jump address) stmfd
sp!,{r0} ;PUSH the work register to stack
# 4 # 4 # 4 # 4
ldr r0,= HandleIRQ;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)
这样执行仍然缺一个参数HandleIRQ,根据程序最后的中断向量表对其的定义HandleIRQ # 4 将其放入,但是这只是地址,需要确定的指向某个地方,否则就是空的,所以,在上电后执行这几行语句后,HandleIRQ变成了IsrIRQ的地址
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c'
str r1,[r0]
接下来应该执行IsrIRQ对应的程序。即以下程序: IsrIRQ
sub sp,sp,#4 ; stmfd
sp!,{r8-r9} ;
ldr r9,=INTOFFSET ldr r9,[r9];
ldr r8,=HandleEINT0; add r8,r8,r9,lsl #2;r8=r8+r9*4 ldr r8,[r8]; str r8,[sp,#8]; ldmfd
sp!,{r8-r9,pc};
执行完上面程序后,中断源就被判别出来,其对应的中断服务程序入口地址就被装载到PC里面,开始执行该中断服务程序。
五、实验总结:
在本次试验中主要是对S3C2410 启动代码进行了分析,理解s3c2410的启动过程的执行顺序,了解S3C2410初始化过程。对以后arm硬件程序的编写奠定了基础。
因篇幅问题不能全部显示,请点此查看更多更全内容