这一篇在干嘛?
静态时序分析(STA,Static Timing Analysis)是工具替你”穷举”设计中所有时序路径的检查手段,不用写测试激励就能确认设计是否满足时序。本章先复习标准分析,再讲 STA 的”疑难杂症”:锁存器、异步电路、组合逻辑反馈环——这些结构在 FPGA 里不推荐用,但万一遇到,你得会分析。
18.1 标准分析
STA 的定义:对照一组约束,对设计中所有时序路径做穷举分析,判断设计是否”时序合规”(timing compliant)。
FPGA 设计者遇到的基本路径有三类:
图18-1 三类基本路径:输入到触发器、触发器到触发器、触发器到输出
每类路径都有各自的时序要求:输入延迟(input delay)、输出延迟(output delay)、建立时间(setup time,时钟沿到来前数据必须提前稳定的时间)和保持时间(hold time,时钟沿到来后数据必须继续保持稳定的时间)。
两组分析:
- Setup 分析 = 长路径分析:数据太慢会违例;
- Hold 分析 = 短路径分析:数据太快(离时钟沿太近)会违例。
设计的最高频率由最长路径(关键路径,critical path)决定。路径分析不只算逻辑延迟,还包括:
图18-2 路径延迟由 clock-to-Q、逻辑、布线、setup 和时钟偏斜组成
最小周期公式(忽略时钟抖动 jitter):
各符号含义:
| 符号 | 含义 |
|---|---|
| 允许的最小时钟周期 | |
| 时钟到达至数据出现在 Q 端的延迟 | |
| 触发器之间逻辑的传播延迟 | |
| 触发器之间的布线延迟 | |
| 建立时间:数据须在下一时钟上升沿前提前到达的时间 | |
| 时钟从发送触发器到捕获触发器的传播延迟差 |
如果要求的周期大于 ,就有正裕量(positive slack):
图18-3 数据在捕获时钟沿减去 setup 之前到达,即为正裕量
数据在”捕获时钟沿 − setup”之前到达 → 正 slack,通过;之后到达 → 负 slack,违例。
Setup 分析同样适用于 I/O:对输入路径,外部芯片的 clock-to-Q 和传播延迟合并成一个”外部延迟”参与分析;输出路径同理。这些外部延迟工具不知道,必须由设计者在约束里定义。
关于 hold 违例:数据在时钟沿后”到得太快”导致,理论上加缓冲就能修。FPGA 里 hold 违例很少见——因为布线矩阵自带最小延迟;一旦出现,通常说明时钟偏斜(clock skew)有问题。
放宽约束:多周期与伪路径
除了准确定义时钟,还要把可以放宽的路径明确告诉工具,最常见的两类:
图18-4 多周期路径:数据允许 n 个周期到达
多周期路径(multicycle path):数据不需要在紧跟着的那个时钟沿被使用,允许 n 个周期传播。注意:setup/hold 要求依然生效——不管数据在哪个时钟沿被采样,只要离采样沿太近,仍可能进入亚稳态(metastable,触发器采样到变化中的数据而输出不确定的状态)。典型场景:DSP 应用中采样率固定、每个采样点有多个时钟周期可用。
伪路径(false path):时序工具从电路结构上”看得到”一条路径,但按设计逻辑,信号根本不可能沿这条路径传播:
图18-5 逻辑上不可能导通的路径,STA 直接忽略
两者关键区别(考试重点):
多周期路径即使约束给到任意大的 n,仍可能时序违例;伪路径永远不会有任何时序违例。
因为多周期路径仍会被检查 setup/hold,伪路径则被彻底排除在分析和优化之外。
STA vs 动态时序分析(仿真)
STA 相对带时序信息的仿真(动态分析)的优势:
- 穷举 vs 抽查:动态分析的覆盖率取决于测试激励编写者的水平,STA 则覆盖标准分析范围内的所有路径;
- 抖动(jitter)等特性很难建模——建模会让 testbench 极复杂、仿真时间爆炸,STA 无此烦恼;
- 各种条件组合时,STA 的运行时间按比例增长,动态仿真则是指数级爆炸;
- STA 不需要仿真周期,没有事件调度器的开销;
- STA 覆盖的违例类型更广:正/负 setup/hold、最小/最大转换时间、时钟偏斜、毛刺检测、总线冲突等;
- STA 工具能自动识别关键路径、违例和异步时钟。
18.2 锁存器(Latches)
锁存器是电平敏感的存储单元(控制信号有效期间数据直通,无效时保持),与边沿触发的触发器(flip-flop)不同。在高速芯片(如微处理器)设计中锁存器有正当用途,但只应被专家级设计者在有充分理由时使用。现实中最常见的情况是:锁存器由糟糕的编码风格无意中制造出来,然后出现在 STA 报告里造成困惑。
锁存器是怎么被无意造出来的
最典型的”锁存器制造机”:
// 注意:会推断出锁存器(LATCH INFERENCE)
module latch (
output reg oDat,
input iClk, iDat);
always @*
if (iClk) oDat <= iDat;
endmodule代码点评:always @* 是纯组合逻辑块,但 if 没有 else 分支——当 iClk 为低时工具只能”保持 oDat 原值”,保持 = 存储 = 锁存器。
更隐蔽的”无意锁存器”长这样:
// 糟糕的编码风格(POOR CODING STYLE)
module latchinduced(
output reg oData,
input iClk, iCtrl,
input iData);
reg rData;
reg wData;
always @*
if (iCtrl) wData <= rData; // 组合块缺 else → 锁存器
always @(posedge iClk) begin
rData <= iData;
oData <= wData;
end
endmodule代码点评:两个标准 D 触发器之间被插入了一个锁存器——输入输出都打了拍,中间却是电平敏感的,这种结构没人会故意写,但缺 else 就够了。
图18-6 两个 D 触发器之间被推断出一个高有效锁存器
STA 怎么分析锁存器
分析”高有效”锁存器(控制为高时数据通过、为低时保持)时:
- 控制为高:数据直通,不关心;
- 控制为低:输出冻结,不关心;
- 真正要检查的是数据被”锁住”的那一刻——控制信号从高变低的边沿。
STA 工具因此把锁存器的控制信号当作时钟来对待,把锁存器当作下降沿触发的触发器来分析:
图18-7 时序检查点:控制信号关闭锁存器的那个边沿
双相锁存(Dual-Phase Latching)
一种正经使用锁存器的流水线拓扑:流水线中一级用时钟的一种极性锁存,相邻级用相反极性锁存:
图18-8 相邻流水级交替使用相反极性的锁存器
四锁存交替极性的实现:
// 注意:会推断出锁存器(LATCH INFERENCE)
module dualphase(
output oData,
input iCtrl, iNCtrl,
input iData);
reg [3:0] wData;
assign oData = wData[3];
always @* begin
if(iCtrl) wData[0] <= iData; // 高有效锁存
if(iNCtrl) wData[1] <= wData[0]; // 低有效锁存
if(iCtrl) wData[2] <= wData[1];
if(iNCtrl) wData[3] <= wData[2];
end
endmodule代码点评:单一时钟产生 iCtrl 与其反相 iNCtrl 两个控制,四个锁存器极性交替,数据像接力一样两级一级传递:
图18-9 时钟两个相位交替控制相邻锁存器
在深度流水的设计(如微处理器)里,双相锁存有两个好处:面积更小(锁存器比触发器小)、功耗更低(时钟的两个边沿都在传数据)。前提是流水线划分非常清晰——没有清晰流水线的电路根本用不了这套方法。
STA 报告的呈现方式:整个设计只有一个时钟域(即锁存器控制信号),所有路径都报告在上升沿(低有效锁存)与下降沿(高有效锁存)之间。
18.3 异步电路
这里的”异步电路”指所有不属于”时钟元件到时钟元件”这种标准分析范畴的电路。这些结构在 FPGA 里通常不推荐使用,列出它们不是鼓励你用,而是万一遇到要知道怎么分析。
18.3.1 组合逻辑反馈(Combinatorial Feedback)
定义:一条从某个网线(net)出发、穿过组合逻辑(AND/OR/MUX 等)、不经过任何同步元件又回到同一网线的路径。
组合反馈通常意味着编码错误——综合工具应报警告,而且会让仿真器出问题(尤其组合延迟为零时)。根据具体电路,组合反馈要么表现为振荡(如反相器+延迟搭的自激振荡器),要么表现为存储元件(交叉耦合 NAND 可以构成基本锁存器和触发器)。
问题在于:这类路径的终点不是时序元件,STA 没法按常规方式测量。解决办法是人为在约束中定义时序端点。看这个自激振荡器的例子:
// 糟糕的编码风格(BAD CODING STYLE)
module freeosc(
output oDat,
input iOscEn, iOutEn);
wire wdat;
assign wdat = iOscEn ? !wdat: 0; // 组合反馈:wdat 依赖自身取反
assign oDat = iOutEn ? wdat : 0;
endmodule代码点评:wdat = !wdat 形成了组合环路,综合成这样的结构——
图18-10 wdat 通过反相器反馈回自身,构成振荡环
STA 默认只会分析从输入到输出的路径,甚至没有数据去测量环路自身的延迟。解决方法:给反馈网线 wdat 加一个时序端点。Xilinx UCF(用户约束文件)写法:
NET "wdat" TPSYNC = "looptime";
TIMESPEC "ts_looptime" = FROM "looptime" TO "looptime" 1 ns;
约束点评:先把 wdat 定义为时序组 looptime 的端点,再约束”从 looptime 到 looptime”的环延迟为 1 ns——这样 STA 就有测量依据了,会报告出约 850 ps 的环路延迟。
要点小结
- 多周期路径给多大 n 仍可能违例;伪路径永远不会有违例——这是两者最本质的区别。
- 锁存器多半是编码疏忽(组合块里 if 缺 else)造出来的。
- STA 分析锁存器时,关注点是控制信号把锁存器”关上”的那个边沿,锁存器等效为下降沿触发器。
- 深度流水设计中,双相锁存能换来更小、更快的实现。
- 组合反馈通常指示编码错误;STA 分析时需人为定义时序端点。
常见坑
组合逻辑块(
always @*)里的if忘写else、case忘写default,都会推断出锁存器——而且它藏在两个正常触发器中间,时序报告看着眼熟却对不上号,极其折磨人。写组合逻辑时养成习惯:每条 if 必有 else,每个 case 必有 default,让所有分支都给信号赋值,锁存器就无处藏身。
通关标准:
学完本篇你应该能做到:① 用 公式拆解一条路径的延迟构成,说清 setup/hold、slack、clock skew 各是什么;② 区分多周期路径和伪路径,知道什么时候该用哪种约束;③ 识别代码中的无意锁存器,并理解 STA 如何把锁存器当作下降沿触发器来分析。
自测:为什么 FPGA 设计中 hold 违例很少见?出现了说明什么?
FPGA 布线矩阵自带最小延迟,数据不可能”太快”到达。一旦出现 hold 违例,通常意味着时钟偏斜(clock skew)出了问题,而不是逻辑本身太快。
自测:多周期路径和伪路径最本质的区别是什么?
多周期路径只是允许数据用 n 个周期到达,setup/hold 仍会被检查,n 再大也可能违例;伪路径是逻辑上根本不可能发生信号传播的路径,被 STA 彻底忽略,永远不会有违例。
自测:STA 如何分析一个高有效锁存器?
把锁存器的控制信号当作时钟,锁存器等效为下降沿触发的触发器。真正检查的时序点是控制信号从高变低、把数据”锁住”的那个边沿;控制为高(直通)和为低(冻结)的状态都不需要检查。
自测:双相锁存为什么能省面积、降功耗?限制是什么?
锁存器比触发器面积小;时钟的两个边沿都被用来传递数据,等效每个相位都在工作。限制是要求电路有非常清晰、规则的流水线划分,否则交替极性的锁存链根本搭不起来。
自测:STA 遇到组合反馈环为什么会"没办法"?怎么解决?
组合反馈环的起点和终点都是网线而非时序元件,STA 默认只分析输入到输出的路径,没有数据测量环路自身的延迟。解决方法是在约束中人为给反馈网线定义时序端点(如 UCF 中的 TPSYNC + TIMESPEC FROM … TO …),让 STA 有测量基准。