SDRAM理论篇之基础知识及操作时序讲解

作者: Kevin 分类: FPGA学习笔记 发布时间: 2016-01-12 21:03

这个星期在进行SDRAM的学习,当然关于SDRAM的理论知识,在上周讲了一部分,而这周主要的还是写代码及调试。如果有朋友在这之前没有接触过内存的话,可以看下业内写的比较好的扫盲文章《高手进阶 终极内存技术指南》(点击进入下载),虽然Kevin在这之前也曾经看过这篇文章,但是看完之后对于写代码还是毫无头绪,所以Kevin在这里建议大家,如果是完全不知道内存是神马的朋友可以看下这篇文章,对于一些有基础但是不了解SDRAM操作时序的朋友可以直接看SDRAM厂商的官方文档,虽然Kevin也会在这篇博文中会根据自己对官方手册的理解来讲解SDRAM操作时序,但还是建议大家多看英文文档,这样才能体会原汁原味的知识,这也是为了避免所谓的“权威”中文版给大家带来某些误导,毕竟也是会有一些朋友在看着“权威”的中文翻译版本却在不停的骂娘,哈哈。所以大家也不要轻信所谓的中文翻译版手册,因为每个人对于手册中的英文意思的理解都有可能有区别,所以大家要相信厂商的手册才是最权威的。

接下来,咱们正式开始讲解SDRAM的有关知识。

SDRAM(Synchronous Dynamic Random Access Memory),同步动态随机存储器。同步是指 Memory工作需要同步时钟,内部的命令的发送与数据的传输都以它为基准;动态是指存储阵列需要不断的刷新来保证存储的数据不丢失,因为SDRAM中存储数据是通过电容来工作的,大家知道电容在自然放置状态是会有放电的,如果电放完了,也就意味着SDRAM中的数据丢失了,所以SDRAM需要在电容的电量放完之前进行刷新;随机是指数据不是线性依次存储,而是自由指定地址进行数据的读写。

关于SDRAM的内部结构

我们借用《高手进阶 终极内存技术指南》中的部分文字来进行讲解:

SDRAM内部其实是一个存储阵列,大家可以想象一下,如果SDRAM内部不是以阵列的形式存在而是以管道的形式存在,那SDRAM是很难做到随机访问的。阵列就如同一张表格一样,将数据填进去。和表格的检索原理一样,先确定一个行(Row),再确定一个列(Col),我们就可以准确的找到所需要的单元格,这就是内存芯片寻址的基本原理。对于内存,这个单元格可以成为存储单元,那么这个表格(存储阵列)叫什么呢?这就是逻辑Bank (Logic Bank,下文简称Bank).

sdram_bank

看到这里,大家应该不难想象,我们对SDRAM进行寻址的方式为:先确定Bank的地址,再确定行和列的地址。

SDRAM内存容量计算方式

关于内存的计算,我们先看下手册sdram_addr

图中的128Mb表示SDRAM的总容量,2M:表示1个Bank共有2M个地址,也就是说一个bank有2M个存储单元;4Bank表示有4个Bank;16 I/O表示位宽为16位,数据总线用的是16位的,1个存储单元能存储的最大的数为2^16-1=65535。然后再根据各个参数算下容量:2M x 4 x 16b = 128Mb .

SDRAM芯片引脚介绍

第一个图是SDRAM的引脚图,第二图是各个引脚信号的描述信息

sdram_pin

pin_description

上面的两个图在SDRAM芯片的手册上都是可以找到的,下面Kevin来简单的介绍下我们做FPGA开发需要重点关注的信号

CLK:SDRAM工作的时钟,并且所有的输入信号都是在CLK的上升沿进行检测的,也就是说我们给SDRAM给的任何命令,一定要在CLK的上升沿保持稳定,以免SDRAM获取我们给出的命令时出现错误。

CKE:时钟使能信号,是用来控制SDRAM内部时钟是否工作的一个信号(在SDRAM内部也是有时钟的哦)

CS:片选信号,这里需要注意的是,如果要对SDRAM进行操作,必须要将片选信号拉低

BA0,BA1:Bank地址线,用来给bank的地址,可以控制SDRAM的4个bank

A0~A11:地址线,当我们选择SDRAM某个Bank的Row地址的时候,需要到12根地址线(A0~A11);当选择Col地址的时候,只用A0~A8这9根线;A10这个信号可以用来控制Auto-precharge。

RAS、CAS、WE:这三根线就是用来给SDRAM发命令的,包括初始化、读、写、自动充电等命令。

UDQM、LDQM:数据输入/输出掩码。

DQ0~DQ15:SDRAM的数据线,为双向的,向SDRAM写数据或者从SDRAM中读出来的数据都是在DQ上进行传输的

大家看到上面的A0~A11这根地址总线既可以来控制row也可以控制col,而且控制col地址与控制row地址用到的线的数量又不一样,可能会有些疑问,下面Kevin向大家解释一下:

SDRAM的厂商一般为了节约成本,采用同一总线来对SDRAM进行寻址是无可厚非的,对于Row地址用到了12根线,也就是总共有2^12=4096个Row地址,而col地址使用9根线,也就是有2^9=512个col地址,而总共加起来的话,一个Bank就有4096×512=2097152 (2x1024x1024)  ,也就是有2M个地址,这样的话,就与我们前面计算SDRAM的容量想吻合了。

另外一个的话,可能大家不太明白掩码(UDQM、LDQM)的作用,下面请您耐着性子听Kevin的解释:

就拿咱们使用的这块SDRAM芯片来讲,数据线有16根,也就是说明我们数据的位数可以达到16位,但是呢,请注意,也许在我们使用SDRAM的时候,也许我们在向SDRAM写数据的时候,我们生成的数据只有8位,但FPGA是与SDRAM的16根数据线连在一起的,这个时候,存到SDRAM中的数据还是16位的,所以为了避免这个问题,我们就可以使用掩码来屏蔽掉高8位了。当然掩码在读数据的时候起到的作用也是类似的。

SDRAM的相关操作时序

前面铺垫了这么多SDRAM的相关知识,相信大家已经迫不及待想知道怎么来操作SDRAM了,各位看官且听Kevin详细道来。

sdram_state

上图是SDRAM内部的状态跳转图(可能在网页上看到的图片不太清晰,大家可以在导航菜单“福利”下的“文档手册”中下载这些文档),粗黑线表示在该状态下会自动跳转到另一个状态,细黑线表示需要给命令才会跳转。

SDRAM初始化

我们先找到SDRAM最开始的状态“POWER ON”,这是刚上电的状态。在“POWER ON”状态给‘Precharge’命令之后就会跳转到“Precharge”状态,然后自动跳转至“IDLE”状态。在“IDLE”状态下,我们需要给SDRAM两次Auto-refresh命令,然后接着需要进行模式寄存器设置,对模式寄存器设计完毕之后,我们的初始化过程也就结束了。说了这么多,我们还是不知道怎么给SDRAM进行初始化,当时Kevin看过《高手进阶 终极内存技术指南》之后也是依然不知道怎么写代码对SDRAM进行初始化。在这篇博文里边,Kevin暂时不会讲如何写代码,我只会把初始化这个过程相关的时序及原理给大家讲清楚,原理理解透彻了,相信大家对于代码如何进行书写也是会有一定的想法的,关于代码如何书写,也可以参考Kevin的另一篇博文。

我们继续上一张官方文档中的图片,这次来的可以波形图哦,各位看官要看仔细咯,对于你写代码是很有帮助的:

sdram_init_timming

可能大家第一次接触到这种时序图的时候会有点头疼,不过没关系,请听Kevin给大家慢慢解释,在看文档解释的同时,与上边的状态图结合起来会更容易理解一点,对于图中出现的每一个指令,我们都是可以在我们所使用的器件手册中找到:

根据官方文档的介绍,SDRAM在刚上电的100us期间,除了“COMMAND INHIBIT”或“NOP”是不可以给SDRAM发其他指令的,所以大家能看到发送“Precharge”命令与刚上电的时间间隔“T”最小值为100us,这个100us其实也相当于SDRAM的一个稳定期。看过《高手进阶 终极内存技术指南》这篇文章的朋友可能会发现,在《高手进阶 终极内存技术指南》上写的稳定器是200us,而这里出现的却是100us,这样岂不是出现了矛盾,Kevin刚看手册的时候,也出现了这个疑问。我们可以肯定的一点是,官方的手册出错的可能性是很小的,如果连官方的文档都出错了,那还让我们这些使用他们器件的人怎么活呢 ╭( T □ T )╮。那会不会是《高手进阶 终极内存技术指南》的作者写错了呢?这么久经考验的经典文章,如果有错误,肯定是早就提出来了。最终,Kevin在官方手册上找到了答案:0@[]7E~02L}W$56V1NH89XT

对于这段英文,Kevin的理解是:在200us期间,SDRAM是都有可能执行“COMMAND INHIBIT”或“NOP”命令的,所以这里我们就索性把稳定器弄成200us。(如果理解出错,请大家批评指出)

稳定器过了之后,我们先给“Precharge”命令,然后再过“tRP”的时间再给“Auto Refresh”命令,然后再过“tRC”的时间再给“Auto Refresh”命令,然后再经过“tRP”的时间进行模式寄存器设置。在给命令的时候,我们需要注意,“Precharge”、“Auto Refresh”、”Load Mode REGISITER“这三个命令都是只出现了一次的,没有给这些命令的时候都是给的”NOP“命令。

给”Precharge”命令时,我们需要指定A10及Bank地址,如果A10为高(All Banks),就意味着是给所有的Bank进行预充电,此时不需要给Bank地址,如果A10为低(SINGLE BANK),就需要指定某一个bank的地址。

给”Auto Refresh“命令时,是不需要指定bank地址的(大家注意看右下角有说明,灰色部分的数据我们是不需要关心的)。

进行模式寄存器设置的时候,需要给的指令会稍微复杂一点,手册上显示A0~A11及BA0,BA1都用到了,下面我们来看下模式寄存器应该怎么进行设置,请看图:mode_register_set

A9:用来指定操作模式:为高表示突发读/突发写,为低表示突发读/单写;

A4~A6:指定潜伏期的长度,关于潜伏期,《高手进阶 终极内存技术指南》上有非常详细的介绍,不过潜伏期设置的实际效果就是,如果潜伏期设置的是3,在我们进行SDRAM读操作的时候,读出来的数据就会相对于”READ”命令延后3个周期出来,如果潜伏期是2,那就会延后2个周期。

A3:设置突发的类型,连续型和非连续型。

A2~A0:用来指定突发的长度。

对于上面的解释,大家可能会由于不懂突发是什么会觉得Kevin这里边写得可能还是显得不够直白,下面Kevin举个例子:A9设置为0,潜伏期设置为3,突发类型A3为0,突发长度(A2~A0)设置为4,在我们进行写操作的时候,数据是每4个数据写一次的,就是说我们给一次写指令,就会向SDRAM写进去4个数据,而且四个地址是连续的(如果突发类型设置的是非连续,则地址不会连续,具体的地址会是怎么样的一种规律,Kevin自己也没搞懂);在我们进行读操作的时候,在给完一次读命令的3个周期(潜伏期为3)后,会有4个数据连续的崩出来.

至此,对于初始化的过程,到这里算是基本讲完了。

SDRAM写操作

在状态转移图中,我们可以发现在初始化完成之后,对SDRAM进行读或者写操作之前,我们还需要有一个命令”ACT”(这个命令在我们的初始化时序图中也出现了,只是我们是拿到了这里来讲),这个命令的意思说成大白话就是”行有效”命令,就是让SDRAM中的某一行活动起来,以便我们进行读或写。

在正式讲写操作之前,还有一点希望大家能注意一下,可能细心的朋友已经发现了,在我们的状态转移图中有”WRITE”和”WRITEA”这两个状态,处于这两个状态时,我们都可以对SDRAM进行写操作,只是说在”WRITEA”状态,我们每写完一个突发长度的数据之后,SDRAM会自动跳出这个状态进行刷新,而在”WRITE”状态,是需要给相应的指令之后才会跳出”WRITE”状态的,所以为了提高SDRAM的运行速度,我们一般采用不让SDRAM进入”WRITEA”状态来提高速度。当然”READ”和”READA”这两个状态的区别也是这样的。

下面我们继续上一张写操作的时序图,这个时序图是不进入”WRITEA”状态的:write-without_autoprecharge

按照老套路,Kevin继续和大家一起来分析写操作的时序:

在初始化完成后的 “tMRD” 之间之后,给一个”ACT”命令,同时指定是哪一个bank的哪一行,然后再经过 “tRCD”的时间给“WRITE”指令,同时指定是bank地址和列地址并把A10拉低,然后再根据设定好的突发长度将对应数量的数据写进去,这样就完成了我们的写操作。写完四个数据之后干嘛呢,这个就完全是我们自己说了算,如果我不给SDRAM发任何指令,那SDRAM内部还是处于“WRITE”状态的,如果我还想让SDRAM进行写,那我可以在给SDRAM发送写指令,如果此时刷新SDRAM的时间到了,那我们就必须去执行SDRAM的刷新操作来保证SDRAM的数据不被丢失。

可能看到这里大家会想,如果我正在让SDRAM写数据,是不是SDRAM刷新的时间到了,我就必须是让SDRAM马上执行刷新操作吗?这样的话肯定不是现实的。大家试想一下,SDRAM写数据是每四个每四个数据进行的,如果正在写第二数据却马上让SDRAM执行刷新操作,那必然会把还没写的剩下的两个数据丢失,On  My  God, 我们是断然不能让我们的数据丢失的。不能让我们的数据丢失,又要保证SDRAM进行刷新来保证我们整个SDRAM相应BANK中的数据不被丢失,我们应该怎么来写代码呢?天啊,想到这里,是不是已经有朋友头都要快炸了呢?大家不要慌,关于如何写代码,Kevin会在下一篇博文中进行讲解的,大家先耐着性子把SDRAM的时序彻底弄明白。关于刷新的时序,会在这篇文章的后续部分进行介绍。

写操作到这里就已经基本上说完了。

SDRAM读操作

下面来说说我们的读操作,其实读操作和写操作是类似的,相信大家有了写操作的基础,对于SDRAM的读操作理解起来还是会比较轻松的,咱们继续上图:

首先,依然要给“ACT”命令,记住不要忘了指定ROW地址和BANK地址,然后在给“READ”指令,经过设定好的潜伏期之后,咱们的数据就出来了。大家也不要忘了,在给相关命令的时候,千万不要忘记其他信号线上的信号。

SDRAM的自动刷新操作

Kevin已经在前面多次提到了SDRAM的自动刷新,SDRAM为什么要自动刷新,相信大家通过前面的介绍已经知道原因了,可以说如果SDRAM到了该刷新的时间没有进行刷新,那不管我们给SDRAM写进去了多少数据都是白搭,所以大家一定要高度重视SDRAM的刷新操作,到了该刷新的时候就一定要给刷新的命令。SDRAM内部电容保存数据的最长时间是64ms,而我们一个BANK有4096行,64ms/4096~=15us,也就是说为了保证SDRAM内部的数据不被丢失,两次刷新之间的最大时间间隔为15us,所以为了能让SDRAM有更多的时间进行读或者写,我们就设定SDRAM刷新的周期为15us.

在这里Kevin还想补充一个小知识,SDRAM每进行一次刷新,是对每一行进行操作的,并不是单独针对每一个电容进行充电,所以每进行一次刷新,该行中的电容进行充电我们可以理解为是同步发生的。

auto_refresh

这就是SDRAM进行自动刷新的时序图了,相信大家有了前面的基础,对于这个图应该不会害怕了吧。

在每次自动刷新时,我们需要给一个“Precharge”命令,这个命令有什么作用呢?大家可以看下在前面的那张状态图,如果此时SDRAM正处于“WRITE”或“READ”状态时,这个“Precharge”命令可以使SDRAM跳出“WRITE”或“READ”状态从而进入“IDLE”状态。接下来,经过“tRP”的时间,给一个“Auto-Refresh”命令,注意,我们这里只需要给一次“Auto-Refresh”命令(在电子发烧友论坛正点原子论坛中的帖子中,Kevin写成了给两次“Auto-Refresh”命令,是错误的,希望看到了这篇帖子的朋友注意一下),至此,自动刷新操作就完成了。关于这里的自动刷新操作,和我们的初始化过程有点类似,只是在这里没了模式寄存器的设置。

好了,对于SDRAM的初始化、读、写以及自动刷新,Kevin在这里根据自己的理解已经全部讲完了,如果在这篇文章中出现了某些错误,还希望您及时批评指出。另外,Kevin也会在接下来的一篇博文中分享怎么来写SDRAM的控制器,也就是怎么来把这篇文章中的这些操作用代码写出来。

 

 

参考书籍:

1.《高手进阶 终极内存技术指南》

2. 厂商官方手册

9条评论
  • 斌彬

    2016年9月13日 上午12:25

    能发个电子书吗

    1. Kevin

      2016年9月16日 下午5:13

      您好,SDRAM相关内容暂无编写电子书,您可以前往发烧友课堂观看由我们录制的视频教程http://t.elecfans.com/1418.html

  • frank

    2016年9月19日 上午10:37

    两个fifo的目的是跨时钟域数据缓存

  • yangzi

    2017年4月12日 上午9:01

    纠正下:active应该翻译成“激活”,active row 就是激活行;还有初始化阶段的T,后面标注了是min,也就是最小时间为100us

    1. Kevin

      2017年4月12日 下午11:58

      好的,感谢

  • dasf

    2019年10月12日 下午3:19

    版主的笔记写的真好。非常感谢。

  • sklli

    2019年12月25日 上午10:53

    楼主,文中的这个问题,好像没有解答了。。。求答
    “不能让我们的数据丢失,又要保证SDRAM进行刷新来保证我们整个SDRAM相应BANK中的数据不被丢失,我们应该怎么来写代码呢?”

    1. Kevin

      2020年1月8日 下午3:36

      在博客中有有一篇专门讲SDRAM控制器实现的代码,或者观看SDRAM第一季的视频,有解答的哈

  • Yve

    2021年6月30日 上午9:46

    太棒,学习收藏博主博客,我虽然你不是专门搞FPGA的,但是楼主介绍的SDRAM时序非常有帮助

回复 frank 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注