时序约束系列:
XIlinx的官方文档写的比任何一本讲时序约束的书都好,这里自己学习顺便整理翻译后的时序例外约束内容,方便复习。
当逻辑以一种非默认情况时序的方式运行时,就需要一个时序例外(Timing Exception)。
每当希望以不同的方式处理时序时,必须使用时序例外命令(例如,对于在不同时钟周期捕获结果的逻辑)。
时序例外命令:
命令 | 功能 |
---|---|
set_multicycle_path | 指示将数据从路径的起点传播到终点所需的时钟周期数 |
set_false_path | 指示不需要分析的设计中的逻辑路径。 |
set_max_delay set_min_delay | 设置最小和最大路径延迟值。这将覆盖默认设置,并使用用户指定的最大和最小延迟值所定义的建立和 hold 约束 |
注意:出于运行时间考虑,Vivado工具不为冲突的时序例外提供动态分析。运行report_exceptions以全面分析和报告时序例外。
多周期路径约束允许根据设计的时钟波形,修改时序分析器确定的setup和hold关系。
默认情况下,Vivado IDE时序引擎执行单周期分析。这种分析可能过于严格,并且可能不适合某些逻辑路径。
最常见的示例是逻辑路径,它需要多个时钟周期才能在 endpoint 稳定数据。
如果路径 startpoint 和 endpoint 的控制电路允许,Xilinx建议使用多周期路径约束来放松 setup 要求。
根据自己的意图,hold可能需要仍然保持为原来的hold关系(single-cycle分析下的hold关系)。这有助于时序驱动算法专注于其他要求更严格且具有挑战性的路径。它还可以帮助减少运行时间(runtime)。
set_multicycle_path 命令用于修改路径关于源时钟或目标时钟的需求倍数(multiplier)(用于setup分析、hold分析或两者兼而有)。
set_multicycle_path [-setup|-hold] [-start|-end]
[-from ] [-to ] [-through ]
必须指定
hold 关系与 setup 关系相关联。使用下面的公式来检索大多数常见情况下的 hold 周期数:
Hold cycles =
重要:每个 setup 关系都有两个 hold 关系。
所谓有效沿,就是我们需要分析的目标边沿,关系也很简单,当前沿不能采到上一个数据,当前数据不能被下一个沿采到。
重要:当对由相同时钟或由两个相同时钟(即时钟具有相同的波形,没有相移)驱动的路径应用多周期路径约束时,-start和-end选项没有明显的影响。
总结 -end 和 -start 选项如何影响有效的启动和捕获边沿:
源时钟(-start),移动启动边沿 | 目的时钟(-end),移动捕获边沿 | |
---|---|---|
setup | <----(向后) | ---->(向前)(默认) |
hold | ---->(向前)(默认) | <----(向后) |
注意上表非常重要,需要理解牢记。这里时间增大方向为前,减小方向为后。
重要:set_multicycle_path命令的-setup选项不仅修改setup关系。它还影响始终与hold关系相关联的 hold 关系。
如果要将hold关系恢复到原来的位置,则需要使用-hold实现另一个set_multicycle_path规范。
注意:多周期约束可以设置在单个路径上,多个路径上,甚至两个时钟之间。
定义在同一时钟域中或具有相同波形(无相移)的两个时钟之间的多周期约束具有相同的工作方式。
由静态定时分析(STA)工具定义的默认setup和hold关系:
这里的hold对于第一个hold关系:确保有效启动沿(当前启动沿)不被上一个 捕获沿 采到。相对应的,对于上一个 启动沿来说,这个hold就是第二个hold关系:确保下一个启动沿 不会被 自己对应的 捕获沿采到。
对于的setup和hold时序要求:
有下面的电路,每两个周期产生一个有效数据,在这个路径上定义一个多周期路径约束是安全的,它表明目标时钟的第一个边沿是无效的,只有目标时钟的第二个边沿将捕获一个新数据。
下面的约束建立了一个新的setup关系: 确保 [setup启动沿(launch edge)] 不会被 [有效捕获边沿(capture edge)之前到达的边沿] 捕获。数据需要保持一整个周期才可以改变,需要在数据路径插入大量延迟。
set_multicycle_path 2 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
当修改 setup 关系时,hold 关系也会随着 setup 启动和捕获边沿的变化而修改。
重要:如果新的 hold 要求变得过于激进,可能会导致难以时序收敛。需要在保证设计安全的前提下,放宽要求。
难以收敛的原因:根据第一个hold关系,
由于启用了时钟,因此不需要将data0_reg中的数据保存一个周期,以使该路径发挥作用。在这种情况下,Xilinx建议将 hold 关系更改回原来的关系,即相同的启动和捕获边沿之间的关系。为此,必须添加第二个多周期路径约束,只修改hold检查:
set_multicycle_path 1 -hold -end -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
-end选项与set_multicycle -hold命令一起使用,因为捕获时钟的边沿必须向后移动。
注意:因为启动时钟和捕获时钟具有相同的波形,-end选项是可选的。向后移动捕获边沿与向前移动启动边沿会产生相同的保持关系。为了简化表达式,下面两个示例中删除了-end选项。
所以总的约束为:
set_multicycle_path 2 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path 1 -hold -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
按自己的理解总结一下,第一条约束把setup关系捕获设为两个周期,即捕获沿(setup 默认 -end)往前移1个周期(2-默认值1=1),同时hold关系捕获沿也会跟着往前移。所以第二条约束把hold关系设为1个周期,即把启动沿(hold 默认 -start)往前移1个时钟周期(1-默认值0=1),把hold关系改回去。
如果需要设置setup 4个周期,约束为:
set_multicycle_path 4 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path 3 -hold -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
总结:
第一条约束把setup关系捕获设为4个周期,即捕获沿(setup 默认 -end)往前移3个周期(4-默认值1=3),同时hold关系捕获沿也会跟着往前移3个周期。
第二条约束把hold关系设为3个周期,即把启动沿(hold 默认 -start)往前移3个时钟周期(3-默认值0=3),因为是相同的时钟周期,所以第3个启动沿到第3个捕获沿的hold等效于第1个启动沿到第1个捕获沿的hold,即hold关系改回原来的关系。
假设setup路径乘数被设置为5。因为没有指定hold路径乘数,hold 关系是从setup启动和捕获边沿派生的:
set_multicycle_path 5 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
对于四个周期的hold需求,时序驱动的实现工具通常必须在数据路径中插入大量的延迟,以满足Slow和fast的时序边界。这将导致不必要的面积和功耗消耗。因此,在可能的情况下,放松hold要求是很重要的。
这相当于每隔5个周期启动并捕获一个新数据。
set_multicycle_path 5 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path 4 -hold -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
默认情况下,对目标时钟应用setup乘数,在这种情况下,这会导致将捕获边沿向前移动到第五个周期,而不是第一个周期。
因此,默认情况下,hold检查跟随setup检查。
在指定第二个命令时,将对源时钟应用hold乘数,在本例中,这将导致将启动边沿向前移动到第四个周期。
由于源时钟和目标时钟具有相同的波形,并且是相位对齐的,因此上图与下图等价。
总结:
第一条约束把setup关系捕获设为5个周期,即捕获沿(setup 默认 -end)往前移4个周期(5-默认值1=4),同时hold关系捕获沿也会跟着往前移4个周期。
第二条约束把hold关系设为4个周期,即把启动沿(hold 默认 -start)往前移4个时钟周期(4-默认值0=4),因为是相同的时钟周期,所以第4个启动沿到第4个捕获沿的hold等效于第1个启动沿到第1个捕获沿的hold,即hold关系改回原来的关系。
重要:通常,在一个时钟域内或两个具有相同波形的时钟之间,当定义了setup乘数N时,定义了N-1的hold乘数(最常见的情况),如下所示。
set_multicycle_path N -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path N-1 -hold -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
有时必须在具有相同时钟周期的两个时钟域之间定义一个时间约束,但两个时钟之间存在相移。在这些情况下,理解默认setup和hold时序引擎使用的关系是至关重要的。如果不仔细调整,两个时钟之间的相移可能会导致两个时钟域之间的逻辑过度约束。
例如,假设如下:
时序引擎通过观察两种波形的所有边沿并选择启动和捕获时钟的两个边沿来计算setup关系,从而产生更严格的约束。
由于时钟相移,时序引擎使用的setup和hold关系可能不是预期的。
在本例中,由于相移造成的setup约束为0.3 ns。这使得几乎不可能实现时序收敛。另一方面,hold检查是-3.7 ns,这太宽松了。
setup和hold边沿必须调整以匹配你的意图。这是通过加设置乘数为2的多周期约束来完成的:
set_multicycle_path 2 -setup -from [get_clocks CLK1] -to [get_clocks CLK2]
这将导致设置要求的捕获边沿向前移动一个周期。hold的默认边沿来自setup要求。它不需要指定。
在相移为负的情况下,如图5-14所示,在两个时钟域之间,用于setup和hold检查的启动和捕获边缘类似于前面的那些(单一时钟域,无相移)。
对于负相移,通常不需要多周期约束来平衡相移的影响。如果相移太大,时钟启动或捕获边缘必须调整,以保持实际的setup和hold要求,否则会发生异常。
STA工具默认关系:
示例1:setup = 3,hold自动跟随
set_multicycle_path 3 -setup -from [get_clocks CLK1] -to [get_clocks CLK2]
setup默认 -end ,设置setup为3个时钟周期,即捕获时钟向前(时间增大方向)移2个时钟周期(3-1(默认)=2),hold跟随。
要使该路径发挥作用,不需要在一个周期的CLK2中保存启动寄存器中的数据。这样做会增加不必要的逻辑,从而增加面积和功耗。
因为接收寄存器有一个时钟使能信号,可安全的放松hold要求而没有亚稳态的风险。
示例2:setup = 3,hold = 2(-end)
set_multicycle_path 3 -setup -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path 2 -hold -end -from [get_clocks CLK1] -to [get_clocks CLK2]
hold 指定了 -end,设置hold关系为2个周期,则捕获边沿向后(时间减小方向)移动2个周期(2-0(默认)=2)。
重要:对于从slow到fast的跨时钟域,当定义了setup乘数N时,针对捕获时钟(-end)定义一个hold乘数N-1(最常见的情况),如下面的代码示例所示。
set_multicycle_path N -setup -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path N-1 -hold -end -from [get_clocks CLK1] -to [get_clocks CLK2]
STA工具默认关系:
示例1:setup = 3(-start),hold = 2
set_multicycle_path 3 -setup -start -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path 2 -hold -from [get_clocks CLK1] -to [get_clocks CLK2]
第一条约束:指定了 -start 的 setup 多周期为3,对应 启动沿 前移2个周期(3-1(默认)=2),hold也对应移动2个周期。
第二天约束:默认-start 的hold 多周期为2,对应 启动沿 后移动2个周期,恢复默认hold。
重要:对于快到慢的跨时钟域,针对启动时钟(-start)定义setup乘数为N,hold乘数为N-1(最常见的情况)。如下面的代码示例所示。
set_multicycle_path N -setup -start -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path N-1 -hold -from [get_clocks CLK1] -to [get_clocks CLK2]
情况 | 多周期约束 |
---|---|
相同时钟域,或同频无移相的同步时钟域 | set_multicycle_path N -setup -from CLK1 -to CLK2 |
set_multicycle_path N-1 -hold -from CLK1 -to CLK2 | |
SLOW-to-FAST 的同步时钟域 | set_multicycle_path N -setup -from CLK1 -to CLK2 |
set_multicycle_path N-1 -hold -end -from CLK1 -to CLK2 | |
FAST-to-SLOW 的同步时钟域 | set_multicycle_path N -setup -start -from CLK1 -to CLK2 |
set_multicycle_path N-1 -hold -from CLK1 -to CLK2 |
虚假路径是指在设计中拓扑上存在的路径,但其中任何一种路径:
虚假路径的例子包括:
非功能路径示例:
由于两个多路复用器都是由相同的选择信号驱动的,所以从Q到D的路径不存在,应定义为假路径。
提示:使用多周期约束来代替假路径约束:(1)意图只是松弛同步路径上的时序要求;但是(2)该路径仍然需要时序分析、验证和优化。
从时间分析中删除虚假路径的原因包括:
虚假路径是在Xilinx设计约束(XDC)命令set_false_path中定义的:
set_false_path [-setup] [-hold] [-from ] [-to ] \
[-through ]
可以对该命令使用以下附加选项来微调路径规范。
注意:当使用-through选项而不使用-from和-to时要小心,因为它会从计时分析中删除所有经过这个引脚或端口列表的路径。当时间限制是为IP或子块设计的,但随后在不同的上下文中或更大的项目中使用时,要特别小心。单独使用-through时,可以删除比预期多得多的路径。
-through选项的顺序很重要。请参见以下示例:
例如,以下两个命令是不同的:
set_false_path -through cell1/pin1 -through cell2/pin2
set_false_path -through cell2/pin2 -through cell1/pin1
删除从复位端口到所有寄存器的定时路径的示例如下:
set_false_path -from [get_port reset] -to [all_registers]
使用实例禁用两个异步时钟域之间的时序路径(例如,从时钟CLKA到时钟CLKB):
set_false_path -from [get_clocks CLKA] -to [get_clocks CLKB]
前面的示例禁用从时钟CLKA到时钟CLKB的路径。从时钟CLKB到时钟CLKA的路径没有被禁用。因此,禁用两个时钟域之间的任意方向的所有路径需要两个set_false_path命令:
set_false_path -from [get_clocks CLKA] -to [get_clocks CLKB]
set_false_path -from [get_clocks CLKB] -to [get_clocks CLKA]
重要:尽管前面的两个set_false_path示例实现了预期的功能,但是当两个或多个时钟域是异步的,并且在两个方向上都应该禁用这些时钟域之间的路径时,Xilinx建议使用set_clock_groups命令:set_clock_groups -group CLKA -group CLKB
可以覆盖路径的最大延迟或最小延迟:
Maximum Delay 约束语法:
set_max_delay [-datapath_only] [-from ]
[-to ] [-through ]
Minimum Delay 约束语法:
set_min_delay [-from ]
[-to ] [-through ]
可以使用其他命令选项来微调路径规范。
默认情况下,时序裕量的计算包含时钟偏斜。
-datapath_only选项可用于从裕量计算中移除时钟偏斜。只有 set_max_delay 命令支持 -datapath_only 选项,并且需要 -from 选项。
带或不带 -datapath_only选项 的 set_max_delay 的路径延迟计算的常见行为:
当没有使用 -datapath_only 选项时,设置路径的最大延迟约束不会改变该路径的最小要求。该路径上的 hold(或 removal)检查仍然是默认的。
注意:使用 -datapath_only 选项和 set_max_delay 会导致在对应路径上忽略 hold 需求(会生成一些内部的set_false -hold约束)。
类似地,在路径上设置最小延迟约束不会修改默认setup(或 recovery)检查。
如果一个路径只有最大延迟要求,例如,可以使用set_max_delay和 set_false_path命令组合来限制该路径。请看下面的例子:
set_max_delay 5 -from [get_pins FD1/C] -to [get_pins FD2/D]
set_false_path -hold -from [get_pins FD1/C] -to [get_pins FD2/D]
set_max_delay命令和set_min_delay命令通常不用于约束输入或输出逻辑。输入端口和第一级寄存器之间的输入逻辑通常由set_input_delay命令约束。该命令提供了将时钟与输入端口相关联的选项。
出于同样的原因,set_output_delay命令通常会限制寄存器最后一层和输出端口之间的输出逻辑。但是,set_max_delay命令和set_min_delay命令通常用于约束主输入端口和主输出端口之间的纯组合路径(in-to-out I/O路径)。
set_max_delay命令还可以用于约束没有时钟关系但需要最大延迟的异步信号。
例如,两个异步时钟域之间可以使用set_clock_groups命令(推荐使用)或set_false_path命令(不推荐)关闭。这假设你已经正确地设计了跨时钟域,例如,一个双寄存器同步器或FIFO。但是,您仍然必须确保两个时钟域之间的路径延迟不是不必要的高。
在一些多bit CDC场景中,bit之间的偏移必须在特定要求范围内。即使可以通过总线倾斜约束(set_Bus_skew)来约束倾斜,也必须确保两个时钟域之间的路径延迟不会过高。这可以通过将源XDC文件中相关路径上的 set_false_pat h或 set_clock_groups 约束替换为set_max_delay –datapath_only来实现。
注意:False Path约束和Max Delay约束之间存在运行时影响,因为这些路径的时间是基于Max Delay的。
如果必须为两个时钟域之间的部分或所有路径指定最大延迟,则必须使用命令 set_max_delay -datapath_only 来约束这些路径。在这种情况下,set_clock_groups 不能用于将两个时钟域定义为异步,因为它在约束优先级方面取代了 set_max_delay 约束。其他跨时钟域路径必须结合set_false_path或 set_max_delay 约束进行约束。
例子:
set_max_delay -datapath_only -from -to
与其他XDC约束不同,set_max_delay 命令和 set_min_delay 命令在 -from 和 -to 选项的情况下,可以分别接受无效的 startpoint 或 endpoint 列表。
当指定了无效的 startpoint 时,时序引擎将停止通过该节点的时间传播,以便该节点成为有效的起始点。
在下面的例子中,唯一有效的起始点是FD1/C:
set_max_delay 5 -from [get_pins FD1/C]
如果约束作用于FD1/Q,时序引擎通过时序弧 C->Q 停止传播,使引脚Q成为一个有效的起点:
set_max_delay 5 -from [get_pins FD1/Q]
停止时间传播以创建有效起始点的过程称为路径分割(path segmentation)。路径分割会影响最大和最小延迟分析。路径分割还影响通过这些节点(FD1/C和FD1/Q)的时序约束。
注意:由于路径分割,从FD1/Q开始的路径的启动时钟没有使用时钟插入延迟。这可能会潜在地导致较大的偏斜,因为 endpoint 的时钟偏斜仍然被考虑在内。
注意:路径分割可能会产生意想不到的后果。完全避免路径分割,或者非常小心地使用它。
路径分割后,对路径没有默认hold要求。假设如果没有指定-datapath_only 选项,可以使用 set_min_delay 命令设置路径的hold要求。
由于存在风险,当路径分割发生时,会发出一个严重的警告。
如果将输出FD1/Q作为起始点,以避免考虑时钟偏差,Xilinx建议使用-datapath_only选项:
set_max_delay 5 -from [get_pins FD1/C] -datapath_only
同样,当指定无效 endpoint 时,时序引擎将在节点之后停止传播,以便该节点成为有效 endpoint 。
在下面的例子中,最大延迟是在LUTA/O上指定的,这不是一个有效的 endpoint :
set_max_delay 5 -from [get_pins LUTA/O]
为了使LUTA/O成为有效端点,时间在LUTA/O之后停止传播。因此,所有通过LUTA/O的计时路径都受到setup和hold的影响。对于从REGA/C开始到LUTA/O结束的路径,只考虑启动时钟的插入延迟。这可能会导致非常大的偏斜
因为路径分割通过时序弧停止传播,它可能会产生意想不到的后果。所有经过这些节点的时间路径都会受到影响。
下面的例子中,在LUTA/O和REGB/D之间设置了一个最大延迟:
set_max_delay 6 -from [get_pins LUTA/O] -to [get_pins REGB/D]
因为引脚LUTA/O不是一个有效的起点,路径分割发生和时间弧从LUTA/I*和LUTA/O被打破。即使set_max_delay约束只在LUTA/O和REGB/D之间设置,其他路径如REGA/C和REGC/D之间的路径也被打破。
路径分割可能会让人觉得时序例外之间的优先级发生了改变,但事实并非如此
set_max_delay 约束是否被 set_clock_groups 约束取代可能存在差异。考虑以下两个场景。
场景1:
set_max_delay -datapath_only -from -to
在这个场景中,为-from/-to提供了实例名称。set_max_delay约束始终由 set_clock_groups -asynchronous 覆盖,因为Vivado在提供实例时总是选择有效的起始点。
场景2:
set_max_delay -datapath_only -from -to
在这种情况下,如果随-from提供的管脚名称导致路径分割,则特定的set_max_延迟约束不会被set_clock_groups -asynchronous 覆盖。背后的原因是路径分割迫使从管脚名称开始的路径不再被认为是由第一个时钟域启动的。因此,该路径不再被set_clock_groups约束所覆盖,set_max_delay约束将被应用。
在某些设计中,某些信号在特定模式下具有恒定值。例如,在功能模式下,测试信号不会切换,因此根据其活动电平与VDD或VSS绑定。这也适用于在设计通电后不会切换的信号。同样,今天的设计有多种功能模式,在某些功能模式下处于活动状态的一些信号在其他模式下可能处于非活动状态。
为了帮助减少分析空间、运行时间和内存消耗,让静态时序分析引擎知道具有常量值的信号是很重要的。这对于确保不报告非功能路径和无关路径也是至关重要的。
使用set_case_analysis命令向计时引擎声明一个无效的信号。该命令适用于引脚和/或端口。
注意:在对引脚进行情况分析后,与该引脚相关的时序弧被禁用。时序引擎不会报告任何路径通过禁用的时序弧。
set_case_analysis命令的语法如下:
set_case_analysis
参数可以是以下任何一个:
0, 1, zero, one, rise, rising, fall, or falling
当值被指定为rise, rising, fall, 或 falling,这意味着应该只考虑给定的引脚或端口进行指定转换的时序分析。另一个转换被禁用。
case 值可以在端口、cell的引脚或分层模块的引脚上设置。
在下面的例子中,在多路复用器clock_sel的输入管脚上创建了两个时钟,但在选择管脚S上设置恒定值后,只有clk_2通过输出管脚传播。
create_clock -name clk_1 -period 10.0 [get_pins clock_sel/I0]
create_clock -name clk_2 -period 15.0 [get_pins clock_sel/I1]
set_case_analysis 1 [get_pins clock_sel/S]
可以使用set_disable_timing命令禁用cell内的计时弧。只有从cell的输入端口到输出端口的时序弧可以被禁用。
注意:set_disable_timing命令还可以用于禁用端口或wire的时序弧。在这种情况下,不使用命令行选项-from和-to,只指定端口对象或时序弧对象。
语法:
set_disable_timing [-from ] [-to ] [-quiet] [-verbose]