rtl,和 lm-eval、evalscope
并列。
分数落进同一张看板,所以同一个模型的 RTL 分数和 GSM8K 分数在一行上。
能跑什么
一次评测用一个 runner,所以 RTL 评测集和 GSM8K 这类要分开两个作业。
rtl-repo 的指标和其他几个不可比。 它不跑仿真,因为没有 testbench,上报的是文本相似度。把
它的 exact_match 和 VerilogEval 的 pass@1 排在一起,是在比两件不同的事。确认成功
pass@1。
作业报 the RTL harness produced no rtl_report.json,说明评测所在的镜像里,
TUNEPLANE_RTL_HARNESS 指向的位置没有 harness。那是下一节。
镜像需要什么(管理员做一次)
仿真器在deploy/docker/Dockerfile.evalkit 里:iverilog 和 verilator。那是一个 GPU 镜像 ——
它带着 torch、lm-eval 和 evalscope,存在的目的是生成。
评测器、形式化工具和钉死版本的上游题库在第二个镜像里:deploy/docker/Dockerfile.eda-oss。它带一
套工具链、不带模型运行时,存在的目的是检查生成出来的东西。把两者分开不是为了整洁:离线评测那
一步可以在检查开始之前把 GPU 释放掉,而合成一个镜像会逼着卡被一段用不上它的仿真一直占着。分开也让
EDA 工具链按自己的节奏钉版本 —— 仿真器版本是结果含义的一部分,因为某个 CUDA wheel 动了就升级它,
会悄悄改掉档案里每一个硬件分数。
eda-oss 镜像里有什么
基于ubuntu:24.04,用发行版自带的版本:
再加上钉死 commit 克隆到
/opt/verilog-eval 的 VerilogEval 和 /opt/rtllm 的 RTLLM —— 从不
vendor 进本仓库:上游一改题目,复制出来的那份就变成了另一个评测集。
三样东西是它刻意不带的:
- 不带商业工具。 Synopsys、Cadence 和 Siemens 的流程需要这个镜像拿不到的授权,以及它不应自行
连上的 license server。有这些的部署从这个镜像
FROM出自己的镜像,并注册成自己的 Toolchain Profile。这个镜像是地板:让公开评测集在不依赖任何人授权的前提下可复现的那套开源流程。 - 不带模型。 生成发生在 evalkit 镜像或某个模型部署上;这个镜像收 RTL,返回结论。
- 不装
tuneplane。 运行时由 Job Capsule 提供 SDK,和 evalkit 的安排一样,这样契约和说这个 契约的东西不会漂移。
TUNEPLANE_CVDP_DIR 和 TUNEPLANE_RTL_REPO_DIR,默认是 /opt/cvdp 和 /opt/rtl-repo;
没有这些目录的镜像会拒绝这两个 suite,而不是给它们打分。
harness 是一个不打分的适配器
平台依然只定义契约:TUNEPLANE_RTL_HARNESS 现在有了默认值 tuneplane-rtl-eval —— SDK 附带的一个命令行入
口。它是适配器,不是评测集。它从上游评测器自己的 JSONL 里读题,向模型要样本,把每个样本交给那
个评测器,再把回来的东西转成报告契约。电路对不对的每一个判断,都是上游工具按上游自己的方式做的。
评测器压根没装时,它会抛错并点名要设的变量,而不是报成一次工具失败:没装评测器是运维必须看见的部
署错误,而某个设计上仿真器崩了是单题级别的事实。
TUNEPLANE_RTL_TIMEOUT_S 限定单次仿真 ——
默认 600 秒,下限 10 秒 —— 因为 testbench 跑多久是客户的事,不是适配器的事。
适配器按 suite 上报
pass@1、pass@<samples> 和 tool_failures。 rtllm-v2 和
rtl-repo 两个 pack 声明的 func_pass 与 exact_match 不在其中;想要那些数字的部署需要一个
会算它们的 harness,并把 TUNEPLANE_RTL_HARNESS 指过去。报告里有什么
适配器写出<output-dir>/rtl_report.json。第 2 版在分数旁边加了每道题一份的验证报告;第 1 版
(只有 {"results": {…}})仍然能解析。
lint、compile、simulate、
formal、synthesize、coverage,以及留给部署自有流程的 custom —— 顺序按流程执行的顺序、
便宜的在前,因为一个连 lint 都过不了的设计,没有值得等的仿真结果。这里没有「模型的回答看起来挺像
样」这种阶段:背后没有工具的判断是 Rubric,而 rubric 有自己的路径。
构造 stage 时有两种形状会被拒,因为它们各自在一个方向上撒谎:说工具跑过却不给结论的阶段,以及从没
跑起来的工具那里带回
pass 或 fail 的阶段。
没跑起来的工具,不等于没通过的设计
每个阶段的结局有三种,而不是两种:
license server 挂了、仿真器 segfault、容器的 PATH 上没有
iverilog:这些都不是关于设计的证据。
把它们当成失败计分,是对「生成的工程产物」做评测时最有破坏性的一件事。在训练里,它教模型躲开
license 队列繁忙时它正在做的那件事;在报告里,它是对一个没人检查过的电路下的断言。
所以这条规则一路贯穿:
- 报告的结论是推导出来的,从不由谁声明。 没有任何阶段,或任何地方有一次工具失败,整份报告就是
unknown—— 哪怕真跑起来的阶段全过了。编译得过、从没仿真过的设计,并没有被证明能工作,而把编译 当成答案,正是「它能构建」变成「它能工作」的路径。 - 带任何一次工具失败的报告,拒绝变成 reward。
as_reward()会抛错并点名坏掉的阶段,而不是返 回0.0。把工具失败记成零分的语料,会教策略躲开工具坏掉时它正在做的事,而模型没有办法把那个和 「电路真写错了」区分开。 tool_failures作为独立计数一路传下去。 它报在通过率旁边而不是折进通过率里,和每个数据扫描 计数都带着rows_scanned是同一个道理。有工具失败的 suite 读作不完整,不是分数更低。- 什么都没法打分时
pass_rate是null,而不是0.0。空的分母不是等于零的通过率,正是这个 区别,拦住了一个没有仿真器的镜像发布一份完美的失败记录。
tool_ok: false 的阶段;仿真超时变成一个
tool_ok: false 的 simulate 阶段(没有 $finish 的 testbench 在正确的模块上一样会挂);只有
评测器返回非零退出码才变成 fail。
哪些还没有被证明
这一节比它「本该有的长度」要长。Dockerfile.eda-oss从未在 CI 里构建过。 发布流水线只构建控制台镜像。scripts/image_smoke.py里带着这个镜像的检查项 —— 每个仿真器和形式化工具在 PATH 上、cocotb可导入、上游题库已克隆 —— 而没有任何 workflow 会去跑它们。- 不钉版本就构建不成功。
SBY_COMMIT、VERILOG_EVAL_COMMIT和RTLLM_COMMIT没有默认值, 构建在检查它们的那一行就失败,所以本仓库里没有一条已知可用、可以照抄的构建命令。 - 适配器调用上游评测器这一段,从未对着真实的 checkout 跑过。 它的测试把子进程打了桩,所以读题 库、拼 prompt、组装阶段、写报告以及每一条失败分支都有覆盖,而真正调用 VerilogEval 或 RTLLM 评测 器的那一步没有。
- 生成是 GPU 路径。 调用 vLLM 的那一个函数是刻意隔离出来的,并且不在任何 CI 里跑,因为这个项目 用的 runner 都没有 GPU。
- 本仓库里没有任何一个分数是对着真实仿真器或真实 GPU 产生的。 测试里的每个数字都来自桩。
- 每题的
verifications段写得出来,但还没有人读。 SDK 能把它从报告里解析回来;控制面和控制台 都还没有消费它,所以这些阶段不出现在任何页面上。
CVDP 的 agentic 赛道跑在调度器之外
CVDP 有两个赛道。非 agentic 那个就是上面全部内容。agentic 赛道让模型在解题过程中反复用工具:读 文件、跑仿真、看波形、改、再试。 两个属性把它放在训练作业之外:- 它会启动容器。 官方 harness 每道题拉一个 Docker 容器。这里的评测作业本身就跑在容器里,而 KubeRay 或 Slurm 上的 Docker-in-Docker 需要特权容器或 sysbox,多数内部集群不给。
- 它需要一个可调用的端点,不是一个权重路径。
--train-run 是让评测门禁和模型注册表把这些分数归属到那次训练运行的那个参数。没有它,它们只是一个
孤立的数字。
关于这个流程有两件事:
- 写入入口只接受结构化分数,不解析 harness 的报告文本。CVDP 输出
report.txt和composite_report.txt,所以把转换留在你自己的脚本里。这样上游改一行格式是改你的脚本,而不是 写入路径坏掉。 - 那个 deployment 令牌只有一项权限:上报分数。它会被粘到一台评测主机的环境里,所以它绝不能同时 能写日志、改作业状态或上传产物。