S3C2440属于统一编址,即存储器和外设统一编址。
CPU可以通过寻址的方式来访问诸如a.GPIO、b.UART、IIC、c.NOR、NAND、网卡等不同的设备。
问:那么CPU是如何访问各个不同的寄存器的呢?
答:CPU只管发出一个地址给内存控制器,内存控制器根据该地址选择不同的模块,然后从模块中得到数据或者发送数据到模块中。
S3C2440存储器控制器概述
–大/小端(通过软件选择)
–地址空间:每个 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根地址线ADDR0~ADDR26,访问范围只有128MB,那么如何访问上面所说的1GB的访问空间的?
答:CPU还对外引出了8根片选信号,对应BANK0~BANK7。
下面看一下地址映射表
S3C2440理论上可以访问4GB的空间,除了上面的1GB用于访问外部的储存器,剩余的地址一部分是特殊功能寄存器的地址。范围是0x48000000~0x5FFFFFFF。
BANK0(nGCS0)的数据总线应当配置为 16 位或 32 位的宽度。因为 BANK0 是作为引导 ROM 的 bank(映射到 0x0000_0000),应当在第一个 ROM 访问前决定 BANK0 的总线宽度,其依赖于复位时 OM[1:0]的逻辑电平。
JZ2440通过拨码开关确定是NAND启动还是NOR启动,原理就是设置OM0的状态。
硬件连线
问:从电路图可以看出,CPU连接一些存储器时,存储器的A0并不连接CPU的A0,而是连接CPU 的A1或者A2,这是为什么?
答:CPU 是按照字节来寻址的,即CPU认为每个字节都有自己的地址,而ROM并不这样,对于8bit的ROM,每个地址代表8bit 数据;对于16bit的ROM,每个地址代表16bit 数据;对于32bit的ROM,每个地址代表32bit 数据。
所以连线方式为:
具体读写过程为:
由上表可以得知,CPU只需发出地址,剩余工作由内存控制器完成。
所以,CPU 只需设置存储器控制器,再给出要访问空间的地址和访问方式(读或写),其余脏活累活都由存储器控制器来完成。
存储器控制器所干的工作包括:
- 发片选信号
- 发地址
- 挑选相应的数据给CPU或把相应的数据给存储器。
时序
问:访问存储器要按照特定存储器的时序来访问,时序如何得知呢?
答:查阅相应芯片手册
得到具体时序之后就要设置对应的BANKCON。
2440内存控制器设置:
内存控制器共有13个寄存器,
BANK0–BANK5只需要设置BWSCON和BANKCONx(x为0~5)两个寄存器;
BANK6、BANK7外接SDRAM时,除BWSCON和BANKCONx(x为6、7)外,还要设置REFRESH、BANKSIZE、MRSRB6、MRSRB7等4个寄存器。
SDRAM比较特别,SDRAM总共有4个块(Banks),可以认为每个块就是一个表格,里面的每个格子表示的是16bit数据。
问题1:怎样访问里面的某个格子呢?
- 首先发出一个片选信号,选中整个芯片;
- 发出Bank地址,选择是哪一个Bank(块,即表格);
- 发出行地址;
- 最后发出列地址,才能选中是个格子;
问题2:那么多的信号有谁发出呢?
由内存控制器发出,所以我们需要设置内存控制器,CPU只是简单的执行读写内存的命令,其他的都交给内存控制起来处理。
综上所述:对SDRAM的访问可以分为如下步:
- CPU发出的片选信号nSCS6有效,它选中SDRAM芯片。
- SDRAM中有4个L-Bank,需要两根地址信号来选中其中之一,根据原理图,可知使用ADDR24,ADDR25作为L-Bank的选择信号。
- 对被选中的芯片进行统一的行/列(存储单元)寻址。
编程
目的:检验SDRAM。
查看SDRAM的时序和S3C2440的芯片手册中的存储器控制器共有13个寄存器。编写SDRAM的初始化函数,和SDRAM的测试函数(写数据到SDRAM然后再读出,检验是否一致)
程序如下:
void sdram_init(void)
{
BWSCON = 0x22000000;
BANKCON6 = 0x18001;
BANKCON7 = 0x18001;
REFRESH = 0x8404f5;
BANKSIZE = 0xb1;
MRSRB6 = 0x20;
MRSRB7 = 0x20;
}
int sdram_test(void)
{
volatile unsigned char *p = (volatile unsigned char *)0x30000000;
int i;
// write sdram
for (i = 0; i < 1000; i++)
p[i] = 0x55;
// read sdram
for (i = 0; i < 1000; i++)
if (p[i] != 0x55)
return -1;
return 0;
}