时序分析的基本步骤:
一个合理的时序约束可以分为以下步骤:
时序约束整体的思路与之前我说的方法基本一致。整体的思路如下:
先是约束时钟,让软件先解决内部时序问题;(在这一步骤中可以适当加入时序例外,以便时序通过)
然后再加入IO的延迟约束;
最后针对没有过的时序,添加时序例外。
在《vivado使用误区与进阶》中,提到了一种叫 UltraFAST 的设计方法。针对下图中所说的根据迭代结果添加必要的例外约束(步骤1),为什么是添加必要的呢?是因为虽然是跨时钟域,但是有时候就算不约束,时序也能过。当碰到时序不过的时候,现阶段可以去针对该部分路径进行时序例外约束,以便后续继续分析。需要注意的是,就算没有报时序为例,现阶段也一定要去留意那些跨时钟的路径(通过clock_interaction),确认代码有没有做相应的跨时钟域处理。针对跨时钟域的代码处理后面会提到。
定义时钟约束
时钟主要可以分为主时钟以及衍生时钟。
创建主时钟。典型的主时钟根包括有以下几种情况:输入端口、千兆位收发器输出引脚以及某些硬件原语输出管脚。
约束实例 :
create_clock -name SysClk -period 10 -waveform {0 5} [get_ports sysclk]
GT 收发器输入引脚,例如已恢复的时钟,
约束实例 :
create_clock -name txclk -period 6.667 [get_pin gt0/TXOUTCLK]
创建生成时钟。
自动衍生时钟
大部分生成时钟都由 Vivado 设计套件时序引擎自动衍生获得,该引擎可识别时钟修改模块 (CMB)及其对主时钟所执行的变换。赛灵思 7 系列器件中,CMB 是 :
• MMCM/ PLL
• BUFR
• PHASER*
如果 Vivado 设计套件时序引擎所选择的自动衍生时钟名称并不合适,您可以使用 create_generated_clock 命令强行定义自己的名称,此时无需指定波形转换。该约束应刚好位于约束文件中定义主时钟的约束之后。例如,由 MMCM 实例生成的时钟的默认名称是 net0,您可以添加如下约束强制将其设定为自己的名称(例如fftClk):
create_generated_clock -name fftClk [get_pins mmcm_i/CLKOUT0]
生成时钟源自另一个现有时钟(主时钟)。通常用来描述由逻辑模块在主时钟上执行的波形变换。由于生成时钟的定义取决于主时钟特性,因此必须首先定义主时钟。要明确定义生成时钟,必须使用 create_generated_clock 命令。
create_generated_clock -name GC1 -source [get_pins gen_clk_reg/C] -divide_by 2 [get_pinsgen_clk_reg/Q]
跨时终域处理
跨时钟域处理,主要是为了避免亚稳态的传播(注意亚稳态不能消除,但是可以采用一定的方式,降低其传播的风险)。触发器进入亚稳态的时间可以用参数MTBF(Mean Time Between Failures)来描述,MTBF即触发器采样失败的时间间隔,表示为:
例如针对单bit数据的跨时钟域处理,是为了让MTBF增大,使得进入一个可接受范围。
跟大家探讨一个问题,为什么打两拍就可以降低亚稳态的传播呢?
我想到的一个场景如下:
信号a,经过跨时钟处理后变成了a’;这样就可以确保在时序满足的前提下,同一个时钟域中的1、2、3模块接收到的a’的值是一致的,如果不做跨时钟域处理,由于布局布线的延迟不一样,不能确保到达1、2、3模块的值是一致的,从而导致逻辑混乱,引起系统的不稳定现象。
IO的延迟约束
输入延迟
外部器件发送数据到FPGA系统模型如下图所示。对FPGA的IO口进行输入最大最小延时约束是为了让FPGA设计工具能够尽可能的优化从输入端口到第一级寄存器之间的路径延迟,使其能够保证系统时钟可靠的采到从外部芯片到FPGA的信号。
输入延时即为从外部器件发出数据到FPGA输入端口的延时时间。其中包括时钟源到FPGA延时和到外部器件延时之差、经过外部器件的数据发送Tco,再加上PCB板上的走线延时。如下图所示,为外部器件和FPGA接口时序。
最大输入延时(input delay max)为当从数据发送时钟沿(lanuch edge)经过最大外部器件时钟偏斜(Tclk1),最大的器件数据输出延时(Tco),再加上最大的PCB走线延时(Tpcb),减去最小的FPGA时钟偏移(FTsu)的情况下还能保证时序满足的延时。这样才能保证FPGA的建立时间,准确采集到本次数据值,即为setup slack必须为正,计算公式如下式所示:
Setup slack =(Tclk + Tclk2(min))–(Tclk1(max) +Tco(max) +Tpcb(max) +FTsu)≥0 (1)
最小输入延时(input delay min)为当从数据发送时钟沿(lanuch edge)经过最小外部器件时钟偏斜(Tclk1),最小器件数据输出延时(Tco),再加上最小PCB走线延时(Tpcb),此时的时间总延时值一定要大于FPGA的最大时钟延时和建立时间之和,这样才能不破坏FPGA上一次数据的保持时间,即为hold slack必须为正,计算公式如下式所示:
Hold slack = (Tclk1(min) + Tco(min) + Tpcb(min))–(FTh + Tclk2(max))≥ 0 (2)
我们很容易就可以从公式(1)和(2),推到出(3)
Tclk – Ftsu ≥Tclk1 - Tclk2 + Tco + Tpcb ≥ FTh (3)
在公式(3)中,我们发现Tclk 、Ftsu以及FTh,对于工具来说是已知的,而Tclk1 - Tclk2 + Tco + Tpcb正是我们需要告知综合工具的延迟量。
从我们推到出的公式,我们可以得到
Input_delay_max = Tclk – Ftsu; Input_delay_min = FTh ;
输出延迟
FPGA输出数据给外部器件模型如下图所示。对FPGA的IO口进行输出最大最小延时约束是为了让FPGA设计工具能够尽可能的优化从第一级寄存器到输出端口之间的路径延迟,使其能够保证让外部器件能准确的采集到FPGA的输出数据。
输出延时即为从FPGA输出数据后到达外部器件的延时时间。其中包括时钟源到FPGA延时和到外部器件延时之差、PCB板上的走线延时以及外部器件的数据建立和保持时间。如所示,为FPGA和外部器件接口时序图。
最大输出延时(output delay max)为当从FPGA数据发出后经过最大的PCB延时、最小的FPGA和器件时钟偏斜,再加上外部器件的建立时间。约束最大输出延时,是为了约束IO口输出,从而使外部器件的数据建立时间,即为setup slack必须为正,计算公式如下式所示:
Setup slack =(Tclk + Tclk2(min))–(Tclk1(max) +FTco(max) +Tpcb(max) +Tsu)≥0 (4)
最小输出延时(output delay min)为当从FPGA数据发出后经过最小的PCB延时、最大的FPGA和器件时钟偏斜,再减去外部器件的建立时间。约束最小输出延时,是为了约束IO口输出,从而使IO口输出有个最小延时值,防止输出过快,破坏了外部器件上一个时钟的数据保持时间,导致hlod slack为负值,不能正确的锁存到数据,最小输出延时的推导计算公式如下式所示:
Hold slack = (Tclk1(min) + FTco(min) + Tpcb(min))–(Th + Tclk2(max))≥ 0 (5)
我们很容易就可以从公式(4)和(5),发现这两条公式与前面推导输入延迟如出一辙。只不过现在FPGA变成了输出器件,而Tsu、Th是下游器件的参数,综合工具并不知情,需要我们告诉他。除了FTco以外,其他参数都需要我们告诉工具。
由公式(4)我们可以推导出:
FTco(max) + Tpcb(max) –(Tclk2(min) – Tclk1(max)+Tsu ≤Tclk
那么output delay max = Tpcb(max) –(Tclk2(min) – Tclk1(max)+Tsu
同理我们由公式(5)可以推导出:
FTco(min) + Tpcb(min) – (Tclk2(max) – Tclk1(min))– Th ≥ 0
那么output delay min = Tpcb(min) – (Tclk2(max) – Tclk1(min))– Th
时钟为例的一些处理方法
下面是我收集到的一些针对时序为例的常规处理办法,也记录下来与大家分享一下:
下面介绍主要面对的两个时序问题的处理技巧。
1)setup time 建立时间问题
建立时间是工程设计中最常遇到的问题了。一般说来,导致建立时间违例主要有两个原因:逻辑级数太大或者扇出太大。
打开Report Timing Summary界面查看路径延迟信息。
Levels指的是逻辑级数logic level,一个logic level的延迟对应的是一个LUT和一个Net的延迟,对于不同的器件,不同频率的设计能容纳的logic level是不同的。假设7系列的-2速度等级250MHz的设计,电路设计的大部分levels最好不要超过8,否则会造成时序收敛困难。
Logic level太大的处理方法就是重定时(Retiming)了,典型的重定时方法就是流水线,将过于冗长的组合逻辑增加寄存器进行打拍。
High Fanout指的是扇出,同样和器件、设计频率等有关,如下图所示:
降低扇出最好不要在综合设置中指定,过低的扇出限制会造成设计堵塞反而不利于时序收敛,最好的方法是根据设计中时序最差路径的扇出进行针对性的优化。如果是寄存器的输出扇出很大,可以使用max_fanout属性标记寄存器声明,也可以手动复制寄存器,具体可参考:https://blog.csdn.net/shshine/article/details/52451997
如果不是关键时序路径,而且高扇出网络直接连接到触发器,对扇出超过25K的net插入BUFG:
set_property CLOCK_BUFFER_TYPE BUFG [get_nets netName]
当然,也可以在后期Implementation的物理优化设置中优化扇出。
2)hold time 保持时间问题
在实践中,我发现保持时间问题的问题往往是异步处理的问题。
对于一个信号的跨时钟域问题,一般使用双寄存器法(对于慢采快的结绳法这里不讨论)。为了降低MTBF(Mean Time Between Failures,平均无障碍时间),这两个寄存器最好位于同一个slice中。可以使用tcl语言指定,如:
set_property ASYNC_REG TRUE [get_cells [list sync0_reg sync1_reg]]
也可以直接在代码中指定:
( ASYNC_REG = "TRUE" ) ( keep = "true" )reg sync0_reg, sysnc1_reg;
也可以参考代码模板使用XPM模板进行处理。
注意:
在发现同一个时钟域中,时序还不满足要求,那应该怎么处理呢?
(这时候只能在修改代码方向努力了,回到Chipplanner中,分析关键路径,看布线后的路径是否过长,导致影响时序。还是由于高扇出导致时序为例。针对不同的原因,修改代码。比如说,路径过长,通常检测是否代码中嵌套的if语句级数太多?(可以尝试case语句替代),又或者if的判断中有表达式(if(a>b)之类的,看能否用电平替代,if(en)等,)对于有些逻辑融合起来一个大的模块,看能否将大的逻辑块划分为若干个小逻辑块实现。
针对高扇出问题,上述已提到相应的处理方式。
写到最后想说的是,调时序是一个比较难啃的活,有时候你增加了约束可能时序还会变的更差了,所以说一个良好的代码风格很重要。假如上述的方法都已经试过了,时序还是很难满足,可能器件已经快到达极限了,尤其是资源占用率比较高的时候。这时候只能降低时钟或者替换更高性能的器件啦。
END
原文链接: https://mp.weixin.qq.com/s/CKmmWt5gnZ_aMloljC9Oyg
微信公众号:
推荐阅读
更多IC设计技术干货请关注IC设计技术专栏。