⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 startup.s

📁 提高μCOS-II在ARM上执行效率的几种方法》相应代码
💻 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 + -