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教程