继续死磕SDRAM控制器
SDRAM控制器
博主上一篇介绍了一些SDRAM的基本原理是否有必要学习使用纯Verilog写一个SDRAM控制器,接下来记录SDRAM控制器的工作原理。首先是上电初始化。
上电初始化
时序图中,tRP、tRC、这些时间参数可以从手册中找到,这里的系统时钟采用50Mhz。
从初始化的时序图可以看出,首先在进行预充电(Precharge)命令之前要等待100us(手册要求是至少100us,我们设定延时200us),等待系统上电稳定和时钟稳定,然后对所有bank进行预充电(Precharge),经历一个trp(20ns,一个时钟周期,手册可以查询)时间,然后进行至少两次自刷新(Auto refresh)(我这里设置进行8次自刷新,),每次自刷新至少需要trc(63ns,四个时钟周期,手册查询)时间,最后进行模式寄存器(MODE register)的配置,需要tmrd(两个时钟周期)时间,初始化完成。
预充电时当A10为高电平对所有的bank进行操作,当A10为低电平时对单个bank进行操作,BA0,BA1选择bank。我们这里预充电时对所有的bank操作,把A10置高即可。
SDRAM初始化流程
- 上电后延时200us
- 对所有的bank进行预充电(Precharge)
- 8个自刷新操作,每次自刷新使用四个时钟周期
- 进行模式寄存器的配置 ,配置完后输出初始化完成标志。
这里先设置SDRAM的突发长度为4 Addr = 12’b0000_0110_0010
SDRAM初始化仿真
这里仿真需要用到SDRAM的仿真模型,通过仿真模型可以把当前SDRAM进行的操作打印出来,如果有错误也会提示错误,这时候再去查看波形时序。这里需要注意的是仿真的时候sdram_dqm信号必须和仿真模型连接,否则数据是写不进去的,设置sdram_dqm = 0就可以了。
将SDRAM仿真模型添加进去后,要对放着模型的参数进行重定义
对于各个参数的值,不同的SDRAM芯片的参数是不同的,具体根据手册而定,mem_sizes设置的是1个bank的容量。Verilog语法笔记:这种方式可以对例化模块里面已有的宏定义进行重定义,写法是defparam + 例化之后的模块名+ . +需要重定义名。
然后在transcript中输入run 200us,先跑200us等待系统上电稳定,在输入run 50us,如上图所示CAS Latency(列选通潜伏期)为3,burst length(突发长度)为4,burst type(突发类型)顺序(Sequrntial)执行,模式寄存器配置都按照我们设置的进行。
SDRAM工作原理
初始化模块设计完成后,对SDRAM已经有了一些简单的了解,下面先来看看SDRAM的工作原理。
粗线——自动跳转,细线——受到命令后才能跳转(看不清楚图片点击阅读原文)
从一个官方给的SDRAM手册的参考状态转移图看,我们可以简单写一下SDRAM的工作流程。这部分用状态机来实现最好不过了。
上电后,给一个Precharge命令,进入Precharge状态,然后自动进入IDLE(初始化)状态,然后给自刷新命令,进入自刷新状态,自刷新完后回到IDLE状态,IDLE状态给一个配置模式寄存器命令,进入模式寄存器的配置,配置完后才能自动回到IDLE状态,然后进行读写状态的操作。
以写操作为例,需要给一个行激发命令,进入行激发状态,给出写命令才能进入写状态,写完后自动返回行激发状态,给一次写命令,会写入一个突发长度的数据,在一行没有写完不需要跳回行激发状态,只需要继续给写命令,只有当一行写完或刷新请求来临或数据写完才会跳出写状态,这里后面设计写模块再讲。数据写完或刷新请求来临或数据写完,需要先进入预充电状态,给一次预充电命令,然后自动跳转到初始状态进行重新等待命令。至于读模块的操作和写模块是完全一样的,读者自行读图。
这里要提的是图中有两个状态WRITEA和READA,这里我们不需要用到,如图也可以看到,这两个状态在进行一次读或写后会自动跳入预充电状态,从而回到初始化状态,这样和WRITE和READ这两个状态相比,读写速度肯定是会慢的,WRITE和READ可以连续给读写命令进行读写,所以直接忽略掉这两个状态不管。
仲裁机制
这里要引出一个设计技巧,初始化完成后,进行自刷新和读写操作状态都是相互独立的,所以我们需要写一个状态机去完后这些状态之间的跳转,大概意思就如下图所示,写一个仲裁状态机,当SDRAM控制器要进行自刷新时,自刷新模块需要给一个刷新请求,仲裁状态收到后,就结束当前进行的状态,给一个刷新使能,刷新模块才会进行刷新操作。同样的,当SDRAM控制器要进行写数据时,写数据模块需要给一个写请求,仲裁状态收到后,就结束当前进行的状态,给一个写使能,写数据模块才会进行写操作。后面的模块相应的也是这样的设计。
刷新模块
手册规定SDRAM自刷新需在64ms内刷新4096次(不同型号的芯片对应的不同),必须64ms是因为SDRAM内部使用电容存储数据的,它保证不断电的时间就是64ms。4096次的意思是,我所使用的这款SDRAM芯片它的行地址为A0~A11,一共是12位,2的12次方一共是4096行,我们每给一次刷新命令实际上是刷新一行,且是四个bank同时刷新,所以说一共要刷新4096次。两次刷新间隔15us。刷新是在SDRAM初始化完成后就要开始进行刷新。
从时序图可以看出,先给一个precharge命令(一般都是对all bank进行操作),经过trp(20ns,一个周期)时间进行一次自刷新命令,再过trc时间,进行再一次自刷新命令,然后trc(63ns, 四个周期)时间后激发读或写命令。
在时序图中我们看到了两次自刷新命令,但是实际上只要给一次自刷新命令即可,所以不要被时序图忽悠了,当然给两次也是没有什么关系的。这里的意思是,每次进行自刷新操作都需要给一次预充电即可。
刷新模块仿真
仿真的时候发现我犯了一个错误,就是每15us的刷新操作是不用每次都给预充电(Percharge)命令,但是从另一个状态跳转到自刷新状态是需要给一个预充电(Percharge)命令。之后便不需要再给了,这上面这里的原因是因为在刷新模块里有预充电(Percharge)命令,所以每次状态跳转到执行刷新模块,都会给一个预充电(Percharge)命令。这个问题已经得到解决。如图每15us进行一个自刷新。
关于SDRAM的读模块和写模块操作,下一篇再写。博主最近基于上次50Mhz下的简易SDRAM控制器的基础上修改成100Mhz的SDRAM控制器,实现用上位机串口发送一副彩色图片到SDRAM存储,再用VGA显示,下一步的目标是实现摄像头实时采集视频流数据显示。目前把最近写的这两个项目工程放到了Github上分享出来,希望能一起讨论,多多指点,这个东西我个人感觉不懂内部的操作时序,想要移植也是很麻烦的,所以干脆直接分享出来,后面博主会继续优化,尽量做成一个像IP Core一样的直接修改参数就可以调用的一个SDRAM控制器。
https://github.com/NingHeChuan/Open-FPGA.git
转载请注明出处:NingHeChuan(宁河川)
个人微信订阅号:开源FPGA
如果你想及时收到个人撰写的博文推送,可以扫描左边二维码(或者长按识别二维码)关注个人微信订阅号
知乎ID:NingHeChuan
微博ID:NingHeChuan
原文地址:http://www.cnblogs.com/ninghechuan/p/8992683.html