DDR MIG IP 设置
1.默认就好了,不需要选择AXI4 Interface ,除非你想用这个接口。
2.Clock Period:你想让DDR3跑的时钟,这里是400M,由于DDR3是在上升沿和下降沿都写数据,那么我们可以知道DDR3的带宽为:400M*2*64bit=51200Mbit。
下面的PHY to Controller Clock Ratie 4:1 ,表示用户时钟是400M/4=100M,这个用户时钟,是IP核自动生成的(ui_clk)。后期的用户接口写数据和读数据都是按照这个时钟去操作。
用户的接口数据位宽为=51200Mbit/100M=512bit
其中Memory Type 要特别的留意,DDR3有插座式的,还有贴片的。要根据不同型号的去选择
3.这个时钟是给DDR的参考输入时钟200M,其他的时钟,都是根据这个时钟产生的。(后面有一个界面设置这个时钟的管脚位置)
4. system clock是差分时钟
Reference clk 直接使用 system clk
4.读写速率只有800M,50即可
只有超过1333M ,才使用40
一般设计DDR都会有一个ucf文件,里面包含设计DDR3的管脚约束,加载进来就可以了。
这个时钟就是刚才说的那个200M时钟
DDR MIG IP 接口信号
下一步之后,就生成了MIG IP接口,如下图所示
mig_7series_0 u_mig_7series_0 (
// Memory interface ports
.ddr3_addr (ddr3_addr), // output [15:0] 行列地址,其中行地址线和列地址线是分时复用的,即地址要分两次送出,先送出行地址,再送出列地址。
.ddr3_ba (ddr3_ba), // output [2:0] bank地址
.ddr3_cas_n (ddr3_cas_n), // output 列地址选通
.ddr3_ck_n (ddr3_ck_n), // output [0:0] 差分时钟p端
.ddr3_ck_p (ddr3_ck_p), // output [0:0] 差分时钟n端
.ddr3_cke (ddr3_cke), // output [0:0] 时钟有效信号,高电平有效
.ddr3_ras_n (ddr3_ras_n), // output 行地址选通信号,低电平有效
.ddr3_reset_n (ddr3_reset_n), // output 复位信号,低电平有效
.ddr3_we_n (ddr3_we_n), // output 0-写允许 1-读允许
.ddr3_dq (ddr3_dq), // inout [63:0] 数据
.ddr3_dqs_n (ddr3_dqs_n), // inout [7:0] 数据选取脉冲
.ddr3_dqs_p (ddr3_dqs_p), // inout [7:0] 数据选取脉冲
.init_calib_complete (init_calib_complete), // output 初始化完成信号 ,高电平有效
.ddr3_cs_n (ddr3_cs_n), // output [0:0] 片选信号,低表示命令有效,否则命令屏蔽
.ddr3_dm (ddr3_dm), // output [7:0] 数据掩码 一位控制一个字节
.ddr3_odt (ddr3_odt), // output [0:0] 片上终端使能,高电平有效
// Application interface ports
.app_addr (app_addr), // input [29:0] 访问的DDR3地址
.app_cmd (app_cmd), // input [2:0] 000 写命令 001 读命令
.app_en (app_en), // input 命令路径:的使能信号,该信号有效时,app_cmd、app_addr信号才有效,其余的时刻信号无效
.app_wdf_data (app_wdf_data), // input [511:0] 写数据路径数据信号
.app_wdf_end (app_wdf_end), // input 4:1模式下与app_wdf_wren相同,2:1模式下每两个app_wdf_wren,使能一次 app_wdf_en。这个可以理解可参考UG586数据手册P169 Figure 1-78/ Figure 1-79
.app_wdf_wren (app_wdf_wren), // input 写数据的有效信号
.app_rd_data (app_rd_data), // output [511:0] 读数据信号
.app_rd_data_end (app_rd_data_end), // output DDR3给用户读命令结束信号
.app_rd_data_valid (app_rd_data_valid), // output 读数据有效信号
.app_rdy (app_rdy), // output 命令路径准备好接受用户的命令信号,与app_en有效时命令有效
.app_wdf_rdy (app_wdf_rdy), // output MIG信号给用户的信号,在该信号与app_wdf_wren信号同时有效时,数据被写入DDR芯片
.app_sr_req (1'b0), // input 一般为0
.app_ref_req (1'b0), // input 一般为0
.app_zq_req (1'b0), // input 一般为0
.app_sr_active (), // output 上面请求的响应信号
.app_ref_ack (), // output 上面请求的响应信号
.app_zq_ack (), // output 上面请求的响应信号
.ui_clk (clk), // output MIG给用户使用的时钟,用户对MIG IP核的操作必须使用该时钟
.ui_clk_sync_rst (rst), // output MIG给用户使用的复位信号,用户对MIG IP核的操作必须使用该复位信号
.app_wdf_mask (64'd0), // input [63:0] 写数据的掩码 ,一般为0
// System Clock Ports
.sys_clk_p (sys_clk_p), // input MIG的系统时钟,在MIG的选项中选择
.sys_clk_n (sys_clk_n), // input
.sys_rst (sys_rst) // input sys_rst MIG IP核的复位信号,与系统时钟相关联
);
需要对接口信号做一个了解,包括DDR3的管脚接口,MIG 用户接口,只需要了解 Application interface ports 接口就可以了,看懂DDR3的读写时序图,这样就可以完成对于DDR3的读写。
Application interface ports 接口主要包括:
控制命令接口信号app_addr,app_cmd,app_en, app_rdy
写通道接口信号: app_wdf_data, .app_wdf_end, app_wdf_wren, app_wdf_rdy
读通道接口信号: app_rd_data, app_rd_data_end, app_rd_data_valid
DDR3 的接口信号读写时序
按照UG586的说明,当app_rdy为高时,将app_cmd,app_addr以及app_en拉高,此时,写命令发出。在写命令发出之前周期、当前周期或者之后3个周期之内,用户可以进行写操作。写操作执行过程同样是:当app_wdf_rdy为高时,将app_wdf_data,app_wdf_wren以及app_wdf_end拉高,此时写操作成功。
图1写时序
如下图所示为读操作的具体时序,同样的先进行写命令操作,时序同写命令操作一样。写命令完成后的若干个周期,app_rd_data、app_rd_data_valid以及app_rd_data_end出现。当app_rd_data_valid为高时,用户取数即可
图2读时序
DDR3读写的地址偏移
做读写测试时一直困扰我的问题.上面说给DDR3指定一个地址,可以往这个地址里读或写8*n bits的数据.按理说我要读写下一地址,只要地址加1就可以.但仿真时地址加1得到的数据都是乱的.要想正常读写数据,需要每次地址加8,这是为什么呢?
我们可以看一下这个DDR3的内部结构图,从这个图中我们可以看出10bit的Column Address的寻址能力只有128,只有7bits用于列地址译码!列地址0,1,2并没有用,而这列地址的低三位被用于确定Burst Length 与Burst Order了.见下图:
所以列地址的低三位并没有用于寻址,所以如果我们想要访问下一存储单元的话,地址需要加8而不是加1
DDR3 的仿真验证
.在我们上板子之前,最好可以通过仿真,先验证自己的读写控制时序是否正确,如果没有仿真,就直接上板,可能后期调试起来也比较麻烦。关于如何在Vivado中建立一个DDR3的仿真工程,可以参考特权同学2020版《深入浅出玩转FPGA视频教程》 中的DDR3 IP仿真的章节。哔哩哔哩上有这个课程。
在建立完工程后,我们可以把工程中example_top 文件下的
mig_7series_v4_0_traffic_gen_top 文件注释掉,添加我们自己的读写控制文件,完成DDR3的仿真。本次仿真结构如下,主要想完成对于DDR3全部地址的读写测试。
整体设计思路如下:突发传输写数据一次,然后突发读出来,校验数据是否正确。如果出错,会有指示信号。
mig_ctrl #
(
.DATA_WIDTH (APP_DATA_WIDTH),
.ADDR_WIDTH (ADDR_WIDTH)
)
mig_ctrl_inst(
// Application interface ports
.clk (clk),
.rst (rst),
.app_addr (app_addr),//output [29:0]
.app_cmd (app_cmd), //output [2:0]
.app_en (app_en), //output
.app_wdf_data (app_wdf_data),//output [511:0]
.app_wdf_end (app_wdf_end),// output
.app_wdf_wren (app_wdf_wren),//output
.app_rd_data (app_rd_data),// input [511:0]
.app_rd_data_end (app_rd_data_end),//input
.app_rd_data_valid (app_rd_data_valid),//input
.app_rdy (app_rdy), //input
.app_wdf_rdy (app_wdf_rdy), //input
.init_calib_complete (init_calib_complete),
// user interface
.wr_request (wr_request), //突发写请求
.wr_start_addr (wr_start_addr),//突发写首地址
.wr_len (wr_len), //突发写长度
.wr_data (512'd0),//写数据,本次仿真未使用
.wr_data_valid (1'b0), //写数据有效,本次仿真未使用
.wr_finish (wr_finish),//一次突发完成结束信号
.rd_request (rd_request),
.rd_start_addr (rd_start_addr),
.rd_len (rd_len),
.rd_data (rd_data),
.rd_data_valid (rd_data_valid),
.rd_finish (rd_finish),
.check_error (check_error)
);
test_ddr test_ddr(
.clk (clk),
.rst (rst),
//mig_ctrl interface
.init_calib_complete (init_calib_complete),
.wr_request (wr_request),
.wr_start_addr (wr_start_addr),
.wr_len (wr_len),
.wr_finish (wr_finish),
.rd_request (rd_request),
.rd_start_addr (rd_start_addr),
.rd_len (rd_len),
.rd_finish (rd_finish),
.start_test_pulse (start_test_pulse) //测试开始信号
);
图1写时序
写时序仿真
读时序仿真结果
DDR3 仿真工程百度云盘:
链接:https://pan.baidu.com/s/19vtorLOS4zQPDnqX4Yzd0A
提取码:53iz
希望可以对初学者有一定的用处,加油!FPGA一定可以学好的。