9.1 时序分析工具使用
前面的章节我们介绍时序分析的模型和命令,大家可能不能很好的理解这些内容,这一章我们学习如何在工具中进行时序约束并查询时序报告,通过对时序报告的结果进行分析,这样能帮助我们更好地理解这些模型以及工具是如何进行计算的。
9.1.1 添加约束
Xilinx官方文档推荐我们按照四个步骤添加约束:
·第一步,约束时钟,即可以覆盖全部片内寄存器到寄存器的路径,包括全局时钟引脚输入的时钟,PLL生成的时钟,高速收发器生成的时钟以及用户逻辑生成的时钟。
·第二步,约束输入/输出延迟,告诉分析工具信号传播延时。
·第三步,对异步时钟域间的时钟进行分组,或设置虚假路径以避免不必要的时序分析,节省布线资源。
·第四步,设置特殊路径的时序约束。
建议按照Xilinx提供的步骤添加约束,这样的做法安全且高效,下面学习如何在Vivado工程中添加约束。
方法一:打开XDC文件,点击Language Templates找到XDC原语模板,根据原语添加约束。
方法二:综合后点击Open Synthesized Design,直接在Tcl Console界面输入约束。
方法三:综合完成后,点击Constraints Wizard或Edit Timing Constraints,打开GUI界面添加约束。
推荐使用Edit Timing Constraints选项来添加约束,下面通过实例来学习添加约束的操作流程,首先创建一个新的vivado工程,写一个简单的verilog程序如下:
module timing
(
input wire I_clk ,
input wire [3:0] I_sdr_data ,
input wire I_sdr_valid ,
output reg [3:0] O_tx_data ,
output reg O_tx_valid
);
reg [3:0] data_reg0 ;
reg valid_reg0 ;
reg [3:0] data_reg1 ;
reg valid_reg1 ;
always@(posedge I_clk) begin
data_reg0 <= I_sdr_data;
valid_reg0 <= I_sdr_valid;
end
always@(posedge I_clk) begin
if(valid_reg0)
data_reg1 <= data_reg0 + 2'b11;
else
data_reg1 <= data_reg0;
end
always@(posedge I_clk) begin
valid_reg1 <= valid_reg0;
end
always@(posedge I_clk) begin
O_tx_data <= data_reg1;
O_tx_valid <= valid_reg1;
end
endmodule | 综合完成后,创建两个XDC文件timing.xdc和pin.xdc,timing.xdc用来存放时序约束命令,pin.xdc用来存放引脚的物理约束。在source界面右击timing.xdc,选择Set as Target Constraints file,我们通过GUI编辑的约束会自动保存到timing.xdc文件中。
我们使用方法三添加约束。点击Edit Timing Constraints,以添加主时钟约束为例,选择create clock,点击添加按钮。
选择外部晶振输入的时钟为源对象,按如下步骤操作。
然后对主时钟进行命名,设置时钟的周期和占空比。
点击OK,输入ctrl + s保存,打开XDC文件,可以看见已经生成了主时钟的约束。
9.1.2 查看时序报告
在布局布线,注意添加引脚约束,布局布线结束后,点击Open Synthesized Design->Report Timing Summary。
可以看到报告中的最差负时序裕量即建立时间裕量为8.207ns,保持时间裕量为0.056(注意:不同型号的FPGA会有差异,分析方法一样)。
周期-建立时间裕量 = 最小周期,这个系统的时钟最大可以跑到558MHz(周期为10ns -8.207 = 1.793ns),那我们将主时钟的约束改成周期为1.533ns,下方timing界面点击rerun后得到的时序报告如下:
可以看到在没有重新布局布线的情况下建立时间裕量为0,保持时间裕量并没有改变。那如果我们进行重新布线呢?前面我们提到了工具会根据约束自动优化布局布线结果,当我们将时钟频率提高,工具在布线时会将走线尽量短,这样建立时间裕量会得到优化,而由于走线长度变短,保持时间裕量就会减小,重新布线得到的时序报告结果如下:
我们右击左下角的Timing Summary选项卡将该页面关闭,点击Report Timing。
在Target选项卡中,将路径起始点选为我们设置的主时钟,在Option选项卡中,将每组路径的数目设置成10,将每一端点的数目设置成2,注意每一端点的数目不能超过每组路径的数目。设置完毕后,点击OK。
界面中显示了各条路径起始点、终点和裕量等信息,从裕量最差的路径依次向下排列,通过双击选中的路径来打开详细的时序报告,对报告的分析方法我们在之后的章节介绍。
我们还可以通过Check Timing查询没有被时序约束覆盖的路径,由于我们只对主时钟做了约束,报告中显示需要对输入输出端口做约束,对于没有约束的路径,工具会自动将建立时间和保持时间裕量设置为无穷大,以避免在布局布线时产生错误。
以上是在GUI界面查询时序报告的方法,也可以在Tcl console界面直接输入命令report_timing生成报告。
9.2 内部路径时序报告分析
9.2.1 路径分析
上一节已经演示了如何查看时序报告,接下来我们用刚才创建的工程来看看工具具体是如何对路径进行分析的。首先在GUI界面打开时序报告,在setup分析中选中一条路径,右键后点击schematic打开该路径的原理图。
主时钟由引脚进入FPGA内部以后,经过IBUF和BUFG到达两个寄存器的时钟引脚,data_d0_reg[1]为源寄存器,data_d1_reg[1]为目的寄存器。
再双击选中的路径,打开路径报告,里面有详细的建立时间分析和计算过程。
图中序号1表示该路径类型为建立时间检查,使用的角点模型是慢速角模型。慢速角模型指的是芯片工作在最高温度、最低电压下的模型,快速角模型指的是芯片工作在最低温度、最高电压下的模型。vivado工具在时序分析时,会对快速角和慢速角两个模型都进行分析,如果慢速角模型下满足建立时间的要求,那其它模型也能满足,如果快速角模型下满足保持时间的要求,那其它模型能满足。
序号2表示发射沿和锁存沿的时间关系。序号3表示数据延时,其中分为寄存器内部延时和走线延时。序号4表示时钟偏斜,即时钟分别到达两个寄存器C端的延时差。序号5表示时钟不确定度,其计算公式如下:
对路径上的延时分析,我们在原理图上标注每一个单元的延时,以方便进行分析。
在上图中,上方的数字为数据需求路径中每个单元的延时,下方的数字为数据到达路径中每个单元的延时。可以看到两条路径中公共的部分延时不同,这是因为工具在分析建立时间时,会使用数据需求路径中每个单元的最小延时和数据到达路径中每个单元的最大延时进行计算。但是,在电路实际工作时,同一时间不可能有两个延时值,由于时钟路径公共部分采用了不同延时引起的悲观称为时钟悲观,在分析时应该将其消除。序号6为计算中需要消除的时钟悲观,其值为工具在分别计算数据需求时间和数据到达时间时,公共部分的延时差。
·1.423 + 1.693 + 0.081 - (1.357 + 1.604 + 0.077) = 0.159
在时钟经过BUFG之后还有一段共同路径,但时序报告中没有给出,实际在计算时钟悲观时工具会加上这段走线的悲观。上式在计算时没有加上BUFG之后的悲观值,得到的值和实际的时钟悲观0.159ns相同,是因为时钟从BUFG输出后,共同路径很短,虽然工具已经将该悲观值计算进去,但报告中数值的精度不够高,没有能够体现在计算中,走线的长度如图:
下图为相同路径下保持时间分析的时序报告,可以看到工具使用快速角模型分析,发射沿和锁存沿为同一个沿,数据需求路径上各单元的延时取最大值,数据到达路径取最小值。
9.2.2 片内资源时序优化
在理论分析的过程中,我们知道可以通过降低时钟频率的方法来优化时序,但是在实际的工程当中,时钟往往是确定的,这就需要通过修改内部逻辑来改善时序,下面介绍几种优化时序的方法。
(1)逻辑复制
对于高扇出的信号,可能会因为到前级寄存器和到后级寄存器的延迟过大,导致时序不能收敛。对于大扇出信号的排查,可以在控制台使用report_high_fanout_nets命令,信号扇出由大到小排列。如下图所示的列表中,复位信号的扇出非常大。
通过将设置信号的综合属性MAX_FANOUT,可以修改信号的最大扇出,工具会自动将逻辑进行复制,以降低扇出。将复位信号的最大扇出设置为200。得到的扇出报告如下,可以看到复位信号的扇出减小了。
(*MAX_FANOUT = 200*)reg reset;
(2)流水线设计
Xilinx器件中每一级组合逻辑约会带来0.5ns的延时,对于逻辑级数过大的路径,可以通过插入寄存器来减少级数。虽然增加寄存器的数量会增大面积和延时,但能显著提高性能,这是数字电路设计中典型的面积换速度的方式。对于以下RGB转YUV的算法,在一个always块内完成计算,代码如下:
always@(posedge I_clk) begin
img_y <= img_r * 8'd76 + img_g * 8'd150 + img_b * 8'd29;
img_cb <= img_r * 8'd43 + img_g * 8'd84 + img_b << 3'd7 + 16'd32768;
img_cr <= img_r << 3'd7 + img_g * 8'd107 + img_b * 8'd20 + 16'd32768;
end
这段代码综合出来的逻辑级数很大,并且其中有4个CARRY4,意味着需要经过多个SLICE,导致延迟很大。
插入一级寄存器,将计算过程拆分开,代码如下:
always@(posedge I_clk) begin
img_r0 <= img_r * 8'd76;
img_r1 <= img_r * 8'd43;
img_r2 <= img_r << 3'd7;
img_g0 <= img_g * 8'd150;
img_g1 <= img_g * 8'd84;
img_g2 <= img_g * 8'd107;
img_b0 <= img_b * 8'd29;
img_b1 <= img_b << 3'd7;
img_b2 <= img_b * 8'd20;
end
always@(posedge I_clk) begin
img_y <= img_r0 + img_g0 + img_b0;
img_cb <= img_r1 + img_g1 + img_b1 + 16'd32768;
img_cr <= img_r2 + img_g2 + img_b2 + 16'd32768;
end | 综合出来的最大级数为6,裕量增加了较多。
(3)均衡设计
对于下列代码,使用两级寄存器进行加法运算。
always@(posedge clk) begin
a_reg <= a;
b_reg <= b;
c_reg <= c;
sum <= a_reg + b_reg + c_reg;
end | 综合出来的结果如下图所示,最大逻辑级数为9。
实际上第一级寄存器中没有组合逻辑,而第二级寄存器的组合逻辑比较大。我们可以将部分计算放在第一级寄存器,代码如下:
always@(posedge clk) begin
add_temp <= a + b;
c_reg <= c;
sum <= add_temp + c_reg;
end | 综合的结果中最大逻辑级减小,并且裕量更大了。
还有很多时序优化的方法如比较器拆分、关键路径重组等在高速设计时经常使用,这里就不一一说明了。时序优化需要FPGA工程师对数字电路和器件的底层十分熟悉,在于长时间学习和工作的经验积累。
9.3 Input Delay时序优化
源同步相比与系统同步更为常见,这里对源同步进行分析。对于源同步中心对齐和边沿对齐两种情况,中心对齐更容易满足时序要求,而边沿对齐由于数据在时钟的边沿变化,导致很难满足建立时间或保持时间的要求。下面分析时序不满足条件的,布线资源不足无法优化布局布线的场景下input delay的约束命令。
9.3.1 时钟直接输入的情况
在如上图所示的源同步数据和时钟中心对齐的情况下,实际上时钟的发射沿和锁存沿为同一个沿,但由于工具在分析时,还是按照锁存沿比发射沿晚一个时钟周期进行分析,会导致锁存沿不在数据的有效窗口内,如下图所示。
这就需要对约束命令进行调整,让数据延迟一个时钟周期,以便工具能够正确地按照我们的设定进行分析。
假设cycle = 10ns,dv_bre = 3ns,dv_are = 3ns,约束命令如下:
create_clock -period 10.000 -name sys_clk -waveform {0.000 5.000} [get_ports I_clk]
set_input_delay -clock [get_clocks sys_clk] -rise -min 3.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}]
set_input_delay -clock [get_clocks sys_clk] -rise -max 7.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}] | 得到的时序报告如下,可以看出数据和时钟中心对齐的情况可以很好地满足建立时间和保持时间的要求。
在路径报告中,建立时间分析使用最大输入延迟进行计算,保持时间分析使用最小输入延迟进行计算,与上一节的分析一致。
源同步数据和时钟边沿对齐的波形图如下图所示,对数据的采样沿为①②两个沿的情况进行分析。
假设cycle = 10ns,skew_bre = 2ns,skew_are = 2ns,使用①对data进行采样时,发射沿在锁存沿前一个上升沿,input_delay约束命令如下:
create_clock -period 10.000 -name sys_clk -waveform {0.000 5.000} [get_ports I_clk]
set_input_delay -clock [get_clocks sys_clk] -rise -max 12.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}]
set_input_delay -clock [get_clocks sys_clk] -rise -min 8.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}] | 得到的时序报告如图,建立时间裕量为负,因为采样沿和数据的起始对齐,难以满足建立时间要求。
使用②对data进行采样时,对应的发射沿位置在①,input_delay约束命令如下:
create_clock -period 10.000 -name sys_clk -waveform {0.000 5.000} [get_ports I_clk]
set_input_delay -clock [get_clocks sys_clk] -rise -max 2.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}]
set_input_delay -clock [get_clocks sys_clk] -rise -min -2.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}] | 得到的时序报告中,保持时间裕量为负,因为采样沿和数据的结束对齐,难以满足保持时间要求。
可见,在数据和时钟边沿对齐的情况下,时序难以满足要求。这就需要通过IDELAY原语对时钟或数据进行延迟,或者PLL对时钟延迟进行补偿,从而让时钟能捕获到稳定的数据。
9.3.2 时钟经过PLL的情况
若要使用PLL增强时钟的驱动能力,时钟的延时会更短,更容易满足保持时间的要求,所以考虑用②沿对数据采样的情形对input_delay进行约束。删除掉原来的约束,在代码中例化clk_wizard IP核,将PLL输出的时钟作为寄存器的触发时钟,重新进行综合和布局布线。布局布线完成后,重新添加如下约束:
set_input_delay -clock [get_clocks sys_clk] -rise -max 2.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}]
set_input_delay -clock [get_clocks sys_clk] -rise -min -2.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}] | 报告的结果如图所示,保持时间的负裕量增加了,这是因为PLL增加了时钟的驱动能力,将时钟信号负向偏移。
加入PLL的情况下还是无法满足保持时间的要求,这就需要主动设置PLL的相移,打开IP设置,将PLL输出时钟的相移改成-30°,重新综合和布局布线完成后,再次添加约束。得到的时序时序报告如下:
在时序报告中,工具实际上是按照下图进行时序分析的,分析保持时间时将锁存沿提前了0.833ns即1/12个时钟周期,使得时钟能够满足保持时间的要求,在数据结束前采样到稳定的数据。
使用PLL对时钟进行正向偏移时,时钟延时更长,更容易满足建立时间的要求,这种情况下考虑用①沿对数据采样的情形对input_delay进行约束。将PLL的相移改成正的90°,删除之前的约束,重新进行布局布线后,添加如下约束:
set_input_delay -clock [get_clocks sys_clk] -rise -max 12.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}]
set_input_delay -clock [get_clocks sys_clk] -rise -min 8.000 [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}] | 得到的报告如下图所示。
工具实际上是按照下图进行分析的。
而我们在约束时,想要让工具分析第二个上升沿,如图所示。
所以我们得到的时序报告其实是错误的报告,工具没有按照我们的设想进行约束,不能认为在这种情况下,时序就一定违例。那如何得到一份正确的时序报告呢,这就需要使用到多周期约束,让工具在第二个上升沿分析建立时间关系,该约束将在第7章时序例外约束详细讲解,约束命令如下:
set_multicycle_path -setup -from [get_ports {{I_sdr_data[0]} {I_sdr_data[1]} {I_sdr_data[2]} {I_sdr_data[3]} I_sdr_valid}] 2 | 最终得到的时序报告如下图所示。
虽然时序报告中建立时间裕量还是为负,但工具分析的过程和我们的设想一致,所以这是正确的时序报告,我们只需要根据报告的结果再进行调整,就可以让时序收敛。比如将时钟继续正向偏移,在PLL中设置相位为240°,得到的时序报告如下图所示。
通过PLL使时钟正向相移最终实现了时序收敛。
9.4 Output Delay时序优化
9.4.1 随路时钟选择
在使用输出延迟约束时,注意随路时钟的选择。当输出数据的随路时钟为系统输入的主时钟,如果输入如下约束命令:
create_clock -name sys_clk -period 10 -waveform {0 5} [get_ports I_clk]
set_output_delay -clock sys_clk -max 4 [get_ports data] -clock rise
set_output_delay -clock sys_clk -max 2 [get_ports data] -clock rise | 得到寄存器到引脚路径的时序报告如下图,目标时钟路径的延迟没有被计算进去,实际上时序分析工具在进行分析时将该模型当作了系统同步模型进行分析,认为随路时钟在器件外部。
为了避免这种情况,需要对时钟输出端口进行生成时钟约束,让时序分析工具正确地分析输出时钟的路径延时,得到正确的计算结果,从而可以进行下一步的时序优化。约束命令如下:
create_clock -name sysclk -period 10 -waveform {0 5} [get_ports I_clk]
create_generated_clock -name tx_clk -source [get_ports I_clk] -multiply_by 1 [get_ports O_tx_clk]
set_output_delay -clock tx_clk -max 4 [get_ports data] -clock rise
set_output_delay -clock tx_clk -max 2 [get_ports data] -clock rise | 得到报告中目标时钟路径延时计算过程如下图。
这里的时钟经过了ODDR原语输出,Xilinx建议用户在输出时钟时使用ODDR,这是因为使用ODDR在IOB上,有更低的延迟,输出的时钟质量更好。下图是时钟不经过ODDR直接输出的情况,BUFG到OBUF的走线延时明显更长。
9.4.2 Output Delay时序违例解决办法
解决Output Delay的时序违例,我们可以参考上一节Input Delay时序优化中采用的方法,即根据不同的情况调整数据和时钟的延时。
当保持时间违例时,可以将通过ODELAY将数据延迟。注意Xilinx有些器件的IOB没有ODELAY如A7系列,此时可以通过PLL将时钟增加一个负相位,以达到数据相对时钟延迟了的效果。当建立时间违例时,一般通过PLL给时钟正向相移来增大延时。
在Output Delay约束时,经常会遇到时序报告与自己分析的结果不同的情况,这是由于工具的分析策略导致的。这时就需要使用时序例外约束来调整工具的分析策略,这样做不仅可以得到正确的时序报告来让我们进行下一步优化,还可以提高编译的效率。
9.4.3 Output Delay和Input Delay的区分
读者可能会在input_delay和output_delay之间混淆,其实二者有很大的差异。input_delay指的是外部输入数据和时钟送到FPGA输入管脚时,数据相对于时钟发射沿的延迟;output_delay指的是为了满足目标器件的时序要求,输出数据和时钟在FPGA输出管脚必须在一定的延迟范围内。对于output_delay,我们可以将外部走线和器件全部看作一个整体,即一个寄存器。将FPGA数据输出管脚看成寄存器的D端,时钟输出管脚看成寄存器的C端,所有外部延时信息看成寄存器的建立时间和保持时间,如下图所示。
最大输出延迟即建立时间要求,最小输出延迟即保持时间要求,输出数据必须在这个窗口内保持稳定,如下图所示。
|