前言

本文摘自《FPGA之道》。

根据时序报告修改设计

时序分析报告示例

让我们仍以ISE自带的时序分析工具为例,来看一个非常简单的时序分析报告示例。

待分析设计

这里选择了一个非常简单的FPGA设计来进行时序分析,虽然其内部逻辑和接口都非常简单,但麻雀虽小五脏俱全,通过它可以使我们对时序分析有一个更具体的了解。其HDL代码描述如下:

	-- VHDL example 
	library IEEE;
	use IEEE.STD_LOGIC_1164.ALL;
	use IEEE.STD_LOGIC_ARITH.ALL;
	use IEEE.STD_LOGIC_UNSIGNED.ALL;

	entity Dut4Timing is
	port (
	   clkIn : in std_logic;
	   DinA, DinB : in std_logic_vector(7 downto 0);
 	   clkOut : in std_logic;
	   Dout : in std_logic_vector(7 downto 0);
	);
	end Dut4Timing;

	architecture Behavioral of Dut4Timing is
		signal mid : std_logic_vector(7 downto 0);
	begin

	process (clkIn) 
	begin  
   if (clk'event and clk = '1') then
   	mid <= DInA and DInB;
		Dout <= mid;
   end if;
	end process;

	clkOut <= not clkIn;

	end Behavioral;
// Verilog example
module Dut4Timing(
	input clkIn,
	input [7:0] DinA, DinB,
	output clkOut,
	output reg [7:0] DOut
);

reg [7:0] mid;

always@(posedge clkIn)
begin
	mid <= DinA & DinB;
	DOut <= mid;
end

assign clkOut = ~clkIn;

endmodule

时序约束文件

针对当前待分析设计,给出ucf中的时序约束描述如下:(注:“#”在ucf文件中表示注释)
#时序环境约束,设定为某款FPGA芯片的最大工况

TEMPERATURE = 85 C;
VOLTAGE = 0.95 V;

#对clkIN端口添加周期约束

NET "clkIn" TNM_NET = clk50MHz;
TIMESPEC TS_clk50MHz = PERIOD "clk50MHz" 20 ns HIGH 50%;
    #由于设计非常简单,所以对全局添加输入约束
	OFFSET = IN 20 ns VALID 20 ns BEFORE "clkIn";

	#由于设计非常简单,所以对全局添加输出约束
	OFFSET = OUT 10 ns AFTER "clkIn";

	#由于设计非常简单,所以对全局添加焊盘到焊盘路径约束
	TIMESPEC TS_P2P = FROM PADS TO PADS 20 ns;

时序报告简介

关于本例的时序分析报告简介如下:

报告综述

如下为本次时序分析报告的一个综述:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_其他
从这段简短的综述中,我们可以了解到本次时序分析有没有发现一些时序冲突的地方,以及一些系统的极限延迟和时钟速率。当然,相比于这个综述,我们可能更关心针对我们所加的那些时序约束的详细时序报告部分,其总体截图如下:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_建立时间_02
从上图可以看出,针对于之前小节中的4类时序约束,报告中分别给出了4个时序分析子项,并且,从时序报告的字体颜色来看,本次时序分析的结果全部满足时序约束的要求(如果某条时序约束不能满足,则会以红色字体或者非常显眼的小红叉凸显)。那么在接下来的几个小节中,就让我们来具体看一下这些时序分析报告。

内部时钟报告

内部时钟报告主要关心的是内部寄存器的建立和保持时间,它针对的是【时序约束文件】小节中的周期约束。其截图如下:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_数据_03
从上图中,可以看出关于内部寄存器的建立和保持时间,各列举出了最差的3个(数量跟设定有关)情况,下面我们分别来看一下其中最差的一个。

建立时间最差情况的细节报告如下所示:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_时序分析_04
从该详细报告我们可以看出,该FPGA芯片内部寄存器的建立时间要求为0ns,所以只要d-clk延迟差小于一个时钟周期即可,故requirement为20ns。而建立时间余量slack就是基于requirement、d-clk延迟差以及一些时钟不确定因素所得出的(报告中附有计算公式)。针对于本次时序分析来说,内部寄存器最差建立时间所对应的锥顶寄存器为DOut_5,锥底寄存器为mid_5,两者之间的最大路径延迟为1.113ns(详细路径信息见细节报告的最后部分),时钟skew为-0.021ns(负值表示时钟边沿先到达锥顶),时钟uncertainty为0.035ns(可以发现影响时钟不确定性的主要因素是jitter,通过其计算公式,可以看出ISE关于jitter因素的考虑特点)。因此,建立时间的slack为18.831ns。
注:计算公式为T-T_delay+T_skew-T_uncertainty = 20 -1.113-0.021-0.035=18.831ns
由于建立时间为0,所以,上面没有减去建立时间。

保持时间最差情况的细节报告如下所示:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_数据_05
从该详细报告我们可以看出,该FPGA芯片内部寄存器的保持时间要求为0ns,所以只要d-clk延迟差大于0ns即可,故requirement为0ns。而保持时间余量slack就是基于requirement、d-clk延迟差以及一些时钟不确定因素所得出的(报告中附有计算公式)。针对于本次时序分析来说,内部寄存器最差保持时间所对应的锥顶寄存器为DOut_2,锥底寄存器为mid_2,两者之间的最小路径延迟为0.615ns(详细路径信息见细节报告的最后部分),时钟skew为-0.003ns(负值表示时钟边沿先到达锥顶),时钟uncertainty为0 ns(因为保持时间是锥底、锥顶寄存器相对于同一个时钟边沿来说的,所以不受jitter的影响)。因此,保持时间的slack为0.618ns。
计算方式为:slack=T_delay-T_skew= 0.615+0.003 = 0.618ns
这里的时钟偏斜一般指的是正时钟偏斜计算方式,也就是目的寄存器时钟减去发射寄存器时钟。

输入接口报告

输入接口报告主要关心的是采集输入信号寄存器的建立和保持时间,它针对的是【时序约束文件】小节中的输入约束。其截图如下:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_数据_06
从上图中,可以看出关于采集输入信号寄存器的建立和保持时间,各列举出了最差的3个(数量跟设定有关)情况,下面我们分别来看一下其中最差的一个。

建立时间最差情况的细节报告如下所示:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_时序分析_07
其中各项参数、数据的意义可参考【内部时钟报告】小节理解。如下再附上关于本最差建立时间所对应的数据和时钟路径的详细信息:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_数据_08

保持时间最差情况的细节报告如下所示:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_时序分析_09
其中各项参数、数据的意义可参考【内部时钟报告】小节理解。如下再附上关于本最差保持时间所对应的数据和时钟路径的详细信息:

输出接口报告

由于ISE自带时序分析工具不支持直接针对输出时钟与数据端口添加约束,所以我们只能分别约束数据和时钟输出,并结合时序报告来人工计算出输出接口的最终时序报告。因此,对于ISE自带时序分析工具来说,其对于输出端口所自动生成的分析报告无论报错与否,都不能说明同步输出接口是否工作正常。

输出数据延迟报告

输出数据延迟报告主要关心的是数据输出端与驱动其的时钟端之间的延迟情况,它针对的是【时序约束文件】小节中的输出约束。其截图如下:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_时序分析_10
从上图中,可以看出关于数据输出端与驱动其的时钟端之间的延迟,各列举出了最大(慢)、最小(快)的3个(数量跟设定有关)情况,下面我们分别来看一下其中最差的一个。

最大延迟情况的细节报告如下所示:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_建立时间_11
此处的requirement取决于之前在输出约束中的设定,等于10ns。此时我们并不关心slack是否为正值,而是关心数据输出端与驱动其的时钟端之间的最大延迟为9.086ns(requirement - slack)。如下再附上此时的最大数据路径延迟和最大时钟路径延迟的详细信息:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_时序分析_12

最小延迟情况的细节报告如下所示:

FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_其他_13
此时数据输出端与驱动其的时钟端之间的最小延迟为6.431ns。如下再附上此时的最小数据路径延迟和最小时钟路径延迟的详细信息:

输出时钟延迟报告

输出时钟延迟报告主要关心的是时钟输出端与时钟输入端之间纯组合逻辑路径延迟的情况,它针对的是【时序约束文件】小节中的焊盘到焊盘路径约束。其截图如下:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_时序分析_14
从上图中,可以看出关于输出时钟的最大、最小(对应于hold path)延迟,各列举出了最差的1个(数量跟设定有关,但当实际数量小于设定上限时,以实际数量为准)情况,下面我们分别来看一下。

最大延迟情况的细节报告如下所示:

FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_其他_15
此处的requirement取决于之前在焊盘到焊盘路径约束中的设定,等于20ns。此时我们并不关心slack是否为正值,而是关心时钟输出端与其对应的时钟输入端之间的最大延迟为5.151ns(requirement - slack)。

最小延迟情况的细节报告如下所示:
FPGA之道(81)静态时序分析(七)根据时序报告修改设计(基于ISE的UCF文件语法)_建立时间_16
此时,时钟输出端与其对应的时钟输入端之间的最小延迟为4.770ns。

同步输出综合报告

基于前两个小节的时序报告结论,本小节将给出该如何人工计算出该纯同步输出接口的最终时序报告。
首先,从功能上来分析(通过功能仿真或阅读代码),可知理想情况下,输出数据相对于输出时钟的建立时间、保持时间分别为10ns(时钟周期20ns,输出时钟上升沿对应数据中间)。
接下来,先求解考虑实际延迟后的建立时间。通过阅读前两个小节中的时序分析报告,可以得出输出数据端口相对于输入时钟端口的最大延迟为9.086ns,输出时钟端口相对于输入时钟端口的最小延迟为4.770ns,故实际建立时间应该为5.684ns(10 - 9.086 + 4.770)。
而对于实际保持时间,由于输出数据端口相对于输入时钟端口的最小延迟为6.431ns,输出时钟端口相对于输入时钟端口的最大延迟为5.151ns,故实际保持时间应该为11.28ns(10 + 6.431 - 5.151)。
最后,再结合后续外围电路对DOut与clkOut之间的实际需求,即可得出关于该纯同步输出接口的最终时序分析报告。

常见问题及修改

尽管时序分析所面临的情况多种多样,但出现的问题无外乎是建立时间不足、保持时间不足、路径延迟过大(小)以及这三者之间的任意组合。而要解决这些问题,需要具体情况具体分析,本章节就为大家介绍一些常用的时序问题修改手段。

可以不改的一些问题

有些情况下,时序分析报告中出现的问题是可以不必修改的。例如,有逻辑功能保证的跨时钟域时序冲突;同步输入、输出接口的时钟数据匹配错位但不会采样到不稳定期,且时钟本身连续;内部多周期路径未被时序约束说明导致建立时间不足;等等。
类似以上这些时序问题,均不会影响到FPGA设计在正常工作时的行为,所以通常不必对此进行修改。但是,如果你对时序分析报告存有“洁癖”心理;如果你希望减轻编译器在这些无关问题上的时间消耗;又如果对于一些特殊场合,当时序分析出问题时就不会生成最终的驱动文件(例如National Instruments公司的LabVIEW FPGA开发工具,它通过内部调用ISE进行编译,虽然ISE会顺利生成bit文件,但当出现时序问题时,LabVIEW FPGA将不会继续生成可供LabVIEW程序所调用的lvbitx文件);等等。那么此时,你就得想办法来去除这些令人讨厌的时序冲突问题。由于此时FPGA设计的功能行为并没有什么问题,因此你需要从完善时序约束的角度出发,去看看是不是哪里漏掉了一些时序约束(例如,该忽略的路径忘记添加相应的跨时钟域忽略约束,多周期路径忘记添加多周期路径约束等等),哪里多加了一些不必要的时序约束(例如,周期约束过于严格,工作时钟只有50MHz,可是却按照100MHz来约束),并以此来达到最终的时序收敛(即时序分析全部通过)。

常见时序收敛的手段

除了上一小节中介绍的那些情况,其他时序冲突问题基本上都是需要修改的。而将一个具有时序冲突问题的FPGA设计修改到能够顺利通过时序分析的过程则叫做时序收敛,接下来就为大家介绍一些常见的时序收敛手段。

逻辑化简&结构调整

逻辑化简和结构调整这两种手段对于解决建立时间不足以及路径延迟过大这两类时序冲突问题非常的有效。具体做法可以参考【程序设计篇->编程思路->时空变换->时空变换之时域优化】->【逻辑化简】、【结构调整】两个章节中的内容。

分布调整

分布调整对于解决建立时间不足有很好的效果,其具体做法可以参考【程序设计篇->编程思路->时空变换->时空变换之时域优化->分布调整】章节中的内容。

缓存降频复用&逻辑拆分&流水线&使能链

这四种手段都是解决建立时间不足的好方法,它们的具体做法可以参考【程序设计篇->编程思路->时空变换->时空变换之空间换时间】->【缓存降频复用】、【逻辑拆分】、【流水线】、【使能链】四个章节中的内容。

模块输入、输出寄存处理

如果你的FPGA设计对于若干个时钟周期的群延迟不是特别在意的话,可以使用模块输入、输出寄存处理的手段来改善模块间数据传递的建立时间。这是由于每个模块内部的这些逻辑往往相关性更强一些,所以FPGA在布局的时候也更倾向于将它们放在一起,但是模块与模块之间可能就会离得比较远,此时线延迟会显著增大。如果对于每一个模块,都能做到输入、输出都加一级寄存器,那么从前级模块的输出到后级模块的输入就只剩下线延迟了,这样将给模块间的布线延迟留有更多的余量。如果为模块的输入、输出添加更多级的寄存器,效果会更好,因为编译器可以利用多余级数的寄存器来切割线延迟,不过需要权衡此类资源的消耗仅能带来模块间数据传递时的建立时间改善。

时钟上树

时钟树存在的意义就是要尽量保证时钟到达各个寄存器的延时差(skew)尽可能的小,这也是为什么通常FPGA设计的建立时间跟时钟周期密切相关,但保持时间却不受其影响。但是,一旦你在使用时钟信号之前,忘记让它先通过时钟树网络,则时钟信号到达各个寄存器的路径长、短便不可控,由于此时时钟、数据都是通过最基本的布线资源进行布线,从而很可能会出现时钟延迟差大于路径延时的情况(也就是我们常说的数据传的比时钟还快),这样一来,便容易出现保持时间不足的时序冲突情况。
对于内部时钟域来说,保持时间不足十有八九是由于时钟信号没有上树,因此当出现这类问题时,首先应该仔细检查一下是否正确的使用了时钟信号。而对于接口部分的保持时间不足,则依赖于数据和时钟的原始(或理想)相位差,以及时钟即数据的延迟差,不属于此范畴。

DCM、PLL相位调整

DCM、PLL这两个时钟处理模块,其输出时钟的相位是可以配置的,因此,无论是输入接口、输出接口还是内部时钟域,都可以利用这一点来解决建立时间不足和保持时间不足两类时序冲突问题。
事实上,DCM、PLL输出时钟的相位也是支持动态调整的,因此,对于那些输入时钟、数据关系或输出时钟、数据要求不明确的情况,我们可以不使用输入约束或输出约束(因为无法使用),而在实际使用中通过动态调整时钟相位以及观察系统工作状态来保证FPGA输入或输出接口的行为正确。

使用延迟模块

有些FPGA芯片中会有一种专门做延迟的模块,例如XILINX公司中的Virtex 5系列FPGA芯片,其接口资源中有一种叫做IODEALY的模块,可以以78ps为步进来静态或动态的决定其输入、输出之间的延迟时间。相比于DCM、PLL仅能针对周期性的时钟信号进行相位延迟,这种延迟模块则可以对任意信号进行延迟,因此常被用来修改路径延迟过大或过小的时序冲突情况。

使用综合约束

通过使用综合约束,可以对FPGA的综合结果及最终实现产生影响,其作用类似于结构调整的方式。不过,此处需要重点介绍一下利用综合约束解决路径延迟过小的问题。
众所周知,编译器在对FPGA设计进行综合的时候会进行优化,逻辑化简就是其中最常见的一种优化。例如,对于如下逻辑:

我们的目的是想通过添加非门来增加输入a到输出c之间的路径延迟,不过由于综合阶段的优化效果,其结果就相当于a与c之间的直连,因此如果仅仅是想在HDL或者原理图描述的时候添加偶数个非门来增加路径延迟的话,无论添加多少个非门都是白搭。
那么,为了使得上述非门的插入变得有效、不被综合过程优化掉,可以使用综合约束中的保持约束保持住b1、b2、b3这三处线网,从而让综合工具无法完成优化,进而达到增加路径延迟的目的。其具体做法可以参考【程序设计篇->编程思路->代码中的约束信息->HDL中的常用约束示例->保持约束->keep】小节中的内容。

改变设计思路

改变设计思路是解决时序冲突问题的一把双刃剑,它也许可以解决所有的时序冲突问题,但也许会带来更令人头痛的问题(不仅仅限于新的时序冲突问题,还可能带来功能仿真、资源占有率等等方面的问题)。总之,如果你已经竭尽全力,都不能让当前设计或设计中的某个模块达到时序收敛,那么你不妨试试换个思路重新编写某个模块或某个算法,也许会带来意想不到的效果。但是,改变设计思路也有逃避问题的嫌疑,我们不能一碰到难以解决的问题就想着改变设计思路,还是应该多思考如何解决当前问题,而不是总想着要逃避当前问题。

改变编译器的策略

编译器的编译策略通常有三种:面积优先、速度优先、平衡考虑,通常编译器的默认设置为平衡考虑。如果编译结果显示资源还有不少剩余,则可以通过将编译器的编译策略改为速度优先,这样便可以在时序分析报告中得到更好的结果,时序冲突也许就得到了解决。

打开编译器的retiming选项

如果你使能了编译器的retiming选项,则说明你将【分布调整】小节中介绍的方法由人工完成改为由编译器去完成了,这肯定会对建立时间不足问题带来福音。

加强布局布线努力程度

有时候出现了时序冲突,并不是说当前的时序要求就不可能被满足。我们要理解,编译器应该是有能力穷举出所有的布局布线可能,但你的生命和这个时间相比可能就显得比较短暂了。为此,编译器在布局布线时通常都有一个努力程度或努力次数上限的设定,如果经过了这么多次的努力还没有达到我们设定的时序要求,编译器便会放弃努力,抱着爱咋咋地的心态把目前的这个烂摊子往我们面前一扔,意思就是“你看着办吧!”。
此时,你只有三条路可走:
1、尝试使用本章所介绍的其他方法达到最终的时序收敛目标;
2、进入到相关设置选项中,增加编译器布局布线的努力程度;
3、接受现实,放弃项目。

人工调整布局、布线

如果对编译器完成的布局、布线结果不满意,可以尝试通过人工干预或调整布局、布线的手段来达到时序约束的收敛。具体做法就是打开编译器中的布局、布线工具(例如Xilinx的FPGA Editor),然后通过拖拽的方式改变寄存器、查找表等资源在FPGA芯片中的位置,进而也改变原有的布线情况。这样一来相关的路径延迟也会发生改变,从而有可能会产生比较好的结果。不过这种方法带有赌博性质,具有一定的运气成分,并且由于其不具有继承性和可重复性,所以往往不推荐使用。