📄 startup.s
字号:
;verson: 051114, rewrite initstack and more stable.
GET option.s
GET memcfg.s
GET 2410addr.s
;---------------------------------------------------------------------------------------------
;在这个宏定义中,我们使用了两个东西:1. $HandlerLabel,这个就是调用这个宏的时候的调用的地方的lable,
;就像这里使用的那样,使用了这个$HandlerLabel以后,就可以在宏中进行跳转,例如可以定义$HandlerLabel1,
;$HandlerLabel2等,作为local lable
;$HandleLabel当然就是参数啦
;该宏的功能:将HandLebel处放置的中断服务例程的入口地址加载到PC中(使用压栈出栈操作)。
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
;==================
;set timer
;==================
STR r0, R0_RESERVE
STR r1, R1_RESERVE
; rTCON = (rTCON & ~(0xF00))|0xA000;
LDR r0, =TCON
LDR r1, [r0]
BIC r1, r1, #0x0F000
ORR r1, r1, #0x0A000
STR r1, [r0]
; rTCON = (rTCON & ~(0xF00))|0x9000;
LDR r0, =TCON
LDR r1, [r0]
BIC r1, r1, #0x0F000
ORR r1, r1, #0x09000
STR r1, [r0]
LDR r0, R0_RESERVE
LDR r1, R1_RESERVE
[ INT_PORT_METHOD = 3
;still use sp_svc as stack in irq mode
msr cpsr_cxsf, #0x000000d3
str sp, SP_SVC
msr cpsr_cxsf, #0x000000d2
ldr sp, SP_SVC
]
[ INT_PORT_METHOD = 4
;store return address
sub lr, lr, #4
str lr, INT_RET_ADDR
;strore cpsr when ret
mrs lr, spsr
str lr, INT_RET_CPSR
;change to svc
msr cpsr_cxsf, #0x000000d3
; save lr_svc to empty a register
STR lr, LR_SVC
; push return address(= pc)
LDR lr, INT_RET_ADDR
STMFD sp!, {lr}
; push r12-r0
STMFD sp!, {r0-r12}
; push lr
LDR lr, LR_SVC
STMFD sp!, {lr}
; push INT_RET_CPSR
LDR r0, INT_RET_CPSR
STMFD sp!, {r0}
]
;SP-4以后放入sp,立即数用符号#表示。
;SP是编译器内定的寄存器,也就是R13。
;由于这里使用FD形式的堆栈,所以sp-4以后,将空出一个位置。
sub sp,sp,#4 ;decrement sp(to store jump address)
;这个指令表示将r0存入SP指向的地址,然后需要改变SP的值,FD表示满递减模式
;实际上是一个典型的压栈操作,但是这个指令的功能远非如此,它可以同时压入多个寄存器。
;将r0压入堆栈,因为这个宏在中断开始处被调用,所以需要保存任何的寄存器。
stmfd sp!,{r0} ;PUSH the work register to stack(lr does't push because it return to original address)
;加载地址到r0,=号的意思是:LDR{ condition} register,=[ expression | label-expression]
ldr r0,=$HandleLabel;load the address of HandleXXX to r0
;加载[r0]的内容到r0,实际使用中,[r0]放置一个中断服务程序的入口地址
ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
;将r0的内容写入sp+4的地址中。将服务程序的入口地址放置到堆栈中不修改sp的值。
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
;从堆栈中弹出r0和pc(pc应该就是设置好的这个地址$HandleLabel)
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
IMPORT |Image$$RO$$Limit| ; End of ROM code (=start of ROM data)
IMPORT |Image$$RW$$Base| ; Base of RAM to initialise
IMPORT |Image$$ZI$$Base| ; Base and limit of area
IMPORT |Image$$ZI$$Limit| ; to zero initialise
IMPORT Main ; The main entry of mon program
IMPORT Led_DisplayAll
;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的顶部
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 ~
AREA Init,CODE,READONLY
ENTRY
;jump to RestHandler
b ResetHandler
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved,跳到当前位置
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt
EXPORT R0_RESERVE
EXPORT R1_RESERVE
R0_RESERVE DCD 0
R1_RESERVE DCD 0
[ INT_PORT_METHOD = 3
SP_SVC DCD 0
]
[ INT_PORT_METHOD = 4
EXPORT INT_RET_ADDR
EXPORT INT_RET_CPSR
EXPORT LR_SVC
INT_RET_ADDR DCD 0
INT_RET_CPSR DCD 0
LR_SVC DCD 0
]
;这里的HANDLER就是一个宏定义,见文件的开头。
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
IsrIRQ
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
;得到是中断号
;这里INTOFFSET是中断中的一个寄存器,代表了是哪个中断
ldr r9,=INTOFFSET
ldr r9,[r9]
;中断服务子程序加载到r8中
;HandleEINT0是2410中断向量表的开头,这里将中
;断号×4加到基地址,就得到了对应中断服务子程序的入口地址所在的地址
ldr r8,=HandleEINT0
add r8,r8,r9,lsl #2
ldr r8,[r8]
;将中断服务程序的入口地址压入到将要弹出到pc堆栈中的位置。
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc} ;弹出的同时跳转到了中断服务程序
ResetHandler
;关闭所有的中断
msr cpsr_c, #0x000000D3
ldr r0,=INTMSK
ldr r1,=0xffffffff ;all interrupt disable
str r1,[r0]
;这个中断寄存器应该是INTMSK的扩展,总共有11bit
ldr r0,=INTSUBMSK
ldr r1,=0x3ff ;all sub interrupt disable
str r1,[r0]
;这里关闭看门狗
ldr r0,=WTCON ;watch dog disable
ldr r1,=0x0
str r1,[r0]
;这是两个PLL的锁定时间的设定,初始值就是0xFFF FFFF
;To reduce PLL lock time, adjust the LOCKTIME register.
ldr r0,=LOCKTIME
ldr r1,=0xffffff
str r1,[r0]
;Configure MPLL
ldr r0,=MPLLCON
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) ;Fin=12MHz,Fout=50MHz
str r1,[r0]
;这里的SMRDATA是在code段定义的一段数据,将SMRDATA中的数据拷贝到连续的Memory配置寄存器中,共13个
;Set memory control registers
ldr r0,=SMRDATA
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
;Initialize stacks
;初始化除了user模式外的所有模式的sp
bl InitStacks
;本来已经在HandleIRQ的地方空出了4个字节的地方,这里将IRQ的服务程序的地址写入该4字节内,
;并用HandlerIRQ HANDLER HandleIRQ,来使用这个设置的地址。
; 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]
;Copy and paste RW data/zero initialized data
;这部分的初始化段部分的工作主要有两个:
; 1)如果RW段不是在RO的后面(可能是RW需要重新搬移),那么将RO段的后面的数据拷贝到RW段中。
; 2)如果ZI段存在初始化为0
;这个可以参考SDT手册:6.10.2 Section-related symbols
;ZO应该是只读段了,RW应该就是可读写段了,ZI应该就是需要初始化的段
;|Image$$RO$$Limit|:Address of the byte beyond the end of the RO section
ldr r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
;Image$$RW$$Base:Address of the start of the RW section
ldr r1, =|Image$$RW$$Base| ; and RAM copy
;Image$$ZI$$Base Address of the start of the ZI section
ldr r3, =|Image$$ZI$$Base|
;Zero init base => top of initialised data
;如果RO段和RW段是紧挨着的则跳转
cmp r0, r1 ; Check that they are different
beq %F2
1
;将RO段后面的内容填充到RW段中,如果RW段存在的话
cmp r1, r3 ; Copy init data
;CC表示carry clear
ldrcc r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4
strcc r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4
bcc %B1
2
ldr r1, =|Image$$ZI$$Limit| ; Top of zero init segment
mov r2, #0
3
;将ZI段的首地址和末地址进行比较,也就是将ZI段全部置为0
cmp r3, r1 ; Zero init
strcc r2, [r3], #4
bcc %B3
bl Main
;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'
;将状态寄存器的内容转移到寄存器
;系统复位以后将处于SVC模式
mrs r0,cpsr
;这段代码的执行方式是,强制将cpsr设置为某个模式,然后修改sp,这样就是修改该模式下的sp了。
;以此来设置各个模式下的sp
;将低5bit清除
bic r0,r0,#MODEMASK ;也就是先将状态寄存器中的模式位清除#0x1f
orr r1,r0,#UNDEFMODE|NOINT ;orr r1,r0,#UNDEFMODE or NOINT 将模式设置为未定义模式,并禁止所有的中断,#0x1b or 0xc0
msr cpsr_cxsf,r1 ;这里的cxsf表示从低到高分别占用的4个8bit的数据域,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
orr r1,r0,#SVCMODE|NOINT
msr cpsr_cxsf,r1 ;SVCMode
ldr sp,=SVCStack
;USER mode has not be initialized.
;这个可能是如果一进入user模式,那么就不能再回到管理模式了。
mov pc,lr
;The LR register won't be valid if the current mode is not SVC mode.
;这是因为,进入该函数是再管理模式下进入,所以lr必须是管理模式下的lr
;为了保证literal pool在4K范围内,用户可以手动让编译器立即进行转化为code段的#data,也就是使用LTORG伪指令。
;LTORG伪指令可以放置的地址为:Place LTORG directives after unconditional branches or subroutine return instructions
LTORG
;DATA关键字的解析:The DATA directive informs the assembler that a label is a data-in-code label. This
; means that the label is the address of data within a code segment.
;也就是说这里定义一个在code区域中的data。
;这里的DCD伪指令用于分配字存储空间(32bit)并初始化
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))
DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0
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
;REFRESH
DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
;BANKSIZE
DCD 0x32 ;SCLK is active only during the access,SDRAM SCKE enable。SCLK power saving mode, BANKSIZE(bank6,7大小) 128M/128M
;MRSRB6
DCD 0x30 ;MRSR6 CL=3clk
DCD 0x30 ;MRSR7
; DCD 0x20 ;MRSR6 CL=2clk
; DCD 0x20 ;MRSR7
;By default, the ALIGN directive aligns the current location within the code to a word
;(4-byte) boundary.
ALIGN
AREA RamData, DATA, READWRITE
;这里定义一个结构化的内存表,内存表的开始地址就是_ISR_STARTADDRESS,_ISR_STARTADDRESS已经在
;前面有定义_ISR_STARTADDRESS EQU 0x33ffff00。这里的^也就是MAP伪指令,它用于定义表的首地址,
;#也就是FIELD伪指令,它用于定义表的具体结构,例如HandleReset # 4表示一个4字节的区域,
;HandleReset可以被程序中引用。
^ _ISR_STARTADDRESS
HandleReset # 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 # 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -