s3c2440学习之路-010 sdram

硬件平台:jz2440
软件平台:Ubuntu16.04 arm-linux-gcc-3.4.5
源码位置: https://github.com/lian494362816/C/tree/master/2440/011_dram

1.主要原理

1.1基础知识

做控制无非就是设置2440的寄存器,只有对寄存器中的那些参数有一定的感性认识可以明白为什么有这个参数,这个参数的实际意义何在。所以在学习SDRAM需要先补充一些基础知识,可以搜索 “高级进阶终极内存技术指南” ,这里面对SDRAM的工作原理、专用名词做了非常好的讲解。下面贴出我在阅读 “高级进阶终极内存技术指南” 时做的一些记录。

1,P-Bank 物理块,指的就是有多少个内存芯片
2,L-Bank 逻辑块,存储阵列, 1个P-Bank可以包含多个L-Bank
3,主要的引脚有:
	1)片选信号,用来选择P-Bank
	2)L-Bank地址信号, 用来选择L-Bank
	3)行/列 地址
	4)数据地址
 
4,RAS(Row Address Strobe,行地址选通脉冲)
5,CAS(Column Address Strobe,列地址选通脉冲)
6,tRCD,即RAS to CAS Delay(RAS至CAS延迟) 发送行命令与列命令之间的间隔时间
7,CL(CAS Latency,CAS潜伏期)又称为RL(Read Latency,读取潜伏期), 在读操作才会有这个时间,写操作是没有的,原因是带读取的数据与CAS命令不可能在同一个上升沿触发,所以至少延时1个时钟周期
8,tAC (Access Time from CLK,时钟触发后的访问时间),读取操作才会有,每次读取1笔数据都会有tAC(包括连续读取),原因是信号需要经过放大后才能被有效识别
9,写入/校正时间(tWR,Write Recovery Time),这个操作也被称作写回(Write Back), 写命令和数据可以同时发送,但是三极管的选通需要一定的时间
10,突发长度(Burst Lengths,简称BL) 数据连续读/写的长度
11,L-Bank关闭现有工作行,准备打开新行的操作就是预充电(Precharge)
12,tRP(Precharge command Period,预充电有效周期)
13,自动刷新(Auto Refresh,简称AR)与自刷新(Self Refresh,简称SR),AR又称CBR(CAS Before RAS,列提前于行定位)
 
14, 要寻址的行与L-Bank是空闲的。也就是说该L-Bank的所有行是关闭的,此时可直接发送行有效命令,数据读取前的总耗时为tRCD+CL,这种情况我们称之为页命中(PH,Page Hit)。
15, 要寻址的行正好是前一个操作的工作行,也就是说要寻址的行已经处于选通有效状态,此时可直接发送列寻址命令,数据读取前的总耗时仅为CL,这就是所谓的背靠背(Back to Back)寻址,我们称之为页快速命中(PFH,Page Fast Hit)或页直接命中(PDH,Page Direct Hit)。
16, 要寻址的行所在的L-Bank中已经有一个行处于活动状态(未关闭),这种现象就被称作寻址冲突,此时就必须要进行预充电来关闭工作行,再对新行发送行有效命令。结果,总耗时就是tRP+tRCD+CL,这种情况我们称之为页错失(PM,Page Miss)。

在这里插入图片描述

1.2 SDRAM的连接

jz2440使用了2片32MB-16bit的SDRAM芯片组合成了64MB-32bit(32MB-16bit说的是总容量32MByte, 位宽为16bit,这里的写法可能有问题,所以我要强行解释一波)。使用的GCSpin脚是GCS6, 因此SDRAM的起始地址为0x30000000。SDRAM总的大小为64M,所以只要访问地址在0x30000000~0x34000000就可以访问到外部的SDRAM了。(2440的控制机制就是这样,每1种存储外设接在一个bank上,通过访问不同的地址来访问不同的bank, GCS引脚会自动去控制)

在这里插入图片描述

在这里插入图片描述

看到硬件原理图可能会有一个问题, 64MB = 2^27B,所以应该需要使用到27根地址线才可以完全访问到。为何实际中没有用到27根地址线?

1)这里先讲一片32MB的SDRAM。一片SDRAM的规格是4M word X 16bit X 4Bank , 那么每个bank需要访问的数据量为4M。这里的word是代表容量的意思或者干脆不看这个词,绝不能当做2Byte来看待。 4M = 2 ^ 22,也就是访问4M的地址需要22根地址线, 这22根地址由A0~A12通过组合完成。先把A0-A12当做row地址,再把A0-A8当做col地址,加起来就是22根地址线了。 4个Bank则通过BA0, BA1来控制。 综上,访问32MB的SDRAM只需要A0-A12, BANK0-BNAK1总共15根地址线。

在这里插入图片描述

在这里插入图片描述

为什么LADDR2与A0连接,需要跳过LADDR0, LADDR1?

2)这2片SDRAM的地址连线是完全相同的,2440是32位的cpu,每次2440的读写都是按4Byte对齐。为了同时获取4个Byte中的高2个字节个和低2个字节,就需要同时操作2片SDRAM。第1片SDRAM负责存储低2个字节,第2片SDRAM负责存储高2个字节。

3)这2片SDRAM是16bit的,所以1个地址存储2个Byte,(2440是1个地址存储1个Byte)。假设2440要把#0地址存放0x11112222, #4地址存放0x33334444, #8地址存放0x55556666, #C地址存放0x77778888。那么对应的伪代码为

//把0地址强制转换成指针
unsigned int *p = (unsigned int *) 0;
*p = 0x11112222;
p++;
*p = 0x33334444;
p++;
*p = 0x55556666;
p++;
*p = 0x77778888;

当执行*p = 0x11112222时,2440发送的地址为0, 数据为0x11112222,LADDR[26:0]上的数据都是0,所以SDRAM接收到的地址也是0,因此0x1111存放在SDRAM2的0地址, 0x2222存放在SDRAM1的0地址。

当执行*p = 0x33334444时,2440发送的地址为0x4, 数据为0x33334444, LADDR[26:0]上的数据都是0x4。0x4=0100b, 因为是从LADDR2开始与SDRAM连接,所以最低2位被丢弃了。0100b >> 2 = 01b=0x1,所以SDRAM接收到的地址为0x1。最后0x3333存放在SDRAM2的1地址, 0x4444存放在SDRAM1的1地址。

当执行*p = 0x55556666时,2440发送的地址为0x8, 数据为0x55556666, LADDR[26:0]上的数据都是0x8。0x8=1000b, 同理,最低2位被丢弃,SDRAM接收到的地址为10b=0x2。因此0x5555存放在SDRAM2的2地址, 0x6666存放在SDRAM1的2地址。

当执行*p = 0x77778888时,2440发送的地址为0xc, 数据为0x77778888, LADDR[26:0]上的数据都是0xc。0xc=1100b, 同理,最低2位被丢弃,SDRAM接收到的地址为11b=0x3。因此0x7777存放在SDRAM2的3地址, 0x8888存放在SDRAM1的3地址。

(上面的说明只是为了更好的理解, 实际上要访问SDRAM需要发送的起始地址为0x30000000)
在这里插入图片描述

2.主要寄存器

2.1 BWSCON

SDRAM是接在BANK6上,所以我们只管与BANK6有关的配置。需要把[25:24]配置成10b,因为2片16bit的SDRAM组合成了32bit。
在这里插入图片描述

2.2 BANKCON6

[16:15]选择11b, 因为外接的是SDRAM。
[3:2] 选择00b-2clock。查看SDRAM的手册可以知道,RAS to CAS delay是18ns, 而SDRAM连接的是HCLK(100M),所以选择00b-2clock=20ns就可以了。
[1:0] 选择01b。前面说过SDRAM 先把A0-A12当做row地址,再把A0-A8当做col地址,所以col=9bit

在这里插入图片描述

在这里插入图片描述

2.3 REFRESH

[23] 设置成1b, SDRAM需要不断的去刷新
[22] 设置成0b, 启动自动刷新
[21:20] 设置成00b。查看SDRAM手册, pre-charge的时间为18ns, 因此设置为00b-2clock-20ns即可满足。

[19:18] 设置为00b。T
rc=Tsrc+Trp
Trc=60ns, 查询SDRAM手册所得
Trp=20ns, SDRAM手册上为18ns, 不过我们设置成了20ns
所以Tsrc=Trc-Trp=60ns-20ns = 40ns。

[10:0]
8192 refresh cycles/64ms,因此每刷一行的时间为64ms/8192=7.8125us。带入公式,2^11 + 1 - 100*7.8125 = 1269=0x4F5。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.4 BANKSIZE

[7] 设置为1b
[5] 设置为1b
[4] 设置为1b
[2:0] 设置为001b。
在这里插入图片描述

2.5 MRSRB6

这里只有[6:4]可以设置,设置为010b或011b都可以。
在这里插入图片描述

在这里插入图片描述

3.源码

init.c

int bank6_sdram_init(void)
{

    /*1, set BWSCON */
    //clear bank6 [24:27]
    BWSCON &= ~(0xF << 24);

    //set data bus [25:24] 00->8bit, 01->16bit, 10->32bit
    BWSCON |= 2 << 24;

    //[26]=0, disable WAIT
    BWSCON |= 0 << 26;

    //[27]=0, Not useing UB/LB, it means use nWBE
    BWSCON |= 0 << 27;

    /*2, set BANKCON6 */
    //clear [0:3]
    BANKCON6 &= ~(0xF);

    //[1:0] SCAN(Column Num) 00->8bit, 01->9it, 10->10bit
    BANKCON6 |= 1 << 0;

    //[3:2] = 0, Trcd(RAS to CAS delay) 00->2 clock, 01->3clock, 10->4clock
    BANKCON6 |= 0 << 2;

    //clear [16:15]
    //00->ROM or SRAM, 11->SDRAM
    BANKCON6 |= 3 << 15;

    /*3, set REFRESH */
    //clear [0:23]
    REFRESH &= ~(0xFFFFFF);

    //Refresh counter, refresh time = 7.8us,
    //counter = 2^11 +1 - 100*7.8 = 1269= 0x45F
    REFRESH |= 0x4F5;

    //[19:18]=00, Tsrc 00->4clock, 01->5clock, 10->6clock, 11->7clock
    //Trc=Tsrc+Trp, Trc=2clock, Trc=60ns=6clock, Tsrc=6-2=4clock
    REFRESH |= 0 << 18;

    //[21:20] Trp(pre-charge), 00->2clock, 01->3clock, 10->4clock
    REFRESH |= 0 << 20;

    //[22] TREFMD 0->Auto Refresh, 1->Self Refresh
    REFRESH |= 0 << 22;

    //[23] REFEN 0->Disable, 1->Enable
    REFRESH |= 1 << 23;

    /*4, set BANKSIZE */
    //clear [7:0]
    BANKSIZE &= ~(0xFF);

    /*[2:0] memery map
    010->128M,
    001->64M,
    000->32M
    111->16M
    110->8M
    101->4M
    100->4M
    */
    BANKSIZE |= 1 << 0;

    //[4] SLCK_EN mode 0->always, 1 active druing access
    BANKSIZE |= 1 << 4;

    //[5] SCKE_EN, power donw mode,0->disable, 1->enable
    BANKSIZE |= 1 << 5;

    //[7] burst enable, 0->disable, 1->enable
    BANKSIZE |= 1 << 7;

    /*5 set MRSRB6 */
    //clear [9:0]
    MRSRB6 &= ~(0x3FF);

    //[2:0] brust length, must be 0, burst length = 1
    MRSRB6 |= 0 << 0;

    //[3] Burst byte, must be 0, sequential
    MRSRB6 |= 0 << 3;

    //[6:4] CAS latency, 000->1clock, 010->2clock, 011->3clock
    //can be 2clock or 3clock
    MRSRB6 |= 2 << 4;

    //[8:7] Test Mode, must be 0
    MRSRB6 |= 0 << 7,

    //[9] Write burst length, 0->burst, must be 0
    MRSRB6 |= 0 << 9;

    return 0;
}

bank6_sdram_init 按照之前讲的配置把寄存器设置了一遍。

main.c

#include "uart.h"
//#include "key.h"
//#include "led.h"
#include "delay.h"
#include "my_printf.h"
#include "init.h"

//2018.8.27 dram init test
// sdram use bank6, start address is 0x30000000

#define SIZE (1000)

int sdram_test(void)
{
    int i = 0;
    volatile unsigned char *p = (volatile unsigned char *) 0x30000000;

    for (i = 0; i < SIZE; i++)
    {
        p[i] = 0x55;
    }

    for (i = 0; i < SIZE; i++)
    {
        //printf("p[%d]= %d\n", i, p[i]);
        if (0x55 != p[i])
        {
            printf("p[%d]= %d\n", i, p[i]);
            return -1;
        }
    }

    return 0;
}

int main(int argc, char *argv[])
{
    int c = 0;
    uart0_init();
    bank6_sdram_init();

    if (0 == sdram_test())
    {
        printf("sdram test success\n");
    }
    else
    {
        printf("sdram test failed \n");
    }

    return 0;
}

main 需要先调用bank6_sdram_init来初始化sdram。sdram_test函数从0x30000000(SDRAM0地址)开始写0x55进去,然后再读出来看是否等于0x55。(0x55是随意写的,可以修改成任意的数字)

在没有初始化sdram的情况下,往0x30000000写0x55是会失败的,因此再读出来一定不等于0x55。
当初始化了sdram,往0x30000000写0x55会自动写到外部的SDRAM里面,再读取时也是从外部的SDRAM读取的,因此再读取出来的数据等于0x55。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值