通过上面我们可以看到能修改的只有一个CL:CAS(列地址信号)延迟,我们这里设置为0x30。
》》》》》》》》 至此,关于本实验中所有的存储控制器相关的寄存器学习完毕,那么接下来。就是最后一步了。写代码《《《《《《《《
.equ:
.equ symbol, expression: 把某一个符号(symbol)定义成某一个值(expression),该指令并不分配空间,相当于C语言中的#define宏定义。
LR寄存器:
LR(link register)连接寄存器,在ARM体系结构中LR的特殊用途有两种:
1.用来保存子程序的返回地址。
2.当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以根据LR的值返回到异常发生前的位置继续执行。
当通过BL或者BLX指令调用子程序时,硬件自动将子程序返回地址保存在R14(LR)寄存器中,在子程序返回时,把LR的值赋值到程序计数器PC即可实现子程序返回(eg:MOV PC,LR)。
ADR指令:
这是一条小范围的地址读取伪指令,它将基于PC的相对偏移地址值读到目标寄存器中。
使用格式: ADR register,exper
在编译源程序时,编译器首先计算出当前PC到exper的偏移值#offset_to_exper,然后使用一条ADD或者SUB指令来替换这些伪指令,例如,ADD resister,PC,#offset_to_exper
注意标号exper与指令必须在统一代码段。
ADRL指令:
这是一条中等范围的地址读取伪指令,它将基于PC的相对偏移地址值读到目标寄存器中。
原理和ADR一样,不同的是它在编译的时候,会被用两条合适的指令来替换伪指令。
eg:ADD register,PC,offset1
ADD register,register,offset2
所以上面adrl r2, mem_cfg_val 意思是把下面那13个值的起始存储地址存入到r2寄存器。
.long:
定义一个4字节数据,并为它分配空间
.align n:
它的作用是对指令或者数据存放的地址按 2^n 进行对齐。下面是指定按 2^4 对齐。
汇编语言head.S:(作用是将 RAM 程序复制到 SDRAM中,并在SDRAM 中执行)
- .equ MEM_CTL_BASE, 0x48000000 @ MEM_CTL_BASE 为芯片内存中的13个存储控制器中寄存器的起始地址
- .equ SDRAM_BASE, 0x30000000 @ SDRAM_BASE 为SDRAM在芯片中的内存地址
-
- .text
- .global _start
- _start:
- bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
- bl memsetup @ 设置存储控制器(配置其相应的寄存器)
- bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中
- ldr pc,=on_sdram @ 跳转到SDRAM中继续执行
- on_sdram:
- ldr sp,=0x34000000 @ 设置堆栈
- bl main
- halt_loop:
- b halt_loop
-
- disable_watch_dog:
- mov r1, #0x53000000 @ WATCHDOG在芯片中的内存地址
- mov r2, #0x0
- str r2, [r1] @ 把WATCHDOG寄存器写0
- mov pc, lr @ 把连接寄存器 lr 的值赋值给程序计数器 PC。用来子程序返回继续执行主程序
-
- copy_steppingstone_to_sdram:
- @ 我们这里将steppingstonede 4K数据全部复制到SDRAM中去
- @ steppingstone的 起始地址为0x00000000, SDRAM中的起始地址为0x30000000
-
- mov r1,#0
- ldr r2,=SDRAM_BASE
- mov r3,#4*1024 @ 因为我们要复制4K的大小
- 1:
- ldr r4,[r1],#4 @ 从steppingstone 读取4字节的数据,然后让源地址加4
- str r4,[r2],#4 @ 将r4 里4字节的数据复制SDRAM中,然后目的地址加4
-
- cmp r1, r3 @ 参考我下面的解释
- bne 1b
- mov pc, lr
-
- memsetup:
- mov r1, #MEM_CTL_BASE
- adrl r2, mem_cfg_val @ 注意这里 adr 后面是L 而不是数字1,反正我开始就是傻傻分不清。
- @ 这个语句就是把 mem_cfg_val 段定义的几个数据的其实地址传递给 r2
-
- add r3, r1,#52 @ 把 r1+13*4 传递给r3,cpu是32bit的,即一个寄存器包含4个字节,所以13个就包含52个字节
- @ 那么,如果把13个寄存器全部赋值结束后地址就应该是 r1+13*4。
- 1:
- ldr r4, [r2],#4 @ 读取设置值,并让r2+4。
- str r4, [r1],#4 @ 将此值写入寄存器,并让r1+4,以便下一个数据写入下一个寄存器
-
- cmp r1, r3 @ 关于 cmp 指令其实也就是计算r1-r3,但是它的结果并不改变其寄存器的值,只是改变程序状态寄存器 CPSR的标志位
- @ 然后下一条语句在指令后加上条件判断就能完成我们想要的循环。
- bne 1b @ ne(如果不相等) 1b 这里的b(backwark),向后跳转到局部标签1处执行。
- @ 相应的还有1f(foward),向前跳转到局部标签1处执行,注意理解这里的前和后,前代表地址增的方向,后代表地址减的方向
- mov pc, lr
-
- .align 4
- mem_cfg_val:
- .long 0x22011110 @ BWSCON 寄存器要写入的值
- .long 0x00000700 @ BANKCON0 寄存器要写入的值
- .long 0x00000700 @ BANKCON1 寄存器要写入的值
- .long 0x00000700 @ BANKCON2 寄存器要写入的值
- .long 0x00000700 @ BANKCON3 寄存器要写入的值
- .long 0x00000700 @ BANKCON4 寄存器要写入的值
- .long 0x00000700 @ BANKCON5 寄存器要写入的值
- .long 0x00018005 @ BANKCON6 寄存器要写入的值
- .long 0x00018005 @ BANKCON7 寄存器要写入的值
- .long 0x008C07A3 @ REFRSH 寄存器要写入的值
- .long 0x000000B1 @ BANKSIZE 寄存器要写入的值
- .long 0x00000030 @ MRSRB6 寄存器要写入的值
- .long 0x00000030 @ MRSRB7 寄存器要写入的值
复制代码
c语言main函数:
- #define GPFCON (*(volatile unsigned long *)0x56000050)
- #define GPFDAT (*(volatile unsigned long *)0x56000054)
-
- #define GPF4_out (1<<(4*2))
- #define GPF5_out (1<<(5*2))
- #define GPF6_out (1<<(6*2))
-
- void wait(volatile unsigned long dly) // 简单的延时函数
- {
- for(;dly>0;dly--);
- }
-
- int main(void)
- {
- unsigned long i=0;
-
- GPFCON = CPF4_out | GPF5_out | GPF6_out; // 将LED对应的引脚设置为输出
-
- while(1)
- {
- wait(30000);
- GPFDAT = (~(1<<4)); // 根据i的值,点亮LED
- if(++1 == 8)
- i=0;
- }
- return 0;
- }
复制代码
Makefile:
- sdram.bin : head.S leds.C
- arm-linux-gcc -c -o head.o head.S
- arm-linux-gcc -c -o leds.o leds.c
- arm-linux-ld -Ttext 0x30000000 head.o leds.o -o sdram_elf
- arm-linux-objcopy -O binary -S sdram_elf sdram.bin
- arm-linux-objdump -D -m arm sdram_elf > sdram.dis
- clean:
- rm -f sdram.dis sdram_elf sdram.bin *.o
复制代码
注: objdump 命令是Linux下的反汇编目标文件或者可执行文件的命令
-D 表示反汇编 (要反汇编的文件) 中的所有section
-m 后面跟的cpu架构,arm就表示arm架构的cpu
> 表示将这个程序的反汇编文件西写入到led1.dis这个文件中,在终端中不显示出来
如果不加这个> ,那么你在终端上就可以看到输出的LED1_elf反汇编程序 .