FPGA 原型验证专题 14:可观测性插桩——dont_touch、信号保留、probe 插入与可见性预算
更新时间:2026-09-12。本文是「ASIC FPGA 原型验证系统」专题 06~25 深体系列分册,对应阶段 2(约束标记/插桩)。观测硬件架构见既有 专家 45 片上 trace 系统,平台侧调试入口见 22 ILA/VIO 高级调试。
这一篇在链路里的位置
阶段 2 输出的 dont_touch 约束保护需要观测的信号,避免阶段 3/4 的优化把网线删并。插桩信息必须与 RTL ID/GateID 绑定,阶段 5 采到的数据才能在阶段 6 回映到 RTL 层次。观测点设计因此是"编译期决策、运行期受益"。
本文要回答的问题
- 综合为什么会"删掉/合并/重命名"信号?哪些优化(常量传播、死逻辑消除、跨层次打平)破坏可见性?
- dont_touch / keep / mark_debug 各管什么层级,放在哪一层(Compiler vs Vivado)最有效?
- probe 如何把深层层级信号引到观测点?扇出很大的 probe 会不会影响时序?
- 可见性预算:trace 存储深度/宽度、时钟、布线、引脚如何限制同时可见信号数?
- 静态全量插桩 vs 动态可切换 probe(多路复用、信号交换矩阵)如何取舍?
- 插桩如何与 ID 体系绑定,保证 Gate 域采集可回映 RTL?
- 插桩对面积/时序/频率的扰动(observer effect)如何控制与评估?
一、为什么默认「看不见」:综合会主动删信号
RTL 波形里的信号是按源码命名的网线,但从 RTL 到比特流经过两级优化(Compiler 逻辑综合 15 编译器逻辑综合 工艺无关网表/两级综合 + 厂商 P&R),大量网线在物理上不复存在:
- 常量传播:接常量的输入被折叠,整条逻辑被证明永不变;
- 死逻辑消除(DCE):输出无人使用的模块和网线被删——调试想看的内部状态,对「功能输出」而言常常正是「无用」的;
- 等价合并/重命名:两根恒等网线合并、表达式重构,原名消失;
- 层次打平:跨模块边界优化后,层次路径不再对应物理边界;
- 寄存器重定时(retiming):触发器在组合云前后移动,原寄存器不存在了。
结果是:等上板后再加探针,工具报「找不到该网线」。可见性必须在编译期提前买——这正是本篇在阶段 2 存在的原因。
二、保护机制:dont_touch / keep / mark_debug
不同层级的保护约束解决不同问题(Compiler 约束层与 Vivado XDC/属性层两级,约束体系见 18 原型约束体系 SDC/XDC/跨片时序/例外):
| 约束 | 作用对象 | 防什么 |
|---|---|---|
dont_touch(网表/实例级) | 模块实例、cell | 防整体被删、被打平、被跨边界优化;wrapper 替换边界也靠它 |
keep/syn_keep(网线级) | 具体 net | 防网线被 DCE 或合并 |
mark_debug(XDC/属性) | 网线 | 既保网又自动接入厂商调试网(ILA) |
| DONT_TOUCH(XDC) | cell/net/pin | 厂商实现阶段(优化/布局/布线)全程保护 |
传递原则:在最早的 Compiler 层打保护最可靠——如果信号在 Compiler 综合阶段就没了,Vivado 层再写 keep 也无对象可保。Compiler 把交付侧的观测清单(07 面向原型的 RTL 交付规范 层次/黑盒/电源域 里的 dbg_*/trace_* 命名与观测需求表)翻译成两级约束并透传。保护范围过宽会让优化大面积失效(面积/频率恶化),所以保护要按信号清单精确给,不能全设计 dont_touch。
三、probe 网络:把深层信号引到观测点
被保护的信号往往埋在层次深处、甚至别的 FPGA 上,观测硬件(ILA/trace RAM,45)只在固定位置,中间要插一张观测网络:
- probe 点:源信号经一级观测寄存器(时钟域对齐,防亚稳与长组合扇出)接入;
- 汇聚网络:大量 probe 点经 probe mux 分级汇聚到 trace 采集引擎;
- 跨片观测:别的 FPGA 上的信号作为一类 TDM 时隙流量经互联链路回传(13 跨 FPGA 互联与 TDM SerDes/跨片同步/降频 §七),在采集片解出;
- 触发/限定逻辑:比较器(=、≠、范围)、事件计数器与 trigger sequencer 决定「什么时候采、采哪段窗」,尽量只存有效窗口以省存储;
- 导出通路:trace RAM → JTAG/PCIe/以太网导出到上位机(01 原型验证 Debug IP 寄存器是怎么被访问的)。
四、可见性预算:看得见多少是一道资源题
可同时观测的信号数不是无限的,受四个物理量约束,必须像容量一样做预算并在分区时预留:
trace 带宽预算 ≈ 观测信号总位宽 × 采样率
trace 存储深度 ≈ trace RAM 容量 / 每拍存储宽度
导出带宽 = JTAG/PCIe 回读速率(决定窗口搬空时间)
布线/时序 = probe 汇聚网络本身占 LUT/布线,扇出过大拖垮时序典型取舍:宽而浅(一次抓几千个信号、几百拍,适合看连接关系与状态机)vs 窄而深(几十个信号、几十万拍,适合看协议序列与长间隔事件)。预算不匹配时三选一:减信号、降采样(1/N 拍采一次)、加 trace RAM/导出通道——这些都要在编译期定,上板后不能凭空加。跨片 probe 还要占互联 TDM 预算(12 多 FPGA 分区算法 割集/容量/图划分 的余量预留正是为此)。
五、静态插桩 vs 可配置观测
两种模式常组合使用:
- 静态插桩:观测点、mux 连接在编译期固定,硬件最省、时序最好,但换一个信号要重综合 P&R(小时~天级);
- 可配置 probe(probe mux / 信号交换矩阵):编译期把「候选信号池」全部保留并接到软核可控的交叉开关,运行时通过寄存器选择当前哪批进 trace RAM——换信号只重配、不重编译,代价是候选池越宽,汇聚逻辑和布线开销越大;
- 工程做法:分层观测——核心信号(状态机、关键总线、跨片接口)静态固定保证深度;外围信号进可配置 mux 池保证灵活度;候选池在每次版本构建时按调试申请增减。
六、插桩必须与 ID 体系绑定
观测点的全部意义是让阶段 5 采到的 Gate 域数据能回映 RTL(23 Gate→RTL 数据回映 查表/聚合/SAIF)。因此每个 probe 点在插桩时就写入映射信息(汇入 17 info_table 映射表 GateID↔RTLID/回溯/版本):源信号 RTL ID/层次路径 → 保护后网名 → probe 点 GateID → 在 trace 帧中的槽位号/位偏移 → 采样时钟域。没有这层绑定,trace 导出的只是「3 号槽有翻转」,无法回答「这是 top.u_core.u_alu 的哪根线」。插桩随 RTL ID 版本走,改 RTL 后 probe 槽位按最小失效集重新分配(10 RTL ID 体系 分配规则/层次绑定/稳定性),旧 trace 与新版本不混用(20 比特流烧录与四方版本一致 manifest/门禁)。
七、观测扰动:observer effect
加探针不是零成本,它会改变设计本身:probe 给信号增加扇出负载、汇聚网络抢布线资源、跨片 probe 占 TDM 时隙,可能让本来刚好收敛的路径违例、让系统频率下降。控制手段:① probe 接入一律先经本地观测寄存器(隔离组合扇出、把扰动变成「多一个 D 输入」而非长线);② 观测网络走独立时钟/独立布线区域,关键路径附近不堆 mux;③ 报告里区分「插桩版频率」与「干净版频率」,性能结论以干净版或按扰动外推;④ 分批次插桩定位问题,而不是一次全插满。观测规划要回答的不是「能不能看到」,而是「在多少频率/资源代价下看到」。
八、观测规划流程
可观测性是编译期决策、运行期受益,标准流程在项目早期启动:
① 验证/软件团队提观测需求(按场景:启动流程、AXI 事务、跨片死锁…)
② Compiler 汇总为信号清单(层次路径 + RTL ID + 时钟域 + 优先级)
③ 按可见性预算分静态点/可配置池,生成 dont_touch/keep/mark_debug
④ 分区时为 probe 汇聚和跨片 trace 预留资源与 lane([56]/[57])
⑤ 编译后校验:每个观测点在网表中存在、槽位与 info_table 一致
⑥ 上板采集([66])→ 回映 RTL([67])→ 活动/覆盖率分析([68])观测硬件本身(trace 引擎、RAM、触发、导出)的架构与容量设计在 专家 45 片上 trace 系统,本篇只负责「编译期如何决定并保住观测点」这半边——两者合起来,才构成原型相对真实芯片的最大优势:看得见。
九、dont_touch 的层级语义:网表对象、跨阶段与多片一致性
「保留信号」听起来像一个布尔开关,实际 dont_touch(及各家等价属性:keep、mark_debug、syn_keep)作用在不同网表对象上语义完全不同,用错层级会导致保护失效或保护过度污染优化。对象层级:Verilog 信号在展开后可能对应一根 net、一个 pin、一个 cell 的端口,或者被优化后什么也不剩。对 net 打 keep 通常阻止该网线被常量传播和合并,但不阻止驱动它的寄存器被删除;若寄存器本身因输出无观测点而被综合掉,保留一根悬空网毫无意义——因此正确做法是沿数据通路标注:要观察寄存器输出,保护点要落在该时序单元及其输出网,必要时连驱动逻辑一起保护。跨阶段有效性:综合期的 dont_touch 与布局布线性的属性是两层。综合后生成的网表在交给 Vivado 实现时,工具可能再次做物理优化(跨层级复制、寄存器复制降扇出、逻辑重组),必须确认属性以 XDC/网表属性形式穿透到实现阶段,否则「综合后网表里还在,布线后又没了」。工程上常见做法是在 Compiler 生成的 XDC 里对关键观测点再生成 set_property DONT_TOUCH true,双层保护。多片一致性:一个 RTL 信号被分区拆到跨片后,物理上变成「发送端寄存器—TDM—接收端寄存器」三个部分,dont_touch 要明确保护的是哪一端的语义。活动采集中,通常要在 TDM 封装前(观察真实发射拍)和解封装后(观察接收拍)分别有观测点,且这两个点的 GateID 都映射回同一 RTL 信号,回映时知道它们是同一逻辑信号在跨片路径两端的化身。保护的代价分级:对单个叶子信号 dont_touch 代价微小;对高扇出总线(如 4096 位 NoC 数据线)整体保护会阻止工具做位宽裁剪和复制优化,显著影响面积时序。观测规划因此必须与资源预算联动(第四节),不能让验证工程师「为了保险把所有信号都 mark_debug」——那样的设计综合质量会被拉低,观测本身也会改变时序(见第七节扰动)。审计与过期清理:每次构建应输出「被保护对象清单」并与观测需求比对——哪些保护仍被采集配置引用、哪些是上版调试遗留;过期保护要能批量清除,否则几轮迭代后设计里挂满无人消费的观测逻辑,成为隐性面积税和时序包袱。
十、观测网络的物理实现:汇聚树、采集存储与触发分发
深层信号不可能各自引一根线到芯片边缘,观测基础设施必须像设计一棵「片上数据汇聚网络」一样工程化,这棵网络本身的架构决定了可见性预算的利用率。汇聚树(mux tree):被保护信号按层次汇入多级多路选择器,选择信号由采集配置寄存器控制,允许运行时切换「此刻观察树的哪一支」;树的层数带来观测延迟,但都在采集时钟域内,对 DUT 时序零影响的前提是汇聚点打拍——深层信号进入观测网络的第一级即与 DUT 时钟对齐寄存,之后整棵树跑在独立采集时钟域,边界做 CDC。存储与导出:汇聚后的采样数据写入片上 BRAM/URAM 块(trace buffer),深度与宽度是核心预算:宽而浅适合看总线数据但窗口短,窄而深适合看长时间握手;成熟方案支持多块 buffer 按信号动态映射。写满后的导出路径(JTAG、以太网、PCIe DMA,见 66 篇)决定触发后多快能拿到数据,农场化场景里导出带宽本身就是回归吞吐瓶颈。触发分发:触发条件(比较器、状态序列、计数器超阈)在靠近汇聚树的位置实现,命中后经触发网络控制 buffer 的写使能(环绕模式/前后窗口比例)。触发网络与汇聚树最好共享选择配置,避免「触发检测的信号」和「能记录的信号」是两个不同集合。时钟域处理:DUT 有几十个时钟域,观测网络的第一级按域分组寄存,再通过异步汇聚进入统一采集时间轴;多域时间对齐靠公共基准计数器(见 66/67 篇)。基础设施成本治理:汇聚树和 buffer 一般占用 1~5% 片内资源,在分区时作为「平台税」预分配,且因为打了拍、独立时钟域,它不进入 DUT 的关键路径——这条性质必须在时序报告里验证:观测网络的路径不应出现在 DUT 的 WNS 报告中,若出现,说明插桩方式错误地把组合逻辑伸进了 DUT 时钟域。与厂商 ILA 的关系:厂商调试核是这套网络的现成实现,适合板级单点调试;大规模原型平台往往自研汇聚网络,因为要支持多片统一时间轴、与 GateID 体系绑定、软件可批量配置——这些是通用 ILA 不提供的。理解这层架构,验证工程师在提「我想看 512 个信号、抓 100 万周期」时,才能把需求翻译成树宽、buffer 深度和导出带宽的现实组合。
十一、观测数据的安全语义:触发竞态、丢样与采集自检
采集到的数据要能用于严肃结论(尤其 68/69 篇的活动和签核用途),观测系统本身必须提供「数据可信」的机制,否则调试者无法区分「DUT 真的没翻转」与「采集链路丢了样」。溢出与丢样显式化:trace buffer 写满、导出跟不上、跨片某片采集 FIFO 溢出时,系统必须设置 sticky 溢出标志并随数据落盘;分析工具见到溢出标志必须把受影响窗口标灰,禁止把不完整数据当完整统计。无声丢样是采集系统最严重的设计缺陷。触发竞态:触发条件与被观测信号来自同一时钟域时,触发命中拍与数据写入拍的先后关系要有确定定义(典型设计:触发拍本身记为窗口中心,前后各 N 拍);多域联合触发(A 域错误且 B 域状态为 X)本质是异步条件,要在采集域重新同步,标注解析误差(±1~2 拍),不能对这类时间差做亚周期级解释。采集路径自检:观测网络需要自己的 BIST——注入已知翻转图案(经由调试总线上的测试模式源),校验汇聚树各级选择、buffer 写入、导出链路和时间戳;每次刷写后先跑采集自检再跑 DUT 场景,和 39 篇的 BIST 理念相同。时间戳与丢周期:环绕采集时,记录的不是绝对周期号而是「触发前/后拍数」;长时活动计数模式下若计数器位宽不足会回绕,分析侧按回绕展开。所有计数器都应有「停钟/复位期间冻结」语义,与 67 篇时间重建纪律一致。多片数据完整性:一次跨片采集产生多份数据文件,靠采集组号(run id)和各片版本号归并;任何一片缺失或版本不符,整组数据拒绝入库,防止「用 A 片的旧数据和 B 片的新数据拼时间轴」。权限与并发:农场里多人共用板子时,采集配置、触发、导出是共享资源,需要会话锁与配置归属,避免甲设置的触发被乙的脚本覆盖。把采集系统当成一个需要正确性证明的数据通路来对待——它有输入、缓冲、时钟域交叉、输出协议——而不是调试时临时拖出来的几个 ILA,观测数据才配得上驱动架构决策和签核消费。
十二、大规模观测的组织:从临时抓波到持续观测体系
当系统规模到十几片 FPGA、验证团队几十人、回归每天几十轮时,观测方式必须从「某人临时加 mark_debug 抓波形」升级为持续运转的观测体系,否则调试带宽会成为整个项目的瓶颈。这一节描述成熟体系的组织形态。观测对象分层常驻:把信号分成三层(这一思想在 66 篇会再展开)——架构关键信号(顶层状态、跨片握手、错误与中断、链路健康)永久插桩、随每版比特流存在,任何失败都有基本现场;数据面摘要(FIFO 水位、valid/ready、活动计数器)常驻低成本统计;逐位波形按需接入、用完释放。三层组合让「常见问题无需重综合即可查」的覆盖率达到很高比例,这是迭代速度的分水岭。观测配置即代码:观测点列表、触发条件、窗口深度以版本化配置文件管理,与测试用例一一对应;失败回归自动带出它需要的观测配置,而不是等工程师看完日志再手工加信号——后者意味着再跑一轮、再等一次复现窗口。配置与 GateID 的绑定由构建系统校验(58 篇正文第六节),版本升级时失效配置自动报告。采集与构建解耦:尽量利用运行时可切换的汇聚树,使同一比特流支持多组观察视图,把「加信号」从重新实现(过夜)降级为改配置(分钟);只有观测树物理上够不到的深层信号才触发重综合,并通过每周固定的「插桩构建」批量吸收,而不是随时打断主线。数据流水线化:采集结果自动上传、按 run id 归档、与测试日志和版本 manifest 关联,失败用例的波形/计数自动生成链接挂到缺陷单;工程师的工作从「搬数据」变成「读结论」。trace 留存策略分级(失败现场长期留、通过用例抽样留、原始大数据短期留),平衡存储成本。观测质量本身要度量:统计两个数——「缺陷从发现到定位所需的构建次数」和「因加观测点而重综合的比例」,这两个数持续下降才说明体系在变好;同时监控观测税(资源占用、对时序的影响),防止观测网络悄悄吃掉频率。组织配套:观测基础设施需要明确 owner(平台团队),验证工程师是用户而不是建设者;提供自助文档和模板,让加一个标准观测点像加一行断言一样便宜。持续观测体系的终态是:大多数硬件问题在失败的那次回归里就带着足够证据死去,不需要第二轮复现——这正是仿真世界里「失败即带波形」的体验在硬件世界的复现,也是原型平台从演示工具变成验证主力的标志。
十三、观测规划的经济学:有限预算下信号怎么排优先级
观测预算(trace buffer、汇聚树宽度、导出带宽、dont_touch 的时序面积代价)永远不够观测所有想观测的东西,观测规划因此是一门显式的优先级经济学,而不是「重要信号都加上」。一个实用的排序框架按四档分配预算。第一档:无预算争议的常驻项。顶层状态机、跨片握手、所有错误/中断/状态异常位、链路健康与水位告警。它们位宽小、价值密度极高,且问题定位几乎总从它们开始;这一档占用应控制在总预算的一小部分但永远保留。第二档:接口边界的事务摘要。主要总线上的地址、长度、命令、响应码与时间戳,FIFO 的满空与水位,仲裁授权序列。摘要比完整波形便宜一个数量级,却能回答「哪一方先停、谁在等谁」这类八成的系统级问题;这是预算回报最高的一档,应占主要份额。第三档:数据通路的统计与稀疏采样。不需要逐周期波形的数据面,用活动计数器、校验和采样、头尾事务记录代替连续 trace;长时运行发现的异常再触发第四档临时接入。第四档:深度波形,按需申请。模块内部位级信号只在具体假设下接入,明确生命周期,问题关闭即释放。预算决策还要乘上「复现代价」权重:只在长时 soak 偶发的问题,其相关信号值得常驻(因为重新等一次复现可能是几天);定向测试稳定复现的问题,临时观测即可。预算评审在每个大版本做一次:统计上版本各档信号的实际使用率(配置过几次、抓到过问题没有),长期无人使用的观测点降级或删除,把预算让给新热点。最后要把「观测成本」也讲给提需求的人:一个 512 位、百万周期的波形需求,折算成多少 BRAM、多少导出时间、可能影响多少频率,透明之后需求方自然会在「想看」和「需要看」之间做出理性选择。把观测当资源管理的团队,和把观测当免费愿望清单的团队,在项目后期的迭代速度差距会拉开数倍。
十四、结语:可见性是原型价值的兑现方式
FPGA 原型相对仿真的全部优势——真实并行、真实速度、真实软件负载——只有在现象可被观察时才能兑现;一块跑得飞快但内部漆黑的板子,验证价值近乎为零。因此观测不是调试阶段的临时开支,而是原型平台的核心能力建设:它从设计第一天的 dont_touch 纪律开始,经过汇聚树、时间轴、触发和缓冲的物理设计,受资源预算的经济学约束,与 ID 和版本体系绑定,最终在持续观测流水线里变成「失败即带证据」的组织能力。这个主题与 66 篇的采集门控、67 篇的时间重建、68 篇的活动分析构成一条完整链条——本篇解决「信号为何消失、如何让它存在且不干扰系统」,后续各篇解决「怎么在多片、多时钟下把它有时间意义地收回来并变成结论」。对工程师个人,最可迁移的一条经验是:在系统还能正常工作时就规划好它失败时要看什么;等到故障已经发生才去加观测点,等待重新综合的那一夜往往是职业生涯中最漫长的夜。
知识脉络

与系列及现有篇目的衔接
- 观测硬件:专家 45 片上 trace 系统、22 ILA/VIO 高级调试
- bring-up 信号可见性:03 原型平台 bring-up 多片点亮/trace、01 原型验证 Debug IP 寄存器是怎么被访问的
- 映射回映:17 info_table 映射表 GateID↔RTLID/回溯/版本、22 Gate 域信号采集 ILA/trace/带宽深度权衡
总结
一句话总结:原型最大的优势是「看得见」,但综合的常量传播、死逻辑消除、网线合并改名、层次打平和 retiming 会在编译期主动删掉你想看的信号,所以可见性必须在阶段 2 提前买;保护要分两级——Compiler 层对实例/替换边界下 dont_touch、对网线下 keep 并透传 mark_debug/DONT_TOUCH 到厂商实现层(信号若在第一级综合就消失,XDC 再保也无对象),且必须按信号清单精确给,不能全设计保护否则面积时序双崩;被保信号经本地观测寄存器隔离后,由 probe 汇聚网络、跨片 TDM trace 时隙、触发限定逻辑和 trace RAM/导出通路组成完整观测链;同时可见信号数受「信号位宽×采样率≤trace 带宽、RAM 容量÷宽度=深度、导出速率决定搬空时间、汇聚布线吃时序」四道预算约束,要在宽而浅与窄而深之间取舍并在分区时预留资源;静态插桩最省但换信号要重编译,可配置 probe mux 候选池可运行时软核切换但花资源,实践中核心信号静态、外围信号入池;每个 probe 点在插桩时就必须写入「RTL ID/层次→保护网名→probe GateID→trace 槽位」绑定并进 info_table,否则采集无法回映;插桩本身有 observer effect(扇出/布线/TDM 开销可能压垮频率),一律先打本地观测寄存器、观测网络独立布线、报告区分插桩版与干净版频率;全流程按需求提报→清单(RTL ID+时钟域+优先级)→预算分级生成约束→分区预留→编译校验点表一致→采集回映推进,本篇管编译期「保住观测点」,专家 45 管运行期 trace 硬件,两半合起来才是原型相对真芯片的可观测性优势。