1. 从一份规格书到可综合电路:我为什么要折腾这套流程
去年年底接手一个中等规模的IP模块,功能不复杂,但接口协议是AXI4,寄存器组有几十个,还带一个轻量级的仲裁逻辑。按老路子,我得先啃完两百多页的规格书,手写RTL,再搭验证环境,最后跑综合看时序。整个过程下来,光是把规格书里的文字翻译成可综合的Verilog,就花了我将近三周。更别提中间因为理解偏差返工的那几次——寄存器地址映射错了一位,仲裁优先级搞反了,AXI的BVALID握手时序没对齐,每一个都是低级错误,但每一个都要花半天到一天去定位。
后来我就在想,规格书本身就是结构化的描述,寄存器表、状态机、接口时序,这些东西本质上都是可以被机器理解的。如果能让AI先帮我生成一版RTL骨架,我再在上面做优化和修正,是不是能把重复劳动压缩掉?这就是我开始尝试AI-Native IP研发流程的起点。所谓AI-Native,不是简单地在写代码时用一下补全工具,而是把AI嵌入到从规格解析、架构设计、RTL生成、验证用例生成到综合约束的每一个环节里,让整个流程围绕AI的能力重新组织。
这篇文章适合谁看?如果你正在做FPGA或ASIC的IP开发,手头有AXI、APB、SPI这类标准接口的模块要写,或者你是一个SoC集成工程师,需要快速评估第三方IP的接口行为,那这套思路你可以直接参考。如果你只是好奇AI能不能写RTL,那也可以看看我在哪些环节踩了坑、哪些环节确实省了时间。我不会吹嘘AI能替代工程师,但我会告诉你,在哪些具体步骤上,它确实能把你的效率提升一个档次。
2. 整体流程设计:把规格书拆成AI能吃的“饲料”
2.1 为什么选择以Spec为中心驱动
传统RTL开发是“人读Spec,人写RTL,人写Testbench”,整个链条里,Spec是给人看的,不是给机器看的。AI-Native流程的第一步,就是把Spec变成机器可解析的结构化数据。我试过两种方式:一种是直接把PDF丢给大模型让它生成RTL,另一种是先人工把Spec拆成结构化的YAML或JSON,再让AI基于结构化数据生成RTL。实测下来,第二种方式靠谱得多。
原因很简单。PDF里的表格、脚注、跨页引用,对AI来说都是噪声。你让它从一段“寄存器0x10的bit[3:0]用于配置时钟分频系数,默认值为4”的文字里提取信息,它可能给你生成一个带复位值的寄存器,也可能忘了复位值,还可能把bit范围搞错。但如果我先把这个寄存器写成结构化的描述,AI的出错率会大幅下降。所以我的做法是:人工做一次“Spec结构化”,把关键信息提取成机器可读的格式,后续所有AI生成环节都基于这份结构化数据。
2.2 结构化Spec的字段设计
我用的是一份YAML文件,核心字段包括模块名、接口列表、寄存器映射、状态机描述、时序约束。接口列表里每个接口要写清楚协议类型(AXI4、APB、SPI等)、数据位宽、地址位宽、ID位宽。寄存器映射里每个寄存器要有偏移地址、复位值、字段列表,每个字段要有位范围、读写属性、功能描述。状态机描述里要有状态列表、转移条件、输出信号。时序约束里要写清楚时钟频率、建立保持时间要求、跨时钟域处理方式。
这份YAML不是给AI看的最终产物,而是我用来和AI对话的“底稿”。我会把这份YAML连同模块的功能描述一起发给AI,让它生成RTL。这样做的好处是,AI不需要从自然语言里猜意图,它只需要把结构化数据翻译成Verilog语法。我试过同一个模块,直接丢PDF给AI,生成的RTL有七处错误;用结构化YAML,错误降到两处,而且都是可以快速修正的语法问题。
2.3 工具链选型与AI模型选择
工具链方面,我用的是开源的Verilator做仿真,Yosys做综合,GTKWave看波形。AI模型方面,我试过几个主流的大语言模型,最后固定用两个:一个擅长代码生成,一个擅长代码审查。代码生成的模型用来出RTL初稿,代码审查的模型用来找时序问题和协议违规。两个模型交叉验证,比单用一个模型靠谱。
这里有个细节:不要用同一个模型既生成又审查。我试过,它对自己的错误有“盲区”,审查时经常放过自己生成的bug。换一个模型来审,它能挑出不少问题。另外,AI生成的RTL一定要过Lint工具。我用的Verilator自带Lint,能抓出位宽不匹配、未驱动信号、组合环路这些问题。Lint过了再进仿真,能省很多调试时间。
3. 核心细节解析:AXI接口与仲裁逻辑的AI生成要点
3.1 AXI4接口的握手时序怎么让AI理解
AXI4的握手时序是AI生成RTL时最容易出错的地方。VALID和READY的依赖关系、BVALID和BREADY的握手、RLAST和RVALID的配合,这些如果只靠自然语言描述,AI很容易搞混。我的做法是在结构化Spec里专门写一段“时序规则”,用伪代码的形式描述握手行为。
比如对于写地址通道,我会写:“AWVALID由主机置高,直到AWREADY为高后的下一个时钟沿拉低。AWREADY由从机置高,表示可以接收地址。AWVALID和AWREADY同时为高的时钟沿,地址被采样。”这段伪代码发给AI后,它生成的RTL里AWVALID和AWREADY的握手逻辑基本正确。但BVALID的生成逻辑它经常出错——BVALID应该在写数据接收完成后置高,而不是在写地址接收完成后。这个细节我在Spec里专门加了一条注释:“BVALID的置高条件是WLAST和WVALID同时为高且WREADY为高,与AW通道无关。”加了这条之后,AI生成的BVALID逻辑就对了。
3.2 仲裁器的优先级反转问题
仲裁器是另一个容易出问题的地方。我设计的仲裁器支持四个主设备,优先级可配置。AI生成的初版仲裁器用的是固定优先级,高优先级设备一直占用总线,低优先级设备饿死。我在Spec里写的是“轮询仲裁”,但AI理解成了“固定优先级”。后来我在Spec里加了一段状态转移描述,明确写了“每次仲裁完成后,优先级指针移向下一个设备”,AI才生成正确的轮询逻辑。
这里有个经验:对于仲裁器、状态机这类有明确状态转移的逻辑,最好在Spec里画出状态转移表,用表格形式列出当前状态、输入条件、下一状态、输出信号。AI对表格的理解能力比纯文字强得多。我试过用文字描述状态机,AI生成了五个状态,但转移条件错了三个;换成表格后,一次通过。
3.3 寄存器组的自动生成与地址映射
寄存器组是IP里最枯燥的部分,但也是AI最擅长的部分。只要Spec里的寄存器表足够清晰,AI生成的寄存器读写逻辑基本不会出错。我的做法是:在YAML里定义每个寄存器的偏移地址、复位值、字段位宽和读写属性,然后让AI生成一个寄存器文件模块,包含地址译码、读写使能、字段拼接。
这里要注意的是地址对齐。AXI4的地址是字节地址,但寄存器通常是32位对齐的。AI有时候会把地址译码写成按字地址译码,导致偏移量错位。我在Spec里明确写了“地址位[31:2]用于寄存器选择,位[1:0]忽略”,AI生成的译码逻辑就正确了。另外,对于只读寄存器,AI有时候会生成写逻辑,虽然综合时会优化掉,但Lint会报warning。我在Spec里标注了每个寄存器的读写属性,AI生成的代码就干净了。
4. 实操过程:从YAML到可综合RTL的完整步骤
4.1 第一步:手工提取Spec关键信息到YAML
这一步不能省。我试过让AI直接从PDF提取,结果它把两个寄存器的地址搞混了,还把一个保留字段当成了有效字段。手工提取虽然花时间,但这是整个流程里唯一需要人工深度参与的部分,大概占整个项目时间的20%。提取的时候,我习惯用双屏,左边开PDF,右边开YAML编辑器,逐条对照。
YAML的结构我固定为几个顶层字段:module、interfaces、registers、fsm、timing。interfaces下面每个接口有name、protocol、data_width、addr_width、id_width。registers下面每个寄存器有name、offset、reset_value、fields,fields下面每个字段有name、bits、access、description。fsm下面有states和transitions。timing下面有clock_freq、cdc_handling、handshake_rules。
4.2 第二步:用AI生成RTL骨架
把YAML和一段简短的模块功能描述拼成一个prompt,发给代码生成模型。Prompt的模板我固定为:“你是一个资深RTL工程师,请根据以下结构化规格生成可综合的Verilog RTL。要求:使用同步复位,复位信号低有效;所有寄存器输出;AXI接口遵循AXI4协议;仲裁器使用轮询策略。规格如下:[YAML内容]。”
生成的时候,我习惯让AI分模块生成,而不是一次性生成整个IP。先让它生成AXI接口模块,再生成寄存器文件,再生成仲裁器,最后生成顶层连线。分模块生成的好处是,每个模块可以单独Lint和仿真,出了问题容易定位。一次性生成整个IP,出了问题你得在几千行代码里找,效率反而低。
4.3 第三步:Lint与仿真验证
AI生成的RTL先过Verilator Lint。我用的命令是:
verilator --lint-only -Wall -Wno-DECLFILENAME top.v axi_if.v reg_file.v arbiter.vLint过了之后,写一个简单的Testbench跑仿真。Testbench不用手写,让AI根据Spec生成。我通常会让AI生成一个带随机激励的Testbench,覆盖寄存器读写、AXI突发传输、仲裁切换这几个场景。仿真跑起来后,用GTKWave看波形,重点看握手信号和状态转移。
这里有个技巧:让AI生成Testbench时,要求它加入断言(assertion)。比如“AWVALID拉高后,AWREADY必须在10个周期内置高”、“BVALID拉高后,BREADY必须在5个周期内置高”。这些断言能在仿真时自动抓出协议违规,比人工看波形快得多。
4.4 第四步:综合与时序检查
仿真通过后,用Yosys做综合,看资源占用和时序报告。我用的命令是:
yosys -p "read_verilog top.v axi_if.v reg_file.v arbiter.v; synth; stat"综合报告里重点看两个东西:一是LUT和寄存器的数量,二是关键路径的延迟。如果关键路径延迟超过时钟周期的80%,就得考虑优化。AI生成的RTL有时候会有冗余逻辑,比如重复的地址译码、不必要的多路选择器。这些可以在综合后手动优化,也可以让AI重新生成一版,在Prompt里加上“优化关键路径”的要求。
5. 常见问题与排查技巧实录
5.1 AI生成的RTL仿真不通过怎么办
先看Lint有没有过。Lint没过,先修Lint。Lint过了仿真不过,大概率是握手时序或状态转移的问题。我的排查顺序是:先看复位是否正常释放,再看时钟是否正常翻转,再看状态机是否进入预期状态,最后看输出信号是否符合预期。AI生成的RTL有时候会忘记复位状态机的状态寄存器,导致仿真时状态机停在未知状态。这个在Lint里不一定能抓出来,但仿真波形一看就知道。
5.2 AXI突发传输数据错位怎么定位
AXI突发传输的数据错位,通常是地址对齐或数据计数的问题。我遇到过AI生成的RTL里,突发长度计数器在WLAST时没有正确清零,导致下一笔传输的数据错位。排查方法是:在Testbench里发一笔4拍的写突发,看波形里WSTRB和WDATA的对应关系。如果第一拍数据对了,第二拍开始错,那就是计数器的问题。让AI重新生成时,在Spec里明确写“突发长度计数器在WLAST和WVALID同时为高且WREADY为高时清零”。
5.3 仲裁器优先级不生效的排查
仲裁器优先级不生效,通常是优先级编码逻辑的问题。我遇到过AI生成的仲裁器里,优先级掩码没有正确更新,导致高优先级设备释放总线后,低优先级设备仍然拿不到授权。排查方法是:在Testbench里让两个设备同时请求,看授权信号给谁。如果总是给同一个设备,那就是轮询逻辑没生效。让AI重新生成时,在Spec里用表格形式写出状态转移,明确每个状态下哪个设备获得授权。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 修正方式 |
|---|---|---|---|
| 仿真时状态机停在未知状态 | 状态寄存器未复位 | 看波形里状态信号 | 在Spec里明确复位值 |
| AXI写数据错位 | 突发计数器未清零 | 看WLAST和WVALID波形 | 在Spec里写明清零条件 |
| 仲裁器优先级不生效 | 优先级掩码未更新 | 看授权信号波形 | 用表格描述状态转移 |
| 寄存器读写地址错位 | 地址译码位宽错误 | 看地址译码逻辑 | 明确地址位[31:2]用于译码 |
| 综合后关键路径过长 | 冗余逻辑未优化 | 看综合时序报告 | 让AI重新生成并优化 |
6. 我在这套流程里踩过的坑和总结的经验
第一个坑是过度依赖AI。我试过让AI一次性生成整个IP的RTL,结果生成了两千多行代码,Lint报了三十多个warning,仿真跑了三天没跑通。后来改成模块化生成,每个模块单独验证,效率反而高。第二个坑是Spec结构化做得不够细。我一开始只写了寄存器地址和位宽,没写复位值和读写属性,AI生成的寄存器文件里有一半的寄存器没有复位值,仿真时全是X。后来把复位值和读写属性补上,问题就解决了。
第三个坑是忽略了跨时钟域处理。我的IP里有一个异步FIFO,AI生成的RTL里没有做同步处理,仿真时数据丢失。后来在Spec里明确写了“跨时钟域信号需要两级同步器”,AI才生成正确的同步逻辑。第四个坑是Testbench的覆盖率不够。AI生成的Testbench只覆盖了正常读写场景,没有覆盖错误注入和边界条件。后来我让AI生成Testbench时加上“随机注入错误响应”和“边界地址访问”的要求,覆盖率才上去。
这套流程跑下来,我的体会是:AI能帮你省掉重复劳动,但不能帮你做架构决策。Spec结构化、模块划分、时序约束这些关键决策,还是得人来定。AI的价值在于,你定好框架后,它能快速填充细节,让你把精力集中在真正需要思考的地方。我现在做一个中等规模的AXI IP,从Spec到可综合RTL,大概两周左右,其中AI生成占三天,人工修正和验证占一周,剩下四天是综合优化和文档。比传统流程快了一倍多,但前提是Spec结构化做得足够好。如果你也想试这套流程,我的建议是:先从一个小模块开始,比如一个带AXI-Lite接口的寄存器组,跑通整个流程后再扩展到复杂模块。别一上来就搞大IP,容易翻车。