首页 / 资讯中心 / 文章详情

静态时序分析与形式验证:芯片tapeout前的关键闸门

静态时序分析与形式验证:芯片tapeout前的关键闸门 ★ FEATURED ARTICLE
简介面向数字IC设计工程师与学习者的一份归纳型技术文档聚焦Synopsys PrimeTime静态时序分析与Formality形式验证的完整使用方法。内容覆盖Tcl脚本基础、时序约束设置、路径分析、等价性检查流程以及验证失败时的逻辑锥Debug技巧并配有实际设计示例适合作为入门实操与快速查阅的参考。资料包为单个PDF文件大小约545KB排版紧凑、目录清晰从绪论、PrimeTime简介、Tcl与pt_shell操作到静态时序分析前的准备工作、具体分析流程、Formality验证及逻辑锥诊断层层递进方便按需查阅。该资源已有1114人学习下载对于希望系统掌握STA与形式验证流程的读者可快速建立从准备工作到时序报告解读、从参考设计设置到不匹配点诊断的整体认知也能借此缩短数字电路设计周期、提高验证可靠性。1. 静态时序分析(PrimeTime)和形式验证(Formality)芯片tapeout前绕不过去的两道闸从RTL到GDS的流程里后端工程师被问得最多的问题往往不是“能不能跑通”而是“时序能不能过、功能对不对”。静态时序分析(PrimeTime)在做的就是在给定约束下用数学方法算出每条路径的建立时间和保持时间余量证明芯片在目标频率下不会因为时序翻车形式验证(Formality)则对着两个网表做等价性检查证明综合、布局布线之后的电路和原始RTL逻辑功能一致。前者管“能不能按时干活”后者管“干的是不是同一件事”两道闸缺一不可。这篇归纳面向做数字IC后端和验证的工程师目标是把你从“听过PrimeTime和Formality”带到“能跑通、能调通、能定位坑”。2. PrimeTime在做一份什么账时序弧、建立保持时间和SDC约束2.1 建立时间、保持时间和时序弧公式背后的物理含义我第一次接触静态时序分析时最难的是理解PrimeTime到底在算哪几个数。其实它只关心一件事寄存器之间数据路径上的延迟是否还留有余量。数字电路里寄存器的时序参数有两条底线。建立时间setup time要求数据在时钟有效沿到达之前提前稳定提前的量不能小于库给定的setup值保持时间hold time要求数据在时钟沿到达之后继续维持稳定维持的时间不能小于库给定的hold值。这两个参数不是拍脑袋出来的而是工艺库在每个单元.lib里以查表Lookup Table形式给出的随输入转换时间和输出负载变化。PrimeTime把电路拆成一条条launch到capture的路径。launch端是被分析的源头寄存器capture端是接收数据的目的寄存器。两者之间可能隔着组合逻辑、多路选择器、扇出网络。路径上每个单元的延迟用一个叫时序弧timing arc的条目描述组合单元里有从输入引脚到输出引脚的弧寄存器里有从CK到Q的弧。PrimeTime把launch时钟沿到达时间、时钟网络延迟、单元延迟、互连延迟逐项叠加得到数据到达时间再从捕获时钟沿减去库里的setup/hold约束得到数据要求时间。两者相减就是slack。setup slack的正负取决于谁更晚。粗略地说setup slack 捕获沿时刻 捕获时钟网络延迟 - CRPP校正-launch沿时刻 launch时钟网络延迟 CK到Q延迟 组合逻辑延迟 互连延迟- 库setup值hold slack刚好反过来hold slack launch沿时刻 launch时钟网络延迟 CK到Q延迟 组合逻辑延迟 互连延迟-捕获沿时刻 捕获时钟网络延迟 - CRPP校正- 库hold值理解这几个加项和减项的目的不是解方程而是读报告。你看到一条违例路径的slack为负时要知道它是数据来得太晚setup违例还是数据消失得太早hold违例才能往正确的方向修。专门说下时钟偏斜clock skew。时钟网络延迟本身不是坏事坏的是launch时钟和capture时钟分别到达不同寄存器时产生的差。同一个时钟到达不同寄存器的时间差就是skew。setup时序里capture clock偏晚对setup有利因为数据可以多走一会儿hold时序里capture clock偏晚反而更危险数据要保持更久才能满足hold。这也是为什么place阶段做时钟树综合时要严格控制skew不能只看平均延迟。还有CRPPclock reconvergence pessimism removal。它是PrimeTime对公共路径悲观度做的修正当launch和capture共享一段时钟网络时这段网络的延迟被同时算在两条路径上实际不会产生那么大的悲观值。工具会把这部分悲观量消除后再报slack。做后端的你会在报告里频繁看到这一行属正常现象不影响违例判断的真实性。2.2 用PrimeTime跑最小分析库、网表和SDC的三件套要跑起PrimeTime输入就三样工艺库、门级网表、约束文件。工艺库通常是.db格式也支持.lib里面承载所有单元的时序和功耗信息门级网表是综合之后或布局布线之后的数字网表约束则是SDCSynopsys Design Constraints文件。一个最小可运行的PrimeTime脚本这样写# 指定库搜索路径顺序决定查找优先级 set search_path [list ./lib ./netlist] # link_path里的*表示允许链接顶层设计自身 set link_path [list * std_cells.db slow_vtt.db] # 读入门级网表 read_verilog ./netlist/top_netlist.v # 链接设计把网表里的单元引用和库单元对应上 link_design top # 读入时序约束 read_sdc ./constraints/top.sdc # 报25条最差的setupmax路径 report_timing -delay_type max -nworst 25 -path_type full ./rpt/top_setup.rpt # 报25条最差的holdmin路径 report_timing -delay_type min -nworst 25 -path_type full ./rpt/top_hold.rptread_verilog只是把网表读进内存真正把实例和库单元对应起来的是link_design。如果link_design报warning说某个单元找不到检查link_path里的库是否完整、单元命名是否和网表一致这是新项目最常见的开场坑。另外注意同一个设计如果被多次读入或跨模块引用link_design后面必须跟顶层模块名PrimeTime才能确定分析的入口。SDC里最常用的那组约束长这样# 创建主时钟周期10ns对应100MHz create_clock -period 10 -name clk [get_ports clk] # 设置时钟不确定性cover掉jitter和剩余skew set_clock_uncertainty -setup 0.30 [get_clocks clk] set_clock_uncertainty -hold 0.10 [get_clocks clk] # 输入端口数据相对时钟沿的最晚到达时间 set_input_delay -clock clk -max 2.0 [get_ports din] # 输出端口数据相对时钟沿的最晚要求时间 set_output_delay -clock clk -max 2.5 [get_ports dout] # 两个异步时钟域之间的路径不做STA检查 set_false_path -from [get_clocks clk] -to [get_clocks test_clk] # 某条跨周期握手路径允许两个周期才捕获 set_multicycle_path 2 -setup -from [get_pins reg_a/CK] -to [get_pins reg_b/D]create_clock的period直接决定频率目标设太小会让所有路径集体变红set_clock_uncertainty不能过大否则等于给真正的物理约束加了多余余量set_input_delay和set_output_delay是边界条件量值一般从芯片接口时序预算或者上一级模块约束推导出来。set_false_path只用在确实不存在同步关系的跨时钟域乱用会掩盖真实违例这是STA里最需要责任心的一条命令。工程上还需要注意分析模式。传统做法是BC-WCbest case worst case分开跑setup看worst case的慢库hold看best case的快库。进阶一点用OCVon-chip variation加上derate系数更精细地估计片上偏差。脚本里可能会看到类似这样的配置# BC-WC模式setup用慢库cornerhold用快库corner set_analysis_view -setup view_wc -hold view_bc多数项目会直接用signoff view来描述PVT组合比如ss_0p9v_125c对应setup的worst corner、ff_0p9v_0c对应hold的best corner。这个选择影响整个项目后续所有时序结果建议在项目启动时就和工艺负责人确认清楚不要等到中间再换。提示run_case里如果同时存在多个corner务必核对每个view绑定的库和约束避免setup和hold使用了同一套库导致分析失真。3. 读懂report_timing并收敛违例slack、CRPP和时钟路径调整3.1 report_timing的字段从Start point到End point拿到报告不要只看最后一行的“VIOLATED”或“MET”。理解一份完整报告要按字段逐个读Startpoint : reg_a/Q (rising edge-triggered flip-flop clocked by clk) Endpoint : reg_b/D (rising edge-triggered flip-flop clocked by clk) Path Group : clk Path Type : max Delay Time Description ---------------------------------------------------------------------- 0.000 0.000 clock clk (rise edge) arrival time 0.522 0.522 clock network delay 0.045 0.567 clock reconvergence pessimism removal 1.234 1.801 reg_a/CK (rising edge-triggered flip-flop clocked by clk) 0.310 2.111 reg_a/Q (dff) 1.865 3.976 u_logic_0/Z (and2) 0.420 4.396 net (fanout12) data arrival time 5.000 5.000 clock clk (rise edge) arrival time 0.500 5.500 clock network delay 0.045 5.545 clock reconvergence pessimism removal -0.350 5.195 library setup time 5.195 data required time 0.799 slack (MET)Startpoint就是launch触发器的输出引脚endpoint是capture触发器的数据输入端。Path Type是max对应setupmin对应hold这决定了后面所有延迟的加减方向。再看Time列上半段是launch沿时刻加上各类延迟最后累出data arrival time下半段是capture沿时刻减去库setup得到data required time两者一减就是slack。这份报告里slack是正数路径MET。如果slack为负说明数据到达比要求的晚即setup违例需要减少中间组合逻辑延迟或者让capture时钟晚点到。hold违例的报告会反过来data arrival time比required time早数据被下一拍冲掉。注意net (fanout12)这个字段。它说明驱动节点带了12个负载这是延迟大头之一。如果你看到一段net的delay比其他同类路径大很多先去查fanout而不是死磕单元延迟。高扇出网络经常是setup违例的隐性凶手。3.2 修时序的三种日常手段约束修正、时钟调整、驱动优化第一板斧是检查约束本身。假路径有没有设对多周期路径是不是比实际需要更宽时钟group划分对吗如果某个功能模块本来就接受2个周期的握手你却只给1个周期约束必然会报大量setup违例。修这类问题要用“为什么”来解释不能为了过check而乱放false_path。第二板斧是调整时钟网络和不确定度。时钟偏斜对setup和hold的作用方向不同。如果你的违例集中在setup看看capture时钟是不是比launch时钟到得早此时可以在CTS阶段设置skew目标给capture端稍晚的时钟。反过来hold违例特别多时通常要把CTS的skew调小把uncertainty里留的余量收一部分回来。不过uncertainty不可随意清零它要承载jitter预测和工艺偏差这里属于经验活。违例类型数据路径表现常见原因优先修复手段setup (max)数据到达太晚逻辑级数高、负载大、约束过紧降逻辑深度、插buffer、检查约束与时钟偏斜hold (min)数据消失太早快corner下路径过短、skew过大插delay buffer、收紧CTS skew、检查uncertainty第三板斧是看数据路径本身。组合逻辑级数太高、驱动能力不足、高扇出net都会让data arrival time飙高。常见物理手段包括插入buffer、复制高扇出节点、把多级逻辑重新综合。逻辑综合阶段就要控制不要等布局布线都完成了再回头改逻辑。这里有一个血泪经验setup违例往往可以通过约束调整或时序预算协调hold违例在signoff阶段最棘手。因为hold是在最快corner下检查的数据路径必须足够长才能保证不被早到的下一拍冲刷。修正hold最常用的是在路径中间插delay buffer但用力过猛又会反过来拖累setup两头都要看着调。工程上的习惯是先修到setup裕量足够再回头专门处理hold因为hold违例在布局布线迭代中很常见也常常后面还会浮出来。4. 形式验证Formality的核心思路等价性检查到底在验证什么4.1 为什么仿真覆盖不够——等价性检查的数学本质仿真是通过施激励、观察输出来验证的。它的局限在于你只能验证你给到的那些case。一个模块的状态空间是巨大的就算做上百万个随机激励仍然没有覆盖所有可能。后端的很多bug恰恰发生在那些你没测到的输入组合上。形式验证走的是另一条路。它把设计建模成数学对象然后用推理证明两个对象在所有输入下行为一致。等价性检查Equivalence Checking比较的是被验证设计implementation和参考设计reference之间的逻辑等价。它不关心timing、不关心功耗、不需要激励只回答一个布尔问题两个设计的输出在所有可达输入状态下是否完全相同。组合逻辑部分的等价性可以分解到每个关键点cut point上把复杂的逻辑锥切成一个个小锥再用SAT或BDD求解。寄存器通常作为天然匹配点。如果两个设计在所有匹配点上的组合锥输出一致结论就是逻辑等价。但这里有一个必须说破的边界形式验证证明的是两个设计实现一致不证明设计符合规格。如果你的参考RTL本身写错了等价性检查照样通过。所以reference的质量是整个验证链的地基这也是Formality流程里“reference设计要经过充分review”的原因。顺序逻辑的等价性更复杂。现代工具支持顺序等价性检查SEC可以对不经过重置的状态空间做更充分的比较但代价是求解规模更大。工程上依然优先用寄存器匹配来证明只有在寄存器被优化掉或者做了retiming时才依赖SEC。4.2 Formality里的reference、implementation和匹配点Formality的具体操作对象是两个容器。reference容器通常命名为r放的是你想当作“黄金标准”的设计implementation容器命名为i放的是待验证的设计。两者各自读入后需要把对应关系建立起来这就是match阶段。匹配可以发生在多个层次。名称匹配是最直观的如果两边寄存器名称一致工具直接对应。但DC综合过程经常改名字、合并常量、展开结构所以还要靠结构匹配和逻辑锥匹配来找回对应。综合时生成的SVF文件Synopsys Verification Format会记录这些关键信息。Formality的verify阶段会对每个匹配点做组合逻辑锥的比较。报告里会告诉你哪些点通过、哪些失配、哪些未匹配。未匹配不一定是功能错误可能是工具无法把两个设计对应起来这时需要你在调试阶段给出更多信息。值得强调的是匹配点越细验证粒度越细。若只把寄存器作为匹配点组合逻辑之间的等价证明粒度就是每两个寄存器之间的锥形若把某些关键net设计成匹配点判断粒度会精细到net级别。工程上通常先看整体结果再针对fail的点逐层展开。提示RTL和网表之间做LEC时reference一侧尽量选用综合前经过仿真充分验证的版本。reference错了后续所有验证都白做。5. Formality与后端流程集成LEC脚本、svf和踩坑排查5.1 最小LEC脚本与每条命令的参数说明一个典型的最小Formality脚本长这样# 指定库路径 set search_path [list ./lib ./rtl ./netlist] # 读入门级库提供单元功能定义 read_db ./lib/std_cells.db # reference容器读入RTL read_verilog -container r -libname WORK ./rtl/top.v # 指定参考设计顶层 set_top r:top # implementation容器读入门级网表 read_verilog -container i -libname WORK ./netlist/top_netlist.v # 指定实现设计顶层 set_top i:top # 读取综合时生成的SVF保留命名映射和常量信息 read_svf ./netlist/top.svf # 建立匹配点 match # 执行等价性验证 verify # 输出所有失配点 report_failing_points -allread_db读的是库单元的功能描述这一步给后续逻辑锥比较提供了单元的布尔功能。read_verilog后面跟container r和container i是为了区分两个验证角色libname WORK只是个工作库名只是个约定不决定验证行为。set_top指定哪个模块是顶层一定要确保reference和implementation设置的顶层对应同一个逻辑模块。match建立reference和implementation之间寄存器的对应关系verify做组合逻辑锥比较。两者的区别要命match没过verify根本没法做match全过verify也未必能全pass。命令本身有大量可选开关比如match -robust用于应对复杂名称映射set_constant用于强行把某个pin置成常量set_dont_verify用于跳过某些不该参与验证的逻辑块。5.2 SVF在综合与验证之间的桥梁作用SVF是DC在综合时生成的文本文件里头记录了综合过程中发生的逻辑转换、寄存器重命名、常量折叠、边界裁剪等关键操作。Formality有了它match阶段就不用从零猜名字可以直接把映射信息接过去。DC里写SVF的方式是# 在综合脚本末尾输出SVF write_svf outputs/top.svf在Formality中一定在match之前读svf。一个常见的问题是综合工程师修改了DC脚本但忘了重新写svf或者svf和网表不同步导致Formality这边拿着一份过时的映射来看新的网表结果就是一大堆失配。所以每次综合重新跑完SVF必须跟着网表一起归档。有些团队会把svf和网表放在同一目录、带同一时间戳这个习惯能省掉大量“看起来是形式验证问题、实际是文件版本问题”的排查时间。如果你的项目里svf和netlist分开管理至少要在回归脚本里确认文件生成顺序。5.3 五条高频踩坑记录现象、原因、解决现象match阶段大量点unmatchedverify直接无法执行。原因SVF没读或者读了但和网表不对版两边寄存器的命名风格完全不同。解决先确认read_svf成功并检查日志再看match报告必要时用set_user_match手动指定几条关键映射。仍然不行就检查DC是否开了retiming这类跨边界优化。现象寄存器匹配上了但组合逻辑失配报的失配点集中在某个控制逻辑锥。原因综合时把某些常量折叠到了寄存器复位端而RTL里没有对应的复位语义。解决在Formality里用set_constant给相关引脚设置常量或者检查RTL复位写法让复位语义和实现一致。现象失配点都落在某个IP模块周围。原因IP在网表里是空的或者只有壳没有可比较的功能信息。解决对这类IP用set_dont_verify跳过或者把IP的库模型也读进来让它变成可比较的逻辑锥。现象用时钟树综合后的网表做LEC大量寄存器名称变了也多了很多buffer。原因CTS或逻辑优化对时钟结构做了改动寄存器位置变化导致reference和implementation对不上。解决分层验证。第一层RTL对综合后网表第二层CTS后网表对综合后网表第三层route后网表对CTS后网表让每一层只比较该层引入的改动。现象新项目第一次跑Formality所有寄存器都fail。原因reference RTL没有初值而implementation里寄存器有复位或置位连接两边初始状态不一致。解决检查set_constant或set_dont_verify的设置必要时对复位相关的点统一处理并确认svf里的constant信息被正确读入。6. 把时序和等价性检查变成流程我的验证习惯与进阶技巧6.1 三次LEC的验证节奏我的实用习惯是把LEC分成三次做而不是等到tapeout前去折腾一头雾水。综合完成后跑一次RTL对门级网表CTS完成后跑一次综合后网表对CTS网表route完成后跑一次CTS网表对route网表。每次只引入当前阶段的改动复杂度可控出问题也容易定位。这是业内常见做法具体节点可以根据项目流程微调但原则是“小步验证不要一次跨太大”。6.2 报告解析的小技巧另一个值得养成的习惯是别只看最后一次报告。把PrimeTime和Formality的报告存成文本用几行脚本把slack、失配点、时间戳抓出来拼成一个趋势表grep -E slack|VIOLATED top_setup.rpt | head -20看的是多轮回归里slack的走向是持续恶化还是收敛比单次输出更能判断当前流程是否健康。如果连续几轮修出来的slack都在缓慢下降说明还有收敛空间如果波动很大先查约束或corner是不是变了。我自己的翻车记录是有次改了时钟树后忘了同步svfFormality全线失配我盯着组合逻辑锥查了整整一天最后发现是svf旧了一个版本。从那以后每次综合归档都强制把svf和网表放同一目录、带同一时间戳省掉的调试时间比什么技巧都值。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站