最近学习了DDR3控制器的使用,也用着DDR完成了一些简单工作,想着以后一段可能只用封装过后的IP核,可能会忘记DDR3控制器的一些内容,想着把这个DDR控制器的编写过程记录下来,便于我自己以后查看吧,哈哈哈,闲话少说开始工作。这个DDR3控制器分两节内容吧,第一节就是MIGIP核的简单介绍和生成这个IP核再介绍一下自己封装这个IP的整体架构,第二节就来介绍一下各个模块的内容。
1.1 MIG IP 核介绍
1) MIG IP核架构
通过查阅ug586_7Series_MIS,我们可以看到MIG这个IP核的架构如下,直观可以看出,MIG主要有面向用户端口和面向DDR端口,用户通过使用MIG能够通过用户端口的信号,来完成对DDR SDRAM的访问,达到简化操作的目的。
我们重点要关注的就是用户端口的这些信号,在这里我们主要介绍一下我们将使用到的信号,具体信号的含义,可以去datasheet中查看具体信号的作用。
2) cmd path signals(命令通道信号)
首先我们需要了解与用户命令相关的信号,并且了解命令能够被正确接收的时序。在下面的接口列表中信号的I/O方向均是相对MIG IP核而言的。
端口名称 |
I/O |
位宽 |
备注 |
app_cmd |
I |
3 |
命令总线,3’b000表示写命令,3’b001表示读命令 |
app_addr |
I |
29 |
将要访问的DDR内存地址,具体位宽与用户生成IP核时的设置有关 |
app_rdy |
I |
1 |
空闲信号,指示当前IP核的工作状态,只有该信号为高时,IP核才能正确的想用用户给出的命令 |
app_en |
I |
1 |
命令使能信号,该信号有效且app_rdy有效时,命令才能 |
下图是一张用户命令正确被IP核接收的时序图,可以看到,只有在app_en 和app_rdy均为有效的时候,命令才能正确得被接收到MIG IP核中。
3) 用户读端口相关信号
端口名称 |
I/O |
位宽 |
备注 |
app_rd_data |
O |
256 |
从DDR中读出得数据,一个时钟周期突发读出8个32bit数据(位宽根据IP核设置会有不同) |
app_rd_data_valid |
O |
1 |
读出数据有效信号,该信号为高时表示从IP核中读出的数据有效 |
app_rd_data_end |
O |
1 |
指示当前数据时突发读写的最后一个周期的数据,这个信号与设置的用户时钟和DDR时钟的比例有关 |
下面的时序图显示了从MIG IP中读出数据的用户操作时序,当用户的读数据命令被IP核接收后,在几个时钟周期后会将读出的数据输出到数据总线上。
4) 用户写端口相关信号
端口名称 |
I/O |
位宽 |
备注 |
app_wdf_wren |
I |
1 |
写数据有效信号,当app_wdf_rdy也为有效时,IP核才会接收到用户端发送的app_wdf_data |
app_wdf_rdy |
I |
1 |
写空闲信号,IP核内部的写FIFO能够接收用户数据的标志 |
app_wdf_data |
I |
256 |
用户写入IP核的256bit数据 |
app_wdf_end |
I |
1 |
该信号有效时,表示当前是一次DDR写突发的最后一个数据 |
app_wdf_mask |
I |
32 |
32bit数据掩码,每一位对应app_wdf_data的一个8bit数据 |
下面是一张MIG控制器的写时序图和官方对这几种写操作的解释,通过官方文档的介绍我们可以知道写入的数据可以在写命令给出之前,之时或者之后给出,但是在写命令之后给出的写数据不能超过两个时钟周期。在写命令之前给出写数据则没有这些限制。
之所以能过这样操作,是因为在IP核内部有写入数据的FIFO能够对数据实现缓冲。
在了解了MIG控制器的相关用户端口的信号后,我们就可以着手来自己写一个控制器来简化用户操作,更加简单地对DDR进行操作。接下来我们首先根据开发板所有的资源生成一个对应MIG控制器的IP。
1.2 创建工程并生成DDR控制器IP核MIG
1) 生成DDR控制器IP核
首先点击右侧 IP Catalog 在收索页面输入MIG选择Memory Interface Generator,双击弹出IP核配置界面。
在最开始的弹出界面中,主要是介绍了我们的FPGA核心芯片的相关信息,点击next
在这一个页面中,我们选择创建一个设计,并将IP核名称更改为ddr3,选择生成一个DDR控制器,点击Next。
在接下来的页面中选择可以同种类型的FPGA芯片,在这里我们不勾选,点击next。
在这一界面中选择的是存储的种类,因为我们核心板上有两粒镁光的MT41K256MXX-107的DDR3内存芯片,所以这里选择DDR3 SDRAM
在接下来的这一页中要选择DDR3的工作频率,这里我们选择400MHz,若是FPGA芯片的速率等级不同,选择的工作频率有可能不同。
然后选择DDR芯片的信号,这里根据核心板上的两粒镁光的DDR内存芯片选择具体的型号,这里为MT41K256MXX-107;
接下来是电压的工作,选择1.5V;
接下来是数据位宽,因为开发板上两粒DDR3芯片的位宽为16bit所以在这里选择位宽为32bit;
工作模式选择normal;
这里解释一下,为什么DDR控制器的工作频率要选择400MHz:在这里选择400M的工作时钟,是为了便于我们后面在进行突发读写数据时提供便利,400M的时钟在双沿传输数据,此时我们可以看到下面的PHY to Controller Clock Ratio 固定为4:1,这样,在一个时钟周期内,就可以一次突发传输8个数据,这与DDR3采用8位预取计数是相关的。而且,设置时钟为400MHz,也简化了我们在之后对于app_wdf_end和app_rd_end这两个信号理解与设计。
DDR3控制器的带宽计算:
在本设置下,传输数据的理论带宽可以看作400M*2*32bit=25.6Gbps,其中400M是工作时钟,2是双沿进行数据传输,32bit是内存的每一个地址的容量。
在接下来的页面中选择输入IP核的时钟为5000ps(200MHz),选择突发类型,和确定地址的组合方式为bank-row-col的组合方式。
在这一页中,我们选择系统时钟类型为No Buffer,参考时钟类型为 Use System Clock,系统复位选择为低复位。
在这一页面中,选择阻抗为50欧姆,这是由核心板的硬件设计决定的
接下来的页面中,是对DDR控制器进行管脚分配,在这里我们选择 Fixed Pine Out,通过读取UCF文件中的引脚分配给DDR3分配引脚。在这一界面中,选择 Read XDC/UCF文件为控制器分配引脚,在弹出界面选择我们为用户提供的引脚分配的UCF文件,点击OK;
读取完UCF文件后,点击Validate就可以验证管脚分配是否合理;若管脚分配合理,点击OK后,方可点击Next;接下来一路点击OK就可以了。
到这里就生成了本次实验的IP核,我们可以打开例化模板看看该IP核的各个信号。
至此生成了IP核,下面简单介绍一下刚刚没有提到的几个信号:
以ddr开头的这写信号是该IP核与DDR3芯片连接的信号,我们不必太关心。
app_sr_req,app_ref_req,app_zq_req这个三个信号再这里我们先暂时不应管,在简单的应用中也用不上。
ui_clk:是MIG IP核提供给用户使用的一个100MHz时钟;
ui_clk_sync_rst:是ui_clk的复位信号,当该信号拉低的时候表示ui_clk已经复位完成;
sys_clk_i:是输入到该IP 核的系统时钟,前面我们选择的是NO BUFFER和use system clock所以这里有一个单端的输入时钟接口;
sys_rst:是IP核复位信号,前面设置的是低有效;
init_cail_complete:是DDR初始化完成信号,在本设置下,一般情况下DDR在复位后108us时完成初始化;
3、基于MIG 设计一个DDR读写控制器
1)整体架构
整体的架构如下图所示:主要有一下几个模块,分别是读控制模块,写控制模块仲裁模块在下一节中将介绍这几个模块,在本节中简单介绍一下这个整体模块的功能:
外界想要写入到DDR中的数据通过写控制模块写入DDR中,写入的地址从0地址开始增加;
外界想要从DDR中读出数据通过读控制模块,读出的数据从0地址开始;
仲裁模块用来控制本次该执行哪种操作,避免读写冲突;
具体的功能将在下一节中进行介绍,有点写累了,先去喝一杯茶.