PPA-RTL:让大模型在综合反馈中学会优化芯片设计
1. 从“仿真能过”到“综合能收”AI生成RTL的真正短板先讲个我自己的经历。大半年前我沉迷于让大模型帮我写RTL那会儿朋友圈里晒的都是“AI一分钟写出UART”“ChatGPT生成流水线CPU”看起来一片繁荣。我也跟着试让模型写了一个带优先级仲裁的队列管理模块仿真确实一把过功能波形挑不出毛病。我当时还挺高兴想着这下前端效率要翻倍了。结果等我把这段代码丢到OpenROAD里做综合麻烦就来了。时序报告一拉出来WNS负了0.23纳秒关键路径长得离谱。更尴尬的是我说给模型听“这段代码综合出来时序违例了”它根本不知道我在说什么。它只见过RTL文本没见过RTL被综合器翻译成门级网表之后的样子。这就是我想聊的“PPA-RTL”概念的起点如果大模型写RTL只关心语法正确、功能正确却不关心这段代码综合出来之后功耗、性能、面积到底怎么样那它本质上还是在“纸上谈兵”。1.1 为什么功能正确的代码会在物理设计阶段“翻车”很多做软件出身的朋友不太理解这一点芯片设计的验收标准不是“仿真过了”就行而是“综合能收”。所谓综合是把Verilog/VHDL这种行为级描述映射到特定工艺库下的门级网表再经过布局布线才算真正接近物理实现。这个过程中代码的写法对结果的影响极其巨大。举个例子同样是实现“四输入优先级仲裁”有人写成串行的if-else嵌套有人写成并行的优先级树。两者功能完全等价仿真波形一模一样但综合出来的关键路径延迟可能差出20%到30%。前端RTL工程师如果只盯着仿真看永远发现不了这类问题只有把综合报告拉出来看到WNS、TNS、逻辑级数才知道自己写的代码到底行不行。更要命的是大模型生成RTL时脑子里并没有“物理实现”这个概念。它从海量代码片段里学习到的规律大多是“功能怎么写才对”而不是“结构怎么排才优”。于是AI生成的RTL经常出现一种通病代码style很规范注释很齐全仿真也很干净但一综合就露出原形——要么组合逻辑链太长要么高扇出信号满天飞要么到处是 latch。1.2 PPA三件套频率、面积、功耗到底在反馈什么信息既然要让模型“关心”综合结果首先得把PPA这三个字母拆开看清楚。PPerformance主要指最高工作频率落实到报告里就是WNS最差负裕量和TNS总负裕量。WNS是全局最差的那条路径的时序裕量它决定了芯片能不能跑到目标频率TNS则是所有违例路径的裕量之和反映整体的时序健康程度。我在项目中看WNS看的最多因为它是一个项目能不能按时收敛的“体温计”。PPower功耗分为动态功耗和泄漏功耗。动态功耗跟翻转率、负载电容、供电电压相关泄漏功耗跟工艺节点、单元阈值电压相关。低功耗设计在移动端和AI推理芯片里是硬指标大模型如果不知道怎么通过RTL结构调整数据通路来降功耗生成的代码很难直接商用。AArea面积直接决定成本。同样的功能组合逻辑打散了重写、状态机编码方式换了面积可能差一大截。对FPGA来说对应的是LUT、FF、DSP、BRAM的用量对ASIC来说就是标准单元总面积和利用率。这三个指标之间还存在典型的跷跷板关系。插入流水线寄存器能提频率但会增大面积和功耗把组合逻辑做并行化能提频率但会增加资源占用采用低阈值电压单元能提时序但泄漏功耗会上升。所谓的“PPA优化”本质上就是在这三个维度之间反复博弈找一个满足约束的平衡点。1.3 PPA-RTL想做的事把综合报告变成模型的“眼睛”我理解的“PPA-RTL”不是一个具体的工具也不一定是一个新语言而是一种工作范式的转变让大模型在生成RTL之后能“看见”这段RTL综合出来的真实结果并根据结果进行迭代修改。这意味着两件事。第一要在RTL生成流程里接入综合工具跑出真实数据第二要把综合报告里的海量信息压缩成模型可以理解、可以行动的上下文。前者属于流程搭建后者属于提示词工程和数据结构设计两者缺一不可。现在的公开大模型大多做过代码生成训练写RTL的正确率已经不错了。但它们缺乏“感知下游物理实现”的能力。你问它“这段代码综合出来频率多少”它只能根据经验猜一个大概没法真正知道。而PPA-RTL的思路是把这个缺失的环节用工程手段补上——把综合报告喂回去让模型看到自己写的代码在工艺库映射下到底长什么样。我做了三轮迭代实验之后最大的感受是模型一旦能“看到”WNS和逻辑级数它给出的优化建议跟盲猜完全是两个水平。下面两章我把完整的方法和实测过程展开讲。2. 搭建PPA反馈回路从综合报告到模型可读的优化提示要让大模型关心PPA核心工程问题是综合工具输出的报告和模型能消费的文本上下文中间的翻译层怎么做。直接拿整份综合报告喂给模型是行不通的。一份稍微复杂点的设计report_checks跑出来可能有几百条路径加上面积报告、功耗报告动辄几十KB的文本既超出上下文窗口里面还充斥着大量模型不需要看的中间细节。你需要的是先提炼再结构化最后转换成优化提示。2.1 一个可复现的基线环境OpenROAD加标准单元库我实验用的基线环境是开源的OpenROAD流程配合一组开放的标准单元库比如Nangate45或者SkyWater 130nm。这套流程的好处是完全可以脚本化适合做自动化迭代。先准备一个综合脚本核心步骤大致是这样# run_flow.tcl 片段 set ::env(CLOCK_PERIOD) 2.0 read_verilog rtl/arbiter_ppar.v read_liberty -lib Nangate45.lib read_sdc constraints/arbiter_ppar.sdc link_design arbiter_ppar synth -top arbiter_ppar -generic report_checks -path_delay max -digits 4 -format end reports/timing.rpt report_area -digits 4 reports/area.rpt report_power -digits 4 reports/power.rpt注意几个细节读入的库和SDC约束必须跟真实设计目标一致不能随便拍脑袋写一个。CLOCK_PERIOD设成2.0ns对应500MHz这是我这次实验的目标频率如果实际场景是低频低功耗约束就完全不同。跑完综合后OpenROAD会在reports目录下生成三份文本报告。我的做法是写一个小的解析脚本把关键数据抽出来拼成一段结构化的“PPA摘要”。2.2 报告解析从WNS、面积、功耗文本中提取关键事实OpenROAD的时序报告格式大致长这样我截取关键几行Startpoint: req_masked[0] (input port clocked by clk) Endpoint: grant_int[1] (internal endpoint) Path Group: clk Path Type: max Delay Time Description --------------------------------------------------------- 0.0000 0.0000 clock clk (rise edge) 0.0000 0.0000 clock network delay (ideal) 0.0000 0.0000 input external delay 0.1644 0.1644 input slew ... 0.0988 5.0428 data path 0.1240 5.2300 clock uncertainty -0.2300 0.0000 slack (VIOLATED)解析脚本不需要很复杂把WNS、TNS、单元面积、总功耗这几个字段抽出来就够了。用Python写个几十行的脚本最顺手import re def extract_timing(path): wns tns None with open(path) as f: for line in f: m re.search(rwns\s([-\d.]), line) if m: wns float(m.group(1)) m re.search(rtns\s([-\d.]), line) if m: tns float(m.group(1)) return wns, tns当然不同工具输出格式不一样Vivado的report_timing_summary、Design Compiler的report_qor、OpenROAD的report_checks格式各有差异。解析逻辑也要跟着变。但它们的共同点都是最终落到几个数字上。我们要的就是这几个数字以及违例路径里最集中的逻辑级数分布。抽完数据之后我习惯拼成下面这段摘要作为喂给模型的上下文PPA 摘要 for arbiter_ppar (Nangate45, 500MHz约束): - WNS: -0.230ns (最差路径违例230ps) - TNS: -3.710ns (共12条违例路径) - 关键路径逻辑级数: 32级 - 面积: 8425 um^2 (单元数 31247) - 总功耗: 43.2mW (动态 38.9mW, 泄漏 4.3mW) - 高扇出信号: grant_int[1] 扇出 256这段摘要信息量其实已经很大了。逻辑级数32级说明组合逻辑链太长高扇出信号256说明某个内部信号驱动的负载过重。模型拿到这些事实才有“诊断”的依据。2.3 反馈提示模板不让模型“瞎猜”只让它做“结构分析”光有摘要还不够得设计提示词的结构。我的做法是给模型一个明确的角色和工作范式限制它不要乱改接口也不要一上来就输出完整代码而是先给“结构诊断”再给“修改建议”。以下是我实验里实际用过的提示模板分享出来供参考你是一位熟悉数字电路物理设计的RTL优化工程师。 下面是某个模块综合后的PPA摘要 {PPA摘要} 请按下面的步骤输出 1. 用两三句话概括当前的主要性能瓶颈指出最可能导致时序违例的结构问题。 2. 给出最多三条具体的RTL修改建议。每一条必须包含 - 修改位置哪个模块/哪个信号 - 为什么这样改能改善PPA比如缩短组合逻辑链、降低扇出 - 预期收益 - 可能的副作用比如面积增加、功耗上升、流水延迟变化 3. 不要改变模块端口列表和对外接口协议。 4. 如果当前问题靠RTL层面无法解决请明确说“需要工具层面/后端介入”不要硬给方案。这个模板看起来简单但作用很明显。它把模型从“自由发挥写代码”拉回到了“有依据地做工程分析”的轨道上。模型看到WNS是负的就会主动去猜关键路径在哪个模块看到高扇出信号就会想到打散复制寄存器。这比直接问“帮我优化这段RTL”要有效得多。3. 三轮实测复盘仲裁器从WNS-0.23ns到满足设计约束理论讲完必须上实测。我选了一个很小的设计来做PPA-RTL实验模块是一个四输入负载均衡仲裁器大概一百行Verilog。选它的原因很简单结构不复杂但组合逻辑链和高扇出问题很典型非常适合验证反馈回路是否有效。3.1 初始设计功能没问题时序一言难尽初始版本我用大模型直接生成代码能跑优先级轮询功能正常。核心逻辑的核心问题在于我用了一层嵌套的if-else来做轮询优先级选择结构大概长这样// 轮询优先级选择逻辑简化的意图展示 always (*) begin if (masked_req[0]) begin grant_candidate 2d0; end else if (masked_req[1]) begin grant_candidate 2d1; end else if (masked_req[2]) begin grant_candidate 2d2; end else if (masked_req[3]) begin grant_candidate 2d3; end else begin grant_candidate 2d0; end end把这段代码综合之后第一轮报告的表格如下指标初始综合结果WNS-0.230 nsTNS-3.710 ns违例路径数12关键路径逻辑级数32面积8425 um²总功耗43.2 mWWNS负到230ps按2ns的时钟周期来看这意味着关键路径实际需要2.23ns才能稳定目标频率500MHz完全跑不到。我把这份报告摘要丢给大模型它给出的第一轮诊断是嵌套if-else生成了串行比较链建议改成树形比较结构同时把grant信号的最终输出用寄存器寄存一拍打破组合环路。这个建议方向是对的但执行起来需要想清楚代价。3.2 第一轮拆长组合链WNS涨了但是面积也涨了按照模型的建议我做了两部分修改。一是把if-else的优先级比较改成逐级比较的树形结构让关键路径深度从O(N)降到O(logN)二是在grant输出之前加了一级寄存器把组合逻辑的尾巴截断。改完RTL再跑综合结果如下指标第一轮结果WNS-0.080 nsTNS-1.020 ns违例路径数4关键路径逻辑级数18面积9123 um²总功耗47.6 mWWNS从-0.23涨到了-0.08逻辑级数从32降到18说明拆链有效果。但注意面积和功耗都涨了因为树形结构需要更多并行逻辑而且新增寄存器也带来了面积开销。这验证了我前面说的跷跷板关系性能改善代价是面积和功耗上升。不过WNS仍然是负的意味着一轮优化还不足以让设计收敛到500MHz。这时候如果继续盲目加流水线可能会导致吞吐率变化甚至引入功能正确性问题。我把第二轮报告再喂给模型让它继续诊断。3.3 第二轮并行优先级树才是真正的转折点第二轮反馈中模型指出了一个新的关键问题关键路径并不全在仲裁逻辑本身还有一部分在“轮询指针更新”和“请求mask生成”的路径上。原来的设计里轮询指针更新逻辑跟请求mask逻辑串在一起导致下个周期的grant输出必须要等当前周期的grant结果算出来形成了一条很长的“跨周期组合链”。模型的建议是把轮询指针的更新逻辑单独拆开用一组独立的寄存器提前计算下一轮指针并将grant选通逻辑改为低扇出的并行查找结构。我照着改了。核心变化是引入了预先计算好的一组候选grant值而不是等请求mask稳定后现场算。相当于用面积换时序把“计算”提前了。第二轮结果指标第二轮结果WNS0.050 nsTNS0.000 ns违例路径数0关键路径逻辑级数12面积9867 um²总功耗50.1 mW到这轮结束WNS转正所有路径都满足时序约束设计跑到了目标频率500MHz。代价是面积比初始版本增加了17%功耗增加了16%。在某些低功耗项目里这个代价可能不可接受但在这个实验的目标是“时序收敛优先”所以结果可接受。这轮迭代给我最大的启发是模型在看到了第二轮WNS从-0.08只涨到-0.08之后并没有继续建议“拆链”而是转到了“跨周期结构”这个更深层的诊断。这就是PPA反馈回路的价值——模型不再盲目优化而是根据真实数据判断瓶颈在哪。3.4 第三轮AI“卷”出来的激进改动怎么甄别和拒绝有意思的是第三轮。在WNS已经转正之后我又故意问了一遍“还能不能继续优化面积”模型给出的方案是把轮询指针的并行查找表改成共享时序单元并把grant信号从8bit压缩到4bit编码再解码。它甚至说“可以节省大约600um²的面积”。我实际试了一下面积确实降了一些但代价是组合逻辑又变深了WNS重新回到了-0.03ns。也就是说这个建议在面积和时序之间偷偷做了一个不利于时序的权衡。换做是人工工程师大概率会直接否决这个方案因为当前设计的时序余量只有50ps非常脆弱为了省600um²的面积把时序推向悬崖风险太大。这个例子说明一件事大模型给的优化建议不能全盘照收。它会在你不知道的时候给你挖坑。我现在的处理方式是收到模型建议之后先让它在建议里标注“风险等级”并强制要求它列出“如果改坏对WNS的估计影响值”。然后我再结合当前项目的PPA预算来做判断。第三轮我们没有继续采纳激进改动最终保留第二轮的结果作为收敛版本。整个迭代过程的经验我浓缩成一张表优化手段对WNS影响对面积影响是否推荐树形化优先级比较正向明显小幅上升推荐输出寄存器打拍正向中等上升视接口时序约束而定跨周期预计算grant正向明显明显上升推荐需评估面积预算信号编码压缩负向不稳定小幅下降不推荐在时序紧张时用4. PPA-RTL能走多远落地形态与评测方法的一些思考实验做到这一步我开始认真思考一个问题这个框架能不能真正落到工程流程里甚至成为评价“大模型懂不懂硬件”的一种新方法4.1 预期定位不是替代工程师而是给芯片团队配一个“PPA顾问”我个人的判断是短期内PPA-RTL不应该是全自动的RTL生成器而是一个“PPA顾问”角色。它先把综合报告变成简明摘要再给出结构层面的优化方向最后仍然由人来把控关键取舍。这样做的好处是它能大幅压缩工程师在“仿真—综合—看报告—改代码”这个循环里花的时间。以前我排查一条关键路径可能要半天先把report里的路径展开看再去RTL里定位再改再综合再验证。现在这个循环里很大一部分“从报告到结构诊断”的工作模型可以直接做掉剩下的人工干预集中在对设计意图的把握上。我在实际使用中的体会是模型给出的建议至少有七成是靠谱的但最后的三成往往需要人工修正。它擅长识别“组合链太长”“扇出太高”这类模式化问题但在涉及特殊设计约束、工艺库特性、跨模块时序预算的时候还是需要人来做最终判断。所以定位成“顾问”而不是“替代者”是现阶段最务实的路径。4.2 工程化挑战工具链闭环、约束一致性和回归成本如果把PPA-RTL真正集成到流程里会面对几个很现实的工程问题。一个是工具链闭环。模型的上下文里必须包含真实综合结果那么这个综合流程就得能在一个可控的、分钟级的时间内跑完。大设计综合一次可能要跑几小时根本没法做在线迭代。目前的解决办法是把综合拆成“模块级综合”和“顶层集成综合”PPA反馈回路只在模块级生效顶层仍然靠人工把控。另一个是约束一致性。如果SDC约束跟实际工作条件不一致那综合报告里的WNS、功耗数字本身就没有意义模型根据错误数据给出的优化建议当然也会跑偏。所以每次实验前必须确认工艺库、时钟频率、输入延迟、输出负载这些信息都已经正确写入约束文件。回归成本也是一个躲不开的话题。每次模型改完RTL不能只看WNS一个数字还需要做功能仿真回归防止优化过程引入行为偏差。在我的实验流程里每轮修改之后都会跑一遍完整的仿真用例集确保仲裁结果、输出协议没有变化。这一步虽然费时间但它才是“优化”和“破坏”之间的安全网。4.3 怎么评测一个大模型是否“真的关心PPA”最后我想聊一个更发散但很有意思的问题怎么评测一个大模型懂不懂PPA。目前公开的大模型榜单基本都在刷代码生成准确率、推理能力这些通用指标几乎没有人关注“模型生成的RTL综合出来PPA表现如何”。这其实是一个被严重忽视的评测维度。我的建议是可以设计一组“等效功能、不同结构”的RTL生成任务让模型在不同的PPA约束条件下生成代码再统一跑综合看它能否根据约束动态调整自己的生成偏好。举例来说给模型同一个功能需求分别附加“优先低功耗”“优先高频”“优先小面积”三条不同的约束然后看三条输出代码在综合后的PPA差异是否明显。如果模型输出的代码在三组约束下几乎没有区别那说明它对PPA只是嘴上关心没有真正转化为代码结构上的选择。反过来如果它能针对低功耗约束主动选用门控时钟、操作数隔离这类技术针对高频约束主动插入流水线那才算有了“PPA意识”。这种评测方式比单纯问“你知道什么是WNS吗”要硬核得多因为它是从结果倒推能力。我觉得未来如果出现“RTL综合评测榜单”这里面的空间非常大。对做AI芯片工具链的团队来说这甚至可能成为大模型选型时的重要参考维度。我在实验结束之后把整套PPA-RTL的反馈回路固化成了一个小脚本。现在每天生成新的RTL模块时不再只跑仿真就收工而是顺手跑一次综合把PPA摘要打印出来。这个习惯看着简单但对设计质量的提升是实打实的。如果你也在用大模型写RTL不妨也搭一个这样的闭环让模型真正看见自己写出来的代码综合出来是什么样。