自学内容网 自学内容网

从零开始讲DDR(7)——modelsim独立仿真DDR4

一、概述

        在进行DDR仿真的时候,我们会面临一个以往常规仿真不存在的问题:DDR本身并不是一个我们通过RTL代码设计出来的部件,常规的模块或者IP仿真,都是基于RTL代码就能进行的,但是回顾一下我们DDR的开发流程,调用的IP核也只是MIG ip,其本质是一个DDRC+DDRPYH的组合,并不能模拟任何DDR本身的行为,因此,在进行仿真之前,我们首先要做的就是获取DDR的仿真模型。

二、仿真环境搭建

2.1 DDR模型获取

        让我们从零开始去写DDR模型显然是不现实的,xilinx官方为我们提供了DDR的模型,只需要在我们定制好的MIG ip核上选择打开IP 示例设计:

        这样会打开一个新的工程,这就是xilinx提供的示例工程。在这个工程下,我们可以直接进行仿真操作。

2.2 导出相关文件

        我们这篇文章的目标是通过modelsim独立仿真DDR4,在获得了上面的工程后,需要把内部的文件进行导出,以便我们后续把它们添加到modelsim的仿真环境中。

        在导出的一些选择中,首先目标仿真器我们选择Modelsim,Compiled library location需要选择modelsim编译后的vivado库地址(下图还没有进行修改),最后是导出文件的地址。在下面的Advanced Options中勾选上Copy source files to export directory

        导出完成后,我们会在ddr4_0_ex的工程下,看到一个modelsim的文件夹。这里面就已经包含了我们进行modelsim独立仿真所需的绝大部分文件。modelsim文件夹下:

        我们打开srcs文件夹:

        这里面包含了incl和ip两个文件夹,incl是include的缩写。

        接下来,为了后续操作的方便,我们需要额外加入两个没有被导出的文件:在我们导出的工程文件下找到imports文件夹,找到其中的glbl.v和ddr4_sdram_model_wrapper.sv文件,分别复制出来。

        把glbl.v复制到modelsim/srcs文件夹下面

         把ddr4_sdram_model_wrapper.sv复制到incl文件夹下

 2.3 工程文件夹设置

        对于一个工程来讲,怎么进行文件管理是重要的一个环节,接下来介绍我本人的习惯方式,一个新的工程下,首先是:

  • Docu:用于存放文档
  • modelsim_lib:用于存放modelsim的库
  • Prj:用于存放vivado工程
  • src:用于存放源代码

        对于src文件夹下, 又会进行细分:

  • Cons:用于存放约束文件xdc
  • Header:用于存放宏定义文件
  • ip:用于存放导出的IP
  • RTL:用于存放RTL设计代码
  • Scripts:用于存放脚本文件
  • Testbench:用于存放仿真测试文件

        对于当前工程,文件夹架构如下,我们需要

  • 把示例工程中的modelsim/srcs文件夹里的内容复制到下面架构中的dd4_0文件夹下。
  • 把示例工程中的modelsim文件夹里的compile.do,simulate.do,wave.do文件复制到Scripts文件夹下。
- project
  ├─ Docu
  ├─ modelsim_lib
  ├─ Prj
  ├─ src
      ├─ Cons
      ├─ Header
      ├─ ip
      │   └─ ddr4_0
      ├─ RTL
      ├─ Scripts
      └─ Testbench

2.4 modelsim仿真

        首先我们来了解一下工具,其实对于modelsim和vivado这样的软件来说,一般有2种操作模式,一种是GUI模式,就是大家最常用的在界面上点击的形式,另一种是直接在终端输出TCL指令的形式,这里我们推荐采用的是TCL指令的形式。    

        我们可以在终端直接输入TCL指令,也可以编写.do文件来统一编写TCL文件,出于重复使用和后续调试便捷性的考虑,我们一般都会采用后者。在之前的操作中,我们已经把官方提供的.do文件复制到自己的Scripts文件夹下了。

        关于语法的解析,可以参考:

ModelSim基本命令解析icon-default.png?t=O83Ahttps://blog.csdn.net/apple_53311083/article/details/143840878?spm=1001.2014.3001.5501       

        这里我们需要根据自己实际的路径进行一些简单的修改,修改后的compile.do文件如下:

cd "your/directory"     # 在这里填写项目路径

vlib modelsim_lib
vlib modelsim_lib/work
vlib modelsim_lib/msim

vlib modelsim_lib/msim/xpm
vlib modelsim_lib/msim/microblaze_v11_0_2
vlib modelsim_lib/msim/xil_defaultlib
vlib modelsim_lib/msim/lib_cdc_v1_0_2
vlib modelsim_lib/msim/proc_sys_reset_v5_0_13
vlib modelsim_lib/msim/lmb_v10_v3_0_10
vlib modelsim_lib/msim/lmb_bram_if_cntlr_v4_0_17
vlib modelsim_lib/msim/blk_mem_gen_v8_4_4
vlib modelsim_lib/msim/iomodule_v3_1_5

vmap xpm                           modelsim_lib/msim/xpm
vmap microblaze_v11_0_2            modelsim_lib/msim/microblaze_v11_0_2
vmap xil_defaultlib                modelsim_lib/msim/xil_defaultlib
vmap lib_cdc_v1_0_2                modelsim_lib/msim/lib_cdc_v1_0_2
vmap proc_sys_reset_v5_0_13        modelsim_lib/msim/proc_sys_reset_v5_0_13
vmap lmb_v10_v3_0_10               modelsim_lib/msim/lmb_v10_v3_0_10
vmap lmb_bram_if_cntlr_v4_0_17     modelsim_lib/msim/lmb_bram_if_cntlr_v4_0_17
vmap blk_mem_gen_v8_4_4            modelsim_lib/msim/blk_mem_gen_v8_4_4
vmap iomodule_v3_1_5               modelsim_lib/msim/iomodule_v3_1_5 

vlog -work xpm -incr -sv            "+incdir+src/ip/ddr4_0/incl"  "src/ip/ddr4_0/xpm_memory.sv"
vlog -work blk_mem_gen_v8_4_4 -incr "+incdir+src/ip/ddr4_0/incl"  "src/ip/ddr4_0/ip/ddr4_0/blk_mem_gen_v8_4_4/blk_mem_gen_v8_4.v" 
vlog -work xil_defaultlib     -incr "+incdir+src/ip/ddr4_0/incl"  "src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/bd_9054_lmb_bram_I_0.v" 
vlog -work xil_defaultlib     -incr "+incdir+src/ip/ddr4_0/incl"  "src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/bd_9054_second_lmb_bram_I_0.v" 
vlog -work xil_defaultlib     -incr "+incdir+src/ip/ddr4_0/incl"  "src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/bd_9054.v" 
vlog -work xil_defaultlib     -incr "+incdir+src/ip/ddr4_0/incl"  "src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_0_microblaze_mcs.v" 

vlog -work xil_defaultlib     -incr -sv "+incdir+src/ip/ddr4_0/incl" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_0_phy_ddr4.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_xiphy_behav.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_xiphy.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_iob_byte.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_iob.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_pll.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_xiphy_tristate_wrapper.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_xiphy_riuor_wrapper.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_xiphy_control_wrapper.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_xiphy_byte_wrapper.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_phy_v2_2_xiphy_bitslice_wrapper.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_0_phy.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_wtr.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ref.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_rd_wr.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_periodic.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_group.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ecc_merge_enc.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ecc_gen.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ecc_fi_xor.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ecc_dec_fix.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ecc_buf.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ecc.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_ctl.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_cmd_mux_c.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_cmd_mux_ap.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_arb_p.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_arb_mux_p.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_arb_c.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_arb_a.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_act_timer.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc_act_rank.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_mc.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_ui_wr_data.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_ui_rd_data.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_ui_cmd.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_ui.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_infrastructure.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_xsdb_bram.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_write.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_wr_byte.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_wr_bit.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_sync.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_read.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_rd_en.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_pi.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_mc_odt.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_debug_microblaze.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_cplx_data.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_cplx.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_config_rom.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_addr_decode.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_top.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal_xsdb_arbiter.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_cal.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_chipscope_xsdb_slave.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_v2_2_dp_AB9.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_0_ddr4.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_0_ddr4_mem_intfc.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_0_ddr4_cal_riu.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/ddr4_0.sv" \
"src/ip/ddr4_0/ip/ddr4_0/xil_defaultlib/microblaze_mcs_0.sv" \
"src/ip/ddr4_0/arch_package.sv" \
"src/ip/ddr4_0/proj_package.sv" \
"src/ip/ddr4_0/ddr4_model.sv" \
"src/ip/ddr4_0/example_tb.sv" \
"src/ip/ddr4_0/example_top.sv" \
"src/ip/ddr4_0/interface.sv" \
"src/ip/ddr4_0/sim_tb_top.sv" \

vlog -work xil_defaultlib "src/ip/ddr4_0/glbl.v"

        对于simulate.do文件,我们只需要保留以下的核心代码:

vsim -voptargs="+acc" -t 1ps -L xpm -L microblaze_v11_0_2 -L xil_defaultlib -L lib_cdc_v1_0_2 -L proc_sys_reset_v5_0_13 -L lmb_v10_v3_0_10 -L lmb_bram_if_cntlr_v4_0_17 -L blk_mem_gen_v8_4_4 -L iomodule_v3_1_5 -L unisims_ver -L unimacro_ver -L secureip -lib xil_defaultlib xil_defaultlib.sim_tb_top xil_defaultlib.glbl
log -r /*
run -all

         对于wave.do,我们保留

add wave *
add wave /glbl/GSR

        接下来,我们依次执行运行compile.do,simulate.do和wave.do即可。运行方式可以是

  • 把tcl脚本复制到modelsim命令行
  • 直接在modelsim命令行运行.do文件
  • 编写.bat批处理文件

        大家根据自己实际对于modelsim的熟悉程度操作即可。

        仿真执行后,单独添加一行:

add wave -position insertpoint sim:/sim_tb_top/u_example_top/*

        我们就可以看到仿真波形结果了。 

三、仿真分析

        我们简单分析一下这个官方例程:其实完成的事情并不复杂,就是生成一些数据,写进DDR,然后写了一定数量的数据后,把这些数据再重新读出来,把两个做一个比较,看写入的数据和读出的数据是否一致。

3.1 主要功能

  1. 初始化校准完成init_calib_complete信号在任何命令发送之前被置为高,以确保DDR控制器初始化完成。

  2. 命令生成:在测试平台中,使用WR_INSTR(写命令)和RD_INSTR(读命令)来驱动DDR内存的读写操作。首先发送一组写命令,直到达到设定的写命令数量(NUM_WRITES)。然后开始发送读命令,直到读取操作完成。

  3. 地址生成:地址是基于一个固定的起始地址BEGIN_ADDRESS生成的,并且每次命令发出时会增加。写命令和读命令分别使用相同的地址,但读命令是在所有写命令完成后才开始的。

  4. 写数据生成app_wdf_data生成的是写入DDR的实际数据,数据根据wr_data值来生成。数据在每个时钟周期内根据一定的规则递增。

  5. 命令使能和数据使能cmd_en信号在命令和数据需要时被激活,用来确保命令和数据的正确传输。

  6. 错误检查:平台还实现了一个比较功能,用于检查读写操作中是否存在数据错误。通过比较实际读取的数据和期望数据,来验证是否出现了错误。若发生错误,compare_error信号会被激活。

3.2 错误处理和结束条件

        在仿真过程中,如果发现读写数据不匹配,compare_error会被触发。仿真在完成所有命令后结束(即cmd_cnt达到NUM_TRANSACT时)。

3.3 波形分析

        我们保留一部分关键信号,并对其进行一个分类,整体上分成4组:

  • 系统信号
  • 指令信号
  • 写相关信号
  • 读相关信号

        在下图光标处,init_calib_complete拉高,代表DDR 初始化完成。

        对于cmd信号,0代表写操作,1代表读操作,这个测试先进行了一系列的写操作,把数据写进DDR,然后进行读操作,把数据读出DDR,并和写入的数据进行比较。

       app_en代表操作使能信号,app_rdy代表DDR ready,只有当这两个信号同时有效,操作才能进行,如下图中红框所示。对于写操作,只有当wrf_en(写使能)和wrf_rdy(写ready)同时有效,写操作才会进行,如下图中蓝框所示。

         对于读操作,只有valid信号拉高,才能代表读出的数据有效。

        整个测试过程,compare_error信号没有被激活,代表写入数据与读出数据一致,测试完成。


原文地址:https://blog.csdn.net/apple_53311083/article/details/145067640

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!