什么是存储控制器?
S3C2440中文概述如下:
存储器控制器
概述
S3C2440A 存储器控制器为访问外部存储的需要器提供了存储器控制信号。
S3C2440A 包含以下特性:
–大/小端(通过软件选择)
–地址空间:每个Bank 有128M字节(总共1G/8个Bank)
–大/小端(通过软件选择)
–除了BANK0(16/32位)之外,其它全部BANK都可编程访问宽度(8/16/32 位)
–总共8 个存储器Bank
6 个存储器Bank为ROM,SRAM等
其余2 个存储器Bank为ROM,SRAM,SDRAM等
–7 个固定的存储器Bank起始地址
–1 个可变的存储器Bank起始地址并Bank大小可编程
–所有存储器Bank 的访问周期可编程
–外部等待扩展总线周期
–支持SDRAM自刷新和掉电模式
S3c2440向外引出27根地址线,可实现128M的寻找空间;那他怎么实现1G的空间范围访问呢?
这就涉及到了nGCSx片选信号,由控制这几根信号线实现bank的切换,如下图所示:
左边为norflash下;右边为nand flash;
说到此又想起一概念,就是有关于nor flash与nand flash启动方式;
一、norflash启动
简而言之,既是norflash启动模式下cpu启动就会从地址0x00000000开始执行
nor flash下是就是nor flash的首地址;其中原因为nor flash支持一种名为XIP的执行机制;
二、nandflash启动
nand flash下不支持XIP因此在执行nandflash的程序是,系统将会把nandflash的头4K(最大)启动代码拷贝至系统SRAM中既s3c2440中的steppingstone;
S3c2440向外引出27根地址线,可实现128M的寻找空间;那他怎么实现1G的空间范围访问呢?
答案:
S3C2440有27根地址线ADDR[26:0],
8根片选信号ngcs0-ngcs7,对应bank0-bank7,当访问bankx的地址空间,ngcsx引脚为低电平,选中外设。
2^27=2^7 * 2^10 * 2^10 =128Mbyte
8*128Mbyte = 1Gbyte
所以S3C2440总的寻址空间是1Gbyte
选择的SDARM是HY57V561620F,4Mbit * 4bank *16,共32Mbyte。
在嵌入式中,存储控制器只是提供一种总线访问的形式,所接的外设不一定是内存。
本节讲解使用S3C2440的存储控制器,操控外部SDRAM
首先了解下SDRAM的寻址原理。
SDRAM内部是一个存储阵列。可以把它想象成一个表格。和表格的检索原理一样,先指定行,再指定列,就可以准确找到所需要的存储单元。
这个表格称为逻辑BANK。目前的SDRAM基本都是4个BANK。寻址的流程就是先指定BANK地址,再指定行地址,最后指定列地址。这就是SDRAM的寻址原理。
查看HY57V561620F的资料,这个SDRAM有
13根行地址线 RA0-RA12
9根列地址线 CA0-CA8 /与行地址复用,选低9位
2根L-BANK选择线 BA0-BA1/S3C2440要求用ADDRESS24,ADDRESS25控制SDRAM的L-BANK
SDRAM的地址引脚是分时复用的,在读写SDRAM存储单元时,操作过程是将地址分两次输入到芯片中,
两次送到芯片上去的地址分别称为行地址和列地址。它们被锁存到芯片内部的行地址锁存器和列地址锁存器。
/RAS是行地址锁存信号,该信号将行地址锁存在芯片内部的行地址锁存器中;
/CAS是列地址锁存信号,该信号将列地址锁存在芯片内部的列地址锁存器中
DRAM的逻辑BANK概念是针对内存颗粒内部的。大家都知道 DRAM内部的存储单元是以阵列形式排列的。
如下图所示。行列地址总线分别经过行列地址译码器译码后分别指向一行和一列,行列重叠的单元就是我们所寻找的存储单元,这就是内存芯片寻址的基本原理。对于内存颗粒来说,这个阵列就是逻辑 Bank(Logical Bank,简称L-Bank)。
但是,在实际应用中,由于技术、成本等原因,不可能只做一个全容量的 L-BANK,而且最重要的是,由于 DRAM 的工作原理限制,单一的 L-Bank将会造成严重的寻址冲突,大幅降低内存效率。
所以人们在DRAM 内部分割成多个 L-Bank,每个 L-Bank 形状相同,彼此独立,可以独立工作。早期的DRAM 芯片内部分为2个L-Bank,后来是4个,DDR3 内存芯片为8个。
在进行寻址时需要先确定是哪个 L-Bank,然后再在这个选定的 L-Bank中选择相应的行与列进行寻址。对内存的访问,一次只能是一个L-Bank,而每次与 CPU交换的数据就是 L-Bank 存储阵列中一个“存储单元”的容量。SDRAM内存芯片一次传输的数据量就是芯片的位宽,那么这个存储单元的容量就是芯片的位宽(也是 L-Bank 的位宽)。上图为4BANK内存颗粒内部结构示意图。
内存芯片容量的计算方法为:存储单元数量=行数×列数(得到一个 L-Bank 的存储单元数量)×L-Bank 的数量。在很多内存产品介绍文档中,都会用 M×W 的方式来表示芯片的容量。M 是该芯片中存储单元的总数,单位是兆,W 代表每个存储单元的容量,也就是 SDRAM 芯片的位宽(Width),单位是 bit。计算出来的芯片容量也是以 bit 为单位,但用户可以采用除以 8 的方法换算为字节(Byte)。
SDRAM 的行地址有13根 (RAS# ="L", CAS# = "H", WE# = "H", BAs = Bank, A0-A12 =Row Address)
SDRAM 的列地址有 9根 (RAS# = "H",CAS# = "L", WE# = "L", BAs = Bank, A10 = "L",A0-A8 = Column Address)
S3C2440存储控制器的地址线A24,A25接到SDRAM的BA0,BA1,用来控制4个 L-bank的选择
举个例子:SDRAM芯片就像小学生练字本,比如这个练字本有4页(可以理解为SDRAM有4个L-bank),
每页有2^13行,每页有2^9列,每一行与列对应的单元格都是一个存储单元(EM63A165 是16bit 此处的存储单元是16bit)
容量计算:(2^13 * 2^9 *16 *4)/8=32M
解释:13位行地址译码器,对应有2^13行单元
09位列地址译码器,对应有2^9行单元 2^13* 2^9 = 4194304个存储单元/L-bank
每个存储单元是16bit,单片16bit SDRAM一次读取就是16bit,每片SDRAM有4个L-bank ,4194304*4=16777216个存储单元/每个sdram
每8个bit是一个字节
16777216*16=268435456个bit
有了L-bank,行地址,列地址后,就可以访问sdram的任意一个单元了,
就像小学生,先翻开第几张纸,选哪一行,再选那一列,然后写下一个汉字(刚好16bit GBK编码)
SDRAM芯片内部分为4个bank,BA0,BA1用来选择第几个bank(相当于小学生准备读/写的这个汉字,你要存到那一页)
实验:在SDRAM上,跑一个程序,LED闪烁
思路:
硬件平台准备JZ2440
配置相关寄存器【行地址,列地址,l-bank,刷新率等等】
程序从nand copy 4K 到sram
程序在sram中执行,把第二段程序 copy到sdram
跳到sdram中执行!
实验完成
1,硬件平台准备:JZ2440
chunli@ubuntu:~/my03$ ll
total 12
-rw-rw-r-- 1 chunli chunli 2703 Apr 13 03:49 head.S
-rw-rw-r-- 1 chunli chunli 532 Apr 13 03:50 leds.c
-rw-rw-r-- 1 chunli chunli 312 Apr 13 03:50 Makefile
chunli@ubuntu:~/my03$ vim head.S @************************************************************************* @ File:head.S @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行 @************************************************************************* .equ MEM_CTL_BASE, 0x48000000 .equ SDRAM_BASE, 0x30000000 .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: @ 往WATCHDOG寄存器写0即可 mov r1, #0x53000000 mov r2, #0x0 str r2, [r1] mov pc, lr @ 返回 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 = 54 1: ldr r4, [r2], #4 @ 读取设置值,并让r2加4 str r4, [r1], #4 @ 将此值写入寄存器,并让r1加4 cmp r1, r3 @ 判断是否设置完所有13个寄存器 bne 1b @ 若没有写成,继续 mov pc, lr @ 返回 .align 4 mem_cfg_val: @ 存储控制器13个寄存器的设置值 .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 @ REFRESH .long 0x000000B1 @ BANKSIZE .long 0x00000030 @ MRSRB6 .long 0x00000030 @ MRSRB7
chunli@ubuntu:~/my03$ vim leds.c #defineGPFCON (*(volatile unsigned long *)0x56000050) #defineGPFDAT (*(volatile unsigned long *)0x56000054) #defineGPF4_out (1<<(4*2)) #defineGPF5_out (1<<(5*2)) #defineGPF6_out (1<<(6*2)) void wait(volatile 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; }
chunli@ubuntu:~/my03$ vim 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 chunli@ubuntu:~/my0