拓冰建站拓冰建站
首页 / 资讯中心 / 正文

静态时序分析(STA)核心概念与约束实战详解

1. STA到底在干嘛一次把概念说清楚静态时序分析Static Timing Analysis简称STA是所有数字IC设计工程师和FPGA工程师都绕不开的核心技能。还没接触过STA的朋友不需要把它想得太玄乎说直白一点STA就是把你设计出来的电路放在给定的工艺条件下把每一条从触发器到触发器、从输入端口到触发器、从触发器到输出端口、从输入端口到输出端口的路径按照时序约束的要求用静态计算的方式过一遍检查每一条路径的建立时间setup time和保持时间hold time是否满足要求。这个静态二字体现在哪里对比一下就清楚了。我们做仿真Simulation的时候需要喂测试向量然后看波形这是一种动态验证方式。动态验证的最大问题是你验证到的路径数量严格依赖于你写的测试向量能覆盖到什么程度。一条路径没被激励到它的时序问题就不会在仿真中暴露。STA则不管这些它不需要任何输入向量直接基于网表和库文件把芯片里所有的时序路径全部穷举出来计算覆盖率是100%的。这正是STA在数字IC流程中不可替代的原因。那STA能干什么、解决什么问题具体来说它能告诉你三件事芯片能不能跑到你想要的时钟频率最高工作频率评估在某个指定频率下哪些路径不能满足建立时间setup violation哪些路径不能满足保持时间hold violation每条违例路径的裕量slack是多少、关键路径在哪里、该从哪里入手优化对于正在学习数字IC的在校学生、刚刚接触ASIC流程的初级工程师以及FPGA开发到一定阶段想要往高性能、高可靠性方向深入的朋友STA都是必修课。这套知识本身是成体系的我的习惯是分几次把它讲透。这一篇先把STA的框架搭起来重点讲四个东西STA的基本概念、标准工艺库怎么看、时钟约束怎么建、IO约束怎么做。这些都是STA流程里最底层的部分后面对时序报告的分析、时序收敛的方法全都建立在这四个地基之上。2. 标准工艺库STA计算的基础数据来源2.1 Lib文件里到底装了什么STA不是算命的不会拍脑袋告诉你一条路径快还是慢。它所有的计算都依赖于工艺库Liberty文件通常叫.lib文件提供的数据。你可以把工艺库想象成一本元件性能手册——里面记录了标准单元与门、或门、触发器、缓冲器等在不同条件下的延迟、功耗、面积以及引脚间的时序约束信息。打开一个lib文件你会看到它按逻辑单元Cell来组织内容。每个Cell下面分好几个组Group常见的有ff触发器组定义Q端到D端的内部时序关系包括rising_edge和falling_edge等时序弧timing arc。timing组定义输入引脚到输出引脚的组合逻辑延迟以及引脚本身的电容、转换时间slew属性。power组定义内部功耗、开关功耗等。area组定义单元的物理面积。进一步往timing组里看你会看到非常关键的概念——查找表Lookup TableLUT。标准单元库里单元格的大小固定但实际工作环境千差万别比如输入信号的上升时间是快还是慢、输出端接了几个电容负载是重还是轻这都会直接影响延迟。库文件的做法是把延迟做成一张二维表格横轴是输入转换时间input transition time纵轴是输出总电容output capacitance表格里每个交叉点的值就是这个条件下的单元延迟。2.2 时序弧和单元延迟的本质一个组合逻辑单元比如二输入与非门NAND2它的延迟不是单一数值而是由两条时序弧决定的A端到Z端、B端到Z端。每个引脚还分上升沿和下降沿所以A到Z就有A上升沿到Z下降沿、A下降沿到Z上升沿等几种组合。为什么非要分这么细因为工艺偏差就是如此——n管和p管的参数不一样上升沿的充放电速率和下降沿天然就不一样。触发器的时序检查约束也是库文件里的重要内容。以D触发器为例库文件里会定义setup timingD端数据相对于时钟沿需要提前稳定的时间hold timing时钟沿到来之后D端数据需要继续保持的时间clock to Q delay即CK-Q时序弧从时钟有效沿到输出Q变化的延迟。除了这些库文件还会定义引脚电容、最大转换时间限制等Design Rule ChecksDRC信息。我见过不少刚接触STA的朋友直接拿工具报出来的时序报告就开始改代码却完全不看库文件里的延迟模型。这样做的坏处很明显——你不知道工具的数值是怎么来的出了问题也没法定位。其实排查时序问题时很多结论都能在库文件里找到根源。比如同样一个缓冲器在VT阈值电压不同、工作电压不同、温度不同的PVTProcess、Voltage、Temperature条件下延迟差个两三倍是非常正常的。正常芯片设计流程里STA会分别在慢速工艺角SS/低电压/高温setup分析用和快速工艺角FF/高电压/低温hold分析用下分别跑把最坏情况覆盖全。这也是为什么你写约束之前一定要搞清楚当前综合/实现工具默认用了哪个corner的库。2.3 实操中怎么处理库文件综合和STA工具读库文件的时候一般直接用set_target_library指定一个或者一组库文件路径就行。比如在Synopsys Design Compiler里set target_library /home/user/lib/ss_nom_lvtt_0p99v_125c.db set link_library * $target_library这里.db是Liberty文件.lib编译后的二进制形式工具加载速度更快但读入前需要先用read_lib或库编译器把.lib转成.db。FPGA流程中厂商工具通常会自动选好对应速度等级的库不需要你手动指定但你最好清楚它用的文件在哪里后面手写XDC/SDC时才能知道placer和router的延迟模型是什么。注意不同工艺角下约束要求不同setup检查在worst case慢库下做hold检查在best case快库下做。千万不要一份约束拿到底就以为万事大吉后面Multi-Mode Multi-CornerMMMC分析时你会分别给每条约束链挂上不同的库。3. 时钟约束整个STA的灵魂3.1 create_clock是一切的开端STA里所有路径的检查都基于时钟沿来计算所以时钟约束是整个约束文件里最核心的部分。时钟约束的第一步是用create_clock命令定义一个时钟对象。下面是一个最基本的例子create_clock -name clk_sys -period 10.0 [get_ports clk]这段约束的含义是端口clk上有一个周期为10ns、占空比50%、第一个上升沿在0时刻的时钟命名为clk_sys。定义完之后工具就知道这个时钟每10ns触发一次时序检查。创建时钟之后有几个与之紧密相关的约束点要逐个说清楚时钟延迟clock latency时钟信号从外部源点到触发器时钟引脚的传播延迟。这里面包括source latency时钟源到时钟端口的延迟和network latency时钟端口到触发器时钟引脚的延迟。工具会用命令算延迟也可以用set_clock_latency做预估。时钟不确定性clock uncertainty这是初学者最容易和时钟抖动混淆的概念。set_clock_uncertainty通常用来预留时钟周期偏差的余量包括时钟抖动jitter和时钟偏斜skew的预算。简单理解它是你在工具分析时人为加上的悲观余量确保实际流片后即使在最差情况下时序还能收敛。实际项目中前端约束时一般会留0.05ns到0.1ns的余量后端实现后再逐步收紧。时钟转换时间clock transition时钟信号的上升/下降时间可以直接定义也可以让工具自动计算。3.2 从时钟树到分频时钟的约束写法数字芯片里的时钟信号不是一根线从端口直接连到所有触发器而是会经过时钟树综合Clock Tree SynthesisCTS插入大量的buffer来平衡各触发器的时钟到达时间。对于前端STA来说你不需要关心具体插了哪些buffer但在约束时要给工具留够余量特别是时钟uncertainty里的skew预算就是为了覆盖CTS之后的偏差。很多设计里不止一个时钟还有分频时钟、门控时钟、多路选择时钟。分频时钟比如系统时钟经触发器分频得到的半速时钟的正确约束方法有两种一种是在分频触发器的输出引脚上直接create_clock并设置与源时钟的相位关系另一种是create_generated_clock定义它与源时钟的关系更清晰。create_clock -name clk_src -period 10.0 [get_ports clk] create_generated_clock -name clk_div2 -source [get_ports clk] -divide_by 2 [get_pins REG_DIV/Q]这个写法告诉工具clk_div2这个时钟是由clk_src除以2得到的源端是端口clk生成位置是触发器REG_DIV的Q端。门控时钟clock gating在低功耗设计里太常见了。ICG单元Integrated Clock Gating cell内部就是一个带锁存功能的与门使能信号有效时时钟通过无效时关断。对STA来说ICG单元内部到输出引脚的路径本身就是组合路径工具会自动检查时钟使能信号的建立保持时间约束时不需要额外做特殊处理只要保证ICG单元的时序弧在库文件里定义正确即可。但有一种情况要注意如果你用的IP或者RTL里存在纯组合逻辑门控时钟比如assign gclk clk en;STA工具默认可能会把它识别成组合逻辑而不会自动推断出这是时钟门控。这种情况在综合时容易产生隐患最好在RTL阶段改写为ICG的实例化方式或在综合时用set_clock_gating_check之类的方式显式约束。我踩过这个坑当时在FPGA里用组合门控时钟控制一个跨时钟域逻辑结果由于门控毛刺导致数据采样偶发出错花了两天才定位到问题源头。3.3 异步时钟域不强制就不会正确多时钟设计中最常见的错误是两个异步时钟域的路径工具默认会去检查时序结果报出一堆不切实际的violation。异步时钟域Asynchronous Clock Domain比如两个时钟分别由不同的晶振/PLL产生它们的相位关系是随机变化的、不确定的。对于这类路径正确的做法是使用set_clock_groups -asynchronous把它们声明为异步set_clock_groups -asynchronous \ -group [get_clocks clk_a] \ -group [get_clocks clk_b]声明之后工具就不会对这两个时钟域之间的路径做时序检查但这不代表你可以放任不管。异步数据跨越时钟域必须用两级同步器、异步FIFO、握手协议等方式做同步处理否则仍然有亚稳态风险。STA只帮你脱管真正的安全需要靠设计结构来保证。另外不要滥用set_false_path。很多工程师图省事看到跨时钟域报violation就直接set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]。这个写法的风险在于你等于告诉工具这两条路径的时序我不关心但如果你没有正规的同步机制这条路径的亚稳态问题就真实存在且被掩埋了。我建议的原则是set_clock_groups -asynchronous用于完整的异步时钟域声明set_false_path只用于单个特定路径比如复位释放路径、静态配置路径跨时钟域的数据路径一定必须配同步器设计。4. IO约束把芯片跟外部世界对好表4.1 为什么要手动建立IO约束IO约束可能是STA入门阶段最让新手困惑的部分。原因很好理解——芯片内部的路径是从触发器到触发器起点终点的时钟沿都是工具已知的约束完时钟之后自动检查就行。但芯片边界上的输入输出路径连接的是外部器件外部器件的时序参数输出延迟、建立保持时间你不在STA工具环境里给出来工具就不可能知道该怎么检查。IO约束的本质就是把你片上逻辑的时序和外部器件的时序衔接起来让工具检查输入数据能否被正确采到、输出数据能否被外部正确采到。这里有两种约束类型务必要分清楚系统同步system synchronous接口输入数据和时钟都由同一个外部源产生并到达芯片但到达芯片内部后数据经过外部器件输出延迟进入芯片时钟直接或经PLL后进入芯片。整个系统的时序参考是同一个系统时钟。源同步source synchronous接口时钟和数据由发送端器件一起发出DDR、SDRAM、SPI等接口都是这种结构。约束时参考的是随路时钟而不是全局系统时钟。大多数项目里两种都有而初学阶段最容易出错的是没搞清楚一个约束的参考时钟到底是系统时钟还是随路时钟。4.2 输入延迟约束的建立方法以系统同步为例。假设芯片的输入端口data_in接收外部器件送来的数据外部器件从系统时钟clk_sys上升沿输出数据输出延迟为2ns即clk_sys上升沿之后2ns数据才在芯片的引脚上有效。这时要在STA里约束输入延迟set_input_delay -clock clk_sys -max 2.0 [get_ports data_in] set_input_delay -clock clk_sys -min 1.0 [get_ports data_in]这里的-max对应建立时间检查时最晚到达的数据外部器件输出延迟最长的情况-min对应保持时间检查时最早到达的数据外部器件输出延迟最短的情况。-max和-min通常需要分别赋值因为在芯片内部工具会在慢库下用-max做setup检查、在快库下用-min做hold检查。输入延迟的数值怎么定它不是拍脑袋填的。来源是外部器件的datasheet即发送端器件的时钟到输出延迟Tco加上PCB走线延迟。比如外部器件数据手册写Tco_max 2nsPCB走线延迟是0.1ns那set_input_delay -max就是2.1ns。输出延迟同理对应的是外部接收器件的建立时间要求写成set_output_delay -clock clk_sys -max 2.5 [get_ports data_out] set_output_delay -clock clk_sys -min 0.5 [get_ports data_out]含义是芯片内部输出数据经过组合逻辑到达输出端口后留给外部器件去采样前在端口外面的整个外部路径上需要满足的时间余量。简单理解set_output_delay的值是外部器件所需的建立时间加上PCB走线延迟。4.3 真正的应用以FPGA为例的XDC写法FPGA用户接触IO约束最直接的场景是给Altera或Xilinx的时钟和IO管脚加约束。这里以Xilinx的Vivado为例XDC里IO约束的语法与SDC保持一致create_clock -name sys_clk -period 10.0 [get_ports clk] set_input_delay -clock sys_clk -max 4.0 [get_ports {din[*]}] set_input_delay -clock sys_clk -min 1.0 [get_ports {din[*]}] set_output_delay -clock sys_clk -max 5.0 [get_ports {dout[*]}] set_output_delay -clock sys_clk -min 0.5 [get_ports {dout[*]}]写完IO约束之后另一个容易被忽视的点是set_input_delay还要配合set_input_transition使用输入引脚信号本身有上升下降时间这个转换时间会影响片内逻辑延迟以及set_output_delay配合set_load输出端口外部负载电容会影响最后一级单元的延迟。不过这些参数工具会有默认值只有在做高精度时序收敛时才需要手动指定。我个人的建议是初学IO约束时不要把精力全放在命令语法上先彻底搞清楚延迟数值是怎么计算和折算的。等你能手算出一条输入路径在芯片内部还剩下多少裕量IO约束就再也不会是拦路虎。5. 建立时间和保持时间STA的核心检查机制5.1 为什么要有两个时间检查STA最频繁出现、也最需要深刻理解的两个词是setup和hold。建立时间指的是数据必须在时钟有效沿到达之前提前稳定下来的时间窗口保持时间指的是时钟有效沿到达之后数据必须继续保持不变的时间窗口。这两个时间不是芯片设计者凭空定的而是由触发器电路结构本身决定的物理属性。时序分析里满足建立时间本质要求是数据从上一个触发器时钟沿launch edge出发经过组合逻辑到达目的触发器D端的时间必须早于目的触发器下一个时钟采样沿capture edge减去建立时间。满足保持时间本质要求是数据到达目的触发器D端之后在capture edge之后保持时间内不能因为launch触发器又发出新数据而导致D端变化。STA工具会自动沿着每一条时序路径做这两个检查并在时序报告里给出slack值。slack为正值代表余量充足slack为负值代表违反约束必须进行修复。而修复的带宽工具说到底就是插缓冲器、调整触发器位置、优化逻辑级数这些手段——但前提是你能看懂报告、判断瓶颈在哪条路径上。5.2 时序报告的解析方法以Synopsys PrimeTime的report_timing为例一份时序报告通常按以下结构呈现Startpoint: REG_A/Q (falling edge-triggered flip-flop clocked by clk_sys) Endpoint: REG_B/D (rising edge-triggered flip-flop clocked by clk_sys) Path Group: clk_sys Path Type: max clock clk_sys (rise edge) 10.00 10.00 clock network delay (propagated) 2.00 12.00 output external delay -2.00 10.00 data arrival time 10.00 clock clk_sys (rise edge) 20.00 20.00 clock network delay (propagated) 2.50 22.50 clock uncertainty -0.10 22.40 library setup time -0.20 22.20 data required time 22.20 -------------------------------------------------------------------- data required time 22.20 data arrival time -10.00 -------------------------------------------------------------------- slack (MET) 12.20这份报告的信息量很大我用大白话翻译一遍数据的起点是REG_A的Q端下降沿触发的D触发器终点是REG_B的D端上升沿触发的D触发器。源时钟第一个上升沿在0ns经过2ns网络延迟到达发起触发器时钟端所以数据在2ns时刻开始发射数据经过组合逻辑后到达终点所以arrival time是10ns。终点采样的时钟沿在20ns经过2.5ns网络延迟到达目的触发器时钟端同时还要扣除时钟不确定度0.1ns和目的触发器的建立时间0.2ns所以数据最晚必须在22.2ns前稳定。22.2ns减10ns得到12.2ns的正slack时序满足。做的时候不用锱铢必较但看报告时最好养成一个习惯先看是哪条路径违例再沿着报告里的每一条注释逐项核对尤其确认是时钟uncertainty设置过大还是path上的组合逻辑级数太多还是input delay给的数值太大。我发现很多时候新手第一眼看到violation就慌了其实只要把某一项约束数值放宽松一点、或者优化一两级逻辑就能解决工具报告会把每项延迟都列得很清楚逐项排查是最高效的方式。5.3 关于setup和hold的感悟还想多说一句刚学STA时最好把setup和hold当成两个独立的问题分开处理而不要混在一起想。Setup不满足通常说明路径太慢需要减少逻辑级数、降低负载、提升单元驱动能力Hold不满足通常说明数据太快需要在路径上加延迟缓冲器。修复setup是加速修复hold是减速两者手段完全不同甚至可能矛盾——修复setup时把路径变快了反而可能在另一条路径上引入hold问题。这也是为什么STA工具会把setup和hold的检查分开跑setup在慢库、hold在快库的原因。6. 常见问题与项目实战中的避坑记录STA的常见报错和坑我经历得不少挑几个具有代表性的写出来给后来的人提个醒。第一类问题约束没写到对应的时钟域上。这类问题最常见的表现是时序报告里出现大量无时钟unconstrained路径或者一条路径被报出来了但你找不到是哪个时钟在检查它。排查方法很简单启动STA工具后第一步先用report_clocks看看时钟对象建了几个、有没有被识别为generated clock。如果时钟端口名字写错了或者get_ports的匹配语法写错工具会直接报group为空这时候最容易被忽略。第二类问题set_input_delay/set_output_delay的参考时钟整错。源同步接口经常有这种问题——外部数据和时钟一起进来但你用系统时钟做了参考结果时序报告里data arrival time和clock arrival time的时钟沿对不上。这种情况工具计算出来的slack会非常离谱不是大正就是大负。检查手段是把参考时钟单独看一下确认沿的位置和路径的时间起点。第三类问题异步时钟域忘记声明。我在上一条已讲过这里继续展开说一个实际经历。之前做一颗SoC里面有USB的48MHz时钟和CPU的200MHz时钟两个时钟本身是异步的但工程师写约束的时候忘了声明asynchronous结果综合之后时序报告有上千条violation开头还以为是RTL逻辑有问题。我逐条往下查才发现这上千条路径全部集中在两个时钟域的交界处本质是约束缺失。后来的流程里我们规定团队里任何一个时钟在CREATE之后必须同步做clock group声明这已经是写进检查清单的硬性规定了。第四类问题IO约束的数值来源没有追溯。很多新人在开发初期看到外部器件datasheet里的时序参数不假思索就把某个值填进去后面流片或上板验证时出现采样错误才发现约束数值给的不对。数据库里每一条IO约束的值都应该能追溯到一个具体的计算公式比如Tco_max PCB走线延迟。我自己做代码评审时候最常问的问题就是这个4.0ns是哪里来的回答不上来那这条约束就是不合格的。第五类问题只跑了一个corner。很多实践经验不足的工程师只会在默认库下跑一次STA看到没有违例就觉得万事大吉。但流片后芯片在不同电压、温度下可能直接跑不过。我建议最少做到setup用SS慢速库加低电压高温hold用FF快速库加高电压低温各跑一遍。如果工具或者流程支持把OCVOn-Chip Variation也打开这会让你对时序裕量的把控更接近真实情况。7. 最后聊一点自己的体会做STA这么久我的感受是STA其实是一门约束的艺术。RTL逻辑写错了通常一眼就能看出来但约束文件里的问题往往是隐性的甚至要等到chip回来起不了振、测试失败才能暴露。因此我养成了一个习惯先不看工具报告而是先审视自己的约束文件——时钟定义有没有覆盖所有时钟异步时钟域有没有声明IO延迟有没有物理依据约束本身就是一份需要评审、需要注释、需要版本管理的产品文档认真程度值得跟RTL代码同等对待。如果只能从这篇文章带走一样东西我建议是把STA当作一个证明题系统——你给出约束工具用库数据证明每一条路径是否满足时序。所有的报错和violation本质上都说明这个证明过程中某个前提不成立了。顺着这个方向去排查和修复很多看似复杂的时序问题都能一步步拆解清楚。后续我有时间会继续写第二篇重点讲时序报告深度分析、关键路径优化手段以及跨时钟域CDC在时序约束中的实践处理。如果这篇文章对你有帮助可以先收藏起来等真正动手写约束、跑时序的时候再拿回来对照着操作。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门