简介:《VCS User Guide》是Synopsys面向IC设计与验证工程师发布的VCS仿真工具官方用户指南(S-2021.09版),适合数字、模拟及混合信号芯片验证场景的初中级读者查阅,也可作为搭建仿真环境与排查运行问题的参考手册。压缩包内为1个PDF文件,约11.36MB,内容围绕Getting Started、仿真器配置、许可证获取、环境变量与库映射等基础环节展开,并延伸至命令行与图形界面配置、日志查看和调试工具使用。读者可从中了解VCS的工作机制、工程目录与库管理方式,掌握编译仿真前的系统检查、synopsys_sim.setup配置及常见故障定位思路,从而减少环境搭建试错成本。该文档已有4339人学习,适合需要系统查阅官方说明、补齐验证流程细节的IC设计从业者。
1. 一条 license 报错挡住整条回归:从 checkout 失败说起
做 IC设计 的验证环境里,VCS 最难缠的报错是这种:明明一行代码都没动,昨天还跑得通的用例,今天编译命令刚敲下去就挂了,日志里只有两三行Failed to check out license 'vcs_runtime'。它不指向任何一行 RTL,也不指向 testbench,新手第一反应是重装工具,做了多年验证的人也可能被卡半天。下面几章按实际踩到故障的顺序拆开讲:license 拿不到、模块找不到、波形是空的、结果偶发对不上,最后说明如何在三分钟内判断该看哪一段日志。
2. 编译阶段就挂:Error-[MFNF] Module not found背后是库映射没生效
这条报错几乎每个接手别人环境的人都会遇到一次。它的迷惑之处在于:RTL 明明在 filelist 里,ls也能看到文件,编译却坚持说找不到某个 module。多数人第一反应是 filelist 漏写了,于是逐行核对,半小时过去,真正的问题在库映射上。
2.1 三种“找不到”,报错文案很像,根因差很远
第一类是Error-[MFNF] Module not found: "xxx"。这种情况是设计单元没有进库:文件被读了,但分析时进了 A 库,精化时到 B 库找。第二类是逻辑库本身不存在,日志开头会有cannot open library或直接告诉你某个库不存在,通常是synopsys_sim.setup根本没被读到。第三类是already defined、design unit already exists这种,同一个模块被两个库同时收录,或者上一次的增量残留没清干净。
之所以要先把这三类分开,是因为第一类改 filelist 可能有用,第二类改 filelist 完全无效,第三类改 filelist 只会让你更困惑。判断方法是看报错之前那段日志:如果文件被成功分析过,就是第一类;如果连库都没打开,就是第二类。
2.2 根因:VCS 认的是逻辑库名,不是目录路径
VCS 的库是两层的。你在-work后面写的RTL_LIB是逻辑库名,它要经过一份映射表才能落到磁盘上的物理目录,这份表就是synopsys_sim.setup。表不存在的时候,VCS 会按默认规则自己造一个work目录,于是你用-work RTL_LIB分析进去的东西,和精化阶段找的地方对不上,报错就来了。
查找顺序按优先级排列:-setup命令行指定的文件 →SYNOPSYS_SIM_SETUP环境变量 → 当前工作目录下的synopsys_sim.setup→ 用户主目录下的同名文件。任何一环命中就停,后面的不再看。这条顺序后面会反复用到,它是很多“改了没生效”的根源。
# 文件名固定为 synopsys_sim.setup,一般放在工程根目录 # 语法:逻辑库名 [> 继承别名] [: 物理路径] WORK > DEFAULT DEFAULT : ./work # RTL 与 TB 各自独立成库,路径是相对“执行 vcs 命令时的当前目录” RTL_LIB : ./libs/rtl TB_LIB : ./libs/tb # 只读的 VIP 库,用绝对路径挂进来,避免被误写 VIP_LIB : /opt/vip_lib逐行说明:WORK > DEFAULT是别名继承,让WORK直接复用DEFAULT的物理位置,团队里有人习惯-work work、有人干脆不写,这一行能让两种写法落到同一个目录。冒号右侧才是磁盘路径,而且相对路径是按执行编译命令时的当前目录算的,不是按 setup 文件所在目录算,这一点是后面避坑清单里的首要陷阱。最后一行用绝对路径挂只读库,是为了防止不同人往同一份 VIP 库里写东西,出了问题很难查。
提示:把
synopsys_sim.setup提交进版本库,让所有人的库映射一致,比自己本地建一份更省事。
2.3 照做的修复:清库、分析、精化三步走
# 1) 清掉上一位留下的增量库,不要在旧库上叠加 rm -rf ./work ./libs/rtl ./libs/tb mkdir -p ./work ./libs/rtl ./libs/tb # 2) 分析:-work 写逻辑库名,必须和 setup 里的定义完全一致 vlogan -full64 -sverilog \ -work RTL_LIB \ -f ./filelist_rtl.f \ -l ./vlogan_rtl.log vlogan -full64 -sverilog \ -work TB_LIB \ -f ./filelist_tb.f \ -l ./vlogan_tb.log # 3) 精化并直接起仿真:-top 指顶层,-R 是编完立刻跑 vcs -full64 -sverilog \ -debug_access+all \ -top tb_top \ -l ./vcs_compile.log \ -R参数逐个说明。-full64表示 64 位编译,和后面simv运行的位宽必须一致,混用会在运行期报库格式错,而不是编译期。-sverilog打开 SystemVerilog 语法,纯 Verilog-2001 的工程可以不加,但混了 SV 语法又漏了这个开关,报的是语法错误,很容易把人误导到 RTL 本身。-work后面跟的是逻辑库名,写成./libs/rtl这种路径,VCS 会把它当成一个新库名处理。-f读 filelist,里面还能再写+define+、-y,比在命令行堆一长串好维护。-l把这一阶段日志单独留一份,出事时不用翻终端 scrollback。-top是顶层模块名,大小写敏感,名字写错时报的正是Module not found,而不是“顶层不对”。-R编译完立刻仿真,本地调试方便;上回归时一般去掉它,把编译和仿真拆开,编译一次跑多个用例。
2.4 改完还报同样的错:按这四条顺序查
第一条,环境变量劫持。执行echo $SYNOPSYS_SIM_SETUP,很多人的 shell 启动脚本里早就写死了一份旧路径,它的优先级高于你当前目录那份,改多少遍都没用。第二条,库目录权限,只读库被写会失败,而且有时不报错,用ls -l看目录 mtime 是不是这次编译产生的。第三条,-top名与实际 module 名的大小写和层次,用grep -rn "module tb_top"对一遍最快。第四条,用-liblist显式指定搜索优先级,把RTL_LIB排在TB_LIB前面,避免同名模块被错误解析;这套搜索顺序规则在手册的“Library Search Order Rules”一节写得很细,遇到同名模块冲突时值得翻一遍。
3. 仿真跑完了波形却是空的:$fsdbDumpvars与-debug_access的权限设置
这种问题最耗时间。仿真日志干干净净,simv正常退出,$finish也打印了,但打开波形文件只有几十字节,或者波形工具的层次树里一片空白。接着就开始怀疑 testbench、怀疑路径拼接、怀疑波形工具装坏了,一轮折腾两小时,最后发现是编译选项少了一个。
3.1 先分清三种“没波形”
第一种,波形文件压根没生成。这很少见,通常是$fsdbDumpvars所在的分支没被执行到,或者 dump 调用写在了$finish后面。第二种,文件生成了但只有文件头,几十到几百字节,说明 dump 打开了,但参数指向了一个空层次,比如$fsdbDumpvars(0, tb_top.u_dut)里u_dut名字写错,这种情况往往只有一行 warning。第三种,文件有内容,但你要看的那个信号是条平线,或者根本 grep 不到,这是编译期调试可见性没开,信号被优化掉了。
三类现象对应三种修法,混着查就是白折腾。先ls -lh看文件大小,就能把范围缩掉三分之二。
3.2 根因:可见性在编译期就定了,dump 只是运行期执行
关键在于:VCS 为了性能,默认会在精化阶段裁掉大量信号的调试可访问性。运行期的波形 dump、force/release、UCLI 里的scope和show、跨模块引用,全都依赖编译期是否保留了对应对象的访问能力。没保留,dump 调用可能被静默跳过,也可能报找不到层次。
还有第二层原因,PLI/DPI 的 tab 文件。波形 dump 任务本质上是外部库提供的系统任务,编译时没把对应的.tab链进来,VCS 会把它当成未知系统任务,不同版本表现还不一样,有的是 error,有的只打一行 warning 然后什么都不做。所以“波形空”经常是两个独立问题叠在一起:可见性没开 + tab 没链。
3.3 按需要选档位:三档配置与代价
| 使用场景 | 编译选项 | 能做的事 | 代价 |
|---|---|---|---|
| 本地定位 bug | -debug_access+all | 全信号可见、可 force、UCLI 全功能 | 编译慢、内存高、仿真变慢 |
| 只出波形 | -debug_access+r | 读信号、dump 波形 | 不能 force、不能改值 |
| 回归 + 关键层次 | -debug_access+all -debug_region=<层次> | 指定区域拿全量能力 | 需要事先知道要查哪块 |
# 本地调试:开全量可见性,并链入波形工具提供的 tab 与静态库 vcs -full64 -sverilog \ -debug_access+all \ -P ${WAVE_HOME}/share/PLI/VCS/LINUX64/xxx.tab \ ${WAVE_HOME}/share/PLI/VCS/LINUX64/pli.a \ -top tb_top \ -l ./compile.log \ -R # 回归:只留读权限,波形按层次限定,编译时间能省一截 vcs -full64 -sverilog \ -debug_access+r \ -debug_region=cell+lib \ -top tb_top \ -l ./compile_regress.log-P后面第一个参数是 tab 文件,第二个是与之配套的静态库,路径按你本地波形工具的安装位置替换。-debug_access+all等价于把读、写、force、class 调试能力全打开;+r只保留读权限,本地只看波形足够用;-debug_region决定哪些设计区域获得全量能力,不写就是整个设计。各版本支持的具体取值不完全一样,用vcs -help | grep debug_access翻一下最稳。另外注意,老工程里常见的-debug、-debug_pp属于旧选项,等价于某个固定档位,新环境统一用-debug_access系列,免得和其他选项互相覆盖。
注意:
-debug_access+all别留在回归的编译脚本里,它带来的仿真变慢是实打实的,见过从 6 小时涨到 12 小时的例子。
3.4 跑完怎么确认 dump 真的生效
三步确认。第一步,ls -lh看波形文件大小和 mtime,几十字节基本等于空。第二步,在仿真日志里 grep dump 相关的 warning,重点看 “no such scope”、“not a valid system task” 这两类。第三步,进交互模式后scope一下,看层次能不能列出来;层次都列不出来,波形也不用指望,那说明可见性压根没开。
4. 结果偶发对不上:0 时刻竞争和 X 传播,别靠猜
同一份 RTL、同一条用例,A 机器过 B 机器挂;或者单跑能过、混在回归里就挂;甚至只在某个$display改了之后才好。这类问题最容易被归成“玄学”,其实根子很明确:要么是竞争,要么是 X 传播。
4.1 三个高频触发场景
第一,同一时刻既读又写同一个变量。第二,同一个时刻对同一个 reg 赋值两次,比如两个 always 块各自在时钟沿写同一个寄存器。第三,阻塞赋值和非阻塞赋值混用,导致求值顺序影响最终结果。再加一个隐蔽的:连续赋值之间的求值顺序依赖,assign看起来是并行的,但上游变化传到下游的顺序在不同优化级别下可能有差别。
// 危险写法:两个 always 块在同一时刻写同一个 reg always @(posedge clk) begin if (en_a) q <= d_a; // 非阻塞赋值,进入 NBA 区排队 end always @(posedge clk) begin if (en_b) q <= d_b; // 同一时刻第二次赋值,最后谁生效看调度顺序 end非阻塞赋值在同一个时间片里排队执行,最终写进去的是哪一个,取决于同一区域内语句的调度顺序。当两个en同时有效,仿真结果的随机性就由此产生。
4.2 根因:仿真的调度分层存在,代码里却没约定先后
Verilog 的调度是按区域分的:阻塞赋值在 active 区立即执行,非阻塞赋值推到 NBA 区,同一区域内的语句顺序由代码书写顺序和工具实现共同决定。代码里没写清楚先后关系的地方,仿真器就有合法的自由度去选择。这不是工具的问题,是建模时把顺序留成了未定义。手册“Modeling Your Design”开篇第一节讲的就是这个,值得在改 RTL 之前先读一遍。
这类问题还有一个放大器:复位释放的时机。如果复位信号是从 testbench 直接驱动、而 DUT 内部又是异步复位同步释放,那么释放那一刻的时钟沿附近极容易出现一个时间单位的错位,表现出来就是“复位后第一个周期数据不对”。
4.3 让工具自己找:开动态竞争检测跑一遍
# 编译期打开动态竞争检测,代价是仿真明显变慢 vcs -full64 -sverilog \ -race \ -debug_access+all \ -top tb_top \ -l ./compile_race.log # 跑仿真,竞争信息会写到当前目录新生成的报告文件里 ./simv -l ./run_race.log # 报告文件名各版本略有差异,编完先看一眼目录里多出了什么 ls -lt | head-race是动态检测,只报仿真实际
本文还有配套的精品资源,点击获取