news 2026/9/8 14:57:18

AI生成Verilog如何摆脱“巨型代码块”?HiVeGen层次化生成解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI生成Verilog如何摆脱“巨型代码块”?HiVeGen层次化生成解析

刚接触用AI写Verilog的时候,估计不少人和我有同样的感受:明明只是要一个小功能模块,大模型经常甩给你一个两三百行的module;要是让它“写一个完整的Cache”或者“实现一个支持AXI的UART”,它恨不得把整个芯片塞进一个文件里。更夸张的是,这些生成结果里所有状态机、计数器、接口逻辑全部堆在一个always块中,甚至把寄存器堆、FIFO、仲裁逻辑全部硬塞进同一个module的端口列表里,review的时候血压直接拉满。

这种现象在ICLAD 2025 的 Best Paper 中被正式摆上了台面。这篇获奖论文叫 HiVeGen(Hierarchical Verilog Generation),专门研究了“AI生成Verilog为什么总是一片平铺的大代码块”这个问题,并给出了一套层次化生成方案。我花了一晚上精读这篇论文,结合自己平时用AI写RTL、做验证的经验,今天把这篇文章的核心逻辑拆给各位硬件工程师听。这不仅是论文内容梳理,更是一次关于“怎么让大模型更好地辅助硬件设计”的方法论翻新。

1. 先把问题说清楚:AI为什么总爱“一锅端”

1.1 大模型写RTL的“职业病”从哪来

先说结论:这还真不能全怪大模型。很多人以为AI写代码的逻辑和资深工程师一样,先搭架构、再分模块、逐层例化,最后再做集成。但实际上的工作方式完全不同,大模型本质上是一个逐token生成文本的自回归模型,它对你输入的prompt的响应,是在概率空间里一步一步“猜”下一个字符。这种机制决定了它有几个致命倾向。

第一个倾向是追求连续完整性。自回归模型在生成代码时,会倾向于输出一段从头到尾中间不断裂的连续文本。你让它写一个“包含FIFO、状态机和数据通路的完整UART模块”,它的训练数据告诉大家:最安全、最符合用户表面期待的答案,就是把FIFO、状态机、波特率发生器、数据通路全部糅合在一个module里面线性输出。如果它中途停下来告诉你“建议分成三个模块,你分别生成”,反而会让多数用户觉得“这AI不行,写不完整”。

第二个倾向是训练数据的分布偏差。大模型预训练时喂进去的Verilog语料,很大一部分来自GitHub等公开仓库里的零散RTL文件。这些文件里占多数的是独立的module,比如某个IP的单一文件、某个算法模块的单文件实现。真正来自大型芯片项目的多层级目录结构、几十个子模块互连的工程化代码,反而不容易被完整采集。模型学习的统计规律,自然偏向“一个文件一个module”的简单形态。

第三个倾向是对硬件设计方法学的理解缺失。软件代码里,一个函数长一点、一个类里代码多一点,虽然不优雅,但勉强还能跑。硬件代码的复杂度则完全不同:一个巨型module意味着综合工具要花更长时间做逻辑优化和面积映射,验证时功能覆盖率难以收敛,后端布局布线时timing和congestion问题一抓一大把。可大模型没有做过芯片流片,它在生成时并不会考虑这些下游代价。

1.2 “巨型代码块”在真实项目中到底有多要命

我见过不少同学直接用AI生成一个大模块然后拿去综合,最后被各种工具问题折磨。这里我把巨型代码块的危害分成五个层面,逐一说明。

可读性与维护性层面:一个1000行的module意味着至少五六百行的端口定义和信号声明,两三组不同的状态机逻辑叠在一起。你review代码时,光滚滚动条就要花十分钟。最难受的是,AI还喜欢复制粘贴式命名,state_next1state_next2data_buffer_1data_buffer_2,我甚至见过AI生成同一个信号的三种不同名称然后互相驱动的“名场面”。这种代码在代码评审时根本没法治,只能让写的人自己回去反思。

综合与实现层面:综合工具处理大模块时,通常会把这个模块当作一个整体来做逻辑优化。模块内部的逻辑层级越深、控制流越复杂,综合工具收敛到目标频率所需的时间就越长。一个巨型组合逻辑块意味着关键路径的优化空间被限制在单个模块内部,工具很难跨模块边界做合理的重定时和优化。

验证与调试层面:模块拆分的本质其实是一种“验证边界”。如果所有逻辑都在一个module里,你只能靠顶层的testbench直接拉内部信号来调试。但商用EDA工具在正式验证流程中更倾向于用断言和属性检查,这些检查如果全部集中在顶层,一旦数据路径出了问题,定位效率非常低。层次化设计能让你在子模块级别就锁定bug范围,这比从顶层往下逐层深挖快得多。

复用与迭代层面:芯片设计里最值钱的资产是可复用的IP。如果AI生成的整个设计是一个不可分割的大模块,那下次你想在一个新项目里复用它的一部分功能,只能做移植手术,把需要的逻辑从大module里抠出来再改端口。抠逻辑的时候又容易引入接口不匹配和跨模块信号依赖的坑,整个过程费时费力。

版本管理与协同层面:多人协作时,分割代码是基本操作。一个module一个文件,每个人改自己的文件,merge时的冲突概率低。而一个巨型module占了整个工程的核心文件,多人同时改同一个文件,每一次merge都是一场冲突大战。这种痛苦经历过的人自然懂。

2. HiVeGen 到底在做什么:核心思路拆解

2.1 一句话理解这篇论文

HiVeGen的核心主张其实非常朴素:让AI像工程师一样,先拆模块,再分别实现,最后做集成。它不是对大模型本身做改造,而是设计了一套“生成策略”或者说“编排流程”,让现有的LLM能够产出结构良好的层次化Verilog代码。

论文的比测基线也很有意思,直接把AI生成一个大模块和AI按层次化流程生成的结果做了对比,发现层次化流程不仅让代码结构更好,而且在编译通过率、仿真正确性、可读性评分这些维度上全面占优。这个结论符合一线工程师的直觉:层次化是硬件设计的常识,AI生成策略也应该继承这个常识,而不是让常识迁就AI的原始输出习惯。

2.2 三个台阶的生成流程

按照论文的思路,HiVeGen把生成过程分成了三个递进阶段,我用自己的话复述一下。

第一阶段是设计分解。拿到用户的自然语言设计需求后,会让大模型先生成一个“微架构级别的设计方案描述”。这里要求模型以文本形式列出整个设计需要哪些子模块,例如一个通信IP可能需要“发送端FIFO”“接收端FIFO”“链路层状态机”“CRC校验模块”“寄存器配置模块”等。同时还要说明每个子模块的职责边界和它们之间的数据流、控制流关系。这一阶段不生成任何RTL代码,纯粹是在架构层面做规划。

第二阶段是子模块逐个生成。按照第一阶段列出的模块清单,依次为每个子模块单独生成Verilog代码。由于每个子模块的功能都被限制在较小的范围内,生成结果相对短小,逻辑也更清晰。这里还有一层细节是,子模块的端口定义需要在前一阶段就确定。也就是说,在架构描述阶段,HiVeGen会要求大模型一并输出“端口接口列表”,包括信号的位宽、方向、协议含义,这样在生成每个子模块时,端口定义和模块间互联已经有了统一协定。

第三阶段是顶层集成。当所有子模块代码都生成完毕后,再让大模型生成一个顶层module,把这些子模块像搭积木一样例化起来,完成信号连接。这个顶层模块通常非常薄,主要负责例化、连线和一些必要的跨模块信号转换。

除了生成流程本身,HiVeGen还加入了自动检查和修复机制。每生成一步,都用编译器做语法检查,用仿真器做基础冒烟测试,如果出现编译错误或端口不匹配,会带着错误信息回喂给大模型,让它自行修复。这个“生成-检查-修复”的闭环,也是论文强调的一个重要设计。

2.3 为什么层次化生成有实际收益

论文的实验数据我不在此逐条罗列,但从实操角度来说,这套方案的收益是显而易见的。

首先是问题边界可控。当生成一个子模块的代码出错时,报错信息能精确指向这个模块,修复时也只需要针对这个模块重新生成,或者在这个模块的代码上进行小范围修改,不需要让大模型重新生成整个大设计。从工程成本角度看,这种精细化的容错方式效率高得多。

其次是契合人的介入方式。现在的AI辅助EDA流程里,工程师不可能完全当甩手掌柜。在层次化流程的每个阶段,人都可以在关键节点进行干涉,比如调整模块划分方式、指定某些模块采用特定的握手协议、修改接口位宽等。这种干涉比在巨型代码块里做外科手术式的修改要轻松得多,而且不影响整体设计进度。

第三是验证能够分层进行。子模块可以有自己的独立testbench,顶层又有顶层集成测试。如果子模块验证充分,顶层集成时遇到的接线错误,定位范围和概率都会更小。论文中特别强调,验证效率的大幅提升是层次化生成最大的隐性收益,这我深表赞同。

3. 论文关键技术细节精读

3.1 模块拆分的粒度控制

HiVeGen这套流程里,最考验大模型能力的就是模块拆分的质量。模块拆得太粗,每个子模块本身还是很大的代码块,没有真正解决可维护性问题;模块拆得太细,又会让顶层例化关系变得碎而深,接口数量爆炸,反而增加集成的复杂度。

论文里给出的经验是,把模块粒度控制在单模块代码量不超过200行左右,一个设计拆成5到10个子模块。这个区间兼顾了代码可读性和集成成本。我自己的实操经验也与这个结论相符:追求单个module短小时,状态机、计数器、数据通路分开,每块逻辑都很好理解;但如果拆到更多,比如每个寄存器组都要一个文件,那光文件切换和维护模块之间的依赖关系就够你喝一壶的。

另外,论文提到在分解模块时,会显式要求大模型标注每个模块的类型归属,比如“control logic”“datapath”“memory interface”这样的分类标签。这个做法的好处在于,后续生成代码时,模型能够根据模块类型自动匹配对应的代码风格模板。控制逻辑用状态机写法,数据通路用并行赋值写法,存储交互用握手时序写法,生成质量会有明显提升。

3.2 自顶向下分解与自底向上生成的结合

HiVeGen的流程表面上看是自顶向下的:先有架构,再拆模块,最后集成。但论文特别强调,在生成环节使用了一种“自底向上验证”的策略,这个细节很容易被略读。

具体来说,模块生成的顺序不是随机的。HiVeGen会先处理那些没有依赖关系的“叶子模块”,比如CRC计算、FIFO存储体、波特率分频器等基础模块。这些模块生成后你就能立刻做编译检查和单独的仿真验证,确认没问题后,再向上处理依赖这些底层模块的逻辑模块。最上面的顶层模块是最后才生成的。

这样做的好处非常明显:你在每一层都有经过验证的可靠基础。越往上构建时,底层出现错误的概率越低,排查范围越小。这就像盖房子时先确保地基和每一层楼板的质量过关,而不是等整栋楼都搭完了再发现问题,那时候整改的代价就太大了。

3.3 验证与合法性检查的工程化设计

HiVeGen中让我觉得最务实的地方,是它对验证资源的利用策略。它不追求用现代的UVM或formal验证方案,而是用直接的编译加仿真冒烟测试来筛选生成结果。

每次生成完代码,会跑一次快速的语法检查和功能仿真。仿真激励也不复杂,通常是一个简单的testbench,喂入几组典型输入,查看关键输出是否与预期一致。如果通过,进入下一阶段;如果没有通过,把编译器和仿真器的报错信息连同当前代码一起打包,回喂给大模型,让它务必“根据以下错误信息进行修改”。

这个“错误反馈回路”其实非常关键。很多人在实际中使用AI写Verilog的时候,如果生成的代码仿真不过,往往选择重新生成一个新的版本,但重新生成可能引入新的问题。HiVeGen的这种迭代修复方式,更像是在真实项目中“debug一版代码”,而不是反复推倒重来,收敛速度要快得多。

需要注意的一个细节是:迭代修改的次数并不是无限制的。一旦反复修改多次仍然无法通过基本检查,论文的策略是回到模块拆分阶段,重新考虑模块划分和接口定义。这是因为反复报错往往说明接口定义或模块职责划分本身存在不合理之处,单纯的代码修复解决不了根本问题。这种“向上回退”的思路,工程上非常实用。

3.4 与其他AI硬件生成方法的对比

近几年用LLM生成RTL的论文和工具并不少,但大多数集中在“如何提升单次生成代码的语法正确率”或者“如何通过更长的上下文来直接生成更大的模块”。HiVeGen的视角更偏向设计方法学:

  • 传统方法:prompt工程 + 代码补全 + 语法修复
  • 上下文扩展方法:把整个设计规格和所有模块代码全部塞进一个巨大上下文,让模型一把梭生成全部代码
  • HiVeGen方法:把生成过程拆成“架构设计-模块生成-顶层集成”三个阶段,每个阶段的上下文都相对独立,用验证反馈来串联

HiVeGen的优势在于对上下文窗口的要求更低,不需要为了生成一个大设计而无限扩展上下文长度;对出错后的修复成本也更低,可以在子模块级别完成调试。这其实切中了算力受限场景下的实用需求:不是每个用户都拥有可以处理超大上下文的模型,但几乎所有用户都能运行一个中等长度上下文的模型来分步生成。

当然,论文也坦承了局限性,比如对于接口协议非常严格的设计场景,自动生成的子模块互连可能存在隐藏的时序或协议风险,单纯靠编译和仿真检查未必能发现所有问题。这意味着HiVeGen在可综合级别的简单设计上表现最好,但对于带严格工程约束的高速接口类设计,仍然需要资深工程师的深度介入。

4. 实操视角:没有HiVeGen,工程师怎么用AI写出可维护RTL

读论文的本质在于吸收方法论,而不是期待论文给出一套开箱即用的工具。HiVeGen目前主要还是一个研究性质的工作,但它提出的思路完全可以落到日常工作中。这里分享我自己的实践心得。

4.1 提示词层面先把“模块划分”逼出来

想让AI按层次化方式生成代码,最简单的一步就是在prompt里强制要求模块划分。我平时会让AI先回答三件事后再写任何代码:

  • 这个设计可以拆成哪几个功能子模块,每个模块分别做什么?
  • 子模块之间的接口信号有哪些,各自的位宽和方向?
  • 顶层模块的例化关系是怎样的?

如果AI直接开始生成代码,我就会中断它,明确告知“先输出设计拆分方案,不要写代码”。大多数支持多轮对话的模型都能理解这个约束。这一步强制了“先设计再编码”的流程,从源头降低了巨型代码块出现的概率。

4.2 用AI生成空白模块骨架,而不是直接生成实现

这里分享一个我自己常用的方法:让AI先生成每个子模块的module声明和输入输出端口定义,但内部逻辑留空。把这些骨架代码集中起来review,确认模块划分和端口定义都合理后,再让AI逐个子模块去填充内部实现逻辑。

这个方法的妙处在于,它把“接口设计”和“逻辑实现”变成了两个独立的检查节点。接口设计阶段的错误,在你开始写内部实现之前就被抓住,成本极低。而且每个子模块的实现是相对独立的任务,上下文更精简,模型生成的质量会更高,也不会出现模块之间信号命名不一致的问题。

4.3 用“伪代码 + 引脚表”替代纯自然语言描述

如果想让AI生成更符合你预期的接口,建议别只扔给它一句文字描述。你可以在prompt里加上精确定义的接口表格,包括信号名、方向、位宽、功能备注。这能让大模型在生成时严格遵守接口规范,而不是自己脑补一套接口,然后在顶层集成时对不上号。

举个例子,我想让AI生成一个双端口RAM模块,我会这样写:

请生成一个同步双端口RAM模块,端口定义如下: - clk: input, 1bit, 时钟信号 - rst_n: input, 1bit, 异步复位,低有效 - wr_en: input, 1bit, 写使能 - wr_addr: input, 8bit, 写地址 - wr_data: input, 32bit, 写数据 - rd_en: input, 1bit, 读使能 - rd_addr: input, 8bit, 读地址 - rd_data: output, 32bit, 读数据

这种带有明确引脚定义的prompt,生成结果基本不需要改接口,可以直接用。

4.4 生成后的自动检查清单

不管用哪种方式让AI写代码,我建议生成结果后别急着仿真,先做一个基础的静态检查,我自己常用这份清单:

  • 模块名是否与文件名一致,有没有多个module堆在同一个文件里
  • 是否有未声明的信号,或者声明了但没有driver的信号
  • 是否存在多个always块不小心对同一个reg变量赋值的问题
  • 时钟复位是否统一,有没有在组合逻辑always块里出现时钟信号
  • 端口位宽与连接信号的位宽是否匹配,有没有在顶层例化时出现位宽不匹配的warning

这些检查项不一定需要用lint工具来做,肉眼加文本编辑器搜索也能完成。它们的意义在于把AI代码里最常见的问题在早期就拦截下来,避免带病进入仿真和综合流程。

5. 常见问题与排查技巧实录

5.1 模块拆得太碎反而更难维护

有朋友反馈,看了HiVeGen的介绍后尝试把小模块拆到极致,结果发现设计拆成十来个小模块,每个模块只有二三十行,接口信号满天飞,顶层例化代码比之前大模块里实现功能的代码还长。这种情况就属于模块拆分粒度过细。

模块拆分的判断标准应该是:一个模块最好包含一组完整的、可独立验证的功能语义。比如FIFO是一个整体,CRC校验是一个整体,状态机控制器是一个整体。如果一个模块只能完成“把信号A赋值给信号B再打一拍”这种事,那它不具备独立验证的意义,应该合并到调用它的模块里去。记住,层次化的目的是改善可读性和验证效率,不是单纯地“把代码变短”。

5.2 AI生成子模块时端口定义不一致

这个坑我踩过好几次。AI生成两个子模块时,明明架构设计阶段指定的接口信号是同一个,但在生成不同子模块时它会擅自改名或者改变位宽。A模块的输出叫tx_data,B模块的输入却被写成了rx_data,位宽也从8位变成了16位。

应对方式有两个层面。第一个层面是把端口表写进每个子模块的prompt里,明确告诉模型这里必须严格按照指定接口输出,不要修改信号名和位宽。第二个层面是在顶层集成了之后写一段自动检查脚本,解析所有例化语句,确保所有连接信号的位宽对齐。有些经验丰富的工程师会直接在JSON里定义整个设计的接口,然后让AI从JSON里读取接口信息,这样一致性会好很多。

5.3 仿真能过但综合不过的类型问题

AI生成的代码做个简单的仿真测试很容易通过,但一旦拿到综合工具里就疯狂报错。常见的情况有两类,一类是不可综合的语法,比如initial块里给reg赋值、循环次数无法确定、使用了不支持的延时控制;另一类是敏感列表不完整或者在组合逻辑always块里不小心嵌入了时序逻辑描述。

我的经验是:让AI生成代码时,在prompt里加一句话“请确保生成的代码是可综合的,不要使用initial、delay、fork join等不可综合语法”,能减少一半以上的综合错误。同时,每一层模块在完成静态检查后,尽量用综合工具做一次快速的“语法综合检查”,不需要跑到布局布线,只用elaborate阶段跑一遍确认没有不可综合的语法结构就行。

5.4 仿真失败时别忙着重新生成

前面提到HiVeGen的错误反馈机制,换言之就是迭代修复而不是重新生成。这个策略我特别认同。在实践中,如果第一次生成的代码仿真失败,直接把报错信息和波形信息回喂给大模型,让它修改当前代码,通常比让它重新写一遍有效得多。

原因在于,重新生成时模型会重新做概率抽样,语法结构、模块划分思路、状态机编码方式都可能完全不同,修复了旧bug又可能引入新bug。而基于当前代码迭代修改,相当于在局部搜索空间内做优化,稳定性高很多。我建议把AI当作“第一个版本的作者”,而不是“最终的实现者”。它写完的代码经过你的修改后回喂给它,它能够在这个基础上继续优化,这种交互方式更高效。

5.5 完全放手让AI写大型设计仍然不现实

最后说一个比较清醒的认知。尽管HiVeGen提出了很好的流程优化,但以当前大模型的代码生成能力和上下文理解能力,完全放手让AI独立完成一个大型芯片设计仍然不现实。复杂设计里的微架构规划,尤其是涉及多核缓存一致性、低功耗状态切换、时钟域交叉策略等高级主题时,大模型的理解深度仍然不够。

所以我更倾向把AI定位成**“高效的初级工程师”+“全知的技术顾问”**。它可以帮你把80%的常规RTL写掉,可以给出初级方案,可以帮你在系统verilog和UVM中搭建验证环境,但架构决策、关键时序约束、协议设计这些核心工作,需要资深工程师来把关。HiVeGen的价值在于,它让“AI生成代码-工程师审查修改”这条协作链路的边界更加清晰:模块化生成给了人更多控制节点,也让审查难度大幅降低。

6. 在真实项目中用AI写RTL的一点体会

读了这篇论文之后,我在自己手头的一个小IP设计里尝试了HiVeGen式的流程:先用AI做设计拆分,确认模块清单,再逐个模块去生成,最后让AI完成顶层集成。整体下来最大的感受不是“AI变聪明了”,而是我作为工程师能控制的地方变多了

之前用AI写RTL,总有一种“代码失控感”,生成结果像一块沉重的毛坯砖头,你只能要么接受、要么推翻重来。现在改用模块化流程,每一步的产出物都很轻,可审查、可修改、可回退,整个过程变得像和一个初级的、速度快到离谱的工程师协作一样顺手。

如果你现在手头也面临类似的问题,不用非等工具落地,完全可以先把这套流程用起来。下次再让AI生成Verilog的时候,记得先说一句话:“先给我模块划分方案,再逐模块生成代码,最后做顶层集成。”这句话的价值,可能比换一个大模型还实在。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 14:55:53

内网通讯软件到底适合谁:数据资产、安全边界与选型自测指南

前阵子一个客户把内网通讯软件列入了下一年度的预算,请我帮忙做产品评估。他老板在立项会上问了一句很实在的话:“我们公司又不是保密单位,聊天工具用什么不是用,为什么非要自己搭一套?”这个问题其实问到了很多企业的…

作者头像 李华
网站建设 2026/9/8 14:52:37

IAR跨平台IDE:Linux与Windows原生支持打通嵌入式开发

从Windows到Linux:IAR这次终于把原生跨平台IDE补齐了 我用了快八年的IAR Embedded Workbench,一直以来都存在一个很别扭的局面:代码在Windows桌面机上写、编译、调试,但一到自动化构建、持续集成、固件批量产线验证这些环节&#…

作者头像 李华
网站建设 2026/9/8 14:52:07

AI Agent推理成本优化:从模型路由到混合推理服务的实战指南

上个月做技术复盘时,我们团队盯着监控大屏上那条持续走高的推理成本曲线,一时间没人说话。做AI Agent快两年,我见过太多项目把“降本”简单理解为“换更便宜的模型”,结果换来的是重试率飙升、用户体验下降,最后账单反…

作者头像 李华
网站建设 2026/9/8 14:51:37

ComfyUI去AI感洗图工作流:Flux构图+SD1.5回洗实战

简介:面向 ComfyUI 与 Stable Diffusion 1.5、Flux 用户的去AI感洗图工作流资源,适合批量出图后仍觉得画面带有明显算法痕迹、希望进一步优化为自然质感的创作者。资源以单个 JSON 工作流文件为核心,文件总数 1 个,大小仅 9KB&…

作者头像 李华
网站建设 2026/9/8 14:49:25

GitNexus架构解析:如何为AI编程加上安全可控的变更治理层

1. 我的GitNexus初体验:AI编程到底哪里不靠谱 先说个场景。我所在的小团队从去年开始全面引入AI辅助编程,Copilot、Cline、Cursor轮着用。代码产出速度确实快了,但随之而来的是一堆让人头疼的问题——AI经常把原本能跑的功能改崩,…

作者头像 李华