;======================================================================================
; 文件名: 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$RO$Base|,
; RO区存放的终止地址 = RO limit - 1 = |Image$RO$Limit| - 1
; RW区在SDRAM中存放的起始地址 = RW base = |Image$RW$Base|
; ZI区在SDRAM中存放的起始地址 = ZI base = |Image$ZI$Base|
; ZI区在SDRAM中存放的终止地址 = ZI limit - 1 = |Image$ZI$Limit| - 1
; RW区在SDRAM中存放的终止地址 = RW limit - 1 = ZI base - 1,所以没必要再给出
;
IMPORT |Image$RO$Base| ; Base of ROM code
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
;************************************** 代 码 段 ***************************************
AREA Init,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输出0001,见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输出0001,见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$RO$Base|
TopOfROM DCD |Image$RO$Limit|
BaseOfBSS DCD |Image$RW$Base|
BaseOfZero DCD |Image$ZI$Base|
EndOfBSS DCD |Image$ZI$Limit|
;================================== 低功耗模式 ===================================
; 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