介绍
每当在逻辑设计中使用锁相环(PLL)以从另一个生成时钟时,通常期望时序工具(timing tools)会计算频率以及时序(timing)在不同时钟之间的关系。
对于 Intel的 Quartus,默认情况下并非如此。在 SDC constraints file 中需要 derive_pll_clocks 命令才能发生这种情况。事实上,这个命令几乎出现在工具自动生成的任何 SDC 文件中。
但可怕的是: 如果 derive_pll_clocks 被省略,人们会期望正确的时序约束(timing constraints)将应用于由锁相环生成的时钟。另一种可能性是根本不会应用时序约束。不幸的是,现实是不同的: 如下图,时序的计算是针对这些时钟进行的,但约束(constraints)是错误的。因此,人们可能会觉得时序约束已经完成并且一切都很好,但实际上没有做出任何正确的事情。
一个例子
假设 FPGA 有一 oscillator 输入和 48 MHz (因此 period 是 20.833 ns)。锁相环生成一个带有 240 MHz 的时钟(即 4.166 ns)。
首先让我们看一个简单且正确编写的 SDC 文件,它说:
create_clock -name root_clk -period 20.833 [get_ports {osc_clock}] derive_pll_clocks derive_clock_uncertainty
请注意,存在 derive_pll_clocks 命令。
现在让我们看一下时序报告(timing report)之间的一条路径(path)两个寄存器(registers),由锁相环生成的时钟驱动。唯一有趣的部分用红色标记:
+-------------------------------------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 4.937 ; 4.937 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; ; ; ; ; source latency ;
; 0.000 ; 0.000 ; ; ; 1 ; PIN_B12 ; osc_clock ;
; 0.000 ; 0.000 ; RR ; IC ; 1 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|i ;
; 0.667 ; 0.667 ; RR ; CELL ; 2 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|o ;
; 2.833 ; 2.166 ; RR ; IC ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|inclk[0] ;
; 1.119 ; -1.714 ; RR ; COMP ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|observablevcoout ;
; 1.119 ; 0.000 ; RR ; CELL ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|clk[0] ;
; 3.274 ; 2.155 ; RR ; IC ; 1 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|inclk[0] ;
; 3.274 ; 0.000 ; RR ; CELL ; 8 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|outclk ;
; 4.336 ; 1.062 ; RR ; IC ; 1 ; FF_X40_Y24_N27 ; clkrst_ins|main_state[0]|clk ;
; 4.937 ; 0.601 ; RR ; CELL ; 1 ; FF_X40_Y24_N27 ; clkrst:clkrst_ins|main_state[0] ;
; 6.774 ; 1.837 ; ; ; ; ; data path ;
; 5.169 ; 0.232 ; ; uTco ; 1 ; FF_X40_Y24_N27 ; clkrst:clkrst_ins|main_state[0] ;
; 5.169 ; 0.000 ; FF ; CELL ; 5 ; FF_X40_Y24_N27 ; clkrst_ins|main_state[0]|q ;
; 5.591 ; 0.422 ; FF ; IC ; 1 ; LCCOMB_X40_Y24_N24 ; clkrst_ins|Equal1~0|dataa ;
; 6.002 ; 0.411 ; FR ; CELL ; 1 ; LCCOMB_X40_Y24_N24 ; clkrst_ins|Equal1~0|combout ;
; 6.370 ; 0.368 ; RR ; IC ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst_ins|the_register|d ;
; 6.774 ; 0.404 ; RR ; CELL ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst:clkrst_ins|the_register ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
+-------------------------------------------------------------------------------------------------------------------------------------------------+
; Data Required Path ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; 4.166 ; 4.166 ; ; ; ; ; latch edge time ;
; 9.005 ; 4.839 ; ; ; ; ; clock path ;
; 4.166 ; 0.000 ; ; ; ; ; source latency ;
; 4.166 ; 0.000 ; ; ; 1 ; PIN_B12 ; osc_clock ;
; 4.166 ; 0.000 ; RR ; IC ; 1 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|i ;
; 4.833 ; 0.667 ; RR ; CELL ; 2 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|o ;
; 6.912 ; 2.079 ; RR ; IC ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|inclk[0] ;
; 5.119 ; -1.793 ; RR ; COMP ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|observablevcoout ;
; 5.119 ; 0.000 ; RR ; CELL ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|clk[0] ;
; 7.187 ; 2.068 ; RR ; IC ; 1 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|inclk[0] ;
; 7.187 ; 0.000 ; RR ; CELL ; 8 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|outclk ;
; 8.199 ; 1.012 ; RR ; IC ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst_ins|the_register|clk ;
; 8.736 ; 0.537 ; RR ; CELL ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst:clkrst_ins|the_register ;
; 9.005 ; 0.269 ; ; ; ; ; clock pessimism removed ;
; 8.985 ; -0.020 ; ; ; ; ; clock uncertainty ;
; 8.890 ; -0.095 ; ; uTsu ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst:clkrst_ins|the_register ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
注意“latch edge time”行,这是时钟的边沿(edge)通过时钟 network (clock network)传播的时间(并累积时钟缓冲器(clock buffers)和接线的时延(delay)),成为接收寄存器(register)使用的时钟。 “latch edge time”就是时钟的 period,因为这是两个寄存器之间的路径(path),它们使用同一个时钟的同一个上升沿(rising edge)。事实上,它说的是 4.166 ns。到目前为止,一切都很好。
但随后灾难
让我们看看如果省略 derive_pll_clocks 命令会发生什么。换句话说, SDC 文件说:
create_clock -name root_clk -period 20.833 [get_ports {osc_clock}] derive_clock_uncertainty
对于完全相同的路径,时序报告表示:
+-------------------------------------------------------------------------------------------------------------------------------------------------+
; Data Arrival Path ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; 0.000 ; 0.000 ; ; ; ; ; launch edge time ;
; 4.937 ; 4.937 ; ; ; ; ; clock path ;
; 0.000 ; 0.000 ; ; ; ; ; source latency ;
; 0.000 ; 0.000 ; ; ; 1 ; PIN_B12 ; osc_clock ;
; 0.000 ; 0.000 ; RR ; IC ; 1 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|i ;
; 0.667 ; 0.667 ; RR ; CELL ; 2 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|o ;
; 2.833 ; 2.166 ; RR ; IC ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|inclk[0] ;
; 1.119 ; -1.714 ; RR ; COMP ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|observablevcoout ;
; 1.119 ; 0.000 ; RR ; CELL ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|clk[0] ;
; 3.274 ; 2.155 ; RR ; IC ; 1 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|inclk[0] ;
; 3.274 ; 0.000 ; RR ; CELL ; 8 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|outclk ;
; 4.336 ; 1.062 ; RR ; IC ; 1 ; FF_X40_Y24_N19 ; clkrst_ins|main_state[0]|clk ;
; 4.937 ; 0.601 ; RR ; CELL ; 1 ; FF_X40_Y24_N19 ; clkrst:clkrst_ins|main_state[0] ;
; 6.765 ; 1.828 ; ; ; ; ; data path ;
; 5.169 ; 0.232 ; ; uTco ; 1 ; FF_X40_Y24_N19 ; clkrst:clkrst_ins|main_state[0] ;
; 5.169 ; 0.000 ; FF ; CELL ; 5 ; FF_X40_Y24_N19 ; clkrst_ins|main_state[0]|q ;
; 5.583 ; 0.414 ; FF ; IC ; 1 ; LCCOMB_X40_Y24_N24 ; clkrst_ins|Equal1~0|datab ;
; 5.994 ; 0.411 ; FR ; CELL ; 1 ; LCCOMB_X40_Y24_N24 ; clkrst_ins|Equal1~0|combout ;
; 6.361 ; 0.367 ; RR ; IC ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst_ins|the_register|d ;
; 6.765 ; 0.404 ; RR ; CELL ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst:clkrst_ins|the_register ;
+---------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
+--------------------------------------------------------------------------------------------------------------------------------------------------+
; Data Required Path ;
+----------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; Total ; Incr ; RF ; Type ; Fanout ; Location ; Element ;
+----------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
; 20.833 ; 20.833 ; ; ; ; ; latch edge time ;
; 25.672 ; 4.839 ; ; ; ; ; clock path ;
; 20.833 ; 0.000 ; ; ; ; ; source latency ;
; 20.833 ; 0.000 ; ; ; 1 ; PIN_B12 ; osc_clock ;
; 20.833 ; 0.000 ; RR ; IC ; 1 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|i ;
; 21.500 ; 0.667 ; RR ; CELL ; 2 ; IOIBUF_X19_Y29_N8 ; osc_clock~input|o ;
; 23.579 ; 2.079 ; RR ; IC ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|inclk[0] ;
; 21.786 ; -1.793 ; RR ; COMP ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|observablevcoout ;
; 21.786 ; 0.000 ; RR ; CELL ; 1 ; PLL_3 ; clkrst_ins|altpll_component|auto_generated|pll1|clk[0] ;
; 23.855 ; 2.069 ; RR ; IC ; 1 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|inclk[0] ;
; 23.855 ; 0.000 ; RR ; CELL ; 8 ; CLKCTRL_G13 ; clkrst_ins|altpll_component|auto_generated|wire_pll1_clk[0]~clkctrl|outclk ;
; 24.867 ; 1.012 ; RR ; IC ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst_ins|the_register|clk ;
; 25.404 ; 0.537 ; RR ; CELL ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst:clkrst_ins|the_register ;
; 25.672 ; 0.268 ; ; ; ; ; clock pessimism removed ;
; 25.572 ; -0.100 ; ; ; ; ; clock uncertainty ;
; 25.477 ; -0.095 ; ; uTsu ; 1 ; DDIOOUTCELL_X41_Y24_N4 ; clkrst:clkrst_ins|the_register ;
+----------+----------+----+------+--------+------------------------+------------------------------------------------------------------------------+
所以分析完全一样,只是假设时钟周期(clock period)是 20.833 ns (再次注意“latch edge time”)。请注意,分析遍历锁相环,但只是忽略了锁相环的输出具有另一个频率的事实。就好像这些工具在说: 您忘记了 derive_pll_clocks 命令?没问题。我们会假装锁相环的输入时钟正好通过它。
坦率地说,我想不出这种行为有意义的单一案例。要么不要用锁相环的时钟计算路径的时序,要么做对了。但只是扔在原来的时钟的 period?这些计算错误的路径不会出现在 unconstrained 路径总结中,也没有任何其他迹象表明时序严重错误。
为了工具的防御,时序分析(timing analysis)在这件事上产生了 warnings ,但没有一个达到关键(Critical)的水平,所以在 FPGA 工具总是产生的 warnings 的海洋中很容易错过它们。
结论
- 如果您涉及锁相环,请确保您的设计具有 derive_pll_clocks 命令(除非您为锁相环的时钟添加了显式约束)。
- 一定要生成一个时序报告(timing report),一定要阅读理解。
- 始终通过要求不可能的值来测试您的约束,并验证 failing 路径的计算是否正确。