摘要:(1)本文设计结果是:自己设计一个SDRAM的接口模块,能够通过控制该接口模块实现对sdram的读写;(2)如果要控制该接口模块要遵循Avalon协议;(3)该接口模块的实现不是绝对的,因为在quartus里面有一个内嵌的ip核(别人已经写好的),自己写一个接口模块是为了熟悉sdram的存储方式;(4)本文主要讲解该模块的控制sdram的原理。
一、SDRAM的器件介绍
1、硬件介绍:
(1)引脚介绍:器件图
单个引脚介绍:
列地址:A0~A12
行地址:A0~A9
Bank块选择:BA1,BA0
数据引脚:D0~D15
控制引脚:CS_N(片选)、RAS_N(行选通)、CAS_N(列选通)、WE(读写控制)
数据有效引脚:UDQM;(一般都是11,表示数据都有效即可,实在不明白直接不管)
(2)SDRAM的存储方式(硬件结构)
介绍:可以把SDRAM看成一个有很多个“坑”的器件,我们选择哪个坑,把该坑打开,然后往该“坑”放入数据,就表示把数据存储在这个坑。SDRAM又有4个bank,表示有四片“坑”。
实现:那么可以通过BA线选择哪个bank,然后通过行地址线选择哪一行,最后通过哪一列选择那个坑。
注:sdram的硬件储存是电容方式,如果长时间不充电,那么数据就会丢失,所以要有间断的刷新。
(3)引脚作用
BA1,BA0:选择SDRAM的哪个Bank,SDRAM一共四个bank;
A0~A12: 行地址;
A0~A9: 列地址
D0~D15:数据线:
CS_N(片选)、RAS_N(行选通)、CAS_N(列选通)、WE(读写控制):组成4位宽的一个控制信号,控制sdram的各种活动,如:预充电,寄存器配置,行激活等命令的发送。
2、控制逻辑介绍:
(1)模块的状态转移图:
(2)状态转移解读:
1、首先:SDRAM启动有一个初始化(相当于有起床气,不能直接进入工作)———红色框所示;初始化只用进行一次(起床气撒气一次就开始工作),初始化干啥事:
第一步:等待100us(发神100us);那么引脚要做的事:
控制引脚:发送NOP指令给SDRAM,表示现在是空闲,啥事不用干,且一个周期发一次NOP指令,指示sdram空闲。
其他引脚:随便你;(因为NOP就表示不接受其他引脚)
第二步:预充电(把所有坑给盖上),引脚要做的事:
控制引脚:先发一个周期预充电指令,然后等待一段时间(关闭坑是需要时间的),在等待时间之内,每个周期发送NOP指令。
其他引脚:在第一个周期(发送预充电命令的那个周期),可以选择对应BANK预充电,也可以不选择,直接全bank预充电。其他引脚:随便你。其他周期:所有引脚随便你。
第三步:刷新(给电容充电,没电怎么储存)。引脚要做的事:
控制引脚:第一个周期发送刷新命令,其他周期每个周期发送NOP命令等待刷新完成(给电容充电完成)。
其他引脚:随便你;
第四步:配置模式寄存器,最最最重要(这个就和你起床穿衣服,配置你今天穿啥一样重要)
控制引脚:第一个周期发送配置模式寄存器命令,之后要等待一段时间(穿衣服要时间),等待时间发送NOP指令。
A0~A12:第一个周期配置要求:A0~A9对应一下表配置给值(A7,A8一般就是给00就可以配置好运行模式),A10~A12不用管,直接全给0就可以了。
配置结果:突发长度:1,列选通潜伏期:3;
解读:突发长度:一次性能进行多少个数据的读写。
列选通潜伏期:给一次读的信号,多个周期之后才会出来有效数据。
时序图:
2、初始化之后就到一个工作的状态,但是由于SDRAM的储存特性,必须要对SDRAM时不时的进行一次充电(可以想象成,开始工作了,但是你要时不时的去上厕所,正常一天要上个8次),正常间隔7.8us充电一次,且一次充电要7个周期。
第一步:当刷新时间间隔来了(可以用计数器计满7.8us表示可以开始刷新了):引脚
控制引脚:第一个周期发送刷新命令,接下来6个周期发送NOP指令,表示空闲等待。
地址引脚:随便你;
第二步:在没有刷新的时候,可以进行读写操作了。
3、对于读写状态:
(1)写入时序:在之前的配置之中,我们设置的是突发读写都是1(发送一次命令值写入一个数据),所以想写入100个数,那么就要发送100个命令和对应100个数据;
写入时序:首先激活一行(表示打开这一行的坑,方便我放东西),然后通过列地址存入相应数据。引脚配置:
控制引脚:第一个周期发送激活命令,然后等待一段时间(激活需要时间),在等待时期发送NOP指令。等待结束后发送写入指令,要写入几个数据,那么就要发送几个周期的写入命令(两个写入命令之后不需要间隔)。之后就是等待写入完成写入(连续发送完100个数据之后)(放入坑中是需要时间的),等待期间发送NOP;
地址引脚:在发送激活命令的时候发送A0~A12选择激活哪一行,在发送写入命令的时候A0~A8表示输入储存哪一列(要注意一个点列选的时候A10要 = 0),其他时刻其他引脚随便你。
数据引脚:在发送写入命令的时候发送对应数据,其他时候随便你。
数据有效引脚:在发送写入命令之后,有个的等待时间,叫列写入时间,此时DQM要 = 00。(从等待时间开始,也就是发送nop命令的时候要一直发送DQM = 0,表示我不要接受数据了)
时序图:(下面时序图是突发读写,不看writte之后的nop即可,把nop想象成wirte命令就是突发长度为1的读写)
注:在写完之后要进行一次预充电,目的是为了关闭这一行(把这个坑盖上),方便下一次进行读写的时候能打开一个行。
注:SDRAM有一个特性:激活了一行(打开了一行的盖子),那个其他行就不能进行激活了,也操作不了其他行,所以我们要读写完就进行预充电关闭。
(2)预充电:目的:关闭这一行
引脚配置:
控制引脚:第一个周期发送预充电命令,之后等待时间,在这个等待期间,发送NOP指令。
地址引脚:A10 = 1控制全刷新,这个时候其他地址线随便你,A10 = 0控制选择bank刷新,BA10表示与关闭哪个BANK。
其他引脚:随便你;
(3)读出时序:
操作时序:首先发送一个行激活命令,再发入写命令。
控制引脚:首先行激活命令,然后等待。再发送读取命令,读取一个数据发送一个命令,然后等待
3个周期就可以陆续有数据出来(列选通潜伏期)。—因为上文设计突发长度是1,所以我一个命令就只能读取一个地址的数据。
地址引脚:首先选择那一行激活,然后选择哪一列读出。其他的随便发。
数据引脚:在发送完读取命令之后的三个周期之后就可以收到数据了
其他引脚:随便你;
时序图:下图也是一个突发读写的时序图,把NOP指令看成读取指令就可以了,在每一个读取指令都要发送对应的地址。
(4)总结:(突发长度设置为1,潜伏期是3)
1、一个完整的写入是:行激活——等待时间——写入命令(同时发送命令和列地址,写入多少个就发送多少个地址和命令)——等待写入时间——预充电——等待时间——回到IDLE。
2、一个完成的读取是:行激活——等待时间——读取命令(同时发送命令和列地址,读出多少个就发送多少个命令和地址)——预充电——等待时间——回到IDLE。
注:读取之后没有等待时间,直接进入预充电即可,只是在接收数据的时候要等待3个周期就可以了,数据是不是因为直接预充电而导致错误。
3、回到IDLE之后:如果刷新时间到了,我们进行刷新。
4、初始化之后就不会再进行初始化了,停留在IDLE做其他事。
有不同意见的,兄弟们来私聊,仅仅供自己学习所用。代码就不做提供,如果有想要的可以私聊。
没有回复内容