在开篇前先推荐两篇文档,一篇是altera的官方文档 Appling Multicycle Execptions in the TimeQuest Timing Analyzer ,另一篇是riple兄很早之前推荐过的Multicycles Exception Between Two Synchronous Clock,这两篇都是关于多周期约束很好的上手文档,虽然可以快速上手解决当务之急,但事后不免还会有些疑惑。TimeQuest中的多周期约束语法的设置选项有:基于setup(建立时间)的个数,基于hold(保持时间)的个数,start模式和end模式,如下所示。
set_multicycle_path -setup -start from [get_clocks {clk_s}] to [get_clocks {clk_r}] 2
set_multicycle_path -hold -end from [get_clocks {clk_s}] to [get_clocks {clk_r}] 2
但在使用过程中,为什么有时只设置基于建立时间的多周期个数就可以了?而为什么有时建立时间和保持时间的多周期个数都要设置?保持时间设置和建立时间设置存在什么依赖关系?为什么会有start和end两种模式,分别在什么情况下使用?我们是TimeQuest的用户,用户使用产品时只会去关心自己的需求,而用户成千上万,需求也千奇百怪,产品设计者为了满足所有用户,必须提高产品的通用性,最简单的方法就是增加选项模式,当一个需求简单的用户使用这个通用产品时,面对多重的选择和模式,往往会无从下手,因为根本就不知道这些选项设置的作用,自然就会产生上述的那些为什么。要解答这些为什么,我们可以试着换位思考,从TimeQuest设计者的角度出发,如何设计多周期约束的语法规则?
明确设计目的
先来看看一个最常见的发送接收波形如图一所示,A时刻发送的数据B时刻锁存,launch(发送)边沿和latch(锁存)边沿间隔为一个时钟周期。
图一
这种情况下,建立时间和保持时间裕量为
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
其中Tskew是时钟偏斜,Tdelay为传输延迟,Tco为源寄存器的输出延迟,Tsu和Th为寄存器需要的建立保持时间。
如果接收波形整体延迟一个时钟周期,如图二所示,latch边沿改变了,A时刻发送的数据C时刻锁存,B时刻发送的数据D时刻锁存。
图二
此时,我们期望的建立时间和保持时间裕量为
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)
和前一种情况比较,建立时间裕量宽裕了一个周期,保持时间裕量缩紧了一个周期,但TimeQuest并不知道latch边沿变了,如果不进行多周期约束,仍按照前一种情况的裕量去约束,结果就会使得建立时间多了没必要的裕量,而保持时间的裕量可能不足。所谓的多周期是相对单周期而言的,launch和latch默认间隔为单周期,当launch和latch间隔为多个周期时,就需要用到多周期约束了,一个完整的时序分析路径是由Tskew、Tco、Tdelay、Tsu、Th以及launch时刻和latch时刻等共同决定的,后两项会随着用户不同的需求而改变,TimeQuest无法自动获得,所以多周期约束语法规则设计的目的就是准确的告诉TimeQuest波形何时launch何时latch,进行适当的建立保持约束。
明确TimeQuest的需求
我们已经知道了TimeQuest无法自己获取launch时刻和latch时刻,必须由用户提供,那么TimeQuest具体需要哪些信息才能完全了解发送接收波形呢?
举一个典型的多周期波形,如图三所示,A时刻发送的数据B时刻锁存,B时刻发送的数据C时刻锁存,A为current launch,B即为current latch又是next launch,C是next latch。
图三
图三的发送接收关系,我们可以表示为下面几个式子:
current latch - current launch = 2个时钟周期
current latch - next launch = 0个时钟周期
previous latch - current launch = 0个时钟周期
由于波形的周期性,后两个表达式是完全等效的,图二的发送接收关系同样可以表示为:
current latch - current launch = 2个时钟周期
current latch - next launch = 1个时钟周期
previous latch - current launch = 1个时钟周期
将这几个表达式推广到更多不同的例子,会发现只要知道了current launch、current latch、next launch和previous latch就能确定任何形式的发送接收波形,因此TimeQuest只需获得这四个量,也就清楚波形的真实情况了。
明确用户的需求
假设用户的波形如图二所示,相比常见的图一单周期波形,建立时间宽裕了一个周期,而保持时间紧缩了一个周期,作为用户,其实并不关心current launch、current latch、next launch和previous latch这些量,只会关注如何把变化了的建立保持关系告知TimeQuest,让其在该放松的地方放宽约束,该紧缩的地方加强约束。
整合TimeQuest和用户的需求
多周期约束的作用是让TimeQuest明晰发送接收波形的真实状态,放宽或缩紧建立和保持时间约束,其中TimeQuest期望从用户那得到current launch、current latch、next launch和previous latch这些量,而用户希望告诉TimeQuest真实的建立保持关系。要将用户的诉求准确的转化为TimeQuest的需求,设计者需在两者中做出权衡,所以在多周期语法规则的设定上,不能完全偏袒某一方,双方都要有些取舍。
在语法的选词上,设计者倾向于用户,使用了通俗易懂的setup和hold,表示建立时间和保持时间对应的宽松周期个数,比如下面的语句表示建立时间宽松2个周期,保持时间宽松1个周期。
set_multicycle_path -setup -end from [get_clocks {clk_s}] to [get_clocks {clk_r}] 2
set_multicycle_path -hold -end from [get_clocks {clk_s}] to [get_clocks {clk_r}] 1
宽松的周期个数是要相对默认路径而言的,为了准确的将setup和hold宽松个数准确的转化为current launch、current latch、next launch和previous latch这些量,设计者制定了倾向于TimeQuest的语法规则,用于默认情况下的current launch,current latch,next launch和next latch的定位。
分析默认的建立时间路径规则。TimeQuest先以一个launch边沿为基准定为current launch,再向后寻找距此边沿最近的一个latch边沿定为current latch,并将两者的setup个数定为1个周期。
图四红色线条为发送和接收波形无相移时默认的建立时间路径。
图四
图五红色线条为发送和接收波形存在相移时默认的建立时间路径,虽然A和B间距不满一个周期,但TimeQuest为了方便处理,仍将此时的setup定为1个周期。
图五
由于默认的建立时间路径setup已被设为1一个周期了,如要宽松1个周期,须在默认的基础上再叠加1个周期,置setup = 2,current latch才会往后移动一个周期,如上图蓝色线所示,结合默认分析规则和setup的个数,TimeQuest就可以定位current launch和current latch。
分析默认的保持时间路径规则。保持时间的检查路径会有两条,一条是current launch到previous latch,另一条是next launch到current latch,当current launch和current latch确定后,TimeQuest会基于单周期分析,默认current launch的后一个时钟为next launch,current latch的前一个时钟为previous latch,并将两条保持时间路径的默认hold个数都设为0,如图六所示。
图六
如保持时间宽松1个周期,如图七所示,previous latch会向前移动一个周期,由于波形的周期性,next launch也会向后移动一个周期。
图七
结合setup和hold个数以及单周期分析准则,TimeQuest可以知晓previous latch和next launch。由上分析可得,保持时间默认路径是相对current launch和current latch而定的,和建立时间路径存在着间隔单周期的依赖关系,会随着建立时间路径变化而改变。回到开篇的一个疑问:为什么有时只要基于建立时间设置多周期个数就可以了,因为setup设定后,保持路径也会相应改变,如果此时刚好满足用户需求,就不用再设置hold了。
至此,依据设计者制定的规则,已经可以将用户提供的setup和hold周期个数转换为TimeQuest想要的launch和latch信息了,虽然规则不算最直观,但考虑到要满足所有用户的需求,通用性已然成为设计的首要目标(规则越简单,通用性越强,在分析setup,hold时默认基于单周期的分析法则真的不能再简单了),而用户体验在FPGA这个垄断性的行业,苦逼的工程师们真的不要奢望太多。
消除不确定性
上述所举例子的发送接收波形的频率都是相同的,如果频率不同,现有的语法规则就可能出现不确定问题了,如图八所示。
图八
仍按照默认的setup路径分析方法,先找一个launch边沿为基准,再去确定之后最近的latch边沿,但会发现存在两个基准,launch边沿A和B对应的最近的latch边沿都是C,launch A到latch C以及launch B到latch C的setup值都为1个周期,此时,TimeQuest无法确定用户波形到底是哪一种。
为了消除歧义,换另一种分析方法,先找一个latch边沿为基准,再确定之前最近的launch边沿,这样current launch和current latch的关系就一一对应了,如图九所示。
图九
两种方法的区别在于基准的选择。根据基准的不同,设计者创造了两种模式start和end,start模式参考的基准是latch边沿,end模式参考的是launch边沿,对于同频波形,不存在不确定性,无所谓使用哪种模式,当发送频率比接收频率快一倍以上时需用start模式,同理,当接收频率比发送波形快一倍以上时使用end模式。
综上,经过一个产品设计的大致思考流程(明确产品目标---> 整合用户需求 -----> 准确表达需求)后,我对TimeQuest多周期约束有了更深的认识,不仅知其然,而且知其所以然。其中基于单周期的分析规则对我也有些启发,规则如此简单,但的确能满足要求,当时在分析默认的保持时间路径时,我自己也尝试着想了些规则,但都是从方便用户的角度出发,忽视了TimeQuest的需求,不仅复杂,通用性还不好,可见对需求挖掘的深浅直接影响着产品的复杂度。
从用户角度出发,追求用户体验,这些道理经常被耳提面令,但工欲善其事,必先利其器,在开发过程遇到的一些开发工具的问题,我们可以尝试下换位思考,从设计者的角度出发,往往除了能解决开发工具的疑惑,还能收获一些其他设计者好的思路并应用到自己的设计中。
最后再无力的吐槽一句:一边使用着用户体验极差的开发工具,一边却想着开发用户体验极佳的产品,真的有点为难工程师们了。(TimeQuest,我不是在说你,你已经很不错了)