聊到Tessent的OCC(On-Chip Clock Controller,片上时钟控制器),很多做DFT的工程师第一个反应都是“这不是个现成IP么,直接例化用不就行了”。但实际项目里,OCC的选型往往比想象中更考验对设计时钟架构的理解。同样是叫OCC,标准OCC、同步OCC、mini OCC在功能、面积、配置方式上都差得很远,选错一个,轻则ATPG覆盖率上不去,重则整个芯片回来了却没法在量产测试里做at-speed coverage。
这篇文章我把三种OCC的选择逻辑、适用场景和配置要点一次讲清楚,重点放在“为什么这么选”和“配置示例怎么落地”上,手把手带你从“知道OCC”走到“会在项目里用对OCC”。
1. 三种OCC的定位:先分清再选择
1.1 标准OCC(Standard OCC)到底解决什么问题
我们通常说的标准OCC,是应用最广的一种片上时钟控制器,核心功能可以拆成三块:shift时钟与capture时钟的选择切换、快速时钟脉冲个数的控制、以及慢速时钟(一般是shift clock)和功能时钟(一般是PLL输出)之间的无缝衔接。
为什么要专门放一个OCC在扫描链和功能逻辑之间?因为at-speed测试里,shift阶段用低频时钟把测试向量灌进扫描链,capture阶段要用功能时钟频率跑一个或两个周期,来覆盖transition fault。如果没有OCC,capture时钟的频率、起始相位、脉冲个数都不可控,很容易在shift和capture切换时产生毛刺,直接导致capture拍到的值不可信。标准OCC就是用来解决这个切换问题的,它内部一般有同步逻辑和脉冲计数器,保证从shift到capture再到shift的整个过程里,时钟总能按照设计期望的波形走。
从实现上看,标准OCC支持把多个功能时钟源作为输入,通过可编程的pulse number寄存器控制capture脉冲个数,还可以配置capture timing上的慢速模式。它的好处是灵活、适配大部分数字SoC,缺点是面积偏大、结构相对复杂,而且要正确工作,必须在RTL阶段就把它挂进时钟树上,不能等综合完再以fix模式乱插。
1.2 同步OCC(Synchronous OCC)什么时候非用不可
同步OCC这个名称容易让人误解,它不是指“让OCC自己同步”,而是指它专门用来处理多时钟域之间的同步捕获问题。
举个例子:一个SoC里经常有CPU域、总线域、DDR域,这些域各自有独立的PLL或者分频关系,频率可能不同、相位可能存在偏移。标准的OCC能控制每个域各自的capture脉冲,但没办法保证两个域之间的launch edge和capture edge在时间上是严格对齐的。如果设计中存在跨时钟域路径,且你在ATPG时又把这些路径纳入约束范围,使用标准OCC就会出现“同一拍capture脉冲,两个域看到的边沿完全对不上”的问题,结果就是数据采样错误,覆盖率再高也没意义。
同步OCC的核心价值就在这里:它内部会对多个时钟域的时钟相位做对齐处理,并针对跨域路径产生对齐的launch和capture沿,使得跨时钟域的捕获结果与功能行为一致。简而言之,设计里有真正的CDC路径要测,且选择不借助约束裁剪掉这些路径,那同步OCC基本就是必需品。
1.3 mini OCC不是“缩水版”,而是场景版
mini OCC第一次出现在很多人的视线里,是在IP验证或者小模块测试的场景中。它的定位非常明确:用最小的逻辑代价,提供OCC最核心的两个能力——时钟切换和单次/两次脉冲产生。
mini OCC通常不支持多个功能时钟源之间的复杂选择,也没有完整的可编程寄存器,只保留一个固定或极少配置的capture模式。因为没有大量控制逻辑,它的面积通常只有标准OCC的几分之一,对后端布局布线非常友好。
在项目里用mini OCC,典型场景有两种。第一种是局部IP核、模拟模块或者某些小规模的数字子模块,它们不需要跟全芯片的DFT策略深度联动,只要在测试时能完成基本的转换故障覆盖就行。第二种是作为TDR(Test Data Register)或者某些内建自测(BIST)逻辑的时钟控制器,用mini OCC做一个简单的快速时钟窗口,不追求复杂的ATPG交互。
所以我的观点是,mini OCC不是缩水,而是精准裁剪。你要在最不重要的区域塞一颗标准OCC,反而是浪费面积和绕线资源。
1.4 三种OCC直观对比
| 对比维度 | 标准OCC | 同步OCC | mini OCC |
|---|---|---|---|
| 主要用途 | 一般at-speed测试 | 多时钟域同步捕获测试 | 局部小模块简单脉冲控制 |
| 多时钟源支持 | 支持,可配置选择 | 支持,且内部做相位对齐 | 一般只支持单一时钟源或极少选择 |
| 脉冲个数控制 | 可编程 | 可编程且支持跨域对齐 | 通常固定1或2个脉冲 |
| 面积开销 | 较大 | 最大(包含同步逻辑) | 很小 |
| 配置复杂度 | 中等 | 较高 | 低 |
| 典型场景 | 大部分数字SoC主时钟域 | 高速多时钟域SoC、CDC路径覆盖率要求高 | IP测试、小模块、BIST时钟控制 |
有没有可能一个设计里同时出现三种OCC?完全可能。我在一个大型SoC项目里就见过主域用标准OCC,两个高速接口域之间用同步OCC,几个小的debug子模块用mini OCC。这恰恰说明OCC选型是“按时钟域、按场景”做出的取舍,而不是一刀切。
2. 选择OCC前,需要先想清楚的四件事
2.1 你测的是哪个时钟域
选OCC之前,先把你芯片里的时钟域画出来。通常按功能频率、是否跨域、是否需要at-speed测试三方面去分类。
如果一个时钟域是纯粹的单域逻辑,路径终点都在本域之内,那标准OCC就够了。它只要保证capture脉冲在正确的频率、正确的数量上出现,覆盖transition fault就完全可行。
但如果你的芯片里存在时钟频率不同、又存在真实数据交互的跨域路径,比如CPU总线和DDR控制器之间的数据通路,你就得问自己两个问题:这些跨域路径要不要在ATPG里测?如果需要测,打算用什么方案?如果你答案是“要测”,而且不想用false path约束把这些路径全部屏蔽掉,那就必须上同步OCC。
这里面有一个很容易犯错的地方:很多人在时钟约束阶段把跨域路径设成false path,以为这样OCC的问题就消失了。从工具角度看,这样做确实能把跨域路径排除出测试范围,但同时也意味着那片设计的功能逻辑覆盖率永远是缺口的。是否接受这个缺口,就是项目决策层面的事情了。
2.2 你接受多大的OCC面积开销
面积是OCC选型绕不开的考量。同样是做一个at-speed测试控制器,标准OCC可能包含完整的脉冲计算器、多个时钟分频、模式配置寄存器、复位同步逻辑,而mini OCC可能只是几十个门。
打个比方,如果你在一个只有几十万门的模拟前端模块里塞一个完整的标准OCC,那OCC自己的面积占比可能超过整个模块的1%,这在成本敏感型芯片里完全不可接受。反过来,如果你在主CPU域里为了省面积强行用mini OCC,导致无法配置多周期脉冲,ATPG覆盖率又会受限制。
我个人的经验是,OCC面积预算建议在芯片DFT方案评审时就定下来。先把每个时钟域分类,再估算每个域需要的脉冲控制复杂度,最后结合面积预算确定OCC类型。不要等RTL都写完了再改,那会牵扯到时钟树综合和DFT pattern重新生成。
2.3 你的clock架构是否已支持OCC输入
OCC不是一个孤立的时钟门控单元,它的输入侧必须接几个关键信号:慢速shift clock、快速功能时钟源、scan enable(或shift enable)、OCC enable,以及可能的复位。因此,RTL里的时钟树架构必须给OCC留出这些输入端口。
很多实际项目翻车不是选型错了,而是RTL阶段根本就没给OCC留输入。比如说功能时钟被深埋在某个PLL的分频配置后面,测试模式里根本没法把这路时钟单独导到OCC的fast clock输入上,那就算用标准OCC也白搭。
所以在做OCC选型时,要同步检查时钟树可测性。换句话说,OCC的类型要跟你的时钟源分布匹配,不能脱离RTL和时钟网络结构单独做决定。
2.4 你和后端、系统软件对OCC复位/使能是否有共识
这一点很容易被忽略,但它往往是项目后期最头疼的问题。
OCC需要复位,复位信号在测试模式下必须可控。OCC还需要使能,这个使能信号通常来自JTAG、可配置寄存器或者专门的测试控制信号。问题在于:如果你在DFT方案里假设OCC使能由某个寄存器控制,而后端在做时钟树时实际挂的却是一个固定电平,那么pattern生成时工具分析到的OCC行为会跟真实电路不一致。
更麻烦的是软件配置。有的芯片支持通过固件在系统启动时配置OCC的脉冲数量,那DFT必须跟软件团队对齐这些寄存器的默认值,不能出现“硬件上OCC默认发出2个脉冲,软件测试却期望4个脉冲”的错位。这类共识问题,建议在OCC选型阶段就写进DFT plan,并让后端的DFT工程师、RTL owner、软件验证团队都签个字。
3. 配置示例:标准OCC、同步OCC与mini OCC的落地写法
3.1 标准OCC配置:最常规的fast capture场景
以一个常见的32位SoC主域为例,功能时钟Source是PLL,测试时钟是扫描时钟,扫描时钟频率假设50MHz,功能频率假设500MHz。标准OCC要完成的任务是:在capture阶段产生2个fast capture脉冲,然后平滑回到shift阶段。
RTL例化层面,标准OCC的端口一般包含以下这些:
tessent_std_occ u_std_occ ( .shift_clk (scan_clk), // 扫描时钟,慢速,用于shift .fast_clk (pll_clk), // 功能时钟,快速,用于capture .shift_enable (scan_enable), // shift使能,高有效表示shift .occ_enable (occ_en), // OCC总使能 .reset_n (occ_rst_n), // 异步复位,低有效 .mode_ctrl (capture_mode), // 控制模式:single / double / slow .occ_clk_out (func_clk_gated) // 输出到时钟树的最终时钟 );关键设计点在于,shift_enable和occ_enable通常都不是简单的裸信号,需要经过跨时钟域的同步处理,避免在时钟切换的瞬间产生亚稳态。很多标准OCC内部已经包含这类同步器,但如果你用自己写的OCC逻辑,这层同步必须自己加上。
在Tessent的测试流程中,标准OCC的脉冲个数通常通过test procedure来体现。以STIL或者Tessent内置procedure为例,capture脉冲数往往写作:
// 示意:capture 2表示fast capture产生2个功能时钟脉冲 WFT capture_2_cycles { scan_enable = 0; capture_clk 2; scan_enable = 1; }这里的核心思想是:工具不需要知道OCC内部具体怎么计数,它只需要告诉OCC“现在要进入capture阶段、给2个脉冲”。OCC内部自己会把fast_clk切换出来、数2个沿、再切回去。所以标准OCC的配置重心通常在两个地方:一是保证RTL例化时端口连接正确,二是在Tessent时钟约束里让工具清楚fast_clk和shift_clk的时序关系。
3.2 同步OCC配置:多时钟域对齐的关键设置
同步OCC比标准OCC多出来的核心能力是“对齐”。
配置同步OCC时,RTL例化层面最明显的区别是会多出多个fast clock输入,以及一组对齐控制的配置端口:
tessent_sync_occ u_sync_occ ( .shift_clk (scan_clk), .fast_clk_i ({ddr_clk, bus_clk, cpu_clk}), // 多路功能时钟 .clock_domain_sel (domain_sel), // 选择当前测试的时钟域 .shift_enable (scan_enable), .occ_enable (occ_en), .reset_n (occ_rst_n), .sync_mode (sync_mode), // 配置跨域对齐策略 .pulse_num (pulse_num_cfg), // 脉冲个数配置 .occ_clk_out ({ddr_occ, bus_occ, cpu_occ}) );真正的配置难点在Tessent侧。同步OCC不仅要把每个域的时钟都约束清楚,还要定义域与域之间的“对齐关系”。最常用的做法是利用Tessent中的时钟组约束,把多个域功能时钟做成一个已知相位关系的时钟组:
# 示意:把同步OCC相关时钟纳入同源时钟组 set_clock_groups -asynchronous [get_clocks shift_clk] set_clock_groups -synchronous -group {cpu_clk bus_clk ddr_clk}这样工具会认为CPU域、总线域、DDR域之间存在同步关系,在做跨域路径的pattern生成时,捕获边沿会按照同一拍来处理。同步OCC的内部逻辑则会确保这三个域输出的func_clk在launch/capture时沿对齐。要注意的是,同步OCC的对齐能力是有前提的:这几个功能时钟必须本质上同源,或者是整数倍分频关系。如果两个域之间完全是独立PLL、频率毫无整数倍关系,那即便同步OCC也无法物理上让它们的沿在每个case下都对齐,这时要么在RTL侧用异步FIFO,要么在DFT约束里仍要设false path。
3.3 mini OCC配置:在局部时钟域里做减法
mini OCC的配置相对简单,它不追求多域控制,只追求把“一键测试时钟”做扎实。
一个典型的mini OCC例化可能是这样:
tessent_mini_occ u_mini_occ ( .test_clk (scan_clk), // 测试时钟 .func_clk (ip_clk), // 功能时钟 .test_mode (test_mode), // 直接进入测试模式 .pulse_sel (1'b1), // 固定选择2脉冲模式 .occ_clk_out(ip_test_clk) // 输出给被测逻辑 );这里pulse_sel固定拉高,意味着只要OCC被使能,它在capture阶段就贡献2个功能时钟脉冲。mini OCC通常没有复杂寄存器配置,所有模式都通过端口硬连接来控制。好处是逻辑简单、可预测性强;坏处是一旦RTL固定之后,脉冲数量没法用软件动态调整。
在Tessent流程里,mini OCC的配置方式也很“轻”:
# 示意:在Tessent中把mini OCC输出时钟当普通测试时钟约束 set_clock -name ip_test_clk -period 2 -waveform {0 1} set_clock_as_scan_clock ip_test_clk这样工具会把mini OCC的输出当成一棵独立的测试时钟树来对待,后续的pattern生成就跟正常时钟域一样了。
3.4 让Tessent正确识别OCC的通用设置
不管哪种OCC,Tessent侧都有一个绕不开的问题:工具怎么知道你的电路里有这么一颗OCC?
通常有三条路径。第一条是OCC以标准单元的形式出现在综合网表里,Tessent通过库里的cell model识别它;第二条是OCC作为IP在RTL中例化,Tessent在elaboration阶段通过IP模型分析它的行为和端口关系;第三条是OCC完全是自己写的RTL逻辑,那Tessent需要你在配置阶段把OCC相关的路径和时钟关系明确描述出来,否则它只会当普通组合逻辑去分析。
实操里最常见的问题出在自研OCC逻辑上。Tessent分析不到OCC行为时,很容易把occ_clk_out当成普通门控时钟,然后按普通组合逻辑的方式去推导时钟关系。结果就是capture时序完全不符合预期。
解决思路是给Tessent提供一个明确的行为描述。最基本的描述要包含三个信息:OCC的输入时钟有哪些、输出时钟和输入时钟的沿对齐方式是什么、capture模式下脉冲个数是多少。比如在Tessent中可以通过对时钟路径的详细约束,把OCC输出时钟的相位和脉冲行为定义出来:
# 示意:定义OCC输出时钟与fast clock的对齐关系 create_clock -name func_clk_out -period 2 [get_pins u_std_occ/occ_clk_out] set_clock_uncertainty 0.2 [get_clocks func_clk_out] set_clock_sense -pulse -clock [get_clocks pll_clk] [get_pins u_std_occ/occ_clk_out]其中set_clock_sense -pulse是让工具明白这个端口的输出是对输入时钟的脉冲型选择输出,而不是组合逻辑直通。这行约束在自研OCC的调试里非常重要。
4. 常见问题与排查技巧实录
4.1 毛刺导致capture采样错乱
现象:pattern仿真时,capture阶段的第一个上升沿经常采到未知值,改慢时钟频率后反而更严重。
这种问题十有八九是OCC在时钟切换时没有做足够长的间隔保护。标准OCC内部即使有同步器,也只保证状态机不亚稳态,不保证两个时钟源之间切换时没有窄脉冲。理想做法是在shift阶段结束时把输出时钟停在低电平,等fast_clk的某个安全相位再开启。很多成熟OCC IP都实现了“低电平切换”机制,但如果你用的是自研OCC,就必须检查切换逻辑是否存在组合冒险。
排查建议是不要只看功能仿真,要跑带时钟树延迟的门级仿真,专门在shift enable下降沿附近看occ_clk_out波形。如果波形上出现明显毛刺,优先增加切换逻辑中的同步级数,或者在高速时钟树上增加等待周期。
4.2 同步OCC配置后仍出现跨域路径失败
现象:代码里已经用了同步OCC,Tessent约束也加了时钟组,但capture后还是出现大量X态或者mismatch。
先区分是逻辑问题还是时序问题。逻辑问题通常是跨域数据没有在RTL侧做逻辑同步,比如没有用两级触发器同步控制信号,那不管OCC多好,采样结果都不可控。时序问题则是OCC确实给出了对齐沿,但两个域的组合逻辑延迟差异过大,导致本来对齐的沿在逻辑深处已经无法满足建立保持。
操作上可以这样排查:先看两个功能时钟的相位关系是否在Tessent的sdc约束里表达正确,再检查OCC内部的对齐逻辑是否真的把两个域的输出时钟沿对齐到同一周期边界。很多时候问题出在“约束正确但实现没跟上”,需要回到RTL波形上去确认OCC真正输出的相位关系。
4.3 mini OCC的OCC enable被误识别成普通信号
现象:mini OCC已经例化进RTL,但Tessent分析时没把occ_enable当作测试控制信号,导致捕获时钟的时序检查全乱。
mini OCC因为逻辑简单,工具往往不会自动意识到它的“特殊身份”。这时候需要在Tessent配置里把occ_enable显式声明为测试控制信号,并跟scan enable区分开。一般来说,只要让工具知道occ_enable在测试模式下是可控的、且和scan_enable存在固定时序关系,后续问题就能解决。
遇到这个问题时,先检查Tessent报出的定时弧,看看OCC输出端口的驱动是不是已经被识别。如果工具把occ_enable当成普通数据信号参与捕获,那意味着它的变化时刻会和capture沿扯上关系,这显然不是你要的结果。
4.4 排查工具无法识别OCC cell model
现象:OCC IP来自第三方厂商,Tessent在read netlist阶段直接报unresolved cell。
这通常不是代码问题,是库模型没给全。Tessent分析OCC依赖cell model或行为模型,你需要确认库里是否包含OCC对应的模型文件,并且在Tessent配置阶段把相应的library加载进去。如果是第三方IP,还要确认拿到的是“DFT view”,而不只是功能仿真模型,因为DFT view里才有时钟分析所需的所有时序弧信息。
排查方法是先用Tessent的library检查命令,针对OCC所在的cell逐一确认是否能正常elaboration。如果某个cell解析失败,可以直接从网表里把它找出来,对照库文件里的cell名,看是不是大小写、_rvt后缀或者其他命名差异导致匹配不上。
4.5 常见问题速查表
| 问题现象 | 可能原因 | 优先排查方向 |
|---|---|---|
| capture脉冲数不对 | OCC配置模式错误 | 检查OCC脉冲配置端口和test procedure是否一致 |
| 波形出现毛刺 | 时钟切换无间隔保护 | 检查OCC切换逻辑和门级仿真波形 |
| 跨域路径大量失败 | RTL未做逻辑同步 / 对齐约束缺失 | 检查CDC同步电路和Tessent时钟组约束 |
| OCC enable影响capture | 工具未识别OCC控制信号 | 显式声明测试控制信号关系 |
| Cell unresolved | 库模型缺失 | 检查DFT library和cell model配置 |
最后再分享一个我自己的习惯:刚接手一个带Tessent OCC的项目时,我不会先翻代码,而是先把测试时钟树画出来。从shift时钟、PLL输出、OCC的输入输出、再到扫描链的时钟端,每一步都标清楚频率和相位关系。这个图挂了之后,选标准OCC、同步OCC还是mini OCC,几乎不用纠结,每个时钟域该配什么类型一目了然。配置报错了,也能按图索骥快速定位是端口接错、约束缺失,还是工具模型没配好。OCC的选型说到底不是选择题,而是设计架构题的必然结果。