ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Vivado 2025.1与VCS 2024.SP1联合仿真及Verdi调试实战指南

Vivado 2025.1与VCS 2024.SP1联合仿真及Verdi调试实战指南 1. 为什么还要折腾 Vivado 与 VCS 的联合仿真如果你平时用 Vivado 自带的仿真器跑 RTL小规模设计还能忍一旦设计里塞进 DDR 控制器、PCIe 硬核、或者带 Memory 的 SoC 子系统仿真速度会慢到让人怀疑人生。Vivado Simulator 在编译大型仿真模型时编译时间动辄十几分钟跑一个后仿真的 Memory 初始化流程可能一晚上都出不来波形。这时候把仿真任务交给 VCS再用 Verdi 看波形是数字前端和 FPGA 验证工程师比较常见的组合。Vivado 2025.1 是 Xilinx 较新的版本VCS 2024.SP1 是 Synopsys 的仿真工具版本两者联合仿真的核心思路并不复杂用 Vivado 生成仿真所需的库文件把 Xilinx 的 IP 仿真模型编译成 VCS 能识别的库然后在 VCS 里调用这些库跑仿真最后用 Verdi 加载 FSDB 波形进行调试。听起来简单但实际操作中编译库的选项、仿真精度、Memory 初始化文件的路径、Verdi 的加载方式每一步都有坑。这篇文章适合两类人一类是刚接触 FPGA 验证、想从 Vivado Simulator 迁移到 VCS 的工程师另一类是被后仿真 Memory 初始化问题折磨过、想找一套稳定流程的从业者。我会把整个流程拆开从库编译的底层逻辑讲到 Verdi 波形调试的实操细节尽量把每个参数为什么这么设讲清楚。提示本文涉及的 Vivado 和 VCS 版本组合是 Vivado 2025.1 与 VCS 2024.SP1其他版本在库编译选项上可能有差异但整体思路一致。2. 联合仿真的底层逻辑Vivado 编译库到底在编译什么2.1 仿真库的本质是预编译的 Verilog/VHDL 模型Xilinx 的 IP 核和原语在仿真时并不是直接拿综合用的网表去跑而是使用行为级或结构级的仿真模型。这些模型分散在 Vivado 安装目录下的data/verilog、data/vhdl等路径里。Vivado 自带的仿真器在启动时会自动编译这些模型但 VCS 不认识 Vivado 的工程结构所以需要提前把这些模型编译成 VCS 的库格式。编译库的过程本质上就是调用 VCS 的vlogan和vhdlan命令把 Xilinx 的仿真源文件编译成 VCS 的库文件存放在指定的库目录中。编译完成后VCS 在仿真时通过-y或-v选项引用这些库就能找到对应的模块定义。这里有个关键点仿真库的编译选项必须和后续仿真时的选项一致尤其是-sverilog、-full64、-timescale这些。如果编译库时用了-sverilog仿真时没用或者反过来都会导致模块找不到或者参数不匹配的错误。2.2 为什么不用 Vivado 自动生成的仿真脚本Vivado 在导出仿真时会生成一个compile.sh或simulate.sh脚本里面包含了仿真库的路径和编译选项。很多人直接拿这个脚本改一改就用结果发现 VCS 报一堆错。原因在于Vivado 生成的脚本默认是给 Vivado Simulator 用的里面的库路径和编译选项并不完全适配 VCS。比如Vivado 生成的脚本里可能会引用xsim相关的库而 VCS 需要的是unisims_ver、simprims_ver、secureip这些库。另外Vivado 2025.1 的 IP 仿真模型可能依赖一些 SystemVerilog 的包如果编译库时没有加-sverilog这些包就无法解析。所以比较稳妥的做法是用 Vivado 的compile_simlib命令手动编译库而不是依赖自动生成的脚本。compile_simlib是 Vivado 提供的一个 Tcl 命令专门用来为第三方仿真器编译仿真库。2.3 compile_simlib 的关键参数拆解compile_simlib的常用参数如下compile_simlib -directory 库输出目录 \ -simulator vcs \ -simulator_exec_path VCS安装路径/bin \ -family all \ -language all \ -library all \ -verbose-directory指定编译后的库文件存放路径。建议放在一个独立的目录比如/home/user/vivado_lib/vcs_2024不要放在 Vivado 工程目录里避免工程清理时误删。-simulator vcs指定目标仿真器为 VCS。-simulator_exec_pathVCS 的可执行文件路径通常是VCS安装目录/bin。这个路径必须正确否则 Vivado 找不到 VCS 的编译器。-family all编译所有器件系列的库。如果只针对特定器件可以指定-family zynq或-family kintex7等减少编译时间。-language all同时编译 Verilog 和 VHDL 库。如果设计里只有 Verilog可以只编译 Verilog节省时间。-library all编译所有库包括unisims_ver、simprims_ver、secureip、xpm等。编译时间取决于器件系列和语言数量全系列全语言编译可能需要 30 分钟到 1 小时。如果只是跑某个具体器件的仿真可以只编译该器件系列的库时间会缩短到 10 分钟左右。注意编译库时Vivado 会调用 VCS 的vlogan和vhdlan如果 VCS 的环境变量没有配置好编译会中途失败。建议先在终端里执行which vlogan确认 VCS 命令可用。3. 从零搭建联合仿真环境库编译与工程配置3.1 环境准备VCS 和 Verdi 的环境变量在 Linux 环境下VCS 和 Verdi 的安装通常由 IT 或 CAD 部门完成但环境变量需要自己配置。典型的配置如下export VCS_HOME/opt/synopsys/vcs/2024.06-SP1 export VERDI_HOME/opt/synopsys/verdi/2024.06-SP1 export PATH$VCS_HOME/bin:$VERDI_HOME/bin:$PATH export LD_LIBRARY_PATH$VCS_HOME/linux64/lib:$VERDI_HOME/linux64/lib:$LD_LIBRARY_PATH配置完成后执行vcs -ID和verdi -version确认版本信息。如果vcs -ID报错说明 License 或者安装路径有问题需要先解决。Vivado 2025.1 的安装路径也需要加入 PATH方便调用vivado和compile_simlibexport XILINX_VIVADO/opt/Xilinx/Vivado/2025.1 export PATH$XILINX_VIVADO/bin:$PATH3.2 用 compile_simlib 编译 VCS 库的完整命令假设 VCS 安装在/opt/synopsys/vcs/2024.06-SP1库输出目录为/home/user/vivado_lib/vcs_2024编译 Zynq UltraScale 系列的库命令如下compile_simlib -directory /home/user/vivado_lib/vcs_2024 \ -simulator vcs \ -simulator_exec_path /opt/synopsys/vcs/2024.06-SP1/bin \ -family zynqultrascaleplus \ -language verilog \ -library all \ -verbose执行后Vivado 会输出编译日志显示每个库的编译进度。编译完成后库目录下会生成unisims_ver、simprims_ver、secureip、xpm等子目录每个子目录里包含编译好的库文件和synopsys_sim.setup文件。synopsys_sim.setup是 VCS 的库映射文件里面定义了逻辑库名和物理路径的对应关系。在仿真时需要通过-synopsys_sim.setup选项指定这个文件或者在当前工作目录下创建一个同名的文件。3.3 仿真工程目录结构的设计一个清晰的仿真工程目录结构能省去很多路径问题。我通常这样组织sim_project/ ├── rtl/ # RTL 源码 ├── tb/ # 测试平台 ├── ip/ # Vivado IP 生成的仿真模型 ├── lib/ # 编译好的 Vivado 仿真库 ├── waves/ # 波形输出目录 ├── filelist.f # 仿真文件列表 ├── run_vcs.sh # VCS 编译和仿真脚本 └── synopsys_sim.setup # 库映射文件filelist.f里列出所有需要编译的 RTL 和 TB 文件以及 IP 仿真模型文件。IP 仿真模型通常在 Vivado 工程的project.srcs/sources_1/ip/ip_name/simulation目录下需要把这些文件路径加入filelist.f。synopsys_sim.setup的内容示例unisims_ver : /home/user/vivado_lib/vcs_2024/unisims_ver simprims_ver : /home/user/vivado_lib/vcs_2024/simprims_ver secureip : /home/user/vivado_lib/vcs_2024/secureip xpm : /home/user/vivado_lib/vcs_2024/xpm这样 VCS 在编译时就能通过逻辑库名找到对应的物理路径。3.4 VCS 编译命令的选项拆解一个典型的 VCS 编译命令如下vcs -full64 -sverilog -debug_accessall \ -timescale1ns/1ps \ -f filelist.f \ -y /home/user/vivado_lib/vcs_2024/unisims_ver \ libext.v.sv \ -synopsys_sim.setup ./synopsys_sim.setup \ -l compile.log \ -o simv-full64编译 64 位仿真器处理大规模设计时必须加。-sverilog支持 SystemVerilogXilinx 的 IP 仿真模型大量使用 SV 语法。-debug_accessall开启调试功能Verdi 需要这个选项才能加载波形和查看信号。-timescale1ns/1ps指定时间精度必须和 RTL 中的 timescale 一致否则时序会错乱。-f filelist.f指定文件列表。-y指定库搜索路径VCS 会在这些路径下查找模块定义。libext.v.sv指定库文件的扩展名。-synopsys_sim.setup指定库映射文件。-l compile.log输出编译日志。-o simv指定输出可执行文件名。编译成功后会生成simv可执行文件。运行./simv即可启动仿真。提示如果编译时报module not found先检查-y路径是否正确以及synopsys_sim.setup里的库映射是否匹配。4. 后仿真 Memory 初始化最容易翻车的环节4.1 后仿真为什么需要 Memory 初始化后仿真Post-Synthesis/Post-Implementation Simulation使用的是综合或实现后的网表网表里的 Memory如 BRAM、URAM通常没有初始值。如果设计里有一个从 Memory 读取数据的启动流程仿真时 Memory 里全是 X读出来的数据也是 X导致仿真结果无意义。解决方法是在仿真开始时通过$readmemh或$readmemb把初始化文件加载到 Memory 中。但在后仿真中Memory 被映射成了 Xilinx 的原语如RAMB36E2这些原语没有直接的$readmemh接口需要通过defparam或者initial块来加载。4.2 用-g选项传递 Memory 初始化文件路径VCS 支持通过-g选项在编译时传递参数可以在 RTL 或网表中用$value$plusargs获取。但在后仿真中更常见的做法是修改网表在 Memory 原语的initial块里加入$readmemh。不过直接修改网表不是好习惯因为每次综合后网表都会变。更好的做法是在测试平台里用force或者deposit的方式在仿真开始时把数据写入 Memory。但这种方法对大型 Memory 效率很低。我比较推荐的做法是在 Vivado 生成网表时勾选“Write Memory Initialization File”选项生成.mem文件然后在测试平台里用$readmemh加载到对应的 Memory 信号上。具体操作是在 Vivado 的 Implementation 设置里找到Write Memory Initialization File选项设置为true。实现完成后Vivado 会在project.runs/impl_1/目录下生成.mem文件。在测试平台里用$readmemh(path/to/memory.mem, dut.memory_instance.mem_array)加载。但这里有个问题后仿真网表里的 Memory 实例名可能和 RTL 不一样需要先在网表里找到对应的实例路径。可以用grep在网表里搜索RAMB或URAM关键字找到实例名。4.3 Memory 初始化文件的格式陷阱.mem文件的格式必须和 Memory 的位宽、深度匹配。如果 Memory 是 32 位宽、1024 深度.mem文件里每行应该是一个 8 位十六进制数32 位 8 个十六进制字符。如果格式不对$readmemh会报错或者加载错误的数据。另外.mem文件里的地址顺序也很重要。有些 Memory 原语的地址是线性的有些是交织的interleaved需要根据原语的数据手册确认。如果加载后发现数据错位大概率是地址映射搞错了。注意后仿真中 Memory 初始化失败最常见的报错是$readmemh: not enough data in file或$readmemh: too much data in file前者说明文件行数不够后者说明行数过多。检查.mem文件的行数和 Memory 深度是否一致。4.4 用 Verdi 的 nWave 查看 Memory 内容Verdi 的 nWave 支持直接查看 Memory 数组的内容。在 nWave 里选中 Memory 信号右键选择Memory-View Memory可以以表格形式查看 Memory 的每个地址和对应的数据。如果发现某些地址是 X说明初始化没有覆盖到这些地址。另外Verdi 的fsdbDumpvars系统任务可以指定 dump Memory 的深度。如果 Memory 很大全 dump 会导致 FSDB 文件巨大仿真速度变慢。可以用fsdbDumpMDA只 dump 需要的 Memory 部分。5. Verdi 波形调试从 FSDB 生成到信号追踪5.1 在测试平台里加入 FSDB dump 代码VCS 默认生成 VCD 波形但 VCD 文件大、加载慢Verdi 更推荐 FSDB 格式。要在仿真中生成 FSDB需要在测试平台里加入以下代码initial begin $fsdbDumpfile(waves/tb.fsdb); $fsdbDumpvars(0, tb); $fsdbDumpMDA(0, tb); end$fsdbDumpfile指定 FSDB 文件名和路径。$fsdbDumpvars(0, tb)dumptb模块下所有层次的信号0表示不限深度。$fsdbDumpMDA(0, tb)dump Memory 数组后仿真中查看 Memory 内容必须加这个。编译时需要链接 Verdi 的 FSDB 库。VCS 的命令里加上vcs -full64 -sverilog -debug_accessall \ -P $VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab \ $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a \ ...-P选项指定 Verdi 的 PLI 接口文件pli.a是静态库。如果 VCS 版本和 Verdi 版本不匹配PLI 接口可能会报错需要确认两个工具的版本兼容性。5.2 Verdi 加载 FSDB 的两种方式仿真运行结束后FSDB 文件会生成在指定目录下。用 Verdi 打开有两种方式方式一命令行直接打开verdi -ssf waves/tb.fsdb -ssf选项直接加载 FSDB 文件Verdi 启动后会自动打开 nWave 窗口。方式二先启动 Verdi再加载 FSDBverdi 然后在 Verdi 的 nWave 窗口里选择File-Open-FSDB选择对应的 FSDB 文件。两种方式效果一样但方式一更适合脚本化流程。如果仿真和调试是分开的比如仿真在服务器上跑调试在本地做可以把 FSDB 文件拷贝到本地再用方式一打开。5.3 用 nWave 做信号追踪和调试Verdi 的 nWave 功能很强但很多人只用了基础的波形查看。以下几个功能在后仿真调试中特别有用Signal Search在 nWave 里按CtrlF可以搜索信号名。后仿真网表里的信号名可能被优化过用搜索功能可以快速定位。Trace Driver/Load选中一个信号右键选择Trace-Driver可以追踪这个信号的驱动源。后仿真中信号被优化时这个功能能帮你找到实际的驱动路径。Event Search在 nWave 里选择Tools-Event Search可以搜索特定的事件比如某个信号从 0 变 1 的时刻。Memory View前面提到的 Memory 查看功能在 nWave 里选中 Memory 信号右键选择Memory-View Memory。另外Verdi 的Hierarchy窗口可以查看设计的层次结构。后仿真网表的层次可能和 RTL 不一样用 Hierarchy 窗口可以快速了解网表的模块结构。5.4 FSDB 文件过大的处理技巧后仿真中如果 dump 了所有信号和 MemoryFSDB 文件可能达到几十 GB加载和查看都很慢。几个优化技巧只 dump 需要的信号用$fsdbDumpvars(1, tb)只 dump 一层信号或者用$fsdbDumpvars(0, tb.u_dut)只 dump DUT 内部的信号。设置 dump 起始时间用$fsdbDumpoff和$fsdbDumpon控制 dump 的时间窗口只在关键时间段 dump。压缩 FSDBVerdi 支持 FSDB 压缩在$fsdbDumpfile里加上-compress选项可以减小文件大小但会增加仿真时的 CPU 开销。提示如果 FSDB 文件已经生成但太大可以用 Verdi 的fsdbedit工具裁剪只保留需要的信号和时间段。6. 联合仿真中那些让人抓狂的报错与解决思路6.1module not found的排查链路这是联合仿真中最常见的报错。排查顺序如下确认synopsys_sim.setup里的库映射路径是否正确。可以用ls命令检查库目录下是否有对应的.v或.sv文件。确认 VCS 命令里的-y路径是否指向库目录。-y路径应该指向包含库文件的目录而不是库文件的父目录。确认libext选项是否包含了库文件的扩展名。Xilinx 的库文件通常是.v或.sv如果只写了libext.vSV 文件就找不到。确认编译库时的选项和仿真时的选项是否一致。比如编译库时用了-sverilog仿真时也必须用。如果以上都确认无误但还是报module not found可以在 VCS 命令里加上-debug_pp或者-v选项查看详细的库搜索过程。6.2timescale不一致导致的时序错乱Vivado 的 IP 仿真模型通常有自己的timescale如果测试平台的timescale和 IP 模型不一致仿真时序会错乱。比如测试平台是1ns/1psIP 模型是1ps/1ps仿真时 IP 内部的延迟会被放大 1000 倍。解决方法在 VCS 命令里用-timescale1ns/1ps统一指定或者在测试平台里用timescale 1ns/1ps覆盖。但注意-timescale选项只对没有指定timescale的模块生效如果模块里已经指定了以模块内的为准。6.3 Verdi 加载 FSDB 时报PLI错误如果 Verdi 加载 FSDB 时报PLI相关错误通常是 VCS 和 Verdi 的版本不匹配或者 PLI 库路径不对。检查以下几点VCS 和 Verdi 的版本是否来自同一个 Synopsys 发布版本。比如 VCS 2024.06-SP1 应该搭配 Verdi 2024.06-SP1。-P选项指定的novas.tab和pli.a路径是否正确。路径通常在$VERDI_HOME/share/PLI/VCS/LINUX64/下。LD_LIBRARY_PATH是否包含了 Verdi 的库路径。如果版本确实不匹配可以尝试用-P指定其他版本的 PLI 库但兼容性无法保证。最好的办法还是统一版本。6.4 后仿真中 Memory 读出 X 的排查后仿真中 Memory 读出 X原因可能有Memory 初始化文件没有加载成功。检查$readmemh的路径是否正确文件是否存在。Memory 实例路径不对。后仿真网表里的实例名可能和 RTL 不一样需要在网表里搜索确认。Memory 的地址映射不对。有些 Memory 原语的地址是交织的需要根据数据手册调整。初始化文件格式不对。检查文件的行数和每行的数据宽度是否匹配。排查时可以在测试平台里加入$display语句打印 Memory 的初始值确认初始化是否成功。6.5 仿真速度慢的优化方向联合仿真速度慢除了设计本身规模大之外还有几个优化方向减少 dump 的信号数量只 dump 关键信号避免全量 dump。使用-debug_accessall的替代选项如果不需要 Verdi 的调试功能可以用-debug_accesspp或者-debug_access减少调试信息的生成。关闭不必要的库编译只编译设计用到的器件系列和语言减少库文件数量。使用 VCS 的增量编译如果只修改了少量文件可以用-Mupdate选项做增量编译避免全量重新编译。7. 一些让流程更顺手的个人经验联合仿真这套流程我踩过的坑主要集中在库编译和 Memory 初始化上。库编译最怕的是 VCS 环境变量没配好compile_simlib跑到一半报错日志里只显示vlogan: command not found。所以每次编译库之前我都会先在终端里执行which vlogan和which vhdlan确认命令可用。Memory 初始化这块我现在的习惯是在 Vivado 里生成.mem文件后先用 Python 脚本检查一下文件的行数和数据宽度确认和 Memory 的配置匹配再放到仿真目录里。这个检查脚本很简单就是读文件、统计行数、检查每行的字符数但能省去很多仿真跑了一半才发现数据不对的时间。Verdi 的 FSDB dump 代码我通常放在测试平台的initial块里但会加一个ifdef开关方便在不需要波形时关闭 dump。比如ifdef DUMP_FSDB initial begin $fsdbDumpfile(waves/tb.fsdb); $fsdbDumpvars(0, tb); $fsdbDumpMDA(0, tb); end endif编译时用defineDUMP_FSDB开启不定义就关闭。这样同一套测试平台可以用于快速回归和详细调试两种场景。最后说一个 Verdi 的小技巧在 nWave 里按Shift鼠标滚轮可以横向缩放波形按Ctrl鼠标滚轮可以纵向缩放。后仿真波形通常很长用这两个快捷键可以快速定位到感兴趣的区间。另外Verdi 的Marker功能可以标记多个时间点方便对比不同时刻的信号状态。
返回列表