一,实验原理:
首先使用汇编语言设置好存储控制器,使得外接的SDRAM可用,然后把程序本身从片内RAM(Steppingstone)拷贝到SDRAM处,最后跳到SDRAM中执行。
二,实验目的:
1.掌握SDRAM的初始化方法
2.掌握位置无关指令,相对跳转,绝对跳转
3.掌握代码重定位
三,实验内容:
1.准备知识:
1.1 访问SDRAM的方法:
SDRAM的内部是一个存储阵列,阵列就好比一个excel表格,将数据填进表格的单元格中,excel中的每个单元格会有“行”和“列”,通过这个行和列我们可以定位excel表格中每个单元格的数据,另外一般我们可能不止用一个excel表格来保存和处理数据,那么现在我要找到某个excel中某个单元格中的数据,该如何做呢?我们可以这样做:先根据excel的编号找到数据存在在哪个excel表格中,然后在这个excel表格中根据行和列定位单元格,最终从单元格中找到我们需要寻找的数据。如果我们把前述从excel表格中寻找数据的过程比作访问SDRAM的存储单元中的数据的话,那么要访问SDRAM中的数据,同样的道理我们得先知道SDRAM的BANK(一个BANK就类似于一个excel表格,一般有4个BANK),然后根据每个BANK的行地址和列地址(行地址和列地址类似于excel的行和列)即可以最终定位存储单元找到数据,这也就是SDRAM寻址的基本原理。
1.2 S3C2440开发板的启动流程:
S3C2440开发板有两种启动方式:一是NOR Flash启动, 此种启动方式,开发板启动时NOR Flash被映射到0x00000000地址,零地址指向NOR Flash,然后Cpu从零地址执行。二是Nand Flash启动,此种启动方式,开发板启动时Cpu会自动地将Nand的前4K内容拷贝到片内RAM中,然后从零地址执行。
1.3 设置存储控制器的汇编代码分析head.S:
@************************************************************************* @ File:head.S @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行 @************************************************************************* .equ MEM_CTL_BASE, 0x48000000 @ 存储控制器的13个寄存器的开始地址 .equ SDRAM_BASE, 0x30000000 @ SDRAM中起始地址 .text @ 代码段 .global _start @ 声明 _start为全局变量 _start: bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启,bl指令实现子程序调用 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: @ 往WATCHDOG寄存器写0即可 mov r1, #0x53000000 @ 0x53000000为看门狗寄存器的物理地址 mov r2, #0x0 str r2, [r1] mov pc, lr @ 返回,注意:执行bl指令时会把当前的pc值(返回地址)保存在lr(R14)中 copy_steppingstone_to_sdram: @ 将Steppingstone的4K数据全部复制到SDRAM中去 @ Steppingstone起始地址为0x00000000,SDRAM中起始地址为0x30000000 mov r1, #0 ldr r2, =SDRAM_BASE mov r3, #4*1024 1: ldr r4, [r1],#4 @ 从Steppingstone读取4字节的数据,并让源地址加4 str r4, [r2],#4 @ 将此4字节的数据复制到SDRAM中,并让目地地址加4 cmp r1, r3 @ 判断是否完成:源地址等于Steppingstone的未地址? bne 1b @ 若没有复制完,继续 mov pc, lr @ 返回 memsetup: @ 设置存储控制器以便使用SDRAM等外设 mov r1, #MEM_CTL_BASE @ 存储控制器的13个寄存器的开始地址 adrl r2, mem_cfg_val @ 装载这13个值的起始存储地址 add r3, r1, #52 @ 13*4 = 52 1: ldr r4, [r2], #4 @ 读取设置值,并让r2加4 str r4, [r1], #4 @ 将此值写入寄存器,并让r1加4 cmp r1, r3 @ 判断是否设置完所有13个寄存器 bne 1b @ 若没有写成,继续 mov pc, lr @ 返回 .align 4 @ 让数据按照4字节对齐 mem_cfg_val: @ 存储控制器13个寄存器的设置值 .long 0x22011110 @ BWSCON,BWSCON每隔4位控制一个BANK,总共8个BANK .long 0x00000700 @ BANKCON0,注意:BANKCON0至BANKCON5都时设置为2440芯片手册上的推荐值 .long 0x00000700 @ BANKCON1 .long 0x00000700 @ BANKCON2 .long 0x00000700 @ BANKCON3 .long 0x00000700 @ BANKCON4 .long 0x00000700 @ BANKCON5 .long 0x00018005 @ BANKCON6,注意:在8个BANK中只有BANK6和BANK7可以外接SRAM或者SDRAM @ 设置BANKCON6和BANKCON7时,只需注意第0到第3位以及第15位到16位的设置,其它位一律设置为0 .long 0x00018005 @ BANKCON7,BANKCON6和BANKCON7的设置相同 .long 0x008C07A3 @ REFRESH,主要是SDRAM的刷新周期设置 .long 0x000000B1 @ BANKSIZE .long 0x00000030 @ MRSRB6 .long 0x00000030 @ MRSRB7
代码分析:上述整个汇编程序的流程是这样的:关闭看门狗(这个往看门狗寄存器里面写0就可以了)-->设置存储控制器是的外设SDRAM可用(这部分主要是要设置存储控制器的13个寄存器) --> 复制代码到SDRAM中(将片内RAM的4K数据复制到SDRAM) -->跳到SDRAM中继续执行(设置堆栈,调用C语言的main函数)
1.4 测试程序leds.c的源代码:
#define GPBCON (*(volatile unsigned long *)0x56000010) #define GPBDAT (*(volatile unsigned long *)0x56000014) #define GPB5_out (1<<(5*2)) #define GPB6_out (1<<(6*2)) #define GPB7_out (1<<(7*2)) #define GPB8_out (1<<(8*2)) void wait(volatile unsigned long dly) { for(; dly > 0; dly--); } int main(void) { unsigned long i = 0; GPBCON = GPB5_out|GPB6_out|GPB7_out|GPB8_out; // 将LED1,2,3,4对应的GPF5/6/7/8四个引脚设为输出 while(1){ wait(100000); GPBDAT = (~(i<<5)); // 根据i的值,点亮LED1,2,3,4 if(++i == 16) i = 0; } return 0; }
源码比较简单,不做分析了。
1.5 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.bin sdram_elf *.o
这里需要说明的是链接地址的问题:何为链接地址?答:程序运行时的地址。这里我们的链接地址是0x30000000而不是0x00000000为什么? 这是因为我们的程序最终是运行在SDRAM中而不是片内RAM中,所以链接地址是SDRAM的起始地址0x30000000。
1.6 程序测试:
把编译好的leds.bin烧写进开发板运行,我们发现运行在SDRAM中的led灯的闪烁程度没有运行在片内RAM中led灯的那么快,究其原因是因为外部SDRAM的性能比内部的片内RAM的差一些。