系列文章目录
FPGA静态时序分析与约束(一)、理解亚稳态
FPGA静态时序分析与约束(二)、时序分析
FPGA静态时序分析与约束(三)、读懂vivado时序报告
文章目录
- 系列文章目录
- 前言
- 一、什么是时序约束?
- 二、约束文件
- 三、约束语法
- 3.1 时钟约束
- 3.1.1 基准时钟约束
- 3.1.2 虚拟时钟约束
- 3.1.3 衍生时钟约束
- 3.1.4 时钟特性约束
- 3.2 输入/输出接口约束
- 3.2.1 输入接口约束
- 3.2.2 输出接口约束
- 3.3 时钟分组和跨时钟约束(虚假路径约束)
- 3.3.1 虚假路径约束
- 3.3.2 异步时钟组约束
- 3.4 时序例外约束
- 3.4.1 时序例外分类:
- 3.4.2 时序例外约束推荐顺序
- 3.5 多周期约束
- 3.6 最大最小延迟约束
- 参考资料
前言
前三章我们已经掌握了时序分析基本原理。但是,综合工具不知道我们的设计时钟频率是多大,我们想要的设计需求延时是多少,外部进来的时钟和数据之间的走线延时是多少,就没办法给出正确的时序报告以及违规路径。因此时序约束是必不可少的,只有正确的时序设计加上合理的时序约束,整个设计系统才能高效的运转。
一、什么是时序约束?
简单理解为:
- 设计者根据实际的系统功能,通过时序约束的方式提出时序要求
- FPGA 编译工具根据设计者的时序要求,进行布局布线
- 编译完成后,FPGA 编译工具还需要针对布局布线的结果,套用特定的时序模型(FPGA 器件厂商能够使用这样的模型,对 FPGA 布局布线后的每一个逻辑电路和走线计算出延时信息),给出最终的时序分析和报告
- 设计者通过查看时序报告;确认布局布线后的时序结果是否满足设计要求
二、约束文件
vivado的约束文件后缀为.XDC。文件里的语法为XDC语言,和TCL语言类似,都属于顺序语言。
- 必须先定义变量,然后才能加以使用。同样,必须先定义时序时钟,然后才能将其用于其他约束中
- 对于覆盖相同路径并具有相同优先级的等效约束,适用最后一项约束
- 当多个时序例外覆盖同一条路径时,适用具有更高优先级的约束
因此在进行时序约束时需要考虑一下约束的顺序。以下是xilinx官方推荐的约束顺序:
从时序约束本身来说,如上图所示,通常可以分为以下 4 个主要步骤进行,即时钟约束(Create Clock)、输入/输出接口约束(Input/Output Delays,I/O 约束)、时钟分组和跨时钟约束(Clock Groups and CDC)、时序例外约束(Timing Exceptions)。
一个工程里可以拥有多个约束文件,根据设计需求可以有以下建议:
- 对于小型设计团队开发的简单设计:
- 1 个文件存储所有约束
- 1 个文件存储物理约束 + 1 个文件存储时序约束
- 1 个文件存储物理约束 + 1 个文件存储时序(综合)+ 1 个文件存储时序(实现)
- 对于复杂设计(含多个 IP 核或多个设计团队):
- 1 个文件存储顶层时序约束 + 1 个文件存储顶层物理约束 + 1 个文件对应 1 个 IP
当使用多个 XDC 文件时,必须特别留意时钟定义,并确认从属关系排序是否正确,物理约束可能位于任意约束文件中的任意位置。
三、约束语法
3.1 时钟约束
时钟必须首先完成定义,方可供其他约束使用。时序约束创建流程的第一步是明确必须定义哪些时钟,以及这些时钟必
须定义为“primary clock”(基准时钟)还是“generated clock”(生成时钟)。
在 Vivado 工具中,设计中未约束的时钟可以通过时钟网络报告(Clock NetworksReport)和时序确认报告(Check Timing Report)进行查看。
3.1.1 基准时钟约束
create_clock -name<clock_name>-period <period> -waveform {<rise_time> <fall_time>} [get_ports <input_port>]
- name 后的< clock_name >是设计者自定义的主时钟名称,用于标示定义的主时钟,后续的约束若引用已经定义的主时钟, clock_name >就是唯一的引用标识。如果约束时不指定< clock name>,则会默认使用< port_name >所指定的时钟物理节点
作为名称 - period 后的< period>是定义的主时钟周期,单位是 ns,取值必须大于 0
- waveform 后的{< rise_time> < fall_time>}用于定义时钟的上升沿和下降沿时刻;<rise_time>表示上升沿时刻,默认值为 0;< fall time>表示下降沿时刻,默认值是时钟周期的一半
- get_ports 表示定义的主时钟的物理节点是 FPGA 的引脚
- 约束实例1:若一个名为 sysclk的板级时钟信号,通过 clk_in 端口传输到 FPGA 器件内,并且它的时钟周期是 20ns,但时钟占空比为 25%,有 90°相移。那么,相应的 XDC约束语法如下:
create_clock -name sysclk -period 20 -waveform {5 10} [get_ports clk_in]
- 约束实例2:如下图所示,FPGA内部高速收发器的输出时钟网络,经过时钟管理单元 mmcm0 后,产生多不同的衍生时钟。在这种应用中,通常需要将高速收发器的输出管脚(例如恢复的时钟)作为基准时钟约束,相应的 XDC约束语法如下:
create_clock -name txclk -period 6.667 [get_pins gt0/TXOUTCLK]
(ps:没有定义上升沿和下降沿时,默认占空比为50%)
- 约束实例3:某些硬件原语输出管脚,如果输入输出没有很强的因果相关性,可以使用原语输出管脚作为基准时钟:
如果从输入引脚 sysclk 经过不同的 BUFG 所产生的时钟clk0(BUFGO)和 clk1(BUFG1)分别作为时序路径中的一对源寄存器(regl)和目的寄存器(reg2)的输入时钟。若此时还是指定 BUFG 原语的输出端作为主时钟约束的根节点,就可能由于 cIk0 和 clk1之间时钟偏斜差异而导致时序分析结果的误差。在这种情况下,clk0,clk1 和输人时钟 sysclk 存在很强的因果相关性,只需要直接对源时钟 sysclk 进行主时钟约束,就能覆盖时钟 clk0 和 clk1 所驱动的所有时序路径。
- 约束实例4:对差分时钟信号的约束,如图 3.所示,一个差分缓冲器(IBUFDS)产生的单端时钟信号作为 PLL的输入时钟在这种情况下,只需要对差分缓冲器的输入正端(sysclkp)进行主时钟约束即可。
create_clock -name sys_clk -period 6.667 [get_ports sys_clk_p]
3.1.2 虚拟时钟约束
管脚上除了时钟,还有一些数据信号,其同步时钟只存在于外部芯片,并不存在于 FPGA 器件内。这种情况下,为了时序分析的需要也必须定义一个时钟用于描述时序数据引脚的外部时钟信号,这个时钟就称为虚拟时钟。顾名思义,这个时钟并不是实际存在于FPGA 器件中的,因此它在定义时无须依附于任何设计中的实际物理节点(不像主时钟约束时必须有实际的端口或网络相映射)。虚拟时钟同样是以createclock 命令进行约束定义的,但无须指定目标端口或网络。约束语法如下:
create_clock -name <> -period <period>
- 约束例子1:如图所示。对于pin2reg的系统同步接口,目的寄存器reg2的驱动时钟是实际存在的,设计者可以对其进行主时钟约束,而源寄存器的主时钟vclk 并不会传输到 FPGA 器件,此时设计者就可以对其进行虚拟时钟约束,以便于时序分析,对源寄存器的同步时钟 vclk 进行虚拟时钟约束,对目的寄存器的同步时钟clk 进行主时钟约束。约束语法如下:
create_clock-period 10.000 -name vclk - waveform {0.000 5.000}
create_clock-period 10.000 -name scLK - waveform {0.000 5.000} [get ports clk]
若源寄存器和目的寄存器的时钟存在相差,则两个时钟的约束参数就会发生改变,应根据实际情况进行约束:
create_clock -period 10.000 -name vclk - waveform {2.000 7.000}
create_clock -period 10.000 -name sclk - waveform {0.000 5.000} [get ports clk]
3.1.3 衍生时钟约束
衍生时钟主要是指由已有的主时钟进行分频、倍频或相移而产生出来的时钟信号,如由时钟管理单元(MMCM等)或一些设计逻辑所驱动产生的时钟信号。衍生时钟的定义取决于主时钟的特性,衍生时钟约束必须指定时钟源,这个时钟源可以是一个已经约束好的主时钟或另一个衍生时钟。衍生时钟并不直接定义频率、占空比等参数,而是定义其与时钟源的相对关系,如分频系数、倍频系数、相移差值、占空比差值等。因此,在做衍生时钟约束前,要求先做好其时钟源的约束定义。
大部分生成时钟都是由 Vivado 自动衍生的,因此也是自动添加好约束的。如果生成时钟由用户自己生成(例如,自分频),时序工具不能自动生成的衍生时钟,需要使用 create_generated_clock 命令对其进行手动约束。语法如下:
create_generated_clock -name <generated_clock_name> \-source <master_clock_source_pin_or_port> \-multiply_by <mult_factor> \-divide_by <div_factor> \<pin_or_port>
- \是换行符号,无实际含义。
- name后的 generated clock name用于指定衍生时钟名称。若不指定该名称,将自动以 pin or_port 指定的物理节点作为该衍生时钟的名称。
- source 后的 master_clock_source_pin_or_port 指定衍生时钟的源时钟引脚或端口,源时钟可以是一个已经定义过的主时钟、虚拟时钟或衍生时钟
- multiply_by 后的<mult_factor>用于指定衍生时钟相对于源时钟的倍频系数,取值必须大于或等于 1.0。
- divide_by 后的<div_factor>用于指定衍生时钟相对于源时钟的分频系数,取值必须大于或等于 1.0。
- <pin_or_port >用于指定衍生时钟的物理节点、引脚或端口名称。
- 约束例子1:如图所示,已经约束好的主时钟 clkin 的周期为 5ns,该时钟通过寄存器 regA 实现时钟的2分频,产生名为 clkdiv2 的衍生时钟:
create_clock -name clkin-period 5 [get ports clkin]
create_generated_clock -name clkdiv2 -source [get_ports clkin]- divide_by 2 [get_pinsREGA/Q]
3.1.4 时钟特性约束
定义时钟及其波形后,下一步是输入与噪声或不确定性建模相关的所有信息。
- 对于时钟抖动,最好使用 Vivado所使用的默认值。若设计者希望更改某个时钟信号的默认时钟抖动值,可以使用
set _input_jitter
命令进行更改指定峰值间抖动值(ns为单位)。set_input_jitter
命令只能约束主时钟的抖动值,不能用于约束衍生时钟的抖动值。除了MMCM或PLL外,主时钟所设定的时钟抖动值将会传递给它的衍生时钟。每条 set_input_jitter 命令只能约束一个主时钟,若需要约束多个主时钟的抖动值,则需要多条命令分别进行约束。 - 如果器件电源有噪声,请使用
set_system_jitter
调整全局抖动。vivado不建议增大默认系统抖动值。
set_input_jitter
约束语法如下:
set_input_jitter [get_clocks <clock_name>] <jitter_in_ns>
- get_clocks用于指定需要约束抖动值的主时钟名 < clock_name> ,注意这个主时钟必须是事先约束定义好的
- <jitter_in_ns>指定抖动值,取值必须大于或等于 0,单位为 ns
约束例子1:以下两条命令分别约束主时钟 clk1 和 clk2的输人抖动值为0.3ns 和 0.3ns。虽然它们的时钟抖动值一样,但仍然需要使用 set input iitter 命令为两个时钟分别设定抖动值。
set_input_jitter clk1 0.3
set_input_jitter clk2 0.3
set_system jitter
约束语法如下:
set_system_jitter <jitter_in_ns>
- <jitter_in_ns>指定所有时钟的系统抖动值,取值必须大于或等于 0,单位为 ns。
约束例子:例如,对时钟引脚 clk 做主时钟约束,命名为sysclk,使用 set_system_jitter 约束该主时钟的输入抖动值为 0.1ns,命令如下:
create_clock-period 10-name sysclk [get ports clk]
set_system_jitter sysclk 0.1
- 除时钟抖动以外的所有可能影响时钟周期性偏差的因素,都可以使用set clockuncertainty 命令进行约束定义。
set_clock_uncertainty
的基本语法结构如下:
set_clock_uncertainty -setup \-from [get_clocks <clock0_name>] \-to [get_clocks <clock1_name>] \<uncertainty_value>
- setup 表示定义建立时间检查的时钟不确定性时间,也可以使用hold 表示定义保持时间检查的时钟不确定性时间;如果不指定-setup 和-hold,则表示同时定义建立时间检查和保持时间检查的不确定性时间。
- from 指定源时钟
- to指定目标时钟,对于非跨时钟域的路径,一般不需要指定from 和to。
- get_clocks 用于指定实际的时钟物理节点名称<clock0_name>或< clock1_name>。
- <uncertainty_value >指定时钟不确定时间,单位为 ns。
当设计者希望为某个时钟或某两个时钟之间的时序路径增加时序余量时,也可以使用 set_clock_uncertainty 命令进行约束定义
例子1:若要给主时钟 clk 增加 500ps(0.5ns)的余量,那么可以使用以下命令为时钟 clk 设定额外的 0.5ns的时钟不确定性时间
set_clock_uncertainty-from clk-to clk 0.500
3.2 输入/输出接口约束
除了指定设计的每个端口的位置和 I/O 标准外,还必须指定输入和输出延迟约束以描述进出器件接口的外部路径的时序。这些延迟是根据通常同样在开发板上生成并进入器件的时钟来定义的。在某些情况下,如果与 I/O 路径相关的时钟所含波形不同于开发板时钟的波形,那么必须根据虚拟时钟来定义延迟
3.2.1 输入接口约束
set_input_delay 命令用于指定输入数据引脚相对于其时钟沿的路径延时。通常输入延时值包括了数据信号从外部芯片到FPGA引脚的板级延时以及与其板级的参考时钟之间的相对延时值。因此,输人延时值可以是正值,也可以是负值,取决于时钟相对数据信号在FPGA引脚上的相位关系。
set_input_delay 命令可以应用于 FPGA 器件的输入数据引脚或双向数据引脚,但不适用于 FPGA 内部信号或时钟输入引脚。若使用 set _input_delay 约束时钟输人引脚,将会被时序工具忽略。set_input_delay 以max 和min 参数分别表示约束的最大值和最小值,最大值用于建立时间检查,最小值用于保持时间检查。
- set_input_delay 约束命令的基本语法如下:
set_input_delay <max delay> -max -clock [get_clocks <clock>] [get_ports <ports>]
set_input_delay <min delay> -min -clock [get_clocks <clock>] [get_ports <ports>]
- max delay表示设定最大延时值
- min delay 设定最小延时值
- get_clocks < clock>为时钟名称
- get_ports < ports>为数据端口
约束例子1:输人引脚为 CLK0 的主时钟 sysclk,同时约束了max 和min 值分别为4ns 和 1ns 的输入延时约束:
create_clock -name sysclk -period 10 [get ports CLK0]
set_input_delay -clock sysclk -max 4 [get ports DIN]
set_input_delay -clock sysclk -min 1 [get ports DIN]
3.2.2 输出接口约束
输出延迟与输入延迟类似,区别在于输出延迟表示为了确保在所有情况下均可正常工作,输出路径在器件外部的最短和
最长时间。
- set_output_delay 约束命令的基本语法如下:
set_output_delay <delay> -clock [get_clocks <clock>] [get_ports <ports>]
- delay表示设定延时值
- get_clocks < clock>为时钟名称
- get_ports < ports>为数据端口
约束例子1:该实例对一个输出引脚DOUT进行输出延时约束,参考时钟是事先定义好的主时钟sysclk。该约束未指定max 和min,表示约束的延时值同时应用于max 和min 两种情况:
create_clock -name sysclk-period 10 [get ports CLK0]set output delay-clock sysClk 6 [get ports DOUT]
3.3 时钟分组和跨时钟约束(虚假路径约束)
所谓虚假路径并非此路径不存在,而是意指该路径是设计中的非功能路径或没有任何时序要求的路径。显然,这样的路径需要通过虚假路径约束,让时序工具放弃对它们的任何时序努力和时序分析。为何要做虚假路径约束?因为移除了这些非功能路径的时序要求后,可以减少花在这些非功能路径上的时序努力,减少编译时间,同时腾出有限的布局布线资源,用于提升整体的时序性能。
工程中施加了虚假路径约束,设计工具在编译时将会完全移除这些路径,不做任何的时序努力和分析。可以预见,在减少了具有时序要求的时序路径后,设计编译的时间可以在一定程度上得以提升。反之,若很多虚假路径没有被移除,时序工具需要为这些虚假路径做出额外的编译努力,势必会导致编译时间的增加,甚至可能由于有限的布局布线资源无法得到最合理的分配,导致时序无法收敛。
因此,设计中的虚假路径都应该有效识别并进行约束。常见的虚假路径包括如下几种:
- 已经做过同步处理的跨时钟域路径,
- 上电后只做一次初始化写入的寄存器路径
- 复位或测试逻辑的路径。
- 某些实际并不存在的时序路径
默认情况下,Vivado对设计中所有时钟之间的路径进行时序约束。设计者可使用以下约束来修改此默认行为:
- set_clock_groups:禁用您识别的时钟组之间的时序分析,但不禁用同一个组中的时钟之间的时序分析
- set_false_path:仅禁用由 -from 和 -to 选项所指定的方向上的时钟之间的时序分析
3.3.1 虚假路径约束
虚假路径约束的基本语法结构如下:
set_false_path -from <startpoints> -to <endpoints>
- from 指定约束路径的起始节点< startpoints>
- to指定约束路径的终止节点< endpoints>
约束例子1:如下虚假(False)路径约束,将覆盖到所有以 reset 信号起始的寄存器路径:
set_false_path -from [get port reset]-to [all registers]
约束例子2:如下路径约束,将覆盖从 CLKA 时钟到 CLKB时钟的所有时序路径。注意这个约束中,仅覆盖从 CLKA 时钟到 CLKB时钟的所有时序路径,但不包括从 CLKB时钟到CLKA 时钟的时序路径:
set_false_path-from [get clocks CLKA]-to [get clocks CLKB]
若希望 False 约束既能覆盖从 CLKA 时钟到 CLKB时钟的所有时序路径,也能覆盖从CLKB 时钟到 CLKA 时钟的所有时序路径,那么必须两个时钟方向都做约束:
set_false_path -from [get clocks CLKA]-to [get clocks CLKB]
set_false_path -from [get clocks CLKB]-to [get clocks CLKA]
3.3.2 异步时钟组约束
异步时钟组约束的基本语法结构如下:
set_clock_groups -asynchronous -group <clock_name_1> -group <clock_name_2>
该时钟组约束下,clock_name_1到clock_name_2、clock_name_1到clock_name_2都将不被分析。
当 2 个主时钟及其相应的生成时钟构成 2 个异步域,并且这 2 个异步域之间的所有路径均已正确完成同步时,即可立
即对多个时钟应用时钟组约束:
set_clock_groups -asynchronous -group {clkA clkA_gen0 clkA_gen1 } \-group {clkB clkB_gen0 clkB_gen1 }
3.4 时序例外约束
3.4.1 时序例外分类:
3.4.2 时序例外约束推荐顺序
时钟约束、I/0约束以及时序例外约束,是时序约束的最基本、最重要的约束方法。对于这三大类约束以及它们所涵盖的具体的约束语法,在进行时序约束时,通常也需要遵循定的顺序进行约束输入,以满足时序约束语法的基本要求。
按照推荐的约束顺序,从上到下罗列了具体的约束语法:
- 时序约束:
- 主时钟约束
- 虚拟时钟约束
- 衍生时钟约束
- I/O约束
- 时序例外约束:
- 虚假路径约束
- 最大/最小延迟约束
- 多周期约束
3.5 多周期约束
多周期约束用于调整建立时间和保持时间检查的起始时钟沿到目标时钟沿所需的时钟周期数。因为默认情况下,Vivado 时序工具都是以单周期为单位进行时序路径分析。但在实际设计中,单周期路径对于某些逻辑可能并不准确,导致对它们的时序要求过高(即过约束)。
其基本语法结构如下:
set_multicycle_path <num cycles> -from <startpoints> -to <endpoints>
set_multicycle_path -hold <num cycles> -from <startpoints> -to <endpoints>
- < num cycles>参数是必须指定的,它就是多周期约束的最重要的“多”的体现即用于设置修改约束路径分析的时钟周期数,该参数的取值必须是大于0的整数
- setup 和hold 选项用于指定约束命令所针对的是路径的建立时间分析(setup)还是保持时间分析(hold),默认人建立时间
- from 指定约束路径的起始节点< startpoints >
- to指定约束路径的终止节点< endpoints>
约束例子1:放宽建立时间要求,同时保持时间要求保留不变,当源时序单元和目标时序单元受时钟使能信号控制并且此信号每 N个周期就会激活时钟时,此状况就会发生。以下示例中,时钟使能每3个周期就会激活一次,并且起点和端点时钟相同
约束:
set_multicycle_path -from [get_pins REGA/C] -to [get_pins REGB/D] -setup 3
set_multicycle_path -from [get_pins REGA/C] -to [get_pins REGB/D] -hold 2
3.6 最大最小延迟约束
最大延时约束(set_max_delay)将覆盖默认的建立时间分析的最大路径延时值。最小延时约束(set_min_delay)将覆盖默认的保持时间分析的最小路径延时值,set_max_delay 和 set_min_delay 通常不建议用于约束输入或输出引脚与内部寄存器对于一些异步信号之间的路径,通常建议使用之间(pin2reg 以及 reg2pin)的路径延时。而set max delay 和 set min delay 进行约束例如,对于设计中的某两个异步时钟域之间的数据通信已经使用双寄存器锁存等方式进行同步了,就可以施加 set false_path 或 setclock_groups 约束关闭某两个异步时钟域之司的数据路径检查。然而,设计者仍然期望查看并确认这两个时钟域之间的时序路径延时就可以使用 set max delay 和 set min delay进行约束(而不是使用 set_false_path 或 set_clock_groups 进行约束)
最大路径延时(maximum delay)约束命令格式如下:
set_max_delay <delay> -from <startpoints> -to <endpoints>
最小路径延时(minimum delay)约束命令格式如下:
set_min_delay <delay> -from <startpoints> -to <endpoints>
参考资料
《UG949》
《FPGA时序约束与分析——吴厚航》