本页属于关于时序的系列文章之一。上一页介绍了时序约束背后的基础理论。下一步是看这套理论如何应用。
概述
本页介绍最重要的时序约束(timing constraint),它定义时钟的频率。随后,我们会用一个详细示例来展示如何对一条路径(path)的时序报告进行分析。
时序报告的细节看起来像是一个进阶话题,但其实不然:时序约束的目的是控制工具对设计进行的时序分析,并设定准确且必须满足的要求。因此,要理解时序约束,就必须深入了解工具所进行的时序分析(timing analysis)。而时序分析的结果会显示在时序报告中。
另外,仅仅写出时序约束是远远不够的。重要的是能够验证这些约束在设计中确实正确地生效。这种验证只有在深刻理解时序报告的前提下才可能做到。如果没有这种理解,很容易误用那些实际上并未起到预期作用的时序约束,然后还奇怪为什么逻辑设计不能正常工作。
所有 FPGA 工具都会生成文本格式的时序报告,下面展示的就是这类报告。这些工具也提供图形界面来显示完全相同的信息。这个图形界面有时有帮助,有时反而令人困惑,因此先从文本报告入手是重要的一步。
周期约束
周期约束(period constraint)是最重要的时序约束:它告诉工具时钟信号的频率。
例如,假设逻辑设计只包含下面这个 Verilog 模块:
module top(
input clk,
input foo,
output reg bar_reg);
reg foo_reg;
reg bar;
always @(posedge clk)
begin
foo_reg <= foo;
bar <= !foo_reg;
bar_reg <= bar;
end
endmodule
从 Verilog 可以清楚地看出 @clk 被用作时钟,而且该信号是一个外部端口(也就是说,它连接 FPGA 上的一个物理引脚)。
如果 @clk 的频率是 250 MHz(周期 4 ns),就需要类似下面的时序约束:
create_clock -period 4 -name clk [get_ports clk]
这条命令的含义是:“在名为 clk 的 I/O 端口上有一个时钟,该时钟的周期是 4 ns。如果还有其他时序约束要引用这个时钟,它们将使用名字 'clk' 来引用。”
注意:
- period 参数的值应该是时钟的时钟周期。不要为了“更保险”而选一个更小的值(过度约束,overconstraining)。任何时候都不应这样做。如果这样做有作用,那说明存在另一个需要解决的问题。
- 如果使用了异步复位(asynchronous reset),create_clock 可能不会保护触发器免受该复位信号上时序要求违例的影响。这一点在另一页有解释。
时序分析
下面我们来看 Vivado 对从 @foo_reg 开始、在 @bar 结束的那条路径的建立时间要求(setup requirement)所进行的时序分析。换句话说,这条路径对应下面这句 Verilog 表达式:
bar <= !foo_reg;
这里展示的时序分析由三个部分组成,它们在时序报告中按以下顺序出现:
- 报告头(header),包含该路径时序分析的结果摘要,以及一些附加信息。
- 一个计算:从某个时钟边沿开始,到第二个触发器(本例中为 @bar)的输入端出现稳定逻辑状态为止,一共需要多少时间。
- 一个计算:为了满足时序要求,第二个触发器的输入必须在什么时候之前保持稳定。
这三个部分下面会分别展示和说明。注意,在时序报告里,这三个部分之间没有可见的分隔线。尤其是不太容易看出第二部分在哪里结束、第三部分从哪里开始。报告读者需要根据内容认出第三部分的起始位置。
虽然这里展示的是 Vivado 的时序报告,但 Quartus 以及其他多种 FPGA 工具也采用同样的方法。其他工具的时序报告中写的信息通常略有差异,但背后的理论是一样的。因此,仔细读这个示例对其他 FPGA 工具也有帮助。
我们先跳过时序报告的第一部分,等讨论完前两部分之后再回头来看它。按这个顺序解释报告会更容易。不过,先讲几点一般性的内容。
时序分析的策略
与上一页展示的简单静态时序分析(static timing analysis)不同,真正的时序分析必须把时钟的不完美性考虑进去。为此,延迟计算要从时钟的源头开始,例如从时钟的物理输入引脚开始。这与简单的数据路径延迟计算不同(如上一页所示),后者只考虑数据路径本身的延迟。
于是,这个理论实验变成这样:在时钟源头处,让秒表与某一个时钟边沿同时启动。让秒表继续走,直到这个时钟边沿到达第一个触发器并触发它。接着继续计时,测出该触发器更新其输出的时间,并跟随更新后的信号到达目的地。当这个信号到达第二个触发器时,让秒表停止。
下一步是检查结果是否合格。对于 tsu 要求来说,意思是相对于下一个时钟边沿,信号是否到得足够早。
但这是到达第二个触发器的时钟边沿。它什么时候到达?
为了找到答案,秒表再次启动,这次与时钟源头的下一个时钟边沿同时启动。当第二个时钟边沿到达第二个触发器时停表。两次实验的起点相同,但这一次时钟边沿更晚,而且时钟边沿的目的地也不同。
经过这个理论实验,我们知道了时钟边沿何时到达第二个触发器。为了满足 tsu,相对于这个时钟边沿,该触发器的输入必须在足够早的时刻保持稳定。
第一个实验的秒表显示数据在第二个触发器处何时稳定;第二个实验的秒表显示时钟边沿何时到达同一个触发器。剩下要做的就是比较这两个数,检查它们之差是否大于 tsu。
这种方法可以把与时钟有关的不确定性考虑进去,因为它允许在两个理论实验中分别套用最坏情况。在建立时间计算中,第一个秒表实验里的所有延迟都取上限,因此计算结果给出的是信号可能到达第二个触发器输入端的最后时刻。但在第二个秒表实验里,所有延迟取下限,结果就是第二个时钟边沿可能到达第二个触发器的最早时刻。
所以,这个计算针对的是信号尽量晚到、而时钟边沿尽量早到的场景。如果在这种条件下建立时间要求仍能满足,那么毫无疑问,这个要求在正常情况下也总能满足。
对于保持时间(hold time)计算则反过来:第一个实验使用最小延迟,第二个实验使用最大延迟。
源端路径的计算
如上所述,时序报告的第一部分稍后再展示。所以现在我们来看时序分析的第二部分:源端路径的计算。它由两段组成:
- 源时钟路径(Source Clock Path),起点是 FPGA 时钟输入引脚上的上升沿,终点是该上升沿到达 @foo_reg 的时钟输入端。
- 数据路径(Data Path),它接着往前,直到 @bar 的数据输入被更新为新值为止。这一段正好对应上一页展示的简单静态时序分析。
这两段合在一起,计算出从 FPGA 外部时钟引脚上的上升沿开始,到数据到达第二个触发器为止的延迟。
时序报告中相关部分如下:
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
------------------------------------------------------------------- -------------------
(clock clk rise edge) 0.000 0.000 r
AG12 0.000 0.000 r clk (IN)
net (fo=0) 0.000 0.000 clk_IBUF_inst/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.738 0.738 r clk_IBUF_inst/INBUF_INST/O
net (fo=1, routed) 0.105 0.843 clk_IBUF_inst/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.049 0.892 r clk_IBUF_inst/IBUFCTRL_INST/O
net (fo=1, routed) 0.839 1.731 clk_IBUF
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.101 1.832 r clk_IBUF_BUFG_inst/O
X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.389 3.221 clk_IBUF_BUFG
SLICE_X49Y58 FDRE r foo_reg_reg/C
------------------------------------------------------------------- -------------------
SLICE_X49Y58 FDRE (Prop_EFF2_SLICEL_C_Q)
0.138 3.359 f foo_reg_reg/Q
net (fo=1, routed) 0.241 3.600 foo_reg
SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O)
0.244 3.844 r bar__0_i_1/O
net (fo=1, routed) 0.046 3.890 p_0_in
SLICE_X49Y58 FDRE r bar_reg__0/D
------------------------------------------------------------------- -------------------
在这个部分中,每一行代表一个逻辑元件或一根导线。当 Delay type 为 net 时,该行对应一根导线。所有这种类型的延迟都是布线延迟(routing delay),也就是信号从一个逻辑元件传播到另一个逻辑元件所需的时间。
Incr 列显示路径中每个元件贡献的延迟量,Path 列显示到该点为止的总延迟。
中间的水平线标志着源时钟路径结束、数据路径开始。下面仔细看看这两条路径的延迟。
前七项延迟都与输入引脚有关,即 AG12(这是该引脚的物理位置)。这份报告显示,输入引脚以及与该引脚相关的逻辑合计贡献了 1.731 ns。
全局时钟缓冲器(global clock buffer)从输入到输出又贡献了 0.101 ns。接下来是全局时钟的布线延迟,这个值相对较大:1.389 ns。这是时钟信号到达 @foo_reg 时钟输入端所需的时间。延迟之所以这么大,是因为使用了一个全局时钟缓冲器和时钟树来分配这个信号。这类布线资源的本意是把时钟分配到 FPGA 中很大的区域,并以相同的延迟到达所有目的地。因此,即使时钟只到达少数几个目的地(例如本例中时钟的扇出(fan-out)只有 3),延迟也仍然会比较大。
此时,时钟终于到达了包含该触发器的切片(slice),本例中是 SLICE_X49Y58。这里就是源时钟路径的终点。报告中的水平线表示数据路径开始。在上一页的例子中,简单的静态时序分析正是从这里开始的。
在右侧的 Netlist Resources 列中,水平线上面的那一行写着 foo_reg_reg/C,紧接着水平线下面的那一行写着 foo_reg_reg/Q。因此,水平线后的这一行就是 @foo_reg 的触发器从时钟到输出的延迟(clock-to-output delay,从 C 到 Q)。这个延迟是 0.138 ns。这里的 FDRE 表示带数据(Data)、复位(Reset)和使能(Enable)的触发器。
随后是到 LUT 的布线延迟(0.241 ns)、LUT 内部的传播延迟(propagation delay)(0.244 ns),以及到第二个触发器的布线延迟(0.046 ns)。这些布线延迟异常小,因为所有元件都被打包进了同一个切片(slice)。
总结一下这一部分:时钟边沿从外部引脚传到第一个触发器的时钟输入用了 3.221 ns。之后,更新后的信号到达第二个触发器的输入又用了 0.669 ns(3.890 − 3.221 = 0.669 ns)。合计下来,从外部引脚上的时钟边沿开始,到最终目的地(第二个触发器的数据输入)出现稳定信号,一共是 3.890 ns(这是最坏情况计算,也就是最多需要这么长时间)。
那么现在的问题是,它到得够不够早?建立时间要求是否满足?
目的时钟路径
第二个计算的目的是求出时钟边沿从外部引脚到达第二个触发器时钟输入端的最短时间(即最早到达时刻)。
注意,这条时钟路径的目的地是第二个触发器,不要与上一次计算中目的地为第一个触发器的时钟路径相混淆。
两者还有其他显著差异:
- 只计算时钟路径。换句话说,与上一次计算相比,只考虑水平线之前的这一段。
- 理论秒表从 4 ns 开始,而不是从 0 开始。这是因为本次计算的目的是判断数据信号是否早到到足以满足建立时间要求。因此,计算从第二个时钟边沿开始;根据 clk 的时序约束(也就是上面给出的 create_clock 命令),这个边沿距第一个边沿 4 ns。
- 这一段上每个元件的延迟都更小。在本例中这一现象很容易看出来,因为两个触发器位于同一个切片(slice)上,两次计算中的时钟路径完全相同(通常情况下不是这样)。后面还会再提到这一点。
- 在这项计算的最后三行(clock pessimism、clock uncertainty 以及 Setup_DFF2_SLICEL_C_D)不再是逻辑元件,而是时序参数。
时序分析的第三部分(目的时钟路径)如下:
(clock clk rise edge) 4.000 4.000 r
AG12 0.000 4.000 r clk (IN)
net (fo=0) 0.000 4.000 clk_IBUF_inst/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.515 4.515 r clk_IBUF_inst/INBUF_INST/O
net (fo=1, routed) 0.066 4.581 clk_IBUF_inst/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.034 4.615 r clk_IBUF_inst/IBUFCTRL_INST/O
net (fo=1, routed) 0.722 5.337 clk_IBUF
BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.091 5.428 r clk_IBUF_BUFG_inst/O
X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.218 6.646 clk_IBUF_BUFG
SLICE_X49Y58 FDRE r bar_reg__0/C
clock pessimism 0.527 7.173
clock uncertainty -0.035 7.138
SLICE_X49Y58 FDRE (Setup_DFF2_SLICEL_C_D)
0.067 7.205 bar_reg__0
-------------------------------------------------------------------
required time 7.205
arrival time -3.890
-------------------------------------------------------------------
slack 3.315
工具选择把 @foo_reg 和 @bar 放在同一个切片(slice)上,因此在本例中,本次分析的时钟路径与第一次分析完全相同。所以很容易看出,在水平线之前,逻辑元件的顺序与上一次分析一模一样。越过这条线之后,有两个时序参数会对计算进行修正:时钟悲观(clock pessimism)和时钟不确定性(clock uncertainty)。这两个参数会在本页末尾分别讨论。
在这项计算的倒数第二行,我们得到第二个时钟边沿可能到达的最早时间:在第一个时钟边沿之后 7.138 ns。建立时间要求数据信号必须在这个边沿之前保持稳定,tsu 给出的是需要提前多少。因此,要得到数据必须稳定的最晚时间,需要从时钟到达时间中减去 tsu。
在上面的例子中,tsu 是负值,等于 -0.067 ns。因此最终结果是 7.138 −(−0.067)= 7.205 ns。也就是说,数据最迟要在第一个时钟边沿之后 7.205 ns 时稳定在第二个触发器的输入端。
上一次计算的结果是:在最坏情况下,数据在这个时钟边沿之后 3.890 ns 时已经稳定。因此这足够好,而且还有裕量:要求的值与能够保证的值之差是 7.205 − 3.890 = 3.315 ns。换句话说,时序余量(slack)为 3.315 ns。
时序分析小结
现在回到该路径时序报告的第一部分。这部分位于上面展示的计算之前,它汇总了主要结果,并解释了计算中出现的一些数值。
记住,一条路径的时序分析是这样开头的:
Slack (MET) : 3.315ns (required time - arrival time)
Source: foo_reg_reg/C
(rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@2.000ns period=4.000ns})
Destination: bar_reg__0/D
(rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@2.000ns period=4.000ns})
Path Group: clk
Path Type: Setup (Max at Slow Process Corner)
Requirement: 4.000ns (clk rise@4.000ns - clk rise@0.000ns)
Data Path Delay: 0.669ns (logic 0.382ns (57.100%) route 0.287ns (42.900%))
Logic Levels: 1 (LUT1=1)
Clock Path Skew: -0.048ns (DCD - SCD + CPR)
Destination Clock Delay (DCD): 2.646ns = ( 6.646 - 4.000 )
Source Clock Delay (SCD): 3.221ns
Clock Pessimism Removal (CPR): 0.527ns
Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Total Input Jitter (TIJ): 0.000ns
Discrete Jitter (DJ): 0.000ns
Phase Error (PE): 0.000ns
Clock Net Delay (Source): 1.389ns (routing 0.002ns, distribution 1.387ns)
Clock Net Delay (Destination): 1.218ns (routing 0.002ns, distribution 1.216ns)
这一部分包含很多不同的信息,所以我一条一条来说明。先从第一行开始:
Slack (MET) : 3.315ns (required time - arrival time)
这一行表示时序约束已经满足(met),而且还有剩余的时间(时序余量,slack),为 3.315 ns。
Source: foo_reg_reg/C
(rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@2.000ns period=4.000ns})
Destination: bar_reg__0/D
(rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@2.000ns period=4.000ns})
这些行说明所检查的是哪一条数据路径。路径由起点(Source)和终点(Destination)定义。数据路径从 foo_reg_reg/C 开始,也就是 @foo_reg 的时钟输入;结束于 bar_reg__0/D,也就是 @bar 的数据输入。
另外,这里提到了 clk,并描述了它的波形。注意,这里的 clk 指的是 create_clock 命令给这个时钟起的名字。在本例中,clk 恰好也同时是时钟信号本身的名字。但如果时序约束的 -name 参数里用了另一个名字,那么无论信号实际叫什么,时序报告中都会出现那个名字。本示例时序报告中所有提到 clk 的地方都是如此。
Path Group: clk
这里的路径组(Path Group)是 clk,这表明检查这条路径的原因是与它同名的时序约束。
Path Type: Setup (Max at Slow Process Corner)
路径类型(Path Type)是 Setup,表示检查的是建立时间要求。“Max at Slow Process Corner”表示在数据路径(也就是第一次计算)中使用了最大延迟。这里的 Corner 在本语境中的含义将在后面解释。
Requirement: 4.000ns (clk rise@4.000ns - clk rise@0.000ns)
回想一下,上面的两个理论实验各有一个假想秒表,并各有一个启动时刻。“Requirement”就是这两个秒表启动时刻之间的时间差。本例中也就是时序约束要求的时钟周期,即 4 ns。
Data Path Delay: 0.669ns (logic 0.382ns (57.100%) route 0.287ns (42.900%))
Data Path Delay 是第一次计算中水平线之后所有延迟之和(也就是数据路径的延迟)。
在这一行里,延迟还按逻辑和布线分别列出。这可以显示出逻辑元件上花了多少时间,元件之间的线上又花了多少时间。通常的经验法则是:大约 60% 的延迟应来自逻辑,其余来自布线。因此,本例中的路径显示了正常情况。
如果布线延迟所占比例明显偏大,就可能说明存在问题,尤其是当这条路径无法满足时序约束时。关于这一点,在下一页中讨论 thold 分析的小节还会进一步说明。
Logic Levels: 1 (LUT1=1)
数据路径只包含一个 LUT,因此逻辑级数(logic levels)为 1。
Clock Path Skew: -0.048ns (DCD - SCD + CPR)
Destination Clock Delay (DCD): 2.646ns = ( 6.646 - 4.000 )
Source Clock Delay (SCD): 3.221ns
Clock Pessimism Removal (CPR): 0.527ns
时钟路径偏斜(clock skew)是同一个时钟边沿到达两个触发器之间的时间差。这个值是一个计算出来的最坏情况,其中包含了一条时钟路径使用最大延迟、另一条时钟路径使用最小延迟的假设。请参阅下面“时钟悲观消除”一节,它解释了这些行背后的算术。
Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
Total System Jitter (TSJ): 0.071ns
Total Input Jitter (TIJ): 0.000ns
Discrete Jitter (DJ): 0.000ns
Phase Error (PE): 0.000ns
请参阅下面的“时钟不确定性(clock uncertainty)”一节。这些行显示了时钟不确定性是如何计算的。计算结果 0.035 ns 乐观得不真实。原因是没有在时序约束中指定抖动(jitter),所以工具假定抖动为零。此外,外部时钟被直接使用(没有经过 FPGA 内部的 PLL),所以几乎没有任何抖动来源需要计入。
虽然不在时序约束中指定外部时钟的抖动是个错误,但通常人们就是这么做的。这很少成为问题的来源,因为与时序计算中考虑的其他量相比,抖动通常很小。
Clock Net Delay (Source): 1.389ns (routing 0.002ns, distribution 1.387ns) Clock Net Delay (Destination): 1.218ns (routing 0.002ns, distribution 1.216ns)
这些是时钟信号本身的延迟,也就是从时钟缓冲器输出端到每个触发器时钟输入端的延迟。在本例中,这些数字没有太多可供解读的信息。
多工艺角时序分析
所有逻辑元件的延迟都取决于少数几个未知参数,例如温度和供电电压。“工艺”(process)一词用来指 FPGA 生产过程中出现的不精确性。尽管每一颗 FPGA 都会经过测试,以保证它符合数据手册,但每个逻辑元件的行为仍有一定的不确定性。
FPGA 工具会在少数几个极端场景下对每条路径进行时序分析,这些场景称为工艺角(corner)。例如,一个场景可以是最低允许温度加上最快工艺(也就是说这颗 FPGA 恰好以低延迟被制造出来);第二个场景是最高允许温度加上最快工艺;第三和第四个场景则把最快工艺换成最慢工艺,再做一遍。
因此,本例中的时序分析覆盖的是两个参数的极端情况:温度和工艺。这叫做四角时序分析(four-corner timing analysis)。但这只是进行多工艺角时序分析的其中一种做法。不同 FPGA 工具进行时序分析的方式各有不同。
不过,无论工具选择考察哪些工艺角,最终结果都会取最坏情况。也就是说,工具会计算每个工艺角下的时序余量(slack),并以其中最小的余量为准。
工具已经被编程为针对每款 FPGA 执行合适的多工艺角时序分析,所以没有必要深入理解这个主题。但在阅读时序报告时,重要的是要弄清报告是只涉及某一个工艺角,还是对所有工艺角做了汇总(也就是最坏情况)。这里是有可能造成混淆的,尤其是在 Quartus 中。
下面将要讨论的是时钟悲观消除(clock pessimism removal)和时钟不确定性(clock uncertainty)。这些是相对进阶的话题。如果你对时序计算中更细的细节不感兴趣,完全可以跳到本系列的下一页。
时钟悲观消除
因为两个触发器碰巧位于同一个切片(slice)上,所以可以直接比较两次计算中的时钟路径延迟(也就是时钟边沿到达该切片所花的时间)。第二次计算中这个时间是 2.646 ns,因为计算从 4 ns 开始、到 6.646 ns 结束,所以是 6.646 − 4 = 2.646 ns。上一次计算的结果是 3.221 ns。两者相差 0.575 ns。
造成这种差异的原因是,第一次计算使用了最大延迟,第二次计算使用了最小延迟。最小延迟与最大延迟之间的差异,反映了这样一个事实:由于制造工艺固有的误差,这些延迟无法被精确知道。因此,如果到达第一个触发器的时钟路径与到达第二个触发器的时钟路径完全不同,就必须考虑最坏情况。这种最坏情况是:第一条路径的所有延迟都取最大允许值,第二条路径的所有延迟都取最小允许值。这就是所谓的时钟悲观(clock pessimism)。
注意,这跟温度或不同 FPGA 个体之间的差异无关:两次计算都针对相同的温度和制造工艺。之所以会有差异,是因为这一段中每个延迟都有一定的容差,而这些容差落在 FPGA 的规格范围内。
但如果两条时钟路径完全相同,为什么计算中还要加入时钟悲观呢?答案是,这样做本身就是个错误。正因为如此,目的时钟路径中才有一行标题为 clock pessimism。这一行给延迟增加 0.527 ns,用来补偿这个错误。这一行其实应该叫作“clock pessimism removal”(时钟悲观消除,CPR)。
因此,时钟悲观消除(clock pessimism removal,CPR)的思路是去掉公共时钟路径上最小延迟与最大延迟之间那些不必要的差异。工具会比较两次计算的时钟路径,找出两条路径共同的那一段。在这段共享路径上所有延迟差异之和,就是应被消除的时钟悲观量。
如前面所述,两次时钟路径计算相差 0.575 ns,但实际施加的时钟悲观只有 0.527 ns,所以少减了 0.048 ns。原因是在切片(slice)内部还有一小段不属于两条路径的公共部分:切片内有一根导线通往第一个触发器,另一根导线通往第二个触发器,这两根导线的延迟可能不同。
时钟不确定性
在这次时序计算中,有 0.035 ns 是从时钟路径的计算中减掉的。这使时序计算变得更严格。
时钟不确定性(clock uncertainty)要计入的是两个相邻时钟边沿之间时间间隔中一切随机变化。这种随机性称为时钟抖动(clock jitter),是各种噪声源以及电子器件随机行为共同造成的结果。
减去 0.035 ns 表示这样一个事实:即使时序约束(create_clock)把时钟周期定义为 4 ns,在实际中这个周期也是随机的。因此,两个时钟边沿之间的时间可能比 4 ns 短。会短多少?在本次计算中,假设两个时钟边沿之间的时间绝不会小于 3.965 ns(4 − 0.035 = 3.965 ns)。
这个假设站得住脚吗?这是一个难题,因为对时钟抖动的估计本身就是一个复杂课题,远远超出本次关于时序约束的讨论范围。尽管如此,还是建议你进一步学习这一课题,因为无论有无时序约束,时钟抖动都可能成为逻辑设计中各种问题的来源。
到这里,关于时钟周期约束的两页文章中的第一页就结束了。下一页还有更多内容……