ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

时钟MUX时序约束详解:从原理到实践避免时钟切换死机

时钟MUX时序约束详解:从原理到实践避免时钟切换死机 做后端时序收敛这么多年每次看到时钟MUX约束报错我基本都能猜到问题出在哪。时钟MUXclock MUX是芯片里最常见也最容易被低估的结构而它的时序约束一旦写错轻则CTS多长出几层buffer重则芯片回片后时钟一切换就挂。我印象最深的一次是某个模块有两个PLL输出通过时钟MUX切换后端为了省事只加了一组set_false_path结果CTS把两条时钟树都长了一遍时钟切换时出现毛刺低频测试全过高频一跑就随机死机。查了整整一周最后发现问题就出在约束没有告诉工具“这两个时钟永远不会同时有效”。这篇文章我想把时钟MUX相关的时序约束思路完整梳理一遍。内容不挑工具主流STA和CTS工具都适用重点讲清楚为什么这么约束、约束写下去之后工具到底看到了什么以及在实际项目中经常踩的坑。适合刚接触时序约束的工程师也适合已经写了几年SDC但对时钟MUX边界场景比较模糊的朋友。1. 时钟MUX电路在芯片里到底解决什么问题1.1 为什么时钟树里要放MUX时钟MUX顾名思义就是时钟路径上的多路选择器。它解决的核心问题只有一个让芯片在不同的运行阶段能使用不同的时钟源。举几个最常见的例子SoC里CPU频率需要动态调整低速运行时切到低频PLL高性能运行时切到高频PLL测试模式下要切到scan_clk功能模式下切回PLL输出低功耗场景下切到外部32kHz时钟以及为了可靠性而做的冗余时钟源备份主时钟失效时切换备用时钟。这些场景不可能用复位后固定选择的方式实现因为很多切换是系统运行中实时发生的。所以芯片里会专门放置一类时钟切换单元也就是glitch-free的时钟MUX结构。这种单元和普通组合逻辑MUX有本质区别普通MUX在字面意义上就是“选择一路输入连到输出”只要选择信号翻转输出就可能立刻跳变而时钟MUX内部带有切换控制逻辑通常会在两个时钟都处于安全电平的时刻完成切换从输出端看不会出现截断的时钟脉冲也不会在切换间隙冒出莫名其妙的毛刺。标准单元库里这类cell的名字一般带CLK_MUX、CLKMUX、ICG_MUX之类的关键词。它通常的电路行为是当sel变化时先屏蔽当前时钟等待目标时钟的下一个有效边沿再打开输出。整个过程需要几个时钟周期不是“啪”一下就能切过去。这个特性直接影响后面我们要怎么约束因为工具如果不知道这是个特殊单元会把它当成普通MUX处理后果就是时钟树上出现多条不存在的传播路径。1.2 时钟切换的核心风险glitch与短脉冲如果直接在时钟路径上用普通组合逻辑MUX选择信号在时钟高电平期间跳变输出时钟可能被切成一个很窄的脉冲。窄脉冲对数字电路是致命的尤其对边沿敏感的触发器来说可能被误判成有效时钟沿也可能导致触发器亚稳态。更麻烦的是这种问题在仿真里可能不明显因为仿真模型对毛刺的响应和真实硅片有差异很多glitch问题要等到流片回来才能暴露。另一个风险是切换过程中时钟短暂丢失。即使使用glitch-free时钟MUX切换也不是瞬间完成的输出端可能需要等待新的时钟源稳定后才恢复输出。在这段安静窗口里寄存器的时钟可能少打一拍或几拍。如果设计没有考虑切换窗口就会导致流水线状态错乱、计数器丢数。这个问题不是单单靠SDC能完全解决的但错误的约束会让工具把时钟树修得过于乐观或过于悲观掩盖掉真实的切换时序风险。所以从约束层面看时钟MUX真正需要回答的问题有两个第一切换完成后最终输出时钟和输入时钟之间的相位/延迟关系是什么第二切换进行中两条输入时钟路径是否会被同时分析。前者靠时钟定义解决后者靠时钟组互斥关系解决。2. 时钟MUX时序约束的整体思路与方案选型2.1 先分场景同步切换与异步切换写约束之前第一步不是找命令而是分清切换场景。场景分错后面写什么都白搭。最常见的一类是两个时钟来自同一个PLL的不同分频输出或者共享同一个参考源。这种情况下两个时钟可能有确定的相位关系比如一个是另一个的二分频或者两个时钟边沿对齐。这类切换可以算同步切换约束重点是让工具知道两个时钟不会同时传播但不能粗暴地把它们设成完全无关的异步时钟否则工具可能会忽略它们之间本身存在的一些固定关系。另一类是来自不同PLL的异步时钟。比如CPU功能时钟来自PLL_A测试时钟来自外部晶振直接分频或者两个独立PLL分别给不同模块供电切换时两个时钟源之间没有任何相位关系。这类切换必须按异步时钟处理工具不能对跨时钟路径做任何时序分析CTS也不能试图让两条时钟树在某一点对齐。还有一个容易被忽略的维度是MUX的位置。如果MUX在整个时钟树根部后面挂着一大片时钟缓冲器那约束影响的是整个时钟树的生成方式如果MUX在某个叶子单元旁边影响范围就小很多。位置不同对CTS的影响范围和约束写法都不太一样。我在项目中习惯先拿时钟结构图把MUX标出来看它在clock tree里的层级再决定用哪种约束组合。2.2 约束工具的核心选择set_clock_groups vs set_false_path vs set_case_analysis很多工程师一看到跨时钟路径就说“设false path”这是最大的误区。set_false_path能做的只是告诉STA忽略某条路径但它没有告诉工具这两个时钟互斥。CTS工具在做时钟树综合时还是会尝试平衡经过MUX的两条输入时钟路径结果就是两个时钟域都被插入大量延迟时钟树面积和功耗明显增加而且切换瞬间的时序行为完全没被约束到。真正合理的做法是用set_clock_groups。这个命令是专门用来描述时钟组之间关系的它分几种类型logically_exclusive表示两个时钟在逻辑上互斥不会同时有效physically_exclusive表示两个时钟在物理上不会同时存在比如测试时钟和功能时钟asynchronous表示两个时钟之间没有确定的相位关系按异步处理。对时钟MUX来说logically_exclusive是最常使用的类型如果两个时钟本来就异步可以在互斥基础上再叠加异步属性。set_case_analysis是另一个容易和set_clock_groups混淆的命令。它用来固定某个port或pin的逻辑值比如test_mode1、scan_en0。当MUX的选择信号来自一个固定模式信号时用set_case_analysis让工具只看被选中的那一支时钟。这里有个关键区别对于运行中可以动态切换的时钟绝对不能set_case_analysis把sel固定死否则工具只会看到一条时钟路径另一条完全失控。这三种命令的关系我用一句话总结set_case_analysis负责固定静态选择set_clock_groups负责描述动态互斥set_false_path只负责忽略具体路径不能用来替代前两者。项目里如果看到有人只加set_false_path没加set_clock_groups基本可以判断这条时钟MUX约束是不完整的。3. 具体约束写法与实操步骤3.1 定义好时钟分清生成时钟和功能时钟写时钟MUX约束的第一步是把每个进入MUX的源时钟定义清楚。假设一个典型场景端口clk_pll0和clk_pll1分别接两个PLL输出MUX输出接到CPU时钟网络。基础SDC长这样create_clock -name pll0_clk -period 10.0 [get_ports clk_pll0] create_clock -name pll1_clk -period 8.0 [get_ports clk_pll1]这只是定义了两个主时钟。问题在于如果到这里就结束工具穿过MUX分析时会把两个主时钟都传播到MUX输出端于是MUX输出端同时存在pll0_clk和pll1_clk两套时钟。这个状态本身不是大问题只要后面正确设置互斥关系STA能理解它们不会同时存在。但如果想让CTS和时钟树报告更清晰我建议在MUX输出端额外定义生成时钟。create_generated_clock -name cpu_clk_pll0 \ -source [get_ports clk_pll0] [get_pins u_clkmux/Z] -add create_generated_clock -name cpu_clk_pll1 \ -source [get_ports clk_pll1] [get_pins u_clkmux/Z] -add注意这里用-add表示同一个pin上存在两个生成时钟。两个生成时钟的源分别是pll0_clk和pll1_clk。加上之后MUX输出端就有明确的“两选一”语义cpu_clk_pll0代表切到PLL0时的实际时钟cpu_clk_pll1代表切到PLL1时的实际时钟。后面做时钟互斥、做CTS report都能直接看到这两个生成时钟排查问题方便很多。这里有个细节要提醒如果MUX输入时钟存在分频或倍频关系需要在create_generated_clock里加-divide_by或-multiply_by参数。实际项目中很多事故就是生成时钟频率写错导致后面时序预算全错。写完之后务必交叉检查报告确认每个生成时钟的频率、边沿、source latency都符合预期。3.2 时钟切换路径的约束set_clock_groups与互斥关系定义好时钟之后核心动作就是设置互斥关系。对于上面这个例子两个PLL输出通过MUX选择通常它们本身就是异步的而且逻辑上不会同时有效。最稳妥的写法是set_clock_groups -logically_exclusive \ -group {cpu_clk_pll0} \ -group {cpu_clk_pll1}如果两个时钟源之间完全没有相位关系也可以写成set_clock_groups -asynchronous \ -group {cpu_clk_pll0} \ -group {cpu_clk_pll1}很多资深工程师会问到底用logically_exclusive还是asynchronous我的经验是只要这两个时钟是通过时钟MUX互斥切换的logically_exclusive是必须的因为工具必须明白“同一时刻只有一组生效”。如果两个时钟还来自不同PLL、无法保证相位关系可以再增加asynchronous约束。有些旧版本工具不支持同时写两种类型那就分两条set_clock_groups命令叠加。反正目的是让STA和CTS同时感知到互斥与异步两层关系。还有一种常见做法是只对MUX的两个输入主时钟设置互斥而不在MUX输出端定义生成时钟set_clock_groups -logically_exclusive \ -group {pll0_clk} \ -group {pll1_clk}这种写法也能工作但排查问题的时候report_clock_tree里显示的是穿过MUX的主时钟名称不够直观。我更推荐“生成时钟主时钟互斥”双保险。尤其在CTS阶段生成时钟名字明确后工具能更准确判断哪个是目标时钟哪个是被屏蔽的时钟。3.3 不要忽略MUX选择端的约束set_case_analysis的使用与常见错误再来聊set_case_analysis。很多设计里时钟MUX的选择信号不是运行时动态切换的控制寄存器而是某个测试模式信号或配置信号。这时候需要针对不同模式分别做约束。举个典型例子scan模式下功能时钟被MUX切换成scan_clk选择信号是scan_mode。那么在功能模式SDC里要固定scan_mode为0set_case_analysis 0 [get_ports scan_mode]在测试模式SDC里固定scan_mode为1set_case_analysis 1 [get_ports scan_mode]这样做的好处是功能模式STA不会去看scan_clk相关的路径测试模式STA不会去看功能PLL相关的路径两边都干净。但是注意set_case_analysis的适用范围是“该信号在设计运行期间取值固定”不是“我们只想暂时让它等于某个值”。如果sel信号来自寄存器并且在运行中会被软件改写那么绝对不能set_case_analysis否则等于把MUX之外的时钟路径从工具眼前抹掉了。我在项目里见过最典型的错误是某模块有两个时钟动态切换其中一个时钟路径hold timing很难收敛工程师为了签核通过直接在sel信号上set_case_analysis固定选了一路时钟。结果CTS阶段另一路时钟树完全没长回片后一切换整个模块时钟都不工作。这种错误比不加约束更隐蔽因为前仿真和STA都显示正常只有真实硅片才会表现出来。所以写MUX选择端约束时一定要回到电路设计意图。先问自己这个sel信号在芯片运行中会不会变会变就不能用case analysis不会变才可以用。4. 时钟MUX约束后的验证与常见问题排查4.1 时钟树综合阶段的行为检查SDC写完之后不要急着跑完整个后端流程先花十分钟检查约束是否被工具正确理解。CTS之后第一件事是打开clock tree报告专门看时钟MUX相关节点。重点检查三件事第一MUX输出端是否存在预期的生成时钟第二两条输入时钟路径是否同时出现在clock tree中第三时钟树深度是否异常。如果发现MUX两个输入上都长了长串的buffer链说明工具没有真正识别互斥关系还在试图平衡两条时钟路径。这时候要回头看set_clock_groups是不是写在了正确的view或mode里或者被其他约束覆盖了。常用的检查命令大概是这样的report_clock_groups report_clock_tree -detail report_clock_utilizationreport_clock_groups能直接列出各组时钟之间的互斥/异步关系一眼就能看出有没有漏设。report_clock_tree -detail里可以看到每个时钟到达MUX输入pin的插入延迟。理想情况下如果两个时钟被正确识别为互斥工具不会对这两条输入路径做等长处理。如果延迟差得离谱要确认是不是某个时钟还经过了别的逻辑而不仅仅是MUX。另外CTS之后还要检查时钟MUX单元本身是否被正确布局和布线。某些工艺库里的CLKMUX单元有专门的布局要求如果工具把它当成普通组合逻辑单元随意摆放可能引入额外的寄生电容导致切换边沿变差。这个在advanced node项目里尤其明显。4.2 仿真与形式验证阶段需要额外注意什么时序约束不只是给STA用的。时钟MUX的互斥关系如果只写在SDC里RTL仿真和形式验证阶段也需要配套处理否则功能验证场景和后端实现场景会不一致。在仿真阶段时钟MUX的行为模型必须和实际电路一致。如果使用普通MUX模型做切换仿真可能会出现虚假glitch导致验证人员误报bug反过来如果使用过于理想化的模型又可能掩盖真实的切换窗口问题。最好在仿真环境里使用库提供的glitch-free模型同时给sel信号加合理的时序约束避免在禁止窗口内翻转。形式验证方面一般情况下综合工具会保留时钟MUX结构CLKMUX单元在网表里很容易识别。但如果你在综合阶段用了不合理的set_case_analysis比如固定了sel信号工具可能把MUX优化成一根导线逻辑等价性检查会通过可真实网表里MUX已经不在了切换功能可能已经丢失。因此在LEC/Formality之后要专门检查网表里是否还存在CLKMUX单元并且确认set_case_analysis只加在了该加的模式端口上。还有一个容易被忽视的点是CDC验证。如果两个时钟来自不同PLL、切换是异步的那么sel信号的跨时钟同步逻辑必须经过CDC检查。很多设计把注意力放在CTSD和STA上忽略了MUX切换逻辑本身可能存在的CDC风险。正确做法是在CDC工具里把sel信号标记为跨时钟域控制信号确保它是经过同步器后才能真正影响MUX选择。4.3 常见问题与排查速查表这里我整理一张速查表都是实际项目中碰到的典型问题可以直接拿去对照排查。问题现象可能原因排查方法解决建议STA报告出现大量跨时钟路径时钟MUX两路时钟没有设置互斥关系report_clock_groups检查时钟组关系对MUX输出生成时钟加set_clock_groups -logically_exclusiveCTS报告两条输入时钟树都被平衡只写了set_false_path没有set_clock_groupsreport_clock_tree看MUX输入端延迟补上set_clock_groups重新跑CTS某个时钟完全没有时序路径sel信号被set_case_analysis固定检查case analysis设置确认该信号是否真实固定动态切换不能设case analysis时钟切换仿真出现意外毛刺仿真模型用了普通MUX检查仿真lib模型换成CLKMUX glitch-free模型回片后切钟挂死MUX切换窗口过大或时钟丢失检查chip level时钟切换测试增加切换保护逻辑约束中不要忽略切换时序hold违规难以收敛互斥时钟组设置错误导致过度悲观查看hold report里的clock pair确认时钟组类型能设logically_exclusive就不要只用asynchronous5. 从真实项目里总结的几点经验时钟MUX的约束看起来只是几行SDC但它背后牵扯到STA、CTS、仿真、形式验证、CDC验证多个环节。我在多个项目里踩过坑之后总结了几条比较实用的经验。第一条时钟MUX约束要在综合前就写好不要等CTS阶段再补。实际操作中如果综合阶段没有正确约束MUX可能被优化成普通组合逻辑或者被插入不必要的buffer。等CTSD阶段再补约束只能修时序修不了结构。所以我在每个项目最开始拿到时钟架构图就会把所有时钟MUX的位置标出来先把生成时钟和互斥关系写到SDC模板里。第二条区分“固定模式选择”和“动态运行时切换”非常重要。测试时钟和功能时钟之间用set_case_analysis加set_clock_groups动态PLL切换只用set_clock_groups。如果把两种场景混为一谈要么过度约束导致另一路时钟没树要么约束不足导致CTS爆炸。第三条不要迷信set_false_path。它只能让STA忽略某条路径却不能改变工具对时钟树结构的理解。真正安全和可维护的写法是把MUX输出端的生成时钟定义清楚然后用set_clock_groups描述互斥关系。这样无论STA、CTS还是ECO阶段任何人来看SDC都能一眼看明白设计意图。最后分享一个我个人的小习惯无论SDC是不是我写的拿到网表后的第一件事就是跑report_clock_groups和report_clock_tree -detail扫一遍有没有任何时钟路径穿过了没有定义互斥的MUX。这个习惯救了我很多次。时钟MUX的约束不难难的是每次都不漏。
返回列表