摘要
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_flag
err_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-review Skill,要求 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.md -
plan.md -
task.md -
implementation_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 工程师手里一把真正好用的工具。
它不会替代工程判断。
但它可以把大量重复、繁琐、机械的工作接过去。
而我们要做的,是把方向盘握稳。