梳理时钟树,基本时钟,衍生时钟
梳理时钟树
- 在进行所有时序约束操作的开始之前,我们有必要对项目工程的时钟树进行梳理。
何为时钟树
所谓时钟树,就是工程中用到了哪些时钟,各个时钟之间的关系又是什么样的。如果自己都没有把时钟关系理清楚,就难以去进行对应的时钟约束。
工程举例
- 以Vivado自带的wave_gen工程为例,该工程的各个模块功能较为明确,如下图所示。为了引入异步时钟域,我们在此程序上由增加了另一个时钟clkin2,该时钟产生脉冲信号pulse,samp_gen中在pulse为高时才产生信号。
- 在这个工程中,有两个主时钟,四个衍生时钟,如下图所示。
- 确定了主时钟和衍生时钟后,再看各个时钟是否有交互,即clka产生的数据是否在clkb的时钟域中被使用。这个工程比较简单,只有两组时钟之间有交互,即:
- clk_rx与clk_tx
- clk_samp与clk2
- 其中,clk_rx和clk_tx都是从同一个MMCM输出的,两个频率虽然不同,但他们却是同步的时钟,因此他们都是从同一个时钟分频得到(可以在Clock Wizard的Port Renaming中看到VCO Freq的大小),因此它们之间需要用set_false_path来约束;而clk_samp和clk2是两个异步时钟,需要用asynchronous来约束。具体的约束方法会在后续章节讲到。
- 完成以上两步,就可以进行具体的时钟约束操作了。
基本时钟(create_clock)
XDC约束代码
- 在Vivado中使用create_clock来创建时钟周期约束。使用方法为:
#-----使用< >标识的内容为填入值-----#
create_clock -name <name> -period <period> \
-waveform {<rise_time> <fall_time>} \
[get_ports <input_port>]
参数 | 含义 |
---|---|
-name | 时钟名称 |
-period | 时钟周期(ns) |
-waveform | 波形参数,上升/下降沿时刻(ns) |
-add | 在同一时刻源上定义多个时钟使用 |
- 需要注意,这里的时钟必须为主时钟(primary clock),主时钟通常有两种情形,一种是时钟由外部时钟源提供,通过时钟引脚进入FPGA,该时钟引脚绑定的时钟为主时钟;另一种是通过高速收发器(GT)的时钟RXOUTCLK或者TXOUTCLK。对于7系列FPGA,需要对GT的这两个时钟手工约束:对于UltraScale FPGA,只需对GT的输入时钟约束即可,Vivado会自动约束通过时钟引脚输入的时钟。
使用Tcl脚本主时钟
- 如何确定哪些时钟为系统主时钟,就成了时钟周期约束的关键,除了可以通过主时钟的两种情形进行人为判断之外,还可以使用Tcl脚本进行自动判断。
- 在“Tcl console”窗口中,输入指令
report_clock_networks -name mainclock
,会自动弹出“Clock Networks”窗口,在窗口中可以看到主时钟信息以及他们驱动的模块数量。 - 在“Tcl console”窗口中,输入指令
check_timing -override_defaults no_clock
,在当前窗口中,会显示该工程还未约束的主时钟名称。
多个主时钟约束举例
- 当系统中有多个主时钟,且这几个主时钟之间存在确定的相位关系时,需要用到
-waveform
参数。约束中的数字的单位默认是ns,若不写wavefrom参数,则默认是占空比为50%且第一个上升沿出现在0时刻。如果有如图所示的两个主时钟。
- 则始终约束如下所示。
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]
检查时钟约束的指令
- 在完成约束后,可使用
report_clocks
指令查看时钟约束是否生效。 - 对于输入的差分时钟,我们只对P端进行约束即可。如果同时约束了P端和N端,通过
report_clock_interaction
命令可以看到提示unsafe。这样既会增加内存开销,也会延长编译时间。
衍生时钟(create_group_clock)
XDC约束代码
#-----使用< >标识的内容为填入值-----#
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>]
#--- [<master_clock_source_pin_or_port>][<pin_or_port>]分别有以下两种写法 ---#
#--- [get_pins <module_name>/<pin_name>] ---#
#--- [get_ports <output_port>] ---#
参数 | 含义 |
---|---|
-name | 时钟名称 |
-source | 产生该时钟的源时钟 |
-multiply_by | 源时钟的倍频数 |
-divide_by | 源时钟的分频数 |
- 该指令用于约束我们在FPGA内部产生的衍生时钟,
-source
参数用于指定这个时钟是由哪里来的,,这个时钟叫做master clock
,即指上级时钟。该时钟不同于上文说到的primary clock
,它可以是源时钟primary clock
,也可以是其他的衍生时钟。该约束指令用于描述时钟电路如何对于上级时钟进行分频、倍频的转换。
#-----使用< >标识的内容为填入值-----#
create_generated_clock -name <generated_clock_name> \
-source [<master_clock_source_pin_or_port>] \
-edge {<1 2 3 >} \
-edge_shift {<2.5 0 2.5>} \
[<pin_or_port>]
#--- [<master_clock_source_pin_or_port>][<pin_or_port>]分别有以下两种写法 ---#
#--- [get_pins <module_name>/<pin_name>] ---#
#--- [get_ports <output_port>] ---#
参数 | 含义 |
---|---|
-name | 时钟名称 |
-source | 产生该时钟的源时钟 |
-edge | 定义衍生时钟的3个边沿分别参照主时钟的第几个边沿 |
-edge_shift | 生成衍生边沿相较参考边沿的偏移时间(ns) |
备注:
1."-edge_shift"的值可以为正值,也可以是负值。
2. "-edge"参数中,主时钟的第几个边沿在计数时,既包括上升沿也包括下降沿。如第2个边沿,指的是主时钟第1个周期的下降沿;第5个边沿,指的是主时钟第3个周期的上升沿。
3. "-edge","-edge_shift"参数中,3个边沿分别为,第1个周期上升沿,第1个周期下降沿,第2个周期上升沿。
- 其实不难看出,使用边沿"-edge"参数描述衍生时钟的上升沿和下降沿的描述方式,可以替代通过分频数"-divide_by"与倍频数"-multiply_by"参数描述方式。需要注意的是,当有"-divide_by"或"-multiply_by"选项时,不能使用"-edge_shift"选项。
衍生时钟约束举例
- 在第一个时钟约束中,主时钟源为基本时钟源点的输入端口(port)"clkin",对其进行2倍倍频和3倍分频后,从REGA模块的Q引脚(pin)输出。具体XDC约束代码与时钟波形如下所示。
create_clock -name clkin -period 10 [get_ports clkin]
create_generated_clock -name clk_mul2_div3 -source [get_ports clkin] \
-multiply_by 2 -divide_by 3 \
[get_pins REGA/Q]

- 在第二个时钟约束中,"clkin"为主时钟,其属性表示为周期为10ns,占空比为50%。该时钟通过连接mmcm模块,由该模块产生一个新的时钟,新时钟的属性表示为占空比为83.33%,即,,周期为原时钟的1.5倍,初始相移为60°。这个衍生时钟参照主时钟的第1,2,4个边沿进行定义,分别在主时钟源边沿的2.5ns,0ns,2.5ns后发生。具体XDC约束代码与时钟波形如下所示。
create_clock -name clkin -period 10 [get_ports clkin]
create_generated_clock -name clkshift -source [get_pins mmcm/CLKIN] \
-edges{1 2 4} -edge_shift{2.5 0 2.5} \
[get_pins mmcm0/CLKOUT]

自动推导的衍生时钟
- 衍生时钟除了通过自定义的方式生成外,还可通过Vivado自动推导的方式生成。如果在工程中使用了PLL或者MCMM模块,则Vivado会自动推导出一个约束。使用
report_clocks
指令,则可以看到自动生成的衍生时钟约束。 - 自动推导的好处在于当MMCM/PLL/BUFR的配置改变而影响到输出时钟的频率和相位时,用户无需改写约束,Vivado仍然可以自动推导出正确的频率/相位信息。劣势在于,用户并不清楚自动推导出的衍生钟的名字,当设计层次改变时,衍生钟的名字也有可能改变。但由于该衍生时钟的约束并非我们自定义的,因此可能会没有关注到它名字的改变,当我们使用者这些衍生时钟进行别的约束时,就会出现错误。
- 所以我们可以自己手动写出自动推导的衍生时钟的名字,也仅仅写出名字即可,其余的不写。还需注意的是,name和source推荐都按照vivado中生成的来,否则可能会被提示"critical warning"。具体书写如下所示。
create_generated_clock -name <generated_clock_name> \
-source <master_clock_source_pin_or_port>
参考:
- FPGA时序约束理论篇之时钟周期约束-Author:猫叔
- 《Xilinx FPGA权威设计指南--基于Vivado2018集成开发环境》,何宾,电子工业出版社