nextpnr 信号被优化看不到:调试信号消失速查
更新时间:2026-10-25。FAQ 速查页,属于 FPGA 开源工具链排错系列。想看布线后某个内部信号落在哪个单元、时序是多少,翻遍 nextpnr 的时序报告和网表就是找不到这个名字——这不是路径写错了,是信号在 yosys 综合阶段就被优化掉了。综合器的职责是等价化简:凡是不影响输出端口的行为都可以删,你「想看的信号」恰恰是最容易被删的那类。相邻问题见 yosys 信号被优化 与 Quartus 信号被优化。
本文要回答的问题
- 布线后报告里找不到信号,是被优化了还是改名了?
- yosys 综合时哪些信号必然被删?有没有规律?
(* keep *)和(* dont_touch *)写在哪、有什么区别?- 保留调试信号会不会改变时序、怎么把影响降到最小?
一、报错原文与现象
Warning: net 'debug_cnt' has no drivers, removing
Warning: removing unused signal 'state_dbg' from module 'top'
Info: flattening design hierarchy
ERROR: Signal 'u_cnt.value' not found in design典型场景:RTL 里专门留了一串 debug 信号打算上板后接逻辑分析仪或看时序,综合布线全绿,打开报告一搜——名字不存在。第四条 ERROR 通常出现在约束或脚本按层级路径引用信号时:综合把层级打散了,u_cnt.value 这个路径整体失效。
二、可能原因总览
| 报错/现象 | 可能原因 | 处置 |
|---|---|---|
| removing unused signal | 信号无负载或未驱动输出 | 加 keep 属性或确认可删 |
| net has no drivers | 信号只被读、从未被赋值 | 检查驱动端;调试需要则 keep |
| 报告里搜不到名字 | 综合改名/层级打散 | 用通配或从 JSON 网表查实际名 |
| 常量折叠后信号消失 | 信号被证明是常量 | 改 RTL 或接受优化结果 |
| 逻辑被合并进 LUT | 信号是中间组合逻辑节点 | keep 强制保留观测点 |
| 脚本按层级路径引用失败 | flatten 后路径失效 | 引用顶层端口名或加 dont_touch |
| 时序报告看不到内部路径 | 内部路径被优化合并 | 在观测点两侧各插 keep 信号 |
三、逐条排查展开
先分清「被删了」还是「改名了」。 两个不同的坑:被优化掉的信号在 JSON 网表里彻底不存在;被打散层级或改名的信号还在,只是名字变了(比如 u_cnt.value 变成 u_cnt_value 或带随机后缀)。查法:综合后直接查 JSON 网表:
grep -o '"[a-zA-Z0-9_.]*cnt[a-zA-Z0-9_.]*"' build/top.json | sort -u按名字片段搜一遍,搜得到是改名,搜不到才是真被删。这个判断决定后面是「改引用名」还是「加属性保留」。
yosys 删信号的规律:不影响输出就删。 综合器从顶层输出端口往回追溯,凡是不在输出锥(cone of logic)里的逻辑全部可以删。所以「为了调试声明但没接到任何输出/触发器」的信号必删无疑;被证明是常量的信号会被常量折叠;纯中间组合逻辑节点会被合并进 LUT,名字自然消失。理解了这条规律就明白:想让信号活下来,要么让它影响输出,要么显式告诉综合器别动它。
keep 与 dont_touch 的正确用法。 两个属性作用不同:
(* keep = 1 *) wire [7:0] debug_cnt; // 保留这条线网本身
(* dont_touch = 1 *) reg [3:0] state_dbg; // 保留这个单元/信号不被优化合并keep 保住线网不被删、不被合并进 LUT,是最常用的调试观测点手段;dont_touch 更进一步,连等价变换都不许做,适合保住整个模块或层级边界。属性写在信号声明行的最前面,yosys 全版本都认。注意别图省事给整个设计铺满 keep——那等于关掉优化,面积和时序都会变差。
调试信号保留的正确姿势:最小观测点。 推荐做法:只给「非看不可」的少数信号加 keep,并且优先保留寄存器输出(时序语义稳定、保留代价低),而不是保留一长串中间组合逻辑。如果目标是上板观测,更好的路子是把 debug 信号接到空闲引脚或片上调试核,让它天然处于输出锥里——这样既不被删,又不依赖属性。
层级打散导致脚本失效。 yosys 的 flatten 会把子模块打平,层级路径全部失效。引用内部信号的约束/脚本两个修法:综合时不打平(去掉 flatten,或用 dont_touch 保住模块边界),或把引用改为打平后的实际名字(从 JSON 网表抄)。和 nextpnr 引用无法解析 里的黑盒问题一样,本质都是「名字与实际网表对不上」,查法互通。
保留调试点对时序的影响。 加 keep 会阻止该节点参与逻辑合并,可能让一条本来融进一个 LUT 的路径变成两级——多数调试场景下这点代价可以接受,但关键路径上慎用。折衷:关键路径上的观测点接到寄存器输出端,不要插到组合逻辑链中间。
什么时候干脆不要保留。 还有一条反向思路值得知道:如果信号只是为了仿真阶段观察,根本不需要活到网表里——仿真器能看到所有内部信号,加 keep 是白付代价。只有「上板后要观测」或「约束/脚本要引用」的信号才真正需要保留。动手加属性之前先问自己这个信号是给仿真看的还是给硬件看的,能省掉一半无谓的 keep。
四、排查决策流程
这张图的关键分叉是「搜得到还是搜不到」:搜得到走「改名/层级打散」支线,改引用名即可;搜不到才是「被优化删除」,要回到 RTL 加属性或把信号接进输出锥。两条支线最后都汇入绿色节点「重综合验证」——别改完不验证就烧板。
五、预防与纪律
- 调试信号先想输出路径:打算观测的信号优先接空闲引脚或调试核,天然防删。
- keep 最小化:只给必要观测点加
keep,优先寄存器输出,关键路径上的组合逻辑中间点慎用。 - 模块边界要保就保整个:需要跨层级引用时给模块加
dont_touch,比事后追打平后的名字省事。 - 综合后核对观测点清单:脚本从 JSON 网表逐个确认调试信号存在,缺了立刻报,不等上板才发现。
- 记录与真实器件的差异:优化前后信号存活情况记入调试笔记,下次设计时直接按可观测性写 RTL。
这一篇在知识体系里的位置
本篇是 FPGA 开源工具链排错簇中「综合优化副作用」节点的 FAQ 页:与 yosys 信号被优化(综合阶段视角)、nextpnr 引用无法解析(名字对不上的另一类成因)、nextpnr 时钟/约束缺失(约束阶段的静默偏离)互补;调试观测的上板手段可参照 FPGA 调试,系列总览见 FPGA 知识版图。
一句话总结
nextpnr 布线后找不到信号,先 grep JSON 网表分清「改名了还是被删了」:改名/层级打散就改约束与脚本的引用名;真被删就回 RTL 给观测点加 (* keep = 1 *)、保模块边界用 (* dont_touch = 1 *),更稳妥的做法是把调试信号接进输出锥(空闲引脚或调试核)让它天然存活;最后把「综合后核对观测点清单」写进脚本,让信号消失这件事在烧板之前就被发现。