个人修改的用于ADS1.2的2440init.s
来源:互联网 发布:林佳一是谁的女儿知乎 编辑:程序博客网 时间:2024/05/16 07:09
- ; 文件名: 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