什么是存储控制器
2440是32位单片机,进行数据访问时通过32位地址访问。
CPU发出32位地址信号给存储控制器,存储控制器根据地址信号设置片选信号及地址总线,将相应数据通过数据总线传回存储控制器,存储控制器将收到的数据以字节为单位发送给CPU。
CPU通过存储控制器读写数据总体概述:
- 存储控制器与相应内存芯片根据芯片手册正确接线。
- 为存储控制器设置好寄存器(结合原理图、内存芯片手册、开发板芯片手册设置相应数据位宽、片选信号、时钟等信息)。
- CPU向芯片手册发送32位地址信号。
- 存储控制器根据收到的地址信号结合步骤2设置好的寄存器信息生成相应片选信号、地址总线等信息。
- 存储控制器与相应内存间的数据传输由位宽决定(8/16/32)。CPU数据以字节为单位(8),与存储控制器的数据交换要遵循一定规律(内存控制器根据寄存器位宽信息自动完成转化过程)。
- 例如内存芯片为数据位宽是16位,则CPU发送的字节会存到存储控制器的高八位或者低八位,另外八位存储另一个八位数据。即当存储控制器数据位宽为16时,地址总线最后一位无效(两个数据共用一个存储控制器地址)。当存储控制器数据位宽为32时,地址总线最后四位无效(四个数据共用一个存储控制器地址)。
2440存储控制器简介(memory controller)
- 支持小端/大端字节序(通过软件选择)
- 地址空间:每个BANK有128M(总共1G,8个BANK)
- 可编程的访问位宽:BANK0为16/32位,其他BANK为8位/16位/32位
- 总共8个存储器BANK,其中6个用于ROM,SRAM,等等。其余的2个用于ROM,SRAM,SDRAM等等
- 7个BANK的起始地址是固定的(BANK0~BANK6)
- 1个BANK的起始地址和大小可编程(BANK7)
- 所有BANK的访问周期可编程
- 外部的wait信号可延长总线周期
- 外接SDRAM支持自刷新和掉电模式
注意:BANK6和BANK7的地址空间大小必须相等(BANK6和BANK7的地址空间大小是可编程的)
以上内容可通过三星数据手册查询。
S3C2440对外引出ADDR0~ADDR26共27根地址线,可访问128*2^20 = 128M 地址空间,配合8个片选信号nGCS0~nGCS8,可达到1G地址空间。
上图为S3C2440地址映射图,分NAND flash和非NAND flash 两种情况。当CPU发出地址信号时,存储控制器会根据地址信号设置片选信号,再根据地址线访问到具体外设。
具体地址信号为片选的初始信号加地址线信号。
2440存储控制器相关寄存器介绍:
1)位宽和等待控制寄存器BWSCON
BWSCON中每四位控制一个BANK
STx:启动/禁止SDRAM的数据掩码引脚
WSx:是否使用存储器的WAIT信号
DWx:设置对应BANK的位宽,0b00对应8位,0b01对应16位,0b10对应32位,0b11表示保留
比较特殊的是BANK0,由硬件跳线决定DW0,0b01表示16位,0b10表示32位,BANK0只支持16、32两种位宽 本板为0x22011110
2)BANK控制寄存器BANKCONx(x为0-5)
这些寄存器用来控制BANK0-BANK5外接设备的访问时序,使用默认0x0700即可
3)BANK控制寄存器BANKCONx(x为6-7)
MT[16:15]:设置BANK外接ROM/SRAM还是SDRAM,00=ROM/SRAM,01=保留,10=保留,11=SDRAM
MT=0b00时,与BANKCON0-BANKCON5类似
MT=0b11时
Trcd[3:2]:RAS to CAS delay,设为推荐值0b01
SCAN[1:0]:SDRAM的列地址数,本开发板使用的SDRAM列地址数为9,0b00=8位,0b01=9位,0b10=10位
本开发板BANKCON6/7均设为0x00018005
4)刷新控制寄存器REFRESH
REFEN[23]: 0=禁止SDRAM的刷新功能,1=开启SDRAM的刷新功能
TREFMD[22]: SDRAM的刷新模式,0=CBR/Auto Refresh,1=SelfRefresh
Trp[21:20]: SDRAM RAS预充电时间 00=2 clocks,01=3clocks,10=4clocks,11=不支持
Tsrc[19:18]: SDRAM半行周期时间 00=4clocks,01=5clocks,10=6clocks,11=7clocks,SDRAM行周期时间Trc=Tsrc+Trp
Refresh Counter[10:0]: SDRAM刷新计数,刷新时间=(2^11+1-refresh_count)/HCLK,在未使用PLL时,HCLK=晶振频率12MHz,刷新周期为7.8125us
refresh_count=2^11+1-12*7.8125=1955
REFRESH=0x008C0000+1955=0x008C07A3
5)BANKSIZE寄存器
BURST_EN[7]: 0=ARM核禁止突发传输,1=ARM核支持突发传输
SCKE_EN[5]: 0=不使用SCKE信号令SDRAM进入省电模式,1=使用SCKE信号令SDRAM进入省电模式
SCLK_EN[4]: 0=时刻发出SCLK信号,1=仅在方位SDRAM期间发出SCLK信号
BK76MAP[2:0]: 设置BANK6/7的大小,0b010=128MB/128MB,0b001=64MB/64MB,0b000=32M/32M,0b111=16M/16M,0b110=8M/8M,0b101=4M/4M,0b100=2M/2M
则本开发板BANKSIZE设为0xB1
6)SDRAM模式设置寄存器MRSRBx(x为6-7)
CL[6:4]: 0b000=1clocks,0b010=2clocks,0b011=3clocks
本开发板取0b011,MRSRB6/7取值为0x30
SDRAM简介
SDRAM的内部是一个个存储阵列,阵列就如同表格一样,将数据“填”进去。和表格的检索原理一样,先指定一个行(Row)和一个列(Column),就可以准确的找到所需要的单元格,这就是SDRAM寻址的基本原理,这个单元格被称为存储单元,这个表格(存储阵列)就是逻辑Bank(Logical Bank,简称L-Bank)。SDRAM一般分为4个L-Bank。
现在我们实验的是通过CPU访问SDRAM,那么我们就大概的想象一下,我们可以大致的分为4个步骤:
①CPU发出的片选信号nGCS6有效,它选中SDRAM芯片。(从V3原理图可得)
②SDRAM中有4个L-Bank,需要两根地址信号线选中哪一个L-Bank。(从V3原理图我们可以知道 CPU的ADDR24、ADDR25作为L-Bank的选择信号)
③对SDRAM进行统一的 行/列 寻址。(这样我们就可以确定具体访问芯片内部哪一个存储单元了)
④找到存储单元后,就要对SDRAM进行数据传输了。(这里我们就需要知道数据宽度等等)
对于①,很显然。
对于②,[ADDR25:ADDR24]=0b00/0b01/0b10/0b11,就正好对应四个L-Bank了。
对于③,(这个我们就得SDRAM的芯片手册和原理图结合看一下了)根据SDRAM的列地址线数目设置CPU相关的寄存器后,CPU就会从32位的地址中自动分出L-Bank选择信号、行地址信号、列地址信号,然后先后
发出行地址信号,列地址信号。L-Bank选择信号在发出行地址信号的同时发出,并维持到列地址信号结束。
在我们这个实验中,行地址、列地址共用ADDR2~ADDR14,然后使用nSRAS、nSCAS来区分它们(Bank6的位宽是32,也就是CPU访问SDRAM,一次访问4个字节。而CPU的单位是Byte,eg:CPU内存地址0x00000000、0x00000001、0x00000002、0x00000003其实访问的都是SDRAM的0x00000000,即CPU真正有效的地址位是从ADDR2开始的,所以ADDR0和ADDR1没用)。
通过原理图我们可以看出,这个开发板的两根地址线ADDR24、ADDR25作为L-Bank的选择信号,行地址数为13,列地址数为9。当nSRAS信号有效时,ADDR2~ADDR14发出的是行地址信号,它对应32bit地址空间的
bit[23:11]。当nSCAS信号有效时,ADDR2~ADDR14发出的是列地址信号,它对应32bit地址空间的bit[10:2](地址信号我目前的理解是行信号加列信号加片选信号,具体行列信号位于地址空间哪一位有上述可能,但不一定)。
代码
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
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
leds.c
#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 = 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;
}