ARTICLE DETAIL

资讯详情

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

综合阶段SDC约束实战指南:时钟、IO、时序例外全梳理

综合阶段SDC约束实战指南:时钟、IO、时序例外全梳理 做芯片前端设计这些年综合synthesis这一步有一个特别容易被低估的环节SDC约束。很多人写RTL的时候逻辑很严谨一到综合阶段SDC文件却写得比较随意结果run完发现时序一堆violation或者面积大得离谱最后查来查去问题的根源往往是约束本身就没写对。这篇笔记是芯片开发学习笔记系列的第十九篇专门把综合阶段最常用、也最容易踩坑的SDC约束系统梳理一遍。我会按时钟约束、I/O约束、时序例外三大块展开结合实际项目里的经验聊聊每条命令背后的逻辑和常见误区。1. 综合阶段的约束到底在约束什么1.1 综合工具眼中的设计综合工具比如DC、Genus把RTL转成门级网表的时候并不是随便拿一个目标工艺库就开始映射。它需要先知道两件事这个设计要跑多快以及外部环境长什么样。SDC就是用来回答这两个问题的。工具基于你提供的时钟周期、输入输出延时、时序例外这三类信息逐条计算每一条路径的约束是否满足然后选择合理的cell类型和连线策略。打个比方综合工具就像一个装修队RTL是户型图工艺库是建材清单而SDC就是你和装修队签的施工合同——什么时候必须完工、哪些位置可以不用管、材料预算控制在多少。合同写得不清楚装修队要么拼命加钱面积失控要么工期延误时序violation要么交付的东西根本不能用。SDC这个合同写得越准确工具的行为就越可预期。1.2 约束不全的三种典型翻车现场第一种是时钟没定义全。工具会把所有寄存器当成同一个时钟域来做时序分析结果跨时钟域的路径因为没有正确的约束被报出大量violation。综合工具为了修复这些根本不是真实问题的violation会拼命插入buffer甚至调整逻辑结构面积和功耗直接飙升最后你拿着report_qor一头雾水。第二种是input/output delay没有设置。工具默认I/O路径的外部延时为0它会高估或者低估实际路径的情况。综合出来的网表在片内看可能很好一旦接到外部芯片或者真实系统上时序完全对不上只能重新综合返工。这里要特别强调SDC不只是让工具能用它是设计意图的一部分约束写错等于设计意图表达错误。第三种是false path和multicycle path漏写。低速接口、异步复位、跨时钟域信号这些路径如果被当成普通单周期路径去优化工具会浪费大量面积去修根本不存在的时序问题真正需要优化的路径反而得不到资源。这也是为什么时序例外约束必须和时钟约束一样认真对待。1.3 学习SDC的抓大放小策略很多初学者一上来就想把所有SDC命令都弄懂其实没必要。综合阶段真正高频使用的SDC命令不超过15条核心就三大块时钟约束、I/O约束、时序例外。先把这三块吃透再去看set_clock_latency、set_clock_transition这些细节命令。我见过不少新人把精力花在研究冷门选项上结果主时钟定义和时钟分组这种最基础的东西反而出错。后面我就按这个优先级顺序展开先讲时钟因为它是一切时序分析的地基。2. 时钟约束整个SDC文件的绝对核心2.1 create_clock主时钟的定义规范定义主时钟是SDC的第一步也是最不能出错的一步。基本语法create_clock -name clk_sys -period 10 -waveform {0 5} [get_ports clk_sys]-period的单位是ns10ns对应100MHz。-waveform指定上升沿和下降沿发生的时刻{0 5}表示0ns上升、5ns下降也就是50%占空比。如果写{0 3}那就是30%占空比适用于某些特殊的接口时钟。这里有一个关键点-name要不要写我的建议是尽量写。不写的话工具默认用时钟端口名作为clock名一旦这个时钟经过buffer或者内部层次结构报告里的名字会变得很乱后面写约束和看timing报告都不方便。-name尽量做到全项目统一命名规范比如clk_sys、clk_ahb、clk_usb一眼能看出用途。还有一个人人都容易踩的坑把时钟定义在内部pin上而不是顶层port上。比如直接在某个寄存器输出端create_clock。这种做法会让工具认为这个点是时钟源严重影响后续CTS时钟树综合阶段的处理。正常做法是时钟从port进入芯片后内部模块生成的时钟统一用create_generated_clock来描述主时钟永远定义在芯片的物理输入端口上。2.2 create_generated_clock分频时钟的正确写法芯片内部几乎都有分频器、倍频器这些内部生成的时钟不能再用create_clock必须用专门的命令create_generated_clock -name clk_div2 \ -source [get_ports clk_sys] \ -divide_by 2 \ [get_pins u_clk_gen/div2_reg/Q]-source必须指向主时钟的port或者pin-divide_by 2表示二分频工具会自动推导分频后时钟的周期和相位关系。如果分频链路里有组合逻辑需要用-combinational等选项来准确描述或者检查分频点的选择是否合适。这里有个很常见的错误有些工程师偷懒直接把分频后的时钟用create_clock定义。这样工具就不知道这个时钟和主时钟之间的相位关系时序分析时会把generated clock和master clock之间的路径当成不相关路径来处理后果就是跨时钟域路径的时序检查完全不正确经常出现hold check一片红的奇怪现象。实际项目中我见过太多因为generated clock描述不准确导致hold检查出错的情况排查起来非常折磨人。所以在这条命令上多花一点时间搞清楚source指到哪个pin比反复试错强得多。2.3 set_clock_uncertainty与latency留够余量别自欺欺人综合阶段的时钟并不是理想时钟。set_clock_uncertainty就是告诉工具时钟jitter和skew带来的不确定性set_clock_uncertainty -setup 0.2 [get_clocks clk_sys] set_clock_uncertainty -hold 0.05 [get_clocks clk_sys]setup uncertainty一般比hold大因为setup检查需要考虑时钟jitter对前后两个沿的影响而hold检查主要考察同一个沿附近的skew。实际项目里setup uncertainty通常包含PLL jitter、时钟树skew预算和design margin三部分具体数值要结合工艺和时钟源规格来定。除了uncertainty还有两条配套命令set_clock_latency和set_clock_transition。set_clock_latency -source用来模拟时钟从片外源到芯片引脚的传播延时set_clock_transition用来设置时钟信号的slew。综合阶段没有时钟树信息这两条命令可以让工具对时钟路径的估算更贴近真实情况避免结果过度乐观。必须提醒一句不要为了把时序报告刷干净就把uncertainty设得极小。我见过有同事把0.3的uncertainty改成0.05来过综合结果网表到了后端时钟树一长真实skew远超这个值所有setup全崩最后只能推翻重来。uncertainty是留给物理效应的真实预算不是拿来凑数的参数。2.4 set_clock_groups异步时钟域怎么交代多时钟域设计必须用set_clock_groups把异步时钟明确隔开set_clock_groups -asynchronous \ -group {clk_sys} \ -group {clk_usb} \ -group {clk_pll_fast}这个命令的含义是这些时钟之间的路径不需要做时序检查工具会默认按false path处理。对于真正异步、并且有同步器保护的跨时钟域路径这是完全正确的做法。但要注意如果两个时钟其实有固定的相位关系只是频率不同那么应该按同步时钟处理而不是简单粗暴地设成asynchronous。另外set_clock_groups还有一种模式叫-logically_exclusive适用于通过mux选择的互斥时钟。用错选项会掩盖真实的时序问题尤其是跨时钟域数据靠握手协议而不是同步器来同步的时候贸然设成asynchronous风险极大。还有一个容易被忽略的点复位信号和测试时钟。test_mode、scan_en这些信号在功能综合时应该用set_case_analysis固定住否则工具会同时优化功能路径和测试路径浪费面积。这个问题我们放到时序例外那一节详细讲。3. 输入输出路径约束把外部世界交代清楚3.1 set_input_delay从外部寄存器到芯片内部外部送进来的数据到达芯片pin时相对于时钟沿到底晚了多久需要用set_input_delay来描述set_input_delay -clock clk_sys -max 2.5 [get_ports data_in] set_input_delay -clock clk_sys -min 0.3 [get_ports data_in]-max对应setup检查用到的最大延时控制的是路径变慢的场景-min对应hold检查用到的最小延时控制的是路径变快的场景。两者都要写只写max不写min会让hold分析完全失真。计算方法并不复杂input delay(max) 外部时钟skew 外部寄存器Tco(max) 外部组合逻辑延时(max)。对应地input delay(min) 外部寄存器Tco(min) 组合逻辑延时(min) - 外部时钟skew。这些数值应该来自外部芯片的datasheet和系统级设计文档而不是拍脑袋。举个例子外部芯片的Tco max是1.5ns外部组合逻辑最长1ns时钟skew 0.2ns那么set_input_delay -max 1.5 1.0 0.2 2.7ns。我习惯把这类计算放在一张Excel表里每个pin一行标注每一项数值的来源每次做新项目直接改参数。比在SDC文件里改来改去清晰得多也方便评审的时候解释给同事听。如果你遇到外部接口的时钟和内部时钟不是同一个比如内部跑100MHz外部接口芯片用50MHz那么正确的做法是先创建一个virtual clockcreate_clock -name vclk_ext -period 20 -waveform {0 10}然后基于这个virtual clock去约束I/O延时。virtual clock没有对应的物理端口只服务于I/O路径的时序分析这是处理异频接口的标准套路。3.2 set_output_delay给别人留够时间输出路径的方向是反过来的set_output_delay -clock clk_sys -max 3.0 [get_ports data_out] set_output_delay -clock clk_sys -min 0.5 [get_ports data_out]-output delay max的含义是外部模块的setup time加上时钟skew。工具会保证内部寄存器Q端到输出pin的组合延时加上这个output delay不超过一个时钟周期。output delay max设得越大留给内部路径的时间就越少约束也就越紧。-output delay min对应的是外部模块的hold time需求。我见过不少工程案例里只写了-max忘了写-min结果hold分析时工具默认外部min delay为0等于把外部hold要求完全忽略了。这样的设计到了系统级联调阶段输出数据在外部可能根本采不住。所以写output delay的时候max和min永远是成对出现。3.3 驱动与负载set_driving_cell和set_load除了延时输入输出pin的电气特性也要交代清楚。输入端要告诉工具外部驱动cell是什么类型set_driving_cell -lib_cell INV_X1 [get_ports data_in]输出端要设置负载电容set_load 0.05 [get_ports data_out]综合工具依靠这些信息计算transition time和总负载从而决定内部逻辑的驱动强度。如果输入端的driving cell假设得太强比如默认外部驱动极强综合出来的输入级cell可能尺寸偏小真实系统里外部驱动能力不足信号slew变差后端的时序和功耗都会受影响。这里给一个偷懒但实用的默认值输出端口的set_load可以先按工艺库推荐值来设等后仿真或者时序signoff阶段再根据实际板级负载校准。综合阶段最重要的是让工具别过度优化输出驱动合理的库默认值已经足够。3.4 工艺环境operating condition别漏这是最容易被忽略、但影响最大的环节之一。综合工具需要知道当前设计工作在哪个PVT工艺、电压、温度条件下才能正确计算cell延时。通常综合用SSslow-slow工艺角做setup分析用FFfast-fast做hold分析具体设置方式因工具而异DC里就是set_operating_conditions。综合阶段必须对齐目标工艺角和频率不然综合结果和实际流片性能会差很多。我之前有一次就是因为忘了切operating condition用典型库做setup综合报告看着很干净换到慢速库之后冒出一大堆violation整个迭代周期白白浪费了。现在我的做法是把operating conditions写进综合脚本最前面和SDC文件分开管理每次跑之前先检查当前用的库和条件对不对。4. 时序例外false path、multicycle path的高危操作4.1 set_false_path能不用就尽量不要用set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b] set_false_path -from [get_ports rst_n] -to [all_registers]异步复位信号、真正异步的跨时钟域路径、测试模式下的部分逻辑确实可以设false path。但这里有一个非常关键的原则你设的false path必须是你真正理解、并且确认不需要时序检查的路径。我见过最惨的项目事故有人在复位释放路径上设置了false path但实际上复位是异步释放、同步采样的释放路径需要满足recovery/removal检查。流片回来后芯片在某些电压温度条件下起不来排查了很久才发现是这条false path的锅。所以拿不准的时候宁可不设先跑一遍让工具报violation根据violation的分布来判断哪些路径应该被例外出来这比一开始就随手写false path安全得多。4.2 set_multicycle_path多周期路径的hold坑有些路径天生就是多周期完成的比如慢速配置寄存器之间的数据通路、某些协议握手信号。这类路径要用set_multicycle_pathset_multicycle_path -setup 2 \ -from [get_pins u_reg_a/CK] -to [get_pins u_reg_b/D] set_multicycle_path -hold 1 \ -from [get_pins u_reg_a/CK] -to [get_pins u_reg_b/D]这里有个99%的新手都会踩的坑只写-setup 2不写-hold 1。因为工具默认hold检查发生在setup检查的前一个时钟沿当你把setup改成2之后如果不手动调整holdhold检查就会落在默认的第0个沿和setup检查的第1个沿之间隔了整整一个周期等于hold约束被放得过宽数据可能提前到达造成hold violation却不被发现。正确做法是setup设为N时hold要对应设为N-1。multicycle的数目也不是随便定的。如果你的数据确实是每两个周期采样一次setup设2没问题但如果只是某个信号偶尔用一下那应该考虑用false path或者case analysis而不是multicycle。写multicycle之前先问清楚这个路径到底是不是真的多周期路径。4.3 set_max_delay跨时钟域的手动挡对异步时钟域之间的握手同步路径有时不想一刀切设false path而是要求数据路径的延时控制在一定范围内。这时候用set_max_delayset_max_delay 5.0 -datapath_only \ -from [get_clocks clk_a] -to [get_clocks clk_b]-datapath_only这个选项非常关键它表示只检查数据路径的组合延时不计算时钟skew和时钟沿之间的offset。这个选项特别适合异步FIFO的写指针到读指针的格雷码同步路径、跨时钟域握手信号这类场景。如果不加这个选项工具会把时钟沿的相位差也算进去结果跟你心里想的数据延时要领完全不是一个东西。这里有个规则要记住set_max_delay和set_false_path不要同时设在同一条路径上。工具的行为不一定是你想象的两个都生效通常是false path优先级更高直接屏蔽了delay检查。写约束之前先report_exceptions查一下现有例外避免重复设置。4.4 set_case_analysis把模式选择固定下来功能综合时测试相关的信号必须固定成功能模式的值set_case_analysis 1 [get_ports test_mode] set_case_analysis 0 [get_ports scan_en]不设的话工具会同时优化功能路径和测试路径面积和时序都会有额外的消耗。set_case_analysis和false path的区别在于case analysis会让工具在逻辑综合阶段直接把另一个分支剪掉优化本质上只保留一个模式下的电路而false path只是不做时序检查逻辑结构还在。所以对于test_mode、scan_en、bypass选择这类信号用case analysis是更合适的选择。5. 综合阶段SDC的实战要点与排查5.1 综合约束和后端约束的定位差异很多人搞不明白为什么综合的SDC和后端PR的SDC不完全一样。综合阶段没有物理信息工具对时钟树没有精确的skew模型所以uncertainty需要预估一个偏保守的值后端阶段时钟树已经长完skew是真实已知的uncertainty可以进一步收紧。同样input/output delay在综合阶段和后端阶段也可能因为pad位置、封装形式的不同而调整。所以我的习惯是维护一套SDC模板按综合和后端分成两个版本把需要微调的地方用变量或者注释标出来。这样不管换工具还是换工艺都能快速对齐不会出现后端同事拿到手发现整套约束没法用的情况。这个习惯看起来不起眼但在多个项目并行的时候能省下大量沟通成本。5.2 常见报错与排查思路速查综合阶段跑约束最常见的几个问题可以整理成一张速查表现象可能原因排查方向check_timing报unconstrained endpointsgenerated clock的source指错或时钟未定义先report_clocks确认所有时钟两个时钟域之间出现大量violation忘记set_clock_groups用report_timing -from -to分组看分布输出路径hold violation成片set_output_delay -min缺失检查output delay是否成对书写面积异常偏大且时序依旧差false path或case analysis漏写检查时序例外约束的覆盖率约束文件反复覆盖导致结果不可预期SDC命令顺序问题拆分文件用source按固定顺序加载第一优先级永远是先用check_timing把unconstrained和no clock问题清零再去看具体的时序violation。如果check_timing都没有过后面所有分析都是空中楼阁。5.3 我自己的几条实操经验最后分享几条这些年用下来最值得记住的经验。第一每个新项目的第一版SDC不要追求完整先保证时钟和时钟分组正确。跑一遍check_timing把unconstrained清零再慢慢加I/O延时。如果一上来就堆一大堆约束出了问题根本不知道是哪个引起的排查时间会比省下的时间多得多。第二input/output delay的数值尽量写成表达式哪怕是简单的加法也要让后面的人看明白是怎么算出来的set_input_delay -max [expr 1.5 1.0 0.2] [get_ports data_in]这样三个月后你自己回来看也知道1.5是外部Tco、1.0是走线延时、0.2是skew预算。直接写死2.7的代码没人知道来由评审的时候也说不清楚。第三约束文件也是代码必须走评审。我见过太多因为约束写错导致流片fail或者后端被迫改方案的案例。SDC评审时要逐条过特别是false path、multicycle path、clock group这三类一定要有人签字确认。把约束评审放在和RTL评审同等重要的位置一点都不夸张。SDC约束这门功夫没有什么捷径就是多写、多跑、多总结。综合阶段的核心理念其实很简单用一套合理且保守的约束集把设计的真实意图完整传给工具。约束不是越紧越好也不是越松越好关键是准确。希望这篇关于综合常用SDC约束的整理能帮你少走一些弯路。如果你在实际项目里遇到过什么奇怪的约束问题欢迎一起交流讨论。
返回列表