4. 签核交付的版本控制与可复现性

签核交付最怕的不是“跑不出来”,而是“昨天能跑出来,今天跑出来不一样”。Xcelium 仿真与 Jasper 形式验证的签核结论,本质上由四类输入共同决定:RTL 代码、运行脚本、约束文件(SDC/Jasper Tcl)、工具配置(编译选项、license 特性、版本号)。只要其中任何一项发生漂移,签核结果就可能从 PASS 变成 FAIL,或者反过来掩盖真实缺陷。因此,签核交付的版本控制目标不是“把文件放进 Git”,而是建立一条从输入到结论的可追溯、可重建、可校验的链路。

一、签核交付物的四类版本对象

签核交付的版本管理对象可以分成四层,每层的变更频率和影响范围不同,管理策略也应有所区别。

  • RTL 与验证环境代码:包括设计源文件、testbench、UVM 组件、Jasper 的 SVA 断言文件。这类文件变更频繁,需要与设计仓库保持同一套 commit 历史,签核时记录精确的 commit hash。
  • 运行脚本:Xcelium 的 xrun 命令封装、Jasper 的 jg 启动脚本、回归调度脚本。脚本决定了“怎么跑”,必须与代码同版本管理,不能放在个人目录里手工维护。
  • 约束与配置:SDC 时序约束、Jasper 的 clock/reset 定义、xrun 的 -define 宏、-sv_seed 随机种子、Jasper 的 -prove 深度参数。这类文件是签核结论的“边界条件”,一旦改动必须重新签核。
  • 工具与环境:Xcelium/Jasper 的版本号、补丁号、license 特性、编译库路径。工具版本升级可能改变优化策略或断言求解行为,必须记录在签核报告中。

这四类对象共同构成一个签核“快照”。可复现性的核心,就是让这个快照可以被完整重建,而不是依赖某台机器上的残留状态。

二、版本控制的具体做法

推荐采用“主仓库 + 签核标签 + 环境锁定”的三层结构。主仓库使用 Git 管理 RTL、脚本和约束,签核时打一个带语义的 tag,例如 signoff_2024Q3_dma_top_r3。tag 的命名应包含时间、模块和修订号,便于回溯。

对于工具版本,不要只写在文档里,而应写入可执行的配置。例如在签核脚本中显式指定:

# signoff_env.sh —— 签核环境锁定脚本
export XCELIUM_HOME=/tools/cadence/xcelium/23.09.001
export JASPER_HOME=/tools/cadence/jasper/2023.12
export SIGNOFF_TAG=signoff_2024Q3_dma_top_r3
export SV_SEED=20240915
export PROVE_DEPTH=64

这样,任何人执行 source signoff_env.sh 后,使用的工具路径、随机种子和证明深度都与签核时一致。随机种子尤其关键:Xcelium 的 -sv_seed 若使用默认随机值,同一份代码两次仿真可能得到不同的覆盖率或不同的失败点,签核结论就失去了确定性。

约束文件的管理要避免“一份 SDC 到处改”。建议按模块拆分约束,主约束文件只做 include,每个模块的约束独立成文件并单独提交。Jasper 的 Tcl 约束同理,clock 定义、reset 定义、assume 约束分文件管理,便于定位某次签核失败是由哪条约束变更引入的。

三、一键复现签核结果的实现

一键复现不是写一个巨大的 shell 脚本,而是把“取版本、锁环境、跑签核、比结果”四步串成可重复执行的流水线。下面给出一个可参考的复现脚本骨架:

#!/bin/bash
set -euo pipefail

# 1. 取指定签核标签
git fetch --tags
git checkout ${SIGNOFF_TAG}

# 2. 锁定工具环境
source ./signoff_env.sh

# 3. 执行 Xcelium 回归
xrun -f xcelium.f -sv_seed ${SV_SEED} -coverage all \
     -xmlibdirname ./lib_xrun -l xrun_signoff.log

# 4. 执行 Jasper 形式验证
jg -batch -f jasper_prove.tcl -prove_depth ${PROVE_DEPTH} \
   -log jasper_signoff.log

# 5. 结果比对
python3 compare_signoff.py --baseline ./baseline/signoff_result.json \
                           --current  ./current/signoff_result.json

其中 compare_signoff.py 负责把本次结果与基线结果做结构化比对,输出差异项。基线文件应随 tag 一起提交,记录每个测试用例的 PASS/FAIL、覆盖率数值、断言证明状态。比对时不要只比“总数”,而要逐用例比对,否则一个用例从 PASS 变 FAIL、另一个从 FAIL 变 PASS,总数不变却掩盖了回归。

签核基线文件建议使用 JSON 或 YAML 等结构化格式,字段包括:用例名、状态、覆盖率、证明深度、工具版本、种子。这样比对脚本可以精确到字段级别,而不是靠人工看日志。

四、可复现性的校验与常见陷阱

可复现性需要被验证,而不是被假设。最直接的验证方法是:在一台干净的机器上,用同一个 tag 和同一份环境脚本重新执行一遍签核,比较结果是否与基线一致。如果出现差异,按以下顺序排查:工具版本是否一致、随机种子是否一致、约束文件是否被本地修改、编译库是否残留了上一次的增量结果。

常见陷阱有三个。第一,隐式依赖:脚本里引用了某个未纳入版本管理的文件,例如个人目录下的 ~/.jasperrc,换机器就失效。第二,增量编译污染:Xcelium 的 -xmlibdirname 若复用旧库,可能跳过重新编译,导致实际运行的 RTL 与 tag 不一致。第三,约束覆盖:Jasper 的 Tcl 脚本中后加载的约束可能覆盖前面的定义,若加载顺序依赖文件系统排序,结果就不稳定。解决方法是显式指定加载顺序,并在日志中打印每条约束的来源文件。

签核交付版本控制与一键复现链路 版本控制对象 RTL / TB / SVA 设计源文件与断言 运行脚本 xrun / jg / 回归调度 约束与配置 SDC / Tcl / 种子 / 深度 工具与环境 版本号 / license / 路径 Git Tag signoff_2024Q3 dma_top_r3 commit hash 锁定 一键复现流水线 ① 取版本 git checkout ${SIGNOFF_TAG} ② 锁环境 source signoff_env.sh ③ 跑签核 xrun + jg(固定种子/深度) ④ 比结果 compare_signoff.py 逐用例比对 ⑤ 出报告 PASS/FAIL + 覆盖率 + 证明状态 基线文件 baseline/signoff_result.json 随 tag 提交,逐用例比对,防止 PASS/FAIL 互换被总数掩盖

五、签核交付的版本记录表

为了让签核结论可审计,建议每次签核生成一份版本记录表,随 tag 一起归档。下表给出一个可填写的示例结构,字段均来自签核实际需要的信息。

字段示例值说明
签核标签signoff_2024Q3_dma_top_r3Git tag 名称
commit hasha3f9c21精确代码版本
Xcelium 版本23.09.001工具版本与补丁号
Jasper 版本2023.12形式验证工具版本
SV 随机种子20240915固定种子保证可复现
证明深度64Jasper prove depth
约束文件版本sdc/dma_top_v7.sdc约束文件路径与版本
回归结果128 PASS / 0 FAIL逐用例统计
覆盖率代码 96.2% / 功能 91.5%签核覆盖率指标
断言证明42 proven / 0 cex形式验证结论

这张表的价值在于:当三个月后有人质疑“这个签核结论是怎么来的”,可以直接根据 tag 和记录表重建环境、重跑签核、比对结果。如果重建结果与记录表一致,签核结论就是可信的;如果不一致,差异项就是需要排查的对象。

不要把签核基线文件放在个人目录或临时目录。基线文件必须与 tag 一起提交到主仓库,否则“一键复现”在换人、换机器后就会退化成“重新摸索”。

总结来说,签核交付的版本控制与可复现性,关键不在于工具多先进,而在于是否把四类输入全部纳入版本管理、是否用固定种子和固定深度消除随机性、是否用结构化基线做逐用例比对。做到这三点,签核结论才能从“某次运行的结果”变成“可重建、可审计的工程结论”。