最近学习了 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地址开始;
仲裁模块用来控制本次该执行哪种操作, 避免读写冲突;
具体的功能将在下一节中进行介绍, 有点写累了, 先去喝一杯茶.
来源: https://www.cnblogs.com/weicc/p/12045782.html