.equ sdram_base, 0x30000000
.equ mem_ctl_base, 0x48000000
.text
.global _start
_start:
blclose_watch_dog
blinit_mem
blcopy_steppingstone2sdram
ldrpc,=sdram
sdram:
blinit_sp
blmain
loop:
bloop
close_watch_dog:
ldrr0,=0x53000000
ldrr1,=0;
strr1,[r0]
movpc,lr
init_sp:
ldrsp,=0x34000000
movpc,lr
init_mem:
adrlr0,init_mem_data @不要写成ldr,否则就会进入0x3000000**,现在是初始化内存,还在片内SRAM中(0x000000**)
addr1,r0,#52
ldrr2,=mem_ctl_base
init_mem_loop:
ldrr3,[r0],#4
strr3,[r2],#4
cmpr0,r1
bneinit_mem_loop
movpc,lr
copy_steppingstone2sdram:
ldrr0,=0 @片内4K内存,也是SDRAM的前4K字节
ldrr1,=sdram_base @sdram的起始地址
ldrr3,=4096 @复制终止的标志
copy_loop:
ldrr2,[r0],#4
strr2,[r1],#4
cmpr0,r3
bnecopy_loop
movpc,lr
.align 4
init_mem_data:
.long0x22011110
.long0x00000700
.long0x00000700
.long0x00000700
.long0x00000700
.long0x00000700
.long0x00000700
.long0x00018005
.long0x00018005
.long0x008c07a3
.long0x000000b1
.long0x00000030
.long0x00000030
上面是2440的启动代码。看了韦东山老师的书发现。Ldr pc,=sdram紧接着下一跳语句就是sdram标号。开始的时候在想直接就把这里两句多余的删掉不就行了吗?仔细分析程序的过程才知道为什么要留下这个。
实验的环境是在NANDFLASH下启动。
(1) 将NAND FLASH的前4K内存放入到2440的片内SRAM(steppingstone)中。
(2) 在2440片内的SRAM中执行。关闭看门狗,设置堆栈指针,设置内存。
(3) 然后将片内SRAM(steppingstone)也就是的内容拷贝到外接的内存SDRAM中。
(4) 这是,我们就想进入内存空间更大的外接内存SDRAM中去执行程序。
Arm-linux-ld –Ttext 0x30000000 **********
上面是Makefile里面的代码。因此实际上的地址空间都被映射到以0x30000000为基址的空间中了。但是前面进行的关闭看门狗初始化内存等必须在片内SRAM中和执行。因为,外部的SDRAM必须初始化才能工作。
事实上也正是这样的。初始化看门狗,内存等也正是在片内SRAM中执行的。因为ldr的寻址是32位数的,可以查询到任何位置。而bl的寻址是±32M。所以,刚上电在片内SRAM中执行。然后bl close_watch_dog。虽然close_watch_dog是0x300000**,但是bl指令的寻址范围只能保证它在跳转到0x000000**。而ldr的寻址范围就可以保证pc直接指向0x3000000**。这样就实现了跳转到片外SDRAM。这样也明白了为什么ARM汇编指令的分为ADR,ADRL,LDR等不同的范围的寻址指令。
对于init_mem汇编子程序,由于最开始的写法是ldr r0,init_mem_data。所以,就会将r0设置为0x3000000**。这时执行copy_steppingstone2sdram,也即是在0x300000**被拷入相应的数值,就会造成初始化mem失败。而copy_steppingstone2sdram必须在初始化mem之后。因此,我们就需要用adrl这条指令在相对小的范围寻址。进入片内SRAM的init_mem_data(0x000000**)而不是片外的SDRAM(0x300000**),这样就能得到正确的设置值了。