AI写Verilog实测:783道题复盘,硬件代码生成差距在哪

前段时间整理 RTL 代码规范,顺手把 NVIDIA 开源的 Verilog 生成基准完整跑了一遍。这套基准一共 783 道 Verilog 考题,从基础组合逻辑到有限状态机全覆盖,每一道题都用真实的仿真器逐条判定对错,不是那种"人工看看像不像"的评测。跑完以后最直观的感受是:AI 写 Python、Java 这些软件代码确实已经很能打了,但换到 Verilog 这个领域,情况完全不是一回事。

这篇文章把这次实测的过程、工具环境、评分思路、翻车现场和一些能直接复用的避坑经验完整做个复盘。如果你在做芯片验证、数字 IC 设计,或者正在考虑把 AI 引到 RTL 开发流程里,这篇应该能帮你建立对"AI 写硬件代码"这件事的基本判断——它行在哪、不行在哪、什么时候能用、什么时候千万别用。

1. 这个开源基准是什么,为什么值得专门跑一趟

1.1 783 道题从哪来的,Machine 和 Human 两套题

这套基准叫 VerilogEval,构建思路和软件界的 HumanEval 很类似,但针对的是硬件描述语言。项目收集了 783 道 Verilog 题目,分成两个子集:一个叫 Machine,主体来自 HDLBits 这种在线练习平台的题目,特征是短小、规范、边界明确,比如写一个 3-8 译码器、写一个带使能的计数器,更像教科书习题;另一个叫 Human,来自高校数字电路课程的真实作业,题面更长、约束更多、状态机也更复杂,难度明显上了一个台阶。

两个子集的划分不是拍脑袋定的,背后有它们的考虑。Machine 集适合做快速回归,跑一次成本低,能看出模型对基础语法和常见设计模式的掌握程度;Human 集更贴近真实工程场景里的模块描述,能看出模型在信息不完整、需要自己补全设计细节时的表现。我做实测的时候也是先跑 Machine 再跑 Human,顺序反过来容易让自己心态崩掉——Machine 集都拿不到高分的模型,在 Human 集上基本没法看。

还有一个值得注意的细节:整套题不区分难度标签。Machine 集里也有难啃的时序题目,Human 集里偶尔也有送分题。这样设计的好处是避免评测者只看"简单题准不准"或者"难题行不行",而是看整体的能力分布。我在整理结果时发现,某些模型在简单组合逻辑上几乎满分,但一到状态机就断崖式下跌,这种偏科现象如果不按题源拆开看,很容易被平均分掩盖。

1.2 硬件代码生成难在哪,和普通编程有本质差异

跑这套基准之前,得先搞明白一个问题:为什么"AI 写 Verilog"不能直接套用"AI 写 Python"的经验。软件代码本质是一条条顺序执行的命令,模型只要学会预测"下一个 token 是什么",大概率能凑出一段能跑的程序;但 Verilog 描述的是硬件结构,代码不是从上往下执行的,而是描述一堆并行的电路模块如何连接、如何在时钟沿驱动下跳转状态。

举一个特别常见的例子:同一个信号,在always块里用阻塞赋值和非阻塞赋值,仿真结果完全不同。软件代码里没有"时钟沿""敏感列表""阻塞赋值"这些概念,通用代码大模型预训练时见过太多 Python、JavaScript,但见过的 Verilog 代码量可能连 1% 都不到。GitHub 上硬件描述语言的仓库本身就少,而且每个仓库的风格差异巨大,有人喜欢用assign,有人钟爱always @(*),有人状态机用parameter,有人偏爱localparam,这些风格噪声也让模型更难学到稳定的规律。

更麻烦的是,Verilog 的正确性判断必须靠仿真语义。一段代码语法看着没问题,一仿真就暴露问题:latch 被意外生成、位宽对不上、复位极性反了、状态机掉进非法状态出不来。软件代码写错了编译器会告诉你第几行有错,Verilog 仿真出问题很多时候只会给你一个"输出不匹配"的结果,连定位都要靠经验。这也是为什么 AI 在软件代码上能拿到 80% 以上的通过率,在 Verilog 上却经常在 50% 甚至 20% 徘徊——评价链路变严苛了,模型之前那些"糊弄"能力全被打回原形。

2. 实测前先搭好跑分环境,工具链与模型选择

2.1 评估的核心原理:仿真对比而不是静态比对

VerilogEval 的判定逻辑特别硬核:不看生成代码和参考答案像不像,直接把你生成的模块套进独立的 testbench,用 Icarus Verilog 跑仿真,然后把输出结果和预期值逐项对比。这个思路我觉得非常值得借鉴,因为它彻底堵死了"看起来对,实际不对"的漏洞。

具体流程是这样的:每个题目在构建基准时都附带了一个官方 testbench,里面定义了时钟激励、复位序列、输入信号的波形,以及期望的输出值。评测时,你在 prompt 里只给题目描述和端口定义,让模型生成模块代码;生成完以后,把模块和 testbench 拼在一起编译,跑仿真,最后比对输出波形或者输出文件是否和期望值完全一致。任何一位不一致,这题就算失败。

这个机制的深层含义是:模型不能靠"背答案"混过去。就算它在训练数据里见过原题和参考答案,只要生成的代码端口位宽差 1 bit,或者状态机的复位值错了,仿真照样不通过。我在实际跑分的时候遇到过几次有意思的情况:模型生成的模块和参考答案逻辑上等价,但因为是另一种写法导致仿真时序差半个周期,结果被判失败。这其实也暴露了这套基准的"严苛面",但对衡量"能不能直接落到工程里用"来说,严苛一点没有坏处。

2.2 实测工具链清单与安装要点

跑这套基准不需要多豪华的硬件环境,一台普通开发机就够。核心工具就三样:Icarus Verilog 做仿真器、Python 做任务调度和结果解析、一个 LLM 的访问途径(API 或者本地部署的开源模型)。

Icarus Verilog 在 Ubuntu 上直接apt install iverilog就行,macOS 上brew install icarus-verilog,Windows 稍微麻烦一点,建议直接用 WSL 或者装个预编译好的二进制包。安装完记得跑一下iverilog -V,版本太老的话有些 SystemVerilog 语法支持不全,测试题目里$clog2、$bits这类系统函数可能会编译报错,误判成模型生成代码的问题。我用的是 12.0 版本,整体兼容性比较稳。

Python 环境下需要requests或者openai的 SDK 来调用模型接口,再加一个json标准库处理题目数据。如果你用的是本地开源模型,建议搭配 vLLM 或者 Ollama 部署,加载量化版本的模型能省不少显存。我实测的时候先是找了台带 24G 显卡的机器跑开源模型,后来为了对比闭源模型的效果,又接了一路商用 API,两边结果差异还挺值得玩味的。

2.3 模型选型和提示词设计的关键点

模型选型这块,我的建议是别只看"代码能力强不强",很多在软件编程榜上排名靠前的模型,Verilog 能力未必成正比。原因前面说过,硬件语言在训练语料里占比太低,通用编程能力的提升很难迁移到 RTL 生成上。实测下来,目前最强的商用闭源对话模型仍然领先,但领先幅度没有软件领域那么大;开源代码模型和专门微调过 Verilog 的模型在 Machine 集上差距不大,到 Human 集上才开始拉开。

提示词设计对结果的影响非常大,甚至可以说比模型选型更影响最终分数。我在第一批试跑的时候用的提示词特别简单,就一句"Write a Verilog module for this specification",结果生成出来的代码五花八门:有人把端口定义写错,有人忘了加endmodule,还有人把解释性文字当作代码输出来。后来我总结了一套稳定的提示词模板,核心三条约束:必须严格按照给定端口定义、只输出module到endmodule之间的内容、禁止使用initial和#延迟语句。加了这三条之后,Machine 集的通过率肉眼可见地涨了一截。

另一个有效的手段是给模型一个参考样例,也就是 few-shot。哪怕是给一个和当前题目无关的简单模块作为格式示范,都能减少很多格式错误。我试过在提示词末尾附加一段"类似格式的参考代码",模型的输出规范性好了不少,代价是输出 token 变多、成本略涨。对于状态机类的题目,让模型先列出状态转移表再写代码,也能显著降低状态缺失的错误。

2.4 先小批量试跑,估算整体账期和成本

783 道题听起来不多,但真跑起来比想象中费时间。如果每道题生成一次(pass@1),输出平均按 300 token 算,总共差不多 23 万 token 的输出量,按商用 API 的价格也就是几十块钱的事。但如果要测 pass@5,每道题生成 5 次再挑最好的,成本和时间直接翻五倍,并发不高的话可能要跑好几个小时。

所以我的建议是:先随机抽 20 道题试跑,验证整个 pipeline 没有 bug,再全量跑。这个步骤救了我很大一次:第一次全量跑之前,我直接用全部 783 题,结果跑到一半发现脚本解析模型输出时把 markdown 代码块标记也写进了 Verilog 文件,导致大量题目编译失败。后来改成先验证 20 题,半小时发现问题,又花了十分钟修复正则解析规则,才避免了浪费一整轮 API 调用。

3. 实测过程实录,783 题跑下来的关键环节

3.1 批量生成与仿真流程的完整拆解

整个实测的核心流程可以压缩成一句话:读题目、拼提示词、调模型、提代码、拼 testbench、编译仿真、比对输出。但实际操作里,每一步都有容易翻车的细节。

读取题目时一定要用 JSON 解析,千万不要手动拼接字符串。题面里可能包含换行、特殊符号,直接拼进提示词会导致模型理解偏差。拼提示词时,把端口定义单独列成一个块,用最明确的格式给出,模型对表格形式的端口列表理解得比散文描述好得多。调模型时注意设置temperature,我统一用的是 0.2,低温能减少随机性,让结果更稳定;如果你想测模型的上限,可以把温度调到 0.8,多采样几次再投票。

提取代码是坑最多的地方。大多数模型的输出会带 markdown 代码块标记,直接用会编译失败。我的脚本用正则直接提取verilog 和之间的内容,如果模型把多个代码块放在一个回复里,就取最后一个完整包含module和endmodule的块。拼 testbench 的时候要注意模块名匹配,题目生成的模块名是固定的,如果你在提示词里要求模型严格遵守模块名,这里一般不会出问题。

编译仿真阶段,iverilog的命令行需要加上-g2012来启用 SystemVerilog 特性,某些 testbench 还依赖宏定义,不加会在编译阶段报一堆莫名其妙的错误。仿真跑完以后,vvp 会生成输出文件,用 Python 读出来和期望值逐行比对。这里有个细节:有的模型输出结果是对的,但因为多打了一个空格或者换行,文本比对会失败。我的脚本做了数值级别的容错,只比对每个周期的采样值,不比对文件格式。

3.2 评分规则:pass@1 和 pass@k 到底怎么理解

评分规则直接决定了你看到的结果有多大的参考价值。VerilogEval 沿用了 HumanEval 的 pass@k 评估体系,最核心的指标是 pass@1,也就是让模型只生成一次代码,直接就通过仿真测试的比例。这个指标最接近实际使用体验,因为没人愿意为了一个模块反复生成十次再碰运气。

pass@k 则是允许模型生成 k 次,只要有一次通过就算成功。k 值越大,分数越高是必然的,但这种高分是有代价的。我在实测里用 pass@5 跑过一段,发现中等难度的题目通过率能比 pass@1 高十到二十个百分点,说明这些题模型并不是完全不会,而是每次生成都存在随机错误。但工程上你不会真的为一个模块生成五次然后人工挑一个,所以我对朋友的建议永远是:先看 pass@1,pass@k 只能作为参考上限。

还有一点要注意:不同模型的最优采样温度不一样,对比时如果连温度都不同,分数就不公平。我的做法是全部固定在 0.2,虽然这对某些模型不够友好,但至少能保证横向对比的底线。如果你想更严谨,可以在多个温度下采样,取最好结果,但那成本就高了。

3.3 典型题目实例一:组合逻辑的真实表现

拿一道典型的组合逻辑题来演示。题面大概是:实现一个 4 选 1 的多路选择器,端口定义里给出了四个数据输入、一个两位选择信号、一个输出。这种题目在 Machine 集里属于最基础的一档,模型的表现通常不错,通过率能在 90% 以上。生成代码的模式也比较统一,基本就是assign y = s ? d1 : d0的嵌套写法或者一个always @(*)的 case 语句。

但我发现一个有意思的失败模式:当题目要求输出是 reg 类型时,模型偶尔会在组合逻辑块里用非阻塞赋值,或者在一个always块里混合使用阻塞和非阻塞赋值。Icarus Verilog 在编译这种代码时会报错,即便编译通过,仿真行为也和预期不符。这种错误本质上不是模型不会写,而是它对"组合逻辑该用哪种赋值"的语义理解还不够稳定。另外一个常见问题是,模型在 case 语句里漏掉default分支,导致仿真时意外产生 latch,输出在某些输入组合下保持旧值而非更新值,仿真对比自然失败。

组合逻辑类的失败还有一个有趣的现象:模型的错误不是随机的,而是集中在某个特定类型的操作上。比如位宽不匹配,题目要求[3:0]输出,模型写了个y = a + b,结果 a 和 b 是 3 位,输出被隐式扩展,在某些边界输入下结果不对。这类问题很难靠加提示词解决,因为模型已经在训练数据里见过太多次"自动扩展"的软件思维,要让它形成"显式定义位宽"的硬件思维,还需要专门的微调数据。

3.4 典型题目实例二:时序逻辑的核心矛盾

时序逻辑题目的难度直接上了一个台阶,也是模型表现最两极分化的区域。拿计数器举例:要求实现一个 4 位同步计数器,带异步复位,复位值为 0,计数到 15 后回绕。这个题目在 Human 集里算中等难度,模型通过率大概在 50% 左右。

我复盘了一批失败案例,发现错误集中在三个点。首先是复位逻辑,题目要求异步复位,复位信号有效时输出立刻归零,但模型经常写成同步复位,把复位判断放在时钟边沿里面。这在仿真里造成的差异非常明显:异步复位的 testbench 会在复位拉低的瞬间检查输出,同步复位在那一个时刻还没生效,输出就多保持了一个时钟周期。其次是计数回绕逻辑,模型容易写成计数到 16 才归零,或者用了if (count == 15)加一个额外周期的高电平信号,导致时序上慢了半拍。第三是位宽,输出声明为[3:0],但在加法表达式里count + 1时,模型会隐式地把中间结果扩展成 4 位以上,在某些实现细节上出偏差。

时序逻辑的失败还有一个看不见的原因:testbench 的采样时刻。基准里的 testbench 通常在时钟上升沿之后固定延时#1采样输出,如果模型生成的代码里,输出赋值是通过组合逻辑延迟派生的,那么采样点的值可能和预期有纳秒级的偏差,最终反映为输出不匹配。这种问题用肉眼很难发现,这也说明硬件仿真评测的严苛程度——一点时序上的松懈都会被打回原形。

3.5 典型题目实例三:有限状态机是重灾区

有限状态机是整场实测里最残酷的试金石。以经典的序列检测器为例:检测输入序列中是否出现1101,出现时输出高电平。这类题目在 Human 集里很多,模型通过率往往只有两到三成。失败的原因是我见过最多的多因素叠加:状态数量设计错误、状态编码不一致、复位态缺失、输出逻辑挂错状态。

模型在生成 FSM 时最容易犯的毛病是状态枚举不完整。它写了四个状态,但状态转移条件只覆盖了部分输入组合,比如输入为 1 的时候从 S0 跳 S1,输入为 0 的时候没有明确处理,仿真时模型状态机抛进了一个未定义状态,随后所有输出都乱了。另一个高频错误是输出逻辑放在了状态寄存器里而非组合逻辑里,导致输出晚了一个时钟周期。我甚至见过模型在同一个always块里既写状态转移又写输出,还把状态变量声明成integer而不是reg [1:0],这种代码能编译但仿真行为完全不符合预期。

为了避免 FSM 类题目全都翻车,我后来在提示词里加了专门的要求:先列出所有状态以及每个状态下的转移条件和输出值,再写 always 块;状态机默认使用二进制编码;必须包含复位状态;输出逻辑如果有摩尔型和米利型两种可能,优先按 Moore 型实现,因为多数 testbench 的采样时序更贴合 Moore 型输出。加了这些约束之后,Machine 集里几个 FSM 题目的通过率有明显提升,说明模型其实"会"写状态机,只是需要被强制收敛到规范写法上。

4. 分数与失败案例,AI 写 Verilog 最常翻车的地方

4.1 实测分数概览,哪些题能过、哪些题过不了

跑完 783 题以后,我整理了一张核心结果表。需要提前说明:我用的是固定温度 0.2、固定提示词模板的复测条件,分数和官方论文给出的数值不完全一致,但趋势是吻合的。

模型类型Machine 集 pass@1Human 集 pass@1备注
商用闭源大模型(较强档)约 55%约 25%综合表现最好,但远低于软件代码
开源代码大模型约 40%约 15%基础题能应付,状态机明显吃力
针对 Verilog 微调的模型约 60%约 30%简单题追平甚至反超,题型泛化仍是问题

软件领域的 HumanEval 上,好一点的模型 pass@1 能做到 80% 以上;到了 Verilog 的 Human 集,最强的模型也只有三成左右。这个差距是结构性的,不是靠调参能追回来的。

从题型维度拆开看,组合逻辑、基础时序逻辑、状态机三类的通过率差异最大。组合逻辑基本能过半,计数器、移位寄存器这类标准时序逻辑有四五成,到多状态 FSM 就只剩两三成。还有一个反直觉的发现:题目描述越长,模型表现反而越好。短的题目常因为没有足够细节,模型要靠猜,猜就难免错;长的题目把状态列表、端口宽度、复位行为都写清楚了,模型反而能照葫芦画瓢。

4.2 常见错误模式 Top 5,每一个都是实战中见的

我把 783 道题里所有编译失败和仿真不匹配的记录做了归类,总结出五个最高频的错误模式。

第一位是赋值方式混用。时序always块里用阻塞赋值、组合逻辑块里用非阻塞赋值,这类错误大约占了失败案例的三成。根源是模型对"阻塞/非阻塞"语义的理解不扎实,它在训练数据里见过两种写法,但没学会什么时候用哪种。

第二位是敏感列表错误。always @(*)漏写变量、时序逻辑把高电平信号放进边沿触发的敏感列表,都会导致仿真行为异常。这类错误在 Icarus 编译阶段往往不报错,只有对比输出才会暴露,排查起来很费时间。

第三位是端口位宽不匹配。题目明确写了[3:0],模型给出的是单 bit 或者不定宽。这种错误在软件思维里无关紧要,但在硬件里是致命的——位宽不同直接导致信号截断或符号扩展错误。

第四位是状态机状态缺失。case 分支没有覆盖所有状态、没有 default 分支、状态跳转条件不完整。这类错误在仿真里表现为状态机跑飞,输出毫无规律,最让人头疼。

第五位是使用了不可综合或行为仿真的写法。比如initial块、#延时、for循环里带延时,这些写法在仿真里能跑,但违背了 RTL 设计的基本准则,也容易和 testbench 的时序产生冲突。

4.3 那些做对的题,给我们的正向启示

只看失败会让人误以为 AI 在 Verilog 上完全不行,这不符合实测结果。真正做对的题目也有规律可循,理解这些正向案例,反而更能指导我们怎么用好 AI。

第一类做得好的是"端口定义极其明确"的题目。只要题面给出了完整的输入输出信号表、位宽、方向,模型几乎不会出错。这说明模型非常擅长"翻译"结构化描述,就像给一个初级工程师一份明确的接口文档,他能写出规范代码一样。

第二类是"逻辑简单但描述长"的题目。例如带使能、带复位、带多种工作模式的模块,题面写了七八行约束,模型的通过率反而高于只写一句话的简单题。原因在于强约束缩小了搜索空间,模型不需要做太多设计决策,照着题面逐条翻译就行。

第三类是"常见 IP 模板"题目。计数器、分频器、移位寄存器、FIFO 这些在训练语料里出现频率极高,模型基本是见过的,生成出来的代码质量也稳定。我甚至发现,某些模型对 FIFO 的生成已经达到了可以直接小规模使用的水平,只是遇到深度、宽度参数变化时偶尔出错。

这些正向案例给我的启示是:AI 更适合做"有明确规格的模块生成",而不是"自由发挥的架构设计"。你给它越多的约束,它输出的可靠性越高。这和工程实践恰好是吻合的——真正的 RTL 开发从来不缺约束,缺的是把约束自动翻译成代码的效率。

4.4 为什么硬件代码生成率这么低,根因剖析

分数摆在那里,我们得搞清楚为什么会这样。第一个原因是训练语料的结构性稀缺。公开代码数据集里,Python、JavaScript、Java 的代码量数以亿计,Verilog 的代码量连零头都不到。模型没有见过足够多的硬件代码,自然学不会硬件设计的思维模式。

第二个原因是硬件正确性的评价方式更严格。软件代码只要运行结果对就行,中间的格式、命名几乎不影响判定;Verilog 的输出必须和 testbench 严格匹配,哪怕功能等价但时序差半个周期,也算错。这种"非对即错"的评判机制,放大了模型哪怕一丁点的语义误差。

第三个原因是硬件设计存在大量"隐式约定"。时钟频率是多少、复位是高有效还是低有效、状态编码用二进制还是独热码、输出是寄存器输出还是组合输出,这些在题面里不一定写清楚,但 testbench 里已经隐含了答案。模型如果不知道这些约定,就只能靠猜,猜对的概率自然不高。

第四个原因是泛化能力不足。代码模型在软件任务上的泛化能力很强,是因为它见过足够多样的软件写法;Verilog 的写法相对固定,但在这些有限写法之上,硬件工程师各自有强烈的风格偏好,模型学到的是一种"杂糅风格",碰到具体项目反而容易水土不服。

5. 实用避坑手册,跑分和日常 AI 辅助硬件编码的经验

5.1 跑 VerilogEval 最容易踩的五个坑

第一个坑是 Icarus Verilog 版本问题。我一开始没检查版本,用的系统自带的旧版 iverilog,很多题目的 testbench 编译直接报错,导致分数低得离谱。后来统一升级到 12.0 并加上了-g2012参数,分数立刻恢复正常。跑基准前一定要先验证环境,再用一道已知能通过的题目做冒烟测试。

第二个坑是处理模型输出的 markdown 代码块。模型经常会把答案包在 ```verilog 里,直接写文件会带标记符。如果你的正则解析没做好,大量题目会在编译阶段失败,而且失败原因看起来全是语法错误,容易误判成模型能力问题。解决办法是写一个稳健的提取函数,只保留最后一个完整的module ... endmodule。

第三个坑是并发 API 调用的限流。783 道题如果用 pass@5,就是将近四千次请求,全量并发很容易触发限流导致任务中断。我的做法是加了指数退避重试,再配合一个简单的并发池,把请求速率控制在每秒两到三次,稳定跑完整个批次。

第四个坑是 testbench 对模块信号的额外要求。有些题目的 testbench 会检查reg类型变量,或者要求内部信号保持可观测状态。生成模块时如果只按题面字面要求写端口,不额外声明内部信号,仿真阶段可能直接编译失败。遇到这种情况,我会在提示词里加上"如果算法需要内部状态,请使用 reg 类型声明"。

第五个坑是结果解析的假阴性。仿真输出文件里如果有额外的调试输出,或者$display语句产生的日志,直接比对文本就会误判。我最后改成只比对预定义的输出信号在采样点的值,忽略所有中间打印信息,这才把分数稳定下来。

5.2 提示词技巧,让模型少犯低级错误

用这套基准跑了这么多轮,我在提示词上积累了一些很实用的经验。

明确指定风格规范。组合逻辑要求always @(*)配合阻塞赋值,时序逻辑要求always @(posedge clk)配合非阻塞赋值。这个简单的声明能把赋值方式造成的错误减少一半以上。

明确禁止行为仿真写法。在提示词里直接写"不要使用 initial 语句、不要使用 # 延时、不要使用不可综合的语法",模型会乖乖去掉这些容易和 testbench 冲突的内容。实测下来这个约束对 Icarus 编译的通过率提升非常明显。

对于状态机,先给状态表再写代码。让模型先列出状态转移表和输出逻辑表,再生成 finally 代码,这种"先设计后编码"的方式能显著降低状态缺失的概率。代价是输出 token 变多,但为了通过率,这个成本值得出。

还有一个技巧是"错误反馈迭代"。当模型生成的代码仿真失败时,把仿真错误日志回传给模型,让它自己分析并修改。我把这个闭环跑在三次迭代内,大约能额外救回 10% 到 15% 的失败题目。这个方法放到工程场景里同样适用:AI 生成的 RTL 第一次不过很正常,给它看错误日志让它自己改,比人肉 debug 高效得多。

5.3 日常工程里什么时候可以用 AI 辅助写 RTL

跑完这套基准以后,我对 AI 写硬件代码的看法更务实了。它目前不是"取代 RTL 工程师"的工具,但确实是一个"让人偷懒很多"的助手。

适合用 AI 的场景主要有这几个:模块接口骨架生成,比如让你写一个带 AXI 接口的模块,AI 能快速给出端口定义、参数定义和空壳逻辑,节省大量打字时间;参数化代码生成,像位宽参数可配的加法器、计数器、移位寄存器,AI 在给定参数语义的情况下表现稳定;状态机伪代码转 RTL,你把手绘的状态转移图或者伪代码贴给 AI,让它生成 case 风格的 FSM,照着一份明确的规范做翻译任务,AI 是称职的;testbench 辅助生成,AI 写测试激励和断言的能力往往比写 RTL 本身更好,因为它不需要处理复杂的时序收敛问题。

不适合用 AI 的场景也很清晰:涉及到流水线时序优化、跨时钟域处理、低功耗设计、复杂仲裁逻辑这些对细节要求极高的部分,AI 生成的结果往往"看起来逻辑正确、实际不可综合或者时序收敛不了"。在这类关键路径上,我强烈建议不要让 AI 直接产出最终代码,最多让它给一个初稿或者思路框架,然后由人来重新设计。

6. 从 783 道题走向真实工程,后续还能怎么扩展

6.1 基准本身的局限性

这 783 道题是目前衡量 AI 写 Verilog 能力最靠谱的公开尺子,但它不是万能的。它只测功能正确性,不测面积、功耗和时序收敛。一个模型可能在 783 道题上拿了高分,但生成一个大模块放到综合工具里跑,时序路径乱七八糟,面积翻了两倍,这样的结果在真实工程里照样上不了板。

它还偏向"模块级"题目,离系统级设计还有很大距离。783 道题里几乎没有涉及总线协议、存储控制器、中断控制器这类复杂 IP 的题目,倒不是说这些题目的代码量更大模型就写不出,而是这些系统级模块需要考虑的交互约束远超一道题能描述的范围。

题目泄漏的风险也必须重视。HDLBits 的题目和题解在网上广为流传,如果模型训练语料里含有这些内容,分数会有虚高成分。我实测时特意把 Human 集里几道模型表现特别好的题目拿出来反查,确认没有直接命中训练集,但无法保证全部排除。这也是为什么官方后来持续补充新题目——基准和模型之间的"军备竞赛"是动态的。

6.2 AI 辅助硬件设计可能的演进方向

这次实测让我确信一个方向:AI 写 Verilog 的下一步不是"让模型独立完成整个模块",而是"让模型嵌入到现有的 EDA 工具链里做协同设计"。具体来说,模型先根据自然语言规格生成初版 RTL,然后调用仿真器跑测试,把失败信息反馈给模型,模型自己改,这样一个闭环会越来越成熟。我在实测里用的那种"错误反馈迭代"其实就是这个思路的简化版,效果已经不错了。

另一个可能的方向是"规格到验证"的一体化。AI 不仅能写 RTL,还能根据同一个规格生成对应的 testbench 和断言,然后用覆盖率工具验证自己生成的代码。这个闭环比单纯生成 RTL 更实际,因为验证工作量的占比往往比设计更大,AI 在这里的价值可能更高。

最后说点个人体会。783 道题跑完之后,我没有得出"AI 写硬件代码不行"或者"AI 即将取代硬件工程师"这种极端结论。真实情况是:AI 在中等规模、规格明确的模块级 RTL 生成上,已经达到了初级到中级工程师的水平;但在涉及复杂状态交互、边界时序、真实物理约束的地方,它仍然高度依赖人来兜底。我目前的工作流是让 AI 先出模块骨架和初版实现,仿真不过就反馈错误日志,迭代三轮以内能解决大部分问题。如果后面再出更好的模型,我大概率还会继续用这套基准做回归对标,量化能力总比感觉靠谱。