韦东山嵌入式Linux学习笔记05--存储管理器

只愿长相守 提交于 2019-11-28 01:47:34

SDRAM:

   原理图如下:

        

jz2440 v3开发板上面用的内存芯片为钰创科技公司生产的EM63A165TS,一片内存大小为32MB大小,一共有两块,共64MB的大小.

 

 

 SDRAM接的是BANK 6,所以他的起始地址是 0x30000000.

 SDRAM的基本寻址关系

 

SDRAM的存储结构逻辑如上图,  SDRAM内部是一个存储阵列,阵列就如同表格一样,将数据"填进去".和表格的检索原理一样,先指定一个行,再指定一个列,两点确定一个坐标,就可以准确地找到所需要的

单元格,这就是SDRAM寻址的基本原理.这个单元被称为存储单元,这个表格(存储阵列)就是逻辑Bank(就是L-Bank).SDRAM一般含有四个L-Bank.

对SDRAM的访问可以分为如下几个步骤:

  1.CPU发出的片选信号nSCS0有效,它选中SDRAM芯片.

  2.SDRAM有四个L-Bank,需要两根地址线来选中其中的一个,根据开发板的设计, 选用了ADDR24和ADDR25作为L-Bank的选择信号.

  3.对被选中的芯片进行统一的行/列(存储单元)寻址.

    根据SDRAM芯片的列地址线数目设置CPU的相关寄存器后,CPU就会从32位的自动分出L-Bank选择信号, 行地址信号,列地址信号,然后发出行地址信号,列地址信号. L-Bank选择信号在发出行地址信号的同时发出,并维持到列地址信号结束. 有原理图可以看出, 行列地址公用地址线ADDR2-ADDR14(因为DDR的数据位宽为32位,根据2440 datasheet可知,地址线应从A2开始.没有使用ADDR0/1.

使用nSRAS,nSCAS两个信号来区分他们.对于jz2440 v3开发板的SDRAM芯片EM63A165TS的行地址数为13,列地址数位9,所以当nSRAS信号有效时,ADDR2-ADDR14发出来的时行地址信号,它对应的

32位地址空间的bit[23:11](为什么?),当nSCAS信号有效时,ADDR2-ADDR10发出来的时列地址信号,它对应32位地址空间的bit[10:2];

 

 

  4.找到存储芯片后,被选中的芯片就要进行统一的数据传输了.

    开发板使用两片16位的SDRAM芯片并联组成32位的位宽,与CPU的32跟数据线相连(DATA0-DATA31).

    BANK6的起始地址为0x30000000,所以SDRAM的访问地址为0x30000000-0x33FFFFFF,共64MB.

 存储控制器的的寄存器的使用方法

存储控制器共有13个寄存器,BANK0-BANK5只需要设置BWSCON和BANKCONx两个寄存器,BANK6-BANK7外接SDRAM时, 除BWSCON和BANCONx外,还要设置REFLESH,BANKSIZE,MRSRB6,MRSRB7等4个寄存器.

 

 由上图BANCON寄存器发现,每四位控制一个BANK,最高四位对应BANK7,以此类推.

STx: 启动/禁止SDRAM的数据掩码引脚,对于SDRAM,此位为0;对于SRAM,此位为1.

WSx: 是否使用寄存器的WAIT信号,通常设为0.

DWx: 使用两位来设置相应BANK的位宽,0b00对应8位,0b01应16位,0b10对应32位,0b11保留.

  比较特殊的是BANK0,它没有ST0和WS0,DW0只读--由OM0和OM1上电前的电平决定,OM1一直为低,所以只有当OM0为高时,位宽为16bit, 当OM0为低时,选择Nandflash mode.

                         

 

‭BWSCON可以设置成‭0010 0010 0000 0001 0001 0001 0001 0000‬    ‭0x2201 1110

 

BANKCONx (x为0~5)

这几个寄存器用来控制BANK0-BANK5外接设备的访问时序,jz2440上使用默认的0x0700

 

 BANKCONx (x为6~7)

在8个BANK中,只有BAN6 AND BAN7可以外接SRAM和SDRAM.BANKCON6和BANKCON7与其他BANK有所不同.

 

 

MT: 用于设置本BANK外接的时ROM/SRAM还是SDRAM.SRAM 0b00, SDRAM 0b11

当MT=0b00,此寄存器与BANKCON0---BANKCON5类似.

当MT=0b11,值如下设置

Trcd: RAS to CAS delay, 设为推荐之0b01

SCAN: SDRAM的列地址数,对于这个SDRAM来说, 为9, 所以SCAN=0b01.

这个寄存器应设为 0x00018005

 

刷新控制寄存器RFFRESH(REFRESH CONTROL REGISTER) 设为0x008c0000 + R_CNT

 REFEN: 0= 禁止SDRAM的刷新功能, 1=开启SDRAM的刷新功能

TREFMD: SDRAM的刷新模式. 0 =CBR/auto Refresh,1=Self Refresh(一般在系统休眠时使用).

Trp: 设置为0即可.

Tsrc: 设为默认值0b11即可.

Refresh Counter: 即上述的R_CNT

R_CNT如下计算:(SDRAM时钟频率就是HCLK)

   R_CNT = 2^11 + 1 - SDRAM时钟频率(Mhz) * SDRAM刷新周期(us)

如下图, 刷新周期 64ms/8192 = 7.8125us

在未使用PLL时, SDRAM时钟频率等于晶振频率12Mhz.

可以计算:

  R_CNT= 2^11 + 1 - 12 * 7.8125 = 1955

所以在未使用PLL时, REFRESH = 0x008c0000 + 1955 = 0x8c07a3

 

 

 

 

BANKSIZW寄存器REFRESH (BANKSIZE REGISTER)

BURST_EN: 

  0: ARM核禁止突发传输

  1: ARM核允许突发传输

SCKE_EN[5]:

  0: 不使用信号令SDRAM进入省电模式,1使用SCKE信号令SDRAM进入省电模式.

 SCKE_EN[4]: 

  0: 时刻发出SCLK信号,1=仅在访问SDRAM期间发出SCLK信号.

BK76MAP:

  设置BANK7/6的大小.

  BANK6/7对应的地址空间与BANK0-5不同. BANK0-5的地址空间大小都是固定的128MB,BANK6/7的地址空间大小都是可变的.以保持这两个空间的地址连续,即BANK7的起始地址会随它们的大小变化. BANK6/7的取值意义如下:

jz2440 v3开发板BANK6外接的时64M的SDRAM(两片32MB),令[2:0]=0b001(64M/64M),表示BANK6 BANK7的容量都是64M.虽然BANK7没有使用.

综上, BANKSIZE寄存器的值应该设为 0xB1

 

SDRAM模式设置寄存器MRSRBx

能修改的只有CL,这是SDRAM时序的一个时间参数; 由datasheet可知, SDRAM芯片支持CL =2或者3,开发板选择了3,所以 MRSRB6/7的值为0x30

 

 操作实例: 使用SDRAM

 

从NAND Flash启动CPU时, CPU会通过内部硬件将NandFlash开始的4KB数据复制到称为Steppingston的4KB内部RAM中(起始地址为0),然后跳到地址0开始执行.

 

 

 

 例子中先使用汇编设置好储存控制器,使外接的SDRAM可用,然后把程序本身从Steppintstone复制到SDRAM处;最后跳转到执行.

 

.equ        MEM_CTL_BASE,       0x48000000  @命令用于把常量值设置为可以在文本段中使用的符号
.equ        SDRAM_BASE,         0x30000000

.text
.global _start
_start:
    bl  disable_watch_dog               @ 关闭WATCHDOG否则CPU会不断重启.
    bl  memsetup                        @ 拷贝之前,要先设置存储控制器,不然SDRAM用不了
    bl  copy_steppingstone_to_sdram     @ 复制代码到SDRAM中
    ldr pc, =on_sdram                   @ 跳转到中继续执行
on_sdram:
    ldr sp, =0x34000000                 @ 随便设一个没用到的地址吗?
    bl  main                 @ 跳转到main函数
halt_loop:                  @ 死循环
    b   halt_loop

disable_watch_dog:
    mov r1,     #0x53000000        @ 直接往这个寄存器写0就可以达到关闭看门狗的目的
    mov r2,     #0x0
    str r2,     [r1]
    mov pc,     lr                @不会自动返回吗? 还要将PC指向跳转函数bl保存的Lr值

copy_steppingstone_to_sdram:        @ 这个函数主要实现将4K大小的数据从CPU内部SRAM拷贝到SDRAM
    mov r1, #0                @ 这个地址就是Steppingstone的起始地址.
    ldr r2, =SDRAM_BASE           @ SDRAM的起始地址
    mov r3, #4*1024             @ 4KB大的空间
1:  
    ldr r4, [r1],#4               @ 从steppintstone中读取4字节的数据到r4中,并让r4地址加4
    str r4, [r2],#4               @ 紧接着将刚刚刚读取到r4的数保存到r2所指向的内存单元中,r2一开始指向SDRAM的起始地址
    cmp r1, r3                    @ 比较是否已经拷贝4KB大小的数据了
    bne 1b                        @ 如果不相等,说明还没由拷贝完,继续跳转到标号1处执行.
    mov pc,     lr                @ 如果相等,函数返回.

memsetup:
    mov r1,     #MEM_CTL_BASE       @ 0x4800000000
    adrl    r2, mem_cfg_val        @ ADRL 伪指令用于将一个地址加载到寄存器中。
    add r3,     r1, #52           @ 13*4 = 54 r3 = r1 + 52, 基于base地址需要52个字节空间. 每个寄存器大小为4字节,共有13个寄存器
1:  
    ldr r4,     [r2], #4               @ 将r2中存储寄存器中的地址保存到r4中, r2 = r2 + 4;指向下一个寄存器所代表的值
    str r4,     [r1], #4          @ 将r4中的值取出来写到r1中, 以达到配寄存器的目的
    cmp r1,     r3                      @ 比较一下是否写完了
    bne 1b                              @ 如果没有,跳转到1标号处继续执行
    mov pc,     lr                      @ 否则函数返回


.align 4
mem_cfg_val:
    .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

 main函数, 可以不关注这个函数的功能,实现LED点亮的功能, 方便验证函数已经跑到这.

#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;        //

    while(1){
        wait(30000);
        GPFDAT = (~(i<<4));         //
        if(++i == 8)
            i = 0;
    }

    return 0;
}

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

最后将程序烧录到nandflash中验证一下.

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!