FPGA基础学习(12) -- 多周期路径约束

在我实际涉及的项目中,基本没有遇到多周期路径约束的情况,所以之前关注的不多,为了巩固基本知识,借此梳理这个约束。

1. 目的

目的就是说什么时候需要用到多周期约束?

Vivado、TimeQuest等时序引擎默认是按照单周期关系分析数据关系的,即数据在发起沿发送,在捕获被捕获,发起沿和捕获沿相差一个周期。

但是很多情况是,数据路径逻辑较为复杂,导致延时较大,使得数据无法在一个时钟周期内稳定下来, 或者数据可以在一个时钟周期内稳定下来,但是在数据发送几个周期之后才使用;在这些情况中,设计者的意图都是使数据的有效期从发起沿为起始直至数个周期之后的捕获沿,这样的意图无法被时序分析工具猜度出来,必须由设计者在时序约束中指明;否则时序分析工具会按照单周期路径检查的方式执行,往往会误报出时序违规;

说白了,就是根据用于设计需求,改变原有的时序检查机制,从而避免非真实的时序违例或者时序过紧导致的资源浪费。

2. 单周期时序检查

如上图所示,为时序引擎默认的单周期检查机制。确定建立时间路径和保持时间路径规则如下:

  • 建立时间路径

以第一个发送沿为基准(current launch),再向后寻找距此发送沿最近的一个捕获沿(current capture),并将两者的setup定为1个周期。

  • 保持时间路径

每确定一个建立路径,都会检查两个保持时间路径:1)确保当前发送沿推出的数据不被上一个捕获沿给捕获,即hold1;2)确保下一个发送沿推出的数据不被当前捕获沿给捕获,即hold2。对这描述,为了便于分析记忆,简而言之,1)previous capture与current launch 构成一组检查;2)current capture与next launch 构成一组检查。

对于两条保持时间的检查,时序报告最终只会给出裕量最小的一条。此外,还需要记住:一旦确定建立时间路径,保持时间路径会自动根据规则做调整。

3. 使用方法

对于多周期路径的约束语法,就一条指令:set_multicycle_path

set_multicycle_path <path_multiplier> [-setup|-hold] [-start|-end]

[-from ] [-to ] [-through <pins|cells|nets>]

参数名称 含义
-setup 表示建立时间所需要的时钟周期个数
-hold 表示分析保持时间时,相较于默认的捕获沿,实际捕获沿偏差的时钟周期个数
-start 表示以目的端时钟作为时钟周期计数基准
-end 表示以源端时钟作为时钟周期的计数基准

在默认的情况下,set_multicycle_path

  • 对建立时间的分析是设置目的时钟为多周期,即-setup 与-end 搭配使用;
  • 对保持时间的分析是设置源时钟为多周期,即-hold与-start搭配使用;

注意,上述是默认情况,-end和-start可以省略。根据场景不同会有所不同,并且数周期个数是往前还是往后也会不同,下面会介绍。

知道了命令参数的含义,结合第2节中的路径规则的文字描述,那么默认检查机制就应该是下面的表达:

set_multicycle_path 1 -setup -end -from [get_clocks s_clk] -to [get_clocks d_clk]
set_multicycle_path 0 -hold -start -from [get_clocks s_clk] -to [get_clocks d_clk]

实际上这个约束的意义,就是使引擎按照设计者的逻辑来重新定义路径分析的规则。如果没有深刻理解多周期路径,是不知道怎么使用的。为什么有的时候只需要setup,有的时候又需要setup和hold,怎么加-start和-end又不清楚。所以结合文档给出的4中案例来加深对多周期路径的理解,并且学会约束语句的使用。

4. 案例

4.1 同一时钟域

在同一时钟域下,有的时候,我们希望第一个发送沿推出数据,经过N各周期才被被第二个寄存器的捕获沿给捕获。最常见的就是,时钟使能控制数据捕获的情形。

如上图所示,假设使能信号周期为驱动时钟的2倍,显然如果按照默认的规则进行时序检查,就会发生错误。

首先,修改建立时间路径,使用语句

set_multicycle_path 2 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]

表示以目的时钟为基准,往前数,第2个沿作为捕获沿

根据默认规则,保持路径会自动调整,如图AFTER。显然这个时候的捕获沿的确定是不合适的,会导致保持时序过紧(实现工具会通过增加数据延迟的方式,来优化时序,从而导致资源浪费,功耗增加)。所以增加修改hold规则语句:

set_multicycle_path 1 -hold -end -from [get_pins data0_reg/C] \
-to [get_pins data1_reg/D]

表示以目的时钟为基准,相较于默认的捕获沿,往后数1个周期的沿作为实际的捕获沿

综上,得到最终的路径关系,如下图所示:

同理,针对setup延迟5个周期的情况,修改setup规则:

set_multicycle_path 5 -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]

随着hold关系的自动变化,得到如下图所示的情形。

修改hold规则,约束语句如下:

set_multicycle_path 4 -hold -end -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]

表示以目的时钟为基准,相较于默认的捕获沿,往后数4个周期的沿作为实际的捕获沿,如下图所示

还有一种修改hold的方式,将-end改为-start,对应的关系如下图所示。

那么上面两种情况有什么区别?实际上,是一样的,因为两时钟为同一时钟域。所以针对同一时钟域,可以不考虑基准,即约束语句可以忽略-end或-start。但是设计者一定要心里清楚。

针对同一时钟域的多周期路径,一般语句可表示为:

set_multicycle_path N -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path N-1 -hold -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]

4.2 两时钟同一周期,但有相移

对于发送和捕获的两时钟是同周期,但存在相移。有两种情况:clk2较clk1,正相移;clk2较clk1,负相移。


4.2.1 正相移

如下图所示,clk2较clk1偏移+0.3ns。根据默认的检查规则,建立和保持路径已经在图中表示出来。

可见,数据要在0.3ns之内满足建立时间,这太紧了。反之,数据给的保持时间却是-3.7ns,对于时序检查又太宽松。先对建立时间放宽一些,使捕获沿往后沿一个周期,约束如下:

set_multicycle_path 2 -setup -from [get_clocks CLK1] -to [get_clocks CLK2]

随着建立路径的修改,自动判断新的保持路径,如上图所示。此时不需要在对保持路径进行调整了,所以这种情况只需要修改建立路径规则即可

4.2.2 负相移

如下图所示,clk2较clk1偏移-0.3ns。可见,此时的建立路径和保持路径即不太紧又不太松,可不进行多周期约束。


综上,对于同周期不同相位的两时钟分析,必须要考虑相移方向和相移量。对于负相移,也不是绝对的不用添加约束,如果负相移量太大,实际上就可视为正相移,同样需要添加约束。所以视情况而定。

4.3 从慢时钟到快时钟

假设捕获时钟为发送时钟频率的3倍,默认的检测机制如上图所示。在实际设计中,捕获寄存器肯定需要一个使能信号,才能合理捕获数据,这个时候会用到多周期路径约束。同样先修改建立路径规则:

set_multicycle_path 3 -setup -from [get_clocks CLK1] -to [get_clocks CLK2]

随着建立路径的修改,自动判断新的保持路径,如上图所示。这个时候的保持路径检查,会导致消耗过多的资源,增加面积,增加功耗,所以对保持路径进行修改:

set_multicycle_path 2 -hold -end -from [get_clocks CLK1] -to [get_clocks CLK2]

注意,这种情况和同周期时钟的多周期路径约束有相似的地方。但是此时-hold一定要增加-end,表示以目的时钟为基准,较默认路径,往回数2个周期作为捕获沿

综上,对于慢时钟到快时钟的情况,一般约束如下:

set_multicycle_path N -setup -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]
set_multicycle_path N-1 -hold -end -from [get_pins data0_reg/C] -to [get_pins data1_reg/D]

4.4 从快时钟到慢时钟

如上图所示,clk1是clk2频率的3倍。原本默认检查规则,满足setup检查的有3条路径(有两条是红线补充),因为最终时序报告只会给出最苛刻的一条,所以只保留黑色这条setup路径。

针对这种情况,需要同时修改建立路径和保持路径:

set_multicycle_path 3 -setup -start -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path 2 -hold -from [get_clocks CLK1] -to [get_clocks CLK2]

分析这两条约束语句:首先,-setup必须搭配-start,因为必须要以源时钟为基准,再往后数;

然后,根据默认规则,保持路径自动调整为红色的路径;最后,约束hold,因为hold默认搭配-start,所以以源时钟为基准,往前数了2个时钟沿。

综上,对于快时钟到慢时钟的情况,一般约束如下:

set_multicycle_path N -setup -start -from [get_clocks CLK1] -to [get_clocks CLK2]
set_multicycle_path N-1 -hold -from [get_clocks CLK1] -to [get_clocks CLK2]

综上所有案例,可总结到下图中:

5. 补充说明

  1. 关于setup 和hold 搭配start和end之后,是往前数还是往后数周期个数

一开始被高老师的《vivado从此开始》中有关多周期路径约束讲述的一点误导了,在对-hold参数描述时,是这样写的

-hold 表示分析保持时间时,相对于默认的捕获沿实际捕获沿应回调的时钟周期个数

“回调”二字我理解偏了,以为有关hold,不论-start还是-end都往回数周期,结果始终分析不出正确的结果。最后细读UG903,才看到下面一图。

所以,不论是setup还是hold,都可能往前数或者往后数,看具体搭配的是-start还是-end

  1. 时序过松、过紧?资源又过渡消耗?

对于多周期路径约束的使用,我们无非就是要解决由于默认的规则导致时序误判。但是什么时候觉得过松或者过紧呢?实际上要深刻理解时序分析原理之后,灵活应用于多周期路径分析。

如下图所示,默认的检查规则:

建立时间和保持时间裕量计算公式如下:

setup slack = (current latch edge - current launch edge ) + Tskew - (Tdelay + Tco + Tsu)
= T + Tskew - (Tdelay + Tco + Tsu)
hold slack = Tdelay + Tco - (Th + previous latch edge - current launch edge)
= Tdelay + Tco - Th

以4.1节两时钟是同一时钟域的情况举例,建立路径延后1个周期,如下图所示:

代入公式得到:

setup slack = (current latch edge - current launch edge ) + Tskew - (Tdelay + Tco + Tsu)
= 2*T + Tskew - (Tdelay + Tco + Tsu)
hold slack = Tdelay + Tco - (Th + previout latch edge - current launch edge)
= Tdelay+Tco - (T+Th)

可见,建立时间裕量增加了(多加了一个周期T),而保持时间裕量减少了(多减了一个周期T),这就导致保持时间过紧,实现的过程中需要尽可能增加Tdelay,这样又会导致资源过度消耗,所以需要调整保持时间。

另一种情况,4.2.1 正相移那节中,如果hold路径为负-3.7ns,根据公式,我的理解是保持裕量增加太多(减掉一个负数),导致时序太宽松。定性的理解就是,由于上一个捕获沿在当前发送沿的左边(正相移导致),所以当前推送的数据,不太可能被上一个捕获沿所捕获。

参考文献

  1. 换位思考多周期约束

  2. Vivado使用技巧(19):多周期路径

  3. 《Vivado从此开始》

  4. 《UG903》

posted @ 2020-03-14 14:11  肉娃娃  阅读(4147)  评论(1编辑  收藏  举报