搬砖之韦东山学习笔记——基于S3C2440的内存控制器与SDRAM(12)


搬砖链接

存储控制器(SDRAM操作)
2440裸机-12-2内存控制器与SDRAM_不同位宽设备的连接
2440裸机-12-3内存控制器与SDRAM_分析NOR FLASH时序
韦东山:第012课 内存控制器与SDRAM
04-内存控制器与SDRAM

推荐

这个大佬的文章对寄存器的讲解更为详细,推荐:https://blog.youkuaiyun.com/mxgsgtc/article/details/71405213

内存接口概念

通常ARM芯片内置的内存很少,要运行linux,需要扩展内存,ARM9扩展内存使用SDRAM内存,ARM11使用DDR SDRAM。S3C2440通常外接32位64MBytes的SDRAM,采用两片16位32M的SDRAM芯片,SDRAM芯片通过地址总线、数据总线、若干控制线与S3C2440芯片相连。

内存控制器

  • 2440是32位单片机,进行数据访问时通过32位地址访问。CPU发出32位地址信号给存储控制器,存储控制器根据地址信号设置片选信号及地址总线,将相应数据通过数据总线传回存储控制器,存储控制器将收到的数据以字节为单位发送给CPU。CPU只管发出一个地址,内存控制器根据该地址选择不同的模块,然后从模块中得到数据或者发送数据到模块中。

  • S3C2440有ADDR0~ ADDR26共27根地址线(128M),可访问128*2^20 = 128M 地址空间,配合8个片选信号nGCS0~ nGCS8,可达到1G地址空间。其中ADDR0,ADDR16~ ADDR26为GPIO/地址线复用,GPIO只能是输出,ADDR1~ADDR15为专用地址线,就只能是当地址线用。有32根数据线DATA0 ~DATA31全部都是专用数据线。虽然是32位的芯片,CPU发出的地址是32位的,但是只有27根地址线,所以32位地址只取了0 ~26位来使用。

  • 对于CPU是不管什么接口的,它只写相应的寄存器,由控制器根据寄存器的配置去控制具体的引脚。

  • 片选信号是根据你的地址范围,自动进行拉低操作的,不需要再人为的去控制引脚。外接存储器处于写入或读取状态,要根据外接存储器的特性进行对引脚的操作,有的需要一个根W引脚,一个R引脚,有的只需要一根引脚控制W/R。

  • 当选择Nor Flash启动时,CPU发出的指令的地址范围处于0x0000000 - 0x06000000,内存控制器就会使nGCS0处于低电平(片选引脚被选中),Nor Flash被选中。

  • 当CPU发出的指令的地址范围处于0x20000000 - 0x26000000,内存控制器就会使nGCS4处于低电平(片选引脚被选中),网卡被选中。

  • 当CPU发出的指令的地址范围处于0x30000000 - 0x36000000,内存控制器就会使nGCS6处于低电平(片选引脚被选中),SDRAM被选中。

  • 内存控制器根据不同的地址地址范围,发出不同的片选引脚,只有被片选引脚选中的芯片才能正常工作,不被选中的芯片就像不存在一样,不工作。

  • GPIO/门电路接口、协议类接口、内存类接口都属于CPU的统一编址。对于Nand Flash,在原理图上它的地址线并没有连接到CPU,因此它不参与CPU的统一编址。但它的数据线也接到了数据总线上,为了防止干扰,它也有一个片选信号(CE)。当CPU访问Nand Flash时,Nand Flash控制器才会片选Nand Flash,让其接收数据总线上的数据。
    补充:
    nGCS0~ nGCS8也可以说是BANK0~BANK8.我们也可以这样说

    • 每个BANK的地址空间都是128MB,总共8个BANK,合计1GB空间
    • 可编程选择总线的位宽(8/16/32位),但是BANK0只支持两种位宽(16/32位)
    • 共有8个BANK,其中BANK0~ BANK5只支持外接ROM或SRAM,BANK6、BANK7还支持外接SDRAM,BANK0~BANK6的起始地址是固定的
    • 当访问相应的地址时,存储控制器会自动将对应的nGCSx引脚输出低电平,从而选中外接设备

在这里插入图片描述

外接的存储器可以像GPIO模块,USRT模块,I2C模块等一样,由CPU发起,通过内存控制器进行操作,只不过这个外设是在片外的。NAND FLASH和外接的存储器不是同一类型的,片内有一个专门的NAND控制来对外接的NAND FLASH硬件进行控制,所以NAND FLASH又属于另外一部分。
在这里插入图片描述

外接存储器都是共用地址线和数据线,但是每个外接存储器有各自连接的独立片选信号引脚,以此可以得以区分当前地址线和数据线的外接模块
在这里插入图片描述

不同位宽设备的连接

市面上很少有32位的SDRAM,一般采用2片的16位SDRAM扩展成32位的SDRAM,或者用4片的8位SDRAM扩展成32位的SDRAM

假设我们要去地址为3的内存上进行操作,假设是读取数据,那么下面各个扩展的SDRAM如何操作呢?

1、参考2440芯片手册,可以看到内存接口与8-bit ROM连接时,2440的A0与外部芯片的A0相连。

  • 对于8bitROM ,8bit是一次读写的最小单位,即0地址是第一个8bit,1地址是第二个8bit;CPU发出的命令是读取地址为3(0000 0011)上的数据,即A0和A1都为1,8bitROM的A0和A1收到的也都是1,于是找到了ROM上地址为3的8bit数据,包含了我们需要的数据。
    在这里插入图片描述

当与两个8-bit ROM拼接成的一个16-bit ROM连接时,2440的A1与外部芯片的A0相连。

  • 对于16bitROM ,16bit是一次读写的最小单位,即0地址是第一个16bit,里面有两个8bit数据;CPU发出的命令是读取地址为3(0000 0011)上的数据,即A0和A1都为1,16bitROM的A0和A1分别收到的是1和0,于是找到了ROM上地址为1的16bit数据,包含了我们需要的数据,最后内存控制器再帮我们挑选出所需的8bit数据。
    在这里插入图片描述

** 当与四个8-bit ROM拼接成的一个32-bit ROM连接时,2440的A2与外部芯片的A0相连。**
在这里插入图片描述
当与一个16-bit ROM连接时,2440的A1与外部芯片的A0相连。
在这里插入图片描述
从图中可以看出

  • 当使用一个8位的芯片时,CPU的A0接芯片的A0

  • 当使用两个8位的芯片拼接(DATA0-7接一个芯片,DATA8-15接一个芯片,也就是组成了一个16位的芯片)的时候,CPU的A1接芯片的A0

  • 当使用四个8位的芯片拼接(也就是组成了一个32位的芯片)的时候,CPU的A2接芯片的A0

例一

MOV R0, #3 @去地址为3的内存上 
LDRB R1, [R0] @ 从内存为3的地址上,读出一个字节

在这里插入图片描述

  • 对于8bit,A0-A0,,A1-A1,A2-A2,所以CPU发出什么地址,内存控制器就转发给存储器什么地址,返回值是精确哪个地址就是哪个地址。

  • 对于16bit,A0没接,A1-A0,A2-A1,所以内存控制器转发给存储器的地址向右移动了一位,转发的不是3,而是1,但是16位的存储器返回的数据是16位的,所以返回到CPU的是Byte3|Byte2,但是内存控制器根据A0的0/1对这个返回的16为数据进行挑选,高低电平选择高位,低电平选择低位,所以返回到CPU的值依然是Byte3。虽然A0没接,但是依然是在起作用。

  • 对于32bit,A0,A1没接,A2-A0,A3-A1,所以内存控制器转发给存储器的地址向右移动了两位,接收到的不是3,而是0,但是32位的存储器返回的数据是32位的,所以返回到内存控制器的是Byte3|Byte2|Byte1|Byte0,但是内存控制器根据A0,A1的0/1对这个返回的32为数据进行挑选,所以返回到CPU的值依然是Byte3。
    在这里插入图片描述
    接到芯片上的引脚用来确定读取芯片上的哪一个单元的数据,把这个单元的数据返回给内存控制器,内存控制器会根据没有连接芯片的引脚,来确定返回哪一个单元的数据给CPU,

  • 对于8位的ROM,里面的数据以8位为最小数据单位保存

  • 对于16位的ROM,里面的数据以16位为最小数据单位保存,可以拆分为2个字节

  • 对于32位的ROM,里面的数据以32位为最小数据单位保存,可以拆分为4个字节

怎样确定芯片的访问地址:

  1. 根据片选信号确定基地址,
  2. 根据芯片所接地址线确定范围

实例:

  • Nor Flash 使用的是片选0(nGCS0),基地址为0,用到A20,A19……A1,A0共21条地址线,所以地址范围为0x00000000 ~ 0x1FFFFF也就是2M的空间大小。
  • 网卡(Net)使用的是片选4(nGCS4),基地址为0x20000000,用到A2,A0共2根地址线,所以地址范围为0x20000000 ~ 0x20000005。
  • SDRAM使用的是片选6(nGCS6),基地址为0x30000000。

例二

MOV R0, #4 
LDR R1, [R0] @去地址4,读取4字节数据

在这里插入图片描述
说实话我这就没看懂,为啥要连续四次读取数据,返回32位的数据,知道的请评论解答,解答,谢谢。

时序图

我们分析一下我们了解时序图,信号之间是怎样一起工作的,以Nor Flash 为例。

2440和Nor Flash 之间有地址线,数据线,还有各种数据线连接。
在这里插入图片描述

读时序

在这里插入图片描述

写时序

在这里插入图片描述

在这里插入图片描述

为什么需要通过编程来控制读写时序呢?

  • 因为2440可以接很多种不同型号的内存芯片,这些芯片的性能有差别,有些性能很强,只要我发出读信号,立马就可以读取内存数据,这样就可以把等待时间减少,加快内存读取速度,假如性能比较弱的话就需要去调整时间了,所以说根据外接不同的芯片需要设置不同的时序时间,而这个时间需要CPU和外接内存时序匹配才可以,所以还需要看外接内存NOR FLASH 的读取时序图。

S3C2440如何能读写NOR FALSH的数据?

使2440发出的读写时序,满足NOR FLASH 的读写时序。

在这里使用NOR FALSH的芯片型号是:MX29LV160DBTI,查看他的数据手册。

NOR FALSH时序名词解释

在这里插入图片描述

NOR FLASH 读时序

在这里插入图片描述
结合Nor Flash芯片的两张图,可以得到如下信息:

  • 发出地址数据(Addresses)后,要等待Taa(要求大于等于70ns)时间,地址数据才有效;
  • 发出片选信号(CE#)后,要等待Tce(要求大于等于70ns)时间,片选信号才有效;
  • 发出读信号(OE#)后要等待Toe(要求大于等于30ns)时间,读信号才有效;

我们的目的是需要2440的读时序能满足NOR FALSH的读时序

为了简单我们把地址数据(Addresses),片选信号(CE#),读信号(OE#),同时发出,然后让它们都等待70ns(等待信号有效)。对应S3C2440的Nor Flash控制器的读时序图,需要让地址信号A[24:0]、片选信号nGCS、读信号nOE同时发出,保持Tacc大于等于70ns。就不用去管等待多少时间才有效啥的。其他的都可以默认设置为0,即可满足nor flash的读取要求.

程序具体匹配

内存控制器共有13个寄存器,

  • BANK0–BANK5只需要设置BWSCON和BANKCONx(x为0~5)两个寄存器;

  • BANK6、BANK7外接SDRAM时,除BWSCON和BANKCONx(x为6、7)外,还要设置REFRESH、BANKSIZE、MRSRB6、MRSRB7等4个寄存器。

设计NOR FLASH

BANKCON0寄存器

第一步,设置BANKCON0寄存器(NOR FLASH 接在片选0)
在这里插入图片描述
然后设置Tacc,上面已经讲解了,若是把Tacc设置大于70ns就可以了,所以我们只管Tacc

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210617155937273.png?x-oss-process=image/wat
可以看到Tacc上电初始值是111,对应14个clocks。系统上电采用12MHz的晶振,HCLK=12MHz,Tacc=(1000/12*14)≈1166ns,这个值很大,几乎可以满足所有Nor Flash的要求。

启动后,将HCLK设置为100MHz,T=1000/100=10ns,Tacc需要大于等于70ns,因此设置Tacc等于101,8个clocks即可。

在这里插入图片描述
这个默认就行

程序测试

init.c

#include "s3c2440_soc.h"
void bank0_tacc_set(int val)
{
    BANKCON0 = val << 8;
}
#ifndef _INIT_H
#define _INIT_H
void bank0_tacc_set(int val);
#endif
#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"

int main(void)
{
    unsigned char c;

    uart0_init();
    puts("Enter the Tacc val: \n\r");

    while(1)
    {
        c = getchar();
        putchar(c);
        if (c >= '0' && c <= '7')
        {
            bank0_tacc_set(c - '0');
            led_test();
        }
        else
        {
            puts("Error, val should between 0~7\n\r");
            puts("Enter the Tacc val: \n\r");
        }
    }
    return 0;
}

SDRAM工作原理

SDRAM的内部是一个存储阵列。阵列就如同表格一样,将数据“填”进去。在数据读写时和表格的检索原理一样,先指定一个行(Row),再指定一个列(Column),我们就可以准确地找到所需要的单元格,这就是内存芯片寻址的基本原理,如图所示。
在这里插入图片描述

在这里插入图片描述
这个单元格(存储阵列)就叫逻辑 Bank(Logical Bank,下文简称 L-Bank)。 由于技术、成本等原因,不可能只做一个全容量的 L-Bank,而且最重要的是,由于SDRAM的工作原理限制,单一的 L-Ban k将会造成非常严重的寻址冲突,大幅降低内存效率。所以人们在 SDRAM内部分割成多个 L-Bank,目前基本都是 4个(这也是SDRAM规范中的最高L-Bank数量),由此可见,在进行寻址时就要先确定是哪个 L-Bank,然后在这个选定的 L-Bank中选择相应的行与列进行寻址。因此对内存的访问,一次只能是一个 L-Bank工作。 SDRAM总共有4个块(Banks),可以认为每个块就是一个表格,里面的每个格子表示的是16bit数据。

问题1:怎样访问里面的某个格子呢**

  • 首先发出一个片选信号,选中整个芯片;
  • 发出Bank地址,选择是哪一个Bank(块,即表格);
  • 发出行地址;
  • 最后发出列地址,才能选中是个格子;

问题2:那么多的信号有谁发出呢?

由内存控制器发出,所以我们需要设置内存控制器,CPU只是简单的执行读写内存的命令,其他的都交给内存控制起来处理。

LDR R0,=0x30000000 
LDR R1,[R0]

1、CPU把0x30000000这个地址发给内存控制器,内存控制器根据这个地址,判断属于哪个范围,然后发出相应的片选信号。
2、根据类型(比如SDRAM)拆分成三部分:发出Bank地址、发出行地址、发出列地址。

问题3:那问题是怎么拆分呢??

行地址线有几条,列地址线有几条,都要设置内存控制器里面相关寄存器。

读数据

综上所述:对SDRAM的访问可以分为如下步:

  1. 控制发出的片选信号nSCS6有效,它选中SDRAM芯片
  2. SDRAM中有4个L-Bank,需要两根地址信号来选中其中一个,根据原理图,可知使用ADDR24,ADDR25作为L-Bank的选择信号
  3. 对被选中的芯片进行统一的行/列(存储单元)寻址。
    3.1. 根据SDRAM芯片的列地址数目设置CPU的相关寄存器后,CPU就会从32位的地址中自动分出L-Bank片选信号,行地址信号,列地址信号,然后先后发出行地址信号,列地址信号(也就是说你只需要设置寄存器告诉CPU你的SDRAM列地址数目是多少,CPU就会自动的把地址分为L-Bank片选信号,行地址信号,列地址信号三部分,并按顺序发出)。L-Bank选择信号在发出行地址信号的同时发出,并维持到列地址信号结束。
    3.2. 根据原理图可知:行地址、列地址共用地线ADDR2—ADDRI4(BANK6位宽为32,ADDRO/I没有使用),使用nSRAS、nSCAS两个信号来区分行地址、列地址。(注:这里的bank6,翻译来说就是堆,可以理解为设备即SDRAM(nGCS6),对应nGCS6片选信号,并对应2440中的BANKCON6寄存器。注意这里说的bank不是SDRAM里面的L_Bank)(注:SDRAM即BANK6的位宽是32,是两个16位的SDRAM拼接的)
    比如本开发板中,使用两根地址线ADDR24、ADDR25作为L-Bank的选择信号SDRAM芯片K4s5m632的行地址数为13,列地址数为9,所以当nSRAS信号有时,ADDR2—ADDR14上发出是行地址信号,它对应32位地址空间的bit[23:11]:当nSCAS信号有效时,ADDR2—ADDR10上发出的是列地址信号,它对应32位地址空间的bit[10:2];由于BANK6以32位的宽度外接DRAM,ADDR0、ADDR1恒为0,不参与译码。
  4. 找到了存储单元后,被选中的芯片就要进行统一的数据传输了。
    开发板中使用两片16位的SDRAM芯片并联组成32位的位宽,与CPU的32根数据线(DATA0—DATA31)相连。 BANK6的起始地址为0x30000000,所以SDRAM的访问地址为0x30000000~低0x33FFFFFF,共64MB。

设置SDRAM

2440内存控制器设置:

  • 内存控制器共有13个寄存器,

  • BANK0–BANK5只需要设置BWSCON和BANKCONx(x为0~5)两个寄存器;

  • BANK6、BANK7外接SDRAM时,除BWSCON和BANKCONx(x为6、7)外,还要设置REFRESH、BANKSIZE、MRSRB6、MRSRB7等4个寄存器。

下面分类说明各个寄存起的设置。

1. BWSCON位宽寄存器

位宽和等待控制寄存器BWSCON(BUSWIDTH&WAITCONTROLREGISTER)
在这里插入图片描述
BWSCON中每4位控制一个BANK,最高4位对应BANK7(没有使用)、接下来4位对应BANK6。

  • (1)ST6[27]: 启动/禁止SDRAM的数据掩码引脚,对于SDRAM,此位为0:对于SRAM此位为1。

  • (2)WS6[26]:是否使用存储器的WAIT信号,通常设为0。

  • (3)DW6[25:24]:使用两位来设置相应BANK的位宽,0b00对应8位,0b01对应16位,0b10对应32位(开发板用的就是32位的),0b11表示保留。

因此BWSCON寄存器的值为:0x22000000。(这里似乎吧bank7的DW位也写进去了)

2. BANK控制寄存器BANKCON

在8个BANK中,只有BANK6和BANK7可以外接SRAM或SDRAM。

在这里插入图片描述
(1)MT[16:15]:用于设置本BANK外接的是ROM/SRAM还是SDRAM,SRAM:0b00,SDRAM:0b11(开发板使用的是SDRAM)。

(2)Trcd[3:2]:行地址和列地址间隔多长时间,看芯片手册时间间隔是20ns,本来开发板的HCLK是100MHZ,clocks为10ns,所以设置为推荐值0b00(2clocks)。

(3)SCAN[1:0]:SDRAM的列地址位数,对于本开发板使用的SDRAMK4S561632。列地址位数为9,所以SCAN=0b010如果使用其他型号的SDRAM,需要查看其数据手册。来决定SCAN的取值。0b00表示8位,0b01表示9位,0b10表示10位。

3. 刷新控制寄存器REFRESH(REFRESHCONTROLREGISTER)

在这里插入图片描述

  • (1)REFEN[23]:0=禁止SDRAM的刷新功能,1:开启SDRAM的刷新功能(设置开启SDRAM的刷新功能)。

  • (2)TREFMD[22]:SDRAM的刷新模式,0=CBR/AutoRefresh,1=SelfRefresh(一般在系统休眠时使用),我们设置默认值。

  • (3)Trp[21:20):根据芯片手册设为0即可。

  • (4)Tsrc[19:18]:根据芯片手册设为默认值0b01即可。

  • (5)RefreshCounter[10:0]:即R_CNT

    • R_CNT可如下汁算(SDRAM时钟频率就是HCLK):
      R_CNT=2^11+1-SDRAM时钟频率(MZ)*SDRAM刷新周期(us)
      SDRAM的刷新周期在SDRAM的数据手册上有标明,在本开发板使用的SDRAM:K4S561632的数据手册上,可看见这么一行“64msrefreshpenod(8KCycle)所以,刷新周期=64ms/8192=7.8125us。
      Refreshcount=2^11+1-100x7.8=1269=0x4F5。

因此,本开发板中REFRESH设为0x8404F5。

4. BANKSIZE寄存器REFRESH(BANKSIZEREG ISTER)

在这里插入图片描述

  • (1)BURST_EN[7]:0=ARM核禁上突发传输,1=ARM核支持突发传输(推荐);

  • (2)SCKEEN[5]:0=不使用SCKE信号令SDRAM进入省电模式,1=使用SCKE信号令SDRAM进入省电模式(推荐);

  • (3)SCLK-EN[4]:0=时刻发出SCLK信号,1=仅在访问SDRAM期间发出SCLK信号(推荐);

  • (4)BK76MAP[2:0]:设置BANK6的大小。本开发板BANK6外接64MB的SDRAM,令[2:0]=b001(64M/64M),表示BANK6/7的容量都是64MB,虽然BANK7没有使用。

因此,本开发板中BANKSIZE设为0xB1。

5. SDRAM模式设置寄存器MRSRBx6(SDRAM MODE REGISTER SET REGISTER)

在这里插入图片描述

  • 能修改的只有位CL[6:4],这是SDRAM时序的一个时间参数,表示发出行、列地址后,等多久才返回收到数据
  • CL可以取值为0b0l0(2 clocks)或0b011(3 clocks)。

本开发板取最保守的值0b010,所以MRSRB6的值为0x20。

SDRAM程序如下

下面开始写程序:
在init.c里面进行对内存控制器的寄存器依次进行设置:

void sdram_init(void)
{
    BWSCON = 0x22000000;
    BANKCON6 = 0x17001;
    BANKCON7 = 0x17001;
    REFRESH  = 0x8404f5;
    BANKSIZE = 0xb1;
    MRSRB6   = 0x20;
    MRSRB7   = 0x20;
}

再写一个测试函数,向SDRAM里面连续写1000个数,再读出数据对比是否是设置的数,返回对比结果:

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

在主函数里调用sdram_test()测试函数,如果测试成功,LED闪烁:

int main(void)
{
    uart0_init();

    sdram_init();

    if (sdram_test() == 0)
        led_test();

    return 0;
}

实验结果:

LED按预期闪烁,屏蔽掉sdram_init()后,LED不闪烁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值