1. 为什么要脱离Vivado自带仿真器自己编译一套VCS仿真库做数字IC前端验证或者FPGA大规模逻辑调试的人对这套组合一定不陌生设计在Vivado里写但真正跑到仿真验证环节却不想用Vivado自带的XSim而是想用VCS跑回归、用Verdi看波形。这几乎是工业界的标配做法原因也很直接——XSim在大型设计和复杂testbench面前效率偏低调试手段也不够灵活而VCS的编译优化、回归管理、覆盖率收集能力都是经过了海量项目验证的。Verdi更是数字前端调试绕不开的一环nWave看波形、debussy现在叫Verdi看原理图、层次结构分析一套流程下来定位问题的速度比单纯靠仿真日志快一个量级。但问题来了Vivado工程里用到了Xilinx的IP核比如FIFO、BRAM、GT收发器、DDR控制器、浮点运算单元这些东西在仿真阶段需要对应的仿真模型库来支撑。Xilinx把这些库以VHDL/Verilog源码或者编译好的库形式放在Vivado安装目录下VCS不能直接拿过来用必须先针对VCS的编译器和仿真器把库编译成VCS认识的格式。这就是我们常说的“编译仿真库”。很多朋友拿到一个Vivado工程用VCS一编译报一堆找不到glbl模块、找不到UNISIM包、secureip编译不过的错误第一反应是VCS装坏了或者Vivado版本不兼容其实根本原因是仿真库没有编好或者编了但指定的路径不对。这篇博文就详细拆解整套操作把我自己在多个项目中反复验证过的编译流程、坑点和优化技巧全部写出来帮助大家少走弯路。这套方法适合谁正在做FPGA原型验证、需要跑Xilinx IP仿真、想把仿真环境从XSim迁移到VCSVerdi、或者刚开始搭数字IC验证环境的同学。理论部分我会讲清楚编译库的底层逻辑实操部分给出完整命令和脚本即便你之前完全没有自己编过仿真库照着做也能把环境跑起来。2. 编译仿真库之前的环境准备版本匹配比想象中更重要2.1 三大工具的版本兼容关系编译仿真库这件事情最容易被低估的就是版本匹配问题。VCS、Verdi、Vivado三个软件分别来自Synopsys和AMD/Xilinx两条产品线它们之间的兼容性并不是哪家官方拍胸脯保证的完全靠实践中摸索。我的经验是Vivado版本不要太新VCS版本不要太老尽量选择两边生态都已经稳定支持的组合。具体来说这里有一个非常实用的判断逻辑VCS的MAJOR版本决定了它对IEEE库文件和Verilog-2005、SystemVerilog-2012等语言标准的支持程度而Vivado版本决定了编译出来的仿真库文件内容。如果VCS版本过老可能对Vivado生成的某些IP模型语法解析出错如果Vivado版本过新库文件里可能使用了最新的原语原语特性老版本VCS读不懂。我这里测试过的比较稳定的组合是这样的Vivado版本VCS版本建议备注Vivado 2019.2 ~ 2020.2VCS 2018.06 ~ 2020.12最稳的组合大项目用的最多Vivado 2021.1 ~ 2022.2VCS 2020.12 ~ 2022.06注意SecureIP库编译后的兼容性Vivado 2023.xVCS 2022.06 及以上新语法多建议用较新VCS有人问Vivado 2022.2配VCS 2017能不能行我也试过能编但编译时会有大量warning仿真时部分IP核行为奇怪尤其是DDR和收发器这类包含模拟行为的模型需要在编译时额外加宏定义来规避问题。所以不要在这上面省事版本选对后面能少踩一整片的坑。2.2 环境变量是仿真库能被找到的前提编译仿真库之前先把环境变量检查一遍。很多“诡异”的编译失败最后查出来都是环境变量没配对。# 查看当前环境 which vcs which verdi echo $VIVADO_HOME ls $VIVADO_HOME/data/verilog/src正常情况下which vcs能返回VCS的安装路径VIVADO_HOME指向Vivado的安装根目录。如果没有输出就要先把环境变量配上。我习惯把所有工具的环境配置写在一个脚本里source一次终身受用。比如export VIVADO_HOME/tools/Xilinx/Vivado/2022.2 export VCS_HOME/tools/synopsys/vcs/O-2018.09-SP2 export VERDI_HOME/tools/synopsys/verdi/Verdi3-2018.09-SP2 export PATH$VCS_HOME/bin:$VERDI_HOME/bin:$VIVADO_HOME/bin:$PATH export LM_LICENSE_FILExxxxlicense_server这里要注意一个细节Verdi和VCS的版本最好在同一个release时间点。比如VCS 2018.09配Verdi 2018.09-SP2这样在后续做fsdb波形dump时Verdi的PLI库和VCS编译器的接口兼容性最好。如果Verdi版本比VCS新太多或者老太多也有可能遇到$fsdbDumpfile无法识别、fsdb波形文件打出乱码之类的问题。2.3 编译库的本质把HDL源码转成VCS认识的“预编译产物”在进入实操之前想清楚一个底层原理能帮你理解后面所有命令的含义。VCS有两种编译方式一是直接把Verilog/VHDL源码全部读入并编译成可执行文件适合小设计二是先把库文件编译成VCS的“预编译库”类似-y加libmap的物理映射再在仿真时通过搜索路径去链接。Vivado仿真库的编译属于后者——因为Xilinx的库源文件非常多每次仿真都重新编译一遍时间和内存都不可接受所以用vlogan/vhdlan分别把Verilog和VHDL库源码转成VCS的库文件存放在专门的目录里。编译好的库在VCS里分两种-y指定的目录库和vcs -comp64用的编译库路径。Vivado的compile_simlib脚本会自动帮你生成一个VCS能识别的库目录结构但生成的脚本有时候不完全符合我们的使用习惯所以很多资深工程师选择自己写脚本调用vcs命令来完成最后的编译步骤。两种方案我都会展开讲。3. 用Vivado的compile_simlib脚本一键生成VCS库以及它的局限性3.1 compile_simlib命令的正确打开方式Vivado自带的compile_simlib其实是一个Tcl脚本位于$VIVADO_HOME/bin目录下。它的作用就是把Vivado安装目录下的官方库源码按你指定的仿真器类型编译成对应该仿真器的库格式。命令行启动方式很简单compile_simlib -simulator vcs -language all -family all -library all -dir ./vcs_lib参数含义-simulator vcs告诉脚本目标仿真器是VCS。-language all同时编译Verilog和VHDL库。如果只是Verilog工程也可以指定-language verilog时间能省一半。-family all编译所有FPGA系列的库。如果只做UltraScale或者7系列用-family ultrasealeplus或-family artix7可以大幅减少编译时间。-library all编译所有类型的库包括unisim、unimacro、secureip、xpm等。-dir ./vcs_lib指定库的输出目录。这里我建议第一次编译时全量编后面如果确定只用某些系列了再增量编译。3.2 编译时间优化技巧不是所有系列都要编很多人在这一步踩坑直接-family all编译结果为一块不是很新的板子编了三个小时的库中间还报了一堆SecureIP的错误。实际上对于绝大多数项目你只需要编译自己用的FPGA系列对应的库。比如一个Artix-7的工程命令可以缩成compile_simlib -simulator vcs -language verilog -family artix7 -library all -dir ./vcs_lib这样的编译时间从三个小时锐减到二十分钟左右而且库目录也更干净。Vivado支持的具体系列名称可以用compile_simlib -help查看。还有一个小技巧如果机器内存不是特别大编译时不要同时开太多并行任务否则内存溢出会直接导致编译进程被杀掉。在compile_simlib里没有直接的并行参数它是串行编译的所以一般还好。真正容易内存溢出的是后面用VCS自己编译SecureIP时那个需要大内存。3.3 compile_simlib生成的目录结构到底生成了什么编译完成后打开vcs_lib目录里面大概长这样vcs_lib/ ├── artix7/ │ ├── secureip/ │ ├── unisim/ │ ├── unimacro/ │ └── xpm/ ├── axi_lite_ipif/ ├── blk_mem_gen_v8_4/ ├── fifo_generator_v13_2/ └── ...这里面分为两块一类是通用库比如unisim、unimacro、secureip、xpm这是所有Vivado工程都可能用到的底层原语库另一类是每个具体IP核生成的仿真目录比如blk_mem_gen_v8_4里面包含该IP核的仿真模型和编译脚本。但这里有个关键点compile_simlib生成的更多是一个“标准答案”。在你的Vivado工程里如果某个IP核的版本和这里编译的不完全一致可能出现仿真时找不到模块或者模块行为不匹配的问题。最好的实践是工程内部使用哪个IP版本就用该版本对应的仿真文件重新编译一遍。所以很多有经验的团队不是直接用compile_simlib的结果而是用它的输出作为基础再结合工程内的IP生成脚本做定向编译。4. 手动编译VCS仿真库一条条命令拆开讲看得懂也改得动4.1 Verilog库的编译命令如果你不想依赖compile_simlib封装好的脚本或者想彻底搞明白每一步在干什么手动编译是最佳路径。先进入库源文件目录cd $VIVADO_HOME/data/verilog/src ls你会看到unisims、unimacro、secureip、xpm等子目录。VCS编译Verilog库的命令一般长这样vcs -sverilog \ -full64 \ -compile -o ./lib_unisim \ -y $VIVADO_HOME/data/verilog/src/unisims \ libext.v \ $VIVADO_HOME/data/verilog/src/unisims/unisim_VCOMP.v拆解一下-sverilog让VCS以SystemVerilog模式解析源码有些Xilinx库源码里用了SV语法不加会报错。-full64以64位模式编译仿真器。现代Vivado和IP库动辄几个GB内存32位模式根本不够用。-compile告诉VCS只编译不链接生成最终可执行仿真文件。-o ./lib_unisim指定编译生成的库文件名。-y加上libext.v这是VCS最经典的库搜索方式。-y告诉VCS到哪个目录去找模块libext.v告诉它找扩展名为.v的文件。两者通常搭配使用。上面的命令其实只是示例。更可靠的做法是用Vivado生成的tcl脚本输出的命令或者用下面这种更贴近项目实际的方式先去$VIVADO_HOME/data/verilog/src下面看真正的文件名。不同版本的文件命名有差异比较常见的是unisim库包含unisim_VCOMP.vVHDL的Verilog封装、unisim_VPKG.v、大量.v文件unimacro库包含unimacro_VCOMP.v和大量宏模型文件secureip库每个IP一个子目录需要单独编译我给大家一个可以直接用的Verilog库编译脚本#!/bin/bash # compile_verilog_lib.sh VIVADO_HOME/tools/Xilinx/Vivado/2022.2 WORK_DIR./vcs_proj_lib mkdir -p $WORK_DIR # 编译unisim vcs -full64 -sverilog -compile \ -o $WORK_DIR/unisim \ -y $VIVADO_HOME/data/verilog/src/unisims \ libext.v \ $VIVADO_HOME/data/verilog/src/unisims/unisim_VCOMP.v \ $VIVADO_HOME/data/verilog/src/unisims/unisim_VPKG.v \ $VIVADO_HOME/data/verilog/src/unisims/unisim_retarget.v # 编译unimacro vcs -full64 -sverilog -compile \ -o $WORK_DIR/unimacro \ -y $VIVADO_HOME/data/verilog/src/unimacro \ libext.v \ $VIVADO_HOME/data/verilog/src/unimacro/unimacro_VCOMP.v # 编译xpm库Xilinx参数化宏 vcs -full64 -sverilog -compile \ -o $WORK_DIR/xpm \ -y $VIVADO_HOME/data/verilog/src/xpm \ libext.v \ $VIVADO_HOME/data/verilog/src/xpm/xpm_VCOMP.v注意有些Vivado版本里unisim目录的路径带sunisims有些不带一定要先ls核实。4.2 SecureIP库单独处理别用通用逻辑编译在编译仿真库这个领域SecureIP是最让人头疼的一块。它包含了一些需要加密仿真模型的IP核比如收发器、高速串行接口、DDR物理层控制等。这些模型以加密形式发布VCS编译时需要专门的流程。SecureIP编译的核心问题在内存。我在刚接触时直接用普通的VCS编译命令去编secureip结果编译器跑了十几分钟后突然报killed进程被杀。看日志才发现是内存不够。SecureIP里的模型包含极大的行为级描述单模块占用内存轻松超过4GB。所以编译secureip库时建议至少有16GB物理内存并且编译时不要开其他吃内存的软件。常见的手动编译方式cd $VIVADO_HOME/data/secureip # 编译secureip注意必须使用-sverilog并且加-v2005兼容选项 vcs -full64 -sverilog -compile \ -o ./vcs_proj_lib/secureip \ -y $VIVADO_HOME/data/secureip \ libext.v \ $VIVADO_HOME/data/secureip/secureip_globals.vsecureip_globals.v是关键文件里面声明了一些全局信号编译时不能漏掉。这个文件的位置需要确认有些版本在data/secureip根目录有些在data/secureip/glbl.v。建议直接find $VIVADO_HOME/data/secureip -name *global*查一下。secureip编译完成后用vcs仿真时一定要加defineSECUREIP_OVERRIDE之类的宏吗其实不需要SecureIP模型编译好后仿真阶段会自动从库路径中找到对应模块。需要注意的反而是编译时的acc1参数如果你的Verdi需要看得见SecureIP模型内部信号编译时可以考虑加-debug_accessall不过这会导致仿真性能下降通常只在调试IP内部信号时才用。常规回归仿真不加。4.3 glbl模块仿真时必不可少的全局复位/时钟资源很多人第一次跑Vivado IP仿真时报错Error: Module glbl not found.glbl是Xilinx仿真库里的一个全局模块里面定义了tris、gsr、gts等全局信号用于模拟FPGA内部的全局置位/复位行为。仿真时顶层结构一般长这样module tb_top; ... glbl glbl(); endmodule但VCS不会自动帮你实例化这个模块你需要手动在testbench里加上或者在编译命令里把它作为顶层文件之一包含进去。推荐的做法是把glbl.v放到testbench同目录然后在VCS编译命令里加上vcs -full64 -sverilog -debug_accessall \ -f filelist.f \ $VIVADO_HOME/data/verilog/src/glbl.v \ -o simv这样仿真时glbl就存在了。注意glbl.v在data/verilog/src目录下所有Vivado版本几乎保持同名。4.4 VHDL库的编译如果你的工程是纯Verilog/SystemVerilog可以跳过这一节。但很多IP核的验证环境或者参考设计是VHDL写的VCS编译VHDL库时需要用vhdlan而不是vcs。# 编译VHDL库unisim vhdlan -full64 \ -work unisim \ $VIVADO_HOME/data/vhdl/src/unisims/unisim_VCOMP.vhd \ $VIVADO_HOME/data/vhdl/src/unisims/unisim_VPKG.vhd \ $VIVADO_HOME/data/vhdl/src/unisims/unisim_retarget.vhd注意vhdlan -work后面指定的库名必须和Vivado IP核综合时生成的库名一致否则仿真是找不到模块的。现代Vivado版本里VHDL库路径在data/vhdl/src下已经按库名分好了子目录。如果vhdlan报语法错误大概率是VCS里的VHDL编译器版本太老对Vivado新版本库源码里的VHDL-2008特性不支持。这种时候要么升级VCS要么在Vivado的IP设置里把目标仿真器文件生成选项改成Verilog-onlyGenerate IP Simulation Files → Verilog。很多场景下可以绕开VHDL编译。5. 把编译好的库和Verdi调试环境无缝接起来5.1 仿真时如何指定库路径让VCS自动找到IP模型仿真库编译完了该怎么用这是把库和应用场景衔接的关键。在VCS编译testbench时用-v和-y参数指定库文件与库路径vcs -full64 -sverilog \ -f rtl_filelist.f \ -f tb_filelist.f \ -v $WORK_DIR/unisim \ -v $WORK_DIR/unimacro \ -y $VIVADO_HOME/data/verilog/src/unisims libext.v \ -y $VIVADO_HOME/data/verilog/src/unimacro libext.v \ -y $VIVADO_HOME/data/secureip libext.v \ $VIVADO_HOME/data/verilog/src/glbl.v \ -o simv这里有几个容易混淆的点-v用于直接指定一个库文件VCS会从该文件中搜索需要的模块。-y用于指定一个目录VCS在该目录下搜索模块搜索的后缀由libext.v控制。二者可以混用实际项目中我偏向用-y指向unisims目录因为模块文件分散在不同子目录-y搜索更全面。如果编译时出现“Module not found”优先检查-y的路径对不对以及libext.v有没有写。很多人漏了libext.v结果目录明明有.v文件VCS就是找不到模块。对于Vivado生成的IP核工程目录下会有一个ip_name.sim/sim_1/synth/的仿真文件里面包含该IP的RTL仿真模型。这个路径通常会出现在project_name.ip_user_files目录下。更省事的方式是使用Vivado生成的filelist文件路径类似于project.gen/sources_1/ip/ip_name/ip_name.sim/sim_1/synth/ip_name.v把这些文件添加到VCS编译列表里仿真实体和在Vivado里仿真完全一致。5.2 Verdi联合仿真的环境配置fsdb波形文件生成仿真做完了怎么看波形这里就要把Verdi接进来。Verdi本身不直接参与编译但它需要一个PLI编程语言接口库才能在VCS仿真时把波形dump成fsdb格式。这个PLI库在Verdi安装目录下通常叫$VERDI_HOME/share/PLI/VCS/LINUX64/verdi.tab $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a使用方式是在VCS编译命令后追加vcs -full64 -sverilog \ -f filelist.f \ -v $WORK_DIR/unisim \ -y $VIVADO_HOME/data/verilog/src/unisims libext.v \ -P $VERDI_HOME/share/PLI/VCS/LINUX64/verdi.tab \ $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a \ -debug_accessall \ -o simv注意-P后面先跟.tab文件再跟.a库文件顺序不能反。编译完成后运行仿真前在testbench里加上fsdb dump的系统任务initial begin $fsdbDumpfile(wave.fsdb); $fsdbDumpvars(0, tb_top, all); end运行时./simv仿真结束目录下出现wave.fsdb然后启动Verdiverdi -f filelist.f -ssf wave.fsdb -top tb_top 这样就能在Verdi里看到完整的层次结构和波形。如果Verdi打开后看不到某个模块的层次多半是VCS编译时没有加-debug_accessall导致层次信息被优化掉了。这一点在调试大型设计时尤其重要all会保留全部信号和层次访问能力代价是仿真速度降低10%~20%但Verdi能访问所有内部信号调试体验完全不同。5.3 后仿真的memory初始化问题前面热搜词里有“vcs后仿memory初始化”这是后端仿真阶段经常遇到的一个问题。带时序的后仿真或者叫网表仿真里如果设计使用了分布式RAM或BRAM初始值往往由initial语句或某个初始化文件.mem、.coe决定。在VCS仿真时如果加了一个不正确的初始化时序比如读写冲突或者异步复位会出现memory内容全X或者部分X。解决思路分两步第一步确认在testbench里把glbl模块正确实例化因为三态门和GSR信号直接影响memory复位后的初始值。第二步在VCS编译和运行阶段加入./simv vcsinitreg0vcsinitreg0表示将所有寄存器初始化为0vcsinitreg1表示初始化为1。如果你的memory模型是用行为级代码写的这条选项能帮你在没有初始文件的情况下给一个干净的初始状态。对于真正的BRAM还要在IP配置里设置Memory Initialization确保.mem文件生成正确并打包进仿真模型。排查这类问题的技巧在Verdi中打开memory实例查看其内部寄存器的初始值是否和预期一致。如果初始值就是X问题多半出在复位时序如果初始值为0但业务逻辑错误那就要检查初始化文件是否成功加载到模型中。Verdi里可以查看$readmemh或$readmemb读取的文件路径节省很多时间。5.4 FSDB波形过大用层次过滤和信号过滤减小文件大型仿真的wave.fsdb动辄几十GBVerdi打开卡得半天磁盘也受不了。我用的办法是在testbench里不直接$fsdbDumpvars(0, ...)全量dump而是指定dump层级initial begin $fsdbDumpfile(wave.fsdb); $fsdbDumpvars(0, tb_top.dut, all); // 只dump dut层次 // 或者更深层 $fsdbDumpvars(4, tb_top.dut.axi_master, all); end层级数字表示往下dump多少层。0表示全部层级4表示到第四层。如果只关心某个子模块可以像上面这样限定范围。更进一步可以用VCS的defineDUMP_ONLY配合条件编译ifdef DUMP_ONLY initial begin $fsdbDumpfile(wave.fsdb); $fsdbDumpvars(0, tb_top.dut.axil, all); end endif编译时加defineDUMP_ONLY不调试时就不加这样回归仿真完全不产生FSDB性能大幅提升。6. 仿真库编译与联合仿真的高频坑点排查实录6.1 编译报错Unknown identifier - 大概率是宏定义问题新拿一个Vivado工程用VCS编译经常遇到类似Error: Unknown identifier: XIL_PARAM_TEST_ALL这类错误通常源自Xilinx库源码本身使用了条件编译宏。不同Vivado版本对编译时宏定义的要求不同解决方法是打开报错文件看\ifdef或者ifndef的分支再把对应的宏在编译命令里加上。比如常见的defineXILINX_SIMULATOR defineXILINX_FPGA defineSIM_AVAILABLE具体需要哪个宏可以直接搜索报错文件中未定义的标识符。这些宏的作用域大多是让库源码走“仿真分支”而不是“综合分支”。6.2 仿真时IP核行为异常复位后输出高阻Z这通常是glbl模块没有正确加载或者复位信号没有正确同步。症状是仿真能跑但IP输出全是Z或者X。排查步骤确认VCS编译命令行里有glbl.v。在testbench顶层实例化glbl glbl();。检查复位时序是否满足IP要求。有些IP要求复位至少保持几个时钟周期而testbench里的复位可能太短导致初始化流程没有完成。Verdi定位时直接看glbl层次里的GSR信号是否正常翻转。如果GSR没有按预期变化大概率是testbench里的激励没接对。6.3 内存占用过高导致编译kill编译secureip或超大IP时VCS进程被系统杀掉日志里出现Killed。这种情况基本都是物理内存不足。除了加大内存还可以优化编译方式分模块编译先用-compile单独编译每个库不要在一条命令里编译所有库。减少并行任务如果你用了-j并行参数降为2或1。关闭VCS的某些高级优化编译时加-O0或-noIncremental能降低峰值内存。64位VCS比32位VCS内存占用大一些但这是必要的——4GB以上的设计用32位编译根本不可能。6.4 Verdi打开FSDB后波形全X或者波形不更新这个问题我遇到过多次最终定位到两个原因。第一VCS仿真阶段确实生成了fsdb但文件里记录的信号名和Verdi打开的-top不匹配。处理方式在Verdi里用-ssf指定正确的fsdb然后在nWave里重新“Get Signals”刷新层次。第二FSDB文件本身损坏仿真实例异常退出导致。这时候重新跑仿真但要注意VCS跑完后应当正常退出$finish不要在仿真中途用CtrlC杀掉。如果实在要中断最好先让testbench跑到一个安全点再中断。6.5 常见问题速查表现象根本原因解决方法编译报Module glbl not found没有编译glbl.v编译命令加入glbl.v文件路径仿真输出全部为X缺少UNISIM库或库路径错误检查-y目录和libext.v编译SecureIP时内存被杀物理内存不足单独编译secureip加大内存Verdi打开后看不到层次编译时没有debug信息加-debug_accessallIP核行为有时序异常IP仿真模型与版本不匹配用工程特定版本的IP仿真文件编译VHDL库编译失败VCS不支持新VHDL语法升级VCS或改用Verilog仿真模型FSDB文件极大全层次dump用$fsdbDumpvars(层级,...)限制范围复位后信号高阻Zglbl未实例化在testbench手动实例化glbl glbl();仿真速度极慢编译时加了很多调试选项回归仿真去掉-debug_accessall6.6 增量编译改testbench不用重编整个库仿真库一旦编译好属于相对静态的部分。后续每天改的是testbench和RTL。VCS提供了增量编译机制使用-Mupdate参数或-incrementalVCS会检测哪些源码文件发生变化只重新编译变化的部分链接时复用之前生成的对象。用法很简单第一次完整编译vcs -full64 -sverilog -f filelist.f \ -v $WORK_DIR/unisim \ -Mupdate \ -o simv之后每次修改testbench再用同样的命令编译VCS会自动分析增量。实测下来一个大工程从零编译需要十分钟增量编译只要二十秒。这个技巧在迭代调试阶段非常香。7. 把整个流程封装成自动化脚本团队复用更快环境跑通之后我强烈建议把编译仿真库、编译RTL、跑仿真、开Verdi这一整套流程写成Makefile或Shell脚本。这里分享一个精简版的Makefile模板VIVADO_HOME /tools/Xilinx/Vivado/2022.2 VCS_HOME /tools/synopsys/vcs/O-2018.09-SP2 VERDI_HOME /tools/synopsys/verdi/Verdi3-2018.09-SP2 LIB_DIR ./vcs_proj_lib VCS $(VCS_HOME)/bin/vcs VCS_OPTS -full64 -sverilog -Mupdate -debug_accessall UNISIM_DIR $(VIVADO_HOME)/data/verilog/src/unisims UNIMACRO_DIR $(VIVADO_HOME)/data/verilog/src/unimacro SECUREIP_DIR $(VIVADO_HOME)/data/secureip GLBL_FILE $(VIVADO_HOME)/data/verilog/src/glbl.v .PHONY: lib compile sim verdi clean lib: mkdir -p $(LIB_DIR) vcs -full64 -sverilog -compile -o $(LIB_DIR)/unisim \ -y $(UNISIM_DIR) libext.v \ $(UNISIM_DIR)/unisim_VCOMP.v $(UNISIM_DIR)/unisim_VPKG.v vcs -full64 -sverilog -compile -o $(LIB_DIR)/unimacro \ -y $(UNIMACRO_DIR) libext.v \ $(UNIMACRO_DIR)/unimacro_VCOMP.v vcs -full64 -sverilog -compile -o $(LIB_DIR)/secureip \ -y $(SECUREIP_DIR) libext.v \ $(SECUREIP_DIR)/secureip_globals.v compile: $(VCS) $(VCS_OPTS) -f filelist.f \ -v $(LIB_DIR)/unisim -v $(LIB_DIR)/unimacro \ -y $(UNISIM_DIR) libext.v \ -y $(SECUREIP_DIR) libext.v \ $(GLBL_FILE) \ -P $(VERDI_HOME)/share/PLI/VCS/LINUX64/verdi.tab \ $(VERDI_HOME)/share/PLI/VCS/LINUX64/pli.a \ -o simv sim: ./simv vcsinitreg0 verdi: verdi -f filelist.f -ssf wave.fsdb -top tb_top clean: rm -rf simv simv.daidir csrc *.fsdb ucli.key这个Makefile划分了几个核心目标make lib编译仿真库make compile编译整个仿真可执行文件make sim运行仿真make verdi打开波形调试。团队里新同学拿到这个Makefile只要环境变量配置正确一条命令跑通仿真学习成本降到最低。最后分享一点个人体会从最开始照着网上的命令一行行敲到后来能把整个流程封装成脚本最深的体会是仿真库编译不是一锤子买卖它会随着工程里IP版本的变化而不断维护。环境稳定之后大部分时间我们不需要关心库本身但当它出问题的时候理解了编译原理和VCS的库搜索机制你就能快速定位到具体是哪个库、哪个模块、哪种语法出了问题而不是靠运气去试。另外想提醒一下工具版本更新后务必要重新编译一遍仿真库尤其是升级Vivado大版本时。很多人图省事觉得库文件都是差不多的东西拷贝过来直接用结果仿真出现莫名其妙的X态或者崩溃溯源下来全是库版本不匹配埋的雷。我的习惯是每换一次Vivado版本就重新编译一次全套库宁可在编译上花二十分钟也不要在调试时花两天。如果后续项目里遇到比较特殊的IP仿真问题比如GT收发器、DDR4控制器、以太网MAC的仿真模型有异常可以从Vivado的仿真文件生成设置入手把IP的Generate IP Simulation Files选项重新生成一遍再配合VCS编译选项调整大概率能解决。搞仿真环境就是这样一个需要耐心和细致的过程环境搭好了后面所有验证工作都会顺畅很多。