全系列:《基于Xilinx的时序分析、约束和收敛》目录与传送门
最近研究vivado里的时序分析路径时,发现了3个很有意思的问题。经过一番查找资料后,总算把问题搞明白了,在这里分享给大家。
在如下文件建立的工程中:
module test
(
input sys_clk ,
input rst ,
output reg [7:0] cnt
);
always @(posedge sys_clk)begin
if(rst)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
endmodule
时序约束只做了主时钟约束,约束时钟100M:
create_clock -period 10.000 -name sys_clk -waveform {0.000 5.000} [get_ports sys_clk]
有一条建立时间的路径是这样的:

- 左侧是源端时钟路径,右侧是目的端的时钟路径
- ①是时钟从FPGA管脚进入后到IBUF,在IBUF内部的延迟,这段路径在源端和目的端就是同一条路径,但是两者的时间增量却不相同,源端是0.915ns,而目的端是0.784ns
- ②是时钟IBUF出来后到BUFG的布线延迟,这段路径在源端和目的端同样是相同一条路径,但是两者的时间增量却不相同,源端是1.693ns,而目的端是1.604ns
- ③是时钟在BUFG内部的延迟,这段路径在源端和目的端依然是同一条路径,但是两者的时间增量却不相同,源端是0.081ns,而目的端是0.077ns
- ④是时钟从BUFG出来后分别到源端寄存器时钟端和目的端寄存器时钟端的布线延迟,二者不是同一条路径,但二者可能存在部分路径重合(需要具体分析),所以时间增量不同是正常的
所以现在问题来了: ①②③明明就是同一条路径,但是为什么在源端报表和目的端报表的时间增量却不相同?
上面是建立时间的路径分析,这一诡异的情况同样出现在保持时间的路径分析下:

哪怕都是源端时钟路径或者目的端时钟路径,在建立时间的时序报表和保持时间的时序报表里也都不一样。比如,源端的BUFG在建立时间分析时延迟是0.081ns,但在保持时间分析时延迟却成了0.026ns。其他相同项也都有不同的时间延迟。

所以问题到底出在哪里?
OCV 与 PVT
即便是同一种 FF,在同一个芯片上不同操作条件下的延时都不尽相同,我们称这种现象为 OCV(on-chip variation)。OCV 表示的是芯片内部的时序偏差,虽然很细小,但是也必须严格考虑到时序分析中去。
产生 OCV 的原因主要有 PVT(Process / Voltage / Temperature)三个方面,而 STA 要做的就是针对不同工艺角(Process Corner)下特定的时序模型来分析时序路径,从而保证设计在任何条件下都能满足时序要求, 可以正常工作。 通常 PVT 对芯片性能的影响如下图所示:

不同的 PVT 条件组成了不同的 corner,另外在数字电路设计中还要考虑 RC corner 的影响,排列组合后就可能有超过十种的 corner 要分析。但是在 FPGA 设计中的静态时序分析一般仅考虑 Best Case(最优情况) 和 Worst Case(最差情况),也称作 Fast Process Corner 和 Slow Process Corner,分别对应极端的 PVT 条件。

Multi-Corner
Vivado 中的 STA 支持多角时序分析(Multi-Corner Timing Analysis),会对以上两种 corner 下的时序同时进行分析,然后报告最差的情况。因为每个 corner 下的延时也会有一定的变化范围,所以时序分析还会考虑每种 corner 下的最大延时和最小延时。

如果一个设计在 Best Case 和 Worst Case 下都能满足时序要求,则可以推算出这个设计在其允许的任何操作条件下都能保持正常工作。
这里要提醒大家,不要被 corner 的名字误导,实际上,同样一条路径可能在 Slow Corner 中满足时序却在 Fast Corner 中有时序违例。但是你在 Vivado 中看到的时序报告只会显示其对两种 corner 并行分析后选出的最差情况。
所以现在问题大概就有答案了,由于不同的 PVT 条件可能会产生不同的路径延迟,所以vivado选择的办法就是找到最优、最差两种极端路径,在极端路径都满足时序要求的话就可以保证在所有情况都满足时序要求。
建立时间分析的时候使用的是Slow Corner模型,保持时间分析的时候使用的是Fast Corner模型。所以才会出现同一条路径在做建立时间分析和保持时间分析时出现不同延迟值的情况。
建立时间报表分析分析的是max at slow process corner。在slow process corner模型下所有节点的时延都是最大的。在这种情况下如果能满足setup时间的话,那么在其他模型就都可以满足;保持时间报表分析分析的是min at fast process corner。在fast process corner模型下所有节点的时延都是最小的。在这种情况下如果能满足hold时间的话,那么在其他模型就都可以满足。

同样的,为了实现最极端情况的分析,所以在:
建立时间检查的最大延迟:
- 针对源时钟路径和数据/复位路径累积延迟,使用给定角点 (corner) 的最差情况延迟(最慢延迟)
- 针对目标时钟路径累积延迟同样使用该角点 (corner) 的最佳情况延迟(最快延迟)
保持时间检查的最小延迟:
- 针对源时钟路径和数据/复位路径累积延迟,使用给定角点 (corner) 的最佳情况延迟(最快延迟)。
- 针对目标时钟路径累积延迟同样使用该角点 (corner) 的最差情况延迟(最慢延迟)
也就是说,源端路径的延迟和目的端路径的延迟是不一致的。
至此,问题1解决。
在建立时间分析时,我发现下面两条路径都存在一个clock path skew的值,且两者还不一致。

clock path skew的概念我懂,就是时钟到达源端和目的端两个寄存器的时间存在偏差嘛,就像这样:

时钟网络延时 Tskew 就是 Tc2d 与 Tc2s 之差,即 Tskew=Tc2d - Tc2s。如下图:

所以两条路径的Tskew不同也很正常,毕竟走线不一样。
在分析下面的问题前,我还是把这张经典的时序图请出来:

- Data Arrival Time = launch edge + Tclk1 + Tco +Tdata
- Data Required Time = latch edge + Tclk2 - Tsu
- Setup Slack = Data Required Time – Data Arrival Time
然后,我分别点开了 Tskew的值,就出现了这样两张图:

可以看到,vivado计算Tskew,不光使用目的端的时钟延迟 减掉 源端的时钟延迟,它还加上了一个Clock Pessimism Removal,CPR,那么这个CPR是啥?
在问题1的时候我们了解到,为了分析最极端情况的路径延迟,对于源端时钟的路径延迟和目的端时钟的路径延迟采用了不同的建模方式(Corners)。但是会有意外,两条路径之间偶尔会有相同的路径嘛,就像这样:

相同的路径结果最后计算出来的路径延迟是不一样的,这明显不合理不是?所以xilinx就采用了 时钟悲观度(Clock Pessimism Removal(CPR))这个概念用来补偿两条路径之间的相同部分。顺便多说一句,CPR的xilinx的官方翻译是时钟消极因素移除(出自xilinx文件c_ug906),但是网上多用时钟悲观度这个翻译。
对于第一条路径path119:

目的端延迟是3.794ns,源端延迟是4.128ns,4.128ns - 3.794ns = 0.334ns,这和vivado补偿的CPR的值0.334ns是一致的,这说明到源端寄存器和到目的端寄存器都是100%的共同路径。
最后时钟悲观度是作为一个增量加入到了目的端时钟路径上,作为共同路径采用不同模型导致延迟差异的补偿。

再来看看第二条路径path 114:

同样的,目的端延迟是3.794ns,源端延迟是4.128ns,4.128ns - 3.794ns = 0.334ns,这和vivado补偿的CPR的值0.306ns不一致的,二者之间差了0.306ns - 0.334ns = -0.028ns,这个值也就是Tskew, 这说明到源端寄存器和到目的端寄存器的路径除了有共同部分外,还有不相同的部分。

从FPGA管脚到 IBUF到net再到BUFG这条路径不管是源端还是目的端都是相同的,源端延迟是0.915+1.693+0.081 = 2.689ns;目的端延迟是0.784+1.604+0.077 = 2.465ns,所以照理来说,补偿值CPR应该是 2.689 - 2.465 = 0.224ns,但是实际却补偿了0.306ns,那么多出来的0.306 - 0.224 = 0.082ns是来自哪里?
从BUFG到源端和目的端的时钟管脚之间各自都还有一段走线,在源端这个值是1.440ns,在目的端这个值是1.330ns,如果这段走线都是共同路径的话,那么在这一部分要补偿的CRP的值应该是 1.440-1.330 = 0.11ns,但是我们前面算了,这段实际补偿的CPR值是 0.082ns,这说明这段路径仅有一部分是二者的共同路径,共同路径的比例应该是 0.082/0.11 ≈ 74.5%。这说明剩下的25.5%就是源端时钟与目的端时钟的Skew值,即0.11*25.4%≈ 0.028ns,这与vivado计算的clock path skew值是一致的。
至此,第二个问题也解决。
建立时间Tsu的概念我们很熟悉了,也很重要,毕竟是整个时序分析的基础,在这里再回顾一下:
为了使寄存器稳定地采样到当前D端的数据,D端数据必须满足建立时间和保持时间的要求:
- 建立时间:Setup Time,缩写是 Tsu,即在时钟上升沿之前数据必须稳定的最短时间
- 保持时间:Hold Time,缩写是 Th,即在时钟上升沿之后数据必须稳定的最短时间
通俗来讲:建立时间和保持时间就是在寄存器采样窗口中输入数据必须保持不变,以免寄存器无法稳定采样。也就是说,在我寄存器的采样窗口之前你输入数据就必须要保持稳定,即输入数据不能来的太晚(建立时间);同样的,你寄存器的输入数据也必须在我寄存器的采样窗口结束后才变化,在此之前必须保持问题,即输入数据不能走的太早(保持时间)。
建立时间和保持时间的示意图如下:

建立时间和保持时间是寄存器的固定属性,不同的FPGA器件都应该是一个不同的值,且理论上这两个值都应该是正数。
现在依然来看path 119的时序报告:

上图是计算 数据要求到达时间 的公式,建立时间是0.059,且是作为一个增量给加到 数据要求到达时间 里去的,但是从更上面的公式我们又知道 数据要求到达时间 应该是这样计算的:Data Required Time = latch edge + Tclk2 - Tsu,也就是说理论上建立时间是应该作为一个负的增量加到 数据要求到达时间 的计算中,即减去 Tsu 。
但报表中的Tsu显然是加上去了,这说明在这个报表中 Tsu 实际上是个负数值。这就很奇怪了,why?
这种情况在芯片设计中很常见,比如使用standard cell,分析的是CELL外部的时序,不过如果CELL内部clock的延迟大于data的延迟。假设这个延迟是delay,CELL内部寄存器固有的setup时间为tsu,则CELL的setup_time=tsu - delay,当clock延迟足够大,那么从CELL看建立时间就是负值。

尽管这种情况的建立时间从整体看起来是个负值,但这个建立时间已经不是传统意义上的概念了,而应该是一个对于系统的相对值。还要注意的是,setup和hold不能同时为负值,而且二者之和必须为正。
在xilinx的官方论坛也有人发了这个问题:

这是官方的回答:

大概意思和上面说的差不多:在时钟路径比数据路径慢很多的情况下,是有可能出现建立时间为负数这个情况的,这个时候报表展示的也是个相对概念的建立时间。
至此,第三个问题也解决了。
ug903,Vivado Design Suite User Guide--Using Constraints
ug949,适用于 FPGA 和 SoC 的 UltraFast 设计方法指南
ug906,Vivado Design Suite 用户指南--设计分析与收敛技巧
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
由于fast-stemmer的问题,我很难安装我想要的任何rubygem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub
这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build
我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。