从零开始讲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基本命令解析https://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 主要功能
初始化校准完成:
init_calib_complete
信号在任何命令发送之前被置为高,以确保DDR控制器初始化完成。命令生成:在测试平台中,使用
WR_INSTR
(写命令)和RD_INSTR
(读命令)来驱动DDR内存的读写操作。首先发送一组写命令,直到达到设定的写命令数量(NUM_WRITES
)。然后开始发送读命令,直到读取操作完成。地址生成:地址是基于一个固定的起始地址
BEGIN_ADDRESS
生成的,并且每次命令发出时会增加。写命令和读命令分别使用相同的地址,但读命令是在所有写命令完成后才开始的。写数据生成:
app_wdf_data
生成的是写入DDR的实际数据,数据根据wr_data
值来生成。数据在每个时钟周期内根据一定的规则递增。命令使能和数据使能:
cmd_en
信号在命令和数据需要时被激活,用来确保命令和数据的正确传输。错误检查:平台还实现了一个比较功能,用于检查读写操作中是否存在数据错误。通过比较实际读取的数据和期望数据,来验证是否出现了错误。若发生错误,
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)!