这章节描述了如何配置静态时序分析的环境。正确的约束对于分析STA的结果十分重要。只有准确的指定设计的环境,静态时序分析才能识别出设计中所有的时序问题。准备工作包括:配置时钟、指定IO时序特征、指定伪路径以及多周期路径。下面详细介绍本章节。
目录
7.2.1 时钟不确定度(clock uncertainty)
典型的时钟生成(typical clock generation scenario)
7.1 什么是STA环境
大多数数字设计是同步时序的,意味着在前一个时钟周期计算出来的数据,将会在时钟的有效边沿被锁存。下图是一个典型的同步时序设计。假设待分析设计(design under analysis, DUA)会与其他同步时序设计交互。这就意味着DUA从一个时钟触发器获取数据并将数据输出至DUA外的另一个时钟触发器。
在这个设计上运行STA的话,我们需要指定触发器的时钟,指定所有通向、离开该设计的路径的时序约束。上图例子假设只有一个时钟,C1C2C3C4C5代表组合逻辑模块,C1C5在设计的外面被分析。在一个典型设计中,从一个时钟域到另一个时钟域有多个时钟有多个路径。下面将讲述在这些情况下,环境是如何设置的。
7.2 指定时钟(specifying clocks)
定义时钟所需的信息:
- 时钟源:可以是设计的一个端口,或者设计内部的一个单元引脚。
- 周期:时钟的周期。
- 占空比:高电平多久;低电平多久。
- 边沿时间:上升边沿和下降边沿的时间。
时钟定义了之后,所有内部的时钟路径都被约束了(FF 2 FF的路径) ;这也就说明所有的内部路径都可以使用时钟约束(clock specification)分析。时钟的约束也就指定了由一个寄存器到另一个寄存器的路径只用一个时钟周期。
create_clock \
-name SYSCLK \
-period 20 \
-waveform {0 5} \
[get_ports2 SCLK]
上面的定义了名字,端口,周期为20单位也就是20ns。通常时间会在技术库进行指定。-waveform的第一个参数指定了上升沿的时间(第一个边沿必须是上升沿),第二个参数指定了下降沿的时间。可以指定任意多的边沿,但是前提是这些边沿都在一个周期内。边沿时间从时刻0开始,随后上升下降来回交替。这也就意味这边沿时间是逐渐增加的。
-waveform {time_rise time_fall time_rise time_fall ...}
此外,必须指定偶数个边沿时刻。waveform指定了一个周期内的波形,然后他自己不断重复(repeat itself).如果没有waveform明确指定上升沿与下降沿,那么默认为
-waveform {0, period/2}
下面是一个不指定波形的例子:在这例子中,没有显式指出name,那它的名字就和端口的名字一样,为SCAN_CLK。也鼓励这样做。
create_clock -period 5 [get_ports SCAN_CLK]
另一个例子在下面,边沿在信号的中间。
create_clock -name BDYCLK -period 15 \
-waveform {5 12} [get_ports GBLCLK]
下面是更多的例子:
# See Figure (a):
create_clock -period 10 -waveform {5 10} [get_ports FCLK]
# Creates a clock with the rising edge at 5ns and the
# falling edge at 10ns.
# See Figure (b):
create_clock -period 125 \
-waveform {100 150} [get_ports ARMCLK]
# Since the first edge has to be rising edge, the edge at 100ns is specified first and then the falling edge at 150ns is specified. The falling edge at 25ns is automatically inferred.
# See Figure 7-6(a):
create_clock -period 1.0 -waveform {0.5 1.375} MAIN_CLK
# The first rising edge and the next falling edge
# is specified. Falling edge at 0.375ns is inferred
# automatically.
# See Figure 7-6(b):
create_clock -period 1.2 -waveform {0.3 0.4 0.8 1.0} JTAG_CLK
# Indicates a rising edge at 300ps, a falling edge at 400ps,
# a rising edge at 800ps and a falling edge at 1ns, and this
# pattern is repeated every 1.2ns.
create_clock -period 1.27 \
-waveform {0 0.635} [get_ports clk_core]
create_clock -name TEST_CLK -period 17 \
-waveform {0 8.5} -add [get_ports {ip_io_clk[0]}]
# The -add option allows more than one clock
# specification to be defined at a port.
通过使用add指令可以在一个端口指定多个时钟。除了上述的属性以外,可以在时钟源处显式的指出过渡时间,因为顶层的输入端口或某些PLL的输出端口,工具无法自动计算出过渡时间。过渡时间的约束仅适用于理想时钟,因为时钟树建立之后,将使用时钟引脚上的实际过渡时间。
7.2.1 时钟不确定度(clock uncertainty)
不确定度将会用来模拟多种减少有效时钟周期的因素。这些因素可以是时钟抖动,或者是其他的。
set_clock_uncertainty -setup 0.2 [get_clocks CLK_CONFIG]
set_clock_uncertainty -hold 0.05 [get_clocks CLK_CONFIG]
之前在第二章中提到的时钟不确定度
对于建立时间而言的时钟不确定度,减少了有效时钟周期。对于保持时间而言的时钟不确定度,可以被用作需要满足的额外时间裕度。
set_clock_uncertainty -from VIRTUAL_SYS_CLK -to SYS_CLK -hold 0.05
set_clock_uncertainty -from VIRTUAL_SYS_CLK -to SYS_CLK -setup 0.3
set_clock_uncertainty -from SYS_CLK -to CFG_CLK -hold 0.05
set_clock_uncertainty -from SYS_CLK -to CFG_CLK -setup 0.1
使用以上指令指定跨时钟边界的路径上的内部时钟不确定性。下图是路径跨越了两个不同的时钟域。100ps被用于建立检查,50ps被用于保持检查。
7.2.2 时钟延迟(clock latency)
可以使用下列指令去指定时钟延迟
# Rise clock latency on MAIN_CLK is 1.8ns:
set_clock_latency 1.8 -rise [get_clocks MAIN_CLK]
# Fall clock latency on all clocks is 2.1ns:
set_clock_latency 2.1 -fall [all_clocks]
# The -rise, -fall refer to the edge at the clock pin of a flip-flop.
存在两种时钟延时:网络延时(net latency)以及源延时(source latency):
- 网络延时指的是,从时钟的定义点(definition point),到触发器时钟引脚的延时。
- 源延时,也是内插延时(insertion delay),是从时钟源到时钟定义点,之间的延迟。源延时既可以代表片上延时,也可以代表片外延时。区别在于时钟源的位置。
时钟引脚处的延时是上述两者之和。下面的是代码示例:
# Specify a network latency (no -source option) of 0.8ns for
# rise, fall, max and min:
set_clock_latency 0.8 [get_clocks CLK_CONFIG]
# Specify a source latency:
set_clock_latency 1.9 -source [get_clocks SYS_CLK]
# Specify a min source latency:
set_clock_latency 0.851 -source -min [get_clocks CFG_CLK]
# Specify a max source latency:
set_clock_latency 1.322 -source -max [get_clocks CFG_CLK]
网络延时与源延时之间的显著区别在于,一旦设计的时钟树建立之后,网络延时可以忽略。但源延时不可以忽略,它仍然存在。网络延时是在时钟树建立之前对于延时的估计。在进行时钟树的综合之后,总的时钟延时(从时钟源到触发器的引脚),是源延时加上从时钟定义点到引脚的实际延时,两者的和。
7.3 衍生时钟(generated clocks)
衍生时钟是源于主时钟(master clocks)的,一个主时钟是利用create_clock指令来指定的。例如,如果有一个时钟的三分频电路(divide-by-3 circuitry),则将在该电路的输出处定义一个衍生时钟。由于STA不知道时钟的周期在输出端口已经改变,以及新的时钟的周期是多少,所以需要对于衍生时钟进行定义。下图是一个二分频的例子。
create_clock -name CLKP 10 [get_pins UPLL0/CLKOUT]
# Create a master clock with name CLKP of period 10ns
# with 50% duty cycle at the CLKOUT pin of the PLL.
create_generated_clock -name CLKPDIV2 -source UPLL0/CLKOUT \
-divide_by 2 [get_pins UFF0/Q]
# Creates a generated clock with name CLKPDIV2 at the Q
# pin of flip-flop UFF0. The master clock is at the CLKOUT
# pin of PLL. And the period of the generated clock is double
# that of the clock CLKP, that is, 20ns.
首先定义了主时钟CLKP,周期,带有50%的占空比。然后定义了衍生时钟CLKPDIV2,指出了源时钟为模块UPLL0的CLKOUT引脚,进行了2分频,最终时钟的引脚为UFF0的Q引脚。
我们其实也可以在触发器的输出引脚定义一个主时钟,但是有一些缺点。在触发器的输出,定义一个主时钟,而不是衍生时钟,创建了一个新的时钟域。除了在进行静态时序分析设置约束时,会有更多的时钟域,此外不是什么问题。把新的时钟定义为衍生时钟并不会创建新的时钟域,因为我们认为它与主时钟同相位。衍生时钟也就不需要额外的时序约束。因此我们必须要尝试去定义一个内部的衍生时钟,而不是新定义一个主时钟。
主时钟与衍生时钟另一个重要区别——时钟的源头(origin)。主时钟的源头在于主时钟的定义点;衍生时钟的源头是其主时钟的时钟源。这也就说明,在一个时钟路径报告中,时钟路径的起始点,永远是主时钟的定义点。与定义新的主时钟相比,衍生时钟具有很大优势,因为对于新的主时钟,是不会自动考虑源延迟的。
下图的例子中,2to1MUX,两个输入都是时钟。在这种情况下,就没有必要再在MUX的输出端定义一个时钟了。如果选择信号是个恒定值,那么mux的输出端就会自动得到该传播的时钟。如果选择信号没有进行约束,两个时钟信号都可以经过传播到达输出端。这时,STA可能会报告TCLK和TCLKDIV5之间的路径,但是这种路径是不存在的,因为一次只有一个时钟被选择信号选中,并且通过。为了防止错误路径的报告,我们需要设置伪路径或者指定这两个之间的互斥关系。这也假定了设计的其他部分没有两者之间的路径。
如果选择信号不是静态的,并可以在设备运行期间改变。在这种情况下,会对多路复用器输入端进行时钟门控(clock gating)检查,这保证了mux输入端的信号可以安全的切换。
SYS_CLK由触发器的输出端进行门控,由于触发器的输出可能不是恒定的,解决办法就是在与门的输出重新定义一个时钟CORE_CLK。与输入时钟相同。
create_clock 0.1 [get_ports SYS_CLK]
# Create a master clock of period 100ps with 50%
# duty cycle.
create_generated_clock -name CORE_CLK -divide_by 1 \
-source SYS_CLK [get_pins UAND1/Z]
# Create a generated clock called CORE_CLK at the
# output of the and cell and the clock waveform is
# same as that of the master clock.
下面是一个2倍频时钟的例子:
create_clock -period 10 -waveform {0 5} [get_ports PCLK]
# Create a master clock with name PCLK of period 10ns
# with rise edge at 0ns and fall edge at 5ns.
create_generated_clock -name PCLKx2 \
-source [get_ports PCLK] \
-multiply_by 2 [get_pins UCLKMULTREG/Q]
# Creates a generated clock called PCLKx2 from the
# master clock PCLK and the frequency is double that of
# the master clock. The generated clock is defined at the
# output of the flip-flop UCLKMULTREG.
注意由于是2倍频,所以参数由divide_by变成了multiply_by。乘除指的都是频率,而不是周期。
时钟门控单元输出端的主时钟
下面的例子中,and单元的输入是两个时钟,与门的输出是什么?如果两个输入都是时钟,则可以安全的在输出定义另一个时钟。因为输出不太可能与任何一个输入有相位关系。
create_clock -name SYS_CLK -period 4 -waveform {0 2} \
[get_pins UFFSYS/Q]
create_clock -name CORE_CLK -period 12 -waveform {0 4} \
[get_pins UFFCORE/Q]
create_clock -name MAIN_CLK -period 12 -waveform {0 2} \
[get_pins UAND2/Z]
# Create a master clock instead of a generated clock
# at the output of the and cell.
使用 Edge 和 Edge_shift 的衍生时钟
下图是一个衍生时钟的例子,产生了一个二分频的时钟,以及两个不同相位的时钟。
create_clock 2 [get_ports DCLK]
# Name of clock is DCLK, has period of 2ns with a
# rise edge at 0ns and a fall edge at 1ns.
create_generated_clock -name DCLKDIV2 -edges {2 4 6} \
-source DCLK [get_pins UBUF2/Z]
# The generated clock with name DCLKDIV2 is defined at
# the output of the buffer. Its waveform is formed by
# having a rise edge at edge 2 of the source clock,
# fall edge at edge 4 of the source clock and the next
# rise edge at edge 6 of the source clock.
create_generated_clock -name PH0CLK -edges {3 4 7} \
-source DCLK [get_pins UAND0/Z]
# The generated clock PH0CLK is formed using
# the 3, 4, 7 edges of the source clock.
create_generated_clock -name PH1CLK -edges {1 2 5} \
-source DCLK [get_pins UAND1/Z]
# The generated clock with name PH1CLK is defined at
# the output of the and cell and is formed with
# edges 1, 2, 5 of the source clock.
注意上面的 2 4 6,3 4 7,1 2 5,都是针对于源时钟的边沿而言的,三个参数分别为{rise_edge, fall_edge, rise_edge}。如果第一个边沿是下降沿,会如何?考虑下图情况,对于这种出现的第一个边沿是下降沿的,我们可以错后一个,指定边沿为5 7 10。1ns的下降沿,通过上述的指定,工具可以自动推断出来。代码如下。
create_generated_clock -name G3CLK -edges {5 7 10} \
-source DCLK [get_pins UAND0/Z]
Edge_shift 可以与 Edge 选项一起使用,用于指定任何边沿的偏移,从而用来形成新的波形。他指定了边沿列表中每个边沿的偏移量以及时间单位。
create_clock -period 10 -waveform {0 5} [get_ports MIICLK]
create_generated_clock -name MIICLKDIV2 -source MIICLK \
-edges {1 3 5} [get_pins UMIICLKREG/Q]
# Create a divide-by-2 clock.
create_generated_clock -name MIIDIV2 -source MIICLK \
-edges {1 1 5} -edge_shift {0 5 0} [get_pins UMIIDIV/Q]
# Creates a divide-by-2 clock with a duty cycle different
# from the source clock's value of 50%.
代码中首先创建了一个周期为10的时钟,占空比为0.5。然后创建了衍生时钟,是一个二分频的时钟。然后使用了edge_shift指令创建了第三个时钟,源时钟的边沿1移位0ns获得了衍生时钟的第一个边沿,通过将源时钟的边沿1偏移5ns获得了衍生时钟的第二个边沿,而通过将源时钟的边沿5移位0ns获得了衍生时钟的第三个边沿。
使用Invert选项创建的衍生时钟
create_clock -period 10 [get_ports CLK]
create_generated_clock -name NCLKDIV2 -divide_by 2 -invert \
-source CLK [get_pins UINVQ/Z]
invert选项是在最后执行的,这样的一个翻转时钟如下图所示。
衍生时钟的延时
衍生时钟的延时同样也是可以指定的。对于衍生时钟指定的源延时,是从主时钟的定义点,到衍生时钟的定义点。到触发器输入引脚的总衍生时钟延时(total clock latency),为主时钟的源延时+衍生时钟的源延时+衍生时钟的网络延时。
一个衍生时钟可以选择另外一个衍生时钟作为它的源时钟。也就是说他可以有衍生时钟的衍生时钟(儿子的儿子)。但是一个衍生时钟只能有一个主时钟(只认一个爹)。
典型的时钟生成(typical clock generation scenario)
下图的例子中展现了如何进行时钟分配。晶振在芯片的外部,产生了一个频率位于10-50MHz之间的时钟。他被用于基准时钟。这个基准时钟被片上的PLL用来产生一个高频低抖动时钟(频率处于200-800MHz)。这个相位锁定环时钟,此时作为分频逻辑的输入,从而产生所需的频率的时钟。
在时钟分配的部分分支,可能存在时钟门,用于关掉设计的某个不活跃部分(inactive portion)的时钟,从而节约功率。在PLL单元的输出同样也有一个MUX,因此PLL单元也可以被跳过。主时钟是从芯片的输入引脚(也就是时钟进入芯片的位置,如上图)被定义的,这也作为基准时钟(ref clock)。第二个主时钟是从PLL的输出引脚定义的。第二个主时钟与基准时钟不存在任何的相位关系。因此,PLL的输出时钟,不能作为基准时钟的衍生时钟(否则,就存在固定相位关系了)。大多数情况,由时钟分频器产生的时钟都是PLL输出主时钟的衍生时钟。
7.4 时序路径的约束
静态时序分析无法检查没有进行约束(constraints)的路径,因此所有的路径都应该进行约束。在某些例子中由于并不关心某些逻辑,可以对部分输入不进行约束的情况,稍后再说。举例来说,设计人员可能并不在乎一些输入控制信号的时序,因此可能并不需要进行本节中将要介绍的时序检查。但是,本节假定我们要约束全部的输入路径。
约束:时序路径的约束是指对信号在电路中传输的时间要求所做的限制或规定,时序约束确保电路的各个部分能够在给定的时钟周期内正确地工作。
下图的待分析设计中,UFF0在设计之外,给设计之内的UFF1提供数据。数据通过输入引脚的INP1进入。
对于时钟CLKA的定义,指定了时钟周期,也就是UFF0和UFF1之间的时间。外部逻辑所需的时间是Tclk2q(发起寄存器UFF0,CK-Q的延时),加上Tc1(外部组合逻辑的延时),如图所示。因此,在输入引脚INP1上定义了两个延时Tclk2q,加上Tc。这个外部延迟是相对于一个时钟指定的,本例中为CLKA。
set Tclk2q 0.9
set Tc1 0.6
set_input_delay -clock CLKA -max [expr Tclk2q + Tc1] \
[get_ports INP1]
这个代码说明了INP1引脚上的外部延时为1.5ns,以及这是与CLKA有关的。我们设想CLKA的时钟周期为2ns。因此,留给设计内部传播的时间只有0.5ns。说人话就是,Tc2+Tsetup的值要小于0.5ns才能使UFF1寄存器正确的捕获UFF0寄存器发送过来的数据。注意,上述的外部延迟被设定为了最大值。
如果最大、最小延时我们都考虑。如下图的例子。
INPA的最大、最小延时都是在CLKP-INPA的路径上讨论的。名词对应:
- 最大延时路径-最长路径-最大时序工艺角
- 最小延时路径-最短路径-最小时序工艺角
在我们的例子中,Tck2q的最大、最小值分别为 1.1ns 0.8ns。Tc1的最大、最小值分别为 5.6ns 2.2ns。INPA的波形窗口,展示了在哪段时间内数据到达并希望数据达到稳定。CLK到INPA的最大延时为1.1+5.6=6.7ns。最小延时为0.8+2.2=3ns。这些延时的指定都与时钟的有效边沿有关。考虑到外部输入的延时,设计内部的建立时间,最小为8.3ns(15-5.6-1.1=8.3),最大为12ns(15-0.8-2.2=12.0)。所以,8.3ns是最坏情况下也要满足的时序值。
另外的代码用于指定延时:
set_input_delay -clock clk_core 0.5 [get_ports bist_mode]
set_input_delay -clock clk_core 0.5 [get_ports sad_state]
因为max min的选项并没有指定,所以500ps的值对于最大延时、最小延时的分析都适用。这个外部延时的指定是依据clk_core的上升沿指定的。如果一定要指定下降沿,则一定要写-clock_fall的选项。
部分内容翻译自“Static Timing Analysis for Nanometer Designs A Practical Approach” 欢迎指正!