调取 DDR3 IP核后,是不能直接进行读写测试的,必须先进行初始化操作,对 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 需要的输入输出端口,将其复制过来。
1 module top_ddr3_hdmi 2 //========================< 端口 >========================================== 3 ( 4 // inout ---------------------------------------- 5 inout wire [15:0] ddr3_dq , 6 inout wire [ 1:0] ddr3_dqs_n , 7 inout wire [ 1:0] ddr3_dqs_p , 8 // output --------------------------------------- 9 output wire [13:0] ddr3_addr , 10 output wire [ 2:0] ddr3_ba , 11 output wire ddr3_ras_n , 12 output wire ddr3_cas_n , 13 output wire ddr3_we_n , 14 output wire ddr3_reset_n , 15 output wire [ 0:0] ddr3_ck_p , 16 output wire [ 0:0] ddr3_ck_n , 17 output wire [ 0:0] ddr3_cke , 18 output wire [ 0:0] ddr3_cs_n , 19 output wire [ 1:0] ddr3_dm , 20 output wire [ 0:0] ddr3_odt , 21 // system --------------------------------------- 22 input wire sclkin , 23 input wire srst_n 24 );
3、对顶层模块进行编写,可以进入 IP Source 的 ddr3_ctrl.veo文件找到接口复制过来例化,并根据需求更改部分信号。
1 ddr3_ctrl u_ddr3_ctrl 2 ( 3 // Memory interface ports ----------------------- 4 .ddr3_addr (ddr3_addr ), // output [13:0] ddr3_addr 5 .ddr3_ba (ddr3_ba ), // output [2:0] ddr3_ba 6 .ddr3_cas_n (ddr3_cas_n ), // output ddr3_cas_n 7 .ddr3_ck_n (ddr3_ck_n ), // output [0:0] ddr3_ck_n 8 .ddr3_ck_p (ddr3_ck_p ), // output [0:0] ddr3_ck_p 9 .ddr3_cke (ddr3_cke ), // output [0:0] ddr3_cke 10 .ddr3_ras_n (ddr3_ras_n ), // output ddr3_ras_n 11 .ddr3_reset_n (ddr3_reset_n ), // output ddr3_reset_n 12 .ddr3_we_n (ddr3_we_n ), // output ddr3_we_n 13 .ddr3_dq (ddr3_dq ), // inout [15:0] ddr3_dq 14 .ddr3_dqs_n (ddr3_dqs_n ), // inout [1:0] ddr3_dqs_n 15 .ddr3_dqs_p (ddr3_dqs_p ), // inout [1:0] ddr3_dqs_p 16 .init_calib_complete (init_calib_complete ), // output init_calib_complete 17 .ddr3_cs_n (ddr3_cs_n ), // output [0:0] ddr3_cs_n 18 .ddr3_dm (ddr3_dm ), // output [1:0] ddr3_dm 19 .ddr3_odt (ddr3_odt ), // output [0:0] ddr3_odt 20 // Application interface ports ------------------ 21 .app_addr (app_addr ), // input [27:0] app_addr 22 .app_cmd (app_cmd ), // input [2:0] app_cmd 23 .app_en (1'b0 ), // input app_en 24 .app_wdf_data (app_wdf_data ), // input [127:0] app_wdf_data 25 .app_wdf_end (1'b0 ), // input app_wdf_end 26 .app_wdf_wren (1'b0 ), // input app_wdf_wren 27 .app_rd_data (app_rd_data ), // output [127:0] app_rd_data 28 .app_rd_data_end (app_rd_data_end ), // output app_rd_data_end 29 .app_rd_data_valid (app_rd_data_valid ), // output app_rd_data_valid 30 .app_rdy (app_rdy ), // output app_rdy 31 .app_wdf_rdy (app_wdf_rdy ), // output app_wdf_rdy 32 .app_sr_req (1'b0 ), // input app_sr_req 33 .app_ref_req (1'b0 ), // input app_ref_req 34 .app_zq_req (1'b0 ), // input app_zq_req 35 .app_sr_active (app_sr_active ), // output app_sr_active 36 .app_ref_ack (app_ref_ack ), // output app_ref_ack 37 .app_zq_ack (app_zq_ack ), // output app_zq_ack 38 .ui_clk (ui_clk ), // output ui_clk 39 .ui_clk_sync_rst (ui_clk_sync_rst ), // output ui_clk_sync_rst 40 .app_wdf_mask (app_wdf_mask ), // input [15:0] app_wdf_mask 41 // System Clock Ports --------------------------- 42 .sys_clk_i (sysclk ), // input sys_clk_i 200Mhz 43 .sys_rst (srst_n ) // input sys_rst 44 );
4、调取 DDR3 IP 核时,选择了对此 IP 核输入一个200 Mhz 的时钟,由于板卡晶振生成的时钟为 50Mhz,所以还得用一个 IP 核来生出 200 Mhz 时钟。
5、生成时钟后同样找到 .veo 文件接口复制过来例化。
1 ddr3_clk_gen u_ddr3_clk_gen 2 ( 3 .clk_out1 (sysclk ), // output clk_out1 4 .clk_in1 (sclkin ) // input clk_in1 5 );
7、有几个信号是我们需要观察的,用 wire 引出来吧。
1 //========================< 连线 >========================================== 2 wire sysclk ; 3 wire init_calib_complete ; 4 wire ui_clk ; 5 wire ui_clk_sync_rst ;
二、测试文件
1、在 Simulation Sources 右键选择 Add Sources,创建 testbench 文件。
2、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 即是我们需要的仿真模型,将其接口在 testbench 中例化过来。
1 ddr3_model u_ddr3_model 2 ( 3 .rst_n (ddr3_reset_n ), 4 .ck (ddr3_ck_p ), 5 .ck_n (ddr3_ck_n ), 6 .cke (ddr3_cke ), 7 .cs_n (ddr3_cs_n ), 8 .ras_n (ddr3_ras_n ), 9 .cas_n (ddr3_cas_n ), 10 .we_n (ddr3_we_n ), 11 .dm_tdqs ({ddr3_dm[1],ddr3_dm[0]} ), 12 .ba (ddr3_ba ), 13 .addr (ddr3_addr ), 14 .dq (ddr3_dq[15:0] ), 15 .dqs ({ddr3_dqs_p[1],ddr3_dqs_p[0]} ), 16 .dqs_n ({ddr3_dqs_n[1],ddr3_dqs_n[0]} ), 17 .tdqs_n ( ), 18 .odt (ddr3_odt ) 19 );
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教程