二、存储管理器--SDRAM
2.1 硬件结构
2.1.1 硬件框图
CPU 通过存储管理器来读取 SDRAM 网卡 等外部设备,CPU不管外部设备是怎么样的,只是读存储管理器中的地址
CPU从0x30000000地址读取数据。
1 mov R1, #0x30000000 2 ldr R0, [R1]
存储管理器会根据配置信息来访问外部的设备。
配置信息包括:
-
- 外部设备的地址
- 外部设备的数据,数据宽度是多少位(8位/16位/32位)
- 时钟信号频率
- 外部设备相关的特性,SDRAM包括行地址,列地址和bank
SDRAM的存储结构逻辑如下:
SDRAM内部为一个存储阵列。读写原理为,先指定一个行(ROW),再指定一个列(COLUMN),就可以找到所需要的内容。
一个存储阵列称为一个BANK。
2.SDRAM
2.1 硬件结构
两个16位的芯片组成32位位宽。
具体参数可以查看SDRAM的芯片手册。
SDRAM 需要知道的信息:
- 列地址和行地址的数目:由芯片手册可以知道,行地址为13位,列地址为9位
- 刷新周期:刷新周期为 64/8192
- bank 地址
- 位宽:由原理图可以知道,位宽为32
由芯片手册可以知道,行地址为13位,列地址为9位
刷新周期为 64/8192
2440 芯片手册上面写了位宽与PIN脚之间的关系,如下图:
若是8位位宽,只接A0 A1.......,若是16位位宽,则为A1 A2........,若是32位位宽,则为A2 A3........;这些引脚接到SDRAM上都是从 SDRAM的A0开始,32位,即LADDR2接到A0上开始
2.2 编程
要想使用SDRAM,需要进行以下几步:
1)配置存储管理器
2)将SDRAM 的信息写入到寄存器中
对于存储管理器,涉及到13个寄存器,具体看芯片手册
- 程序设计思路:
- 程序烧写进NAND Flash 中后,NAND Flash 中的前4K会拷贝到CPU的SRAM中去,SRAM的起始地址为0x00000000,程序从这里开始执行,SDRAM中的4K内容称为steppingstone
- 然后在SRAM中执行的时候,会将SRAM中的程序拷贝进SDRAM中去,然后继续在SDRAM中执行
- 若是程序较大,则前4K执行完后,会将NAND Flash 的中的所有代码一次性拷贝进SDRAM中执行,此章不涉及
链接地址:运行时,程序应该位于哪里
代码 head.S
1 @****************************************************************************** 2 @ File:head.S 3 @ 功能:SDRAM 进行初始化 4 @****************************************************************************** 5 6 @ .equ 命令用于把常量值设置为可以在文本段中使用的符号 7 @ 经过设置之后,数据符号值是不能在 程序中改动的。 8 @ .equ 命令可以出现在数据段中任何位置,但是出于好的代码习惯,最好是在定义其他数据之前或之后集中定义所有数据符号 9 .equ MEM_CTL_BASE, 0x48000000 @ 设置内存控制器的基地址,寄存器的起始地址 10 .equ SDRAM_BASE, 0x30000000 @ 设置SDRAM的基地址,按接在2440的哪个片选上看 11 12 .text 13 .global _start 14 _start: 15 bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启 16 bl memsetup @ 设置存储控制器 17 bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中 18 ldr pc, =on_sdram @ 跳到SDRAM中继续执行 19 20 on_sdram: 21 ldr sp, =0x34000000 @ 设置堆栈,SDRAM 总共64M,栈指针指向SDRAM的最上端 22 bl main 23 24 halt_loop: 25 b halt_loop 26 27 @ 关闭看门狗 28 disable_watch_dog: 29 ldr r0, =0x53000000 @ WATCHDOG寄存器地址 30 mov r1, #0x0 31 str r1, [r0] @ 写入0,禁止WATCHDOG,否则CPU会不断重启 32 mov pc, lr @ 返回 33 34 @ 设置存储控制器,以便使用SDRAM等外设 35 memsetup: 36 mov r1, #MEM_CTL_BASE @ 存储控制器的13个寄存器的开始地址 37 adrl r2, mem_cfg_val @ 这13个值的起始存储地址 38 add r3, r1, #52 @ 13*4 = 54 39 1: 40 ldr r4, [r2], #4 @ 读取设置值,并让r2加4 41 str r4, [r1], #4 @ 将此值写入寄存器,并让r1加4 42 cmp r1, r3 @ 判断是否设置完所有13个寄存器 43 bne 1b @ 若没有写成,继续 44 mov pc, lr @ 返回 45 46 47 copy_steppingstone_to_sdram: 48 @ 将Steppingstone的4K数据全部复制到SDRAM中去 49 @ Steppingstone起始地址为0x00000000,SDRAM中起始地址为0x30000000 50 51 mov r1, #0 @ 片内内存的起始地址为 0 52 ldr r2, =SDRAM_BASE @ SDRAM 中的基地址 53 mov r3, #4*1024 @ 拷贝的大小为 4K 54 1: 55 ldr r4, [r1],#4 @ 从Steppingstone读取4字节的数据,并让源地址加4 56 str r4, [r2],#4 @ 将此4字节的数据复制到SDRAM中,并让目地地址加4 57 cmp r1, r3 @ 判断是否完成:源地址等于Steppingstone的末地址? 58 bne 1b @ 若没有复制完,继续 59 mov pc, lr @ 返回 60 61 62 .align 4 63 mem_cfg_val: 64 @ 存储控制器13个寄存器的设置值 65 .long 0x22011110 @ BWSCON 66 .long 0x00000700 @ BANKCON0 67 .long 0x00000700 @ BANKCON1 68 .long 0x00000700 @ BANKCON2 69 .long 0x00000700 @ BANKCON3 70 .long 0x00000700 @ BANKCON4 71 .long 0x00000700 @ BANKCON5 72 .long 0x00018005 @ BANKCON6 73 .long 0x00018005 @ BANKCON7 74 .long 0x008C07A3 @ REFRESH 75 .long 0x000000B1 @ BANKSIZE 76 .long 0x00000030 @ MRSRB6 77 .long 0x00000030 @ MRSRB7
sdram.c
1 /* GPF */ 2 #define GPFCON (*(volatile unsigned long *)0x56000050) 3 #define GPFDAT (*(volatile unsigned long *)0x56000054) 4 /* GPG */ 5 #define GPGCON (*(volatile unsigned long *)0x56000060) 6 #define GPGDAT (*(volatile unsigned long *)0x56000064) 7 8 /* GPF */ 9 #define GPF4_out (1<<(4*2)) 10 #define GPF5_out (1<<(5*2)) 11 #define GPF6_out (1<<(6*2)) 12 13 #define GPF4_msk (3<<(4*2)) 14 #define GPF5_msk (3<<(5*2)) 15 #define GPF6_msk (3<<(6*2)) 16 17 /* 18 * S2,S3,S4对应GPF0、GPF2、GPG3 19 */ 20 #define GPF0_in (0<<(0*2)) 21 #define GPF2_in (0<<(2*2)) 22 #define GPG3_in (0<<(3*2)) 23 24 #define GPF0_msk (3<<(0*2)) 25 #define GPF2_msk (3<<(2*2)) 26 #define GPG3_msk (3<<(3*2)) 27 28 int main() 29 { 30 unsigned long dwDat; 31 32 // LED1,LED2,LED4对应的3根引脚设为输出 33 GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk); 34 GPFCON |= GPF4_out | GPF5_out | GPF6_out; 35 36 // S2,S3对应的2根引脚设为输入 37 GPFCON &= ~(GPF0_msk | GPF2_msk); 38 GPFCON |= GPF0_in | GPF2_in; 39 40 // S4对应的引脚设为输入 41 GPGCON &= ~GPG3_msk; 42 GPGCON |= GPG3_in; 43 44 while(1) 45 { 46 //若Kn为0(表示按下),则令LEDn为0(表示点亮) 47 dwDat = GPFDAT; // 读取GPF管脚电平状态 48 49 if (dwDat & (1<<0)) // S2没有按下 50 GPFDAT |= (1<<4); // LED1熄灭 51 else 52 GPFDAT &= ~(1<<4); // LED1点亮 53 54 if (dwDat & (1<<2)) // S3没有按下 55 GPFDAT |= (1<<5); // LED2熄灭 56 else 57 GPFDAT &= ~(1<<5); // LED2点亮 58 59 dwDat = GPGDAT; // 读取GPG管脚电平状态 60 61 if (dwDat & (1<<3)) // S4没有按下 62 GPFDAT |= (1<<6); // LED3熄灭 63 else 64 GPFDAT &= ~(1<<6); // LED3点亮 65 } 66 return 0; 67 }
Makefile
1 led_on.bin : head.S sdram.c 2 arm-linux-gcc -g -c -o head.o head.S 3 arm-linux-gcc -g -c -o sdram.o sdram.c 4 arm-linux-ld -Ttext 0x30000000 -g head.o sdram.o -o sdram_elf 5 arm-linux-objcopy -O binary -S sdram_elf sdram.bin 6 arm-linux-objdump -D -m arm sdram_elf > sdram.dis 7 clean: 8 rm -f sdram.dis sdram.bin sdram_elf *.o