1. 时钟周期约束:对时钟的周期进行约束。
2. vivado中时钟约束指令:
使用create_clock来创建时钟周期约束,使用方法:
create_clock -name -period -waveform { } [get_ports ]
值得注意的是,这里的时钟必须是主时钟 primary clock。主时钟通常有两种情况:一种是由外部时钟源提供,另外一种是告诉收发器的时钟提供。
如上图所示的两个主时钟,进行如下约束:
create_clock -name clk0 -period 10.0 -waveform {0 5} [get_ports clk0]
create_clock -name clk1 -period 8.0 -waveform {2 8} [get_ports clk1]
数字单位默认是ns。如果不写waveform参数,则默认占空比是50%且第一个上升沿在0时刻。
约束在FPGA内部产生的衍生时钟,使用方法如下:
create_generated_clock -name \
-source \
-multiply_by \
-divide_by \
-master_clock \
使用方法:
set_clock_groups -asynchronous -group -group
set_clock_groups -physically_exclusive -group -group
第一种用法:来指定两个主时钟是异步关系,使用asynchronous。
create_clock -period 10 -name clk1 [get_ports clk1]
create_clock -period 8 -name clk2 [get_ports clk2]
set_clock_groups -asynchronous -group clk1 -group clk2
第二种用法:当我们需要验证同一个时钟端口在不同时钟频率下能否获得时序收敛时使用。 比如有两个异步主时钟clk1和clk2,需要验证在clk2频率为100MHz,clk1频率分别为50MHz、100MHz和200MHz下的时序收敛情况,我们就可以这样写。
create_clock -name clk1A -period 20.0 [get_ports clk1]
create_clock -name clk1B -period 10.0 [get_ports clk1] -add
create_clock -name clk1C -period 5.0 [get_ports clk1] -add
create_clock -name clk2 -period 10.0 [get_ports clk2]
set_clock_groups -physically_exclusive -group clk1A -group clk1B -group clk1C
set_clock_groups -asynchronous -group "clk1A clk1B clk1C" -group clk2
第三种用法:当我们使用BUFGMUX时,会有两个输入时钟,但只会有一个时钟被使用。 比如MMCM输入100MHz时钟,两个输出分别为50MHz和200MHz,这两个时钟进入了BUFGMUX。在这种情况下,我们需要设置的时序约束如下:
set_clock_groups -logically_exclusive \
-group [get_clocks -of [get_pins inst_mmcm/inst/mmcm_adv_inst/CLKOUT0]] \
-group [get_clocks -of [get_pins inst_mmcm/inst/mmcm_adv_inst/CLKOUT1]]
虚拟时钟通常用于设定对输入和输出的延迟约束,这个约束其实是属于IO约束中的延迟约束。虚拟时钟和前面讲的延迟约束的使用场景不太相同。顾名思义,虚拟时钟,就是没有与之绑定的物理管脚。
虚拟时钟主要用于以下三个场景:
外部IO的参考时钟并不是设计中的时钟
FPGA I/O路径参考时钟来源于内部衍生时钟,但与主时钟的频率关系并不是整数倍
针对I/O指定不同的jitter和latency
简而言之,之所以要创建虚拟时钟,对于输入来说,是因为输入到FPGA数据的捕获时钟是FPGA内部产生的,与主时钟频率不同;或者PCB上有Clock Buffer导致时钟延迟不同。对于输出来说,下游器件只接收到FPGA发送过去的数据,并没有随路时钟,用自己内部的时钟去捕获数据。
比如:如下图所示,在FPGA的A和B端口分别有两个输入,其中捕获A端口的时钟是主时钟,而捕获B端口的时钟是MMCM输出的衍生时钟,而且该衍生时钟与主时钟的频率不是整数倍关系。
这种情况下时序约束如下:
create_clock -name sysclk -period 10 [get_ports clkin]
create_clock -name virclk -period 6.4
set_input_delay 2 -clock sysclk [get_ports A]
set_input_delay 2 -clock virclk [get_ports B]
可以看到,创建虚拟时钟用的也是create_clock
约束,但后面并没有加get_ports
参数,因此被称为虚拟时钟。
再举个输出的例子,我们常用的UART和SPI,当FPGA通过串口向下游器件发送数据时,仅仅发过去了uart_tx这个数据,下游器件通过自己内部的时钟去捕获uart_tx上的数据,这就需要通过虚拟时钟来约束;而当FPGA通过SPI向下游器件发送数据时,会发送sclk/sda/csn三个信号,其中sclk就是sda的随路时钟,下游器件通过sclk去捕获sda的数据,而不是用自己内部的时钟,这是就不需要虚拟时钟,直接使用set_output_delay
即可。
注意,虚拟时钟必须在约束I/O延迟之前被定义。
asynchronous
或者false_path
。对于异步时钟,我们一般都会通过设计来保证时序能够收敛,而不是通过时序约束来保证。