由于jtag-mini是并口的,笔记本没有,不能烧写norflash,所以,用mini2440出厂前norflash中固化的supervivi配合dnw软件,用usb烧写nandflash。使用nandflash也是现在比较节省、流行的做法。以下代码是从2440init.s裁剪而来,其中norflash、sdram等启动方式已被删减,仅支持从nandflash启动。
注意,由于从nandflash仅拷贝128KB,所以程序大小需要把握一下,当然还可以修改以下代码实现支持更大的代码长度。
GET memcfg.inc
GET 2440addr.inc
;GET option.inc
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
IMPORT Main ; The main entry of mon program
AREA RESET,CODE,READONLY
EXPORT __ENTRY ;导出__ENTRY标号
__ENTRY
ResetEntry
b ResetHandler
LTORG
ResetHandler
ldr r0,=WTCON ;关闭看门狗; watch dog disable
ldr r1,=0x0
str r1,[r0]
ldr r0,=INTMSK
ldr r1,=0xffffffff ;关闭所有中断;all interrupt disable
str r1,[r0]
ldr r0,=INTSUBMSK
ldr r1,=0x7fff ;关闭所有子中断;all sub interrupt disable
str r1,[r0]
;===============================================================================
;设置内存控制器等寄存器的值,因为这些寄存器是连续排列的,所以采用如下办法对这些
;寄存器进行连续设置.其中用到了SMRDATA的数据,这在代码后面有定义
;===============================================================================
adrl r0, SMRDATA;be careful!
ldr r1,=BWSCON ;BWSCON Address
;SMRDATA数据的结束地址,共有52字节的数据
add r2, r0, #52 ;End address of SMRDATA
0
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne �
;上面这段代码的作用就是设置存储控制器。在代码的后面有一个SMRDATA的数据区,
;用r0来定义它的起始地址,用r2来定义它的结束地址。r3是代表那13个存储控制器.
;代码很明显,就是把内存的数据赋给这13个存储控制器里面的。
;================================================================================
;清除SDRAM
;================================================================================
; mov r1,#0
; mov r2,#0
; mov r3,#0
; mov r4,#0
; mov r5,#0
; mov r6,#0
; mov r7,#0
; mov r8,#0
; ldr r9,=0x4000000 ;64MB
; ldr r0,=0x30000000
;0
; stmia r0!,{r1-r8}
; subs r9,r9,#32
; bne �
;很明显可以看出,程序利用r1~r8这几个寄存器把0x30000000到0x34000000的内存全部清零了。
;===============================================================================
;设计堆栈指针
;===============================================================================
ldr sp, =0x33ff5800
;===============================================================================
;初始化nandflash及代码搬移
;===============================================================================
nand_boot_beg ; 这一段代码完成从NAND读代码到RAM
mov r5, #NFCONF ;首先设定NAND的一些控制寄存器
;set timing value
ldr r0, =(7<<12)|(7<<8)|(7<<4)
str r0, [r5]
;enable control
ldr r0, =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)
str r0, [r5, #4]
bl ReadNandID ;按着读取NAND的ID号,结果保存在r5里
mov r6, #0 ;r6设初值0.
ldr r0, =0xec73 ;期望的NAND ID号
cmp r5, r0 ;这里进行比较
beq � ;相等的话就跳到下一个1标号处
ldr r0, =0xec75 ;这是另一个期望值
cmp r5, r0
beq � ;相等的话就跳到下一个1标号处
mov r6, #1
1
bl ReadNandStatus ;读取NAND状态,结果放在r1里
mov r8, #0 ;r8设初值0,意义为页号
ldr r9, =ResetEntry ;r9设初值为初始化程序入口地址
;=========================================================================
; 注意,在这里使用的是ldr伪指令,而不是上面用的adr伪指令,它加载的是ResetEntry
; 的决对地址,也就是我们期望的RAM中的地址,在这里,它和|Image$$RO$$Base|一样
; 也就是说,我如我们编译程序时RO BASE指定的地址在RAM里,而把生成的文件拷到
; NAND里运行,由ldr加载的r9的值还是定位在内存.
;=========================================================================
2
ands r0, r8, #0x1f ;凡r8为0x1f(32)的整数倍-1,eq有效,ne无效
bne � ;这句的意思是对每个块(32页)进行检错
mov r0, r8 ;r8->r0
bl CheckBadBlk ;检查NAND的坏区
cmp r0, #0 ;比较r0和0
addne r8,r8,#32 ;存在坏块的话就跳过这个坏块
bne � ;没有的话就跳到标号4处
3
mov r0, r8 ;当前页号->r0
mov r1, r9 ;当前目标地址->r1
bl ReadNandPage ;读取该页的NAND数据到RAM
add r9, r9, #512 ;每一页的大小是512Bytes ;SDRAM address increase
add r8, r8, #1 ;r8指向下一页;flash page add 1
4
cmp r8, #256 ;比较是否读完256页即128KBytes;copy 256 pages except bad block
bcc � ;如果r8小于256(没读完),就返回前面的标号2处
mov r5, #NFCONF ;DsNandFlash;DsNandFlash
ldr r0, [r5, #4]
bic r0, r0, #1
str r0, [r5, #4]
;ldr pc, =copy_proc_beg ;调用copy_proc_beg,已经在SDRAM里啦!
;ADR装载的是当时运行的时候的地址
;LDR装载的是连接时候的绝对地址
;ResetEntry 是一个程序开头的地址,用了ADR,所以生成的是一个偏移量,也就是当时运行
;的时候的地址,如果确实从从nor flash启动的话,那么这个地址就应该为 0,如果这程序
;是用仿真器下载到 0x30000000 的SDRAM中运行的话,那么这个地址就应该等于 0x30000000
copy_proc_beg
;adr r0, ResetEntry
;ldr r2, BaseOfROM
;接着代码判断刚才的两个地址是否相等。我们可以分两种情况来看
;1,如果从norflash启动,那么 ResetEntry=0 BaseOfROM=0x3000000,显然不相同
;2,如果用工具将这个文件加载到 0x30000000 的话,那么这个时候 ResetEntry=0
;BaseOfROM=0x3000000 显然这两个地址是相同的。
;这么做的效果就是:如果从nor flash启动的话,那么就将程序代码 copy 到运行时地址,也
;就是 |Image$$RO$$Base| ,如果地址是相同的,那么就不需要执行 COPY 动作了。
;
;cmp r0, r2 ;两个进行比较
;ldreq r0, TopOfROM ;如果相同的话,为r0赋上R0的结束位置,也是RW的起始位置。
;如果相等的话(在内存运行),TopOfROM->r0
;beq InitRam ;同时跳到InitRam
ldr r0, TopOfROM
ldr r2, BaseOfBSS
ldr r3, BaseOfZero
0
cmp r2, r3
ldrcc r1, [r0], #4
strcc r1, [r2], #4
bcc � ;可以看出这一段是对ResetEntry里面定义好的数据拷贝到RW段。
;接着就是将所有的ZI 段全部清零,这个很简单,因为只需要知道运行域就OK,
;而这个参数是直接从编译器传递而来的。
mov r0, #0
ldr r3, EndOfBSS
1
cmp r2, r3
strcc r0, [r2], #4
bcc � ;如果拷贝完数据后还剩下多余的空间的话,就往里面填充0
;===============================================================================
;检验代码运行位置
;===============================================================================
ldr r0, =ResetEntry
ldr r1, =0x30000000
cmp r0, r1
beq �
;灯全亮
ldr r0,=GPBCON
ldr r1,=0x00555555
str r1,[r0]
ldr r0,=GPBDAT
ldr r1,=0
str r1,[r0]
loop
b loop
1
ldr pc, =� ;goto compiler address
2
bl Main
;===============================================================================
; nandflash相关函数
;===============================================================================
ReadNandID
mov r7,#NFCONF
ldr r0,[r7,#4] ;NFChipEn();
bic r0,r0,#2
str r0,[r7,#4]
mov r0,#0x90 ;WrNFCmd(RdIDCMD);
strb r0,[r7,#8]
mov r4,#0 ;WrNFAddr(0);
strb r4,[r7,#0xc]
1 ;while(NFIsBusy());
ldr r0,[r7,#0x20]
tst r0,#1
beq �
ldrb r0,[r7,#0x10] ;id = RdNFDat()<<8;
mov r0,r0,lsl #8
ldrb r1,[r7,#0x10] ;id |= RdNFDat();
orr r5,r1,r0
ldr r0,[r7,#4] ;NFChipDs();
orr r0,r0,#2
str r0,[r7,#4]
mov pc,lr
;bx lr
ReadNandStatus
mov r7,#NFCONF
ldr r0,[r7,#4] ;NFChipEn();
bic r0,r0,#2
str r0,[r7,#4]
mov r0,#0x70 ;WrNFCmd(QUERYCMD);
strb r0,[r7,#8]
ldrb r1,[r7,#0x10] ;r1 = RdNFDat();
ldr r0,[r7,#4] ;NFChipDs();
orr r0,r0,#2
str r0,[r7,#4]
mov pc,lr
;bx lr
WaitNandBusy
mov r0,#0x70 ;WrNFCmd(QUERYCMD);
mov r1,#NFCONF
strb r0,[r1,#8]
1 ;while(!(RdNFDat()&0x40));
ldrb r0,[r1,#0x10]
tst r0,#0x40
beq �
mov