代码的顺序分别为head.S, led.c, Makefile
我把0x56000000映射到0xb0000000,把sdram的地址映射到0xa0000000,这里的做法也和韦老师的相反,主要内容是head.S
我把0x56000000映射到0xb0000000,把sdram的地址映射到0xa0000000,这里的做法也和韦老师的相反,主要内容是head.S
.text
.global _start
_start:
bl Disable_Watch_Dog
bl MemSetup
bl MMUSetup
bl Copy_SteppingStone_to_Sdram
ldr pc,=On_Sdram
Disable_Watch_Dog: ;关看门狗
mov r1,#0x53000000
mov r2,#0
str r2,[r1]
mov pc,lr
MemSetup: ;初始化SDRAM
mov r1,#0x48000000
adrl r2,SetMem_avl
ldr r3,=0x48000034
Loop1:
ldr r4,[r2],#4
str r4,[r1],#4
cmp r1,r3
bne Loop1
mov pc,lr
Copy_SteppingStone_to_Sdram: ;将程序复制到虚拟地址0xa0004000处运行
mov r1,#0
ldr r2,=0xa0004000
mov r3,#4096
Loop2:
ldr r4,[r1],#4
str r4,[r2],#4
cmp r3,r1
bne Loop2
mov pc,lr
MMUSetup: ;初始化MMU
;建立4G内存空间页表
mov r4,#0x30000000 ;页表基址
ldr r3,=0x00000c12 ;domain 0,uncached,unbuffered
mcr p15, 0, r4, c2, c0, 0 ;将页表基址存入C2中
ldr r5,=0x1000 ;循环4K次(4K*1MB=4GB)
l1:
str r3,[r4],#4 ;建立页表
add r3,r3,#(1<<20) ;更新描述符
subs r5,r5,#1
bne l1
;映射0x56000000到0xb0000000
mov r4,#0x30000000 ;页表基址
ldr r3,=0x00000c12 ;domain 0,uncached,unbuffered
ldr r1,=0xfff00000
mov r2,#0xb0000000 ;VA
mov r0,#0x56000000 ;PA
and r2,r2,r1
orr r3,r3,r0 ;段描述符 4Byte
add r4,r4,r2,lsr #(18) ;描述符地址=基址+虚拟段地址>>(20-2)位 ([1:0]位为00,4字节对齐)
str r3,[r4]
;映射0x30000000到0xa0000000
mov r4,#0x30000000
ldr r3,=0x00000c1e ;domain 0,cached,buffered
ldr r1,=0xfff00000
mov r2,#0xa0000000 ;VA
mov r0,#0x30000000 ;PA
and r2,r2,r1
orr r3,r3,r0 ;段描述符 4Byte
add r4,r4,r2,lsr #(18) ;描述符地址=基址+虚拟段地址>>(20-2)位 ([1:0]位为00,4字节对齐)
mov r5,#64 ;SDRAM总共有64MB 循环64次
l2:
str r3,[r4],#4
add r3,r3,#(1<<20)
subs r5,r5,#1
bne l2
mov r0,#0
mcr p15,0,r0,c7,c7,0 ;使无效ICache和DCache
mcr p15,0,r0,c7,c10,4 ;清空缓冲区
mcr p15,0,r0,c7,c5,4 ;清空预取缓冲区
mcr p15,0,r0,c7,c5,6 ;清空整个跳转目标cache
mcr p15,0,r0,c8,c7,0 ;使无效TLB
mvn r0,#0
mcr p15,0,r0,c3,c0,0 ;域访问控制寄存器设为0xFFFFFFFF,不进行权限检查
mrc p15,0,r0,c1,c0,0 ;读取C1内容到R0
bic r0,r0,#0x3000
bic r0,r0,#0x380
bic r0,r0,#0x7
orr r0,r0,#0x1000
orr r0,r0,#0x7 ;开启对齐检查,使能ICache,使能DCache,打开MMU
mcr p15,0,r0,c1,c0,0 ;将R0中内容写回C1中
mov pc,lr
On_Sdram:
mov sp,#0xa4000000 ;设置栈顶在SDRAM最上方
bl main
Halt_loop:
b Halt_loop
.align 4
SetMem_avl:
@ 存储控制器13个寄存器的设置值
.long 0x02011110 @ BWSCON
.long 0x00000700 @ BANKCON0
.long 0x00000700 @ BANKCON1
.long 0x00000700 @ BANKCON2
.long 0x00000700 @ BANKCON3
.long 0x00000700 @ BANKCON4
.long 0x00000700 @ BANKCON5
.long 0x00018009 @ BANKCON6
.long 0x00018009 @ BANKCON7
.long 0x00aC07A3 @ REFRESH
.long 0x000000B1 @ BANKSIZE
.long 0x00000030 @ MRSRB6
.long 0x00000030 @ MRSRB7/*
* leds.c: 循环点亮4个LED
* 属于第二部分程序,此时MMU已开启,使用虚拟地址
*/
#define GPFCON (*(volatile unsigned long *)0xb0000050) // 物理地址0x56000050
#define GPFDAT (*(volatile unsigned long *)0xb0000054) // 物理地址0x56000054
#define GPF4_out (1<<(4*2))
#define GPF5_out (1<<(5*2))
#define GPF6_out (1<<(6*2))
/*
* wait函数加上“static inline”是有原因的,
* 这样可以使得编译leds.c时,wait嵌入main中,编译结果中只有main一个函数。
* 于是在连接时,main函数的地址就是由连接文件指定的运行时装载地址。
* 而连接文件mmu.lds中,指定了leds.o的运行时装载地址为0xB4004000,
* 这样,head.S中的“ldr pc, =0xB4004000”就是跳去执行main函数。
*/
static inline void wait(unsigned long dly)
{
for(; dly > 0; dly--);
}
int main(void)
{
unsigned long i = 0;
GPFCON = GPF4_out|GPF5_out|GPF6_out; // 将LED1,2,4对应的GPF4/5/6三个引脚设为输出
while(1){
wait(30000);
GPFDAT = (~(i<<4)); // 根据i的值,点亮LED1,2,4
if(++i == 8)
i = 0;
}
return 0;
}mmu.bin : head.S led.c
arm-linux-gcc -c -o head.o head.S
arm-linux-gcc -c -o led.o led.c
arm-linux-ld -Ttext 0xa0004000 head.o led.o -o mmu_elf
arm-linux-objcopy -O binary -S mmu_elf mmu.bin
arm-linux-objdump -D -m arm mmu_elf > mmu.dis
clean:
rm -f mmu.dis mmu.bin mmu_elf *.o