摘要
AI 编码助手已经能帮我们写 RTL、补 testbench、查 bug、整理文档,但在 FPGA 开发里,真正危险的不是“AI 不会写代码”,而是它写得太快、改得太多、想得太少。
软件代码改错了,大不了跑单测、回滚提交;FPGA 代码改错了,可能仿真是绿的,上板是红的,时序是炸的,资源是超的,接口协议还悄悄变味了。
所以,面向 AI 的 FPGA 开发不能只靠一句“帮我实现这个模块”。更合理的方式是:先让 AI 读懂项目,再让它写计划,然后由工程师审计划,最后才让它动代码。
本文结合 Codex 的使用方式,总结一套适合 FPGA 项目的 AI 辅助开发流程,并通过一个典型场景说明:如何让 AI 成为可靠的工程助手,而不是一个“热情但莽撞的实习生”。
一、为什么 FPGA 开发更需要“管住 AI”
很多人第一次用 AI 写代码时,体验都很相似:
“帮我写一个 AXI-Stream 模块。”
几秒钟后,代码出来了。
看起来挺像回事,端口也有,状态机也有,注释也有,甚至 testbench 都能跑。
但 FPGA 项目不是孤立代码片段的拼图游戏。一个 RTL 模块能不能用,至少要同时满足下面这些条件:
接口协议要对;
时钟复位要对;
跨时钟域要对;
时序约束要对;
资源消耗要可控;
仿真行为和上板行为要一致;
代码风格要和项目一致;
后续维护人员要能看懂。
AI 最擅长的是“根据上下文生成内容”,但它不天然知道你的项目边界、工程取舍和历史包袱。
它可能会为了实现功能,引入一个你根本不想要的 FIFO;
它可能会为了让仿真通过,偷偷改接口时序;
它也可能会重新实现一个项目里已经存在的通用模块。
这就是 AI 辅助 FPGA 开发的核心矛盾:
AI 很会执行,但工程判断必须留在人手里。
因此,我们要做的不是让 AI 自由发挥,而是给它一条清晰轨道。
二、核心原则:先审计划,再写代码
这套流程的核心只有一句话:
在你审查并批准计划之前,不要让 AI 写一行 RTL。
这句话听起来有点“保守”,但在 FPGA 开发里非常必要。
因为 FPGA 的很多问题不是语法错误,而是工程错误。比如:
valid/ready 握手语义理解错;
异步信号没有做同步;
reset 极性和项目规范不一致;
多周期路径被误当成普通路径;
状态机多加了一个“看起来很安全”的等待状态;
本来应该用 BRAM,结果被综合成大量 LUTRAM;
本来项目只允许 Verilog-2001,AI 却写了 SystemVerilog 语法。
这些问题,单靠“生成后再改”会非常痛苦。
更好的做法是把 AI 的工作拆成两个阶段:
思考阶段:阅读代码、理解系统、写研究报告和实施计划;
执行阶段:严格按照已经审过的计划修改代码。
一句话概括:
让 AI 先把脑子里的方案写出来,工程师确认没跑偏,再让它动手。
三、Step 0:用 AGENTS.md 给项目立规矩
进入一个新仓库后,第一件事不是提需求,而是先让 AI 理解这个项目的“家规”。
可以执行:
codex /init通常会在项目根目录生成一个AGENTS.md文件。这个文件可以理解为:
给 AI 编码助手看的项目说明书。
README 是写给人看的,AGENTS.md 是写给 AI 看的。
在 FPGA 项目里,建议把这些内容写进去:
# AGENTS.md ## 项目背景 本项目为 FPGA RTL 工程,主要语言为 Verilog。 除非明确说明,不允许使用 SystemVerilog 语法。 ## 代码规范 - 时序逻辑使用非阻塞赋值。 - 组合逻辑使用阻塞赋值。 - 所有寄存器必须有明确复位策略。 - 模块端口命名遵循现有工程风格。 - 不允许随意修改已有公共模块接口。 ## 时钟与复位 - 不允许在 RTL 中硬编码时钟频率假设。 - 时钟约束统一由 XDC 管理。 - 跨时钟域信号必须说明同步方式。 - 单 bit 控制信号使用两级同步。 - 多 bit 数据跨域优先使用异步 FIFO 或握手机制。 ## 验证要求 - 修改 RTL 后必须同步更新 testbench。 - 必须说明仿真激励覆盖了哪些边界场景。 - 若涉及 AXI / AXIS / APB 等协议,必须说明握手时序。 ## 实施要求 - 在修改代码前,必须先输出 research.md 和 plan.md。 - 未经确认,不允许直接改代码。 - 实施完成后,更新 task.md 的完成状态。AGENTS.md 的价值不是“让 AI 变聪明”,而是让 AI 少犯低级错误。
它就像项目的护栏:
护栏不能替你开车,但能减少冲出赛道的概率。
四、Step 1:深度研究,先让 AI 读懂代码
很多 AI 生成代码失败,不是因为模型能力不够,而是它根本没读懂工程。
所以第一个正式步骤应该是:让 AI 研究现有代码,并把理解写成文档。
可以使用类似提示词:
深入阅读当前目录下的 RTL 和 testbench 文件,理解模块功能、接口协议、时钟复位、数据流和已有约束。 完成后,将你的研究结果写入 research.md。 先不要修改任何代码。如果是某个子系统,可以更聚焦:
详细研究 axis_packet_parser 相关文件,理解输入输出协议、状态机行为、异常处理和 testbench 覆盖情况。 将结论写入 research.md,列出你发现的潜在问题和不确定点。 先不要实施。research.md最好包含这些内容:
# research.md ## 1. 模块功能概述 该模块负责…… ## 2. 输入输出接口 | 信号 | 方向 | 含义 | |---|---|---| | s_axis_tvalid | input | 输入数据有效 | | s_axis_tready | output | 模块接收准备 | | s_axis_tdata | input | 输入数据 | | s_axis_tlast | input | 帧结束标志 | ## 3. 时钟与复位 - 主时钟:clk - 复位:低有效同步复位 rst_n - 未发现跨时钟域逻辑 ## 4. 状态机分析 状态包括 IDLE、HEADER、PAYLOAD、DROP。 状态跳转条件如下…… ## 5. 已有测试覆盖 当前 testbench 覆盖了正常包、短包、连续包。 未覆盖 backpressure、异常 tlast、空包等场景。 ## 6. 潜在风险 - ready 拉低场景覆盖不足。 - tlast 异常时状态机可能无法回到 IDLE。 - 输出侧没有明确错误标志。这一步非常关键。
因为research.md是你和 AI 的第一个对齐界面。
你可以通过它判断:AI 到底是真读懂了,还是只是“看起来读懂了”。
五、Step 2:写 plan.md,把方案摊开给人审
研究完成后,第二步是让 AI 写实施计划,而不是马上动代码。
提示词可以这样写:
基于 research.md,针对“增加异常帧检测和错误标志输出”这个需求,编写详细 plan.md。 计划中必须包含: 1. 要修改的文件路径; 2. 接口变更; 3. 状态机变更; 4. testbench 变更; 5. 风险和权衡; 6. 关键代码片段示例。 先不要实施。一份合格的plan.md不应该只写“修改状态机,增加错误处理”这种空话,而应该能让工程师直接判断方案对不对。
例如:
# plan.md ## 1. 目标 为 axis_packet_parser 增加异常帧检测能力。 当输入帧长度小于最小长度,或 tlast 出现在非法位置时,输出 err_flag。 ## 2. 修改文件 - rtl/axis_packet_parser.v - tb/tb_axis_packet_parser.v - docs/interface.md ## 3. 接口变更 新增输出端口: ```verilog output reg err_flagerr_flag 在检测到异常帧时拉高一个时钟周期。
4. 状态机变更
在 PAYLOAD 状态下:
如果 payload_cnt 小于 MIN_LEN 且 s_axis_tlast 拉高,则置位 err_flag;
下一拍回到 IDLE;
不改变正常帧输出路径。
5. 验证计划
新增以下用例:
正常帧;
过短帧;
tlast 提前;
tready 拉低;
连续异常帧;
异常帧后恢复正常帧。
这一步的目的,是把 AI 的“隐式想法”变成“显式设计”。 只要设计还写在模型脑子里,风险就很大。 一旦写成文档,工程师就能审、能改、能追踪。 --- ## 六、Step 3:标注循环,把工程判断注入进去 这是整套流程里最有价值的一步。 AI 写完 `plan.md` 后,不要急着实施。 打开文档,像审设计文档一样逐条看。 看到不对的地方,直接在文档里写注释。 例如: ```markdown <!-- 这里不对:这个信号来自另一个时钟域,不能直接采样,必须先做两级同步。 -->或者:
<!-- 删除这一节。本版本不引入外部 DDR 缓存,所有数据只走片上 BRAM。 -->再或者:
<!-- valid/ready 是整条 AXIS 通道的握手语义,不是单个字段的有效标志。 请按整帧字段同时有效的语义重构这一节。 -->然后告诉 AI:
我已经在 plan.md 中添加了注释。 请处理所有注释,并相应更新 plan.md。 先不要实施。注意最后一句:
先不要实施。
这句话很重要。
否则 AI 可能一边改计划,一边顺手把代码也改了。
在 FPGA 项目里,这种“顺手”往往就是 bug 的开始。
这个循环可以重复 1 到 6 次。
不要嫌麻烦。
真正节省时间的地方不是少审几分钟计划,而是少花几天时间查一个上板才暴露的诡异问题。
七、Step 4:生成 task.md,让实施变成 checklist
当plan.md已经审得比较稳之后,建议再生成一个任务清单:
根据 plan.md 生成 task.md。 task.md 需要包含所有实施阶段和具体任务,可用于进度跟踪。 先不要实施。示例:
# task.md ## 阶段一:接口修改 - [ ] 在 axis_packet_parser.v 中新增 err_flag 输出端口 - [ ] 更新模块例化处的端口连接 - [ ] 更新 interface.md ## 阶段二:状态机修改 - [ ] 增加异常帧检测条件 - [ ] 保证 err_flag 只拉高一个周期 - [ ] 确认正常帧路径不受影响 ## 阶段三:验证修改 - [ ] 增加过短帧用例 - [ ] 增加 tlast 提前用例 - [ ] 增加 backpressure 用例 - [ ] 增加异常帧后恢复正常帧用例 ## 阶段四:回归检查 - [ ] 运行仿真 - [ ] 检查波形 - [ ] 检查是否使用了不允许的语法 - [ ] 总结修改结果task.md的作用很简单:
让 AI 不是“边想边改”,而是“照单执行”。
这对 FPGA 项目尤其重要。
因为我们希望 AI 在实施阶段少发挥,甚至不发挥。
创造性应该发生在计划阶段;
实施阶段越无聊,结果越可靠。
八、Step 5:实施,严格按计划执行
到这里,才进入真正写代码的阶段。
可以使用类似提示词:
实施 task.md 中的所有内容。 完成每个任务后,在 task.md 中标记为已完成。 严格按照 plan.md 执行,不要引入计划外功能。 不要添加不必要的注释。 不要使用 SystemVerilog 语法。 完成后运行相关仿真,并总结修改内容、验证结果和剩余风险。这里有几个关键点:
第一,严格按照 plan.md 执行。
不要让 AI 临场加戏。
第二,不要引入计划外功能。
AI 很容易“顺便优化一下”,但 FPGA 项目最怕这种没有审过的顺便。
第三,完成后必须总结剩余风险。
比如仿真通过,不代表时序通过;功能通过,不代表资源合理;单模块通过,不代表系统集成没问题。
实施完成后,建议让 AI 输出类似总结:
# implementation_summary.md ## 修改内容 - 修改 rtl/axis_packet_parser.v - 修改 tb/tb_axis_packet_parser.v - 更新 docs/interface.md ## 验证结果 - 正常帧通过 - 过短帧检测通过 - tlast 提前检测通过 - backpressure 场景通过 ## 未覆盖风险 - 尚未进行综合后资源检查 - 尚未进行时序分析 - 尚未进行板级联调这份总结不是形式主义。
它能帮助你快速判断:现在只是“代码写完了”,还是“工程闭环了”。
九、一个 FPGA 场景示例:AI 不是直接写模块,而是先写方案
假设我们要做一个简单需求:
在现有 AXI-Stream 数据通路中增加异常帧检测,发现短帧或非法 tlast 时输出错误标志。
错误用法通常是:
帮我改一下 axis_packet_parser,让它支持异常帧检测。这样问,AI 很可能直接开始写代码。
如果上下文不完整,它可能会:
改错状态机;
误解 valid/ready;
漏掉 backpressure;
忽略已有 testbench;
把错误标志做成电平保持;
甚至顺手改了模块接口风格。
更稳的方式是:
第一步:研究
深入研究 axis_packet_parser 相关 RTL 和 testbench。 理解 AXI-Stream 握手、状态机、异常处理和已有测试覆盖。 将结果写入 research.md。 先不要修改代码。第二步:计划
基于 research.md,设计“异常帧检测”功能的实现方案。 写入 plan.md,包含接口变更、状态机变更、验证用例和风险。 先不要实施。第三步:人工标注
在plan.md中写:
<!-- err_flag 只能拉高一个周期,不能保持到下一帧。 --> <!-- tready 拉低时不能更新 payload_cnt,否则 backpressure 场景会计数错误。 --> <!-- 不允许新增 FIFO,这个需求只允许修改状态机和计数逻辑。 -->第四步:更新计划
我已经在 plan.md 中添加了注释。 请处理所有注释并更新 plan.md。 先不要实施。第五步:生成任务并实施
根据 plan.md 生成 task.md,列出所有实施任务。 先不要实施。确认后:
实施 task.md 中的所有任务。 完成后更新 task.md,并运行仿真。这套流程的重点不在于提示词多华丽,而在于顺序正确。
先理解,再设计;先审查,再执行。
这就是 AI 参与 FPGA 开发时最朴素、也最有效的纪律。
十、Skills:把经验封装成可复用流程
如果说 AGENTS.md 是项目级规则,那么 Skills 更像是“某类任务的标准操作手册”。
比如:
如何写 FPGA 编码规范;
如何审查 RTL;
如何生成 testbench;
如何整理 Markdown 文档;
如何生成 Mermaid 流程图;
如何把 Markdown 转成 Word;
如何检查 AI 写作痕迹。
Skills 的本质不是魔法插件,而是一份结构化的 Markdown 指令。
它告诉 AI:遇到某类任务时,应该按什么流程做。
可以把它理解成:
把一个资深工程师的习惯动作,写成 AI 能重复执行的说明书。
例如,我们可以创建一个fpga-rtl-reviewSkill,要求 AI 每次审 RTL 时都检查:
# fpga-rtl-review ## 触发场景 当用户要求审查 FPGA RTL、Verilog 模块、testbench 或时序相关代码时使用。 ## 检查清单 1. 接口协议是否清晰; 2. valid/ready 握手是否正确; 3. 时钟复位是否规范; 4. 是否存在跨时钟域风险; 5. 是否存在锁存器推断; 6. 是否存在组合环路; 7. 状态机是否有默认分支; 8. testbench 是否覆盖边界条件; 9. 是否使用了项目禁止的语法; 10. 是否可能引入资源或时序问题。这样,下一次你让 AI 审代码时,就不用每次重新打一长串要求。
这对团队尤其有用。
个人经验不再只存在脑子里,而是沉淀成可复用的流程资产。
十一、文档也要 AI 友好:优先 Markdown
很多 FPGA 项目不缺代码,缺的是能长期维护的文档。
推荐内部技术文档优先使用 Markdown,原因很简单:
纯文本,方便 Git 管理;
diff 清晰,方便代码审查;
AI 容易读取和修改;
格式负担小,工程师能专注内容;
后续可以再转换为 Word、PDF 或网页。
建议流程:
先用 Markdown 写清楚内容;
让 AI 帮你校对逻辑和表达;
再用工具导出为 Word 或 PDF;
最后人工调整格式。
不要一开始就纠结字体、页边距和目录样式。
文档最重要的是先把事情讲明白。
对 AI 来说也是一样:
结构清晰的 Markdown,比一份格式复杂的 Word 更容易处理。
十二、常见问题
1. AI 写的 RTL 怎么维护?
关键不是“谁写的”,而是有没有留下可审查的工程痕迹。
至少保留:
research.mdplan.mdtask.mdimplementation_summary.md仿真用例
代码提交记录
只要这些材料完整,半年后别人接手时,不需要猜当时为什么这么写。
2. 怎么避免 AI 改一处炸一片?
核心做法是限制改动范围。
在计划阶段要求 AI 明确:
修改哪些文件;
不修改哪些文件;
哪些接口不能动;
哪些公共模块不能改;
哪些行为必须保持兼容。
不要让 AI 自由搜索全仓库后随意“优化”。
3. 换人接手怎么不断档?
靠文档,不靠口口相传。
AGENTS.md 记录项目规范;
research.md 记录理解过程;
plan.md 记录设计决策;
task.md 记录实施步骤;
summary 记录验证结果和遗留风险。
这些都是工程资产。
4. 如何确保规范真的被执行?
把规范写进 AGENTS.md,再把检查动作写进任务流程。
例如:
实施完成后,检查是否违反 AGENTS.md 中的 Verilog 编码规范。 重点检查: 1. 是否使用 SystemVerilog 语法; 2. 是否存在跨时钟域直采; 3. 是否存在未复位寄存器; 4. 是否存在锁存器推断; 5. 是否更新 testbench。规范不能只写在文档里,还要变成每次任务结束前的检查项。
十三、总结:AI 负责执行,工程师负责判断
AI 编码助手正在改变开发方式,但 FPGA 开发的底层逻辑没有变:
协议要严谨;
时序要收敛;
资源要可控;
验证要闭环;
文档要可继承。
所以,面向 AI 的 FPGA 开发流程,不是“让 AI 替你开发”,而是“让 AI 在你的工程框架内高效执行”。
推荐工作流可以压缩成一句话:
深度阅读,写研究;详细规划,反复标注;确认无误,再让 AI 按任务清单执行。
更短一点:
人定方向,AI 干活;先审计划,再写代码。
只要把这个边界守住,AI 就不再是一个到处乱改的黑盒,而会变成 FPGA 工程师手里一把真正好用的工具。
它不会替代工程判断。
但它可以把大量重复、繁琐、机械的工作接过去。
而我们要做的,是把方向盘握稳。