64人参与 • 2024-08-03 • fpga开发
目录
rtl代码描述了电路的时序逻辑和组合逻辑,但代码中并不包括电路的时间信息(路径延时)和电路面积(门数)。为了使电路在规定的工作频率和工作环境下正确的工作,我们需要对设计中的所有时序路径进行约束。其目的是为了满足寄存器的建立时间和保持时间。
建立时间:在时钟有效沿到达之前,数据必须保持稳定的最小时间,简称tsu
保持时间:在时钟有效沿到达之后,数据必须保持稳定的最小时间,简称th
时序路径:一个点到点的数据通路。每一条时序路径有一个起点(startpoint)和一个终点(endpoint)。
发送沿:源端寄存器用于发送数据的时钟有效边沿。
捕获沿:目的端寄存器用于捕获数据的时钟有效边沿。默认情况下,发送沿和捕获沿相差一个时钟周期,有效边沿一般为上升沿。
起点:输入端口/触发器或寄存器的时钟引脚。
终点:输出端口/时序器件除时钟引脚以外的所有输入引脚。
时钟的四种属性:偏移(skew),抖动(jitter),延时(latency),转换(transition)时间。
时钟分支信号在到达寄存器的时钟端口过程中,都存在线网等延时,由于延时,到达寄存器时钟端口的时钟信号存在相位差,也就是不能保证每一个沿都对齐,这种差异称为时钟偏移(clock skew),也叫时钟偏斜。时钟的偏移如下图所示。
相对理想时钟沿而言,实际时钟存在不随时间积累,时而超前、时而滞后的偏移称为时钟抖动,简称抖动。
时钟偏移和时钟抖动都影响着时钟网络分支的延迟差异(相位差异),在design compiler里面,我们用时钟的不确定性(uncertainty)来表示这两种情况的影响。
自然界没有突变的事物,时钟由低电平到高电平的跳变或者由高电平到低电平的跳变称为时间的转换时间,并不是理想的时钟跳变。默认的上升转换时间为从电压的20%上升至80%的时间,下降的转换时间为从电压的80%下降至20%的时间。
时钟从时钟源(比如说晶振)出发到达触发器时钟端口的延时,称为时钟的延时,包含时钟源延迟(source latency)和时钟网络的延迟(network latency),如下图所示。
时钟源延迟(clock source latency),也称为插入延迟(insertion delay),是时钟信号从时钟原点到设计中时钟定义点(时钟的输入引脚)的传输时间,上图是3ns。
时钟网络延迟(clock network latency)是时钟信号从其定义的点(端口或引脚)到寄存器时钟引脚的传输时间,经过缓冲器和连线产生的延迟(latency),上图是1ns。
时钟不确定建模:set_clock_uncertainty命令用于对时钟的偏移和抖动进行约束。假设时钟clk的周期为10ns,时钟的建立偏差为0.5ns。用下面的命令进行约束:
create_clock -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.5 [ get_clocks clk]
set_clock_uncertainty命令如果不加开关选项-setup或-hold,那么该命令给时钟赋予相同的建立和保持偏差值。此外,还可以对时钟的上升沿和下降沿进行偏差建模,比如上升沿的偏差是0.2ns,下降沿的偏差是0.5ns
set_ clock_ uncertainty -rise 0.2 -fall 0.5 [get_clocks clk]
时钟转换时间建模:使用命令set_clock_transition进行约束设置。如果set_clock_transition命令中不加开关选项”-setup”或”-hold" ,那么该命令给时钟赋予相同的上升和下降转换时间。一般情况下,我们只约束最大的转换时间,如最大转换时间是0.2ns,那么就加上-max选项。
set_clock_transition -max 0.2 [get_clocks clk]
时钟延时建模:一般情况下,我们把时钟源延迟(source latency)和时钟网络的延迟(network latency)分开来,因为时钟源延时需要建模,是因为dc是真的不知道这个延时是多大,但是对于时钟网络的延迟,dc在布局布线前不知道,但是在布局布线后就可以计算出来时钟网络的延时了,因此在布局布线之后进行综合时,就没有必要对时钟网络进行约束,因此就要把这两个延时分开来进行约束。
布局布线之前:假设时钟周期为10ns,时钟源到芯片的时钟端口时间是3ns,时钟端口到内部触发器时钟引脚的时间是1ns。可以使用如下命令:
create_clock -period 10 [get_ports clk]
set_clock_latency -source -max 3 [get_clocks clk] //时钟源延时
set_clock_latency 1 [get_clocks clk] //时钟网路延时
布局布线之后:就可以计算实际的线网延时,就要使用set_propagated_clock [ get_clocks clk]这个命令代替上面的set_clock_latency 1 [get_clocks clk] 这个命令。
如下图所示,中间绿色部分是我们数字电路设计逻辑模块所要综合的单元,device a为芯片的上游器件,device b为芯片的下游器件。四类最常见的时序路径依次作如下分析。
路径编号 | 起点/s | 终点/e | 描述 |
1 | dina | ff2/d | 设计的输入端口到第一级寄存器数据端口 |
2 | ff2/cp | ff3/d | 寄存器时钟端口到下一级寄存器数据端口 |
3 | ff3/cp | douta | 末级寄存器时钟端口到设计的输出端口 |
4 | dinb | doutb | 输入端口到输出端口的纯组合逻辑电路 |
前三类时序路径的共同点在于:起点为寄存器的时钟端口,终点为寄存器的数据端口;第四类为组合逻辑路径。我们以路径2作为分析入口:
对于路径2,数据从ff2的d端口传输到ff3的d端口,主要经历的时间为:触发器的翻转时间(tco),寄存器与寄存器之间的组合逻辑x延时tx,到达寄存器ff3的数据端口d。
为了满足ff3建立时间的要求,也就是数据经过上面的延时(触发器ff2的翻转时间/转换延时、寄存器与寄存器之间的组合逻辑延时)之后到达ff3的d端的时间再加上ff3的建立时间,需要小于时钟周期。以发送沿作为0时刻参考点:
数据到达时间:tarrival=tco+tx。(数据到达寄存器ff3的d端口的实际时刻)
数据需求时间:trequired=tclk-tsu-tuncertainty。(为了满足建立时间需求,数据到达寄存器ff3的d端口最晚的时刻)
时间裕量:tslack=trequired-tarrival
当tslack>=0时,表示满足时序要求。
当tslack<0时,表示时序违例。
根据公式tslack=trequired-tarrival=tclk-tsu-tuncertainty-(tco+tx)>=0
即:tco+tx+tsu+tuncertainty<=tclk
由此可知:当系统时钟周期确定之后,组合逻辑x的最大延时就已经确定,或者说组合逻辑延时决定了系统所跑的最大频率。
为了满足ff3保持时间的要求,也就是数据经过上面的延时(触发器ff2的翻转时间/转换延时、寄存器与寄存器之间的组合逻辑延时)之后到达ff3的d端的时间,不能小于某个值,也就是说,这些延时也不能太小。
举个极端的例子说明一下:假设时钟周期为20ns,在0ns时刻,寄存器有效沿到来,ff2和ff3都要更新数据,ff2准备锁存高电平,ff3准备锁存低电平。由于ff2的反应很快,电路的延时很小,ff2的高电平很快就传输到ff3的d端口,高电平可能会冲掉ff3要锁存的低电平(也就是ff3的d端口低电平还没稳定,违反了保持时间),导致ff3对低电平保持时间不满足,导致ff3更新数据失败,要锁存的低电平可能产生亚稳态,因此就有传输延时需要大于ff3的保持时间。也就是说新的数据不能太早的到达,否则破坏原来数据的保持时间。由此可以知道,保持时间的分析比建立时间的分析提前一个时钟周期沿,也就是在0ns时刻传输数据,建立时间是在下一个时钟上升沿(20ns时刻)进行检查ff3的d端口数据是否稳定(若不稳定,则违反了建立时间),而保持时间是在发送数据的同一时刻(0ns时刻)检查ff3的d端口数据是否稳定(若不稳定,则违反了保持时间)。
关于保持时间的分析比建立时间的分析提前一个时钟周期沿这一点需要特别注意。以发送沿作为0时刻参考点:
数据到达时间:tarrival(new)=tclk+tco+tx //新的数据到达时间
数据需求时间:trequired=tclk+th+tuncertainty
时间裕量:tslack=tarrival(new)-trequired=tco+tx-(th+tuncertainty)>=0
即:tco+tx>=th+tuncertainty
结论:传输延时需要大于寄存器的保持时间和不确定之和。
注意:保持时间一般是能够满足的,也就是传输延时一般是大于触发器的保持时间的,即使满足不了,在后端版图设计的时候,也可以有修改措施(比如路径加缓冲器增加延时)。因此我们在约束的时候,我们一般不关注保持时间,而是注重建立时间。
图中路径n的最大时延应满足下列关系:
tn<=tclk-tco-tsu-tuncertainty
假设时钟周期为10ns,定义时钟约束如下:
create_clock -period 10 [get_ports clk]
一旦定义了时钟,对于寄存器之间的路径,我们就已经做了约束。
在上图中,在clk时钟上升沿,通过外部电路的寄存器ff1发送数据经过输入端a传输到要综合的电路,在下一个时钟的上升沿被内部寄存器ff2接收。
对于我们要综合的模块,dc综合输入的组合逻辑,也就是上面的电路n,它的延时是tn,但是这个tn是否满足的要求(比如说满足触发器的建立时间)呢?在进行约束之前,dc是不知道的,因此我们通过约束这条路径,也就是告诉dc外部的延时(即寄存器ff1的翻转延时和组合逻辑、线网的传输延时)是多少,比如说是tco+tm,在约束了时钟之后,dc就会计算这条路径留给电路n的延时是多少,也就是tclk -(tco+tm)。然后dc就拿tclk -(tco+tm)和tn+tsu相比较,看tn+tsu是否比tclk -(tco+tm)大,也就是看综合得到的电路n的延时tn是不是过大,如果tn+tsu太大,大于tclk -(tco+tm),那么dc就会进行优化,以减少延时。如果延时还是太大,dc就会报错。因此我们要进行输入端口的约束,告诉dc综合工具外部电路的延时是多少,以便dc约束输入的组合逻辑。
假设时钟周期为20ns,外部电路延时为7.4ns(包括寄存器ff1的翻转时间,组合逻辑m延时和线网延时到达端口a的延时时间),触发器的建立时间为1ns,则内部逻辑n最大延时为20-7.4-1=11.6,则输入端口的约束如下所示:
create_clock -period 20 [get_ports clk]
set_input_delay -max 7.4 -clock clk [get_ports a]
clk时钟上升沿通过内部电路的寄存器ff1发送数据经要综合的电路m,到达输出端口b,在下一个时钟的上升沿被到达外部寄存器的ff2接收。
我们要约束的组合路径电路m的延时,要dc计算它的延时是否能够满足时序关系,就要告诉dc外部输出的延时大概是多少。假设我们已知外部电路n的延迟为5.4 ns,ff2的建立时间1ns,ff1的翻转时间为1ns,就可以很容易地计算出m逻辑的最大延迟为20-5.4-1-1=12.6ns。
create_ clock -period 20 [get_ports clk]
set_output_delay -max 6.4 -clock clk [get_ports b]
组合逻辑部分m的最大延时tm=tclk-tinput_delay-toutput_delay,时钟周期减去两端,就得到了中间的延时约束,对于上面的模型,假设时钟周期为20ns,可以这样约束:
set_input_delay 0.4 -clock clk -add_delay [get_ports c]
set_output_delay 0.8 -clock clk -add_delay [get_ports d]
//set_max_delay 1.2 -from [get_ports c] -to [get_ports d]
set_max_delay用于从输入端口到输出端口的组合逻辑延时约束
如上图所示,在我们要综合的电路中,只有一个时钟端口clkb,即只有clkb时钟驱动要综合电路中的寄存器。其他的时钟clka,clkc和clkd在我们要综合的电路中并没有对应的时钟端口。因此,它们并不驱动要综合电路中的任何寄存器。它们主要用于为输入/输出端口延时作约束,可能会出现一个端口有多个约束的情况。
clkb在要综合的设计中有对应的输入端口,其定义与单时钟时一样,即:
create_clock -period 20 [get_ports clkb]
由于clka,clkc和clkd在要综合的设计中没有对应的输入端口,因此需要使用虚拟(virtual)时钟。虚拟时钟在设计里并不驱动触发任何的寄存器,主要用于说明相对于时钟的i/o端口延迟,dc将根据这些约束,决定设计中最严格的约束。建立虚拟时钟的格式如下:
create_clock -name clkd -period 10 //-name参数必须要
上面定义了名字为clkd的虚拟时钟,周期为10ns。因为虚拟时钟不驱动设计中的任何寄存器,设计中没有其对应的输入端口。所以定义中没有源端口或引脚。由于虚拟时钟没有对应的时钟端口,我们必须给它一个名字。四个时钟为同源时钟,所以他们有固定的相位关系,那么可以用作同步约束处理。
根据clka和clkb时钟分频可以得到如下时钟信号波形相位关系。
由于是输入端口,数据是从clka时钟域传到clkb时钟域,因此从clka出发,也就是说数据从clka上升沿开始(时刻0),然后clkb的下一个上升沿到来的时刻(时刻20)捕获数据,因此留给输入逻辑的延时是20-5.5- tsu;clka下一个上升沿(时刻30ns处)又传输数据过来,而这时相对于clka的下一个clkb上升沿在40ns处捕获数据,因此留给输入逻辑的延时是10 -5.5 - tsu;这两个延时必须同时满足,故满足10 -5.5 - tsu。
对应的约束为:
create_clock -period 30 -name clka
create_clock -period 20 [get_ports clkb]
set_input_delay -max 5.5 -clock clka [get_ports a]
根据clkb、clkc和clkd时钟分频可以得到如下时钟相位关系。
要综合电路的输出部分s必须满足:ts<10-4.5-tco和ts<6.7-2.5-tco中最严格的要求,也就是ts<6.7-2.5-tco。(ts满足:10ns = tco+ts + tuncertainty + 4.5ns和6.7ns=tco+ts+tuncertainty+2.5ns中最严格的)
与前面的类似,只不过这里是数据输出端口,clkb的上升沿是数据发送沿,clkc和clkd是数据接收端,因此都是从clkb出发。比如clkb分别从0时刻和20时刻出发,到clkc或者clkd的上升沿结束,跟前面同理,由此也可以得出最严格的延时约束是ts<6.7-2.5-tco。
创建的虚拟时钟和对应的约束为:
create_clock -period [expr 1.0/75*1000] -name clkc
create_clock -period 10 -name clkd
create_clock -period 20 [get_ports clkb]
set_output_delay -max 2.5 -clock clkc [get_ports b]
set_output_delay -max 4.5 -clock clkd -add_delay [get_ports b]
第二条set_output_delay命令里,使用了-add_delay选项,意思是输出端口b有多个约束,如果不加选项-add_delay,第二个set_output_delay命令将覆盖(取代)第一条set_output_delay命令,这时,输出端口b只有一个约束,就达不到我们的预期要的约束了。
时钟clkc的频率为75mhz(300mhz/4),为了计算时钟周期,我们需要用实数来得到时钟的周期。要注意的是[expr 1/75*1000]与[expr 1.0/75*1000]得到的结果是不一样的,前者是0,后者不为0的实数。
由此可见,综合时,dc计算出所有时钟的公共基本周期(common base period),对计算出每个可能的数据发送/数据接收时间,按最严格的情况对电路进行综合。这样可以保证得到的结果能满足所有的要求(约束),达到设计目标。
时序例外:异步路径和逻辑上不存在的路径,称为时序例外
对于穿越异步边界的任何路径,我们必须禁止对这些路径做时序分析。由于不同时钟源的时钟之间相位关系是不确定的,一直在变,对跨时钟域的路径作时序约束是毫无意义的。因此我们不要浪费dc的时间,试图使异步路径“满足时序要求”。我们可用set_false_path命令为跨时钟域的路径作约束(其实是解除时序路径的约束)。例如,对于如下异步路径。
设计之间是异步的,存在垮时钟域的路径,clka来自于晶振1产生的50m的时钟频率,clkb来自于晶振2产生的100m时钟频率,我们就要用set_false_path命令为跨时钟域的路径作约束,上图的异步电路对应的跨时钟域约束如下所示:
create_clock -period 20 [get_ports clka]
create_clock -period 10 [get_ports clkb]
set_false_path -from [get_clocks clka] -to [get_clocks clkb]
set_false_path -from [get_clocks clkb] -to [get_clocks clka]
//set_clock_group -asynchronous -group [get_clocks clka] -group [get_clocks clkb]
注明:用set_false_path命令对异步路径作时序约束后,dc综合时,将中止对这些路径做时间的优化和分析工作,set_clock_group命令可以代替set_false_path的两条命令。
当选择信号为0时,数据通路为mux0/i0到mux1/i0的路径;当选择信号为1时,数据通路为mux0/i1到mux1/i1的路径。无论s为0或者为1,从mux0/i0到mux1/i1的路径或者mux0/i1到mux1/i0的路径没有数据流通,这种在物理上存在连接关系,但是在逻辑上不存的路径称为逻辑伪路径。
根据上图的电路,我们可以进行如下的伪路径约束。
set_false_path -from [get_ports a] -through [get_pins mux0/i0] -through [get_pins mux1/i1] -to [get_ports b]
set_false_path -from [get_ports a] -through [get_pins mux0/i1] -through [get_pins mux1/i0] -to [get_ports b]
//set_false_path -through [get_pins mux0/i0] -through [get_pins mux1/i1]
//set_false_path -through [get_pins mux0/i1] -through [get_pins mux1/i0]
其中-from参数表示起始点,-through参数表示穿过某个设计部分,-to参数表示伪路径终点。同时我们可以使用下面注释掉的两个命名分别代替上面的命令,表示只要穿越这两个路径的,不管起始点和终止点在哪里,它们均为伪路径。
如下图所示加法器电路,时钟clk的周期定义为10ns,按设计规格,加法器的延迟约为6个时钟周期。
①默认的建立时间约束:
默认的建立时间约束将指示dc在10ns的时候对ff3进行建立时间是否满足的分析。很显然,默认的时序约束会使寄存器的数据输入引脚ff3/d信号变化,不满足建立(setup)的要求,将产生亚稳态,寄存器ff3的输出为不定态。也就是一个时钟周期的约束不能满足约束要求。
②修改后的建立时间约束:
对于多时钟周期的建立时间约束,可以使用下面命令进行修改:
create_clock -period 10 [get_ports clk]
set_multicycle_path -setup 6 -to [get_pins ff3[*]/d]
注意这条命令是要知道多时钟周期的终点寄存器的(注意:这条命令设置了所有的前级寄存器时钟端口到ff3寄存器的d端口路径都是多时钟周期路径,而set_multicycle_path -setup 6 -from ff1/clk -to [get_pins ff3[*]/d],则是仅仅现在从ff1寄存器的时钟端口到ff3寄存d端口的这一条路径而已),通过这条命令,就告诉dc将仅仅在第6个上升沿,即60 ns作建立时间的分析(也就是间隔6个时钟周期后再做建立时间分析)。这时,加法器的最大允许延迟是:60-tco-tsu-tuncertainty
对应的时序关系如下图所示:
①默认的保持时间约束
对于保持时间的约束,我们是不是默认就ok了呢?在前面的建立时间和保持时间的概念中,我们知道默认的保持时间分析在建立时间分析的前一个周期,也就是说,在这个多时钟周期的加法器中,dc将在50 ns这个时刻分析电路有无违反保持要求,要求加法器的最小延时是(注意这是默认情况下的所做的时序要求):50+th-tco+tuncertainty
默认的保持时间分析对应的时序关系如下所示:
也就是说,经过修改过后的多时钟周期建立时间约束和默认的保持时间约束就会告诉dc,要dc综合出一条路径使其建立时间满足60 ns的要求,并且同时满足保持时间50ns的要求。即加法器的延时为(50+th+tuncertainty-tco)≤tadd_delay≤(60-tco-tsu-tuncertainty)
但是要综合出这样一条路径实际上是没有必要的,这样做只会增加电路的复杂度。为什么会这样呢?这是因为默认保持时间不满足约束,也就是说,不应该在50ns的时候进行保存时间的检测,需要修改多时钟周期保持时间的约束。
②修改后的保持时间约束
那在上面什么时候做保持时间分析比较合适呢?我们知道在时间为60 ns的时刻,引起寄存器ff3的d引脚信号变化的是时钟clk在0时刻的触发沿。在0ns时,时钟clk把寄存器ff1和ff2的d引脚信号采样到它们的输出端。再通过加法器把信号传输到寄存器ff3的d引脚。由此可见,会冲掉ff3的d端数据只是ff1和ff2的d引脚的变化的时候,也就0ns时刻,因此应该对保持时间做出调整,应该在0ns的时候做保持时间的检测,也就是应该提前5个时钟周期,从50ns提前到0ns。保持时间的分析提前了5个周期,加法器的允许延时为:th-tco+tuncertainty<加法器允许的延时<60-tco-tsu-tuncertainty
对应的时序关系如下:
修改后的约束如下:
create_clock -period 10 [get_ports clk]
set_multicycle_path -setup 6 -to [get_pins ff3[*]/d]
set_multicycle_path -hold 5 -to [get_pins ff3[*]/d]
仅仅通过约束告诉dc这是一个多时钟周期的加法器电路是不够充分的,一方面是由于后面的触发器应该经过6个时钟周期之后才能采到正确的值(但是ff3不知道什么时候采到的值是正确的),另一方面是约束仅仅是告诉dc如果这块电路的延时太大或者太小的时候要报错;可以这么理解,约束单单保证了时序上这是一个多时钟周期的加法器电路,但是这个加法器电路经过延时得到结果后,后面的ff3采样的正确性,需要在rtl代码的设计上保证,需要加上相应的控制信号,保证dc能够综合出能够正确工作的多时钟周期的加法器。对于前面的加法器,可以需要加上相应的使能信号,使用6bit循环移位寄存器完成设计,因此电路设计如下所示。
前面的例子是单纯的多时钟周期设计,当电路里面同时存在多时钟周期路径和普通路径时,如下图所示:
该电路中,要求寄存器间的乘法运算为两个时钟周期,加法运算为默认的一个时钟周期,这时候,可以使用下面命令进行约束:
create_clock -period 10 [get_ports clk]
set_multicycle_path -setup 2 -from ff1/cp -through multiply/out -to ff2/d
set_multicycle_path -hold 1 -from ff1/cp -through multiply/out -to ff2/d
注意电路图中的from、through、to的对象。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论