Vivado实战总结:时钟约束

Vivado实战总结:时钟约束

我对于时钟约束(Clocking Constraints)的学习与真正理解在于正在进行的项目——拖缆模拟器逻辑设计。我完成了自己功能上的设计,但是在实施(Implementation)阶段,我发现时序出现问题(Timing failed)。下文给出我的解决方案。
请注意,这篇文章如果作为时序约束系统的学习文档并不合适,在本文最后我会给出我所参考的资料。

工作环境

  • Vivado 2016.4
  • -2速度级别的FPGA Kintex7

概念与术语

  • STA static timing analyse 是指Vivado在Implementation或Synthesis阶段对FPGA逻辑中相连的两个D触发器做同步时序分析。
  • 同步时序分析——包括建立时间(Setup)方程,与保持时间(Hold)方程。所谓时序违例就是不满足这两个方程。
  • 时序违例(Timing Violation)与时序收敛(Timing Closure)是相反的意思。违例为不满足方程。
  • 亚稳态(Metastable)如果不满足同步时序方程,则有可能会出现D触发器(D filp-flop, DFF)无法锁存而引起额外‘裁决时间’,从而导致逻辑错误。
  • 时序例外(Timing exception)它或许是告诉STA某些路径不需要分析,放宽对于某些路径的时序要求,或许是告诉综合实施工具对某些路径进行特殊的照顾,因此我们叫它‘例外’。它包含multicycle / false path / max-min delay 等。详见后文#参考资料

未约束前的时序报告

创建一个Timing Report需要先进行Implementation。其实Synthesis后也可以生成Timing Report,但由于没有经过布线优化,它并不可靠。

还有一种生成报告的方式是在Tcl键入report_timing_summary

如上图,我的设计Setup与Hold都存在违例(表示为负的slack)。且Setup violation尤其严重,这在一般的设计中是非常常见的。而hold violation通常比较少见。

左侧显示有三个部分存在违例Intra-Clock / Inter-Clock / Other Path group。
Intra表明这个路径(Path)两端的DFF使用同一个时钟。后二者则为不同的时钟。

为什么要时钟约束?

这个内容我想通过#参考资料来学习会更加系统。不过这里还是简单提一下:

首先时钟约束可以对路径的时钟需求做出一定的优化,但是,它并不是万能的,set_max_delay等时序例外也仅仅是让implementation工具在一定程度优化锁存沿等。主要的logic delay和net delay(后文会提到)还需要设计者通过改变设计来完成。

第二时钟约束可以告诉STA它所不知道的事情,完善时序分析。譬如需要通过约束来告诉STA,哪些管脚有多少频率的时钟,哪些path是跨时钟域的等等,哪些path有例外的时序要求……如果你不告诉STA,那么它的分析可能就是错误的。

时钟约束主要分为定义时钟,与时序例外。但是时钟约束并不是解决时序违例的方法,更大程度上解决违例需要依靠合理的设计。

时序约束的第一步

首先是通过Constraint Wizard来按提示加入必要的约束内容。

它会按流程的指引你创建主时钟衍生时钟虚拟时钟,它们的概念请看#参考资料。通常来讲IPcore使用的时钟,以及PLL等时钟分频/倍频所衍生的时钟,其约束都是自动完成的,这里仅需要添加遗漏的部分即可。

但是请注意,有些RESET管脚等路径,约束向导也会建议你添加约束,此时忽略即可。

Intra-Clock

原因分析

阅读Timing report summary

打开Intra-Clock path 找到标红的path,点开他,看到Setup为负。选中其中一个path我们来分析一下。

slack通常翻译成松弛,我理解为‘裕度’的概念,即为距离不稳定情况还有多大的空间。为负则代表在最差情况下(T,V等因素的影响),就已经无法满足同步时序方程。

From/To代表这个Path连接了哪两个寄存器。由于通过原理图的方法我们很难定位,所以从源代码找到出现问题的位置就要靠它。

Logic delay和Net delay:出现setup violation的主要原因是delay过大,留给下一个时钟沿的建立时间不足。Logic代表path其中经过的组合逻辑延迟,Net代表其布局布线引入的延迟。本例中两种延迟都很高。

通过schematic与源代码来定位问题路径

选中其中一个路径,右键-schematic。如图出现了一个长条。放大我们发现,这其中是一系列的carry和lut。Carry是进位链(用于运算),LUT是查找表(fpga基本单元)。

我们双击这条path,打开它的具体信息,我们发现logic level非常高,这说明它占用的组合逻辑太大,基本可以确定这是造成setup violation的原因。

根据时序报告中的From 和 To我定位到了这条路径在源代码中的大体位置。以cmd_gap_reg_reg[8]为例,它的意思是在源文件中的cmd_gap_reg这个寄存器,后面的reg[8]则是在综合或实施过程中自动生成的。

我们在state_machine_inst.v中找到相关的代码。如下。

input [31:0] cmd_gap;
input [31:0] cmd_scale_frequency;

reg [31:0] cmd_gap_reg, cmd_scale_frequency_reg, gap;

always@(posedge SDMF_CLK_USER)
begin
    if(cmd_gap==0)  cmd_gap_reg<=1;
        else        cmd_gap_reg<=cmd_gap;
    if(cmd_scale_frequency==0)
                    cmd_scale_frequency_reg<=100;
        else        cmd_scale_frequency_reg<=cmd_scale_frequency;
    if(100*cmd_gap_reg/cmd_scale_frequency_reg==0)  gap <= 1;
        else gap<=100*cmd_gap_reg/cmd_scale_frequency_reg;
end

关于怎么找到逻辑过于庞大的点(对应源文件的语句),请见#附:找到path对应源代码

解决方法

优化运算逻辑

这部分是本人认为比较核心的内容。

阅读前文的代码,我使用了多个32位的寄存器,这些寄存器最终得到gap,需要乘法、除法计算判断条件,以及求解gap的值。在timing_report当中,这条路径具有最高的延迟也不足为奇。

我认为后面所述的方式可能能够解决问题,并且不复杂,但是优化逻辑是设计人员应当优先考虑的事情。结合我的拖缆模拟器实际需求,我将运算简化为:

reg [7:0] gap, cmd_gap, fraame_time_interval;

//gap只允许是以下值,对应不同的采样率。
assign gap = (cmd_gap!=1 && cmd_gap!=2 && cmd_gap!=4 && cmd_gap!=8
                && cmd_gap!=16 && cmd_gap!=32) ? 4 : cmd_gap;

// frame_time_interval 代表了在当前gap设置下,一个数据帧包含的时间长度(gap=1,则一个帧包含4ms时间内的数据。)
assign  frame_time_interval = gap * 4'd4;

太具体的内容我就不再阐述了。

Pipeline

既然大量的组合逻辑无法在一个时钟周期内完成,那么就用更多个周期来处理它。

如前所述,我原本的计算方法是对一个32位寄存器乘以100,再除以一个32位寄存器。由于这个计算需要大量进位,因此延迟非常高。

下面的代码是优化过后的pipeline方案。它将复杂的运算拆成多个时钟周期,以流水线的方式进行运算。

Set multicycle path

这是一种时序例外。它的原理是告诉STA,我这条路径上的组合逻辑不要求在1个时钟周期内运算完,我可以将它放宽到第‘5’个时钟的上升沿来锁存。同样,计算hold时间也会因此而自动的推延。

时序例外只是告诉STA去如何分析这条路径,而并没有对这条路径进行实质性的优化。这个概念我一开始也很迷茫。

在本例中,由于我的gap并不需要立刻更新到下一级寄存器,因此我完全可以i采用这种方式。

右击该path

Set false path

直接告诉STA你不要再分析这条路径了,这条路径的时序问题我自己解决。

是不是有点自欺欺人?

布局布线优化

通过Tcl布线优化命令,对这条路径的布线进行优化,它可以一定程度的减轻net delay。但是对于本例中由大量组合逻辑带来的延迟,可能没有很好的优化效果。

Inter-Clock

我的inter-clock path在timing_report中得到了非常糟糕的结果。但是我知道在跨时钟域的设计方面(尤其是自己通过一个异步fifo,或者ram等),经常会遇到一个问题:

没有指定Clock Group

在STA工具当中,除了一些Ip core可能预设的时序约束外,通常需要人为的告诉STA,哪些路径是跨时钟域(异步)的,这些路径的时序问题需要我们自己解决,无需STA工具再报警。

从设计上解决跨时钟域问题是FPGA设计者一定要掌握的技能,这里不再详述。

指定Clock Group就是解决上述问题的方法。
首先我们让vivado提供一份时序关系报告(Report Clock Interaction)

上图中展示了各个时钟(主时钟,衍生时钟,虚拟时钟等)直接跨时钟域连接的情况。黑色的部分就是不存在path。User Ignored蓝色部分,代表时序约束已经告诉STA忽略掉这部分的分析。红色部分则存在跨时钟域时序问题,需要我们解决。

现在我们需要手动的找出异步的时钟,并将其设置Clock Group

右击要设置的时钟块

当然还有一种更为优雅的方式,就是直接在Tcl中通过命令的方式,添加相关的约束,这种方式的好处是,你可以仅设置主时钟的Clock Group,通过指定“递归”,无需再对衍生时钟进行设置。

应用约束,重新报告时序,问题解决。


上文中主要介绍如何解决 setup violation 。而关于 hold violation 虽然出现频率不及前者,也是非常重要的一个问题。详见附2:fix hold time violation

参考资料


附:找到path对应源代码

上文中讲到可以通过schematic来定位源代码。这里介绍另一种方法:

双击这条path。

下滑到Data Path部分,会发现这里列举了这条路径所经过的所有组合逻辑,首尾是两个FDRE(DFF),中间对应的是各种LUT和CARRY。

如果需要知道这些组合逻辑都在源代码哪个位置,需要首先:

左击,Select Cell ***

在左侧Netlist当中,这个Cell将被选中,此时

右击,Go To Source

就能找到对应的源代码位置了。大功告成!

附2:fix hold time violation

原文

Description

When I perform a timing report, I notice that I have a Hold Time Violation. How do I fix it?

Solution

If the Hold Time Violation is associated with an OFFSET IN constraint, the data path is faster than the clock path. Either increase the delay associated with the data path or decrease the delay associated with the clock path.

To decrease the clock path delay, verify that the design is using the global clocking resources. You can also run PAR with a -k option, which tries to perform limited rip up and rerouting to solve problems.

If the Hold Time Violation is associated with a PERIOD constraint, the data path is faster than the clock skew. The resolution is similar to a Hold Time Violation in an OFFSET IN constraint, but decrease the clock skew instead of just the clock path delay.

To decrease the clock path skew, verify that the design is using the global clocking resources. You can also run PAR with a -k option, which tries to perform limited rip up and rerouting to solve problems.


RobertLiang

A post-graduate in USTC.

You must be logged in to post a comment