DDR3(3):初始化

  不管官方例程是否学懂,我们都不能直接用,还是得自己设计一个实用的 DDR3 控制器,我们要做的第一步就是初始化操作,对 IP 核进行校验。本篇采用 Modelsim 软件配合 DDR3 IP核生成的仿真模型,搭建出 IP核的初始化过程。

一、顶层文件

1、生成 DDR3 IP 核后,在 Source 界面空白处右键点击 Add Source,添加顶层文件。

2、在 DDR3_HDMI\DDR3_HDMI.srcs\sources_1\ip\ddr3_ctrl\ddr3_ctrl\user_design\rtl\ddr3_ctrl.v 可得到 top_ddr3_hdmi 需要的输入输出端口,将其复制过来。

//========================< 端口 >==========================================
(
//system ----------------------------------------
input   wire                sclkin              , //50Mhz
input   wire                srst_n              , //复位,低电平有效
//DDR3 ------------------------------------------
inout   wire  [15:0]        ddr3_dq             ,
inout   wire  [ 1:0]        ddr3_dqs_n          ,
inout   wire  [ 1:0]        ddr3_dqs_p          ,
output  wire  [13:0]        ddr3_addr           ,
output  wire  [ 2:0]        ddr3_ba             ,
output  wire                ddr3_ras_n          ,
output  wire                ddr3_cas_n          ,
output  wire                ddr3_we_n           ,
output  wire                ddr3_reset_n        ,
output  wire  [ 0:0]        ddr3_ck_p           ,
output  wire  [ 0:0]        ddr3_ck_n           ,
output  wire  [ 0:0]        ddr3_cke            ,
output  wire  [ 0:0]        ddr3_cs_n           ,
output  wire  [ 1:0]        ddr3_dm             ,
output  wire  [ 0:0]        ddr3_odt              
);

3、对顶层模块进行编写,可以进入 IP Source 的 ddr3_ctrl.veo文件找到接口复制过来例化,并根据需求更改部分信号,仿真时希望app接口不工作,所有输入接口连接为 0

 

//DDR3_IP核 -----------------------------------------------------------------
ddr3_ctrl u_ddr3_ctrl //输入为200Mhz,芯片需400Mhz,内部速率为4:1 = 100Mhz
(
    // Memory interface ports ----------------------------------------
    .ddr3_addr              (ddr3_addr                               ), // output [13:0]
    .ddr3_ba                (ddr3_ba                                 ), // output [ 2:0]
    .ddr3_cas_n             (ddr3_cas_n                              ), // output        
    .ddr3_ck_n              (ddr3_ck_n                               ), // output 
    .ddr3_ck_p              (ddr3_ck_p                               ), // output
    .ddr3_cke               (ddr3_cke                                ), // output
    .ddr3_ras_n             (ddr3_ras_n                              ), // output        
    .ddr3_reset_n           (ddr3_reset_n                            ), // output        
    .ddr3_we_n              (ddr3_we_n                               ), // output        
    .ddr3_dq                (ddr3_dq                                 ), // inout  [15:0]  
    .ddr3_dqs_n             (ddr3_dqs_n                              ), // inout  [ 1:0]   
    .ddr3_dqs_p             (ddr3_dqs_p                              ), // inout  [ 1:0]   
    .init_calib_complete    (init_calib_complete                     ), // output        
    .ddr3_cs_n              (ddr3_cs_n                               ), // output 
    .ddr3_dm                (ddr3_dm                                 ), // output [ 1:0]  
    .ddr3_odt               (ddr3_odt                                ), // output
    // Application interface ports -----------------------------------
    .app_addr               (                                        ), // input  [27:0]  
    .app_cmd                (                                        ), // input  [ 2:0]   
    .app_en                 (                                        ), // input         
    .app_wdf_data           (                                        ), // input  [127:0] 
    .app_wdf_end            (                                        ), // input         
    .app_wdf_wren           (                                        ), // input         
    .app_rd_data            (                                        ), // output [127:0]
    .app_rd_data_end        (                                        ), // output        
    .app_rd_data_valid      (                                        ), // output        
    .app_rdy                (                                        ), // output        
    .app_wdf_rdy            (                                        ), // output        
    .app_sr_req             (                                        ), // input         
    .app_ref_req            (                                        ), // input         
    .app_zq_req             (                                        ), // input         
    .app_sr_active          (                                        ), // output        
    .app_ref_ack            (                                        ), // output        
    .app_zq_ack             (                                        ), // output        
    .ui_clk                 (                                        ), // output 100Mhz       
    .ui_clk_sync_rst        (                                        ), // output        
    .app_wdf_mask           (                                        ), // input  [15:0]
    // System Clock Ports --------------------------------------------
    .sys_clk_i              (sysclk                                  ), // input  200Mhz       
    .sys_rst                (srst_n                                  )  // input  系统复位
);

4、调取 DDR3 IP 核时,选择了对此 IP 核输入一个200 Mhz 的时钟,由于板卡晶振生成的时钟为 50Mhz,所以还得用一个 IP 核来生出 200 Mhz 时钟。

5、生成时钟后同样找到 .veo 文件接口复制过来例化。

//时钟_IP核 ----------------------------------------------------------------
ddr3_clk_gen u_ddr3_clk_gen
(
    .clk_in1                (sclkin                 ),  // input            clk_in1
    .clk_out1               (sysclk                 )   // output           clk_out1
);

7、有几个信号是我们需要观察的,用 wire 引出来吧。

//========================< 连线 >==========================================
//PLL -------------------------------------------
wire                        sysclk              ;
// ddr3 ip --------------------------------------
wire                        app_rdy             ;
wire                        app_wdf_rdy         ;
wire                        app_en              ;
wire  [27:0]                app_addr            ;
wire  [ 2:0]                app_cmd             ;
wire  [15:0]                app_wdf_mask        ;
wire                        app_wdf_wren        ;
wire [127:0]                app_wdf_data        ;
wire                        app_wdf_end         ;
wire [127:0]                app_rd_data         ;
wire                        app_rd_data_valid   ;
wire                        app_rd_data_end     ;
wire                        ui_clk              ;
wire                        ui_clk_sync_rst     ;
wire                        init_calib_complete ;

 

二、测试文件

1、在 Simulation Sources 右键选择 Add Sources,创建 testbench 文件。

2、首先还是把输入输出接口和 top 模块接口在 testbench 中写好。

 1 `timescale 1ns/1ps  //时间精度
 2 `define    Clock 20 //时钟周期
 3 
 4 module top_ddr3_hdmi_tb;
 5 //========================< 端口 >==========================================
 6 reg                         clk                             ;
 7 reg                         rst_n                           ;
 8 wire  [15:0]                ddr3_dq                         ;
 9 wire  [ 1:0]                ddr3_dqs_n                      ;
10 wire  [ 1:0]                ddr3_dqs_p                      ;
11 wire  [13:0]                ddr3_addr                       ;
12 wire  [ 2:0]                ddr3_ba                         ;
13 wire                        ddr3_ras_n                      ;
14 wire                        ddr3_cas_n                      ;
15 wire                        ddr3_we_n                       ;
16 wire                        ddr3_reset_n                    ;
17 wire  [ 0:0]                ddr3_ck_p                       ;
18 wire  [ 0:0]                ddr3_ck_n                       ;
19 wire  [ 0:0]                ddr3_cke                        ;
20 wire  [ 0:0]                ddr3_cs_n                       ;
21 wire  [ 1:0]                ddr3_dm                         ;
22 wire  [ 0:0]                ddr3_odt                        ;
23 
24 //==========================================================================
25 //==    模块例化
26 //==========================================================================
27 //顶层模块
28 top_ddr3_hdmi u_top_ddr3_hdmi
29 (
30     .ddr3_dq                (ddr3_dq                        ),
31     .ddr3_dqs_n             (ddr3_dqs_n                     ),
32     .ddr3_dqs_p             (ddr3_dqs_p                     ),
33     .ddr3_addr              (ddr3_addr                      ),
34     .ddr3_ba                (ddr3_ba                        ),
35     .ddr3_ras_n             (ddr3_ras_n                     ),
36     .ddr3_cas_n             (ddr3_cas_n                     ),
37     .ddr3_we_n              (ddr3_we_n                      ),
38     .ddr3_reset_n           (ddr3_reset_n                   ),
39     .ddr3_ck_p              (ddr3_ck_p                      ),
40     .ddr3_ck_n              (ddr3_ck_n                      ),
41     .ddr3_cke               (ddr3_cke                       ),
42     .ddr3_cs_n              (ddr3_cs_n                      ),
43     .ddr3_dm                (ddr3_dm                        ),
44     .ddr3_odt               (ddr3_odt                       ),
45     .sclkin                 (clk                            ),
46     .srst_n                 (rst_n                          )
47 );

3、DDR3 控制器非常复杂,手写 testbench 是非常困难的。我们上一讲调取 DDR3 IP 核时说过,它已经生成了仿真模型供我们测试。位置在 DDR3_HDMI\DDR3_HDMI.srcs\sources_1\ip\ddr3_ctrl\ddr3_ctrl\example_design\sim,ddr3_model.sv 和 ddr3_model_parameters.vh 即是我们需要的仿真模型,将其复制到 DDR3_HDMI\DDR3_HDMI.srcs\sim_1\new 中,和 top_ddr3_hdmi_tb 文件放在一起。此外还可以看到刚刚那个文件夹中有一个 sim_tb_top 文件,打开它翻到500多行,即可看到该仿真模型的接口模块,我们将其复制到 testbench 中,并根据此次设计情况,更改部分参数。

 1 //仿真模型
 2 ddr3_model u_ddr3_model
 3 (
 4     .rst_n                  (ddr3_reset_n                   ),
 5     .ck                     (ddr3_ck_p                      ),
 6     .ck_n                   (ddr3_ck_n                      ),
 7     .cke                    (ddr3_cke                       ),
 8     .cs_n                   (ddr3_cs_n                      ),
 9     .ras_n                  (ddr3_ras_n                     ),
10     .cas_n                  (ddr3_cas_n                     ),
11     .we_n                   (ddr3_we_n                      ),
12     .dm_tdqs                ({ddr3_dm[1],ddr3_dm[0]}        ), //ddr3_dm为2位
13     .ba                     (ddr3_ba                        ),
14     .addr                   (ddr3_addr                      ),
15     .dq                     (ddr3_dq[15:0]                  ), //ddr3_dq为16位
16     .dqs                    ({ddr3_dqs_p[1],ddr3_dqs_p[0]}  ), //ddr3_dqs_p为2位
17     .dqs_n                  ({ddr3_dqs_n[1],ddr3_dqs_n[0]}  ), //ddr3_dqs_n为2位
18     .tdqs_n                 (                               ),
19     .odt                    (ddr3_odt                       )
20 );

3、此外还需要产生一个 50 Mhz 时钟和低电平有效的复位信号。

 1 //==========================================================================
 2 //==    时钟信号和复位信号
 3 //==========================================================================
 4 initial begin
 5     clk = 0;
 6     forever
 7         #(`Clock/2) clk = ~clk;
 8 end
 9 
10 initial begin
11     rst_n = 0; #(`Clock*20+1);
12     rst_n = 1;
13 end

4、回到 Vivado,发现仿真模型文件已经出现了,但是处于问号状态,我们选中它,右键 Add Sources,将 ddr3_model.sv 和 ddr3_model_parameters.vh 添加进来即可。

 

三、启动 Modelsim 验证 DDR3 IP核

1、使用 Modelsim 进行仿真前,需要先编译 Vivado 和 Modelsim 之间的关联库,具体步骤请另行搜索。

2、点击 Vivado 的 Setting 进行设置,Target simulator 选择 ModelSim Simulator,仿真顶层文件选择第二步的仿真文件,仿真库则自动定位好了。

3、点击 Vivado 左侧菜单 Run Simulation --- Run Behavioral Simulation,Modelsim 就自动打开仿真了。

4、选取信号,跑一段时间,可以看到时钟信号和复位信号正常,  init_calib_complete 信号在拉低一段时间后拉高,表面本次 DDR3 IP核验证成功。

 

以上。

 

参考资料:威三学院FPGA教程

posted @ 2019-11-02 22:08  咸鱼IC  阅读(5157)  评论(0编辑  收藏  举报