FPGA 原型验证专题 08:不可综合构造识别与存根替换——黑盒清单、模拟 IP、时钟门控的原型改造
更新时间:2026-09-12。本文是「ASIC FPGA 原型验证系统」专题 06~25 深体系列分册,处于阶段 0 交付与阶段 1 展开的交界。总览见 06 原型验证系统全景与文档地图,系统化替换规则库见 11 ASIC→FPGA 等价单元替换 SRAM/时钟/IO/PHY。
这一篇在链路里的位置
Elaboration 会输出"不可综合语法、黑盒清单、时钟域清单"三类检查报告。本篇聚焦前两类如何消化:不是所有报警都要消灭,关键是显式地、可追溯地决定每个 ASIC 专属构造在原型里变成什么,并记录这一等价关系(供后续回映时理解行为差异)。
本文要回答的问题
- 哪些 Verilog/SystemVerilog 构造天然不可综合或在 FPGA 上语义不同(
initial、#delay、fork/join、模拟analog块、强制强度等)? - ASIC 专属硬件原语(定制存储、PHY、PLL/DLL、IO 宏、晶振、电源管理)各自怎么处理?
- 时钟门控(ICG)、电源门控、隔离单元、保持锁存器、电平转换器在"始终上电、无电源域"的 FPGA 上如何等价建模?
- 存根(stub)、行为模型、FPGA IP 替身三种策略的适用边界与风险?
- 替换后如何保证功能等价、如何标注"原型不可验证项",避免错误结论?
一、两个层面:「不可综合」≠「FPGA 不能实现」
消化前端检查报告前,先把两类性质不同的问题分开:
- 语言层面不可综合:构造本身没有硬件对应物——
initial里除寄存器初值外的激励行为、#delay延迟、fork/join_none、wait事件、force/release、$display/$time等系统任务、模拟analog块与驱动强度。这类东西通常出现在行为模型和 testbench 里,正确做法是它们根本不该进综合文件列表;误进了就剥离到仿真专用文件。 - 可综合但 FPGA 无法实现:结构在 ASIC 上有明确硬件,只是 FPGA 资源里没有对应物——定制 SRAM 宏、模拟 PLL/PHY、IO pad、ICG 单元、电源开关。这些不能靠「改写法」消除,必须显式替换或旁路。
第一类是卫生问题,第二类才是本篇主角——它需要工程决策,而且每个决策都影响原型结论的可信度。
二、前端检查报告怎么读
Elaboration 输出三份清单(机制见 09 编译器前端展开 Elaboration AST/实例树):不可综合语法、黑盒清单、时钟域清单。最常见的两类告警:
- unresolved module / black box:某个例化找不到 module 定义。来源通常是漏交文件、条件编译宏配置错、或 ASIC 硬宏本就无 RTL。处理不是盲目塞空壳,而是先判定它属于哪一类(§三),再挂对应策略(§六);
- unsupported construct:可综合子集外的写法。逐条判定是剥离到仿真视图,还是改写为等价硬件描述(如电平敏感
latch改成有 default 的case,见芯片侧 组合与时序逻辑)。
原则:报告里不允许存在「无人认领」的条目,每条要么消除、要么在替换台账里登记去向和理由。
三、ASIC 原语分类处理表
| 原语类别 | 典型单元 | 原型策略 | 失真点 |
|---|---|---|---|
| 存储 | SRAM compiler 输出、单/双口 RAM、ROM | memory wrapper → BRAM/URAM/LUTRAM | 初始值/X 态、同口读写冲突行为、写使能粒度、延迟周期 |
| 时钟 | ICG、PLL/DLL、晶振 buffer | ICG→CE;PLL→MMCM/PLL 或外部时钟 | 门控功耗、抖动、锁定过程时序 |
| IO | pad、电平转换、ODT、模拟 IO | SelectIO/LVDS/IBUF 替身或环回 | 真实电平、ESD、模拟特性 |
| PHY | DDR/PCIe/MIPI SerDes 模拟前端 | FPGA 硬核 PHY 环回或外部仪表 co-model | 模拟均衡、眼图 margin |
| 电源 | power switch、isolation、retention、level shifter | 折叠常上电 + 虚拟开关模型 | 漏电、上下电时序、保持值 |
| 模拟/混合 | ADC/DAC、bandgap、比较器 | 整体旁路,数字侧用行为模型注入激励 | 全部模拟量,原型不可验证 |
四、时钟相关改造:ICG→CE 是头号高频项
ASIC 用集成时钟门控单元 ICG 省电(原理见芯片侧 低功耗入门、UPF 低功耗):gated_clk = clk & enable,门控单元内部用低电平透明锁存器保证使能只在时钟非活动半周改变,输出无毛刺。FPGA 上绝不能真的与出一个时钟(会落到普通布线、抖动大、占资源,且使能组合逻辑有毛刺风险),标准改造是保留时钟不动,把门控使能接到触发器的时钟使能端 CE:
// ASIC: clk_g 由 ICG 产生,触发器用 clk_g
// 原型: clk 不变,使能进 CE(综合自动映射 CE 脚)
always @(posedge clk) begin
if (rst_n == 1'b0) q <= '0;
else if (cg_en == 1'b1) q <= d; // 等效门控:不使能就保持
end功能完全等价(低功耗结构里 ICG 本来就是被 enable 控制的保持语义),区别只是 FPGA 触发器没被真正停钟,因此原型上测不到门控省电效果——这是能力边界,不是 bug。其他时钟改造:内部组合/分频时钟归一到 MMCM 同源时钟或 CE(呼应 07 面向原型的 RTL 交付规范 层次/黑盒/电源域 的时钟表);多路时钟选择用 BUFGMUSH/时钟 MUX 原语,不用数据选择器拼。
五、电源相关建模:折叠为常上电世界
FPGA 没有电源域,电源结构按下表给「功能替身」,重点是保留软件可见的控制面:
| UPF 结构 | 原型替身 | 保留/丢失 |
|---|---|---|
| power switch | 虚拟开关模型:接受开关寄存器写,记录 on/off 状态 | 保留控制序列与状态机;丢失真实断电 |
| isolation cell | 可配置常 0/常 1 输出的使能模型 | 保留隔离行为供软件演练;无真实浮空 |
| retention flop | 普通寄存器(save/restore 时由模型搬运影子值) | 保留 save/restore 流程;掉电保持非物理 |
| level shifter | 直连 | 无 |
电源域边界的功能逻辑在原型上始终活动,所以跨域常电/掉电的接口握手死锁反而能测(如断电域还被访问时的响应),但漏电数据、retention 时序、上电爬坡一律不可在原型得出(交给 功耗与电源完整性 与硅后)。
六、三种策略与可信度分级
对每个不可直接实现的单元,三选一:
- FPGA 等价资源替换(SRAM→BRAM、ICG→CE、PLL→MMCM):可信度最高,可做到周期精确,但仍要核对冲突语义、初值等差异;
- 行为模型替身(PHY 数字模型、第三方 IP 简化模型、模拟激励源):功能级等价,能跑协议流程和驱动软件,但时序/性能/margin 结论无效;
- 存根 stub 旁路(彻底不存在的模块给空壳:返回固定值/伪随机/环回):仅接口替身,让设计能展开能编译,只保证系统其余部分可运行。
台账里必须给每个替换标可信度等级,并在测试计划里写明「哪些测试结论受哪级替换影响」。最危险的不是用了 stub,而是用了 stub 却没人记得,最后拿着环回 PHY 上的吞吐数字去做架构决策。
七、替换关系的可追溯记录
每次替换写一条台账:原模块层次路径(对应 RTL ID,10 RTL ID 体系 分配规则/层次绑定/稳定性)→ 替身模块/资源类型 → 策略与可信度 → 行为差异清单 → 负责人。阶段 6 回映(23 Gate→RTL 数据回映 查表/聚合/SAIF)时,被替换单元的活动数据要能被正确解释:例如 BRAM 替身后的翻转不能直接当 SRAM 功耗活动,CE 替身后的时钟网翻转不能计入门控省电。替换台账最终汇入 info_table(17 info_table 映射表 GateID↔RTLID/回溯/版本)。
八、能力边界声明(原型不能回答的问题)
在每个原型版本的报告首页固定声明:模拟/混合信号行为、PHY 电气 margin、绝对时序收敛(ASIC STA)、真实功耗绝对值(尤其是漏电与门控收益)、良率与工艺漂移都不能从 FPGA 原型得出;原型回答的是数字功能、软硬件交互、相对性能趋势和真实激励活动分布。明确边界,才能让上板的每一个结论被正确信任。
九、ICG 转 CE 的完整作业流程与边界情况
集成门控时钟(ICG)改时钟使能(CE)是替换清单里出现频率最高的一项,看似机械替换,实际有一串边界情况决定改完是「功能等价且时序干净」还是「省了功耗却埋了 bug」。标准作业是:先在展开后的网表上识别所有时钟门控单元——既包括显式例化的标准 ICG 单元,也包括 RTL 里 clk_gated = clk & en 这类被综合成门控的写法——然后把门控后时钟驱动的每个时序单元改为由未门控时钟打拍、门控使能接到该单元的使能端。第一件要小心的是使能信号的时序归属:ASIC 的 ICG 单元内部自带锁存器,保证使能在时钟低电平期间变化、输出时钟无毛刺;直接把使能接到 FF 的 CE 端时,CE 是普通数据输入,要满足相对于时钟沿的建立时间,因此使能逻辑必须在原时钟域内、且不能在时钟树之后再组合生成。展开器要检查每个被转换的 CE 是否仍由该时钟域的寄存器驱动,违例即报错而非静默放过。第二是门控扇出分裂:一个 ICG 可能门控几千个单元且使能相同,替换后这些单元共享同一 CE 网络,CE 变成高扇出网线,需要走通用布线并可能成为时序瓶颈,编译器应在台账里标注这些高扇出使能,必要时在分区时把同 CE 扇出的单元聚在一起。第三是嵌套门控与使能逻辑里含复位的情形:门控使能依赖某个可能被复位清零的寄存器时,要确认替换后复位期间 CE 的行为与原门控时钟停摆行为一致(通常一致,但复位释放顺序可能让第一拍行为不同)。第四是活动率语义的保留:门控时钟在 ASIC 上意味着被门控单元时钟引脚零翻转;替换为 CE 后,时钟引脚每拍都翻转、只是不锁存,这对功能等价,但对「时钟引脚活动」类采集是两种不同信号,回映时要知道哪些翻转是真实门控行为、哪些是替换引入的,否则 69 篇的活动数据会系统性高估时钟网络活动。台账因此必须记录每个 ICG→CE 替换的扇出清单,让回映能反向校正。
十、stub 的三种正确用法和两种危险用法
对无法在 FPGA 上实现的模块做 stub(桩)是必要手段,但桩怎么写,直接决定原型结论的可信度。正确用法一:边界模拟型 stub。对真实 PHY、模拟宏、外部接口,stub 提供符合协议的简化行为——例如 PCIe PIPE 接口的替换模型按 PIPE 时序回 ACK、DDR 控制器 stub 用 FPGA BRAM 实现相同读写语义。它不模拟物理层细节,但让数字侧逻辑的协议状态机能完整跑起来,软件看到的寄存器接口与真芯片一致。正确用法二:流量源/汇型 stub。对算法加速场景,被替换的数据生产/消费者可以做成可注入流量模型的源和汇,支持后续 65 篇讲的协同激励;关键是行为参数(吞吐、延迟、反压模式)可配置且记录在案。正确用法三:占位保拓扑型 stub。只为保持层次、端口、引脚而存在,行为是空操作或固定返回,用于在尚未拿到第三方模型时先打通构建和分区;这类 stub 必须打上最低可信度标签,相关功能路径在覆盖报告里显式标灰。危险用法一:stub 里偷偷实现功能。为了让某个测试通过,在桩里塞入本该属于 DUT 的逻辑(例如在接口 stub 里替 DUT 做地址译码),验证结论立刻失效——你验证的是「DUT 加替身的合体」,流片后替身不存在。危险用法二:无台账的静默 stub。stub 存在但映射表没记录,活动采集和覆盖率会把 stub 内部当成 DUT 活动统计,或者把本该有信号的端口报告成零活动,没人知道那是真死代码还是被挖空了。规则库因此要求每个 stub 在台账里有:被替换对象、stub 行为模型来源、可信度等级、影响的信号/层次范围、哪些验证问题不允许在带此 stub 的构建上回答。
十一、能力声明如何进入验证计划:从「免责文字」到「计划输入」
第八节讲的能力边界声明如果只写在文档开头,效果约等于免责声明;让它真正发挥作用的方式,是反向输入到项目的验证计划——每一条「原型不能回答」都要明确改由哪种手段兜底、由谁负责、在什么里程碑关闭。具体做法是维护一张验证手段映射表:每个设计特性(电源域开关、PHY 均衡、上电时序、CDC 正确性、性能指标……)标注它的验证手段组合——仿真定向用例、约束随机、形式验证、Emulation、FPGA 原型、硅后,每项给出该特性上由谁提供「签字级」结论。例如电源关断与保持:原型显式标注「不验证」,主签字手段是 UPF 仿真加形式隔离检查;CDC:原型只能暴露明显死锁,签字靠 Spyglass 类 CDC lint 加形式;绝对时序:原型不回答,签字靠 STA;长软件路径与真实流量活动:这是原型的主签字手段。映射表让验证经理能发现「没人认领」的特性——这才是能力声明最大的价值:不是谦虚,而是堵缝。评审原型报告时,任何结论都要能追溯到「该问题是否在原型能力范围内」;架构团队拿到性能数据时,旁边应附「该数据已扣除 TDM 与降频影响,仅表征微架构相对趋势」之类的限定语。随着项目推进,能力边界本身也可能移动——某个 stub 换成了高保真替换模型、某条跨片路径经重新分区变成片内——能力声明要随构建版本更新,而不是一份写在项目开头再没人维护的静态文档。能力声明、替换台账、版本 manifest 三者合在一起,构成「这次上板运行到底代表什么」的完整答案。
十二、时钟体系的原型化:从多频率、门控到无时钟逻辑
时钟是 ASIC 与 FPGA 差异最系统的部分,52 篇正文讲了 ICG→CE 的头号改造,这里把整个时钟体系的原型化问题梳理完整,因为时钟侧的任何失真都会沿着频率、时序、活动数据三条线同时扩散。频率体系的折叠:ASIC 内部常有几十个频率点(核心、总线、外设、音频、低速管理),FPGA 的 MMCM/PLL 数量和全局时钟缓冲资源有限。原型化时把同源、频率比简单的时钟尽量折叠到 MMCM 输出,异步低频外设时钟用时钟使能分频实现而非独立时钟树;每个折叠动作都要登记(原频率、实现方式、受影响模块),因为改变频率会改变所有时间相关外设的行为预期,软件侧超时参数(63 篇)要跟着改。门控的层次:ASIC 时钟门控分单元级 ICG、模块级可关断时钟、电源域级整域停钟。第一类转 CE(见第九节);第二类在 FPGA 上通常也用 CE 仿真,时钟不停但逻辑静止,语义接近但时钟网络仍翻转,活动数据要校正;第三类没有对应物,归入电源域折叠(第五节),关断/唤醒行为整体不可验证。动态频率调整(DFS/DVFS):ASIC 运行中切换频率在原型上要谨慎处理——MMCM 重配期间时钟不连续,跨片系统还要求所有相关片协同切换;若目标只是功能验证,常用固定几个频率点、切换时走受控的停钟—重配—锁定—恢复序列(57 篇复位编排的一部分),DVFS 的电压维度在 FPGA 上完全无法体现,相关功耗结论一律不做。特殊时序技术:源同步接口、时钟数据恢复、相位插值、半周期路径、锁存器时间借用这些在 ASIC 上常见的手法,FPGA 原型大多不直接支持,要么在替换模型边界吸收(让 DUT 侧看到等价的普通同步接口),要么标记为不可验证;尤其 latch-based 设计,FPGA 锁存器资源和时序模型都不适合,通常要求设计侧改成主从触发器再交付。时钟失真的登记原则:每一处时钟实现与 ASIC 不同,都必须产生一条台账记录,注明它影响哪些模块的周期计数、活动语义、性能解释——时钟台账与替换台账(55 篇)、manifest(64 篇)并列,是「这次运行代表什么」的必要组成部分。对时钟问题保持敬畏的原因很简单:时钟定义了「一拍」是什么,而整个数字世界的所有结论都建立在「拍」的定义之上。
十三、从适配清单到适配层代码:可维护性的工程形态
第八节讲替换关系要可追溯,本节给出适配层在代码层面的推荐组织形态,这是很多团队踩过的具体坑:适配逻辑不是一堆散乱的补丁,而应该有清晰的目录结构、命名约定和生命周期。目录与命名:适配层按被适配对象的层次镜像组织(proto/pdk/ipname/),每个适配模块头部注释块固定字段:上游模块名与版本、适配原因(对应不可实现分类编号)、等价性等级、模型局限、维护人;一眼能看出「这不是 DUT 的一部分」。包装优先于修改:优先用 wrapper 包裹原模块(在 wrapper 内把特殊原语换成 FPGA 实现,对外端口和参数不变),而不是复制一份 RTL 改内部——wrapper 让上游升级时自动获得内部 bug 修复,复制粘贴则制造永久分叉。无法只靠包装解决的(如端口语义变化)才引入显式适配层,并在集成文件清单里登记。参数化与可移植:适配代码本身参数化(器件家族、平台型号通过配置传入),避免把某块板的资源数量写死;跨 FPGA 厂商移植时,适配层是唯一要重写的部分,DUT 视图零改动,这是「两个视图」架构的验收标准。测试伴随:每个适配模块带自己的测试台,用同一激励同时跑上游行为模型与适配模型做周期比对(对时序敏感的接口比到端口事务级而非逐拍);适配层是代码,就该有代码的测试纪律,55 篇的规则库回归是这些测试的系统级汇总。生命周期管理:上游问题修复导致适配不再需要时,删除适配并回收台账编号;适配引入的新参数、新时钟、新约束随适配一起进入 manifest;禁止「临时适配先上板,以后再整理」——以后从不发生,临时层会变成地质层,三层之后没人说得清哪个补丁为什么存在。把适配层当作平台产品的核心代码库来经营(有 owner、有评审、有持续集成),原型项目之间才能复用积累;反之,每个项目从零写补丁是这个领域最大的隐性浪费。
十四、能力声明的对外沟通:如何向不了解原型的人解释「这个结果不算数」
原型工程师有一项非技术但极关键的职责:把能力边界讲给没有 FPGA 背景的管理者、软件负责人和客户听,让他们正确使用结论。常见的沟通失败有两种:一是把原型说得无所不能,于是架构团队拿原型频率给芯片定性能目标,硅片回来后对不上,原型团队背锅;二是把边界讲成「这也不行那也不行」,管理层质疑整个平台的价值。成熟的表达方式是给每个结论贴三类标签。「可直接采信」的结论:功能逻辑正确性(在替身模型覆盖范围内)、软件与硬件的接口行为、协议状态机的长时稳定性、死锁与启动流程——这些是真实并行硬件上的真实行为,置信度高。「趋势可用、绝对值不可用」的结论:微架构相对性能(改了缓存策略后命中率变化、仲裁调整后的空泡比例)、活动的空间分布(哪块逻辑忙、哪块闲)、相对瓶颈排序;使用时必须说明降频和跨片延迟已按模型校正,绝对值留给真芯片。「原型不回答」的问题:物理层、绝对功耗、电源域行为、工艺相关时序——明确指向仿真、形式验证或硅后测量,并说明责任人。沟通时还要主动管理预期:原型最有价值的交付不是「漂亮演示」,而是「提前几个月发现并修掉会在硅上致命的问题」,这个价值要靠缺陷清单和被避免的返工来量化呈现,而不是用跑分包装。每一份对外报告封面固定一段三行声明:本数据由哪个版本产生、替身和折叠覆盖了什么、哪些结论不可用于签核。把丑话说在前面,信任反而建立得最牢;含糊地报喜,是原型团队信誉最大的长期损耗源。
十五、可综合性的思维转换:写给 ASIC 设计师的十条提醒
本篇的最终目的,是让长期写 ASIC RTL 的设计师形成一套面向原型的思维习惯,压缩成十条可直接执行的提醒。第一,任何延时标注、模拟行为、initial 初始化在原型世界都不存在,写的时候就假设它们会被拒绝。第二,时钟只能来自专用时钟结构:端口经 MMCM/PLL 或时钟使能,绝不用组合逻辑造时钟;需要分频用使能。第三,复位只用于定义系统身份的寄存器,数据通路保持未复位并由有效位保护,仿真用 X 初值验证这个假设。第四,存储器只用可推断模板:同步读、显式使能、复位策略与目标 RAM 一致,多端口和写穿透需求在设计阶段就与替换团队对齐。第五,锁存器不进交付物,所有条件分支补全,case 带 default。第六,实例和信号显式命名、保持稳定,名字是跨工具契约的一部分,重构前查影响面。第七,参数集中管理,不在多处写魔数,工艺相关开关与功能开关分离。第八,黑盒和第三方 IP 单独登记,带端口契约、模型来源、版本,不留「到时候再说」的接口。第九,电源域、关断、保持等概念在原型中折叠为常上电世界,相关验证目标安排给仿真和形式,不指望上板。第十,每次交付前自问三个问题:它能不能展开成稳定实例树、每个结构在 FPGA 上的落点是什么、两个月后别人凭什么追溯到我这一行代码。这十条不降低任何 RTL 表达能力,只是把「工具能不能实现、团队能不能追溯」的思考从原型团队前移到了设计源头——原型验证中最深刻的一条经验就是:前期省掉的每一分纪律,后期都会以十倍的等待偿还,而前期多一分结构化,后面整条流水线(展开、替换、分区、综合、采集、回映、签核)都会快一分、可信一分。把这些习惯内化之后,设计师会发现自己交付的不只是「能仿真的 RTL」,而是一份面向整个芯片生命周期的工程资产。
知识脉络

与系列及现有篇目的衔接
- 前置:07 面向原型的 RTL 交付规范 层次/黑盒/电源域
- 展开机制:09 编译器前端展开 Elaboration AST/实例树
- 系统化替换库:11 ASIC→FPGA 等价单元替换 SRAM/时钟/IO/PHY
- 低功耗构造背景:芯片侧 低功耗设计入门、UPF 低功耗
总结
一句话总结:原型适配先要分清两个层面——#delay、fork、initial 激励等语言级不可综合构造是卫生问题,应剥离到仿真视图不进综合列表;ASIC 硬宏(SRAM/ICG/PLL/PHY/IO/电源/模拟单元)是「可综合但 FPGA 没有」,必须显式替换或旁路,Elaboration 的 unresolved module 告警每条都要有人认领、登记去向;六类原语各有替身:存储经 wrapper 映 BRAM/URAM(注意冲突语义/初值/使能粒度失真),头号高频改造是 ICG 时钟门控转触发器 CE 使能(功能等价但测不到门控省电),多路时钟走 MMCM/BUFGMUX 而非数据逻辑,电源域折叠为常上电世界(虚拟开关、可配隔离值、影子寄存器模拟 retention),关键是保留 PMU 可见的控制面以演练上下电流程;三种策略按可信度排序——FPGA 等价资源替换可做到周期精确、行为模型只到功能等价、stub 存根仅接口替身,台账必须记录原层次/RTL ID→替身→可信度→行为差异,最危险的是用了 stub 却被遗忘;最后必须在每份原型报告首页声明能力边界:模拟行为、PHY 电气 margin、ASIC 时序签核、绝对功耗、良率工艺都不能从原型得出,原型只回答数字功能、软硬件交互、相对性能趋势和真实激励活动分布。