参考http://www.cnblogs.com/IClearner/p/6624722.html,写得很好
一:时序约束
1:分类
时钟的约束(寄存器-寄存器之间的路径约束),输入延时的约束,输出延时的约束
2:时序约束对电路的要求
综合工具现在不能很好地支持异步电路,甚至不支持异步电路;
single clock,single cycle,单个时钟,单延触发,不要一会posedge,一会negdege
3:同步电路常见结构
具体路径如下图
起点定义:
输入端口;(input port)
触发器或寄存器的时钟引脚
输出端口:
输出端口;
时序器件的除时钟引脚外的所有输入引脚
如在下面这个电路图中:
有4条路径:
*从输入端口A到FF1的D端口
*从FF1的CLK端到FF2的D端
*从FF2的CLK端到输出端口out1
*从输入端口A到输出端口out1
路径的特性是存在延时,也就是说,路径1、2、3、4都存在有延时,延时最长的一条路径称为关键路径。一般情况下,路径1、2、3是最常见的,路径4比较少见。
二:常见的时序路径约束
1:建立时间,保持时间,亚稳态
*建立时间: 时钟有效沿到来之前的某段时间内,数据必须稳定,否则触发器锁存不住数据,这段时间称为建立时间,用Tsetup或者Tsu表示
*保持时间: 时钟有效沿到来之后的某段时间内,数据也必须稳定,否则触发器锁存不住数据。这段时间称为保持时间,用Thold或者Th表示
在第二个时钟上升沿的时候,要锁存住输入端D的高电平,D1是满足了建立时间和保持时间的情况;而D2则是建立时间没有满足,因此不能成功锁存住输入的高电平;D3保持时间不满足,也不能成功锁存输入的高电平。
*亚稳态:每个触发器都有其规定的建立(setup)和保持(hold)时间参数,该参数存放在由半导体厂商所提供的工艺库中。假如触发器由时钟的上升沿触发,在这个时间参数内,输入信号是不允许发生变化的。否则在信号的建立或保持时间中对其采样,得到的结果是不可预知的,有可能是0或者1,即亚稳态。
2:路径2寄存器到寄存器间的约束(从FF1的CLK端到FF2的D端)
为什么要约束时序路径?是为了满足寄存器的建立时间和保持时间。对于路径2,数据从FF1的D端口传输到FF2的D端口,主要需要经历触发器的翻转时间/转换延时、寄存器与寄存器之间的组合逻辑延时、连线延时这些种延时。因为数据是随着时钟的节拍一拍一拍往后传的,因此这里的寄存器与寄存器之间的路径约束,就是对时钟的建模,或者是说对时钟的约束。下面进行说明
为了满足FF2建立时间的要求,也就是数据从FF1的CLK端口到达FF2的D端口再加上FF2的建立时间,不能超过时钟周期;也就是说(触发器的翻转时间/转换延时、寄存器与寄存器之间的组合逻辑延时、连线延时这些种延时)不能过大。例如:
*为满足建立时间的需求:现在的节拍(0ns这一时刻)到来后,数据(比如高电平)从FF1的D端传来,经过组合逻辑,在下一个节拍(20ns时刻)的时候传到FF2的D端,更新FF2的数据(0ns时,FF2保存的是低电平),如红色箭头所示;由于延时过大,在下一个节拍带来时刻(20ns时刻),数据(高电平)还在组合逻辑那里,如绿色箭头,导致FF2的D端数据不能更新,或者满足不了建立时间,由此可能引起锁存错误。通过对时钟建模,DC就知道了这条路径运行的最大延时,就会选择合适的单元来满足这些延时的约束。如果DC选来选去,发现最牛逼的单元得到的电路延时还是很大,无法满足FF2的建立时间要求,DC就会报错。这个时候,你就要进行修改设计了(比如修改约束或者修改代码)。
*为满足保持时间的需求:也就是数据经过上面的延时(触发器的翻转时间/转换延时、寄存器与寄存器之间的组合逻辑延时、连线延时)之后到达FF2的D端的时间,不能小于某个值。也就是说,这些延时也不能太小。举个极端的例子说,在0ns的时候,触发器有效沿到来,FF1和FF2都要更新下数据,FF1准备锁存高电平,FF2准备锁存低电平;由于FF1反应很快,电路延时很小,FF1寄存住的高电平很快就会传到FF2的D端口,高电平就会冲掉FF2要锁存的低电平(也就是FF2的D端口低电平还没有稳定,违反了保持时间),也就是说会导致低电平对FF2的保持时间不能得到满足,导致FF2更新数据失败,要锁存住的低电平可能就产生亚稳态。因此就有传输延时需要大于FF2的保持时间。
此外,我们由此也可以知道,保持时间的分析比建立时间的分析提前一个时钟周期沿,也就是说在0ns时候传输数据,建立时间是在下一个时钟上升沿(20ns时刻)进行检查FF2的D端口数据是否稳定(若不稳定,就违反了建立时间),而保持时间是在发送数据的同一时刻(也就是0ns时刻)检查FF2的D端口数据是否稳定(如果不稳定,就违反了保持时间);关于保持时间的分析比建立时间的分析提前一个时钟周期沿这一点需要注意。
然而,保持时间一般是能够满足的,也就是传输延时一般是大于触发器的保持时间的,即使满足不了,在后端版图设计的时候,也可以有修改措施(比如路径加缓冲器增加延时)。因此我们在约束的时候,我们一般不关注保持时间,而是注重建立时间。
*时钟约束
寄存器与寄存器间的延时小于时钟周期减去建立时间,即:2 path delay <= Clock Period - Tsetup
定义时钟周期的命令为:create_clock。例如:create_clock -period 10 [get_ports clk]
定义时钟时(虚拟时钟除外,虚拟时钟在后面说),我们必须定义时钟周期(也就是-period这个选项)和时钟源(端口或引脚)(也就是设计中的clk),也可以加上一些可选项(option)来定义时钟的占空因数(duty cycle),偏移(offset/skew)和时钟名( clock name),我们可以通过man create_clock 来查看命令的相关选项。
一旦定义了时钟,对于寄存器之间的路径,我们已经做了约束。我们可以用report_clock命令来查看所定义的时钟以及其属性。如果我们需要使用时钟的两个沿(上升沿和下降沿),时钟的占空因数将影响时序的约束。
然而单单定义一个时钟周期进行约束寄存器与寄存器之间的路径很显然是过于理想的,需要再添加其他的时钟属性,在添加之前,需要知道时钟的偏移(skew)、抖动(jitter)、转换时间(transition)、延时(latency)这几个概念或者这几个时钟的属性。
3:路径1输入端口A到寄存器D端的约束
这里讨论的是模块前后使用的是同一个时钟CLK,如果使用不同时钟,那么约束就不同了。
在上图中,CLK时钟上升沿,通过外部电路的寄存器FF1发送数据经过输入端口A传输到要综合的电路,在下一个时钟的上升沿被内部寄存器FF2接收。它们之间的时序图如下图所示:
对于我们要综合的模块,DC综合输入的组合逻辑,也就是上面的电路N,得到它的延时是Tn,但是这个Tn是否满足要求呢(比如触发器建立时间的要求)?在进行约束1之前,DC是不知道的,因此我们通过约束这条路径,也就是告诉DC外部的延时是多少,比如Tclk-q + Tm,在约束了时钟之后,DC就会计算这条路径留给电路N的延时是多少,也就是,然后DC就拿Tclk-(Tclk-q+Tm)和Tn+Tsetup相比较,如果Tn较大(Tn+Tsetup>Tclk-(Tclk-q+Tm)),那么DC就会进行优化,使Tn减少,如果Tn还是过大,DC就会报错。因此我们要进行输入端口的约束,告诉外部电路的延时是多少,以便DC约束输入的组合逻辑。
如果我们已知输入端口的外部延迟(假设为4 ns,包括翻转延时和外部的逻辑延时),就很容易计算出留给综合电路输入端到寄存器N的最大允许延迟。
DC中,用set_input_delay命令约束输入路径的延迟
set_input_delay -max 4 -clock CLK [get_ports A]
我们指定外部逻辑用了多少时间,DC计算还有多少时间留给内部逻辑。在这条命令中,外部逻辑用了4 ns,对于时钟周期为10 ns的电路,内部逻辑的最大延迟为10 - 4 - Tsetup = 6 。
例如:对于下面的电路。
输入端口延时的约束为
creat_clock -period 20 [get_ports CLK]
set_input_delay -max 7.4 -clock CLK [get_ports A]
对应的时序图如下所示:
如果触发器U1的建立时间为1ns,则N逻辑允许的最大延迟为:
20-7.4-1=11.6ns
上面是没有考虑不确定因素情况,当考虑不确定因素时,则有:
当有抖动和偏移的时候(假设不确定时间为U),如果触发器U1的建立时间为1ns,外部输入延时为D(包括前级寄存器翻转和组合逻辑的延时),则N逻辑允许的最大延迟S为:
20-D-U-1=S,同样可以得到外部输入的延时为:20-U-1-S=D
当输入的组合逻辑有多个输入端口时,如下图所示:
则可以用下面命令对除时钟以外的所有输入端口设置约束:
set_input_delay -max 3.5 -clock CLK [romove_from_collection [all_inputs] [get_ports CLK]];#表示从所有输入端口中除掉时钟CLK。
4:路径3,寄存器到输出端口的约束
clk时钟上升沿通过内部电路的寄存器FF2发送数据经要综合的电路S,到达输出端口B,在下一个时钟的上升沿被到达外部寄存器的FF2接收。他们之间的时序关系如下图。
当我们已知外部电路的延迟(假设为5.4ns),就可以很容易计算出留给要综合电路输出端口的最大延迟,如下图所示:
在DC中,用set_output_delay命令来约束输出路径的延迟,对于上面的电路,有
set_output_delay -max 5.4 -clock CLK [get_ports B]
内部逻辑最大延迟为:Tclk_q+Ts=Tclk-5.4
5:输入路径延时与输出路径延时的实际情况
进行SOC设计时,由于电路比较大,需要对设计进行划分,在一个设计团队中,每个设计者负责一个或几个模块。设计者往往并不知道每个模块的外部输入延迟和/或外部输出的建立要求(这些要求或许在设计规格书里面写有,或许没有,当没有的时候设计者就不知道了),如下图所示:
这时,我们可以通过建立时间预算(Time Budget),为输入/输出端口设置时序的约束,也就是先预置这些延时,大家先商量好(或者设计规格书声明好)。但是预置多少才合适呢?就有下面的基本原则了:
DC要求我们对所有的时间路径作约束,而不应该在综合时还留有未加约束的路径。我们可以假设输人和输出的内部电路仅仅用了时钟周期的40%。如果设计中所有的模块都按这种假定设置对输人/输出进行约束,将还有20%时钟周期的时间作为富余量( Margin),富余量中包括寄存器FF1的延迟和FF2的建立时间,即:富余量=20%时钟周期 - Tclk-q - Tsetup
如下图所示:
举个例子说,对于前面的电路,就要按照这么一个比例进行设置:
对应的约束为:
create_clock -period 10 [get-ports CLK]
set_input_delay -max 6 -clock CLK [all_inputs]
remove_input_delay [get ports CLK] ;#时钟不需要输入延迟的约束
set_output_delay -max 6 -clock CLK [all-outputs]
5:路径4的约束
路径4是组合逻辑的路径,组合逻辑的约束可能需要虚拟时钟的概念。组合逻辑可能有两种中情况,一种是前面电路中的路径4:
模块里面有输入端口到输出端口的组合逻辑外,也有时序逻辑,也就是模块里面有时钟,那么就可以对于路径4,就下面的电路模型进行约束:
组合逻辑部分F的延时Tf就等于时钟周期T-Tinput_delay-Toutput_delay,时钟周期减去两端,就得到了中间的延时约束了,对于上面的模型,可以这样约束为:
set_input_delay 0.4 -clock CLK -add_delay [get_ports B]
set_output_delay 0.2 -clock CLK -add_delay [get_ports D]
set_max_delay $CLK_PERIOD -from [get_ports B] -to [get_ports D]
当然,最后一句的约束可有可无。对于多时钟的同步约束,只需要修改相应的延时和时钟就可以了,可以参考前面的多时钟同步时序约束那里。
6:纯组合逻辑,内部没有时钟
用到虚拟时钟的概念,create_clock -name VCLK -period 2 ;#指定一个虚拟时钟名称。
三:其他
1:当输入延时不同时,第二个输入延时命令会覆盖第一个输入延时命令
四:实战
1:启动DC,
list_designs ;#打开当前设计
remove_design -hierarchy;#移除设计
2:读verilog(analyze - elaborate)
analyze -format verilog ./rtl/TOP.v
elaborate architecture verilog TOP
3:检查设计
link
check_design
3 :输入约束条件(在这里是在dc_shell里一个一个设置,实际会新建一个约束文件,读约束文件source file.con)
*在添加约束之前,使用reset_design清除前面存留的约束。
*添加时钟频率:create_clock -period 2 [get_ports Clk]
*添加输入端口延时
*添加输出端口延时
3.1:若写的是脚本约束,检查语法命令:shell脚本下 dcprocheck ./script/file.con
4:检查所有路径是否约束
check_timing
出现error则有路径没有约束完
5:编译
compile
6:综合后的检查(检查不通过的需要优化,这里只作为一般流程,没有进行优化)
report_constraint -all (查看是否违规)
report_timing (查看时序报告)
report_area (查看面积情况)