FPGA中一个“被优化”的信号,让我白熬了两天——直到用了 AI工具
上周,一位做 FPGA 开发的朋友找到我们,语气非常着急。
他说:
工程已经生成了 bit,PS 侧通过 BRAM 接口往 PL 写数据,总线 Debug 抓到的波形完全正确。
但数据进入控制解析模块后,再传到目标 RTL 模块时,
ad9361_cfg_done这个信号始终拉不起来。我加了
(* dont_touch = "true" *)也没用。打开实现后的原理图,发现 CDC 原语的输入引脚直接接地。可 RTL 里明明连接得好好的。项目催得很紧,我已经熬了两天,实在没办法了。
他的工程规模不小,每次重新综合、实现、生成 bit,都要等待半个小时左右。
两天里,他反复修改代码、重新跑实现、烧录验证,前后试了好多轮。
真正消耗他的,不只是一个 Bug。
而是每一次错误判断背后,都要付出半小时甚至更久的等待成本。
一、最初的判断:地址译码被常量折叠了
用户首先怀疑:
ps_ctrl_addr[13]可能被综合工具优化成了常数,导致控制解析模块的地址使能始终无效,最终造成:
ad9361_cfg_done = 1无法被正常写入。
这个判断并不离谱。
在 FPGA 工程中,如果某一位地址信号没有真正参与有效逻辑,或者上游连接存在问题,综合工具确实可能进行常量传播和逻辑折叠。
一旦地址译码逻辑被折叠,相关控制信号就可能在实现后的网表中“消失”。
于是,他开始沿着这个方向排查:
- 检查顶层端口连接;
- 对比各级模块中的地址位宽;
- 手动追踪实现后网表;
- 给关键信号添加
dont_touch; - 反复重新综合、实现和烧录。
但问题始终没有解决。
大型工程中的实现后网表包含成千上万个 cell 和 net。只靠 Vivado 原理图手动追踪,很容易在层层重命名和优化后的逻辑中迷失方向。
更关键的是:
他的排查方向从一开始就错了。
问题根本不在地址译码。
二、RepoKey 介入:先确认“信号到底还在不在”
我们先让他使用 AI + RepoKey 的工程上下文摘要能力,对项目进行快速扫描。
RepoKey 很快提取出与当前问题最相关的几个文件:
top.v system.bd control_decode_top.v cat_input_lvds_pll.v随后,RepoKey attach 到他已经打开的 Vivado 实现任务:
impl_1并直接查询:
ps_ctrl_addr[13]在实现后网表中的真实状态。
结果很快出来了:
ps_ctrl_addr[13]并没有被综合优化。
它仍然真实存在,并且:
- 驱动了读数据 MUX 中的多个 LUT;
- 进入了控制解析模块;
- 在实现后网表中保留了完整的功能连接。
之所以用户在原理图中难以辨认,是因为 Vivado 对部分 pin 和 net 进行了重命名。
名字变了,但连接没有断。
这一步直接排除了用户两天以来最怀疑的问题:
地址译码逻辑没有被常量折叠,ps_ctrl_addr[13]也没有消失。
既然前半段链路正常,问题就只能继续向后查。
三、沿着 CDC 原语,真正的问题浮出水面
接下来,AI 引导用户把注意力从地址译码转移到:
ad9361_cfg_done_cdc_inst这个xpm_cdc_array_single原语上。
RepoKey 查询该原语的实现后网表连接,得到了一组非常关键的信息:
src_clk net = (empty) src_in[0] net = (empty) syncstages_ff_reg[0][0]/D -> ZERO这意味着:
- CDC 的源时钟没有连接到有效 net;
- CDC 的源数据也没有形成有效输入;
- 第一级同步寄存器的 D 端被直接固定为
ZERO。
此时,问题已经非常清楚了。
我们立刻回到top.v,检查该 CDC 原语的例化代码:
xpm_cdc_array_single #( .DEST_SYNC_FF (2), .INIT_SYNC_FF (0), .SIM_ASSERT_CHK (0), .SRC_INPUT_REG (1), .WIDTH (1) ) ad9361_cfg_done_cdc_inst ( .dest_out (ad9361_cfg_done_cdc), .dest_clk (sdr_clk), .src_clk (ctrl_clk), // 问题在这里 .src_in (ad9361_cfg_done) );问题就出在这一行:
.src_clk (ctrl_clk)继续追踪后发现,ctrl_clk在顶层文件中早已被注释掉。
但由于 Verilog 默认允许隐式 wire,ctrl_clk并没有立即触发明显的编译错误,而是变成了一个未驱动的隐式信号。
综合时,这个未驱动信号被视为常数0。
于是,整个问题链条变成了:
ctrl_clk 未驱动 ↓ CDC 源时钟固定为 0 ↓ CDC 内部寄存器无法采样 ↓ ad9361_cfg_done_cdc 固定输出 0 ↓ 用户在实现后原理图中看到 CDC 输入接地所以,用户看到的“CDC 输入接地”,并不是因为:
ad9361_cfg_done被综合工具优化掉了。
真正原因是:
CDC 原语根本没有拿到有效的源时钟。
这也解释了为什么添加:
(* dont_touch = "true" *)完全无效。
dont_touch可以阻止部分逻辑被优化或重构,但它无法挽救一个根本没有驱动源的时钟信号。
四、修复:只改一行,问题解决
修复方式非常简单。
把错误的:
.src_clk (ctrl_clk)改为工程中真实存在的控制时钟:
.src_clk (ps_ctrl_clk)同时,我们还发现控制解析模块存在一个地址位宽不一致的问题。
原来的连接是:
.ctrl_addr (ps_ctrl_addr[12:0])但模块端口实际定义为:
[13:0]因此一并修改为:
.ctrl_addr (ps_ctrl_addr[13:0])完整修复后,重新执行:
综合 → 实现 → 生成 bit → 烧录验证一次通过。
实现后再次检查 CDC 原语:
src_clk已连接到有效时钟;src_in不再为空;- 同步寄存器输入不再固定为
ZERO; ad9361_cfg_done可以正常拉高。
从 RepoKey attach 工程,到定位真正根因,整个过程不到 15 分钟。
实际用于工程扫描和关键信号定位的 Token 只有几千,远低于用户最初担心的“让 AI 读取大型工程会消耗天文数字 Token”。
五、为什么一个不复杂的问题,会让人白熬两天?
复盘整个过程,真正拖慢排查的不是问题本身,而是以下四点。
1. 一开始就选错了排查方向
用户看到地址使能异常,下意识怀疑地址位被优化。
这个假设虽然合理,但没有先通过实现后网表确认,就直接进入了反复修改和跑 bit 的循环。
对于一个跑一次 bit 需要半小时的工程,错误方向的每一次尝试都非常昂贵。
2. 大型工程中,Vivado 原理图很难手动导航
实现后的:
- cell 名称;
- pin 名称;
- net 名称;
- 层级结构;
都可能经过重命名、折叠或重构。
当工程规模足够大时,手动追踪一个信号,很容易变成“大海捞针”。
3. 真正的问题藏在思维盲区里
谁会第一时间想到:
一个 CDC 原语的源时钟,竟然接到了一个已经被注释掉的信号?
而且这个信号因为隐式 wire 的存在,没有立刻以明显错误的形式暴露出来。
仿真阶段,它还可能被初始化值、测试激励或模型行为暂时掩盖。
直到实现后查看网表,才发现 CDC 内部已经被固定为常数输出。
4. 用户担心 AI 太耗 Token,反而付出了更高成本
用户一直担心:
让 AI 读取这么大的 Vivado 工程,会不会消耗非常多 Token?
所以他想先靠自己解决。
结果,节省下来的可能只是几块钱的 Token 成本,却付出了:
- 两天时间;
- 多次无效实现;
- 数小时等待;
- 高强度熬夜;
- 项目延期风险。
RepoKey 并不是把整个工程、所有源码、所有报告全部塞给 AI。
它会先提取工程结构、关键上下文和网表证据,再把真正需要分析的信息交给 AI。
这也是为什么,大型工程依然可以用较低的 Token 成本完成定位。
写在最后
问题解决后,这位用户感慨了一句:
早知道一开始就用工具,两天时间够我做多少事了。
对于 FPGA 开发者来说,真正稀缺的从来不只是算力,也不是 Token。
而是时间。
一个需要半小时才能跑完 bit 的工程,每一次错误方向的尝试,都在持续消耗开发周期。
RepoKey 的设计初衷,就是让 AI 不再靠“盲读整个工程”来猜问题,而是通过工程上下文、Vivado 状态和实现后网表证据,快速看清项目的真实状态。
RepoKey 现在免费开放
目前已在 Vivado 2017.4~2023.2 的多个版本中完成验证。
你可以使用 RepoKey:
- 一键扫描 Vivado 工程结构,快速定位关键文件;
- Attach 到已经打开的综合或实现任务;
- 查询任意信号、cell、pin 和 net 的真实网表连接;
- 通过自然语言向 AI 提问;
- 用更少的 Token,快速定位大型 FPGA 工程问题。
别再让你的两天,消耗在错误方向上。
📎上RepoKey 官网看详情
本文由 RepoKey 团队原创,转载请注明出处。
