数字 08 vivado的时序约束UI界面操作
实例1 ADC input_delay
现有一块ADC连接到FPGA上,需要在FPGA上实现高速数据的读取,那么第一步自然就是完成可靠的硬件连线,其中需要注意的是:
1. 注意信号的完整性,尽可能的避免边沿退化;这两区分两个概念:
i. 高速信号,指的是信号翻转,由高电平到低电平或者反之所耗得时间非常小;可能一个1MHz的TTL信号或者LVDS信号,只要边沿足够陡,那也算是高速信号!
ii. 高频信号,一般指的是周期性信号的周期时间足够小;
iii. 也就是说高速信号不一定是高频的;数字信号一般都是高速信号,所以必须要保证其边沿的完整性,如果边沿发生退化或者变形,那么将相当于加入了额外的时序上的偏差;
2. 保持时钟信号和数据信号路径等长,不管是单端还是差分信号,需要用绕线等形式迫使两类线几乎等长;这个在之前的时序分析中非常重要,如果真的不能够做到等长,甚至差的还挺多的,也必须提前获得该差值,并将则合成时间差,在后面的时序分析中将会有用;
保证以上两点后,就可以着手时序的分析了,那么首先我们会得到目标器件ADC的时序,如下图所示:
图 1 某ADC的时序图
从上图中我们首先得到几个信息:
1. 这是个同步时序,由两类信号组成,时钟信号DCO和数据信号FCO以及D(忽略它们是差分信号,下面的分析会把它画成单端信号,简化好画一点)
2. 这是个DDR信号,DDR指的是数据是同步于时钟信号的上升、下降边沿,所以该类同步信号需要引入虚拟时钟概念,后面继续介绍;
3. 信号是不是边沿对齐,而是偏移了90度!也就是说DCO边沿翻转后,数据信号没有第一时间翻转,而是延后了四分之一个周期,这是高速信号惯用的伎俩,后面会发现,这个延时让时序更好分析;
将FCO信号和D信号的实际意义扔一边,在设计者看来,他们都仅仅是数据而已,将上面的时序图简化成下图:
图 2 简化后的时序图
可以看到,实际上所有的数据信号都是同步于DCO的边沿,但是并不是对齐的,而是相差了90度,同时还是个DDR系统(上下边沿都是Launch Edge)
一般来说DDR系统会引入一个虚拟时钟的概念,就是说DCO是实际存在的时钟,设计和虚构出一个2倍频的DCOX2时钟,并将其相移180度以后,我们重新得到了下图:
图 3 引入虚拟时钟后的数据时序图
引入虚拟时钟后,我们重新规划Launch Edge,将其规划到DCOX2-Shift180的上升沿,其所对应的的Latch Edge仍然还是在DCO上。
到目前为止,我们已经很清楚的规划了ADC的时钟和数据输出的关系,至于如何用SDC语言描述,见下文;接下来就要考虑到这些信号实际上是各自经过PCB走线后来到FPGA的引脚,从FPGA引脚由进入到FPGA内部,然后又经过各自的FPGA内部走线延时以后来到了他们目标的寄存器,如下图所示:
图 4 傻瓜化后的ADC和FPGA的信号流向图
从上图可以获知:
1. ADC内部看起来有一个源时钟,这个源时钟我们不用管怎么产生,它分成了两路,其中一路经过倍频+移相后触发了ADC上的REG0(就是说其上升沿作为Launch Edge),另一路直接输出到ADC引脚DCO;
2. 数据由REG0产生后输出至ADC引脚D,经过一个延时后来到FPGA的相应输入引脚D`,与此同时,DCO引脚也经由PCB来到了FPGA的输入引脚DCO`;
3. 这两个信号进入FPGA后,都各自分成了两路,分别经过各自的延时来到其目标:
a) DCO`引脚输入后,进过TCLK2,来到了REG1的clk引脚
b) DCO`引脚输入后,进过TCLK3,来到了REG2的clk引脚
c) D`引脚输入后,进过Tdata2,来到了REG1的D引脚
d) D`引脚输入后,进过Tdata3,来到了REG2的D引脚
4. REG1和REG2有所区别,一个是上升沿触发,一个是下降沿触发(clk前加了个小圈圈),这是因为latch edge本来就是上升沿又有下降沿的;后期实际上也可以给DCO`也引入虚拟时钟,这里不表;
5. 不管是REG1还是REG2,想要锁存latch数据就必须满足建立时间和保持时间,这个在下文的图中也有所体现;
说了那么多,都不如实际的时序图来的实际,下面放图:
图 5 实际时序分析
上图由三个颜色的时序,分别是:
1. 紫色代表DAT,也就是数据到达时间,它由Tco(图中没有体现,可以参考手册)、Tdata1和Tdata2三者构成,是不是和我们的DAT定义不一样?公式是死的,只需要理解其意思就可以。和公式相比少了Tclk(源时钟到REG0的clk的延时),是因为我们不需要考虑这个延时,我们是根据ADC数据手册的时序图反推回里面的结构图,所以所有延时在反推的过程中已经都被体现或者被折合!
2. 绿色线和棕色线,表达出两个意思DRTsu和DRTh,分别代表数据建立所需时间和数据保持所需时间;
3. 将两者按照定义做减法,就能够得到建立时间裕量和保持时间裕量!
如上图所示,棕色线所划分的时间窗中,REG1.D已经是新的数据,而且在这个时间窗内并没有变化,所以就同时满足了建立时间裕量大于0和保持时间裕量大于0两个关系,这样的时序是稳定的!
但是这个只是图示而已,所有的Tdata1、2、3以及Tclk1、2、3都是我们目前假设的,在实际进行约束时,那些量时需要设计者提前设定,而那些量是自动生成的那?答案是:
1. Tdata1和Tclk1是由PCB实际布线所决定的,如果能够按照等长布线规则,就能够让两者相互抵消;
2. DCO和DCOX2-shift180的时序是由器件决定的;
3. Tdata2、3,Tclk2、3是FPGA在布线时自动产生的!
所以说这里我们只需要告诉FPGA,DCO`和D`之间的时序关系就可以了,要获得这两者之间的时序关系,我们就必须获得DCO和D之间的原始关系,以及他们是如何被布线延时变成DCO`和D`的;如何去描述上面所说的这种关系呢?利用SDC文件!
也就是说SDC文件就是要准确的告诉FPGA,所有输入(输出先不管)信号在进入FPGA时会是个什么样子,然后根据这个信息,FPGA会自动布线,使得REG1和REG2能够获得正确的数据;如果万一SDC文件所描述的时序关系非常的恶劣,将会导致不管FPGA怎么优化布线和布局,都不能够实现正确时时序时,就会输出报错,这个在以后的文章TimeQuest TA中会有详细的分析;那么接下来就开始写SDC文件吧;
#设置各种延时常数
#这里假设ADC片上的延时都为0
set ADC_CLKs_max 0
set ADC_CLKs_min 0
Set ADC_CLKd_max 0
set ADC_CLKd_min 0
# 同时根据ADC手册去设置Launch edge到有效数据之间的延时,这里假设他为X
set ADC_tCO_max X
set ADC_tCO_min X
#这里设置时钟信号和数据信号在PCB板上的延时差,即使是等长布线,我们也要可以给
#定两个值,这样可以给FPGA布线更多的压力,使得后期布线会往一个最理想的方向进行,
#分别是
set ADC_BD_min XX
set ADC_BD_max XX
#设置两个时钟,第一个时钟为DCO,它会从FPGA的DCO引脚输入
#另一个时钟是虚拟时钟,根据设置,它是DCO的两倍频,而且有180度的相移
#这两个时钟之间是同步的,一个是很是存在的,另一个是虚拟的!
create_clock -name DCO-period 5-waveform {1.25 3.75} [get_ports {DCO}]
create_clock -name DCO_virtual-period 2.5 -waveform {0 1.25}
#最后将所有的数据引脚同步到DCO_virtual的上升沿,根据上面的延时常数设置输入延时和输出延时,这条语句非常关键,它告诉FPGA所有的输入信号,在进入FPGA之前,相对于时钟存在怎么样的关系!
set_input_delay -clock DCO_virtual -max [expr $ADC_CLKs_max + $ADC_tCO_max + $ADC_BD_max - $ADC_CLKd_min] [get_ports {D*}]
set_input_delay -clock DCO_virtual -min [expr $ADC_CLKs_min + $ADC_tCO_min + $ADC_BD_min - $ADC_CLKd_max] [get_ports {}D*}]
SDC文件解释
1. 蓝色底部分代表定义一些延时参数,这些延时参数都是根据实际的PCB布线或者是ADC的书籍参数来设定的
2. 绿色底部分设定同步时钟,如果有必要的话还要设置虚拟时钟;
3. 紫色底部分将所有的输入信号同步到时钟,在这里这个时钟是虚拟时钟,因为我们假设虚拟时钟的上升沿是launch edge,这里其实可以也可以同步到DCO上,但就要设置下降沿同步,会显得比较麻烦;但是一样都是可以实现的!
通过上面的语句,FPGA就知道了,这些属于信号之间的关系:D和DCO之间的关系,D和DCOX2-shift180(就是DCO_Virtual)之间关系;
————————————————
版权声明:本文为****博主「禾刀围玉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.****.net/u012176730/article/details/54426491
实例2 wave_gen
主时钟
输入report_clock_networks -name main
查看主时钟约束情况
其中 clk_pin_p是mmcm输出的自动约束了,我们主需要约束clk_in2
在tcl中键入
create_clock -name clk2 -period 25 [get_ports clk_in2]
所以 主时钟的约束指令是
Create_clock –name 时钟名字 –period 周期 时钟自哪个点输出
主时钟约束完成
衍生时钟
分析一共有四个衍生时钟,但是其中有两个是mmcm组合出来的,不用我们手动约束,我们需要手动约束的是clk_samp和spiclk
Clk_samp在模块clk_gen_i0这个模块里面,
在clk_gen.v中有它的连接方式,是从BUFHCE_clk_samp_i0/O端口输出的一个时钟
create_generated_clock -name clk_samp -source [get_pins clk_gen_i0/clk_core_i0/clk_tx] -divide_by 32 [get_pins clk_gen_i0/BUFHCE_clk_samp_i0/O]
create_generated_clock -name spi_clk -source [get_pins dac_spi_i0/out_ddr_flop_spi_clk_i0/ODDR_inst/C] -divide_by 1 -invert [get_ports spi_clk_pin]
所以 衍生时钟的约束指令是
Create_generated_clock –name 时钟名字 –source 时钟的来源 –divide_by xx 时钟自哪个点输出(port或者pin)
至此,衍生时钟创建完毕
如果需要改衍生时钟的名字,它的格式是
Create_generated_clock –name 时钟名字 时钟源 时钟输出点
延迟约束
延迟约束主要根据以下表格进行
对于输入管脚,首先判断捕获时钟是主时钟还是衍生时钟,如果是主时钟,直接用set_input_delay即可,如果是衍生时钟,要先创建虚拟时钟,然后再设置delay。对于输出管脚,判断有没有输出随路时钟,若有,则直接使用set_output_delay,若没有,则需要创建虚拟时钟。
Rxd_pin只跟随主时钟,直接用set input_delay就可以
set_input_delay -clock [get_clocks -of_objects [get_ports clk_pin_p]] 0.000 [get_ports rxd_pin]
set_input_delay -clock [get_clocks -of_objects [get_ports clk_pin_p]] -min -0.500 [get_ports rxd_pin]
对于txd_pin和lb_sel_pin需要创建虚拟时钟
create_clock -period 6.000 -name virtual_clock
set_input_delay -clock virtual_clock -max 0.000 [get_ports lb_sel_pin]
set_input_delay -clock virtual_clock -min -0.500 [get_ports lb_sel_pin]
set_output_delay -clock virtual_clock -max 0.000 [get_ports {txd_pin {led_pins[*]}}]
set_output_delay -clock virtual_clock -min -0.500 [get_ports {txd_pin {led_pins[*]}}]
对于spi_mosi_pin,不需要虚拟时钟,直接用set_output_delay即可
set_output_delay -clock spi_clk -max 1.000 [get_ports {spi_mosi_pin dac_cs_n_pin dac_clr_n_pin}]
set_output_delay -clock spi_clk -min -1.000 [get_ports {spi_mosi_pin dac_cs_n_pin dac_clr_n_pin}]
dac_*和led_pins没有进行约束
多周期路径约束
多周期路径,我们一般按照以下4个步骤来约束:
1 带有使能的数据
首先来看带有使能的数据,在本工程中的Tming Report中,也提示了同一个时钟域之间的几个路径建立时间不满足要求
2 两个有数据交互的时钟之间存在相位差
3 存在快时钟到慢时钟的路径
4 存在慢时钟到快时钟的路径
实例3 spi
进行约束的是这个工程
约束前的时序情况是:
看到 setup slack不足
工程情况
在tcl命令框里面输入report_clock networks -name mainclock
出现这样的情况
其中
主时钟clk156p已经被约束
查看时序问题报告
时序问题主要发生在inter_clock paths也就是时钟之间的路径上
点进去时序约束的界面,看见slack的实际情况
如上图所示,我为30m到10m时钟进行了一个多周期路径约束之后,30m到10m的时序就满足要求了,所以这设计的主要时序问题是多时钟之间的路径问题。
这两个信号按照频率来说都是uart时钟的1/176,也就是153.6k/176=0.87KHz,周期是1145760
clkout是从pll出来的40m时钟又分频之后的时钟,频率是153.6KHz 周期是6510ns
workclk是spi工作时钟,最高频率40M 周期是25ns 是从pll出来之后又分频的
这几个时钟都是衍生时钟
先对rdidle进行约束
它的源是clkout,
源的点是在 uart_top/div/clkout_reg/Q
然后是自己输出的点uart_top/rdidle_reg/Q
所以最后的约束时这样子的
约束完了之后,用tcl命令report_clocks看一下约束情况
然后时rdstart
我约束完rdidle之后rdstart就不见了,不知道为啥?
约束clkout
他的源头时40M时钟
继续 workclk
/*
**=============================================================
**
** 这部分是已经被约束的引脚和时钟,我将在此基础上进行进一步的
** 时序约束
**
**=============================================================
*/
set_property IOSTANDARD LVCMOS33 [get_ports I_clk]
set_property IOSTANDARD LVCMOS33 [get_ports I_rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports I_spi_miso]
set_property IOSTANDARD LVCMOS33 [get_ports O_spi_cs]
set_property IOSTANDARD LVCMOS33 [get_ports O_spi_mosi]
set_property IOSTANDARD LVCMOS33 [get_ports O_spi_sck]
set_property PACKAGE_PIN K28 [get_ports I_clk]
set_property PACKAGE_PIN Y29 [get_ports I_rst_n]
set_property PACKAGE_PIN AA27 [get_ports I_spi_miso]
set_property PACKAGE_PIN AA25 [get_ports O_spi_cs]
set_property PACKAGE_PIN AB28 [get_ports O_spi_mosi]
set_property PACKAGE_PIN AB25 [get_ports O_spi_sck]
set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]
set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]
set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]
connect_debug_port dbg_hub/clk [get_nets I_clk_IBUF_BUFG]
set_property PACKAGE_PIN K28 [get_ports I_clk_p]
set_property IOSTANDARD LVDS_25 [get_ports I_clk_p]
set_property PACKAGE_PIN W29 [get_ports addra]
set_property PACKAGE_PIN AA28 [get_ports addrb]
set_property IOSTANDARD LVCMOS33 [get_ports addra]
set_property IOSTANDARD LVCMOS33 [get_ports addrb]
set_property IOSTANDARD LVDS_25 [get_ports clk156p]
set_property PACKAGE_PIN K28 [get_ports clk156p]
set_property PACKAGE_PIN M19 [get_ports rd]
set_property IOSTANDARD LVCMOS33 [get_ports tx]
set_property PACKAGE_PIN K24 [get_ports tx]
set_property PACKAGE_PIN Y29 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rd]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
/*
**=============================================================
**
** 这部分是后来进行的时序约束
**
**=============================================================
*/
//约束rdidle信号
create_generated_clock -name rdidle -source [get_pins uart_top/div/clkout_reg/Q] -divide_by 176 [get_pins uart_top/rdidle_reg/Q]
//约束clkout
create_generated_clock -name clkout -source [get_pins spitop/clkpll/inst/plle2_adv_inst/CLKOUT3] -divide_by 260 [get_pins uart_top/div/clkout_reg/Q]
//约束workclk
create_generated_clock -name workclk -source [get_pins spitop/clkpll/inst/plle2_adv_inst/CLKOUT3] -divide_by 1 [get_pins spitop/divspi/workclk_reg/Q]
set_multicycle_path 3 -setup -start -from [get_clocks clk30m_clk_wiz_0] -to [get_clocks clk10m_clk_wiz_0]
set_multicycle_path 2 -hold -from [get_clocks clk30m_clk_wiz_0] -to [get_clocks clk10m_clk_wiz_0]
分析set_multicycle_path
这条路径的出错原因是,数据从div_counert_reg[21]/C出发,到div_counter_reg[5]/R端口,这条路径上的总延迟有4.107ns,有点大,然后导致setup slack小于0,并且,这条路径实际上是从10m时钟下的触发器到20m时钟下的触发器,是一个跨时钟域的路径。
我想要的效果是,让建立时间裕量更加宽裕一些。
我认为有几种方法:
·减少这条路径上组合逻辑的延迟。
·使用set_multicycle_path进行多周期约束
·直接去除掉了这个选择时钟的功能
我对inclk设置了一个40m的约束,它说我的余量是22ns左右
所以是我对inclk没有设置约束的原因,导致软件并不知道inclk到底是多少频率。所以说我的建立时间余量不够,实际上是完全够的。
实例4 分析500m时的时序情况(250m时未出现时序问题)
PLL自动生成的时钟约束
按照名词顺序开始分析,
首先,出现时序约束的路径是path21。
时序裕量是-1.46ns,也就是说不符合时序要求。
路径源是divnumtemp_reg Q端
路径目的地是div_counter_reg R端 这两个端口都是时钟500m驱动
路径组是500m
路径类型是建立 ()
时序要求是2ns 也就是500m的rise到rise,也就是一个周期
数据路径延迟是2.99ns 其中logic延迟0.857,路径延迟是2.133ns
路径上的逻辑块有6个 3个carry4超前进位加法器 3个LUT查找表
时钟偏斜-0.021ns
时序不确定度,可以用于附加时序余量 margin
实例5 利用vivado的UI时序约束向导进行约束
在什么都没有做的时候,时序约束UI界面里面是这样的
里面有一个主时钟clk156p,一个input jitter ,都是关于PLL的输入主时钟的,既然他已经做好了,而且我也明确的知道,这是一个port进来的主时钟,并且这个时钟是锁定住的,时钟约束的开头有个锁,所以我不能再对他进行操作。
所以我要在这基础上,进行我的时钟约束。
首先梳理我的时钟树
其实很简单,我需要约束的时钟是4个
1、主时钟clk156p
2、衍生时钟clk500m
3、衍生时钟clk40m
4、workclk是clk500m的分频
其中,1已经做好了,现在开始做第二个
点击+号
取个名字,然后点击右边的...
查找他的上级时钟
点击箭头放到右边 然后ok
然后是master clock(这里应该理解成上级时钟)
是主时钟,写好如下
这里有个问题,衍生时钟只能是主时钟的整数倍频或者分频
然后是source objects
这个是本时钟的路径,也就是clk500m的路径
点击-add
这样,他就约束好了,但是我们的500m并不是主时钟的整数倍频或者分频,所以这个还需要进一步探讨