OpenLava EDA 实战(三):VCS 仿真最佳实践
OpenLava EDA 实战(三):VCS 仿真最佳实践
摘要:VCS 是数字 IC 验证最常用的仿真器,怎么在 OpenLava 上高效地跑 VCS 仿真大有讲究。本文从实际工作流出发,讲解 VCS 编译与仿真的分离提交、并行编译加速、波形管理(fsdb + 后处理)、UVM 仿真技巧、回归测试组织、以及常见问题排查。每个场景都有可直接套用的脚本模板。
前两篇讲了 EDA 集群搭建和 License 调度。这一篇聚焦 VCS 仿真——数字验证工程师每天都要用的场景。
零、读前必看
0.1 适合谁
- ✅ 数字验证工程师,每天和 VCS 打交道
- ✅ 想把本地跑的 VCS 仿真搬到集群上
- ✅ CAD 工程师,想优化 VCS 仿真流程
0.2 你需要准备
| 项目 | 要求 |
|---|---|
| OpenLava 集群 | 已搭好,VCS 工具已接入 |
| VCS 授权 | VCS_MX License |
| 测试用例 | 至少一个可跑的 UVM testbench |
| Verdi | 可选,用于调试波形 |
一、VCS 仿真工作流
1.1 典型流程
编译 (VCS compile)
│
├── 输入:RTL + Testbench 文件列表
├── 输出:simv 可执行文件
└── 一次性(代码改了才需要重编)
│
▼
仿真 (simv runtime)
│
├── 输入:simv + 测试用例参数
├── 输出:仿真结果 + 波形
└── 可以并发跑 N 个测试用例
关键认知:编译一次,仿真多次。编译是串行的(一次 VCS 编译产出一个 simv),仿真可以并行(多个 testcase 共用同一个 simv)。
1.2 为什么要放集群上跑
| 本地跑 | 集群跑 |
|---|---|
| 一次跑 1-2 个仿真 | 一次跑几百个 regression |
| 占着自己电脑 | 不影响本地工作 |
| 机器配置有限 | 64 核 256G 大内存机器随便用 |
| 出结果慢 | 并行跑,回归时间从几天变几小时 |
| 手动管理 | 自动调度 + 结果汇总 |
二、编译作业提交
2.1 基础编译脚本
#!/bin/bash
#BSUB -J vcs_compile
#BSUB -q vcs_normal
#BSUB -n 8
#BSUB -R "rusage[mem=8192, vcs_license=1] span[hosts=1]"
#BSUB -o compile_%J.log
#BSUB -e compile_%J.err
#BSUB -W 2:00
# ===== 环境加载 =====
source /etc/profile.d/modules.sh
module load synopsys/scl/2019.06
module load synopsys/vcs/O-2018.09-SP2
module load synopsys/verdi/Verdi_O-2018.09-SP2
# ===== 切换到提交目录 =====
cd $LS_SUBCWD
echo "========================================"
echo " VCS Compilation Started"
echo " Job ID: $LSB_JOBID"
echo " Host: $(hostname)"
echo " Cores: $LSB_DJOB_NUMPROC"
echo " Start: $(date)"
echo "========================================"
# ===== 编译 =====
vcs -full64 \
-f filelist.f \
-top tb_top \
-debug_access+all \
-o simv \
-j$LSB_DJOB_NUMPROC \
-ntb_opts uvm-1.2 \
-sverilog \
+v2k \
+incdir+./rtl \
+incdir+./tb \
-LDFLAGS -Wl,--no-as-needed
EXIT_CODE=$?
echo "========================================"
echo " VCS Compilation Finished"
echo " Exit Code: $EXIT_CODE"
echo " End Time: $(date)"
echo "========================================"
exit $EXIT_CODE
2.2 关键参数说明
| VCS 参数 | 作用 | 集群相关 |
|---|---|---|
-j N |
并行编译,N 是线程数 | ✅ 对应 -n N 申请的核数 |
-full64 |
64 位模式 | ✅ 大设计必须开 |
-debug_access+all |
生成调试信息(Verdi/断点) | 会慢一点,调试用 |
-o simv |
输出文件名 | |
-f filelist.f |
文件列表 | |
-ntb_opts uvm-1.2 |
使用 UVM 1.2 | |
-sverilog |
支持 SystemVerilog | |
-LDFLAGS -Wl,--no-as-needed |
链接参数(某些系统需要) |
2.3 核数和内存评估
| 设计规模 | 建议核数 | 建议内存 | 编译时间参考 |
|---|---|---|---|
| 小(< 10 万门) | 4 | 4G | < 5 分钟 |
| 中(10-100 万门) | 8 | 16G | 10-30 分钟 |
| 大(100 万+ 门) | 16 | 32-64G | 30 分钟 - 2 小时 |
| 超大(千万门级) | 32 | 128G+ | 2 小时+ |
**经验**:VCS 编译不是核越多越快。核数超过一定数量后加速比下降。建议从 8 核开始,根据实际情况调整。
2.4 增量编译
代码小改动时,用增量编译快很多:
# 加 -Mupdate 参数
vcs -full64 -Mupdate -f filelist.f ...
原理:保留编译中间文件,只重编改动的文件。
注意:
- 第一次编译必须全量(生成中间文件)
- 改动大时增量可能更慢(权衡使用)
- CI/回归建议全量编译,保证干净
三、仿真作业提交
3.1 基础仿真脚本
#!/bin/bash
#BSUB -J vcs_sim_${TESTCASE}
#BSUB -q vcs_normal
#BSUB -n 1
#BSUB -R "rusage[mem=8192, vcs_license=1] span[hosts=1]"
#BSUB -o sim_%J_${TESTCASE}.log
#BSUB -e sim_%J_${TESTCASE}.err
#BSUB -W 8:00
# ===== 环境 =====
source /etc/profile.d/modules.sh
module load synopsys/scl/2019.06
module load synopsys/vcs/O-2018.09-SP2
module load synopsys/verdi/Verdi_O-2018.09-SP2
cd $LS_SUBCWD
echo "========================================"
echo " TestCase: $TESTCASE"
echo " Job ID: $LSB_JOBID"
echo " Host: $(hostname)"
echo " Start: $(date)"
echo "========================================"
# ===== 仿真 =====
./simv \
+UVM_TESTNAME=$TESTCASE \
+UVM_VERBOSITY=LOW \
+UVM_MAX_QUIT_COUNT=100 \
+UVM_TIMEOUT=1000000000ns \
-l sim_${TESTCASE}.log \
-ucli -do "run -all; quit"
EXIT_CODE=$?
echo "========================================"
echo " TestCase: $TESTCASE"
echo " Exit Code: $EXIT_CODE"
echo " End Time: $(date)"
echo "========================================"
# ===== 检查仿真结果 =====
if grep -q "UVM_ERROR : 0" sim_${TESTCASE}.log && \
grep -q "UVM_FATAL : 0" sim_${TESTCASE}.log; then
echo "RESULT: PASS"
else
echo "RESULT: FAIL"
fi
exit $EXIT_CODE
提交:
bsub -env "TESTCASE=test_smoke" < vcs_sim.sh
3.2 仿真内存估算
| 仿真类型 | 内存估算 |
|---|---|
| 小 testcase,无波形 | 1-2G |
| 中等 testcase | 4-8G |
| 大 testcase + 全波形 | 16-64G |
| 门级仿真 | 32-128G |
**全波形(fsdb)对内存影响很大**。建议:
- 调试时才开全波形
- 回归测试只记关键信号或用断言
- 失败的用例自动保存波形,通过的不用存
3.3 并行跑多个用例
用作业数组最方便:
#!/bin/bash
#BSUB -J "vcs_regress[1-100]%20"
#BSUB -q vcs_normal
#BSUB -n 1
#BSUB -R "rusage[mem=4096, vcs_license=1] span[hosts=1]"
#BSUB -o regress/%J_%I.log
#BSUB -e regress/%J_%I.err
#BSUB -W 4:00
source /etc/profile.d/modules.sh
module load synopsys/scl/2019.06
module load synopsys/vcs/O-2018.09-SP2
cd $LS_SUBCWD
# 测试用例列表文件(一行一个用例名)
TESTCASE=$(sed -n "${LSB_JOBINDEX}p" testlist.txt)
echo "Running: $TESTCASE (index $LSB_JOBINDEX)"
./simv +UVM_TESTNAME=$TESTCASE +UVM_VERBOSITY=LOW \
-l regress/${TESTCASE}.log
# 结果判断
if grep -q "TEST PASSED" regress/${TESTCASE}.log; then
echo "RESULT: PASS - $TESTCASE"
echo "$TESTCASE PASS" >> regress/results.txt
else
echo "RESULT: FAIL - $TESTCASE"
echo "$TESTCASE FAIL" >> regress/results.txt
fi
提交:
mkdir -p regress
bsub < vcs_regress.sh
`%20` 表示最多同时跑 20 个,避免把 License 或集群资源全占满。
四、波形管理
4.1 波形格式选择
| 格式 | 工具 | 文件大小 | 速度 |
|---|---|---|---|
| fsdb | Verdi | 小(压缩) | 快 |
| vpd | DVE | 中 | 中 |
| vcd | 通用 | 大(纯文本) | 慢 |
Verdi + fsdb 是行业标配。
4.2 生成 fsdb 波形
在 Testbench 里加:
// tb_top.sv
initial begin
if ($test$plusargs("DUMP_FSDB")) begin
$fsdbDumpfile("wave.fsdb");
$fsdbDumpvars(0, tb_top);
$fsdbDumpMDA();
$fsdbDumpSVA();
$fsdbDumpflush();
end
end
仿真时加参数:
./simv +DUMP_FSDB +UVM_TESTNAME=$TESTCASE
4.3 波形存哪里
| 存储位置 | 优点 | 缺点 |
|---|---|---|
本地 /tmp |
快,不占共享存储 | 节点宕机就丢了 |
| 共享存储 | 持久化,随时查看 | 占 NFS 带宽,慢 |
| 本地生成 + 跑完归档 | 兼顾性能和持久化 | 需要脚本管理 |
推荐方案:仿真时写本地 /tmp,跑完把 fsdb 移到共享存储:
# ===== 仿真 =====
# 先在本地 /tmp 跑(速度快)
WORK_DIR=/tmp/vcs_${LSB_JOBID}
mkdir -p $WORK_DIR
cd $WORK_DIR
# 链接 simv(不要复制,占空间)
ln -s $LS_SUBCWD/simv .
ln -s $LS_SUBCWD/simv.daidir .
# 跑仿真(波形输出到本地)
./simv +DUMP_FSDB +UVM_TESTNAME=$TESTCASE \
-l sim.log
# ===== 归档 =====
# 跑完把结果和波形移回共享存储
mkdir -p $LS_SUBCWD/results/$TESTCASE
cp sim.log $LS_SUBCWD/results/$TESTCASE/
if [ -f wave.fsdb ]; then
mv wave.fsdb $LS_SUBCWD/results/$TESTCASE/
fi
# 清理临时目录
rm -rf $WORK_DIR
4.4 失败自动保存波形
回归测试时,默认不开波形(省资源),失败时自动重跑并抓波形:
#!/bin/bash
# 先跑一次(无波形,快)
./simv +UVM_TESTNAME=$TESTCASE +UVM_VERBOSITY=LOW -l sim.log
if [ $? -ne 0 ] || grep -q "UVM_ERROR.*[1-9]" sim.log; then
echo "Test failed, re-running with waveform..."
# 失败了,带波形重跑一次
./simv +DUMP_FSDB +UVM_TESTNAME=$TESTCASE \
+UVM_VERBOSITY=DEBUG \
-l sim_debug.log
fi
**策略**:
- PASS 的用例:不存波形(省空间)
- FAIL 的用例:存完整波形(方便调试)
>
这样 100 个用例即使 95% pass,也只存 5 个波形文件。
五、UVM 仿真技巧
5.1 Verbosity 控制
# 回归测试:只看错误(最快)
+UVM_VERBOSITY=LOW
# 调试用:详细日志
+UVM_VERBOSITY=DEBUG
# 单个组件设 verbosity
+uvm_set_verbosity=uvm_test_top.env.agt,DEBUG,all
5.2 超时保护
# 防止仿真卡死跑无限久
+UVM_TIMEOUT=1000000000ns,YES
# 同时用 bsub -W 设置上限(双重保护)
#BSUB -W 4:00
5.3 随机种子
# 用作业 ID 做随机种子(每个作业不同,可复现)
./simv +UVM_TESTNAME=$TESTCASE +ntb_random_seed=$LSB_JOBID
用 `$LSB_JOBID` 当种子,失败时可以用同样的种子复现。
5.4 覆盖率收集
# 编译时加覆盖率选项
vcs -full64 -cm line+cond+fsm+tgl+path ...
# 仿真时收集
./simv -cm line+cond+fsm+tgl -cm_dir cov.vdb +UVM_TESTNAME=$TESTCASE
# 合并覆盖率
urg -dir cov.vdb -format both -report cov_report
覆盖率收集会让仿真慢 2-3 倍,一般只在覆盖率回归时打开。
六、常见问题排查
6.1 编译失败:License checkout failed
Error-[VCS_COM] VCS compilation error
License checkout for VCS_MX failed: Cannot find license file.
排查:
# 1. 环境变量对不对
echo $SNPSLMD_LICENSE_FILE
# 2. License 服务器通不通
lmstat -c 27000@lic_server01
# 3. 还有没有可用 License
lmstat -f VCS_MX -c 27000@lic_server01
常见原因:
LM_LICENSE_FILE/SNPSLMD_LICENSE_FILE没设对- License 服务器网络不通
- License 用完了(OpenLava 应该调度好,但采集有延迟)
6.2 仿真 OOM(内存不足)
Out of memory. Killed by OS.
排查:
# 1. 看节点内存
lsload -l compute01
# 2. 看作业用了多少内存
bjobs -l <jobid> | grep -i mem
解决:
- 加内存(
-R "rusage[mem=16384]"改大) - 减少波形 dump 深度
- 分阶段跑仿真
- 换大内存节点
6.3 仿真速度慢
可能原因:
| 原因 | 解决 |
|---|---|
| 开了全波形 | 只 dump 关键模块 / 用 $fsdbDumpvars 减少层次 |
| 开了覆盖率 | 非覆盖率回归关掉 |
| debug_access+all | 回归用 -debug_access+pp 或更少 |
| 存储慢(NFS) | 波形写本地 SSD |
| 设计太大 | 分模块仿真 / 硬件加速 |
6.4 仿真卡死不动
# 1. 看是不是真在跑
bpeek -f <jobid>
# 日志长时间没更新,可能卡死了
# 2. 进节点看 CPU 占用
ssh <host> top -p <pid>
# CPU 0% 说明卡死了
# 3. 看调用栈(如果有 gdb)
gdb -p <pid> -ex "bt" -ex "quit"
常见原因:
- 死循环 / 死锁
- 等待外部输入
- 文件系统 hang 住(NFS 问题)
设置超时:+UVM_TIMEOUT + bsub -W 双重保护。
6.5 为什么 +UVM_VERBOSITY 不生效
检查 VCS 编译时有没有带 -ntb_opts uvm 参数,UVM 库链进去了没。
七、提交规范建议
7.1 团队推荐脚本模板
#!/bin/bash
# ==============================================
# VCS Simulation Script
# Usage: bsub -env "TESTCASE=xxx" < vcs_sim.sh
# ==============================================
#BSUB -J sim_${TESTCASE}
#BSUB -q vcs_normal
#BSUB -n 1
#BSUB -R "rusage[mem=4096, vcs_license=1] span[hosts=1]"
#BSUB -W 4:00
#BSUB -o sim_%J_${TESTCASE}.log
#BSUB -e sim_%J_${TESTCASE}.err
#BSUB -u ${USER}@example.com
#BSUB -N
# 环境
source /etc/profile.d/modules.sh
module load synopsys/vcs/O-2018.09-SP2
# 检查参数
if [ -z "$TESTCASE" ]; then
echo "ERROR: TESTCASE not set!"
echo "Usage: bsub -env \"TESTCASE=xxx\" < $0"
exit 1
fi
# 工作目录
cd $LS_SUBCWD || exit 1
# 日志头
echo "[$(date)] TestCase: $TESTCASE"
echo "[$(date)] JobID: $LSB_JOBID"
echo "[$(date)] Host: $(hostname)"
echo "[$(date)] Seed: ${SEED:-random}"
# 运行
./simv +UVM_TESTNAME=$TESTCASE \
+ntb_random_seed=${SEED:-$LSB_JOBID} \
${DUMP_FSDB:++DUMP_FSDB} \
+UVM_VERBOSITY=${VERBOSITY:-LOW} \
+UVM_TIMEOUT=500000000ns \
-l sim_${TESTCASE}_${LSB_JOBID}.log
EXIT=$?
# 结果分析
echo "[$(date)] Exit code: $EXIT"
grep -E "TEST (PASSED|FAILED)" sim_${TESTCASE}_${LSB_JOBID}.log || true
grep -E "UVM_ERROR|UVM_FATAL" sim_${TESTCASE}_${LSB_JOBID}.log | tail -5 || true
exit $EXIT
7.2 常用提交方式
# 提交单个用例
bsub -env "TESTCASE=test_smoke" < vcs_sim.sh
# 提交带波形
bsub -env "TESTCASE=test_smoke DUMP_FSDB=1" < vcs_sim.sh
# 提交 verbosity=DEBUG
bsub -env "TESTCASE=test_smoke VERBOSITY=DEBUG" < vcs_sim.sh
# 指定随机种子
bsub -env "TESTCASE=test_smoke SEED=12345" < vcs_sim.sh
八、性能优化 Checklist
- [ ] 波形写到本地 SSD,跑完归档
- [ ] 回归测试不开全波形,失败自动重跑抓波形
- [ ] 用
-debug_access+pp代替+all(回归够用了) - [ ] 编译用
-j N并行,N 取 4-8(再高收益递减) - [ ] 增量编译(
-Mupdate)用于迭代调试 - [ ] 覆盖率只在需要时打开
- [ ] 设置合理的 wall time 上限(防卡死)
- [ ] 大仿真用大内存节点
- [ ] 用作业数组批量跑回归
- [ ] simv 放本地 / 共享存储读取快的地方
九、写在最后
VCS 仿真放到 OpenLava 集群上跑,核心收益是并行化——以前一个人跑 100 个回归要跑好几天,现在 20 个并发几小时跑完。
关键经验就三点:
- 编译一次,仿真多次(别每个用例都重新编)
- 波形管理策略(别什么都存 fsdb,省空间又快)
- 作业数组批量跑(100 个用例一键提交)
下一篇我们讲大规模回归调度:怎么组织上百上千个用例的 full regression、依赖管理(编完再跑、跑完汇总)、结果自动分析和邮件报告。
你的 VCS 回归一次跑多久?有什么提速秘诀? 评论区聊聊。
OpenLava #EDA #VCS #Verdi #仿真 #UVM #数字验证 #运维
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。







