数字 03 时序约束

参考bilibili网址:https://www.bilibili.com/video/BV1ME41127So?p=4

参考CSDN网址:https://blog.csdn.net/zhanghaijun2013/article/details/104105707

参看知乎网址:https://zhuanlan.zhihu.com/p/37068922

 

 

目录

1、时序约束含义

1.1 启动沿(launch)和锁存沿(latch):

1.2 建立(setup)和保持(hold)时间

1.3 数据到达时间(DAT:data arrival time)

1.4 数据建立需要时间(DRTsu:data require time (setup))

1.5 数据保持需要时间(DRTh:data require time (hold))

1.6 建立时间裕量(Setup Slack)

1.7 保持时间裕量(hold slack)

2、Tco和Tsu和Th是什么?和什么有关?

3、时序约束的作用

4、如何查看时钟周期约束状态

5、如何查看时钟约束是否生效

6、clock_group的用法

7、开始进行时序约束

8、实例  利用vivado的UI时序约束向导进行约束


 


1、时序约束含义

1.1 启动沿(launch)和锁存沿(latch):

https://img-blog.csdn.net/20170113220035561?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE3NjczMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

启动沿:数据被launch的时钟边沿;也就是说,每一个启动沿,一般都会产生一个新的数据!

 

锁存沿:数据被latch的时钟边沿;也就是说,每一个锁存沿,都会有一个新的数据被保存!

 

对于如上图所示的级联的寄存器电路而言(忽略上一级触发器输出到下一级触发器输入之间的逻辑关系),一般蓝色的上升沿作为第一个触发器的启动沿,而红色的上升沿(一般都是紧跟着启动沿后的有效边沿)。所以说:蓝色的启动沿之后由REG1输出有效数据,并在红色的边沿被锁存进REG2并输出到下级。

1.2 建立(setup)和保持(hold)时间

https://img-blog.csdn.net/20170113220043913?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE3NjczMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

上述的锁存沿会将数据保存下来,但是必须要满足一定的条件:

建立时间Tsu:在时钟有效沿之前,数据必须保持稳定的最小时间;

保持时间Th:在时钟有效沿之后,数据必须保持稳定的最小时间;

这就相当于一个窗口时间,在有效边沿的窗口时间内,数据必须保持稳定;这里的时钟信号时序和数据信号时序,都是寄存器实际感受到的时序;什么是实际感受到的,下面会继续分析;

1.3 数据到达时间(DAT:data arrival time)

所有的信号在FPGA内部传播都会有延时,包括时钟信号、数据信号(实际上不该如此可以的区分这两种信号,在一定条件下,这两个信号可能可以相互转换,这个是题外话了),也就是说,从信号的发出点到信号的接收点,会存在一个延时,这个延时可能是因为FPGA内部空间分布所致,也有可能是因为组合逻辑造成,这里不再深究;

https://img-blog.csdn.net/20170113220052062?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE3NjczMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

正如上图所示,在计算数据到达时间时,一般都会存在3个延时:

 

1. Tclk1:时钟信号从起点(一般是PLL输出或者时钟输入引脚)到达启动寄存器(或说启动触发器)的相应clk端口所耗的时间;假如CLK是由PLL发出的时钟信号(称之为源时钟),这个信号经过FPGA内部的“连线”最终来到了REG1(启动触发器)的clk端,所以此时在REG的clk1处也会有周期性的时钟信号REG1.CLK(如图所示);可以看到,此时的CLK(源时钟)和REG1.CLK实际上有个(相位差)时间差,这个时间差就是Tclk1;

 

2. Tco:启动寄存器内部延时,是寄存器REG1感受到有效的上升沿后,到数据真正从从REG1的Q输出之间延时;请注意:上述所谓的有效的上升沿,就是REG1.CLK,而不是CLK;所以实际的有效数据输出的时序如上图的REG1.Q;

 

3. Tdata:数据从上级寄存器输出Q(经过所有其他组合逻辑以及FPGA内部走线)到下级寄存器的数据输入D之间的延时;如图所示,数据从从REG1的Q流向了REG2的D,所以REG2.D实际感受到的数据时序是REG2.D;

 

假如将上图中的launch Edge作为时间0点,将一些列的延时累加,所得结果称之为数据到达时间,DAT:

 

DAT=启动沿+Tclk1+Tco+Tdata;

1.4 数据建立需要时间(DRTsu:data require time (setup))

https://img-blog.csdn.net/20170113220101069?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE3NjczMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

分析方法同上,需要计算数据建立时间,也需要两个延时:

 

1. Tclk2,不同于上述的Tclk1,这个延时是时钟从起点(一般是PLL或者时钟输入引脚)到锁存触发器之间的延时;如上图所示,REG2实际感受到的时钟来自于其本身的clk引脚,而不是源时钟CLK,他们之间存在一个延时,即Tclk2;所以REG2实际感受到的时钟,实际上是上图的REG2.CLK;

 

2. Tsu:上面分析过了,每一个数据被锁存都要满足建立时间和保持时间,Tsu就是建立时间,也就是在REG2实际感受到Latch Edge时,数据如果需要被正确锁存,就必须提前Tsu的时间来到REG2的D;

 

综合时钟走线延时Tclk2以及Tsu,我们得到了数据建立时间DRTsu:

 

DRTsu=锁存沿+Tclk2-Tsu。

 

也就是说在DRTsu时刻之前,数据必须已经有效且稳定

1.5 数据保持需要时间(DRTh:data require time (hold))

https://img-blog.csdn.net/20170113220109850?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE3NjczMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

即DRTh=锁存沿+Tclk2+Th;

 

也就是说,数据在DRTh时间之前必须保持住不变;

 

1.6 建立时间裕量(Setup Slack)

https://img-blog.csdn.net/20170113220116328?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE3NjczMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

如上图所示,在0时刻(Launch edge),源时钟CLK说,“啊,我要产生一个新的数据”,但是这个命令(启动沿)并没有马上传达到REG1,而是有个延时Tclk1。所以在Tclk1时刻,REG1终于听到了(感受到了有效的时钟上升沿,就是启动沿)老大的命令,就开始准备,他憋了一会儿,又延时了Tco,终于产生了数据(REG1.Q上有了valid data);这个有效数据也是慢吞吞的来到了他的终点,又浪费了Tdata。最终在Tclk1+Tco+Tdata时间后,REG2得到了这个数据;别以为REG2得到这个数据就完事了,REG2也是个傲娇的娃,怎么个傲娇法,下面继续分析;老大CLK在0时刻发送了启动沿之后,休息了一个时钟周期,在Latch edge时突然想起来,刚才让REG1发出的数据,REG2要接受啊,不然就浪费了,于是乎他又对REG2下达了命令,新数据要来了,准备好接受!但是这个命令也不是马上就到了REG2的耳中,而是经过了Tclk2的时间。等到REG2接收到命令后(实际感受到了有效的Latch Edge),他就看看自己家门口没有数据已经来了(检查REG2.D是否有数据),同时要看看这个数据是不是符合他的胃口的(满足锁存的条件),他要求数据必须在他接收到老大命令的时候已经等了Tsu时间(数据建立时间),又要求这个数据在他家门口不能早退,必要再保持Th时间(数据保持),如果都满足了,REG2就开心的接受了这个数据,反之,他就会觉得,REG1准备的数据太懒惰了(没有提前Tsu时间到达),又或者性子太急(没有多逗留th),一概不收!

 

所以这里涉及到两个要求,第一个就是建立时间裕量:

 

正如上图所示Setup Slack=DRTsu-DAT。

 

如果Setup Slack为正,则说明数据在规定的时间内达到了目标。反之,则认为数据并没有在规定的时间达到目标,此时REG2锁存的数据很有可能是亚稳态;

1.7 保持时间裕量(hold slack)

https://img-blog.csdn.net/20170113220122882?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE3NjczMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

如上所述,hold slack = DAT – DRTh

 

如果为正,则认为数据在被锁存的时候有足够多的稳定时间,是有效的。反之则认为数据有误或者数据可能是亚稳态;

 

 

 

小结:

 

理解了上面的7个概念,就明白了:如果时钟频率过快或者数据延时太大,都会导致错误的时序。在FPGA内部寄存器到内部寄存器之间,所有的延时都是建立在时序模型上的,如slow和fast,这些模型从两个极端工作情况来分析FPGA能否正常工作;只要满足这两个工况,则FPGA在其他环境下都能满足时序!

 

2、Tco和Tsu和Th是什么?和什么有关?

1、Tco是触发器采样到数据之后,数据到Q端的时间,也就是说,触发器已经采样到数据了,已经是经过了建立时间之后的事情,Tco是触发器内部的,固定的时间,一般来说非常地短。

但是在下面这个图的Tco,实际上是clk触发之后到Q端输出的时间,这段时间一定大于保持时间,但是不可确定。

在这个图里,我们实际上可以改变的是Tdata,其他时间都是硬件上固定的,只要告诉硬件是怎么一个情况就可以了。

改变Tdata可以尝试以下方法:

1. 首先是修改代码,找到关键路径上的源寄存器和目的寄存器,拆分它们之间的组合逻辑。插入寄存器是最简单粗暴的办法,实际上在设计之初,如果我们精心设计的话,是可以在算法级对组合逻辑进行分解的,好的时序是设计出来的,不是约束出来的;

2. 如果修改代码实在解决不了,在使用约束关键路径的办法;

3. 应该还有其他很多办法,不过我暂时还不知道;

如果上面都解决不了,我们还可以:

4. 买更好更快的芯片,更好的芯片意味着更低的Tsu要求,更小的Tpd、Tcomb;

5. 降低工作频率,即提高时钟周期。

————————————————

版权声明:本文为CSDN博主「qq_16923717」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_16923717/article/details/82354036

 

建立时间和保持时间的定义

下面我们通过分析D触发器来理解建立时间和保持时间。

如果想深入理解一个概念,那么我们一定要知道这个概念的清晰的定义。

建立时间和保持时间都是针对触发器的特性说的,比如对于一个D触发器:

 

 

时序图如下:

 

 

建立时间(Tsu:set up time):  

是指在触发器的时钟信号上升沿到来以前,数据稳定不变的时间。如果建立时间不够,数据将不能在这个时钟上升沿被稳定的打入触发器,Tsu就是指这个最小的稳定时间。

保持时间(Th:hold time):  

是指在触发器的时钟信号上升沿到来以后,数据稳定不变的时间。如果保持时间不够,数据同样不能被稳定的打入触发器,Th就是指这个最小的保持时间。

 

建立时间和保持时间其实就是数据在时钟上升沿前后的一个时间窗口内必须保持稳定,不然数据就不能正确的存到触发器。

 

为什么需要建立时间和保持时间

那么为什么会有建立时间和保持时间的要求呢?

D触发器的门级电路如下:

 

 

我们知道D触发器是在(上升)边沿进行锁存数据的,也就是clk从在0→1的时候锁存数据,那我们就看看这个上升沿的时候发生了什么:

假设原来的数据是1(也就是从锁存器锁存的数据是1),然后我们要锁存的数据是0。

要让D端的输入0在Q端输出,那么需要主锁存器稳定的锁定输入的0,而从锁存器则负责传输主锁存器锁存的数据。所以我们重点分析主锁存器。

 

 

为了简化分析假设反相器,与门,或非门这些门的延时都是tpd, 我们分析SR锁存器的输入端。假设输入D从0->1相对于CLK上升沿的延时是tdd, 那么输入端S的波形如下:

 

 

可以看到,CLK取反有tpd的延时,S端输入相对于D端也有tpd的延时。这样S=~Clk & D后第一个脉冲的宽度就是tw=tdd。

同样分析R端也可以得到相同的结论。

也就是说,输入数据D在时钟上升沿到来之前稳定的时间就表现为SR锁存器的S/R端信号的脉冲宽度。

SR锁存器对输入信号的脉冲宽度是有要求的。

S输入的低电平信号宽度tw应满足tw≥2tpd

所以,我们可以得到tw=tdd≥2tpd.

也就是说,数据在时钟上升沿到来之前,稳定时间必须要不小于2tpd(tpd为门电路延时), 才能被正确的采样到。

这就是建立时间:Tsu ≥2tpd

同样的分析,我们可以得到保持时间: Th≥2tpd

所以,我们的结论就是,由于门电路的延时和触发器的电路结构,建立时间和保持时间是一定存在的,否则就不能满足触发器的功能要求。

如果要知道实际的保持时间,就要深入与门等门级电路的内部结构,其模拟特性去理解。

 

所有的时序约束都是要告诉编译器,我的时钟和数据要满足怎样的关系,然后交给编译器去计算最糟糕的情况下能不能满足条件,还差多少ns就会不满足条件。

这句话对吗?

我觉得是错的,并不是所有的时序约束都是只告诉编译器这些信息,有些时序约束还会对实际的布局布线进行干预,可能产生积极结果也可能是消极结果。

 

3、时序约束的作用

时序分析的主要作用就是查看FPGA内部逻辑和布线的延迟,验证其是否满足设计者的约束。

 

确定芯片最高工作频率

控制工程的综合、映射、布局布线等关键环节,减少逻辑和布线的延迟,从而尽可能提高工作频率。一般情况下,处理时钟高于100MHz的时候,必须添加合理的时序约束文件。

 

检查时序约束是否满足

检查目标模块是否满足约束,若不满足,通过时序分析器定位程序中不满足的部分,并给出具体原因,然后设计人员修改程序,直到满足约束。

 

分析时钟质量

当采用了全局时钟等优质资源后,仍然不满足目标约束,则需要降低所约束的时钟频率。

 

确定分配引脚的特性

通过时序分析可以指定I/O引脚所支持的接口标准、接口速率和其他电气特性。

 

STA的目的就是要保证 DUT(Device Under Test)中所有的路径满足内部时序单位对 setup time 和 hold time 的要求。信号可以及时的从任一时序路径的起点传递到终点,同时要求在电路正常工作所需的时间内保持恒定。

 

4、如何查看时钟周期约束状态

 

(1)在上图中tcl命令框里,输入命令

report_clock_neworks -name mainclock

会出现

里面包含了已经约束的时钟和未被约束的时钟

(2)输入命令

check_timing -override_defaults no_clock

在tcl框里面出现

这里可以看到,有驱动59个寄存器的时钟未被约束。

5、如何查看时钟约束是否生效

在tcl命令输入

report_clocks

这里的话,实际上pll的输出时钟已经添加的了约束。

其中clkfbout是一个PLL会自动输出一个反馈时钟。

 

我这里的320m实际上是用的200m,所以周期是5ns

 一般来讲,我们的输入时钟都是差分的,此时我们只对P端进行约束即可。如果同时约束了P端和N端,通过report_clock_interaction命令可以看到提示unsafe。这样既会增加内存开销,也会延长编译时间。

 

6、clock_group的用法

使用方法为:

 

set_clock_groups -asynchronous -group <clock_name_1> -group <clock_name_2>

set_clock_groups -physically_exclusive  -group <clock_name_1> -group <clock_name_2>

 

  这个约束常用的方法有三种,第一种用法是当两个主时钟是异步关系时,使用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时钟,两个输出分别为50MHz200MHz,这两个时钟进入了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]]

 

7、开始进行时序约束

(1)梳理时钟树,确定主时钟和衍生时钟等等内容。

(2)查看各个时钟之间是否有交互,及clkA产生的数据是否在clkB的时钟域中被使用。

 

然后依次进行约束,在vivado中,可以在

Edit timing constraints 里面进行约束

 

这里面几乎涵盖了所有的时钟约束类型,一个一个去了解的话,参考文章开头的网址链接,里面有详细的说明。

 

另一个是在

Constraint wizard里面

界面如下:

 时序约束向导会按照主时钟约束、衍生时钟约束、输入延迟约束、输出延迟约束、时序例外约束、异步时钟约束等的顺序引导设计者创建约束。

8、实例  利用vivado的UI时序约束向导进行约束

打开工程,在implementation之后,点击如下选项

在什么都没有做的时候,时序约束UI界面里面是这样的

里面有一个主时钟clk156p,一个input jitter ,都是关于PLL的输入主时钟的,既然他已经做好了,而且我也明确的知道,这是一个port进来的主时钟,并且这个时钟是锁定住的,时钟约束的开头有个锁,所以我不能再对他进行操作。

所以我要在这基础上,进行我的时钟约束。

首先梳理我的时钟树

其实很简单,我需要约束的时钟是4个

1、主时钟clk156p

2、衍生时钟clk500m(我这里改成了300M)

3、衍生时钟clk40m

4、workclk是clk500m的分频(这里是300M的分频)

其中,1已经做好了,现在开始做第二个

点击+号

一项一项说明:

1、clock name是自己取的名字

2、Master pin是上级时钟的pin

3、Master clock是上级时钟

4、倍频和分频,我这里是300M,上级时钟是156.25M,所以,300/156.25=1.92=48/25,所以倍频48,分频25

5、source object 我这里选择了cell pins 搜索clk300m,找到对应得端口

6、把-add 打勾,表示是在原来的基础上添加衍生时钟

7、如果要invert反转自己的时钟,点击对应选项前面的打勾框

下面说明一下找cell pin之类的端口怎么搜

首先 Find names of type要选对,主时钟一般是port ,衍生时钟一般是net或者cell pins

然后在搜索框里输入名称,点击Find即可,在最下面两个框框里面将左边框正确的对象点击到右边框

操作时界面如下

其他衍生时钟也是类似这样设置。

可以在tcl命令框里面输入report_clocks

查看时钟状态

 

猜你喜欢

转载自blog.csdn.net/qq_41034231/article/details/106627487