
我先把OCC电路在CTS阶段最容易翻车的位置讲清楚。前段时间一个项目流片回来ATE机台上扫描链shift测试间歇性fail波形抓下来发现OCC输出的内部测试时钟在切换沿上有毛刺移位数据链被半个glitch脉冲打乱整整排查了两周才定位到是CTS阶段对OCC内部ICG单元的处理方式出了问题。这个项目用的是Synopsys ICC-II做时钟树综合跑完CTS以后check_timing全绿PrimeTime签核也干净但就是上了测试机台才暴露。后面复盘时整个后端团队达成了一个共识OCC电路的时钟树综合不能拿功能时钟树那套“找root、插buffer、平衡skew”的默认流程直接套。OCC的ICG在功能模式下是透明的但在测试模式下要负责从慢速测试时钟切换到快速功能时钟还要精确控制capture脉冲个数它在CTS里的角色比普通门控时钟复杂得多。这篇文章我把实际项目中验证过的5个OCC时钟树综合技巧整理出来每个技巧都会给出对应的Synopsys工具配置命令和使用逻辑。文章适用对象是正在做数字IC后端、特别是负责CTS和DFT时钟这部分工作的工程师。下面内容全部来自真实项目中的操作过程和踩坑记录不是testcase里那种理想情况。1. OCC电路到底在干什么从一次测试fail说起1.1 一次真实的shift失败排查先回到那个fail项目。现象是shmoo图上shift电压/频率窗口明显比预期小而且低电压角下fail。ATE工程师用pattern debug模式抓内部信号发现OCC模块输出的capture时钟在连续脉冲的第二个沿附近多了一个窄脉冲——这就是典型的ICG毛刺问题。当时我的第一反应是查DFT约束确认scan_enable和test_mode的case_analysis有没有设置对。查完发现约束没问题于是把目光转向CTS。逐一检查OCC输出时钟网络的skew报告发现OCC内部ICG的clock pin到ICG输出端的插入延迟和相邻单元不一致偏差大概有几百皮秒而这个偏差正是CTS工具在优化时钟树时往OCC内部ICG的clock网络上多插了两级buffer造成的。1.2 OCC的典型结构与时钟域划分OCC电路在业界通常叫On-Chip Clock Controller主流实现方式是围绕ICG单元搭一套控制逻辑。它一般有两条时钟输入一条是从PLL过来的功能时钟快速时钟可能跑几百MHz甚至GHz一条是从测试引脚直接进来的测试时钟慢速时钟通常10MHz到100MHz之间。内部通过一组状态机控制ICG的enable信号在测试模式下来回切换时钟源并精确控制capture阶段的时钟脉冲数量。从时钟结构角度看OCC的输入端是两条独立的主时钟输出端是经过ICG门控后生成时钟。这个门控后的时钟在DFT里通常定义为generated_clock。问题在于ICC-II在做CTS时默认会把OCC里的ICG当成普通clock gate来优化而这些ICG在测试模式下的开关行为直接决定了内部门控时钟能不能干净地产生脉冲序列。这里的矛盾点就是功能时钟树追求的是整棵树上所有sink点偏移尽可能小而OCC要求ICG的clock pin到内部逻辑的延迟精确可控不能随便插buffer改变时序关系。1.3 为什么OCC的CTS不能照搬常规做法常规功能时钟树的CTS思路是定义好时钟源设好skew group然后让工具做balance。这个方法对纯组合逻辑的寄存器时钟树很好用但放到OCC上就有两个隐患。第一OCC的ICG clock pin如果被当作普通sink点处理工具为了平衡路径会往这些pin上插延迟单元这等于在测试时钟和功能时钟的切换路径上人为增加了一个不可控的延迟变量。第二OCC模块里的同步控制逻辑对时钟上升沿的到达时间非常敏感哪怕skew没有超标只要ICG输入端相对延迟变了就会导致enable信号和时钟沿的相对位置错位进而产生毛刺。所以做OCC的CTS核心原则其实就一句话把OCC当成一个边界盒子让时钟树在盒子边缘停下来而不是穿进盒子里乱优化。2. 技巧一把OCC的ICG钉在时钟树末端防止CTS优化器“好心办坏事”2.1 不约束ICG会发生什么很多CTS工程师第一次接触OCC时都会想“ICC-II不是有自动clock gating check吗交给工具处理不就行了”我在早期项目里也这么干过结果就是上文描述的那种间歇性fail。工具默认的行为是把ICG的clock pin当作时钟树sink点去平衡它会在OCC内部ICG的clock网络上插入buffer有时甚至会因为时序优化而改变ICG单元本身的大小。这些操作对普通门控时钟没有大问题但对OCC这种既要切换时钟源、又要保证ICG enable精确对齐的结构任何一个buffer的插入都会改变时钟沿到达ICG clock pin的绝对时间进而影响ICG输出时钟占空比和脉冲宽度。更隐蔽的是工具改变ICG尺寸后enable引脚到输出端的传播延迟特性也变了时钟门控检查clock gating check虽然能在静态时序分析里报出这个变化但如果你没在CTS阶段设这些检查PrimeTime里即便报了红也往往只当成普通的时序违例处理不会往毛刺方向去查。2.2 边界约束到底怎么设置我最终在项目中采用的方案是用“dont touch size only dont_buffer”三层约束把OCC内的ICG单元完整隔离出来。dont touch的目的是防止工具在时钟树优化过程中对ICG做逻辑重组或替换size only的目的更精细允许工具在必要时调整ICG的驱动强度但禁止改变其逻辑功能。这两个约束看起来差不多实际差别很大。size only保住了功能但工具依然可以基于时序压力改变ICG的尺寸dont touch则连尺寸都不允许动。对OCC内部的ICG我建议用size_only而不是don_touch因为测试模式capture时钟要驱动下游一大片寄存器ICG尺寸如果完全不调整的话驱动能力可能不够工具会额外插入buffer来补反而又绕回了原来的问题。最稳妥的组合是不约束ICG的功能让工具能合法调整尺寸同时禁止在ICG clock pin上做任何buffer插入。2.3 ICC-II中的具体配置命令下面这段TCL配置是我在ICC-II里实际用的每一步都有明确用途。# 1. 找到OCC模块内的所有ICG单元设置size_only set_size_only [get_cells -hier -filter ref_name ~ *CKG* full_name ~ *u_occ*] # 2. 对ICG的clock pin设置dont_buffer禁止CTS在此处插入延迟单元 set_clock_tree_exceptions -dont_buffer [get_pins -hier u_occ/*/CK] # 3. 对OCC输出时钟网络的net设置dont_touch防止布线优化改变网络结构 set_dont_touch [get_nets -of_objects [get_pins -hier u_occ/CLK_OUT]] # 4. 运行clock_opt时显式打开clock gating check set_clock_tree_options -clock_gating_check -setup 0.05 -hold 0.05 clock_opt -from clock_opt_cts -to clock_opt_cts第4条里的setup 0.05和hold 0.05单位是纳秒意思是在ICG的enable引脚上做05ns的保守余量检查。这个值不是拍脑袋定的是结合测试时钟周期和OCC内部控制逻辑的建立保持需求算出来的。如果测试时钟是50MHz周期20nsOCC控制逻辑内部本身要留出几百皮秒的setup余量那么0.05ns已经算比较激进的了。保守一点可以给到0.1ns但不要超过0.2ns否则会让CTS优化器为满足enable路径而过度约束导致面积和功耗变差。2.4 关于ICG位置布局的额外建议除了约束命令OCC在布局阶段就应该被特殊对待。经验做法是在floorplan阶段给OCC模块划一个fence把OCC内部所有ICG和它下游的连接寄存器尽量收拢在一起杜绝OCC输出时钟网络跨模块长距离走线。原因很简单CTS工具做balance时如果两个功能时钟sink点距离过远PPA代价会很大。更重要的是长距离时钟网络在天线效应和串扰上的风险会显著上升。OCC输出时钟一旦上了长线测试模式下这个网络的RC延迟偏差会直接影响capture时钟在扫描链上的分布。所以floorplan阶段多花点时间给OCC划好区域比后期在CTS里补一堆例外要省事得多。3. 技巧二功能/测试双模式下的skew平衡先统一root latency再谈skew3.1 双模式下的矛盾在哪里OCC做CTS时前端DFT通常已经定义好两种时钟模式功能模式下OCC是透明的功能时钟直接穿过ICG到达逻辑测试模式下测试时钟通过OCC内部ICG门控后变成capture时钟。这两种模式对时钟树的要求完全不同。功能模式下功能时钟要驱动全芯片所有寄存器skew目标是几百皮秒以内。测试模式下测试时钟只驱动扫描链上的寄存器但它的频率低、周期大skew目标反而可以放宽到几纳秒。问题在于同一个ICG单元、同一个时钟网络要同时满足两种模式的要求工具在balance功能时钟skew时会努力把OCC输入端和普通寄存器端拉齐这就可能导致测试模式下OCC输出端的延迟变得很大。相反如果优先满足测试模式功能路径又可能因为插入的延迟单元太多而出现setup违例。3.2 先统一root latency再谈skew我实际项目里验证过的最有效做法是在跑CTS之前先把功能时钟和测试时钟的root latency对齐让两条时钟路径在进入OCC的ICG输入端时已经处于同一延迟水平。这个思路的背后逻辑是OCC内部ICG是个汇合点两条时钟都汇到ICG的CLK pin上。如果功能时钟和测试时钟到达ICG CLK pin的绝对延迟差距过大在测试模式进行时钟切换的那一刻ICG输出的时钟相位就会发生跳变这种跳变在ATE测试中表现为capture阶段第一个脉冲宽度异常。对齐root latency的手段是在MMMC环境中对测试时钟设置set_clock_latency。做法如下# 功能模式下功能时钟从PLL出发到达OCC ICG CLK pin的路径已知 # 假设report_timing显示latency约为1.5ns # 测试模式下测试时钟从测试引脚到OCC ICG CLK pin的路径估计为0.8ns # 在test_mode中对test_clock设置额外的source latency来补偿差距 set_clock_latency -source 0.7 [get_clocks test_clock]这样设置之后测试时钟的source latency加实际网络延迟基本等于功能时钟的路径延迟两条路径在ICG入口处对齐。需要注意的是这个补偿值只能在test_mode里设置功能模式如果也设了同样的source latency会把功能时钟树做坏。3.3 MMMC环境下的mode设置与运行策略MMMC是双模式CTS的前提。我在项目里会建两个scenario一个叫func_ss一个叫test_ss两个scenario都用相同的慢工艺角但mode不同create_mode -name func_mode create_mode -name test_mode create_analysis_view -name view_func -mode func_mode -corner ss_corner create_analysis_view -name view_test -mode test_mode -corner ss_corner set_active_views [list view_func view_test] # 在test_mode中标定测试时钟 set_clock_latency -source 0.7 [get_clocks test_clock] # 在func_mode中把OCC的测试模式输入固定为0 set_case_analysis 0 [get_ports test_mode]CTS运行时工具会同时看到两个view在平衡功能时钟skew时会把test_clock的latency约束也纳入考量。有些工程师担心双mode同时跑CTS会互相牵制导致功能时钟树变差实际不会。只要对齐了root latency工具在功能时钟树上做的优化几乎不会受到测试模式的干扰因为测试模式下时钟树负载功耗远小于功能模式。真正需要小心的是不要在两个mode里同时做clock gating check容易把工具搞糊涂。我的做法是只在test_mode里开clock gating check的严格检查功能模式用默认值。3.4 实测数据参考一个实际项目的参考数据功能时钟树在MMMC双模式跑完后最大skew大概是120ps几乎和不带测试模式的单模式CTS结果持平。测试模式下OCC输出capture时钟的skew是1.8ns满足DFT工具给的2.5ns预算。这个结果的关键就在于root latency对齐那一步没对齐之前测试模式skew报告是3.2ns明显超预算。4. 技巧三OCC控制信号必须遵守“先同步、后穿ICG”的时序铁律4.1 控制信号晚到ICG会怎样OCC内部除了时钟路径还有一条控制路径scan_enableSE信号、test_mode信号、以及OCC内部的同步状态机输出使能信号。这些控制信号最终都汇聚到ICG的enable引脚上。ICG cell内部对enable和clock有严格的时序要求ICG上clock gating check就是用来检查enable相对clock沿的建立保持时间的。如果enable信号变换刚好发生在时钟沿附近ICG输出就会产生一个半脉冲或者毛刺。这类问题的棘手之处在于功能模式CTS完全不检查这些路径因为控制信号在功能模式下根本不动测试模式CTS如果不加时钟门控检查工具默认也不管。结果就是时序报告全部绿灯实际芯片测试却出问题。这正是我在项目里反复强调的“先同步、后穿ICG”原则控制信号必须先在OCC模块外的同步器里被功能时钟打一拍保证信号与时钟对齐再进入OCC内部接到ICG的enable上。不能直连。4.2 用set_case_analysis固定测试模式路径为了在CTS阶段让工具沿着正确的路径做时序优化必须显式设置测试模式相关引脚的case_analysis。常见的设置包括# test_mode引脚1表示芯片处于测试模式0表示功能模式 # 在test_mode的analysis view中 set_case_analysis 1 [get_ports test_mode] set_case_analysis 0 [get_ports scan_enable] # shift阶段 # 在func_mode的analysis view中 set_case_analysis 0 [get_ports test_mode]注意scan_enable在shift阶段是0还是1取决于设计约定有些设计在shift阶段scan_enable1但关键的套路是必须把scan_enable固定成一个稳定值不能让工具在CTS阶段把它当作动态信号来优化。否则工具会试图在scan_enable路径上做时序改善插入延迟单元这种优化完全没意义还会白白增加控制路径延迟。4.3 控制信号的输入输出延迟约束示例控制信号从芯片引脚进来经过片内逻辑一路到OCC的ICG enable端。这个路径的输入延迟约束可以由DFT工程师或者后端时序工程师预先计算好。这里给一个典型的I/O约束示例# 假设测试时钟周期100nsscan_enable信号是测试控制器输出的异步信号 # 相对于测试时钟上升沿scan_enable的输入延迟 set_input_delay -clock test_clock -max 2.5 [get_ports scan_enable] set_input_delay -clock test_clock -min 0.5 [get_ports scan_enable]关键点在于scan_enable的输入延迟范围必须落在OCC内部ICG的gating check窗口之外。如果输入延迟范围太宽说明同步链路上的寄存器没有正确地抓住SE信号需要回到RTL级别修改。4.4 检查控制信号路径的实操命令跑完CTS以后建议用下面这两条命令做专项检查而不是只看汇总报告# 报出SE信号到所有ICG enable pin的时序路径 report_timing -through [get_pins u_occ/u_icg*/E] -delay_type max -path full report_timing -through [get_pins u_occ/u_icg*/E] -delay_type min -path full正常情况应该看到SE路径上有同步寄存器并且max和min的差值不会超过半个测试时钟周期。如果报告里看到SE路径的congestion过高或者出现长绕线那就要回到布局阶段去查是不是SE信号穿过了OCC的fence区域。5. 技巧四用clock_gating_check和脉冲宽度检查守好glitch底线5.1 glitch的成因与测试失效机制ICG产生毛刺的底层机制是enable信号在时钟为高电平期间发生变化时锁存器没有足够时间稳定导致输出端出现窄脉冲。在OCC场景里这种情况最容易出现在时钟源切换瞬间OCC从功能时钟切换到测试时钟时如果控制状态机给出的ICG enable清理顺序不对或者enable到达ICG的时刻恰好挨着测试时钟上升沿输出端就会吐出一个宽度只有正常脉冲几分之一的毛刺。这个毛刺打到哪里就坏哪里。打在扫描链的capture触发器上可能导致该触发器的锁存数据错误某个bit的测试向量错位打在分频器上则会让整个时钟序列后续周期全部错位故障类型从单bit失败变成整片fail。5.2 set_clock_gating_check参数应该怎么给Synopsys工具里clock gating check的值是分setup和hold两个方向设置的。setup对应enable在clock沿到达之前必须提前稳定的时间hold对应clock沿到达之后enable必须继续保持稳定的时间。这两个值在OCC场景下建议测试时钟周期除以一个安全因子。如果测试时钟是100ns保守的设置是setup 2ns、hold 1ns。但CTS工具对这些值很敏感值设太大工具为了满足enable路径会疯狂插buffer导致面积膨胀。我的实际建议是先从测试时钟周期的1%到2%开始比如100ns时钟给setup 1ns、hold 0.5ns跑完看到的违例数量如果为零再逐步收紧到0.2ns量级做最终验证。关键是不要在CTS阶段一开始就给太紧否则后面修都修不动。在ICC-II里的设置命令set_clock_gating_check -setup 1.0 -hold 0.5 [get_cells -hier u_occ/*CKG*]注意这里和第一节里的set_clock_tree_options -clock_gating_check不同前者是全局选项主要影响工具在CTS时的优化行为后者是针对具体ICG单元的显式检查值会直接影响时序报告结果。实际项目中两个都要用全局选项开一个宽松的检查范围具体ICG上再设精确值。5.3 脉冲宽度检查与异常定位CTS完成以后在PrimeTime里除了跑常规的setup/hold检查我还额外加了一步脉冲宽度检查# 在PrimeTime中检查OCC输出时钟网络的脉冲宽度 get_clock_gating_check -verbose [get_cells u_occ/u_icg*] report_timing -through [get_pins u_occ/CLK_OUT] -delay_type min如果ICG输出时钟的最小脉冲宽度异常说明enable路径和时钟路径的相对关系出了问题。此时要在时序报告里看ICG的E pin和CP pin之间的edge差正常情况下从CP上升沿到E pin开始稳定的时间差应该远大于gating check要求的值。我在项目中遇到过最隐蔽的情况是某个ICG的check通过但下游两级分频器组合出来的脉冲宽度异常因为分频器本身有自己独立的传播延迟。这提醒我们OCC脉冲宽度检查永远要做在OCC输出端口上而不只是ICG内部。5.4 在PrimeTime里做额外补充检查PrimeTime里还可以用更精确的波形级检查来预测毛刺。但多数场景下只要CTS阶段设置好clock gating checkPR阶段再用PrimeTime签核毛刺问题基本能挡在tapeout之前。我建议把OCC里所有ICG的gating check结果单独输出一份报告作为DFT工程师审查的交付物之一这样如果test pattern生成工具那边对capture时钟宽度有异议可以直接对照这份报告排查。6. 技巧五多角多模下OCC路径的hold修正迭代优化才是正解6.1 什么时候需要修holdOCC路径的hold违例有两个典型场景。第一个是双模式跑完后test_mode下捕获时钟通过OCC路径到达扫描链寄存器和另一条通过测试时钟直接到达寄存器的路径之间产生hold冲突这个在设计里叫时钟汇合clock convergence问题。第二个是慢工艺角到快工艺角的hold漂移OCC内部counter逻辑在新工艺角下跑得更快导致信号提前到达下游寄存器把上一个周期的数据覆盖掉。这两个问题都不会在setup签核时暴露却会在低电压高温角的测试中非常明显。所以多角多模下的hold检查不能等tapeout前临时做应该在CTS完成后的第一轮就加入。6.2 从慢角到快角的裕量计算举个实际例子某个OCC内部生成的capture时钟要驱动扫描链上2000个寄存器。慢角下OCC输出的延迟是2.1ns快角下延迟变成1.2ns差了0.9ns。同时扫描链寄存器之间的组合逻辑在慢角下延迟0.3ns快角下变成0.15ns。这时如果只按慢角修hold快角下数据路径快了0.15ns而时钟路径快了0.9ns数据相对时钟的超前量就是0.75ns。如果寄存器要求的hold时间只有0.1ns那么快角下必然出现hold违例。算清楚这个账之后再去修hold就有的放矢了。6.3 修hold的时机和命令修hold有两个窗口CTS完成后的增量优化和布线后的ECO。建议在CTS阶段只修功能时钟树上的常规holdOCC相关的hold等到收发时钟树做完、综合时钟树deskew完成后再统一处理。因为在OCC这类门控时钟上过早修hold后续deskew操作会把前面插的所有延迟单元全部打乱。下面是ICC-II里修hold的常见命令# 针对OCC输出到扫描链寄存器的hold路径 set_fix_hold [all_clocks] set_clock_tree_options -fix_hold true # 在post-route阶段做增量hold修复 route_opt -incremental -fix_hold true # 针对特定路径手动插入延迟单元 insert_buffer [get_pins u_occ/CLK_OUT] BUFFD8 -new_net_prefix HOLD_FIX_手动插buffer命令要慎用只适用于工具自动修复后仍然有零星违例的情况。如果你发现一条OCC相关hold路径需要插几百个buffer才能修好那通常是时钟树结构设计的问题不是单纯修hold能解决的。6.4 关于buffer插入位置的实战避坑我最后想提醒一个重要细节修OCC路径hold时buffer一定不要插在OCC内部控制逻辑路径上比如ICG的enable路径。原因是enable路径上加buffer会直接影响clock gating check的余量前面刚满足的setup/hold检查可能因此重新变红。正确的位置是插在数据路径上或者插在OCC输出的时钟网络上——注意避开我们在技巧一里加过dont_touch的那条net必须先把dont_touch属性去掉才能插入buffer。实际项目中我通常的做法是CTS阶段用自动fix_hold工具处理完所有OCC路径pre-route阶段做一次时钟树deskewpost-route阶段如果还有剩余hold违例再针对具体路径一条条手动修。每修一条都要重新跑一遍clock gating check确保没有引入新的毛刺风险。这样虽然流程长一点但可以最大程度避免“修了一个hold、冒出一个glitch”的拆东墙补西墙局面。写在最后我在OCC CTS项目里的几个检查习惯OCC电路时钟树综合做得对不对最终会体现在量产测试良率上。我个人在项目收尾前一定会做下面这几件固定动作检查OCC内ICG的dont_touch和size_only属性是否在布线后仍然生效有些优化步骤会悄悄清除属性确认test_mode下OCC输出capture时钟脉冲宽度在快慢角各自满足DFT要求核对ICC-II的CTS log里有没有动过OCC相关ICG cell的报告记录。这几个动作不需要额外投入太多时间但每次都帮我们提前挡掉了至少一个问题。如果你的项目正要开始跑OCC部分的CTS我建议先从技巧一开始把ICG的边界约束做好后面几个技巧都是在它基础上的延伸。