你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

DDR3 Vivado 仿真测试成功

2021/12/29 21:36:04

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_cmdapp_addr以及app_en拉高,此时,写命令发出。在写命令发出之前周期、当前周期或者之后3个周期之内,用户可以进行写操作。写操作执行过程同样是:当app_wdf_rdy为高时,将app_wdf_dataapp_wdf_wren以及app_wdf_end拉高,此时写操作成功。

图1写时序

如下图所示为读操作的具体时序,同样的先进行写命令操作,时序同写命令操作一样。写命令完成后的若干个周期,app_rd_dataapp_rd_data_valid以及app_rd_data_end出现。当app_rd_data_valid为高时,用户取数即可

图2读时序

DDR3读写的地址偏移 

做读写测试时一直困扰我的问题.上面说给DDR3指定一个地址,可以往这个地址里读或写8*n bits的数据.按理说我要读写下一地址,只要地址加1就可以.但仿真时地址加1得到的数据都是乱的.要想正常读写数据,需要每次地址加8,这是为什么呢?

我们可以看一下这个DDR3的内部结构图,从这个图中我们可以看出10bitColumn 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一定可以学好的。