
1. 时序路径分析到底在解决什么问题1.1 从一颗芯片跑不起来说起很多刚接触数字后端或者静态时序分析的朋友第一次听到时序路径这四个字的时候脑子里其实是没有画面的。我当年也是这样觉得这不就是工具跑一跑、出一份报告、看看有没有violation吗直到有一次我负责的一个模块在流片回来后功能异常实验室里怎么调都不对最后定位到的问题竟然是一条看起来完全满足约束的路径——工具报告里它是正的slack但实际芯片在特定工艺角下就是挂了。那次经历让我彻底明白一件事时序路径分析不是跑工具而是理解信号在芯片里怎么走、走得够不够快、有没有走错路。PrimeTime这类工具只是把你的意图翻译成数字真正决定成败的是你对路径本身的理解。所谓时序路径说白了就是信号从一个时序元件触发器、锁存器出发经过一堆组合逻辑最终到达另一个时序元件或者到达芯片的输入输出端口。这条路上信号需要在一个时钟周期内完成传播并且要满足建立时间setup和保持时间hold的要求。听起来简单但一颗现代SoC里可能有几百万甚至上千万条这样的路径每一条都有自己的脾气。1.2 为什么上篇要先讲清楚路径分类我特意把时序路径分析分成上下两篇来写是因为我发现很多人一上来就去抠PrimeTime的命令、去调SDC约束结果连路径分几类都说不清楚。这就像学开车先研究发动机拆解方向就偏了。时序路径按照起点和终点的类型标准分法有四类路径类型起点终点典型场景输入到寄存器芯片输入端口触发器D端外部信号进入芯片被采样寄存器到寄存器触发器CLK端触发器D端芯片内部最核心的路径寄存器到输出触发器CLK端芯片输出端口内部信号送出芯片输入到输出芯片输入端口芯片输出端口纯组合逻辑穿透这四类路径的约束方式、分析重点、常见问题都不一样。比如输入到寄存器的路径你要关心的是外部器件的输出延迟和板级走线寄存器到寄存器的路径你要关心的是时钟树 skew 和组合逻辑深度输入到输出的纯组合路径很多时候是异步接口或者测试逻辑约束起来最容易出错。提示很多人做STA时只盯着寄存器到寄存器的路径因为这类路径数量最多、报告最直观。但实际上输入输出相关的路径往往是芯片和外部世界交互的瓶颈出问题的时候最难查。1.3 一条路径的生命周期我想用一个具体的例子把路径的生命周期串起来。假设你有一颗MCU芯片里面有一个SPI接口外部主机通过SPI把数据送进来芯片内部要把这个数据存到寄存器里再做处理。这条路径的完整生命周期是这样的外部主机的数据在某个时钟沿发出经过PCB走线到达芯片引脚穿过IO pad进入芯片内部经过输入缓冲器再经过一些同步逻辑可能是两级触发器做同步最后到达目标寄存器的D端。整个过程必须在一个SPI时钟周期内完成否则数据就采错了。这里面每一个环节都有延迟PCB走线有延迟pad有延迟缓冲器有延迟组合逻辑有延迟触发器的建立时间也是延迟的一部分。STA要做的就是把这些延迟全部算清楚然后和时钟周期做比较看有没有余量。而PrimeTime做的事情是把这些延迟用工艺库里的模型算出来。工艺库会告诉你在某个工艺角、某个电压、某个温度下一个缓冲器的延迟是多少皮秒。工具把这些数字累加起来再和你的约束做对比最后告诉你slack是正还是负。1.4 为什么时序分析必须和SDC绑在一起讲单独讲时序路径不讲SDC等于讲了一半。SDCSynopsys Design Constraints是你告诉工具我希望这条路径跑多快的语言。没有SDC工具根本不知道你的时钟周期是多少、输入输出延迟是多少、哪些路径是假的不用管。我见过太多项目SDC写得一塌糊涂然后抱怨工具报告不准。实际上工具很无辜它只是忠实地执行了你写的约束。你写create_clock -period 10它就按10ns分析你忘了写set_false_path它就把异步路径也当同步路径分析然后报一堆假violation。所以这篇上篇我会把重点放在路径的本质、分类、以及约束如何映射到路径上。下篇再讲具体的PrimeTime操作、报告解读、以及修时序的实战技巧。这个顺序不能反反了就是空中楼阁。2. 建立时间与保持时间时序分析的两把尺子2.1 建立时间数据不能来得太晚建立时间的物理含义用一句话说就是在时钟有效沿到来之前数据必须已经稳定一段时间。这个一段时间就是触发器的setup time。为什么需要这个时间因为触发器内部是由传输门、反相器、锁存器结构组成的时钟沿到来时内部节点需要时间来建立正确的电平。如果数据来得太晚时钟沿已经过了触发器内部还没来得及把数据锁住就会采到错误的值或者进入亚稳态。用生活类比你赶火车火车开车前5分钟停止检票这5分钟就是setup time。你必须在停止检票之前到达检票口否则就上不了车。数据到达触发器D端的时间必须比时钟沿早至少setup time。建立时间的检查公式简化版是这样的T_launch T_clk2q T_comb T_setup T_capture T_period - T_skew其中T_launch是发射时钟的到达时间T_clk2q是触发器时钟到输出的延迟T_comb是组合逻辑延迟T_setup是建立时间T_capture是捕获时钟的到达时间T_period是时钟周期T_skew是时钟偏斜。这个公式看起来复杂但核心思想很简单数据到达时间 建立时间 时钟捕获时间 一个周期。如果左边大于右边setup violation就出现了。2.2 保持时间数据不能走得太早保持时间的含义是在时钟有效沿到来之后数据必须继续保持稳定一段时间。这个一段时间就是hold time。为什么因为触发器在时钟沿附近有一个采样窗口在这个窗口内数据不能变化。如果数据变化太快时钟沿刚过数据就变了触发器可能锁存到新数据也可能锁存到旧数据结果不确定。还是用火车类比你上车之后火车开动之前你不能马上跳下车。如果你在火车刚启动时就跳下去可能会摔伤。数据在时钟沿之后必须保持hold time否则触发器内部状态会乱。保持时间的检查公式T_launch T_clk2q T_comb T_capture T_hold注意这里没有时钟周期项。因为hold检查是在同一个时钟沿附近进行的和周期无关。这也是为什么hold violation不能通过降低时钟频率来修复——你降频setup会变好但hold纹丝不动。2.3 两者的本质区别与修复思路我把setup和hold的区别总结成一张表这个表我建议每个做STA的人都背下来维度SetupHold检查时机时钟沿之前时钟沿之后与周期关系周期越长越容易满足与周期无关数据路径要求不能太慢不能太快常见修复手段加缓冲器、降频、优化逻辑加延迟单元、调整时钟树工艺角敏感度慢角SS最差快角FF最差温度影响高温更差部分工艺低温更差部分工艺这里有一个非常容易踩的坑很多人以为setup和hold是对称的修setup的方法反过来就是修hold。大错特错。修setup是让数据路径变快修hold是让数据路径变慢方向完全相反。如果你在一条路径上既修setup又修hold很可能按下葫芦浮起瓢。我个人的经验是先修hold再修setup。因为hold violation是硬伤它不会因为你降频而消失而且hold violation往往集中在时钟树skew大的地方。把hold修干净了再去优化setup心里才有底。2.4 工艺角与温度对两把尺子的影响做STA不能只跑一个工艺角。标准做法是跑多个cornerSS慢NMOS慢PMOS、FF快NMOS快PMOS、SF、FS再加上温度变化。对于setup最差的是SS corner加上高温或者在某些先进工艺下是低温取决于温度反转效应。因为晶体管慢延迟大数据来得晚。对于hold最差的是FF corner加上低温因为晶体管快数据来得太早。这里有个细节先进工艺下温度反转效应越来越明显。在28nm以下高温下晶体管反而更快因为载流子迁移率虽然下降但阈值电压下降得更快。所以你不能想当然地认为高温一定差必须看工艺库的说明。注意跑STA时一定要确认你的corner设置和项目要求一致。我见过有人只跑了TT corner就签核结果芯片回来在SS下挂了一片。TT只是典型值不是保证值。3. 时钟树与路径延迟的纠缠关系3.1 时钟偏斜如何偷走你的余量时钟偏斜clock skew是同一个时钟信号到达不同触发器的时间差。理想情况下所有触发器应该在同一时刻收到时钟沿但现实中时钟树有延迟走线长度不同负载不同到达时间必然有差异。Skew对setup和hold的影响是相反的。对于setup如果捕获时钟比发射时钟晚到正skew相当于给了数据更多时间setup更容易满足如果捕获时钟早到负skewsetup更紧张。对于hold正skew会让hold更紧张负skew会让hold更宽松。这就是为什么时钟树综合CTS是时序收敛的关键一步。CTS的目标是让skew尽可能小同时控制插入延迟insertion delay。但skew不可能为零你只能把它控制在一个合理范围内。我做过一个项目时钟树没做好skew到了300ps结果hold violation满天飞修了整整两周。后来重新做CTS把skew压到80ps以内hold问题少了一大半。这个教训告诉我时序问题很多时候不是逻辑问题是时钟问题。3.2 公共路径悲观度移除CPPR做STA时工具默认会考虑最悲观的情况发射时钟走最慢的路径捕获时钟走最快的路径。但实际上如果两条时钟路径有一段是公共的这段公共路径的延迟变化应该被抵消掉不应该同时算最慢和最快。这就是CPPRCommon Path Pessimism Removal的作用。工具会找到发射和捕获时钟的公共节点把这段公共路径的悲观量移除。CPPR对setup和hold都有影响通常能救回几十到几百皮秒的余量。在先进工艺下CPPR的影响越来越大因为时钟树越来越复杂公共路径越来越长。我在看PrimeTime报告时一定会关注CPPR的数值。如果CPPR很大说明时钟树公共路径很长这时候要小心公共路径上的任何变化都会同时影响发射和捕获可能导致时序突然恶化。3.3 时钟不确定性给时序留的安全垫时钟不确定性clock uncertainty是你在SDC里人为加的一个余量用来覆盖时钟抖动jitter、时钟树偏差、以及各种你无法精确建模的因素。set_clock_uncertainty -setup 0.15 [get_clocks clk_main] set_clock_uncertainty -hold 0.08 [get_clocks clk_main]这个值怎么定没有绝对标准。一般来说setup uncertainty要覆盖PLL jitter加上时钟树skew的预估偏差hold uncertainty主要覆盖时钟树skew。在先进工艺下setup uncertainty通常在100ps到200ps之间hold在50ps到100ps之间。但我要提醒一句uncertainty不是越大越好。你加得太大工具会过度优化面积和功耗都上去了加得太小芯片回来可能因为抖动挂掉。这个值需要和时钟设计团队、PLL团队一起确认不能拍脑袋。3.4 生成时钟与分频时钟的路径分析实际芯片里不可能只有一个时钟。PLL输出经过分频、倍频、门控会产生一大堆衍生时钟。这些时钟之间的路径分析是STA里最容易出错的地方。比如一个二分频时钟它的周期是主时钟的两倍。工具需要知道这两个时钟的相位关系才能正确分析跨时钟域路径。如果你只写了create_clock而没写create_generated_clock工具会把它们当成两个无关时钟然后要么报一堆假violation要么漏掉真violation。create_clock -name clk_ref -period 10 [get_ports clk_in] create_generated_clock -name clk_div2 -source [get_ports clk_in] -divide_by 2 [get_pins div_reg/Q]生成时钟的定义必须准确反映电路的实际行为。我见过有人把分频比写错结果整条路径的约束全错了工具报告看起来很正常实际上完全不可信。4. SDC约束如何映射到时序路径上4.1 时钟定义一切约束的起点SDC里最重要的命令就是create_clock。它定义了时钟的周期、波形、以及作用点。没有时钟定义工具不知道按什么频率分析。create_clock -name core_clk -period 5.0 -waveform {0 2.5} [get_ports clk_core]这条命令告诉工具core_clk周期5ns上升沿在0ns下降沿在2.5ns作用在clk_core端口上。工具会基于这个定义自动推导出所有时序路径的检查时间。但这里有个常见错误很多人把时钟定义在端口上但端口后面还有IO缓冲器、时钟门控单元实际到达触发器的时钟已经变了。正确的做法是把时钟定义在时钟树的根节点或者用create_generated_clock描述经过门控后的时钟。4.2 输入输出延迟芯片与外部世界的契约set_input_delay和set_output_delay是描述芯片边界时序的关键约束。它们告诉工具外部信号相对于时钟沿提前或延后多少时间到达/离开芯片引脚。set_input_delay -clock core_clk -max 2.0 [get_ports data_in] set_input_delay -clock core_clk -min 0.5 [get_ports data_in] set_output_delay -clock core_clk -max 1.5 [get_ports data_out] set_output_delay -clock core_clk -min 0.3 [get_ports data_out]这些值从哪来从系统级时序预算来。比如你的芯片要和一颗DDR颗粒通信DDR的建立保持时间要求、PCB走线延迟、芯片IO延迟全部加起来反推回芯片引脚就是input/output delay。我见过最离谱的情况是有人随便填了个set_input_delay 0然后芯片回来发现接口完全跑不通。输入输出延迟是芯片和板级的接口契约必须和系统团队对齐不能自己瞎填。4.3 时序例外不是所有路径都要按周期检查芯片里有大量路径不需要按最严格的方式检查。比如异步FIFO的跨时钟域路径、配置寄存器路径、测试逻辑路径。这些路径如果不管工具会报一堆假violation浪费你大量时间。时序例外主要有三种set_false_path完全不用检查的路径比如异步复位路径set_multicycle_path允许多个周期完成的路径比如慢速配置逻辑set_max_delay/set_min_delay直接指定延迟要求忽略时钟周期set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b] set_multicycle_path 2 -setup -from [get_pins slow_reg/CK] -to [get_pins dest_reg/D] set_multicycle_path 1 -hold -from [get_pins slow_reg/CK] -to [get_pins dest_reg/D]注意multicycle的hold设置如果你setup设了2个周期hold通常要设1个周期否则工具会在错误的位置检查hold。这个细节很多人会漏。4.4 约束的优先级与冲突处理SDC约束是有优先级的。当多条约束作用于同一条路径时工具按优先级决定用哪条。一般来说set_false_path优先级最高set_max_delay/set_min_delay次之set_multicycle_path再次默认的时钟周期检查优先级最低但实际项目中约束冲突很常见。比如你先写了set_false_path后来又写了set_max_delay工具会怎么处理不同工具行为可能不同。我的建议是写完约束后一定要用report_timing_requirements检查确认每条路径实际生效的是哪条约束。提示PrimeTime里可以用report_exceptions命令查看所有时序例外的生效情况。这个命令我每次签核前必跑能发现很多隐藏的约束冲突。5. 从RTL到GDSII时序路径分析的介入时机5.1 综合阶段时序的第一次体检很多人以为时序分析是后端的事其实从综合阶段就开始了。综合工具Design Compiler会根据你的SDC约束把RTL映射成门级网表同时做初步的时序优化。这个阶段的时序分析特点是时钟树还没建线延迟用统计模型估算。所以综合报告的slack只能作为参考不能作为签核依据。但综合阶段能发现大的架构问题比如组合逻辑太深、时钟周期设得太紧。我在综合阶段会重点关注WNS最差负slack和TNS总负slack。如果WNS差得离谱比如-2ns那说明架构有问题赶紧回去改RTL别指望后端能救回来。5.2 布局布线阶段时序分析的主战场到了布局布线PR阶段时钟树建好了线延迟用实际提取的寄生参数计算时序分析才真正有意义。这个阶段又分几个子阶段布局后post-place单元位置确定线延迟还是估算CTS后post-CTS时钟树建好skew确定hold开始检查布线后post-route所有线延迟用实际寄生参数时序最准确每个阶段的时序报告都要看但重点不同。post-place主要看setup的大致趋势post-CTS重点修holdpost-route做最终签核。我个人的习惯是post-CTS后一定要把hold修干净。因为到了post-route线延迟变化可能让hold变差如果post-CTS时hold余量就不够post-route会更惨。5.3 签核阶段多corner、多模式、OCV签核signoff是时序分析最严格的阶段。要跑多个corner、多个模式功能模式、测试模式、低功耗模式还要考虑OCVOn-Chip Variation。OCV是指同一颗芯片上不同位置的晶体管特性有差异。工具通过在数据路径和时钟路径上加不同的derate因子来模拟这种差异。比如时钟路径加5%的延迟数据路径减5%模拟最坏情况。set_timing_derate -early 0.95 -late 1.05先进工艺下OCV越来越重要甚至要用AOCVAdvanced OCV或POCVParametric OCV根据路径深度和位置动态计算derate。5.4 时序收敛的迭代过程时序收敛不是一次性的是反复迭代的过程。典型流程是跑STA - 看报告 - 找violation - 修逻辑或调约束 - 再跑STA。这个循环可能重复几十次。我做过最复杂的一个项目时序收敛花了三个月跑了上百轮STA。关键是要有系统的方法先分类violation再批量修复不要一条一条修。比如把所有hold violation按时钟域分组发现某个时钟域的hold问题特别多那就去查那个时钟树的skew是不是太大了。把所有setup violation按逻辑深度排序发现前10条路径占了80%的负slack那就集中优化这10条。6. 那些年我在时序路径上踩过的坑6.1 假路径没设工具报了一堆狼来了刚入行的时候我做了一个异步FIFO跨时钟域路径没设false path。结果PrimeTime报了几百条violation我吓得连夜加班一条一条看发现全是跨时钟域的路径。后来师傅告诉我异步路径必须设false path否则工具会按同步路径分析报出来的全是假violation。但设false path也要小心。我曾经为了省事把整个时钟域之间都设了false path结果漏掉了一条真正需要检查的路径——那是一条从快时钟域到慢时钟域的控制信号虽然跨时钟域但经过了同步器需要检查同步器第一级触发器的setup。false path要精确到路径不能图省事一刀切。6.2 时钟定义错了后面全白干有一次我接手一个项目发现时序报告怎么看都不对明明逻辑很简单slack却差得离谱。查了半天发现前任工程师把时钟定义在了分频器的输出上但分频器的输入时钟没定义。工具把分频器输出当成了根时钟周期设成了实际周期的两倍。时钟定义是SDC的根基根基错了上面所有约束都是错的。我现在的习惯是拿到一个新项目第一件事就是画时钟树结构图确认每个时钟的来源、分频比、相位关系然后再写约束。6.3 忽略IO路径芯片回来接口跑不通内部时序全过了签核报告漂漂亮亮结果芯片回来SPI接口跑不通。查了半天发现是input delay设得太乐观实际板级走线延迟比预估的大导致建立时间不够。IO路径的时序预算必须和系统团队一起做不能自己拍脑袋。PCB走线延迟、连接器延迟、外部器件的输出延迟全部要算进去。而且要做最坏情况分析不能只用典型值。6.4 过度依赖工具不理解路径本质工具报告说slack是正的你就信了我见过太多工具说没问题芯片回来有问题的案例。工具是基于模型计算的模型有精度限制工艺有波动还有工具根本没建模的因素比如串扰、电源噪声。STA报告是参考不是保证。真正靠谱的做法是理解每条关键路径的物理意义知道它的延迟主要来自哪里有没有优化空间有没有风险点。工具告诉你是什么你要知道为什么。6.5 约束写得太紧面积功耗爆炸为了保险把时钟周期设得比实际需求紧uncertainty加得很大derate设得很悲观。结果工具为了满足约束拼命加缓冲器、加驱动面积涨了30%功耗涨了50%芯片成本大幅上升。约束要反映真实需求不是越紧越好。时序收敛的目标是满足规格不是跑出最漂亮的报告。过度约束是新手最容易犯的错误之一。7. 写给正在学时序分析的朋友时序路径分析这件事工具命令可以一周学会但真正理解路径、理解约束、理解工艺需要几年积累。我到现在也不敢说完全精通每次遇到新工艺、新架构都要重新学习。如果你刚开始学我的建议是先不要急着学PrimeTime命令先把一条路径从发射到捕获的完整过程画出来。画十遍画到闭着眼睛都能画出来。然后拿一个简单的设计手动算一遍setup和hold再和工具报告对比。你会发现工具算的和你算的差距在哪里为什么有差距。SDC约束也是同理。不要抄别人的脚本要理解每一行命令对应的是哪条路径、哪个时钟、哪个需求。抄来的约束出了问题你根本不知道怎么查。最后说一句时序分析是芯片设计的守门员你放过去的每一条violation都可能变成流片后的一个bug。认真对待每一条路径敬畏每一皮秒的余量。这个态度比任何工具技巧都重要。下篇我会具体讲PrimeTime的实操、报告解读、以及修时序的实战技巧。如果你把这篇的路径分类、setup/hold原理、SDC映射关系搞清楚了下篇会轻松很多。