VCS编译与仿真
前言:最近读到一篇关于芯片验证工具VCS编译与仿真的文章,感觉写得特别好遂整理成本篇文章,文章出处如下:
参考:VCS 编译与仿真参数、流程与文件结构解析
VCS 编译仿真流程
VCS 核心工作模式是”两阶段流程”,核心理念是编译与仿真两阶段分离
优势:
- 效率:设计代码只需编译一次,就可以通过不同的运行时参数进行多次不同的仿真(例如,运行不同的测试用例、使用不同的随机种子)。这避免了每次运行都重新解析和翻译整个设计的开销
- 灵活性:编译时关注的是如何“构建”仿真器,例如需要包含哪些调试信息、启用何种语言特性。而运行时则关注如何“使用”这个仿真器,例如具体跑哪个测试、日志详细程度如何
- 可移植性:生成的 simv 文件理论上可以在相同架构和操作系统的其他机器上运行,无需设计源代码
编译阶段
- 输入:设计代码(.v, .sv 等)、测试平台代码、库文件、以及一系列“编译时参数”
- 过程:VCS 首先会对输入的 HDL 代码进行语法分析和解析。然后,它会将这些高级的硬件描述语言代码翻译成中间的 C/C++代码。最后,VCS 会调用系统自带的 C/C++编译器(如 GCC/G++)将这些中间代码编译并链接,生成一个本地机器码的可执行文件
- 输出:默认情况下,这个可执行文件名为 simv。同时,还会生成一些辅助目录和文件,如 csrc 和 simv.daidir
编译流程细化:
- 分析 (Analysis):使用 vlogan (Verilog) 或 vhdlan (VHDL) 命令,将 HDL 源文件解析并编译成中间的、与具体设计层次无关的库单元。这些中间结果通常存放在一个名为 work 的逻辑库(物理上是一个目录)中
- 链接 (Elaboration):vcs 命令在这一步介入,它根据用户指定的顶层模块(top-level module),从分析阶段生成的库中抓取所需的单元,并将它们连接起来,构建起完整的设计层次结构。同时,它会进行参数替换、生成实例等操作
- 代码生成 (Code Generation):链接完成后,VCS 将这个具有完整层次结构的设计模型翻译成 C/C++代码,并最终生成 simv
仿真阶段
- 输入:编译阶段生成的 simv 可执行文件,以及一系列“运行时参数”
- 过程:用户在命令行中直接执行 ./simv,并附上运行时参数。这个可执行文件会根据运行时参数的指令,开始模拟设计行为,执行测试用例,并产生相应的输出
- 输出:仿真日志文件(.log)、波形文件(.vpd, .fsdb)、覆盖率数据(.vdb)等
VCS 编译参数
核心编译控制参数
这类参数控制着编译过程的基本行为,如运行模式、输出文件和资源使用。
-full64:
功能:指示 VCS 在 64 位模式下进行编译和仿真。这将生成一个 64 位的 simv 可执行文件。
适用场景:当设计规模非常大,仿真过程中需要的内存可能超过 4GB 时,必须使用此选项。在现代设计中,这几乎是所有项目的默认选项。使用此选项需要操作系统和硬件本身都是 64 位架构。
语法示例:vcs -full64 my_design.v -o simv_64
-o <filename>:
功能:指定编译后生成的可执行文件的名称。默认名称是 simv。
适用场景:在同一个目录下需要编译多个不同配置或不同设计的仿真模型时,使用此参数可以有效区分它们,避免相互覆盖。例如,一个用于综合后仿真,一个用于 RTL 仿真。
语法示例:vcs design.v -o simv_rtl
-l <logfile>:
功能:指定编译过程的日志文件名。所有编译时的信息、警告和错误都会被重定向到这个文件中。
适用场景:强烈建议始终使用此参数。它能将编译信息与终端的无关输出分离,便于归档、审查和问题追踪。在自动化脚本中,可以通过检查日志文件内容来判断编译是否成功。
语法示例:vcs design.v -l compile.log
-Mdir=<dir>:
功能:指定 VCS 生成中间文件目录(如 csrc 和 simv.daidir)的存放位置。默认是在当前目录下创建。
适用场景:当希望保持主目录清洁,将所有编译生成物集中管理时非常有用。例如,可以创建一个 build 目录来存放所有中间文件。
语法示例:vcs design.v -Mdir=build
-R:
功能:此参数是一个便捷开关,它告诉 VCS 在编译成功后,立即执行生成的 simv 文件。
适用场景:适用于简单的、一次性的编译和运行任务,尤其是在交互式调试或快速验证单个功能点时。在大型回归测试中不常用,因为回归系统通常会将编译和运行作为两个独立的步骤来管理。
语法示例:vcs design.v -R -l sim.log (注意,-R 之后的所有参数都会被传递给 simv 作为运行时参数)。
-jx 或 -j<number_of_processes>:
功能:启用并行编译,使用指定数量的 CPU 核心来加速编译过程。例如 -j8 表示使用 8 个核心。
适用场景:对于中大型项目,并行编译可以显著缩短编译时间。x 的值通常设置为机器的 CPU 核心数。
语法示例:vcs -j16 …
+vcs+lic+wait:
功能:当执行 vcs 命令时,如果当前没有可用的 VCS 许可证(license),此参数会使 VCS 进程进入等待队列,而不是立即失败退出,直到有许可证释放。
适用场景:在许可证资源紧张的共享服务器环境中,这是自动化脚本的必备参数,可以确保编译任务最终能够执行,而不会因为暂时的许可证短缺而失败。
-help/-h:
功能:显示 VCS 的帮助信息,列出常用的编译参数及其简要说明。
设计源文件与库管理参数
这类参数告诉 VCS 在哪里找到设计代码、库代码以及头文件。
<source_files>:
功能:直接在命令行中列出所有需要编译的源文件,这是最基本的方式。
语法示例:vcs top.v sub1.v tb.sv
-f <file>, -F <file>, -file <file> :
功能:这三个参数功能类似,都用于指定一个“文件列表”(filelist)。这个文件是一个文本文件,其中包含了需要编译的源文件路径,每个路径占一行。
区别:-f 中的路径是相对于当前工作目录的。-F 中的路径是相对于该 filelist 文件所在目录的。
适用场景:当项目文件数量众多时,使用 filelist 来管理源文件比在命令行中逐一列出要清晰和方便得多。这是大型项目的标准实践。
语法示例:vcs -f rtl.f -f tb.f
-v <library_file>:
功能:指定一个 Verilog 库文件。VCS 在解析实例化但未找到定义的模块时,会去这个文件中查找模块定义。
适用场景:用于引入标准单元库、IP 库等第三方提供的 Verilog 模型。
-y <library_dir>:
功能:指定一个 Verilog 库目录。VCS 会在该目录下查找与未定义模块同名的文件(例如,寻找 module AND2 时会查找 AND2.v)。+libext+ 参数可以用来指定查找时的文件扩展名。
适用场景:当库文件被组织成每个模块一个文件时,使用 -y 比 -v 更方便。
+incdir+<dir>:
功能:添加一个目录到头文件(include files)的搜索路径中。当代码中使用 `include “filename.vh” 时,VCS 会在此指定的目录中进行搜索,默认搜索的路径是当前仿真目录。
适用场景:用于管理项目中共享的宏定义、参数定义等头文件,使代码结构更清晰。
语言与标准合规性参数
这些参数用于控制 VCS 对不同版本 HDL 语言标准的支持。
-sverilog:
功能:启用对 SystemVerilog 语言特性的支持。没有此开关,VCS 会默认按 Verilog-2001 标准来解析文件。
适用场景:任何使用 SystemVerilog 特性(如 class, interface, constraint, assertion 等)的项目都必须使用此参数。在现代 UVM 验证环境中,这是必须的。
+v2k:
功能:启用对 Verilog-2001 标准的支持。这在早期是一个重要的开关,但在现代 VCS 版本中,大部分 Verilog-2001 的特性已成为默认支持。
适用场景:主要用于兼容一些非常老的代码或环境。
-timescale=<time_unit>/<time_precision>:
功能:为没有 `timescale 编译指令的模块设置默认的时间单位和时间精度。例如 -timescale=1ns/1ps。
适用场景:用于统一项目中模块的时间尺度,或者为那些来自第三方、没有内部定义 timescale 的 IP 设定时间。
-define <macro> / +define+<macro>=<value>:
功能:在编译时从命令行定义一个宏,等效于在代码中使用 `define
适用场景:非常适合用于条件编译。例如,可以通过定义不同的宏来编译不同版本的代码(如 +define+FSDB_DUMP 用于控制是否生成波形,+define+POST_SYNTHESIS 用于区分前后仿)。
设计层次与链接参数
-top <module_name>:
功能:明确指定设计的顶层模块。VCS 从这个模块开始进行链接,构建设计层次。
适用场景:当一个项目中包含多个顶层(例如,多个测试平台)时,此参数是必需的。如果没有指定,VCS 会尝试自动推断顶层,但这可能导致不确定性。明确指定是一种良好的实践
调试与可见性参数(核心)
debug 家族的演进
VCS 的调试参数经历了一个演进过程,了解这个过程有助于理解不同参数的定位。
-debug: 这是一个较早的调试开关,提供了基本的交互式调试能力,但功能有限,性能开销较大。在现代版本中已不推荐使用。
-debug_pp (Post-Processing):
功能:这是一种轻量级的调试模式,主要用于“后处理”调试。它在仿真时以最小的性能开销记录必要的信息,生成波形文件(如 VPD 或 FSDB)。然后,用户可以在仿真结束后,使用 DVE 或 Verdi 等工具加载波形文件进行调试。
优缺点:优点是仿真速度快,适合长时间运行的回归测试。缺点是无法进行交互式调试(如设置断点、单步执行)。
-debug_all:
功能:这是功能最强大的调试开关,它开启了 VCS 所有的调试功能,包括完全的可见性、交互式单步执行、设置任意类型的断点、在仿真过程中修改变量值等。
优缺点:优点是提供了最极致的调试能力,是进行交互式 debug 的首选。缺点是它会生成大量的调试信息,导致编译时间变长、simv 文件变大,并且仿真速度会显著下降。因此,它不适合用于跑批量的回归测试。
debug_access 家族:精细化控制
为了解决 -debug_all 性能开销过大的问题,Synopsys 引入了 -debug_access 系列参数,它允许用户按需、精细地开启所需的调试能力,从而在调试能力和仿真性能之间找到最佳平衡点。
-debug_access (基础开关):
功能:这是一个基础开关,其本身作用有限,通常需要配合后续的子选项来指定具体开启哪种“访问”能力。
-debug_access+all:
功能:这是 -debug_access 系列中最常用的选项。它开启了几乎所有的信号访问和调试能力,功能上非常接近 -debug_all,但在很多现代 VCS 版本中,它的实现经过了优化,性能开销通常会比 -debug_all 稍好一些。它是当前进行交互式调试时,替代 -debug_all 的推荐选项。
-debug_access 的子选项 (Granular Options):
这些子选项提供了外科手术刀式的精细控制,允许只为特定任务开启最小化的调试权限。
+r (Read): -debug_access+r - 启用对设计中所有信号的读取能力。这是生成波形(dump waves)所需的最基本权限。
+w (Write): -debug_access+w - 启用对寄存器(reg)和变量(variable)的写入(deposit)能力。
+wn (Write Net): -debug_access+wn - 启用对线网(net)的写入能力。
+f (Force): -debug_access+f - 启用对寄存器和变量的强制(force)和释放(release)能力。
+fn (Force Net): -debug_access+fn - 启用对线网的强制和释放能力。
+line: -debug_access+line - 启用行断点和单步执行(step/next)功能。这是进行代码级调试的关键。
组合使用: 这些选项可以组合使用,例如 -debug_access+r+line 只开启波形 dump 和单步执行能力,而不开启 force/deposit 能力,从而获得比 -debug_access+all 更好的性能。
-debug_region=<scope>:
功能:此参数可以将上述调试能力的生效范围限制在设计的特定区域(scope)内。例如,只对某个可疑的模块 u_dut 开启完整的调试能力,而设计的其他部分则不包含调试信息。
适用场景:在大型 SoC(片上系统)的调试中极为有用。当问题被定位到某个模块时,可以使用此参数显著减少调试信息带来的性能开销,加快仿真迭代速度。
波形生成
-fsdb:
功能:这是一个便捷开关,它会做两件事:1) 自动在编译时加入必要的调试访问权限(类似于 -debug_access+r);2) 自动在测试平台中插入调用 $fsdbDumpfile 和 $fsdbDumpvars 系统任务的代码,从而在仿真时生成 FSDB 格式的波形文件。FSDB 是 Verdi 调试工具使用的标准波形格式。
适用场景:用于快速生成整个设计的波形,进行基本的调试。对于复杂的 dump 需求(如分层 dump、条件 dump),还是建议在代码中手动调用 FSDB 的系统任务。
+vcs+vcdpluson:
功能:与 -fsdb 类似,但用于生成 Synopsys 自家的 VPD(Verilog Procedural Dump)格式的波形。VPD 文件通常由 DVE 工具使用。
其他调试相关参数
-kdb:
功能:指示 VCS 生成 Verdi 知识库(Knowledge Database, KDB)。这个数据库包含了设计的完整信息,是 Verdi 进行高级调试(如原理图追溯、源代码链接、功耗分析等)的基础。通常与 -debug_access 配合使用。
-xzcheck:
功能:在编译时插入额外的逻辑,用于在仿真时检测和报告设计中 X 态(未知)和 Z 态(高阻)的传播和竞争情况。
适用场景:对于定位复位问题、总线竞争等非常有用,但会带来一定的性能开销。
+vcs+initreg+random:
功能:在仿真开始(0 时刻)时,将设计中所有的寄存器和存储器初始化为一个随机值,而不是默认的 X 态。
适用场景:用于检查设计是否对未正确复位的寄存器有依赖。如果一个设计在默认的 X 态初始化下工作正常,但在随机初始化后出现问题,通常意味着存在复位逻辑缺陷。
代码分析与 Linting 参数
-lint:
功能:开启 VCS 的 Linting 功能,它会在编译时进行一系列静态和动态的代码风格、潜在问题检查。
+warn=<group>: 用于更精细地控制显示或抑制哪一类警告信息。
适用场景:有助于在早期发现代码中不规范的写法、可能导致仿真与综合不匹配的逻辑等问题,是提升代码质量的有效手段。
覆盖率指标参数
-cm :
功能:开启功能覆盖率的收集。可以是 line (行覆盖率), cond (条件覆盖率), fsm (状态机覆盖率), tgl (翻转覆盖率), branch (分支覆盖率)等,也可以用 all 开启所有类型。
适用场景:在覆盖率驱动的验证流程中,这是必选参数。它会在编译时对代码进行“插桩”(instrumentation),以便在仿真时收集覆盖率数据。
与其他语言的接口参数 (PLI/VPI/DPI)
+vpi:
功能:启用 VPI(Verilog Procedural Interface)的支持,允许 C 代码通过 VPI 函数与 Verilog 设计进行交互。
-LDFLAGS:
功能:当需要链接用户编写的 C/C++代码时(例如 PLI/DPI 实现),此参数用于向底层的链接器传递额外的选项,比如链接外部库 (-l)。
UVM (通用验证方法学) 相关参数
+ntb_opts uvm:
功能:这是一个宏开关,用于启用 UVM 库的支持,也可以指定相应的 uvm 版本,只要 vcs 的 etc 目录下有。VCS 会自动找到内置的 UVM 库并进行编译和链接。
适用场景:所有基于 UVM 的验证平台都必须使用此参数。
高级与特殊参数
-lca (Limited Customer Availability):
功能:启用一些处于“受限客户可用”状态的功能。这些功能可能是实验性的、未正式发布的,或者只对特定客户开放的。
适用场景:通常只在 Synopsys 技术支持的指导下使用,用于尝试一些新特性或解决特定问题。其具体功能会随着 VCS 版本变化,没有固定的含义。
-nospecify / -notimingcheck:
功能:-nospecify 指示 VCS 忽略代码中的 specify 块,这些块通常定义了门级延迟信息。-notimingcheck 则禁用时序检查系统任务(如 $setup, $hold)。
适用场景:在 RTL 级的功能仿真中,我们通常不关心精确的时序,使用这两个参数可以关闭时序相关的逻辑,从而加速仿真。在进行门级仿真(gate-level simulation)时则不能使用。
VCS 运行时(仿真)参数
核心仿真控制
-l <logfile> :
功能:指定仿真过程的日志文件名。注意,这与编译时的 -l 是在不同阶段使用的,尽管参数名相同。它会记录 simv 运行期间所有的 $display , $monitor 输出、UVM 报告信息以及仿真结束状态等。
语法示例:./simv -l run.log
+<plusarg>:
功能:这是最通用的运行时参数传递机制。任何以 + 开头的参数(plusarg)都可以被测试平台中的系统任务(如 $test$plusargs 和 $value$plusargs)捕获。
适用场景:用于向测试平台传递变量信息,例如指定随机种子 (+ntb_random_seed=123)、选择要 dump 的波形模块 (+DUMP_WAVE=MODULE_A)等。
交互式与 GUI 模式
-gui:
功能:以图形用户界面(GUI)模式启动仿真。VCS 会自动调用 DVE(Discovery Visual Environment)或 Verdi,并加载当前的设计,进入交互式调试界面。
适用场景:进行深度、可视化的交互式调试。需要编译时已启用相应的调试选项(如 -debug*access+all)。
-ucli:
功能:以统一命令行接口(Unified Command-Line Interface)模式启动仿真。启动后,simv 会进入一个交互式的命令行提示符,用户可以输入命令来控制仿真,如 run (运行), step (单步), show (查看信号值), bp (设置断点)等。
适用场景:适用于不依赖图形界面的远程调试,或者通过脚本自动化执行一系列复杂的调试步骤。
-do <tcl_file>:
功能:在启动 ucli 或 gui 模式时,自动执行一个指定的 Tcl 脚本文件。
适用场景:用于自动化初始设置,例如,可以编写一个 setup.tcl 脚本来设置断点、指定要观察的信号、加载波形等,避免每次手动输入。
UVM 运行时控制
+UVM_TESTNAME=<test_name>:
功能:这是 UVM 最重要的运行时参数。它告诉 UVM 工厂(factory)应该创建并运行哪个 UVM 测试用例(uvm_test)。
语法示例:./simv +UVM_TESTNAME=my_dma_test
+UVM_VERBOSITY=<level>:
功能:设置 UVM 报告的冗余度级别。 可以是 UVM_NONE, UVM_LOW, UVM_MEDIUM, UVM_HIGH, UVM_FULL。级别越高,打印的`uvm_info 信息就越多。
语法示例:./simv +UVM_VERBOSITY=UVM_HIGH
+uvm_set_config_*:
功能:通过命令行直接向 UVM 的配置数据库(uvm_config_db)中设置参数,从而在不修改代码的情况下改变测试平台的行为。
覆盖率数据库控制
-cm_name <name>:
功能:为本次仿真运行产生的覆盖率数据指定一个名称。VCS 会将数据存放在名为 <name>.vdb 的目录中。
-cm_dir <dir>:
功能:指定存放覆盖率数据库的目录。
其他运行时参数
-s:
功能:使仿真在 0 时刻开始后立即暂停。这在 ucli 模式下非常有用,可以让你在仿真真正开始运行前设置好断点和观察点。
+vcs+stop+<time>:
功能:让仿真在指定的 <time> 时刻自动停止。
VCS 编译生成文件与目录解析
每次 VCS 成功编译后,都会在工作目录或 -Mdir 指定的目录下生成一系列文件和文件夹。理解它们的用途对于项目管理、清理和高级调试至关重要。
最终产物:simv 可执行文件
作用:这是编译过程的最终目标,是一个包含了完整设计模型、可以在本地直接运行的二进制可执行文件。
构成:它是由 VCS 将 HDL 翻译成的 C/C++代码,加上 VCS 自身的仿真引擎库、用户可能的 C 代码(PLI/DPI),通过系统 C++编译器(如 g++)编译链接而成。
定制:其名称可以通过 -o 参数在编译时指定。
中间代码:csrc 目录
作用:C-Source 的缩写,这个目录存放着 VCS 将你的 Verilog/SystemVerilog 代码翻译成的中间 C/C++源代码。
内容:包含大量的 .c, .cpp, .h 文件,以及用于编译这些 C 代码的 Makefile。
价值:
增量编译:当用户修改了部分 HDL 代码后再次编译时,VCS 能够通过比较,只重新生成和编译与修改相关的 C 代码,从而大大加快二次编译的速度。csrc 目录是实现增量编译的基础。
底层调试:在极少数情况下,当遇到仿真器本身的疑似 bug 或需要理解某个 HDL 语法结构如何被 VCS 实现时,分析 csrc 中的代码可以提供最底层的线索。
设计数据库:simv.daidir 目录
作用:DAI 是 Direct Access Interface 的缩写。这个目录是一个 VCS 的私有格式数据库,存储了经过链接后设计的完整层次化信息,包括模块实例、信号名称、连接关系等。
内容:一系列二进制数据库文件。
关联:它的名称与 simv 可执行文件强相关,如果可执行文件名是 my_sim, 那么数据库目录就是 my_sim.daidir。
价值:
调试:所有交互式调试功能,如在 DVE/Verdi/UCLI 中按层次路径访问信号、设置断点、单步执行,都依赖于 simv.daidir 提供的设计信息。没有这个数据库,调试器就无法将用户的命令映射到设计内部。
PLI/VPI:PLI/VPI 应用需要通过字符串路径来访问设计内部的信号,这个查找和访问过程也需要 simv.daidir 的支持。
何时生成:只有在编译时使用了需要访问设计数据库的选项(如 -debug_access 系列、PLI/VPI 等)时,VCS 才会生成此目录。
覆盖率数据库:simv.vdb 目录 (或自定义名称)
作用:VDB 是 Verification Database 的缩写。这个目录用于存储仿真运行时收集到的覆盖率数据。
生成:它是在执行 ./simv 时产生的,前提是编译时已经使用了 -cm 选项对代码进行了插桩。
内容:包含了各个覆盖率模型(行、翻转、条件等)的详细数据。
后续:这个数据库本身不是给人直接阅读的,需要使用 Synopsys 的覆盖率报告工具 urg (Unified Report Generator) 对其进行后处理,生成 HTML 格式的、可读的覆盖率报告。目前 verdi 也可以直接加载覆盖率结果,当然先要获取独立的支持 coverage 的 license。
增量编译的“钥匙”:ucli.key 文件
作用:这个文件记录了上次 VCS 编译过程的详细信息,包括编译选项、源文件的哈希值(checksums)等。
功能:在下一次编译时,VCS 会读取 ucli.key 文件,并与当前的编译环境和源文件状态进行比较。如果发现某个文件没有变化,VCS 就可以跳过对该文件的分析和代码生成步骤,直接使用 csrc 中已有的目标文件,从而实现快速的增量编译。
分析库:work 目录
作用:work 目录是 VCS 在传统的“三步流程”中,用于存放分析阶段 (analysis) 产物的默认逻辑库。当 vlogan 或 vhdlan 处理源文件时,它们会将代码解析成一种中间的、与层次无关的表示形式,并存入 work 库。
内容:包含一些 VCS 内部格式的、代表已分析设计单元的文件。
关联:work 目录是链接阶段 (elaboration) 的输入。vcs 命令会从 work 库(以及用 -v / -y 指定的其他库)中提取顶层模块和所有被实例化的子模块,然后将它们连接成完整的设计。链接完成后,才会生成 csrc 和 simv.daidir。因此,work 目录的阶段比 csrc 更早。在现代“一步式” vcs 命令流中,这个目录可能被隐藏或作为临时目录使用,但其概念依然存在。
其他常见文件
日志文件 (.log):compile.log, run.log 等,记录编译和仿真的文本输出。
波形文件 (.vpd, .fsdb):记录仿真过程中信号值的变化,用于调试。
DVE/Verdi 会话文件 (.tcl, .rc):保存 GUI 工具的窗口布局、信号列表等,方便下次恢复调试环境。
VCS 编译及仿真全流程:
1 | (1) 输入: |




