Vivado使⽤技巧(15):时钟的约束⽅法
时钟的基础知识
数字设计中,“时钟”表⽰在寄存器之间可靠地传输数据所需的参考时间;Vivado的时序引擎利⽤时钟特征来计算时序路径需求,通过计算时间裕量(Slack)的⽅法报告设计的时序空余;时钟必须被正确定义以最佳精度获得最⼤的时序路径覆盖范围,包含如下特性:
定义在时钟树的驱动管脚或端⼝,通常称作根或源点;generated
通过周期和波形属性来描述时钟边沿;
周期(period)以ns为单位进⾏设定,与波形重复率相关;
波形(waveform)以列表的形式给出,表中包含上升沿和下降沿在周期中的绝对时间,以ns为单位;第⼀个上升沿对应于第⼀个值,第⼀个下降沿对应第⼆个值;默认情况下,相位偏移从0ns开始,占空⽐为50%;
如下图给出了两个时钟Clk0: period=10, waveform={0 5}、Clk1: period=8, waveform = {2 8};
上述给出的只是时钟的理想特征。当时钟进⼊了FPGA器件,通过时钟树传递时,时钟边沿会有延时,通常称作时钟⽹络延迟;噪声或硬件表现会导致时钟随时可能发⽣变化,通常称作时钟不确定性,包括时钟抖动(Clock jitter)、相位错位(Phase error)等等, Vivado在时序分析时会考虑这些⾮理想因素以得到精确的时序裕量;
通常板⼦上有⼀个外部组件(如有源晶振)产⽣时钟信号,通过输⼊端⼝进⼊器件内部;外部时钟可以通过MMCM、PLL、BUFR等特殊原语⽣成其它时钟,也可以由LUT、寄存器等常规单元进⾏转换(通常称作门控时钟);接下来的部分将讲述如何由源时钟定义成其他类型的时钟;
主时钟Primary Clock
主时钟通常由两个来源:(1)板级时钟通过输⼊端⼝进⼊设计;(2)GT收发器的输出管脚(如恢复时钟);
主时钟必须与⼀个对象相连,该对象代表了所有时钟边沿的开始点,并且在时钟树中向下传递;也可以说,主时钟的源点定义了0时
刻,Vivado靠此来计算时钟延迟和不确定性 ;vivado会忽略所有时钟树上从主时钟上游的单元到主时钟之间的延时!
主时钟只能通过create_clock命令来定义,且必须放在约束的开始,这是因为其它时序约束⼏乎都要参考主时钟;下⾯给出⼏个主时钟的例⼦,第⼀个例⼦如下图所⽰,采⽤单端时钟输⼊:
板级时钟通过sysclk端⼝进⼊FPGA,通过⼀个输⼊和⼀个时钟缓冲器后到达寄存器,使⽤如下命令定义:
create_clock -period 10 [get_ports sysclk]  #10ns周期,50%占空⽐,⽆相移
create_clock -name devclk -period 10 -wavefor {2.5 5} [get_ports sysclk]  #板级时钟名称devclk,10ns周期,25%占空⽐,90°相移
第⼆个例⼦,时钟源由⾼速收发器gt0提供,如下图所⽰:
通过sysclk端⼝进⼊FPGA,之后经过混合时钟管理单元MMCM⽣成其他时钟,以gt0发出的时钟为主时钟,其他⽣成时钟都有⼀个共同的时钟源,使⽤如下命令定义:
create_clock -name rxclk -period 3.33 [get_pins gt0/RXOUTCLK]
第三个例⼦如下图所⽰,采⽤差分时钟输⼊,这也是⾼速时钟的输⼊⽅式:
上图中差分时钟驱动⼀个PLL,定义主时钟时必须只创建差分缓冲器的正极输⼊;如果同时创建了正极、负极输⼊,将会导致错误的CDC路径;使⽤如下命令定义:
create_clock -name sysclk -period 3.33 [get_ports SYS_CLK_clk_p]
虚拟时钟Virtual Clock
虚拟时钟通常⽤于设定对输⼊和输出的延迟约束,之所以称为“虚拟”,是因为这种时钟在物理上没有与设计中的任何⽹表对象相连;定义时使⽤create_clock命令,但⽆需指定源对象 ,在下列情况需要⽤到虚拟时钟:
所有的设计时钟都不是外部器件I/O的参考时钟;
FPGA的I/O路径与⼀个内部⽣成的时钟相关,但是该时钟不能合适地通过对板级时钟计时来⽣成(如两个周期的⽐不是整数);
希望为与I/O延迟约束相关的时钟设定不同的抖动和延迟,但是不希望修改内部时钟的特征 ;
⽐如时钟clk_virt的周期为10ns,且不与任何⽹表对象相连,可以这样定义“create_clock -name clk_virt –period 10”,没有指定objects参数;注意,虚拟时钟必须在使⽤之前便定义好 ;
⽣成时钟Generated Clock
⽣成时钟是指在设计内部由特殊单元(如MMCM、PLL)或⽤户逻辑驱动的时钟;⽣成时钟与⼀个上级时钟(注:官⽅称作master clock,为与primary clock作区分,这⾥称作上级时钟)相关,其属性也是直接由上级时钟派⽣⽽来;上级时钟可以是⼀个主时钟,也可以是另⼀个⽣成时钟;
⽣成时钟使⽤create_generated_clock命令定义,该命令不是设定周期或波形,⽽是描述时钟电路如何对上级时钟进⾏转换。这种转换可以是下⾯的关系:
Vivado计算⽣成时钟的延迟时,会追踪⽣成时钟的源管脚与上级时钟的源管脚之间的所有组合和时序路径。某些情况下可能只希望考虑组合逻辑路径,在命令⾏后添加-combinational选项即可;
这⾥先解释⼀下本⽂甚⾄本系列⼤量使⽤的两个词,端⼝(Port)和管脚(Pin)。端⼝通常⽤get_ports命令获取,管脚使⽤get_pins命令获取。⼆者的含义是不同的,但管脚的范围更⼴泛,⽐如设计中⽤到的⼀个寄存器都有3个管脚:clk、D和Q;下⾯给出⼏个定义⽣成时钟的例⼦:
简单的频率分频
简单的频率倍频
频率倍频与分频的组合,获得⼀个⾮整数的⽐例,通常由MMCM或PLL完成
相移或波形反相
占空⽐改变
上述所有关系的组合
1.简单的2分频
可以采⽤如下两种⽅法对⽣成时钟进⾏约束:
#定义主时钟,周期10ns,50%占空⽐
create_clock -name clkin -period 10 [get_ports clkin]
#约束⽅法1,主时钟作为源点
create_generated_clock -name clkdiv2 -source [get_ports clkin] -divide_by 2 [get_pins REGA/Q]
#约束⽅法2,REGA的始终管脚作为源点
create_generated_clock -name clkdiv2 -source [get_pins REGA/C] -divide_by 2 [get_pins REGA/Q]
约束命令中使⽤**-source选项来设定上级时钟,但如上所⽰,该选项只能设定为⼀个端⼝或管脚类型的⽹表对象,不能直接设置为时钟类型对象。上⾯约束使⽤-divide_by选项设置分频系数,此外还可以使⽤-edges**选项,如下所⽰:
#该约束与上⾯等效
create_generated_clock -name clkdiv2 -source [get_pins REGA/C] -eedges {1 3 5} [get_pins REGA/Q]
#1 3 5 分别为⽣成时钟⼀个周期的三个沿对应master clock的上升沿的位置
-
edges的参数为⼀个列表,该列表通过主时钟的边沿来描述⽣成时钟的波形;列表中的值为主时钟边沿的序号(注意观察上图),由时钟上升沿开始,定义了⽣成时钟边沿的时间点;
2.改变占空⽐与相移
如果仅需要改变时钟的相移,使⽤**-edge_shift**选项可以正向或反向设定每⼀个⽣成时钟波形的相移量;注意,-edge_shift选项不能与-devide_by、-multiply_by、-invert选项同时使⽤;下图中上级时钟为clkin,进⼊mmcm0单元,产⽣⼀个25%占空⽐、相移90°的时钟:
可以采⽤如下⽅法对⽣成时钟进⾏约束:使⽤上级时钟的1、2、3标号边沿(即0ns、5ns、10ns)定义⽣成时钟,为了得到预期波形,1和3标号边沿要分别移动2.5ns,得到2.5ns、5ns、12.5ns的波形:
#定义主时钟,周期10ns,50%占空⽐
create_clock -name clkin -period 10 [get_ports clkin]
#定义⽣成时钟,周期10ns,25%占空⽐,90°相移
create_generated_clock -name clkshift -source [get_pins mmcm0/CLKIN] -edges {1 2 3} \
-edge_shift {2.5 0 2.5} [get_pins mmcm0/CLKOUT]
3.同时倍频与分频
这种情况通常⽤于定义MMCM或PLL的输出,⼀般使⽤这些IP核时会⾃动创建相应约束;考虑上例中的图,假设MMCM将上级时钟倍频到4/3倍,⽆法直接倍频,需要同时使⽤-divede_by和-multiply_by选项来实现:
create_generated_clock -name clk43 -source [get_pins mmcm0/CLKIN] -multiply_by 4 \
-divide_by 3 [get_pins mmcm0/CLKOUT]
4.仅通过组合路径追踪上级时钟
前⾯简单介绍了-combinational选项的使⽤,为了更好理解,这⾥举⼀个具体例⼦;下图中,上级时钟
同时传递到寄存器和多路选择器中,寄存器对时钟进⾏2分频,多路选择器从寄存器的2分频时钟和上级时钟中选择⼀个作为⽣成时钟输出 :
,从上级时钟到⽣成时钟有两条路径,⼀条为时序路径,⼀条为组合路径;如果我们只希望考虑组合路径上的延迟时,定义⽣成时钟时就需要使⽤-combinational选项 :
create_generated_clock -name clkout -source [get_pins mmcm0/CLKIN] -combinational
[get_pins MUX/O]
⾃动⽣成时钟 Automatically Derived Clocks
这种类型时钟算是⽣成时钟的⼀种特例,“⾃动”是指在已经定义了上级时钟的情况下,Vivado会⾃动为时钟管理单元CMBs(Clock Modifying Blocks)的输出管脚创建约束,官⽅称作Automatically Derived Clocks或Auto-generated Clock;
如果约束中已经存在⽤户在某⼀⽹表对象上定义的时钟,则不会创建相同对象上的⾃动⽣成时钟;
下⾯给出⼀个具体例⼦,下图中上级时钟clkin驱动clkip/mmcm0单元的CLKIN输⼊,该单元是⼀个MMCME2资源的实例,则⾃动⽣成时
钟的定义源点为clkip/mmcm0/CLKOUT,顶层与此源点连接的⽹络名为clkip/cpuClk,⾃动⽣成时钟的名字便是cpuClk:
如上所述,Vivado会⾃动创建⾃动⽣成时钟的名称(Name),如果两个名称发⽣冲突也会⾃动添加后缀,如usrclk、usrclk_1等等;
时钟组Clock Group
默认情况下,Vivado会测量设计中所有时钟之间的路径时序,添加如下两种约束可以控制该功能:
划分时钟组通常有两个依据:
(1)原理图或时钟⽹络报告中的时钟树拓扑图,判断哪些时钟不应该放在⼀起做时序分析;
(2)时钟交互报告查看两个时钟间存在的约束,判断它们是否有共享的主时钟(代表是否有已知的相位关系)或者是否有公共周期 ;
但要明⽩,我们设定时钟组的⽬的还是为了保证设计在硬件中能正常⼯作,因此我们必须确保这些忽略了时序分析的路径有合适的再同步电
路或异步数据传输协议;
set_clock_groups:建⽴时钟组,Vivado不会对不同时钟组的时钟之间进⾏时序分析;
set_false_path:将两个时钟之间的路径设置为false path后,不会对该路径进⾏任何时序分析;
根据时钟间的关系,可以做如下分类:
同步时钟:即两个时钟间有可预知的相对相位,通常它们的时钟树源⾃⽹表中的同⼀个根,且有⼀个公共周期;
异步时钟:两个时钟间有⽆法预知的相对相位。⽐如两个独⽴的晶振信号通过两个输⼊端⼝进⼊FPGA中,⽣成两个时钟。由于两个主时钟没有明确的相位关系,两个⽣成时钟间便是异步的;
不可扩展时钟:官⽅称作Unexpandable Clocks,是指时序引擎在1000个周期内⽆法判断两个时钟是否有公共周期。这种情况通常发⽣在两个时钟周期⽐是⼀个特殊的分数,⽐如⼀个主时钟通过MMCM⽣成⼀个周期为5.125ns的时钟clk1和⼀个周期为6.666ns的时钟clk2,尽管它们在时钟树的根上有⼀个确定的相位关系,但是在1000个周期内时钟上升沿⽆法再次对齐;
1.异步时钟组
同步时钟可以安全地进⾏时序分析,异步时钟和不可扩展时钟虽然通过时序分析也会得到⼀个裕量值,但这个值不可作为可靠结果;从这个
⾓度出发,不可扩展时钟也可以视作⼀种特殊的异步时钟;这就需要通过设置时钟组来忽略异步时钟的时序路径上的时序分析 ;
这⾥举个例⼦,⼀个主时钟clk0通过MMCM⽣成两个时钟usrclk和itfclk;另⼀个主时钟clk1通过另⼀个MMCM⽣成两个时钟clkrx和
clktx,⽤如下命令创建异步时钟组:
set_clock_groups -name async_clk0_clk1 -asynchronous -group {clk0 usrclk itfclk} \
-group {clk1 gtclkrx gtclktx}
#如果时钟名称事先不知道,可以⽤如下写法
set_clock_groups -name async_clk0_clk1 -asynchronous -group [get_clocks -include_generated_clocks clk0] -group [get_clocks -include_generated_clocks clk1] 2.互斥时钟组 Exclusive Clock Groups