现在的位置: 首页 > 综合 > 正文

个人修改的用于MDK的2440init.s

2014年01月11日 ⁄ 综合 ⁄ 共 19185字 ⁄ 字号 评论关闭





;======================================================================================
; 文件名: 2440INIT.s
; 描述:	1).ARM 指令集,
;		2).小端格式
;		3).NOR Flash总线16位,大小2MB
;		4).NAND Flash型号:K9F2G08,大小256MB
;		5).GPIO总线32位,
; 更改日期: 2012年1月31日 
;======================================================================================
	


				GET 	2440addr.inc		; 引入寄存器地址
	
_STACK_BASEADDRESS		EQU 	0x33FF8000	; 堆栈基地址
_MMUTT_STARTADDRESS		EQU 	0x33ff8000	; 这个没用着啊~
_ISR_STARTADDRESS		EQU 	0x33FFFF00	; 定义第一级中断向量表的寻址地址

;============================== 系统时钟参数 ================================

; UCLK:UPLL固定为1:1,其中UCLK必须为48MHz(p7-24),UPLL由UPLLCON设置,后面的代码将其设置为48MHz
; 可选择的是 Fclk:Hclk:Pclk 分频比,该分频比可通过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.

CLKDIV_VAL		EQU		5	;选择Fclk:Hclk:Pclk=1:4:8

; FCLK由MPLLCON寄存器设置,可通过M_MDIV,M_PDIV,M_SDIV来调整,此处设置为400MHz,见P7-21

M_MDIV			EQU		92			; Fin=12.0MHz FCLK=400MHz
M_PDIV			EQU		1
M_SDIV			EQU		1

	
;============================= 预定义模式常量 ==============================

;用于设置CPSR的M[4:0],实现异常模式跳转

USERMODE    	EQU 	0x10
FIQMODE     	EQU 	0x11
IRQMODE     	EQU 	0x12
SVCMODE     	EQU 	0x13
ABORTMODE   	EQU 	0x17
UNDEFMODE   	EQU 	0x1b
MODEMASK    	EQU 	0x1f
NOINT       	EQU 	0xc0

;============================== 栈地址定义 ==================================

; 各异常模式的栈在SDRAM内存中的地址
												    	;    地址	   栈空间
UserStack		EQU		(_STACK_BASEADDRESS-0x3800)		; 0x33ff4800,	未知 
SVCStack		EQU		(_STACK_BASEADDRESS-0x2800)		; 0x33ff5800,	4KB
UndefStack		EQU		(_STACK_BASEADDRESS-0x2400)		; 0x33ff5c00,	1KB
AbortStack		EQU		(_STACK_BASEADDRESS-0x2000)		; 0x33ff6000,	1KB 
IRQStack		EQU		(_STACK_BASEADDRESS-0x1000)		; 0x33ff7000,	4KB 
FIQStack		EQU		(_STACK_BASEADDRESS-0x0)		; 0x33ff8000,	4KB
												   	

;=================================  中断例程相关宏定义  ==================================
;
; 在本启动程序的最下面在SDRAM中定义一段空间,用于存放异常处理程序的入口地址,即异常向量表,
; 除了ResetHandler外,其他异常在本启动程序入口跳转后执行的第一段程序就是下面宏定义这段程
; 序,这段程序从SDRAM中取出异常处理程序的入口地址,并跳转到此地址处开始执行.要本启动程序中,
; 复位异常几乎就是本启动程序,而IRQ异常处理程序是IsrIRQ,其他异常处理程序未定义.


				MACRO
$HandlerLabel 	HANDLER $HandleLabel		; $HandlerLabel比$HandleLabel多了个‘r’,两者不一样!
$HandlerLabel
				SUB  	SP, SP, #4          ; SP=SP-4,此地址用于存放转跳地址,也即中断程序的入口地址
				STMFD	SP!, {R0}			; 把工作寄存器R0压入栈,sp=SP-4(sp先减)
				LDR     R0, =$HandleLabel   ; 将HandleXXX的值放入r0
				LDR     R0, [R0]    		; 把HandleXXX的值所指向的内容(也就是中断程序的入口地址)放入R0
				STR     R0, [SP,#4]      	; 通过R0把中断服务程序(ISR)入口地址压入栈的sp=SP+4处
				LDMFD   SP!, {R0,pc}     	; 出栈(sp后增),恢复r0的原值,pc值更新为中断服务程序的入口
											; 地址(也就完成了到ISR的转跳)
				MEND

;=====================================  RO RW ZI  =======================================
;
;	要了解RO,RW和ZI区是什么意思,需要首先了解以下知识:
; 1)ARM程序的组成
;	注意:此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件。
;	一个ARM程序包含3部分:RO区,RW区和ZI区
;	RO是程序中的指令和常量,就是readonly
;	RW是程序中的已初始化"全局"变量,就是read/write
;	ZI是程序中的未初始化的"全局"变量,就是zero initialise(0初始化)
; 2)ARM映像文件的组成
;	所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。
; Image文件包含了RO和RW区数据,之所以Image文件不包含ZI区数据,是因为ZI区数据都是0,没必要包含,只
; 要程序运行之前将ZI区数据所在的区域一律清零即可。包含进去反而浪费存储空间。
; 3)ARM程序的执行过程
;	从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此
; 就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。实际上,RO区中的指令至少应该有这样
; 的功能:
;	1. 将RW区从ROM中搬到SDRAM内存中,因为RW区都是"全局"变量,不能存在ROM中,因为ROM只读不写
;	2. 将ZI区所在的SDRAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI区地址
;	   及大小来将相应得RAM区域清零。ZI区也都是"全局"变量,同理,"全局"变量不能存在ROM/SROM中	   	   
; 在main()执行前,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含"全局"变量的代码。
;
;	RO区存放的起始地址 = RO base = |Image$ER_ROM1$RO$Base|,
; 	RO区存放的终止地址 = RO limit - 1 = |Image$ER_ROM1$RO$Limit| - 1
; 	RW区在SDRAM中存放的起始地址 = RW base = |Image$RW_RAM1$RW$Base|
; 	ZI区在SDRAM中存放的起始地址 = ZI base = |Image$RW_RAM1$ZI$Base|
; 	ZI区在SDRAM中存放的终止地址 = ZI limit - 1 = |Image$RW_RAM1$ZI$Limit| - 1
; 	RW区在SDRAM中存放的终止地址 = RW limit - 1 = ZI base - 1,所以没必要再给出
;
;		RO base 为 Target Options 中设置的ROM1 start , RO limit = ROM1 start + 代码长度,根据ROM1 start
;	的不同设置,RO区可能存放在NOR flash中,也可能在SDRAM中.
;   	RW base 为 Target Options 中设置的RAM1 start , RW limit = ROM1 start + 已初始化全局变量长度
;   ZI base = RW limit, ZI limit = ZI base +  未初始化全局变量长度.	RW,ZI区一定或者说必须在SDRAM中

				IMPORT  |Image$ER_ROM1$RO$Base|		; Base of ROM code
				IMPORT 	|Image$ER_ROM1$RO$Limit|		; End of ROM code (=start of ROM data)
				IMPORT  |Image$RW_RAM1$RW$Base|		; Base of RAM to initialise
				IMPORT  |Image$RW_RAM1$ZI$Base|		; Base of area to zero initialise
				IMPORT  |Image$RW_RAM1$ZI$Limit|		; limit of area to zero initialise


;**************************************  代 码 段  ***************************************
				PRESERVE8					; 不知何用
	
				AREA    RESET,CODE,READONLY	; 代码段开始处

				ENTRY						; 标识程序入口处,要求编译器不将下面的异常跳转列表进行优化
	
				EXPORT	__ENTRY				; 声明__ENTRY可用被其他源文件全局引用,应该是用于.c文件
__ENTRY

;====================================   异常跳转   ======================================

ResetEntry							; 程序开始的地方				 相对地址
				B	ResetHandler	;								0x00000000
				B	HandlerUndef	; handler for Undefined mode,	0x00000004
				B	HandlerSWI		; handler for SWI interrupt,	0x00000008
				B	HandlerPabort	; handler for PAbort,			0x0000000C
				B	HandlerDabort	; handler for DAbort,			0x00000010
				B	.				; reserved,						0x00000014
				B	HandlerIRQ		; handler for IRQ interrupt,	0x00000018
				B	HandlerFIQ		; handler for FIQ interrupt,	0x0000001C
				B	EnterPWDN		; 由正常模式进入低功耗模式(power down),地址必须是
									; 0x00000020,貌似是约定好的

;=====================================  使用宏  ===================================
;	
; 采用上面定义的HANDLER宏去建立Hander***和Handle***之间的联系

HandlerFIQ      HANDLER 	HandleFIQ
HandlerIRQ      HANDLER 	HandleIRQ
HandlerUndef    HANDLER 	HandleUndef
HandlerSWI      HANDLER 	HandleSWI
HandlerDabort   HANDLER	 	HandleDabort
HandlerPabort   HANDLER 	HandlePabort

;==================================   IRQ中断例程  ===============================
;
; IRQ中断可细分为多个中断源的中断,如果异常向量表是一级向量表的话,细分后的中
; 断向量表就是二级向量表,下面的程序就是二级向量表的查询,下面会用到

IsrIRQ
				SUB		SP, SP, #4       	; reserved for PC,SP=SP-4
				STMFD	SP!, {R8-R9}		; SP=SP-8

				LDR		R9, =INTOFFSET		; 把INTOFFSET寄存器的值装入R9,其值指向的存储地址存着中断的
											; 偏移量(以字为单位,即4字节,见P14-16)
				LDR		R9, [R9]			; 中断的偏移量装入R9
				LDR		R8, =HandleEINT0 	; 将HandleEINT0装入R8,其值为二级向量表的入口地址
				ADD		R8, R8, R9, lsl #2	; R8=R8+R9*4
				LDR		R8, [R8]			; 将所要的中断处理程序的入口地址装入R8
				STR		R8, [SP,#8]			; SP=SP+8,将中断处理程序的入口地址推入堆栈
				LDMFD	SP!, {R8-R9,pc}		; 出线,R8,R9还原其值,将中断处理程序的入口地址装入pc
	
				LTORG						; 文字池
;-----------------------------------------------------------------------------------------

ResetHandler

;================================= 关闭看门狗, 屏蔽所有中断 ==============================

				LDR		R0, =WTCON		; 关看门狗定时器,p18-3
				LDR		R1, =0x0
				STR		R1, [R0]

				LDR		R0, =INTMSK
				LDR		R1, =0xFFFFFFFF	; 关所有中断,p14-12
				STR		R1, [R0]

				LDR		R0, =INTSUBMSK
				LDR		R1, =0x7FFF		; 关所有子中断,p14-18
				STR		R1, [R0]

;================================== 配置系统时钟 =====================================

				; 通过设置LOCKTIME寄存器,减少PLL锁存时间
				LDR		R0, =LOCKTIME	; P7-20,
				LDR		R1, =0x01360136	; p7-20,将S_LTIME和U_LTIME由初始值的OxFFFF改为0x136,只有310>300
				STR		R1, [R0]

				LDR		R0, =CLKDIVN	; CLKDIVN寄存器,p7-8,p7-24
				LDR		R1, =CLKDIV_VAL	; CLKDIV_VAL在上面定义,为UCLK:UPLL和FCLK:HCLK:PCLK的分频比选项,UPLL下面设置为48MHz
				STR		R1, [R0]		; CLKDIVN取0x00000101,即5
	
; 如果FCLK:HCLK不是1:1的关系的话,就要转成异步总线模式。反之,如果是这个比例关系的话,就转
; 成快速总线模式。
; MMU_SetAsyncBusMode 和 MMU_SetFastBusMode 都在4K代码以上,不可能拷到4K大小的steppingstone中, 
; 而nandflash启动后,会将前4KB的引导代码拷到steppingstone(4KB大小)中执行,因此不能使用这两个
; 函数如果你不想nandflash启动的话,就可以直接用上面的代码调用MMU_SetAsyncBusMode和MMU_SetFastBusMode
; 下面的代码就是实现和上面两函数一样的功能. 利用的协处理器指令实现了对总线模式的设置
; 至于为什么要用协处理器指令,代码是什么意思,我也不清楚。

				IF	CLKDIV_VAL>1 					; 意味着Fclk:Hclk不是1:1.
					MRC 	p15, 0, R0, c1, c0, 0	; MMU_SetAsyncBusMode,对协处理器15的c1和c0进行
													; 操作0(第0类),并将结果送入r0
					ORR 	R0, R0, #0xC0000000		; R1_nF:OR:R1_iA
					MCR 	p15, 0, R0, c1, c0, 0
				ELSE
					MRC 	p15, 0, R0, c1, c0, 0	; MMU_SetFastBusMode
					BIC 	R0, R0, #0xC0000000		; R1_iA:OR:R1_nF
					MCR 	p15, 0, R0, c1, c0, 0
				ENDIF
					
				; 配置UPLL
				LDR		R0, =UPLLCON
				LDR		R1, =((56<<12)+(2<<4)+2)  	; UPLL=48MHz,Fin=12MHz
				STR		R1, [R0]
				NOP	   ; 配置完UPLL后延迟7-clocks,才能配置MPLL,P7-21
				NOP
				NOP
				NOP
				NOP
				NOP
				NOP
				; 配置 MPLL
				LDR		R0, =MPLLCON
				LDR		R1, =((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)	; Fin=12MHz
				STR		R1, [R0]
				
				;检查这次启动是否是睡眠模式的唤醒导致的
				LDR		R1, =GSTATUS2							; p9-35
				LDR		R0, [R1]
				TST		R0, #0x2
				;如果是则跳到SLEEP_WAKEUP handler
				BNE		WAKEUP_SLEEP
	
;=============================  配置SDRAM内存控制寄存器 ===================================== 
;
;此段代码把13个存储控制器的内容批量的读取到了对应的特殊功能寄存器中首先是有一个数据区SMRDATA,
;在程序的后面有定义,这个数据区给13个寄存器分配52字节的地址空间。BWSCON寄存器的地址0x48000000
;貌似是所有寄存器中最低的,故从它开始设置。

				EXPORT StartPointAfterSleepWakeUp
StartPointAfterSleepWakeUp

				;配置存储器控制寄存器
				ADRL	R0, SMRDATA		; 用adrl要比ldr要好,因为可以省去设置文字池的麻烦	
				LDR		R1,=BWSCON		; BWSCON的地址,p5-14
				ADD		R2, R0, #52		; End address of SMRDATA

0
				LDR		R3, [R0], #4
				STR		R3, [R1], #4
				CMP		R2, R0
				BNE		%B0				; 向后(BACK)搜索标号为0的行,以此实现循环,具体可看局部标号的知识
				
				; 延时等待SDRAM稳定运行,好像设置SDRAM寄存器后都要延时,只是没说要延时多久
				MOV		R0, #256		
1
				SUBS	R0, R0, #1		; 向后(BACK)搜索标号为1的行,以此实现循环,具体可看局部标号的知识
				BNE		%B1

;====================================  初始化栈 =====================================
;
; 下面这段用初始于初始化堆栈,所以不能用stmfd,ldmfd之类用到的堆栈指令,UndefStack,
; AbortStack,IRQStack,FIQStack,SVCstack上面定义过。有一点要搞清楚,每一种模式都有专门
; 的SP寄存器(r13,r13_fiq,r13_svc……),下面代码貌似在设置同一个SP寄存器,其实是设置不同的
; SP寄存器!在工具包2.5以下版本, 'MSR cpsr,R1'可以代替'MSR cpsr_cxsf,R1',cpsr_cxsf相当于
; cpsr_all,在s3c2440的datasheet中用的是cpsr_all(p3-21),但在ADS中用cpsr_all会报错,因为指
; 令比较老,cpsr_cxsf的意思可参考arm_assembler_reference.pdf的P3-138

				MRS		R0, CPSR
				BIC		R0, R0,#MODEMASK
				ORR		R1, R0,#UNDEFMODE:OR:NOINT
				MSR		CPSR_CXSF,R1		; UndefMode
				LDR		SP, =UndefStack		; UndefStack=0x33FF5C00

				ORR		R1, R0, #ABORTMODE:OR:NOINT
				MSR		CPSR_CXSF, R1		; AbortMode
				LDR		SP,=AbortStack		; AbortStack=0x33FF6000

				ORR		R1, R0, #IRQMODE:OR:NOINT
				MSR		CPSR_CXSF, R1		; IRQMode
				LDR		SP, =IRQStack		; IRQStack=0x33FF7000

				ORR		R1, R0, #FIQMODE:OR:NOINT
				MSR		CPSR_CXSF, R1		; FIQMode
				LDR		SP, =FIQStack		; FIQStack=0x33FF8000

				BIC		R0, R0, #MODEMASK:OR:NOINT
				ORR		R1, R0, #SVCMODE
				MSR		CPSR_CXSF, R1		; SVCMode
				LDR		SP, =SVCStack		; SVCStack=0x33FF5800

;================================== 初始化image运行域 ===================================

; BWSON(p5-14)寄存器的DW0[2:1]的值由硬件决定,也就是OM[1:0](p5-4)决定,"00"表示nandflash
; 启动,"01"或"10"为norflash启动,"01"为16位宽度,"10"为32位宽度,"11"为Test mode

				LDR		R0, =BWSCON
				LDR		R0, [R0]
				ANDS	R0, R0, #6			; 若OM[1:0] != 0, 则是NOR FLash启动
				BNE		InitRam				; 不读nand flash,跳到InitRam
				ADR		R0, ResetEntry		; 若OM[1:0] == 0, 则是NAND FLash启动
				CMP		R0, #0				; 如果入口不是0,即使用了仿真器,
				BNE		InitRam				; 则不在启动时读nand flash,跳到InitRam
	
;---------------------------------- NAND Flash 程序搬移 ------------------------------------
nand_boot_beg

				MOV		R5, #NFCONF			; P6-12
				;set timing value
				LDR		R0,	=(3<<12):OR:(7<<8):OR:(7<<4)
				STR		R0,	[R5]			; CLE &ALE duration setting value = 3/HCLK,CLE信号指定nandflash的
											; 的指令周期,ALE信号指定nandflash的地址周期
											; TWRPH0 duration setting value = (7+1)/HCLK
											; TWRPH1 duration setting value = (7+1)/HCLK
				
				; 取消lock-tight,取消 soft lock,关闭非法访问中断,关闭RnB 中断,检测上升沿,锁住
				; spare ECC,锁住main数据区ECC生成码,初始化ECC译码器,取消片选,使能nandflash控制器
				LDR		R0, =(0<<13):OR:(0<<12):OR:(0<<10):OR:(0<<9):OR:(0<<8):OR:(1<<6):OR:(1<<5):OR:(1<<4):OR:(1<<1):OR:(1<<0)
				STR		R0, [R5, #4]		; 配置NFCONT寄存器,其中(1<<1)取消了片选,P6-13
				
				BL 		ReadNandID  		; 按着读取NAND的ID号,结果保存在r5里
				LDR 	R0, =0xECAA 		; EC为maker ID,AA为K9F2G08R0A的设备ID
				CMP 	R5, R0 
				BLEQ	K9F2G08R0A_show 	; 若是K9F2G08R0A,则第一个LED灯亮			
				BEQ 	%F2   				; r5和r0相等的话就跳到下一个1标号处
				LDR 	R0, =0xECDA 		; EC为maker ID,DA为K9F2G08U0A的设备ID
				CMP 	R5, R0
				BLEQ	K9F2G08U0A_show		; 若是K9F2G08U0A,则第二个LED灯亮
				BEQ 	%F2   				; r5和r0相等的话就跳到下一个1标号处
				B		.
2					
				MOV		R8, #0				; r8表示页号
				LDR		R9, =ResetEntry		; 取ResetEntry的绝对地址
3	
				ANDS	R0, R8, #0x3f		; 若r8为64的整数倍(因为1 block=64 pages)则继续执行,否则跳至后面标号为3处
				BNE		%F4
				MOV		R0, R8
				BL		CheckBadBlk			; 这个坏块检测程序应该不适合K9F2G08型号
				CMP		R0, #0
				ADDNE	R8, R8, #64			; 若r0非零,则表示坏块,跳至下一个块。
				BNE		%F0
4	
				MOV		R0, R8
				BL		ReadNandPage
				ADD		R9, R9, #2048		; 一页是2KB+64B,这里读的是main区
				ADD		R8, R8, #1
0
				CMP		R8, #64				; 复制64页,即搬移128KB代码到SDRAM中
				BCC		%B3				
				
				MOV		R5, #NFCONF			; DsNandFlash
				LDR		R0, [R5, #4]
				BIC 	R0, R0, #1
				STR		R0, [R5, #4]		; nand flash controller disable(Don't work),见P6-14

				LDR		PC,=InitRam			;从这一步开始,它就是在SDRAM中运行了!在内存中将RW区复制到RAM1中

;-----------------------------------  初始化RAM1 -------------------------------------
;
; 这段代码是将RW区复制到SDRAM中以BaseOfBSS为起始地址的内存中,如前所述,因ZI区全为0,
; 故不需复制,只需清零ZI区在SDRAM内存中所在区域就行,BaseOfBSS和BaseOfZero-1分别人RW区
; 的起始地址和末尾地址。BaseOfZero和EndOfBSS-1分别是ZI区的起始地址和末尾地址
		
InitRam		
				LDR 	R3, TopOfROM
				ADRL 	R0, ResetEntry 
				LDR 	R2, BaseOfROM
				
				SUB		R2, R2, R3		; R2=BaseOfROM-TopOfROM
				SUB		R0, R0, R2		; R0=R0-R2=ResetEntry-(BaseOfROM-TopOfROM)=ResetEntry+代码长度
										;   =ResetEntry+(TopOfROM-BaseOfROM)
						
				; 复制代码加载位置中的RW区到RW base,RW区大小为BaseOfZero-BaseOfBSS
				LDR 	R2, BaseOfBSS   ; R2<-BaseOfBSS的绝对地址
				LDR 	R3, BaseOfZero  ; R3<-BaseOfZero的绝对地址
6
				CMP		R2, R3			; 复制的内容的大小为BaseOfZero-BaseOfBSS
				LDRCC	R1, [R0], #4	; 复制的内容的起始地址为r0,若在SDRAM内存中运行则为
										; TopOfROM,否则为ResetEntry+TopOfROM-BaseOfROM
				STRCC	R1, [R2], #4
				BCC		%B6	

				; 用0初始化ZI区
				MOV		R0,	#0
				LDR		R3,	EndOfBSS		
7	
				CMP		R2,	R3			; R2=BaseOfZero, R3=EndOfBSS
				STRCC	R0, [R2], #4	; 以BaseOfZero为起始地址,将EndOfBSS-BaseOfZero大小的空间清零
				BCC		%B7			 

 				
;=================================== 保存中断例程地址 ====================================
;
; 因为只写了IRQ中断例程,故只保存IRQ中断例程的地址,以后若把其他中断例程补了,可在此保存中断例程地址
;
; RO、RW、ZI区复制分配完后,将二级向量表的中断查询例程的地址放到一级向量表IRQ异常向量中,使
; 得IRQ向量指向二级向量表的中断查询例程

				; Setup IRQ handler
				LDR		R0, =HandleIRQ	; IRQ异常向量的绝对地址->R0
				LDR		R1, =IsrIRQ	  	; R1 = 二级向量表的中断查询例程IsrIRQ的绝对地址
				STR		R1, [R0]

;==================================== 跳转到C程序入口 =====================================
;
; 所有初始化配置做完后,跳到.c源文件中的Main函数
; 下面的代码不能用[|]合成一段,不知为何

				IMPORT  	Main    	; The main entry of mon program
										; using_the_arm_assembler.pdf的p8-21
					BL		Main		; 注意这里用的是Main,不是main!故不能使用void main(),而应用void Main()
					B 		.			; 死循环,注意小数点

	
;==============================  NAND Flash ID号读取函数 ===================================
;
; 根据K9F2G08U0A.pdf的P42和P32说明,nandflash的ID有5个字节,以下面只读取第一个字节(即Maker Code)
; 和第二个字节(即Device Code),读取ID的操作顺序是:1.发读ID命令0x90;2.发寻址信号0x00;3.读第一个
; 字节;4.读第二个字节……据p10表格,nandflash忙状态下不能接受读ID命令0x90,故要测试是否处于忙状态
; 另因所用nandflash型号只有8位I/O管脚,故NFDATA只有8位有效数字,故每次只读8位,见P6-6

ReadNandID
				MOV      R7, #NFCONF
				LDR      R0, [R7,#4]		; NFChipEn();
				BIC      R0, R0, #2		
				STR      R0, [R7,#4]		; 设置NFCONT,将Reg_nCE位清为0,即允许片选,见P6-14
				MOV      R0, #0x90			; WrNFCmd(RdIDCMD);
				STRB     R0, [R7,#8]		; 设置NFCMMD,发出读NANDflash ID的指令0x90,P6-15
				MOV      R4, #0				; WrNFAddr(0);
				STRB     R4, [R7,#0xc]		; 设置NFADDR,NAND flash存储器寻址值设为0,为何要在此置为0?P6-15
8											;while(NFIsBusy());
				LDR      R0, [R7,#0x20]		; 测试NFSTAT的RnB位,为1表示NAND flash空闲,为0则表示忙状态,循环等待其空闲,P6-18
				TST      R0, #1
				BEQ      %B8
				LDRB     R0, [R7,#0x10]		; id  = RdNFDat()<<8,从NFDATA寄存器中读第一个ID字节(即Maker Code)
				MOV      R0, R0, lsl #8
				LDRB     R1, [R7,#0x10]		; id |= RdNFDat(),从NFDATA寄存器中读第二个ID字节(即Device Code)
				ORR      R5, R1, R0
				LDR      R0, [R7,#4]		; NFChipDs();
				ORR      R0, R0, #2
				STR      R0, [R7,#4]		; 设置NFCONT,将Reg_nCE位清为1,即取消片选,见P6-14
				BX		 LR	

WaitNandBusy
				MOV      R0, #0x70			; WrNFCmd(QUERYCMD);
				MOV      R1, #NFCONF
				STRB     R0, [R1,#8]		; 设置NFCMMD,发出读nandflash状态指令0x70,见K9F2G080A.pdf的P41
9											; while(!(RdNFDat()&0x40));	
				LDRB     R0, [R1,#0x10]
				TST      R0, #0x40			; 判断I/O管脚是否为1,为1表示空闲,为0表示忙状态,见K9F2G080A.pdf的P41
				BEQ		 %B9
				BX		 LR

;====================================== NAND Flash 坏块检测 ==================================

;使用READ指令读出所检查块的第一页的2048列字节,若为FF,则表示此块正常,具体见K9F2G08U0A.pdf的P15

CheckBadBlk
				MOV		 R7, LR				; 因为此段程序有跳转,故要保存返回地址
				MOV		 R5, #NFCONF
				
				BIC		 R0, R0,#0x3F		; Ox3F=64-1, 64为一个block的页数,这里是为保证R0是64的整数倍
				LDR		 R1, [R5,#4]		; NFChipEn()
				BIC      R1, R1,#2
				STR      R1, [R5,#4]		; 设置NFCONT,将Reg_nCE位清为0,即允许片选,见P6-14

				MOV      R1, #0x0			; WrNFCmd(READCMD),发出读指令
				STRB     R1, [R5,#8]

				MOV		 R1, #0
				MOV		 R2, #8
				STRB     R1, [R5,#0xc]		; WrNFAddr(0)
				STRB     R2, [R5,#0xc]		; WrNFAddr(8),和上面的指令合在一起就是发出2048列地址(A0-A11)
				STRB     R0, [R5,#0xc]		; WrNFAddr(addr)
				MOV      R1, R0,lsr #8		; WrNFAddr(addr>>8)
				STRB     R1, [R5,#0xc]
				MOV      R1, R0,lsr #16		; WrNFAddr(addr>>16),和上面的三个指令合在一起就是发出页地址(A12-A28)
				STRB     R1, [R5,#0xc]
				
				MOV      R1, #0x30			
				STRB     R1, [R5,#8]		; 发出命令0x30

				BL       WaitNandBusy		; WaitNFBusy()	

				LDRB	 R0, [R5,#0x10]		; RdNFDat(),读取该block的第一页的第一个字节
				SUB		 R0, R0, #0xFF		; 若此字节为FF,即R0为0,则表示该block正常
							
				LDR      R1, [R5,#4]		; NFChipDs()
				ORR      R1, R1,#2
				STR      R1, [R5,#4]		; 设置NFCONT,将Reg_nCE位清为1,即取消片选,见P6-14
				
				BX		 R7

;================================= 读 NAND Flash 页内容 ================================

; r0表示页号,r9为resetentry的绝对地址,为RO base,也即BaseOfRom
	
ReadNandPage
				MOV		 R7, LR				; 因为此段程序有跳转,故要保存返回地址
				MOV      R4, R9				; r1为要复制的
				MOV		 R2, #0
				MOV      R5, #NFCONF

				LDR      R1, [R5,#4]		; NFChipEn()
				BIC      R1, R1,#2
				STR      R1, [R5,#4]		; 设置NFCONT,将Reg_nCE位清为0,即允许片选,见P6-14	

				MOV      R1, #0				; WrNFCmd(READCMD0)
				STRB     R1, [R5,#8]		; 发出读命令0x00
				
				STRB     R1, [R5,#0xc]		; WrNFAddr(0)
				STRB     R1, [R5,#0xc]		; WrNFAddr(0)和上面的指令合在一起就是发出0x0列地址(A0-A11)
				STRB     R0, [R5,#0xc]		; WrNFAddr(addr)
				MOV      R1, R0,lsr #8		; WrNFAddr(addr>>8)
				STRB     R1, [R5,#0xc]
				MOV      R1, R0,lsr #16		; WrNFAddr(addr>>16),和上面的三个指令合在一起就是发出页地址(A12-A28)
				STRB     R1, [R5,#0xc]

				MOV      R1, #0x30			
				STRB     R1, [R5,#8]		; 发出命令0x30

				BL       WaitNandBusy		; WaitNFBusy()
		
12
				LDRB     R1, [R5,#0x10]		; buf[i] = RdNFDat(),循环一次读取一个字节的数据
				STRB     R1, [R4,R2]		; r4表示复制的SDRAM目标地址
				ADD      R2, R2, #1
				CMP      R2, #0x800			; 0x800为2048,可我们所用的nandflash一页的大小为2K+64bit,这里只读main区
				BCC      %B12
	
				LDR      R0, [R5,#4]		; NFChipDs()
				ORR      R0, R0, #2
				STR      R0, [R5,#4]		; 设置NFCONT,将Reg_nCE位清为1,即取消片选,见P6-14
					
				BX		 R7

;============================= NAND Flash 型号LED指示 =====================================

K9F2G08R0A_show
				MOV		R0, #0x56000000		;GPACON寄存器的地址,见P9-8
				MOV		R1, #0x0055			
				STR		R1, [R0, #0x50]		;配置GPFCON寄存器,GPF0~GPF3为output,GPF4~GPF7为intput,见P9-18	
				MOV		R1, #0xFE
				STR		R1, [R0, #0x54]		;配置GPFDAT寄存器为0x50,即GPF0~GPF3输出1110,见P9-18
				BX		LR

K9F2G08U0A_show
				MOV		R0, #0x56000000		;GPACON寄存器的地址,见P9-8
				MOV		R1, #0x0055			
				STR		R1, [R0, #0x50]		;配置GPFCON寄存器,GPF0~GPF3为output,GPF4~GPF7为intput,见P9-18	
				MOV		R1, #0xFD
				STR		R1, [R0, #0x54]		;配置GPFDAT寄存器为0x50,即GPF0~GPF3输出1101,见P9-18
				BX		LR

				LTORG	;文字池



SMRDATA 		DATA
;=============================== SDRAM寄存器配置值 =================================

; 下面的配置不一定是最优的

				DCD 	0x2212d110		; P5-14,BWSCON
				DCD 	0x00007FF4 		; GCS0, P5-16, BANKCON0,接NOR flash
				DCD 	0x00002e50		; GCS1, P5-16, BANKCON1
				DCD 	0x00002e50		; GCS2, P5-16, BANKCON2
				DCD		0x00002e50  	; GCS3, P5-16, BANKCON3
				DCD 	0x00002e50  	; GCS4, P5-16, BANKCON4
				DCD 	0x00002e50 	 	; GCS5, P5-16, BANKCON5
				DCD		0x00018005		; GCS6, P5-17, BANKCON6,接SDRAM
				DCD 	0x00018005		; GCS7, P5-17, BANKCON7
				DCD 	0x009404F5		; P5-18,	REFRESH
				DCD 	0x32	    	; SCLK power saving mode, P5-19, BANKSIZE,128M
				DCD 	0x30	    	; MRSR6,CL=3clk, P5-20, MRSRB6
				DCD 	0x30	   	 	; MRSR7,CL=3clk, P5-20, MRSRB7

;==================================================================================
		
BaseOfROM		DCD		|Image$ER_ROM1$RO$Base|	; Base of ROM code
TopOfROM		DCD		|Image$ER_ROM1$RO$Limit|	; End of ROM code (=start of ROM data)
BaseOfBSS		DCD		|Image$RW_RAM1$RW$Base|	; Base of RAM to initialise
BaseOfZero		DCD		|Image$RW_RAM1$ZI$Base|	; Base and limit of area
EndOfBSS		DCD		|Image$RW_RAM1$ZI$Limit|	; to zero initialise	

;================================== 低功耗模式 ===================================
	
; 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.

; 下面这一段相当于c语言中的函数void EnterPWDN(int CLKCON),.c源文件可以调用这段代码
; 以实现从正常模式进入低功耗模式,其中r0传递参数CLKCON,也即CLKCON寄存器要设置的值
; 低功耗模式有三种:IDLE Mode, SLOW Mode,SLEEP Mode,其中IDLE Mode和SLOW Mode 本启动程
; 序没写.一旦进行这两个模式,将进入死循环.

EnterPWDN
				MOV		R2, R0		    ; R2即CLKCON要设置的值
				TST		R0, #0x8
				BNE		ENTER_SLEEP
				B		.				; 不是睡眠模式则死循环

;------------------------------------ --  进入睡眠模式 ------------------------------------

; 下面是进入睡眠模式的操作,是根据datasheet的P7-15的步骤来做的

ENTER_SLEEP
				;GSTATUS3是通用状态寄存器,用户自定义其用途,其值在睡眠模式下不变
				;下面的代码将唤醒后开始执行的代码的地址存入GSTATUS3寄存器中
				ADR		R0,StartPointAfterSleepWakeUp
				LDR		R1,=GSTATUS3
				STR		R0,[R1]
				
				LDR 	R0, =REFRESH		; P5-18
				LDR 	R1, [R0]			; R1=REFRESH寄存器的值
				ORR 	R1, R1, #(1<<22)	; BIT_SELFREFRESH有前面定义了,为1<<22
				STR 	R1, [R0]			; 允许 SDRAM 自刷新

				MOV 	R1, #16				; 延时等待刷新完毕,datasheet没说延时多久
13				
				SUBS 	R1, R1, #1
				BNE 	%B13

				LDR		R1, =MISCCR			; MISCCR寄存器,P9-24
				LDR		R0, [R1]
				ORR		R0, R0,#(7<<17)  	; 使SCLK0=0, SCLK1=0, SDRAM自刷新保持使能
				STR		R0, [R1]

				LDR 	R0, =CLKCON			; 将要配置的值送入CLKCON寄存器,进入SLEEP模式
				STR 	R2, [R0]

				B		.					; 死循环

;==================================== 睡眠模式的唤醒 ==================================

; 下面是唤醒睡眠模式的操作,是根据datasheet的P7-16的步骤来做的

WAKEUP_SLEEP
				; 从睡眠模式唤醒后释放SCLKn
				LDR		R1, =MISCCR			; MISCCR寄存器,P9-24
				LDR		R0, [R1]
				BIC		R0, R0, #(7<<17)  	; SDRAM自刷新保持使能,SCLK0=SCLK, SCLK1=SCLK(SCLK是SDRAM时钟?SCLK0?SCLK1?)
				STR		R0, [R1]

				; 设置SDRAM内存控制器,总共有13个,它们的值在下面的SMRDATA数据区中一起指定了
				ADRL	R0, SMRDATA		
				LDR		R1, =BWSCON			; BWSCON Address,BWSCON寄存器的地址是13个中最低的
				ADD		R2, R0, #52			; 13*4=52
14
				LDR		R3, [R0], #4
				STR		R3, [R1], #4
				CMP		R2, R0
				BNE		%B14

				MOV 	R1, #256			; 等待更新完毕,没说要延时多长时间
15
				SUBS 	R1, R1, #1		
				BNE 	%B15

				; 下面程序默认用GSTATUS3来保存SLEEP模式唤醒后要跳往的地址了
				LDR 	R1, =GSTATUS3 	
				LDR 	R0, [R1]
				BX  	R0

;***************************************** 数 据 段 *****************************************
			
;=====================================  异常向量表 ==========================================
		
				AREA 	RamData, DATA, READWRITE,ALIGN=2
				MAP   	_ISR_STARTADDRESS	; _ISR_STARTADDRESS=0x33FFFF00,为第一级中断向量表的寻址地址,在上面有定义

								; 在SDRAM中的地址

HandleReset 	FIELD   4		; 地址0x33FFFF00,这个用不着
HandleUndef 	FIELD   4		; 地址0x33FFFF04
HandleSWI		FIELD   4		; 地址0x33FFFF08
HandlePabort    FIELD   4		; 地址0x33FFFF0C
HandleDabort    FIELD   4		; 地址0x33FFFF10
HandleReserved  FIELD   4		; 地址0x33FFFF14
HandleIRQ		FIELD   4		; 地址0x33FFFF18
HandleFIQ		FIELD   4		; 地址0x33FFFF1C

;===================================  中断向量表 =================================

; Do not use the label 'IntVectorTable',
; The value of IntVectorTable is different with the address you think it may be.
; IntVectorTable
HandleEINT0		FIELD   4		; 地址0x33FFFF20
HandleEINT1		FIELD   4		; 地址0x33FFFF24
HandleEINT2		FIELD   4		; 地址0x33FFFF28
HandleEINT3		FIELD   4		; 地址0x33FFFF2C
HandleEINT4_7	FIELD   4		; 地址0x33FFFF30
HandleEINT8_23	FIELD   4		; 地址0x33FFFF34
HandleCAM		FIELD   4		; 地址0x33FFFF38
HandleBATFLT	FIELD   4		; 地址0x33FFFF3C
HandleTICK		FIELD   4		; 地址0x33FFFF40
HandleWDT		FIELD   4		; 地址0x33FFFF44
HandleTIMER0 	FIELD   4		; 地址0x33FFFF48
HandleTIMER1 	FIELD   4		; 地址0x33FFFF4C
HandleTIMER2 	FIELD   4		; 地址0x33FFFF50
HandleTIMER3 	FIELD   4		; 地址0x33FFFF54
HandleTIMER4 	FIELD   4		; 地址0x33FFFF58
HandleUART2  	FIELD   4		; 地址0x33FFFF5C

HandleLCD 		FIELD   4		; 地址0x33FFFF60
HandleDMA0		FIELD   4		; 地址0x33FFFF64
HandleDMA1		FIELD   4		; 地址0x33FFFF68
HandleDMA2		FIELD   4		; 地址0x33FFFF6C
HandleDMA3		FIELD   4		; 地址0x33FFFF70
HandleMMC		FIELD   4		; 地址0x33FFFF74
HandleSPI0		FIELD   4		; 地址0x33FFFF78
HandleUART1		FIELD   4		; 地址0x33FFFF7C
HandleNFCON		FIELD   4		; 地址0x33FFFF80
HandleUSBD		FIELD   4		; 地址0x33FFFF84
HandleUSBH		FIELD   4		; 地址0x33FFFF88
HandleIIC		FIELD   4		; 地址0x33FFFF8C
HandleUART0 	FIELD   4		; 地址0x33FFFF90
HandleSPI1 		FIELD   4		; 地址0x33FFFF94
HandleRTC 		FIELD   4		; 地址0x33FFFF98
HandleADC 		FIELD   4		; 地址0x33FFFF9C
								; 地址0x33FFFFA0
				END














抱歉!评论已关闭.