Quartus 信号被优化掉:SignalTap 找不到节点速查
更新时间:2026-10-23。FAQ 速查页,面向 Quartus 用户第一次用 SignalTap 在线抓波形的场景。症状分两类:一类是在 SignalTap 的 Node Finder 里输入信号名怎么也搜不到;另一类更隐蔽——节点能加进来,编译却报
node ... was synthesized away,或者抓回来的波形恒为 0。这类「信号消失」九成不是 Quartus 的 bug,而是综合器按规则合法地把它优化掉了,或者增量编译/物理综合阶段又优化了一轮。本文按真实报错原文出发,逐条给出排查路径;用 Vivado 的同学请转 Vivado 信号被优化速查,编译本身长时间不出的问题见 Quartus 编译卡死速查。
本文要回答的问题
- SignalTap Node Finder 搜不到信号,对应综合阶段哪些优化?
- 节点加进去了,编译为什么报
was synthesized away? preserve、noprune、keep三个属性差在哪,写在哪一级才对?- SignalTap 节点选型选 pre-synthesis 还是 post-fit,各自能看到什么?
- 增量编译和物理综合会不会把上一轮还能看到的信号再优化掉?
一、典型现象与一句话根因
以下文案按 Quartus 常见输出整理,工程名与信号名均已占位:
1. Node Finder 里完全搜不到该信号(SignalTap 配置阶段):
Warning: Node Finder could not find any nodes matching "u_sub:u_sub|debug_cnt*"2. 节点已加入 SignalTap,编译阶段报信号被综合掉:
Error (10232): Verilog error at stp_auto.stp(0): node "debug_cnt[3]" was synthesized away
Warning (12125): Ignoring SignalTap II node "debug_cnt" because it was optimized away3. 综合报告里信号被点名删除(Analysis & Synthesis Messages 里的线索):
Info (10041): Inferred latch for "debug_cnt[0]" at ... removed
Info (16010): Register "debug_cnt[0]" has constant value 0; it was removed一句话根因:Quartus 综合器的职责是删减一切「对最终输出无影响」的逻辑——信号没被任何下游使用会被连根裁掉、驱动条件恒真恒假会被常量折叠、层级边界被 Flatten 后原信号名不复存在;而 SignalTap 工作在综合或适配后的网表上,被删的信号在网表里根本不存在,Node Finder 自然搜不到。 排查总原则只有一句:先去编译报告的 Analysis & Synthesis Messages 里确认这条信号的命运,再决定是改 RTL 给它真实负载,还是用属性强制保留。
二、排查决策图
读图方式:红色是入口现象,蓝色是排查动作,绿色是判定通过,橙色是修复动作,紫色是防复发收尾。核心顺序是「先查消息定性,再动属性」——不看 Messages 就直接糊 preserve,大概率保留了一堆没用的逻辑还找不到病根。
这张图对应两类根因轴:一类信号真的没了(被裁、被折叠),只能靠属性或改 RTL 保留;另一类信号还在但选型不对(post-fit 视角下名字变了),换 Node Finder 的 filter 就能命中。注意「消息无删除记录」分支指向换节点选型而不是直接加属性——很多信号活得好好的,只是 post-fit 网表里的名字和 RTL 里长得不一样了。
三、病因处置表
| 报错现象 | 最可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| Node Finder 完全搜不到该信号 | 信号未被任何下游使用,被整体裁剪 | Messages 搜 removed 关键字 | 给真实负载,或加 preserve |
| 节点加进 SignalTap 但编译报 synthesized away | 综合后节点已不存在 | 错误消息指向的节点名 | 换 pre-synthesis 节点或加属性 |
| 搜得到但波形恒为 0/1 | 常量折叠:驱动逻辑恒为定值 | Messages 搜 constant value | 查复位/使能条件是否恒无效 |
| RTL 名搜不到,通配也搜不到 | 层级 Flatten 后改名 | Node Finder 换 post-fit filter 再搜 | 用扁平名,或调试期保留层级 |
preserve 加了还是被优化 | 属性写在 wire 上但优化点在寄存器 | 网表里看属性落在哪个原语 | 属性直接写在 reg 声明上 |
| 中间寄存器链只剩首尾两级 | 寄存器合并/重复删除优化 | Messages 搜 merging、duplicate | 中间级加 noprune,接受资源上涨 |
| 增量编译后上次能看到的信号没了 | 分区重编译又优化了一轮 | 对比前后两次编译报告 | 给分区加 preserve 或锁存网表 |
| 仿真有波形、网表没对应名 | 组合逻辑被综合进 LUT,无独立节点 | 网表搜驱动它的 LUT | 路径上加寄存器再观测 |
四、三个最高频根因的修法
根因一:信号对输出无贡献,被合法裁剪。 最高频也最冤枉——RTL 里声明了一个只用于观察的计数器或状态寄存器,输出没连到任何端口、RAM 地址或后续逻辑,综合器按无用逻辑规则连根拔掉,Messages 里只有一行不起眼的 Info。两种修法按目的选:如果信号本该有功能(比如它本该去控制什么但你忘了连),那是 RTL bug,回去把逻辑补上;如果它就是纯观测信号,用属性强制保留:
(* preserve *) reg [15:0] debug_cnt;
(* noprune *) wire [7:0] debug_bus;preserve 保寄存器不被删除或合并,noprune 保组合逻辑节点不被裁掉,两者都只管「别删」,不管调试可见性。
增量编译的坑:开了 Incremental Compilation 或 Rapid Recompile 的工程,分区重编译时会按新边界再优化一轮,上一轮还能看到、能抓的信号,这一轮换了个优化上下文可能就消失了。表现为「昨天还能抓,今天重新编译就报 synthesized away」。处置是给观测信号所在分区加 preserve,或者调试期间把该分区网表锁存(Design Partition 设为 Post-Fit preserved),防止它反复变脸。
根因二:常量折叠,信号活着但永远是定值。 症状是节点能挂、波形能抓,但抓回来一根直线。典型剧本是使能条件写错导致某段逻辑恒不执行,综合器向前推导出寄存器恒为复位值,把整个驱动链折叠成常量,Messages 里留一条 has constant value。这时加保留属性没有意义——保留一个恒为 0 的信号对调试毫无帮助,真正要修的是让使能/复位逻辑动起来。
根因三:节点选型与层级打平,名字对不上。 SignalTap 的 Node Finder 有两种主要 filter:pre-synthesis 在综合前网表上找,名字和 RTL 一致,但节点可能在后续适配阶段被再优化;post-fit 在适配后网表上找,保证节点真实存在,但名字是扁平化后的形式。默认混合搜不到时,先把 filter 切到 SignalTap II: post-fitting 用通配探名,命中后再决定挂哪个视图。要临时保留层级名字,在 Assignment Editor 里给子模块加 Preserve Hierarchical Boundary(或 RTL 加 keep_hierarchy),代价是跨模块优化变弱、时序可能变差,正式出 SOF 前改回来。属性写进 QSF 的等价形式:
set_instance_assignment -name PRESERVE_REGISTER ON -to "u_sub|debug_cnt*"
set_instance_assignment -name KEEP_HIERARCHY ON -to "u_sub:u_sub"三个属性一句话说清:preserve 保寄存器、noprune 保组合节点、keep_hierarchy 保层级边界;日常观测用前两个足够,第三个只在名字对不上时临时用。
五、修复后验证与防复发
修完不要只看「这次搜到了」。先在 Node Finder 里确认节点真实命中,再完整重跑全编译(不要用增量结果掩盖问题),最后上板抓一次真实波形闭环。防复发三件套:关键调试信号的 preserve 属性直接写在 RTL 源码里随代码入库,不要只在 QSF 里临时糊;团队约定观测信号统一 dbg_ 前缀,code review 一眼可辨;每次插完 SignalTap 在 Messages 里 grep 一遍 removed 与 constant value,把信号被优化这件事从「上板才发现」提前到「编译完就发现」。编译整体耗时与收敛问题另见 Quartus 编译卡死速查。
一句话总结
Quartus 信号被优化掉的本质是综合器合法删减与节点选型视角错位的叠加:未驱动负载的信号被整体裁剪、恒值逻辑被常量折叠、post-fit 网表让原名对不上;先查 Synthesis Messages 定性(removed/constant value/无记录三分支),再对号入座——纯观测信号用 (* preserve *)、(* noprune *) 保留、恒值信号回去修使能逻辑、名字对不上时换 post-fit filter 探名或临时保留层级,最后全编译上板抓波闭环,并把保留属性随 RTL 入库防复发。