什么是存储控制器

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地址空间。

存储架构 ip存储 控制器存储 存储控制器是啥_寄存器


上图为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简介

存储架构 ip存储 控制器存储 存储控制器是啥_数据_02

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;
}