此页面属于关于时序(timing)的一系列页面。在简要介绍了时序约束(timing constraints)背后的理论和关于时钟周期约束的第一页之后,是时候看看这个约束(constraint)的几个现实场景了。
下一步: 使用锁相环(PLL)
在上一页所示的示例中,外部时钟引脚直接连接到逻辑。在大多数现实生活中的设计中,某种锁相环用于创建逻辑使用的时钟。这样做最明显的原因是逻辑需要与外部时钟不同的频率。但是使用锁相环也可以帮助清除外部时钟的缺陷,尤其是抖动(jitter)。
锁相环可以用 Verilog 代码添加到设计,代码如下:
module top(
input clk,
input foo,
output reg bar_reg
);
reg foo_reg;
reg bar;
wire pll_clk;
clk_wiz_0 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk));
always @(posedge pll_clk)
begin
foo_reg <= foo;
bar <= !foo_reg;
bar_reg <= bar;
end
endmodule
这和前面的例子一样,但是这次触发器(flip-flops)的时钟是 @pll_clk 而不是 @clk。锁相环由 Vivado的 Clocking Wizard IP生成,在 Verilog 代码中用作模块,名称为 clk_wiz_0。
对于此示例, Clocking Wizard 已配置为接受 250 MHz 参考时钟,并在输出端口(output port)(即 @clk_out1)上创建 125 MHz 时钟。为了使示例保持简单, clk_wiz_0 没有复位输入(reset input)或 "locked" 输出。在大多数现实生活中的设计中,建议启用这些端口并使用它们。
clk_wiz_0 的另一件事是它启用了相位 alignment (phase alignment)选项,因此它将 @pll_clk的时钟边沿(clock edges)与 @clk的时钟边沿对齐。当设计具有与外部时钟同步的 I/O 端口时,此选项很有用: 由于启用了相位 alignment,外部时钟和内部时钟之间的时序关系变得可预测。这对于必须满足与外部时钟相关的时序要求的 I/O 端口非常有用。
准确的说 Xilinx的术语, Xilinx的 FPGAs 有两种锁相环: 一种称为 PLL ,第二种称为 MMCM。就此示例而言,差异无关紧要。 clk_wiz_0 是 MMCM,但为了清楚起见,我将使用术语锁相环来指代它。
再次值得一提的是,这里所说的锁相环,适用于市面上所有的 FPGAs 。此示例使用 Vivado显示,但可以为所有 FPGAs生成与 clk_wiz_0 完全相同的锁相环。
时序约束与锁相环
关于带有锁相环的时序约束,最重要的是要了解它没有什么特别之处。时序约束是相对于外部引脚(本例中为@clk )编写的,如果锁相环生成具有其他频率的时钟,则工具的工作就是将其考虑在内。
值得再说一遍: 永远不需要编写额外的时序约束,因为使用了锁相环。如果您觉得有必要这样做,那么您的设计很有可能出现了问题,而使用额外的时序约束来“修复”问题并不能解决真正的问题。
所以和以前一样,时序约束就是这样:
create_clock -period 4.000 -name clk [get_ports clk]
使用 Quartus 的人应该知道此页面上描述的陷阱。
时序报告(timing report)与锁相环
我们现在将查看与上一页示例中相同的路径(path)的时序报告 。唯一不同的是增加了锁相环,如上图。此处对相关路径的分析显示顺序与时序报告中出现的顺序相同。首先,这是分析总结:
Slack (MET) : 7.288ns (required time - arrival time) Source: foo_reg_reg/C (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0 {rise@0.000ns fall@4.000ns period=8.000ns}) Destination: bar_reg__0/D (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_0 {rise@0.000ns fall@4.000ns period=8.000ns}) Path Group: clk_out1_clk_wiz_0 Path Type: Setup (Max at Slow Process Corner) Requirement: 8.000ns (clk_out1_clk_wiz_0 rise@8.000ns - clk_out1_clk_wiz_0 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): -0.715ns = ( 7.285 - 8.000 ) Source Clock Delay (SCD): -0.616ns Clock Pessimism Removal (CPR): 0.051ns Clock Uncertainty: 0.062ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns 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)
有几个显着差异。首先, Requirement 是 8 ns,而不是之前的 4 ns 。这是意料之中的,因为锁相环的输出(output)是 125 MHz。这些工具自动为此输出创建了一个额外的时序约束,时钟周期(clock period)等于 8 ns。由于时钟周期变长了 4 ns 而数据路径(data path)保持不变,因此 slack 增加了大约 4 ns。
自动时序约束的另一个迹象是,路径 Group (Path Group)在这个报告(report)中显示为“clk_out1_clk_wiz_0”。之前它是“clk”。事实上,它在这个报告中显示为“clk_out1_clk_wiz_0”,而之前它是“clk”。下面将在多个时钟的上下文中讨论自动时序约束在时序报告中的表示。
锁相环的一个较小后果是时钟 Uncertainty (Clock Uncertainty)已经上升到 0.062 ns: 在前面的例子中是 0.035 ns 。这是因为 Discrete 抖动现在是 0.103 ns,而不是零。
现在让我们来看看时序分析(timing analysis)本身。这次,源时钟路径(Source Clock Path)、数据路径和目的地时钟路径(Destination Clock Path)一起展示,与真正的报告(report)完全一样:
Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_0 rise edge) 0.000 0.000 r AG12 0.000 0.000 r clk (IN) net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.738 0.738 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.105 0.843 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.049 0.892 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.975 1.867 pll_i/inst/clk_in1_clk_wiz_0 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -4.474 -2.607 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.501 -2.106 pll_i/inst/clk_out1_clk_wiz_0 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.101 -2.005 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.389 -0.616 pll_clk SLICE_X49Y58 FDRE r foo_reg_reg/C ------------------------------------------------------------------- ------------------- SLICE_X49Y58 FDRE (Prop_EFF2_SLICEL_C_Q) 0.138 -0.478 f foo_reg_reg/Q net (fo=1, routed) 0.241 -0.237 foo_reg SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O) 0.244 0.007 r bar__0_i_1/O net (fo=1, routed) 0.046 0.053 p_0_in SLICE_X49Y58 FDRE r bar_reg__0/D ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_0 rise edge) 8.000 8.000 r AG12 0.000 8.000 r clk (IN) net (fo=0) 0.000 8.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.515 8.515 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.066 8.581 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.034 8.615 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.873 9.488 pll_i/inst/clk_in1_clk_wiz_0 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -3.934 5.554 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.422 5.976 pll_i/inst/clk_out1_clk_wiz_0 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.091 6.067 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=3, routed) 1.218 7.285 pll_clk SLICE_X49Y58 FDRE r bar_reg__0/C clock pessimism 0.051 7.336 clock uncertainty -0.062 7.274 SLICE_X49Y58 FDRE (Setup_DFF2_SLICEL_C_D) 0.067 7.341 bar_reg__0 ------------------------------------------------------------------- required time 7.341 arrival time -0.053 ------------------------------------------------------------------- slack 7.288
路径与之前的例子相比,只有一处不同: 锁相环(在报告中显示为 MMCME3_ADV_X1Y0 )已插入外部时钟引脚和全局时钟缓冲器(global clock buffer)之间。
这个锁相环(PLL)有一个戏剧性的效果: 在源时钟路径中,锁相环的时延(delay)是 −4.474 ns,在目的地时钟路径中同样是时延是 −3.934 ns。这个负的时延代表锁相环调整时钟边沿使得全局时钟在输入引脚处比时钟稍早。请注意,时钟在全局时钟缓冲器的输出处的总时延是源时钟路径中的 −0.616 ns 。同样的时延就是目的地时钟路径中的 7.285 ns 。这是 0.715 ns ,早于第二个时钟边沿(clock edge)(位于 8 ns)。
也就是说,外接的时钟引脚和全局时钟(即交付给逻辑)在两条时钟路径(clock paths)上的时间差几乎是一样的。尽管一条时钟路径是用最大的时延计算的,第二条时钟路径是用最小的时延计算的,但总的结果几乎是一样的。
这不是巧合: 锁相环把全局时钟输出(global clock output)当作参考(reference)使用,所以调整全局时钟缓冲器的输入的相位(phase),保证与时钟输入引脚(clock input pin)的关系。最小时延和最大时延之间的差异由锁相环补偿。时序计算反映了这一点,因为最快和最慢情况之间的差异只是 0.1 ns。由于没有锁相环(0.575 ns,请参阅上一页的“时钟 pessimism removal(Clock pessimism removal)”),因此前一示例中的差异要大得多。
为什么全局时钟在外置输入之前调整到 0.6 ns 左右,而不是任何其他值是另一回事。这使得在许多情况下更容易实现与 I/O 引脚相关的时序约束,因此工具会自动做出此选择。尽管如此,所有 FPGAs 都可以选择操纵此时延。
两个 related 时钟
通常,一个逻辑设计需要多个时钟。 FPGA 设计中多个时钟的存在是它自己的话题,在时钟域(clock domains)的介绍中讨论。时钟域和时序这两个主题是密不可分的,因此建议在继续阅读之前通读该介绍(可能是简短的)。由于这两个主题之间的密切关系,该介绍与本系列页面之间存在一些重叠。
在下面的讨论中,我会经常使用“信号 X (signal X)与 @clk同步”这样的表述。这意味着 X 是触发器的输出,它仅响应名称为“clk”的时钟的上升沿(rising edge)更改值(异步复位(asynchronous resets)除外,但这无关紧要)。自然地,如果两个信号是触发器的输出响应同一个时钟,那么这两个信号就是“同步同一个时钟”。
我们现在将查看由同一个锁相环生成的两个时钟。这很有趣,因为在大多数情况下,这两个时钟被认为是 related 时钟。要理解为什么这很有趣,假设有一个触发器(flip-flop)与其中一个时钟同步,另一个触发器与第二个时钟同步。在这种情况下,在这两个触发器之间连接信号就可以了,就好像它们与同一个时钟同步一样。 FPGA 工具确保在这种情况下满足时序要求。
为了更好地了解如何使用多个时钟,有一个关于 related 时钟和 unrelated 时钟的单独页面。那一页是跨时钟域(clock domain crossing)的介绍。在这里,我们重点关注 related 时钟的时序方面,特别是与此类时钟相关的时序报告。
为了生成下面的时序报告,示例中使用了不同的锁相环(即时钟 Wizard IP(Clocking Wizard IP))。这个新的锁相环的名字是 clk_wiz_1。使用了以下 Verilog 代码:
module top(
input clk,
input foo,
output reg bar_reg
);
reg foo_reg;
reg bar;
wire pll_clk_8, pll_clk_6;
clk_wiz_1 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk_8),
.clk_out2(pll_clk_6));
always @(posedge pll_clk_8)
foo_reg <= foo;
always @(posedge pll_clk_6)
begin
bar <= !foo_reg;
bar_reg <= bar;
end
和之前一样,这个锁相环(即 @clk)的输入是 250 MHz,但是它有两个输出(outputs): 一个连接到 @pll_clk_8,它运行在 125 MHz (即时钟周期是 8 ns,因此是信号的名称)。这与前面示例的 @pll_clk完全一样。第二个输出连接到 @pll_clk_6,它有一个时钟周期(clock period)等于 6 ns,大约是 166.67 MHz。
因为 @pll_clk_8 和 @pll_clk_6 是由同一个锁相环生成的,所以是 related 时钟。
clk_wiz_1 也启用了相位 alignment 选项,特别是为了与前面的示例保持一致。如果对此有任何疑问: 时序约束依然只有一颗,和之前一样:
create_clock -period 4.000 -name clk [get_ports clk]
了解 clock summary
有关所有时钟的信息都总结在时序报告的开头。我现在才提起这一点,因为当有多个时钟时,时钟 summary (clock summary)更有趣。所有 FPGA 工具都会在报告中生成此类摘要,查看此部分总是一个好主意:
------------------------------------------------------------------------- | Clock Summary | ------------- ------------------------------------------------------------------------- Clock Waveform(ns) Period(ns) Frequency(MHz) ----- ------------ ---------- -------------- clk {0.000 2.000} 4.000 250.000 clk_out1_clk_wiz_1 {0.000 4.000} 8.000 125.000 clk_out2_clk_wiz_1 {0.000 3.000} 6.000 166.667 clkfbout_clk_wiz_1 {0.000 2.000} 4.000 250.000
这部分的主要优点是它很容易理解,也很容易检查关于时序约束的最常见错误: 如果时钟具有正确的频率。
此时钟 summary 表明时序约束已被正确解释: 有一个外部时钟定义为 clk,其时钟周期为 4 ns。除此之外,时钟还有三款衍生品: clk_out1_clk_wiz_1、 clk_out2_clk_wiz_1 和 clkfbout_clk_wiz_1。顾名思义,它们是由于锁相环自动生成的,名称为 clk_wiz_1。
前两个时钟(clk_out1_clk_wiz_1 和 clk_out2_clk_wiz_1)是锁相环的两个输出。第三个时钟(clkfbout_clk_wiz_1)是锁相环用来将全局时钟的相位调整为外置的时钟。 clkfbout_clk_wiz_1 与 @clk具有相同的频率。
当启用相位 alignment 选项时,锁相环的 feedback 时钟(clkfbout_clk_wiz_1) 连接到全局时钟缓冲器。由于锁相环始终在其输入时钟和 feedback 时钟之间同步(通过对齐这些时钟,或在它们之间保持固定的时延), clkfbout_clk_wiz_1 是全局时钟的事实也确保了输入时钟和另一个输出时钟(output clocks)之间的已知时延。
需要注意的是, clk_out1_clk_wiz_1、 clk_out2_clk_wiz_1 和 clkfbout_clk_wiz_1 并没有定义现实中存在的时钟。这些只是软件用于时序计算的时钟符号。表明这些时钟是理论上的一件事是它们的波形,它们在时钟 summary中显示: 这些波形反映了这些时钟中的每一个的占空比(duty cycle)。但这并不意味着所有四个时钟(clk 和三个理论上的时钟)都有一个上升沿(rising edge)正好是 0 ns。特别是,这并不意味着这四个时钟是完美对齐的。即使它们对齐,通过时序计算也是可见的,真正的对齐并不完美。
下面将讨论如何使用这些理论时钟,以及涉及其中两个时钟的时序计算。
这些理论上的时钟的另一个重要之处在于,它们的创建没有明确的时序约束: clk_out1_clk_wiz_* 的创建不存在于设计的 sources 或由工具创建的文件中。在大多数情况下,尝试编写这样的时序约束是不正确的,因为这样将无法正确计算时钟路径(clock path)的时延。如果这样写约束(constraints),时钟之间的相对时序将被工具计算错误, related 时钟域之间的路径将无法正确计算。
所以再一次,只有一个时序约束(timing constraint)应该自动为所有锁相环的输出时钟(output clocks)生成时序约束。
时序报告与两个 related 时钟
从上面的 Verilog 代码可以看出, @foo_reg 与 @pll_clk_8同步, @bar 与 @pll_clk_6同步。因此,更新 @bar 的语句涉及跨时钟域:
bar <= !foo_reg;
两个时钟是 related 时钟,因此工具确保通过此计算满足 @bar的时序要求(对于 tsu):
Slack (MET) : 0.475ns (required time - arrival time) Source: foo_reg_reg/C (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1 {rise@0.000ns fall@4.000ns period=8.000ns}) Destination: bar_reg__0/D (rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1 {rise@0.000ns fall@3.000ns period=6.000ns}) Path Group: clk_out2_clk_wiz_1 Path Type: Setup (Max at Slow Process Corner) Requirement: 2.000ns (clk_out2_clk_wiz_1 rise@18.000ns - clk_out1_clk_wiz_1 rise@16.000ns) Data Path Delay: 1.160ns (logic 0.307ns (26.466%) route 0.853ns (73.534%)) Logic Levels: 1 (LUT1=1) Clock Path Skew: -0.250ns (DCD - SCD + CPR) Destination Clock Delay (DCD): -0.681ns = ( 17.319 - 18.000 ) Source Clock Delay (SCD): -0.600ns = ( 15.400 - 16.000 ) Clock Pessimism Removal (CPR): -0.169ns Clock Uncertainty: 0.182ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.120ns Clock Net Delay (Source): 1.369ns (routing 0.002ns, distribution 1.367ns) Clock Net Delay (Destination): 1.208ns (routing 0.002ns, distribution 1.206ns) Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_1 rise edge) 16.000 16.000 r AG12 0.000 16.000 r clk (IN) net (fo=0) 0.000 16.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.738 16.738 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.105 16.843 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.049 16.892 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.975 17.867 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -4.438 13.429 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.501 13.930 pll_i/inst/clk_out1_clk_wiz_1 BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.101 14.031 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=1, routed) 1.369 15.400 pll_clk_8 SLICE_X49Y58 FDRE r foo_reg_reg/C ------------------------------------------------------------------- ------------------- SLICE_X49Y58 FDRE (Prop_EFF_SLICEL_C_Q) 0.139 15.539 f foo_reg_reg/Q net (fo=1, routed) 0.807 16.346 foo_reg SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O) 0.168 16.514 r bar__0_i_1/O net (fo=1, routed) 0.046 16.560 p_0_in SLICE_X49Y58 FDRE r bar_reg__0/D ------------------------------------------------------------------- ------------------- (clock clk_out2_clk_wiz_1 rise edge) 18.000 18.000 r AG12 0.000 18.000 r clk (IN) net (fo=0) 0.000 18.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.515 18.515 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.066 18.581 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.034 18.615 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.873 19.488 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1) -3.890 15.598 r pll_i/inst/mmcme3_adv_inst/CLKOUT1 net (fo=1, routed) 0.422 16.020 pll_i/inst/clk_out2_clk_wiz_1 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.091 16.111 r pll_i/inst/clkout2_buf/O X2Y0 (CLOCK_ROOT) net (fo=2, routed) 1.208 17.319 pll_clk_6 SLICE_X49Y58 FDRE r bar_reg__0/C clock pessimism -0.169 17.150 clock uncertainty -0.182 16.967 SLICE_X49Y58 FDRE (Setup_DFF2_SLICEL_C_D) 0.067 17.034 bar_reg__0 ------------------------------------------------------------------- required time 17.034 arrival time -16.560 ------------------------------------------------------------------- slack 0.475
如前所述,时序计算是一个假想的实验,假想的秒表与时钟边沿一起开始。在前面的示例中,此秒表从 0 ns开始。但在这个计算中是从时钟边沿开始于 16 ns。这是因为两个时钟的时钟周期不相等。计算基于第一个时钟和第二个时钟之间的最坏情况组合。
首款时钟的上升沿分别为 0 ns、 8 ns、 16 ns、 24 ns 等。第二个时钟的上升沿分别是 0 ns、 6 ns、 12 ns、 18 ns、 24 ns 等等。所以第一个时钟和第二个时钟的时间间隔最小的就是第一个时钟的 16 ns 和第二个时钟的 18 ns。这就是本时序计算检验的情况。
这意味着数据路径被限制在大约 2 ns,就像 500 MHz。这是一个非常严格的要求,但由于数据路径中只有一 LUT ,而两个触发器都在同一 slice上,所以很容易达到这个目的。但是,此示例说明了为什么选择适合 related 时钟的频率非常重要。通常选择一个时钟的频率作为另一个时钟频率的倍数。这样做可以避免像本示例的 2 ns 间隙这样的情况。
顺便说一下,如果无法选择协同工作的频率,解决方案是将时钟视为 unrelated 时钟 。
衍生的时钟
我前面提到 clk_out1_clk_wiz_1 和 clk_out2_clk_wiz_1 理论上是时钟,而这个事实在时序报告上的体现是这样的: 请注意,两条路径(paths)都是以外部引脚(AG12) 作为起点计算的。这怎么说得通?实际上,这个引脚上的信号是参考时钟(reference clock),而不是这两个时钟中的任何一个。
那么我们以 clk_out1_clk_wiz_1 为例: 计算背后的想法是假设在外部引脚处有一个时钟和 125 MHz 。实际上,时钟与 125 MHz 只存在于锁相环的输出,即 MMCME3_ADV_X1Y0的输出。但不是在时序计算中涉及锁相环的频率操纵,而是使用理论上的时钟。这个时钟有一个从 0 ns开始的理想波形。锁相环被视为没有改变任何时钟的频率,而只是增加了时延。
所以理论上的时钟(例如 clk_out1_clk_wiz_1)定义了时钟(频率、占空比、抖动等)的波形。但是,这个理论上的时钟没有定义时序与其他时钟的关系,这些时钟在 FPGA上作为真实的信号存在。
那么我们如何才能看出 @pll_clk_6 和 @pll_clk_8 实际上是对齐的呢?答案在于这些时钟的实际时序和理想的时序之间的比较。比如 clk_out1_clk_wiz_1的理论时钟边沿在上面的计算中就是 16 ns 。另一方面, clk_out2_clk_wiz_1的时钟边沿在 18 ns,后来是 2 ns。下面我们来对比一下全局时钟树(global clock tree)的输出的时钟边沿: 第一个时钟的时钟边沿到达 15.400 ns,根据时序报告。对于第二个时钟边沿,报告表示 17.319 ns。因此根据计算,时间差是 1.919 ns ,而不是 2 ns。这仅比理想差少 0.081 ns 。因此时钟肯定对齐了。
让我们与之前只有一个时钟的示例进行比较: 两个时钟边沿之间的理想时间差是时钟周期,即 8 ns。但是根据相关的时序报告(见上),第一个时钟边沿到达 −0.616 ns,第二个时钟边沿到达 7.285 ns。理想的时差是 8 ns,但实际上是 7.901 ns。所以第二个时钟边沿比 0.099 ns预期的要早。
所以用两个 related 时钟,从理想的时间差分流是 0.081 ns。只有一台时钟,这个分流也差不多, 0.099 ns。在这两种情况下,这种转移的原因是第一个时钟边沿的计算是用最大的时延进行的,而最小的时延用于第二个时钟边沿。
因此,结论是 @pll_clk_6 和 @pll_clk_8 的对齐精度与时钟大致相同。
请注意,即使没有启用锁相环的相位 alignment的选项,这些时钟也会相互对齐: 锁相环的输出通常是对齐的。通过使用具有几乎相同时延的全局时钟缓冲器来确保这种对齐,而不管它们的扇出(fanout)。换句话说,不管这些时钟缓冲器(clock buffers)分别连接了多少个逻辑单元(logic elements),从锁相环的输出到目的地的时延是大致相同的。
我们对 related 时钟的了解
对这个时序报告(timing report)的分析可以看出,两个 related 时钟域之间的一条路径大致相当于一个时钟域(clock domain)里面的一条路径。然而,它并不完全相同。尤其是时钟 Uncertainty ,从 0.062 ns 升级到 0.182 ns,因为每个时钟都有自己的抖动,而且对齐也不是很完美。
这个时序报告还展示了两个时钟的对齐如何反映在时序的计算中。
当 FPGA 设计中有跨时钟域时,最好检查时序报告中这些时钟之间的交互。目的是确保工具能够像我们预期的那样处理时钟。这些是要检查的两个最重要的事情:
- 如果逻辑把两个时钟当成 related 时钟: 验证这些时钟之间的所有路径上都有时序计算。
- 如果逻辑把两个时钟当成 unrelated 时钟: 验证这两个时钟之间没有对路径进行时序计算。
Vivado 可以创建一个时钟 Interaction Report(Clock Interaction Report),它以图形方式显示设计的跨时钟域,以及工具如何处理每个这样的 crossing 。其他 FPGA 工具也有类似的功能,例如 Quartus Pro的 CDC Viewer。如果可能,建议创建并检查此报告。
另一件要看的事情是时钟是否对齐。如果设计误用了未对齐的时钟,可能会给时序约束的实现造成不必要的困难。例如,如果 @clk 和 @pll_clk_8之间有一条路径,工具将强制执行时序约束,从而确保这条路径可靠地工作。但请注意, @clk 是进入锁相环的参考时钟,而 @pll_clk_8 是此锁相环的输出。因此这两个时钟没有对齐。因此,为了实现路径的时序约束,这些工具可能会付出不必要的努力。由于这种不必要的努力,其他路径可能无法实现其时序约束。
再一次,时钟域的主题包含在本系列页面中。
thold 也很重要
到目前为止,我展示的所有时序计算都与 tsu 要求相关。关注这个要求是很自然的,因为当工具无法实现时序约束时,几乎总是因为至少有一条路径无法实现 tsu的要求。
牢记 thold 仍然很重要: 为了满足此要求,工具有时会通过添加布线时延(routing delay)来人为地降低数据路径的速度。因此,尽管 thold 要求很少被提及作为实现时序约束失败的原因,但该要求可能是失败的隐藏原因。
事实上,连接两个触发器的线(wire)的布线时延也发生了一些相关的事情: 在只涉及一个时钟的所有时序报告中,这根线(wire)位于同一 slice (SLICE_X49Y58)内。因此,这根线的时延是所有这些报告中的 0.241 ns 。但是当路径中涉及两个时钟时,这个时延(delay)上升到 0.807 ns。
解释是 0.241 ns 是两个触发器放在同一 slice中可以实现的最小时延。较长的时延(0.807 ns)是这根线的不同布线(routing)的结果。这不是偶然发生的: 为了满足 thold 的要求,工具故意将这个布线(routing)做得特别长。这也反映在报告中的“数据路径时延(Data Path Delay)”行中: 逻辑的时延就是 26.5%,其余的就是布线。仅此一项就表明发生了某些事情。更多关于下面的内容。
thold 需求分析
从本系列页面的理论页面回想一下, thold 是第二个触发器上的数据输入(data input)在时钟边沿之后必须稳定的时间量。下面描述一下违反 thold 的情况: 一个时钟边沿到达第一个触发器,第二个触发器也几乎同时收到一个时钟边沿(请注意,这些时钟边沿可以来自同一个时钟,也可以来自不同的时钟)。为了响应其时钟边沿,第一款触发器在时延(clock-to-output)之后更新了其输出。但更新值过早到达第二个触发器。因此,此触发器无法可靠地对之前的值进行采样: 新值在第二个触发器有时间完成对其时钟边沿的反应之前到达。换句话说,违反了 thold 要求。
当同一个时钟与两个触发器一起使用时,会发生这种情况主要是因为时钟偏移(clock skew): 如果时钟边沿比第一个触发器更早到达,则第一个触发器可能会过早地更改其输出。这为更新的信号到达第二个触发器的速度足以违反 thold 要求的可能性打开了大门。请注意,到达两个触发器的是同一个时钟边沿,因此时钟的频率或时钟抖动(clock jitter)的数量都没有意义。只有不同的时延(即时钟偏移)起作用。
但是对于下面的时序分析,我们将继续使用最后一个例子,其中涉及两个时钟: @pll_clk_8 和 @pll_clk_6。一个时序分析(timing analysis)和一个时钟会很相似,但没那么有趣,因为用一个时钟很容易满足 thold 的要求。
所以我们要看的时序报告是为两个 related 时钟设计的。这两台时钟频率不同,和之前一样,第一台时钟的时钟边沿和第二台时钟的时钟边沿的时间有不同的组合。但与 tsu的计算不同, thold 的最坏情况是两个时钟边沿都在 0 ns时: 当两个时钟边沿几乎同时发生时,就会发生 thold 违规,因此没有比这更糟糕的组合了。
因此,有了这些手头的见解,让我们看看时序报告:
Slack (MET) : 0.093ns (arrival time - required time) Source: foo_reg_reg/C (rising edge-triggered cell FDRE clocked by clk_out1_clk_wiz_1 {rise@0.000ns fall@4.000ns period=8.000ns}) Destination: bar_reg__0/D (rising edge-triggered cell FDRE clocked by clk_out2_clk_wiz_1 {rise@0.000ns fall@3.000ns period=6.000ns}) Path Group: clk_out2_clk_wiz_1 Path Type: Hold (Min at Fast Process Corner) Requirement: 0.000ns (clk_out2_clk_wiz_1 rise@0.000ns - clk_out1_clk_wiz_1 rise@0.000ns) Data Path Delay: 0.458ns (logic 0.104ns (22.707%) route 0.354ns (77.293%)) Logic Levels: 1 (LUT1=1) Clock Path Skew: 0.127ns (DCD - SCD - CPR) Destination Clock Delay (DCD): -0.542ns Source Clock Delay (SCD): -0.248ns Clock Pessimism Removal (CPR): -0.421ns Clock Uncertainty: 0.182ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.120ns Clock Net Delay (Source): 0.495ns (routing 0.002ns, distribution 0.493ns) Clock Net Delay (Destination): 0.576ns (routing 0.002ns, distribution 0.574ns) Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_1 rise edge) 0.000 0.000 r AG12 0.000 0.000 r clk (IN) net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.339 0.339 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.025 0.364 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.015 0.379 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.405 0.784 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -1.721 -0.937 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.167 -0.770 pll_i/inst/clk_out1_clk_wiz_1 BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.027 -0.743 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=1, routed) 0.495 -0.248 pll_clk_8 SLICE_X49Y58 FDRE r foo_reg_reg/C ------------------------------------------------------------------- ------------------- SLICE_X49Y58 FDRE (Prop_EFF_SLICEL_C_Q) 0.049 -0.199 f foo_reg_reg/Q net (fo=1, routed) 0.343 0.144 foo_reg SLICE_X49Y58 LUT1 (Prop_D5LUT_SLICEL_I0_O) 0.055 0.199 r bar__0_i_1/O net (fo=1, routed) 0.011 0.210 p_0_in SLICE_X49Y58 FDRE r bar_reg__0/D ------------------------------------------------------------------- ------------------- (clock clk_out2_clk_wiz_1 rise edge) 0.000 0.000 r AG12 0.000 0.000 r clk (IN) net (fo=0) 0.000 0.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.595 0.595 r pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.042 0.637 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.022 0.659 r pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.457 1.116 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT1) -2.474 -1.358 r pll_i/inst/mmcme3_adv_inst/CLKOUT1 net (fo=1, routed) 0.209 -1.149 pll_i/inst/clk_out2_clk_wiz_1 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.031 -1.118 r pll_i/inst/clkout2_buf/O X2Y0 (CLOCK_ROOT) net (fo=2, routed) 0.576 -0.542 pll_clk_6 SLICE_X49Y58 FDRE r bar_reg__0/C clock pessimism 0.421 -0.121 clock uncertainty 0.182 0.061 SLICE_X49Y58 FDRE (Hold_DFF2_SLICEL_C_D) 0.056 0.117 bar_reg__0 ------------------------------------------------------------------- required time -0.117 arrival time 0.210 ------------------------------------------------------------------- slack 0.093
首先,有一些明显的区别: 路径 Type (Path Type)是 Hold,而不是之前的 Setup 。这是预期的。然后它说“Min at Fast Process Corner”,这与它对 setup 路径所说的相反: 此计算使用最小的时延,而不是数据路径的最大时延。这适用于数据输入变化过早的情况的最坏情况计算。
Requirement 是 0 ns,这是典型的 hold 路径。时钟的频率在这里不起作用: 检查的场景是两个时钟同时有一个上升沿。
至于时钟路径,请注意源时钟路径上每个组件的时延始终比目的地时钟路径的相同时延短(但不会更长)。再次说明,这与本次计算的目的是一致的,因为最坏的情况是数据(data)变化过早,相对于时钟边沿到第二个触发器的到来。 Multi-corner 时序分析已在上一页进行了说明。
但这款时序报告最重要的是 slack 体积小: 只有 0.093 ns。这通常表明工具必须做出努力才能满足要求。但是,请注意,小型 slack 并不一定表示存在需要解决的问题。
当时序分析是为 thold制造时,小型 slack 非常常见。那么这台路径为什么可疑呢?主要是因为同一 slice (SLICE_X49Y58)上的两个触发器之间的布线时延更大,如上所述。很可能是在布局和布线(place and route)的早期阶段,软件检测到这两个触发器之间无法满足 thold 的要求。那么这是如何纠正的呢?
未能满足 thold 要求意味着数据信号(data signal)相对于第二个触发器的时钟边沿到货太早。这是通过人为地将时延添加到数据路径来纠正的。结果,数据输入在第二个触发器上稍晚更改了它的值。这些工具可能使两个触发器之间的布线变长了。这样增加了布线时延,解决了 thold的问题。时延的这种增加可能会产生问题以满足 tsu 的要求,但在这种情况下没有这样的问题: 时序约束是为这条路径实现的。 (即满足 tsu 和 thold 的要求)。
尽管如此,此示例显示了解决 thold 问题的需要如何可能会产生看似与 tsu无关的问题。当路径在逻辑时延(logic delay)和布线时延之间的比率非常低时,请记住这一点。当然还有其他可能的原因导致布线时延较大,尤其是较高的扇出。但是当扇出较低时(如本例中,扇出为 1),值得一问的是,工具是否故意插入了较大的布线时延以解决 thold 问题。
但是为什么有两个时钟的时候只有 thold 有问题呢?答案是两台时钟,时钟边沿什么时候到两台触发器都有较大的不确定性。有几个因素导致了这种不确定性,尤其是时钟偏移和抖动。由于 thold 的计算是从 0 ns 到 0 ns,因此该计算对时钟边沿何时到达的小不确定性更为敏感。
因此,与两个 related 时钟相关的不确定性通常需要纠正措施以满足 thold 的要求。当然,这些更正是由工具自动进行的。但在实现时序约束时遇到问题时,了解这些更正仍然很重要。
概括
本系列的最后两页显示了几个时序报告示例,这些示例都是一个特定时序约束的结果。所有这些时序报告也都与逻辑的一个具体而简单的例子有关。希望这些示例有助于建立对时序基础知识的理解。
此时,建议查看您自己的设计的时序报告,以了解相同的原理如何适用于包含多 LUT的路径。
下一页开始讨论时序问题以及如何解决这些问题。