sdram控制器设计(一)-准备工作

实验背景

本系列分享介绍sdram控制器的设计思路与方法,目的是在设计的过程中提高模块划分,状态机编写,仿真测试,时钟模块处理,复位处理,约束文件编写,板级调试等FPGA开发能力,也是对自己设计能力的一个小的测试与总结,同时给大家提供力所能级的参考,在设计的过程中,由于笔者能力有限,肯定会有不合理的地方,望批评指正。

实验内容

sdram器件手册阅读与初始化模块编写与仿真。

实验步骤

在写sdram控制器之前,笔者看了一些资料与示例,有的示例虽然实现了功能,但是代码整体条理不是太明晰。比较优秀的资料笔者认为有以下几个:

1. 野火FPGA开发教程

在其资料库网站上可以下载:

该教程sdram控制器部分的讲解是很不错的,基于波形设计的方法是具有可操作性的,此外,这个教程在时钟分频方面的讲解,让笔者印象深刻,在笔者看过的教程中,对于分频这个部分的讲解,多半是计数器计数生成时钟,然后将生成时钟直接驱动后续的寄存器,对于初学者来说,这种分频方式会让他们在后续的开发过程中产生各种困惑。但是野火的这个教程分析了这种分频的缺点,并推荐降频(时钟使能)的方法。

2. 开源骚客的博客

在这个博客中有sdram控制器设计的系列文章,以及对应的资料,系列文章中对sdram仲裁模块的讲解是很不错的。

3. Clifford E. Cummings_文献

Clifford E. Cummings的关于状态机的文章,非常不错,其他的关于复位,fifo方面的文章也是很经典的。

参考资料介绍完毕,下面介绍sdram器件手手册,手册是Micron的SDRAM手册。

该手册内容较为详细。下面根据手册介绍一些基本的参数。

关键的timing parameters

不同速度等级的sdram器件工作最大时钟频率不同,对应的timing参数也不同。

tRCD 表示写入激活命令到开始进行数据读写,中间需要等待的时间(ns),表中列出的数据是需要等待的最小时间,下图红色标记部分是激活命令到写数据命令之间的等待时间:

激活命令到读数据命令之间的等待时间如下图:

tRP 表示预充电指令发出到预充电完成需要等待的时间(ns),表中列出的数据是需要等待的最小时间,下图红色标记部分是tRP时间:

CL(CAS(READ)latency)列选通潜伏期,表示数据读指令发出到第一个有效数据输出需要等待的时间,CL在时序图中一般以时钟周期为单位,如下图所示:

地址表如下:

这个表列出了不同数据位宽的器件的行列地址,以及bank地址位宽,

对于数据位宽为16的器件,其行地址为13位,bank地址为2位,列地址为9位。

上表为各种命令对应的编码,其中cs_n, ras_n, cas_n, we_n为主要的命令的主要位,通过器件外部引脚输入,其他的为辅助位。

关于各种命令的作用,器件手册有详细的介绍。也可以参考前文提到的各种资料。

关于手册中的内容,暂时介绍这么多,后面在设计过程会根据需要再详细阅读。

下面介绍初始化过程,波形图如下:

上电且VDD和时钟稳定后,等待至少100 us时间,然后发送NOP命令,一个时钟周期之后,给PRECHARGE命令,该命令需要Ba[1:0]与A10进行配合,是对所有bank进行PRECHARGE还是指定的bank进行PRECHARGE,等待tRP时间,等待结束后,发送AUTO REFRESH命令,等待tRFC时间,在等待tRFC期间,必须发送NOP命令,等待tRFC结束后,再次发送AUTO REFRESH命令,等待tRFC时间,在等待tRFC期间,必须发送NOP命令,等待tRFC结束后,发送LOAD MODE REGISTER命令,该命令需要A[12:0]进行配合,等待tMRD时间,在等待期间,必须发送NOP命令,等待结束后,整个初始化过程完成。

下面开始设计sdram初始化模块,模块框图如下:

在初始化阶段,需要对sdram发送初始化过程中需要的命令,以及A[12:0],BA[1:0]地址位进行配合,达到初始化目的,此外还需要一个标志信号来指示初始化完成。

使用状态机来实现初始化过程,状态转移图如下:

定义一个init_cnt的计数器,当计数到对应的数值时,进行状态跳转,波形图如下:

该波形图将信号分为三组,输入信号,内部信号以及输出信号,包括状态信号的跳变,输出信号的改变等,波形图在一开始并不一定要特别精确,在开发过程中可能需要进行调整。波形图是思路的表现形式,思路清晰了,写代码将会比较快,而且出了问题也可以快速定位。

代码片段如下:

这里需要说明的是,本模块中,初始化过程的PRECHARGE命令是对所有Bank进行,所以输出信号中没有Bank对应的端口。

定义时间节点

对应的命令定义和一些参数写在头文件中,如下:

状态机采用三段式来编写,可以参考Clifford E. Cummings关于状态机的文章《State Machine Coding Styles for Synthesis》,状态编码,寄存状态代码如下:

下一状态跳转的组合逻辑代码如下:

状态机输出采用时序逻辑,如下:

在给PRECHARGE命令时,A[10]拉高,表示对所有bank进行PRECHARGE。在LOAD REGISTER过程中,对应的参数也定义在头文件中,如下:

至此,代码部分介绍完成,编译后查看状态机跳转,如下:

下面编写测试文件,例化初始化模块

接着例化sdram仿真模型,需要注意的是,这里需要修改一些默认参数,如下:

端口连接

输出给sdram器件的时钟sdram_clk与模块控制时钟sys_clk需要有180°相位差,这里为了仿真方便,直接取反,但是在最终的模块中是不可以这样的,需要使用PLL来产生相位差时钟,这样才能保证时钟质量,减少时钟偏斜,以及使用FPGA内部专用时钟布线资源。

仿真过程代码如下:

这里需要说明的一点是,仿真设置需要添加sdram仿真模型,不然modelsim会报错,具体步骤如下:

添加完成后,如下:

编译无问题后,开始仿真,打印信息如下:

可以看到,PRECHARGE命令在100us之后执行,且对所有Bank执行PRECHARGE命令,LOAD REGISTER过程对寄存器的配置也与设置值一致,下面查看波形,

初始化完成后,init_done信号拉高,表示初始化过程完成。至此,sdram器件手册的介绍和sdram初始化过程的内容介绍完毕,后面的内容将介绍自动刷新模块和读写模块。

编辑于 2021-06-14 20:09