☰
OCC电路CTS阶段五大实战技巧:从ICG边界约束到hold修复
2026/9/28 15:40:15 网站建设 项目流程

我先把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 边界约束到底怎么设置

我最终在项目中采用的方案,是用“don't touch + size only + dont_buffer”三层约束,把OCC内的ICG单元完整隔离出来。

don't touch的目的是防止工具在时钟树优化过程中对ICG做逻辑重组或替换,size only的目的更精细:允许工具在必要时调整ICG的驱动强度,但禁止改变其逻辑功能。这两个约束看起来差不多,实际差别很大。size only保住了功能,但工具依然可以基于时序压力改变ICG的尺寸;don't 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,周期20ns,OCC控制逻辑内部本身要留出几百皮秒的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再谈skew

3.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_enable(SE)信号、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_enable=1,但关键的套路是必须把scan_enable固定成一个稳定值,不能让工具在CTS阶段把它当作动态信号来优化。否则工具会试图在scan_enable路径上做时序改善,插入延迟单元,这种优化完全没意义,还会白白增加控制路径延迟。

4.3 控制信号的输入输出延迟约束示例

控制信号从芯片引脚进来,经过片内逻辑一路到OCC的ICG enable端。这个路径的输入延迟约束可以由DFT工程师或者后端时序工程师预先计算好。这里给一个典型的I/O约束示例:

# 假设测试时钟周期100ns,scan_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 check,PR阶段再用PrimeTime签核,毛刺问题基本能挡在tapeout之前。我建议把OCC里所有ICG的gating check结果单独输出一份报告,作为DFT工程师审查的交付物之一,这样如果test pattern生成工具那边对capture时钟宽度有异议,可以直接对照这份报告排查。

6. 技巧五:多角多模下OCC路径的hold修正,迭代优化才是正解

6.1 什么时候需要修hold

OCC路径的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阶段只修功能时钟树上的常规hold,OCC相关的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阶段做一次时钟树deskew,post-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的边界约束做好,后面几个技巧都是在它基础上的延伸。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询