说到FPGA时序约束的流程,不同的公司可能有些不一样。反正条条大路通罗马,找到一种适合自己的就行了。从系统上来看,同步时序约束可以分为系统同步与源同步两大类。简单点来说,系统同步是指FPGA与外部器件共用外部时钟;源同步(SDR,DDR)即时钟与数据一起从上游器件发送过来的情况。在设计当中,我们遇到的绝大部分都是针对源同步的时序约束问题。所以下文讲述的主要是针对源同步的时序约束。
根据网络上收集的资料以及结合自己的使用习惯,我比较趋向于下面的约束流程方式:时序约束一共包含以下几个步骤:时钟约束、IO约束以及时序例外。这几个步骤应该可以解决我们设计当中绝大多数情况下的时序约束问题。 1.首先约束时钟。输入时钟,输出时钟。从种类 来看不外乎以下几种:单端输入时钟、差分输入时钟、GT或恢复时钟(例如LVDS信号恢复出来的时钟)、PLL产生的时钟以及自己产生的门控时钟。 2.IO约束。只有等待内部时钟完全通过后,再配置input delay和output delays,告知FPGA外部端口的数据时序关系。 3.时序例外。在约束完时钟以及IO后,还是有时序违例的时候,注意检查一下是否有时序例外的情况,例如多周期时钟路径、异步时钟、常量、以及互斥时钟路径等等。
下面我们就根据约束的流程来介绍一下每个步骤中的常用指令。
常用指令:Create_clock、Create_generated_clock、derive pll_clocks、create_virtual_clock。
对时钟的约束,首先要明确,我们要约束的时钟有哪些,然后针对不同的时钟进行约束。下面针对不同类型的时钟,对其约束的指令作简要分析。
单端输入时钟:
图1 单端输入时钟约束
这里用到了create_clock,一开始不熟悉语法的同学可以通过Timequest 的GUI界面或者Templete里面的模板里找到该指令。
差分输入时钟:
差分输入时钟,只需约束P端输入时钟即可,方法同上。
PLL产生的时钟:
针对PLL产生的时钟一般有两种方式。一种是通过derive pll_clocks即可,PLL会根据所设定的参数,自行约束输出时钟。这样做的好处就是,指令少,当PLL煽出的时钟比较多的时候,很有优势,不利的地方在于,PLL生产的时钟名字命名不可把控,区分度不明显,不利于后续的引用。所以我比较趋向于后面一种方式,是通过Create_clock、Create_generated_clock这两条指令完成。
图2 PLL生成的时钟约束
首先用create_clock指令对输入的时钟clk_in进行约束,然后通过create_generated_clock指令对PLL的输出时钟进行约束,这里PLL的输出时钟只是做了90°的相位偏移,频率不变。有童鞋可能会问,如果要倍频或者分频呢?该怎样写?说实话,一开始学的时候,我也记不得指令的格式。在这里,再一次强调,在初学的时候,没有必要去纠结语法。很多时候,约束的时候,只需标明用到的约束信息出来即可,再退一步讲,可以通过GUI界面产生相应的指令,你需要做的是明确相关的参数,知道怎么填就行啦,重点是理解时序约束的流程、应该怎么去约束才是重点。
GT或恢复的时钟
GT或恢复的时钟(例如高速串口过来恢复出来的时钟)针对这种情况,一般都是FPGA内部用IP核恢复出来时钟,约束格式同上。
图3 GT或者恢复时钟的约束
自己分频的时钟
(不建议这种方法,推荐PLL来产生,如果非要这样做,务必添加约束)
图4 自己生成的时钟
虚拟时钟create_virtual_clock
官网上的手册推荐在IO约束的时候,使用虚拟时钟,尤其是在约束输入延迟的时候。虚拟时钟表征的是上游器件内部用于输出数据的时钟(从另一个角度考虑,它表征的类似SDR模式中第一级D触发器的时钟)。通过约束告诉FPGA 的Input_Clock(实际上是Clock_Out)和虚拟时钟(数据)的相位关系。FPGA根据参数Input_delay在布线时进行调整使得布线满足时序要求。
图5 虚拟时钟关系
我们分边沿对齐和中心对齐两种模式对输入进行约束,对于边沿对齐的信号,我们通常会将其输入引脚输入值专用的PLL输入口,进行90°相移。约束如下所示:注意在对齐模式下,图6中对clk_in进行约束时不需要进行额外的移相,而仅对clock(经过PLL后的输出时钟)进行移相90°。
图6 边沿对齐输入时钟约束
create_clock-name virtual_clock -period 10
create_clock-name input_clock -period 10 [get_ports clock_in]
create_generated_clock-name plus_90_degrees -source [get_pins PLL|inclk[0]] -phase 90
对于中心对齐模式,其输入时序模型如图7所示,其约束图下方所示。一般而言,对于中心对齐模式,我们FPGA内部不会使用专门的锁相环对齐进行移相操作,但若依然使用PLL,上述边沿对齐对PLL的约束方法依然有效。但对clk_in的约束依然要表现出和virtual clock的相位关系。
图7 中心对齐输入时序约束
create_clock -namevirtual_clock -period 10
create_clock -nameinput_clock -period 10 [get_ports clock_in] -waveform {2.5 7.5}
注:1)复位信号的IO约束——看你的复位是同步复位还是异步复位,通常复位都会做成异步,这时候应该是false path的,如果同步,从管教输入的复位需要设最大输入延时。
2)对异步信号进行同步化(异步复位,同步释放),尽管reset_n已经进行了上述同步化处理,时序约束的时候还是要使用set_false_path命令将其进行切割,而从同步寄存器输出的复位rst_n现在可以使用TimeQuest进行准确地Recovery和Removal分析。
IO的约束主要是指input_delay与output_delay这两种,编译软件(ISE/Quartus)是个很强大而又很傻的工具,在设计的时候,你务必要告诉他在FPGA外部的信号时序关系,他才能够知道怎么去优化内部的时序,以满足时序设计要求。
从输入来看,无非有以下两种情况:SDR与DDR。
SDR是指,数据只在时钟的上升沿更新,而DDR是时钟的上升沿与下降沿都会更新。按照时钟与数据对齐方式来划分,又可以分为沿对齐与中心对齐两种。对于输入延迟的获取,一般来说有以下三种途径:文中直接给出Tco、Tdata等参数;通过查阅上级器件的数据手册;通过示波器来实测。查上游的器件手册(主要看Tsu 与Th),那么可以推算出,FPGA输入延迟的最值,为了方便描述,这里设定时钟与数据在PCB上的传输延迟一致:
Input delay max = T – Tsu;
Input delay min = Th;
当时钟与数据到达FPGA的延时不一致时,计算公式如下:
Input_delay_min = Th_min+ (T_data_max -T_clk_min)
Input_delay_max = T-Tsu_max+ (T_data_min -T_clk_max)
其中T_data是数据延时,T_clk是时钟延时,由此可见在PCB布线的时候,务必让时钟与数据的走线尽可能等长。这样不容易导致时序违例现象。(这个公式无论是中心对齐或者边沿对齐都适用,只不过中心对齐与边沿对齐他们的建立时间和保持时间计算不一样而已,稍后说明。) 其中T为FPGA用于采集上游器件发送过来的数据时钟。又或者直接通过示波器观察时钟与数据的延迟关系。有些情况,例如给定了相关数值的话(Tco ,data_delay等数值),可以直接算出输入延迟。跟上一篇计算数据到达时间里一样,这里就不展开论述。
图8 SDR中心对齐的输入输出延迟最值
图9 SDR边沿对齐的输入输出最值
回顾一下Tsu与Th的定义。setuptime :数据要能够被Latch Edge正确锁存,必须要在Latch Edge 到达之前保持稳定,这个提前到达的最少时间量,就是建立时间。
Hold time : 数据要能够被LatchEdge正确锁存,除了在Latch Edge到达之前提前准备好以外,还必须在Latch Edge到达后,保持稳定一段时间。这段保持稳定的时间,就是Hold time。图中DVW是指数据有效值宽度,图5中心对齐的情况下很好理解。对于图6中提到的边沿对齐情况,我觉得可以这么理解,在计算Tsu的时候,都是Latch时刻减去数据稳定起始沿时刻;而Th是数据稳定终止沿时刻减去Latch时刻。这么一算,不难可以得到在边沿对齐的情况下,保持时间Th为负的。代入公式可得,边沿对齐的情况下,input_delay_max=保持时间=-Th(这里的Th仅为数值,不带符号)。
图10输入延迟约束
图10中,左边是通过查阅上游器件数据手册得到的数据,右边的是用示波器测量得到的数据。
注:在对DDR的约束中,记得时钟下降沿约束的时候加上-clock_fall与-add_delay;上面列举的例程中,原著中都没有用到虚拟时钟,但是官网手册中也只是推荐使用,并没有说一定要用虚拟时钟。看个人喜好吧,如果要用虚拟时钟,可以这样约束,后续会列举一个完整的例子。
输出延时的分析与输入延时类似。这里设定时钟与数据在PCB上的传输延迟一致:
output delay max = Tsu;
output delay min = -Th;
当时钟与数据到达FPGA的延时不一致时,计算公式如下:
Output_delay_min= -Th_max + (Tdata_min-Tclk_max)
Output_delay_max=Tsu_max + (Tdata_max-Tclk_min)
其中,T_data是数据延迟;T_clk为时钟的延迟。
上述是DDR中心对齐输出的输出延迟约束。
上述是DDR边沿对齐输出的输出延迟约束。
SDR的输出约束方式类似,这里就不在累赘了。
时序例外一般用在clock与IO都约束后,还是不满足时序要求的情况下。主要包括以下几种情况:
1.多周期set_milticycle_path
(不推荐)
2.不需要检测路径(常见,重要)set_false_path
3.常量与伪常量
4. 互斥的路径
5.异步时钟(这种情况下,务必要确保逻辑上对异步时钟域的信号做了处理,例如打两拍,FIFO等手段处理)
5.组合电路延时,即逻辑不经过任何时钟处理就输出的情况。
个人比较取向与set_false_path与set_clock_groups-exclusive。