SDRAM自定义操作
**注:本文章是基于韦东山老师arm裸机第一期视频教程
**硬件平台:嵌入式Linux开发板s3c2440
**软件平台:运行于VMware Workstation 12 Player下的UbuntuLTS16.04_x64 系统
**参考资料:《高手进阶,终极内存技术指南——完整/进阶版》、S3C2440芯片手 册、EM63A165TS(SDRAM)芯片手册、开发版电路原理图
一、基础知识点
- 物理 Bank
传统内存系统为了保证 CPU 的正常工作,必须一次传输完 CPU 在一个传输周期内所需要的数据。而 CPU 在一个传输周期能接受的数据容量就是 CPU 数据总线的位宽,单位是 bit(位)。当时控制内存与 CPU 之间数据交换的北桥芯片也因此将内存总线的数据位宽等同于 CPU 数据总线的位宽,而这个位宽就称之为物理Bank(Physical Bank,简称 P-Bank)的位宽。 - 芯片位宽
每个内存芯片也有自己的位宽,即每个传输周期能提供的数据量。 - 存储单元数量(容量)
存储单元数量=行数×列数(得到一个 L-Bank 的存储单元数量)×L-Ban的数量
常用方式:M×W
{
M 是该芯片中存储单元的总数,单位是兆
W 代表每个存储单元的容量,也就是 SDRAM 芯片的位宽(Width),单位是 bit
}
二、SDRAM概念
- 英文全称:Synchronous Dynamic Random Access Memory
- 中文名字:同步动态随机存储器———拆分理解
-同步:其时钟频率与CPU前端总线的系统时钟频率相同(如开发实例中:SDRAM时钟频率为CPU的HCLK)
-动态:存储阵列需要不断的刷新来保证数据不丢失
-随机:数据不是线性一次存储,而是自由指定地址进行数据的读写
- 内存控制器的执行流程
如指令: ldr r1, [r0] /* 读内存的数据 */
a.CPU将地址发送给内存控制器
b. 内存控制器根据地址发出片选信号nGCS6
c.根据接的类型是SDRAM,拆分地址(BANK地址、发出行地址、发出列地址)
三、寄存器配置
- 查看电路原理图、S3C2440芯片手册分析
分析可知:两片十六位的SDRAM芯片地址位和数据位接在一起构成!32!位的芯片;
片选信号为CS6;
分析可知:SDRAM连接HCLK,HCLK已设置为100MHZ
- 查看S3C2440芯片手册,配置所需寄存器
(1)BWSCON(总线宽度和等待控制寄存器)
ST7 [31]=0-------Not using UB/LB (The pins are dedicated nWBE[3:0])时序图可知
WS7 [30]=0-------WAIT disable使用不到
DW7 [29:28]=10—电路图连接为32位芯片
ST6 [27]=0-------Not using UB/LB (The pins are dedicated nWBE[3:0])时序图可知
WS6 [26]=0-------WAIT disable使用不到
DW6 [25:24]=10—电路图连接为32位芯片
最终得:BWSCON = 0x22000000;
(2)BANKCON6 BANKCON7
MT [16:15]=11----Sync. DRAM 使用外接的SDRAM
Trcd [3:2]=00----2 clock 时钟周期为10ns,2 clock=20ns,选取最大值21ns
SCAN [1:0]=01----9-bit 查SDRAM手册知道column address A0-A8
最终得:BANKCON6 = 0x00018001;
BANKCON7 = 0x00018001;
(3)REFRESH
REFEN [23]=1-----Enable (self or CBR/autorefresh)
TREFMD [22]=0----CBR/Auto Refresh
Trp [21:20]=00—2 clocks = 20ns
Tsrc [19:18]=01–5 clocks = 50ns Trc=Tsrc+Trp Trc查手册Trc取最大70ns 70ns=Tsrc+ 20ns(上面已经设置Trp)
Refresh Counter [10:0]=1269 --8192 refresh cycles/64ms
最终得:REFRESH = 0x8404f5;
(4)BANKSIZE
URST_EN [7]=1—Enable burst operation ARM核突发操作使能,即一次访问多个字节
SCKE_EN [5]=1----SDRAM power down mode enable
SCLK_EN [4]=1-----SCLK is active only during the access (recommended)
BK76MAP [2:0]=001–64MB
最终得:BANKSIZE = 0x000000b1;
(5)MRSRB6、MRSRB7
CL [6:4]=010-----2 clock CAS Latency: 2, or 3
最终得:MRSRB6 = 0x00000020;
MRSRB7 = 0x00000020;
补充知识点说明:
——tAC(Access Time from CLK 时钟触发后的访问时间)
此时数据已经被触发,经过一定的驱动时间最终传向数据 I/O 总线进行输出
!必须要小于一个时钟周期!
——tRCD(RAS to CAS Delay(RAS 至 CAS 延迟))
在发送列读写命令时必须要与行有效命令有一个间隔,大家也可以理解为行选通周期
——CL(CAS Latency,CAS 潜伏期)
从 CAS 与读取命令发出到第一笔数据输出的这段时间
!CL 只出现在读取操作中!
四、 代码编写
sdram_init.c文件编写
#include "s3c2440_soc.h"
void sdram_init(void)
{
BWSCON = 0x22000000; //初始化BWSCON,选用BANK6,7
BANKCON6 = 0x00018001;
BANKCON7 = 0x00018001;
REFRESH = 0x8404f5;
BANKSIZE = 0x000000b1;
MRSRB6 = 0x00000020; //设置CL为2clock,具体查手册
MRSRB7 = 0x00000020; //设置CL为2clock,具体查手册
}
int sdram_test(void)
{
int i;
volatile unsigned char *p = (volatile unsigned char *)0x30000000; //p指针指向sdram的地址
//写数据到sdram,从0x30000000到0x30001000都写入0x22
for(i=0;i<=1000;i++)
{
p[i] = 0x22;
}
//从sdram读数据
for(i=0;i<=1000;i++)
{
if(p[i] != 0x22)
return -1;
}
return 0;
}
sdram_init.h文件编写
#ifndef _SDRAM_INIT_H
#define _SDRAM_INIT_H
void sdram_init(void);
int sdram_test(void);
#endif
main.c文件编写
#include "s3c2440_soc.h"
#include "uart.h"
#include "sdram_init.h"
#include "led.h"
int main(void)
{
unsigned char c;
int flag;
uart0_init();
sdram_init();
puts("Uart0 text success!\n\r");
flag=sdram_test();
if(flag == 0)
{
while(1)
{
puts("Sdram init success!\n\r");
led_test();
}
}
else
puts("Sdram init failure!\n\r");
return 0;
}