声明:本文为黑金动力社区(http://www.heijin.org)原创教程,如需转载请注明出处,谢谢!
黑金动力社区2013年原创教程连载计划:
http://www.cnblogs.com/alinx/p/3362790.html
《FPGA那些事儿--TimeQuest 静态时序分析》REV4.0 PDF下载地址:
http://www.heijin.org/forum.php?mod=viewthread&tid=22637&extra=page%3D1
第四章:内部延迟与其他
4.1 PLL与约束命令
4.2 约束PLL时钟
4.3 延迟怪兽①
4.4 延迟怪兽②
4.5 Set Multicycle Path 的噩梦
4.6 不可理喻的Set False Path
总结:
第三章我们知道为何物理时序会发生违规,原来都是“延迟因数惹的祸”。延迟因数即有分内外,在接下来的章节,我们会逐步探索“那个约束命令针对那个延迟因数”。好!事不宜迟,就先拿 PLL 来开刀吧。
与约束命令
PLL是什么?专业的全名是很长很臭的 Phase Lock Loop,至于内部的执行原理更长更臭。不过傻瓜点说,PLL在FPGA里边就是某个“针对时钟信号加工的组合逻辑”,负责的工作有:稳定,增频,减频,位移时钟信号等。
虽说PLL类似加工时钟信号的组合逻辑,不过PLL不是由逻辑资源东拼西凑而成的山寨版组合逻辑,它是基于硬件的组合逻辑。PLL的葫芦里装有么药,除了FPGA的厂商以外,外人都不由而知。此外FPGA厂商又得想办法“约束”一下PLL,就是这种半遮半开的情况下,唯有 PLL 拥有自己专属的约束命令。这种矛盾的情况不知是祸是福?
常用的PLL约束命令如下:
derive_pll_clocks
derive_clock_uncertainty
derive_pll_clocks
读者是否还记得曾在第二章出现的 create_clock 这个约束命令吗?不错,那是告诉TimeQuest某设计到底使用什么时钟,粗略一点也可看成是仿真中的“时钟刺激”。那么 create_clock 又和 derive_pll_clocks 有什么关系?
实际上 derive_pll_clocks 是一个偷懒用的约束命令。一般 PLL 都会声明 N 个时钟,如果换做 create_clock,我们要 N个时钟都添加 create_clock 命令去约束。如果 PLL 声明 50个输出,既不是要断掉十指逐个逐个去约束这50个时钟信号吗?因为如此,derive_pll_clocks 这个约束命令就诞生了,明白吗懒人们?
一概论过,无论我们用 PLL 声明多少时钟输出,只要填上 derive_pll_clocks 这个犯懒的命令,TimeQuest就会识作了。TimeQuest会自行建立相关的时钟约束,然后做出报告。
derive_clock_uncertainty
在这里,笔者先简单讲解一下关于 clock uncertainty,clock uncertainty是指时钟信号的不确定因数。这个命令用来告诉TimeQuest有关时钟的不确定性。严格来讲,这个约束命令不仅针对pll更是针对所有与时钟信号,不过根据手册规定,凡是建造工艺 65nm 或者以上的 FPGA,都必须加上这条约束命令。然而建造工艺是 65nm 以下的FPGA加上也无妨,TimeQuest 会自动无视。
约束PLL时钟
在这里笔者想要讲解的不是针对单个时钟信号的约束,而是静态时序分析的第一步,亦即时钟约束的步骤。
图4.2.1 整合篇——实验十引脚配置,其中从外部借用一个名为CLK 的20Mhz时钟信号。
图4.2.2 整合篇——实验十有关PLL的声明,其中c0是倍频为40Mhz的时钟信号。
假设笔者想要分析一下《整合篇》——实验十的物理时序是否合格,首先我们知道实验十只向外部借用一个名为 CLK 的20Mhz时钟信号(图4.2.1所示)。然而20Mhz不足以驱动 vga,所以借用 PLL 的力量将原有20Mhz 的 CLK 倍频两倍为 40Mhz的时钟信号,结果名为c0(图4.2.2所示)。为此,这个实验的第一步约束步骤就是针对以上几个时钟信号做出约束的动作。
二 vga模块的物理时序分析
图4.2.3 整合篇——实验十建模图。
图4.2.3 是本实验的建模图,我们不管右边组合模块的内容。从图4.2.3中我们可以知道一个事实,亦即 CLK/CLK 经过 PLL 以后成为 CLK_40Mhz/c0,然后 CLK_40Mhz/C0作为组合模块的主时钟源。简单点说,我们所要分析的物理时序是与 CLK_40Mhz/c0时钟信号所扯上的各种资源。
不多话,为该实验建立一个名为 vga_module.sdc 的文件,然后将下面的内容拷贝进去:
create_clock -name {CLK} -period 50.000 -waveform { 0.000 25.000 } [get_ports {CLK}]
derive_pll_clocks
derive_clock_uncertainty
图4.2.4 设定实验二的sdc文件。
再者将该 sdc 文件设定本实验的约束文本,如图4.2.4所示,忘记编译的同学先编译一下。接下来再按下这个可爱的图标 大约几秒钟的功夫就会跳出 TimeQuest 的界面。
这回笔者彻底犯懒了,笔者就用质量最差的网表(默认网表)。
create_timing_netlist -model slow
图4.2.5 快捷生成默认网表
如果同学们也懒惰输入这段 tcl 命令,可以双击Create Timing Netlist ,TimeQuest就会自动生成,最后顺便按次序双击 Read SDC File 和 Update Timing Netlist(如图4.2.5所示)。
我们先暂停一下吧,笔者怕有些同学不仅会更不上也会充满疑问“笔者,用那么差质量的网表真的行吗?”其实这没有什么行不行的问题,以EP2C8Q208C8 的建造工艺为例,40Mhz 的时钟频率用默认网表的质量已经足够了。
图4.2.6 生成时序的简报。
图 4.2.7 时间(时序)简报。
再来双击 Report All Summaries (如图4.2.6所示)就会跳出图4.2.7的时间(时序)简报。根据图4.2.7我们可以读取几个信息 ...
其一CLK时钟信号的频率是20Mhz,周期为50ns,半周期25ns,其功用除了PLL的输入以外几乎残废。其二CLK_40Mhz时钟周期为40Mhz,周期是25ns,半周期是12.5ns
,50% 占比率,输入为 CLK 输出为 ...Pll{Clk[0]},简称为 CLK_40Mhz,该时钟作为本实验的时钟源。
图4.2.8 调出 CLK_40Mhz 的 Report Timing 界面。
接下来就要右键 CLK_40Mhz然后调出 Report Timing 的窗口,如图4.2.8所示。
图4.2.9 执行 CLK_40Mhz关联的时序报告(建立setup和保持hold)。
然后相续执行相关的 Setup 和 Hold 时序报告,如图4.2.9所示。在这里,笔者先插上诺干无关的提示 ... 有心的同学可能都会觉疑 From clock 为什么会留空?笔者就好心为失忆的同学复习一下第二章的内容!
图4.2.10 From clock 和 To clock 的意思。
根据 Time Quest 的模型 From clock就是源基础器(reg1)的时钟;To clock 就是目的寄存器(reg2)的时钟。不过根据实验二,20Mhz的CLK时钟源除了作为PLL的输入以外,它几乎接近残废。换之 40Mhz 的 CLK_40Mhz 作为 vga_module 的时钟源。
图4.2.11 两种角度观看 From 和 To Clock。
如果进一步讲解的话,可以将 From clock 和 To clock 分为两个角度。观点一是从 TimeQuest 的视角来观看 From clock 和 To clock,TimeQuest 是一个笨蛋它觉得两个 Clock理应分开的。换之,观点二是从我们的视角去思考,我们知道 CLK 时钟型号已经残废,换做 Clk_40Mhz取代它成为实验 vga_module 的主时钟源。同时我们也知道,整个模块的寄存器都使用相同的主时钟源。
图4.2.12 三种等价的 From clock 和 To clock 选项。
结果说,无论是哪一种选项对据实验二的 vga_module 来说都是等价的(如图4.2.12所示)。换句话说,只要和 CLK_40Mhz有关联的寄存器都会一一列出。
图4.2.13 path 的深度。
“笔者 ... 请问那个四条九是什么意思?(图4.2.13)”呵呵,那是时序报告的深度。
TimeQuest 实际上是不择不扣的笨蛋既懒惰虫,默认下它只会分析 10 条和 from clock 或者 to clock 相关的时序报告。根据vga_module 模块 from clock 或者 to clock 有关的分析节点大约有1100 个左右,所以将path深度填上 9999 为了一举列出所有时序信息,然后再逐个击破不合规的时序结果。当然 9999 不是固定的参数,随着建模的增加这个参数也会跟着提升,同学们应该注意下。
图4.2.14 部分setup 时序报告。
图4.2.15 部分hold 时序报告。
其他的细节基本不重要,点击 Report Timing(图4.2.9),相续会出现深度为1100的setup 和 hold 报告,其下无论哪个余量 slack 都是正直,结果表示 vga_module 的时序是合格的。如图4.2.14 的部分 setup 时序报告和图4.2.15 的部分 hold 时序报告(由于时序报告的数量实在是太多了,笔者仅载录其中一部分作为演示而已)。
此外还有一个偷懒的tcl指令,也可以一句列出所有相关的时序报告,亦即:
qsta_utility::generate_all_core_timing_reports "Report Timing (Core)" 1000
或者双击 Macros 目录下的 Report All Core Timings(如图 4.2.16 所示)。
图4.2.16 快捷一举列出相关的时序报告。
默认下 Report All Core Timings 只是列出setup时序报告 1000 分而已,如果同学们是执行如图 4.2.16 所示的动作,我们是无法扩大时序分析的深度。唯有手动输入 tcl 命令(如上),将1000 换为 9999 即可取得等价的效果。这个tcl命令到底有多大的用处就见仁见智了,笔者只是好意提醒而已。
根据setup 时序报告(图4.2.14)和hold时序报告(图4.2.15)各1100 ,我们没有看见“红色”的字样出现,换句话说 vga_module 这个模块的物理时序是合格的(在某种程度上),因此我们可以总结说“《整合篇》——实验十的 vga_module 没问题!”
结论:
实验二除了举例有关 PLL 的命令以外,我们也了解静态时序分析的第一步骤,亦即时钟方面的约束。此外,也稍微对Report Timing逐步深入了解,何为一举列出多有相关的时序报告等。
延迟怪兽①
师兄曾在第三章说过,TimeQuest是离不开 TimeQuest模型的数学工具,一旦TimeQuest模型无法被应用在分析上,TimeQuest这个工具就会略显残废。TimeQuest模型最简单的概念就是两个节点,具体点说就是两个寄存器夹着一个组合逻辑,直接点说就是两个寄存器之间存在延迟。
初学TimeQuest的时候,笔者曾产生这样的疑问:“无论是什么样的设计,Quartus的编译器是不是有足够的智能保证FPGA内部各个节点的时序都处于合格的状态?”之所以会产生这样的想法,是因为“即时建模”的出现。“即时”是什么?根据理想时序的思想,即时的定义是“无视时钟取得即时结果”,“即时建模”就基于即时操作的建模技巧。
“即时建模”有一个变态的特性,无论那个模块有多么长的执行步骤,都可以转换为“即时操作”,只要一个时钟就可以取得总结果。说简单点“即时建模”就是将某个模块转换成组合逻辑。
“即时建模”执行“即时操作”,“即时操作”又是组合逻辑的事件触发,粗略点说“即时建模”拥有组合逻辑的特性,但是“即时建模”又和组合逻辑有明显的区别。教科书上所谓的组合逻辑是几个逻辑门组合而成的东东,笔者称为“小组合逻辑”。然而“即时建模”是“大组合逻辑”,因为只要条件允许“即时建模”是没有结局的,它可以无限扩展。
在《整合篇》里笔者也说过 ... 组合逻辑都带有延迟,延迟的真凶有如第三章讨论过的各种延迟因数。“即时建模”不停扩展,亦即组合逻辑的延迟压力也会不停增加。所以说“即时建模”有个一致命的缺点,亦即“庞大的延迟压力”会随着不断扩展而提升。
最终,这种“延迟压力”一定会超过FPGA可承受的极限,成为巨大的延迟怪兽。笔者的问题很简单:Quartus的编译器是不是智能到会自动优化这头怪兽呢?如果是又如何优化?如果不是 TimeQuest 又如何约束?用什么命令约束?
顺便补上,所谓的“极限”是指FPGA的制作工艺,还有时钟周期 ... 越精致的制作工艺(65nm,45nm等)内部的各种延迟因数也会随之缩小;越长的时钟周期,组合逻辑既有更多足够的时间加工数据。
图4.3.1 假设图。
因此,我们可以为上述的种种内容绘出一个假设图,如图4.3.1所示它是一个最基本的TimeQuest 模型,亦即两个寄存器夹着一个组合逻辑。CLK1 是源寄存器的时钟信号,CLK2是目的寄存器的时钟信号,假设它们同为一个时钟。此外,被夹在中间的组合逻辑是由即时模块建立而成的。左边的信号表示,数据从 reg1 移向 reg2 然后经过即时模块。
图4.3.2 时钟周期加长对延迟压力的影响。
图4.3.2 是加长时钟对延迟压力影响的假想图,左边是理想时序另一边则是物理时序。理想时序图与物理时序图之间的差别就是不存在任何物理特性,不过时钟周期的加长对延迟压力的影响却有相同的结果。如左图所示,增长时钟周期则表示组合逻辑拥有更多的时间加工数据,也亦即延迟压力的覆盖率会跟着减少。
右图的变化上虽然没有左图明显,但是却可以看到 CLK2的锁存沿向右偏移些许,不管怎么说CLK2锁存沿越是接近数据的中心,时序就会越稳定。加大时钟周期的方法有许多种,不过最为常见如图4.3.2所示,亦即减慢时钟频率。
图4.3.3 制作工艺精密对延迟压力的影响。
FPGA的制作工艺是老百姓无法控制的参数,这要根据FPGA厂商的天气和心情。2012年的今天,45nm 几乎成为主流了,随着科技的发展比45nm 更精致的制作工艺也会随之问世。不过我们还是稍微了解一下吧。FPGA的制作工艺会直接影响FPGA的体积,内部的各种延迟因数,无论是路径还是逻辑门也会缩小。如图4.3.3 所示,随着制作工艺的提升tPath 和 延迟压力也会跟着缩小,而tPath的延迟因数是路径。
好了,问题开始扯远了,接下来笔者用实验来验证一下笔者问题:Quartus 的编译器有没有足够的智能保证各个节点的时序合格呢?哪怕是巨无霸的延迟怪兽?
延迟怪兽的暴动
图4.3.4 即时式Booth乘法器的建模图。
实验三的测试对象是抽取《整合篇》——实验二十六的即时式booth乘法器,图4.3.4则是建模图。哇!那是什么,好可怕啊 ... 这是《整合篇》中提及的即时建模,想了解的朋友可以复习《整合篇》,不想了解的朋友就无视吧 ... 懂不懂原理对实验三来说都不重要,同学们仅有把它看成拥有乘法功能的组合逻辑即可。
图4.3.5 组合逻辑的即时模块。
实际上,即时式Booth乘法器目前只是组合罗而已,它的前后没有被寄存器夹着,我们知道 TimeQuest 模型要是山寨失败,TimeQuest就无法进行物理时序的分析。结果说,我们需要帮忙为该乘法器的前后加上寄存器。
1. module Delay_monster
2. (
3. input CLK,
4. input RSTn,
5. input [7:0]A,
6. input [7:0]B,
7. output [15:0]Result
8. );
9.
10. /****************************/
11.
12. wire [15:0]Result_U1;
13. immediate_booth_multiplier_module U1( rA, rB, Result_U1 );
14.
15. /****************************/
16.
17. reg [7:0]rA,rB;
18. reg [15:0]rProduct;
19. reg [3:0]i;
20.
21. always @ ( posedge CLK or negedge RSTn )
22. if( !RSTn )
23. begin
24. rA <= 8'd0;
25. rB <= 8'd0;
26. i <= 4'd0;
27. rProduct <= 16'd0;
28. end
29. else
30. case( i )
31.
32. 0:
33. begin rA <= A; rB <= B; rProduct <= 16'd0; i <= i + 1'b1; end
34.
35. 1:
36. begin rProduct <= Result_U1; i <= 4'd0; end
37.
38. endcase
39.
40.
41. /*****************************/
42.
43. assign Result = rProduct;
44.
45. /*****************************/
46.
47. endmodule
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
上面一段非常简单的代码,第1~8行声明该模块的出入口;第13行是即时式 Booth乘法器的实例化——名为U1,它分别由 rA 和 rB驱动,输出致连线Result_U1。第21~38是核心功能,第17~19行声明相关的寄存器,亦即 rA,rB 和 rProduct;第24~27行是复位操作。
第32~33的步骤0先将 A 和 B 的值暂存在 rA 和 rB,随之 rProduct 清零,然后步骤i递增以示下一个步骤。在步骤i进入下一个步骤之际,U1立刻触发即时事件。结果第35~36行,U1会输出结果 Result_U1,然后将其暂存在 rProduct寄存器。最后再由 rProduct驱动 Result (第43行)。
图 4.3.6 即时模块U1 Make Up完毕。
如果不管核心功能等细节问题,粗略点 Delay_monster 模块的模样如图4.3.6所示。分别rA和rB驱动即时式 Booth乘法器的输入,输入则由 rProduct 寄存器暂存。这样一来,U1就是上寨成功,结果 U1 可以用 TimeQuest 来分析。
图4.3.7 成功上寨2两个TimeQuest 模型。
同样,如果不计较一些细节问题的话 ... 结果如图4.3.7所示,我们已经成功上寨2个TimeQuest模型在 U1的身上,一个是 rA经 U1到 rProduct;另一个则是 rB经 U2 到 rProduct。如果同学蛋疼的话,笔者不介意打开 Technology Map 视图来看看,同学一定会被细节打败,咔咔咔!笔者用狗头保证!
先编译一下模块 Delay_monster,然后建立一个名为 Delay_monster.sdc 的文件,随之填下:
create_clock -period 50 -name CLK [get_ports {CLK}]
保存以后就完工了,执行的次序和实验二一样。打开TimeQuest,建立默认网表(双击 Create Timing Netlist)
create_timing_netlist -model slow
然后双击 Read SDC File 和 Update Timing Netlist,载入 Delay_monster.sdc 之余顺便更新网表。接下来双击 Report All Summaries,最终会出现如图4.3.8 的时间简报。笔者稍微罗嗦一下,这个实验的时钟信号只有 CLK 而已,因此时间简报只会呈现 CLK 而不是其他。也就说 CLK 信号贯穿整个 Delay_monster 模块。
图4.3.8 时钟简报。
好的,关键的时刻到了!事实上 Delay_monster 拥有 9999 以上的节点也分析,如果我们像实验二那样一举全部和 CLK 有关的路径或者节点的话,那是一场等待的恶梦(不过同学们可以试试,不过搞当电脑后果要自负呀)。不过,懒惰的同学就会问:“有没有快捷的方法一举列出违规的节点?
图4.3.9 一举列出所有违规的节点。
如下的 tcl 命令就是一举列出所有违规的节点(路径),其中参数200是默认节点数量,参数可以自由修改。同样也可以双击 Report Top Failing Paths 如图4.3.9 所示,不过无法修改参数。
qsta_utility::generate_top_failures_per_clock "Top Failing Paths" 200
双击 Report Top Failing Paths 看看 ... 会发生什么?TimeQuest 反馈如下信息 ... 呵呵!没有任何违规的节点。
No failing paths found.
嘛,笔者还是简单举例10个路径(节点),看看延迟怪兽如何影响各个节点的时序合格化;又或者看看延迟怪兽如何影响 Setup 和 Hold 时序分析。
图4.3.10 快捷调出 Report Timing 界面。
双击 Report Timing 快捷调出 Report Timing 界面(如图4.3.10所示),
图4.3.11 随便列举10个节点,读出setup报告。
根据Delay_monster模块,From clock 或者 To clock 任何选项都会得到等价的结果。分析属性为 Setup 路径深度(节点数量)10,报告纤细Path only,选项准备完毕以后点击 Report Timing 就会跳出右图——随便列举10个节点有关 Setup 的余量分析。咋看 ... Data Delay 比往常几个实验来得还要“凶”,可是却没有造成时序违规 ... 为什么?
图4.3.12 节点1的细节。
打开节点1的细节观察,如图4.3.12所示 ... 会造成 data delay 那么凶的原因是 Data Arrival Path(数据抵达路径)的 Data path 出现怪兽,啊 ... 我的菩萨!这只怪兽就是在Delay_monster模块中饲养的 即时式Booth乘法器,只要同学将其列出 ... 就会看到怪兽的五脏六腑,亦即各种和即时式 Booth 乘法器有关的组合逻辑。
幸好怪兽没有造成时序违规 .... 不过奇怪的是,怪兽只是骚扰 Data Arrival Path 大道而已,又不见怪兽骚扰 Data Required Path大道(数据读取/锁存路径)。不过,不知道怪兽是否也会捣乱 Hold 分析?事不宜迟,让我们来瞧瞧。
图4.31.3 随便列举10个节点,读出hold报告。
setup 余量有关公式:
数据抵达时间 Time Arrival Time = 启动沿时间 + Tclk1 + Tco + Tdata
hold 余量有关公式:
数据保持时间Data Hold Time = 启动沿 + Tclk1 + Tco+ Tdata + 数据周期时间
根据 Setup 余量和 Hold 余量的求得公式Tdata就是组合逻辑的象征,亦即组合逻辑越肥大,Tdata也会一直线上升。结果而言,我们可以肯定延迟怪兽也会捣蛋 Hold 时序的分析。如图 4.3.13 所示,笔者随便列举10个节点(路径)然后列出时序结果,奇怪的事情发生了!注意最右边Data Delay 的数值,既然那么“渺小” ... 有没有搞错?
图4.3.14 节点6的细节。
绝对没有搞错,注意 Data Path 的展开信息,那是延迟怪兽的“五脏六腑”,无疑延迟怪兽确确实实已经影响 Hold 时序分析,不过效果不像 setup 分析那么强烈而已。
===================================================================
笔者曾说过,时钟周期也是其中一个影响组合逻辑的因数,为了让延迟怪兽的骚动更加疯狂,我们尝试缩短时间周期从 20Mhz(50ns) 至 100Mhz(10ns),亦即更改 CLK 的约束参数 ... 如下所示:
create_clock -period 10 -name CLK [get_ports {CLK}]
然后重复上述动作直到双击 Report All Summaries 为止。
图4.3.15 亮红灯的 Setup 报告。
结果如图4.3.15所示,setup 报告既然量红灯了 ... 我们得赶紧一举列出违规的节点(双击 Report Top Failing Paths)。
图4.3.16 亮红灯的setup时序结果。
红啊!满江红啊!怪兽开始杀人啦!同学不要忘了,默认下 Report Top Failing Paths 只是随意举列 200 个有问题的节点而已,实际上有问题的节点大概是超过 10000个以上,到底有多少“人”已经遭殃?完了 ... 彻底玩完了,终于玩火自焚刺激怪兽了 ... 延迟怪兽已经造成一发不可收拾的后果。我们该怎么办 ... 唔呜呜 .... ( To be continue )
延迟怪兽②
前情提要:
一只名为 即时式 booth 乘法器的延迟怪兽养活在 Delay_monster.v 的体内,当时钟频率为 20Mhz 的时候,怪兽虽然有对Setup 和 Hold 时序结果造成影响,不过不至于使结果违规。可是一个好玩,将时钟周期从 50ns 缩短至 10ns,最终造成怪兽暴动,疯狂杀人 ... Setup时序报告出现血粼粼的满江红!
要解决这个难题,我们有几个选择:
(一)消灭怪兽(放弃使用即时式 Booth 乘法器)。
(二)增宽时钟周期。
(三)使用约束命令。
选择一是最直接的方法,亦即消灭怪兽放弃使用该乘法器,说白点那只是潇洒不潇洒的问题而已。选择二是温柔的方法,亦即加宽时钟周期以致抚摸怪兽的情绪,让它不在“杀人”,setup 时序报告也不会满江红。选择三是最最无情的方法,既利用怪兽也无视怪兽的情绪,然而使用强制的约束手段驾驭怪兽 ...
好啦 ... 别把话说到那么难听啦!选择三本来就是学习 TimeQuest 的目的 ... 章节4.3笔者曾提问过“Quartus的编译器有没有足够的智能保证,内部的物理时序不会出现违规呢?如果是 Quartus 如何优化?如果不是 TimeQuest如何约束?用什么命令”,然而答案也非常明显的。接下来我们又该用什么约束命令来确保适时序合格呢?
在此之前,同学们需要回忆一下第三章中曾出现的“约束行为”。在约束行为中“加”的概念比起“减”的概念更为重要,也就是说在接下会使用的约束命令,将演示如何实现“加”的概念。
四 约束延迟怪兽
实验四和实验三的内容基本上不变,除了时钟约束从 20Mhz 变成 100Mhz,亦即:
create_clock -name {CLK} -period 10.000 -waveform { 0.000 5.000 } [get_ports {CLK}]
之所以延迟怪兽会到处杀人,是因为我们没有给足组合逻辑加工数据的时间,会有同学突发奇想道:“能不能设法减少硬件上给组合逻辑带来的延迟?”,这位同学非常遗憾,除了减少即时建模的即时层,或者提高FPGA的制作工艺以外,余下就只有提高网表的质量而已,不过目前还不是时候谈论网表。在此我们需要用到 set_multicycle_path 约束命令。
严格来说set_multicycle_path 的约束行为是告诉TimeQuest更动某对节点的启动沿和锁存沿 ... 想必读者听不明白是吧?说傻瓜点就是,告诉TimeQuest某对节点很可能不是用1个时钟周期工作。
图4.4.1随意举例10个节点的 setup 报告。(约束之前——频率100Mhz时钟周期10ns)
图4.4.2随意举例10个节点的 hold报告。(约束之前——频率100Mhz时钟周期10ns)
图4.4.1是随意举例10个节点的 setup 报告,则图4.4.2则是 hold 报告 ... 继续看图4.4.3 由于时钟周期不足的关系导致 setup 时序报告跟着亮红灯。因为100Mhz 仅有的10ns 时钟周期不足以给足 31ns 左右的薪水(Data Delay)。为此我们需要将时钟周期加宽到 40ns 左右。
图4.4.3 打开 Set Multicycle Path 界面。
我们先打开 Set Multicycle Path,如图 4.4.3所示,该界面在 Constraints 菜单下调出。
图4.4.4 全局节点的节点减频4倍。
接下来我们要将 From 和 To 均设为主时钟-CLK,然后双双为 setup 和 hold的 Value 填入 4与3,Refrence clock 皆为End,然后点击 Run(如图4.4.4所示)。
set_multicycle_path -from [get_clocks {CLK}] -to [get_clocks {CLK}] -setup -end 4
set_multicycle_path -from [get_clocks {CLK}] -to [get_clocks {CLK}] -hold -end 3
然后两条约束命令会自动添加到 sdc 文件下。以上的设置是告诉TimeQuest所有与CLK有关的节点都是用4个时钟周期干活。再双击 Update Timing 该约束命令就会生效。
图4.4.5随意举例10个节点的 setup 报告。(约束之后——频率100Mhz时钟周期10ns)
图4.4.6随意举例10个节点的 hold报告。(约束之后——频率100Mhz时钟周期10ns)
然后再随意举例10个节点的 Setup 和 hold 报告看看,不堪入目的满江红基本上也消失的无影无踪。读者稍微注意一下图4.4.5的 setup relationship,不是原先的10ns而是40ns。
接着笔者再重新打开 Delay_monster ,然而 sdc 文件中的时钟约束从 100Mhz(10ns)改为 25Mhz(40ns)看看 ... 代码更动如下所示:
create_clock -name {CLK} -period 10.000 -waveform { 0.000 5.000 } [get_ports {CLK}]
改为
create_clock -name {CLK} -period 40.000 -waveform { 0.000 20.000 } [get_ports {CLK}]
然后再随意举例 10个setup 和 hold 的时序报告 ...
图4.4.7 随意举例10个节点的 setup 报告。(未约束——频率25Mhz时钟周期40ns)
图4.4.8 随意举例10个节点的 end 报告。(未约束——频率25Mhz时钟周期40ns)
同学自己比较一下图4.4.5~6 与 图4.4.7~8 结果是不是一样呢?它们之间的区别就在于:一个是频率为100Mhz加上约束;另一个频率为25Mhz未约束。
“akuei2你到底要讲什么,我越来越糊涂了 ... 拜托了,别再玩弄我的猪脑了!”—— 想必有写同学已经不耐烦了。笔者不是有意捣乱,在此笔者只是想证明一个事情而已,Set Multicycle Path 的功是告诉TimeQuest “某对节点用N个时钟工作”。不过比较特别的是,它不仅可以针对“所有节点”,同样它也可以针对“单对节点”。
此外,笔者也想提醒一件事 ... Set Multicycle Path 充其量是告诉“某对节点用N个时钟工作”或者告诉TimeQuest“某对节点的启动沿和锁存沿有所更动”。实际上该约束命令没有任何能力干涉硬模型的内容。好了!前面只是热身而已,接下来我们会慢慢解剖
Set Multicycle Path 这个命令。
图4.4.9 Setup Relationship 和 Hold Relationship 的理想关系值。
让我们先回忆第一章曾出现过的 Setup Relationship 和 Hold Relationship 亦即建立和保持理想关系值的取值过程,如图4.4.9 所示在默认即理想的情况下 Setup Relationship必定等价与时钟周期亦即10ns,然而 Hold Relationship必定 0ns。不过对于 Set Multicycle Path 来说还有 Setup Value 和 Hold Value 之分,默认情况下 Setup Value 为1,Hold Value为0。
图4.4.10 Setup Value = 4,Hold Value = 0。
实际上,我们知道Setup Relationship仅有10ns 是不足以应付延迟怪兽的胃口,因此将设置 Set Multicycle Path 中的 Setup Value 为4 ... 如图 4.4.10 所示,Setup的虚线箭头
向右移至CLK2的第4个时钟,亦即40ns处,因此 Setup Relationship 增加为40ns。不过糟糕的是,当 Setup Value 更动 Hold 的虚线箭头也跟着移动,指向30ns的地方。
为什么 Setup虚线箭头移动,Hold 虚线箭头也会跟着移动呢?鬼晓得,这是TimeQuest的游戏规则,我们也能遵守而已。随着 Setup Value 更动,Hold 虚线箭头跟着更动,结果 Hold Relationship 也随之增加至 30ns。我们知道 0ns 是 Hold Relationship 的理想值,该值增加只会坏掉节点的时序而已 ... 不相信吗?我们来计算一下 ...
求得保持余量的公式是:(用屁股的话)
保持余量 = 数据抵达时间 - 数据获取时间
Hold Slack = Data Arrival Time - Data Required Time
数据抵达时间 Data Arrival Time = 启动沿 + Tclk1 + Tco + Tdata
数据获取时间 Data Required Time = 锁存沿 + Tclk2 + Th
= 理想保持关系值 + Tclk2 + Th
上述是用屁股求得 Hold Slack 的公式,其中 Data Arrived Time 和Data Required Time 左右该余量是否违规。接下来我们把焦点转向 Data Required Time 的公式,一段抢眼的红字,亦即“理想保持关系值”就是所谓的 Hold Relationship,只要该值增加 Data Required Time 也会增加。该事实也告诉我们,只要 Data Required Time 越接近 Data Arrived Time ,Hold 余量就越难合格。
图4.4.11 Setup Value =4,Hold Value = 3。
为此,我们必须设法降低 Hold Relationship 的值,如图4.4.11 所示 ... 原先的 Hold Value 从 0 改为3,结果 Hold 虚线箭头向左移动3个时钟,最终落在 0ns的地方,因此 Hold Relationship 也减至 0ns。最后我们得到 Setup Value 为4,Hold Value 为3 ,双双对应 Setup Relationship 为 40ns,Hold Relationship 为 0ns 的结果。
图4.4.12 100Mhz 经 Set Multicycle Path 约束与 25Mhz 未被约束,之间的等价关系。
我们稍微换个角度来思考,如图4.4.12 所示一个是经 Set Multicycle Path 约束的 100Mhz,另一个则是未经约束的 25Mhz。无论是上看还是下看,它们之间的关系是完全等价的。
“某对节点用N个时钟工作”只是set multicycle path比较粗略的功能说明而已,实际上Set Multicycle Path就是告诉TimeQuest “指向Setup/Hold 虚线箭头有所更动”。该约束命令不仅针对所有节点,而且也可针对以单对局部,让我们马上试试看 ...
图4.4.13 随意举例10个节点的 setup 报告。
笔者重新打开频率为 100Mhz 而且还未约束的 Delay_monster 模块,然后随便举例10个 节点的 Setup 报告,如图4.4.13 所示 ... 均为满江红。在此,我们只要针对rA[0] ~ rProduct[15] 进行约束 ...
图4.4.14 针对节点 rA[0]~rProduct[15] 执行 Set Multicycle Path 约束。
如图4.4.14 所示,From 选项只要针对 rA[0],To选项则只要针对 rProduct[15] 然后 设置 Setup Value 为4,Hold Value 为3即可,然后点击 Run,下面两段代码就会自动添加在 SDC 文件下。
set_multicycle_path -from [get_cells {rA[0]}] -to [get_cells {rProduct[15]}] -setup -end 4
set_multicycle_path -from [get_cells {rA[0]}] -to [get_cells {rProduct[15]}] -hold -end 3
接着双击 Update Timing Netlist 更新网表,然后在随意举例10个节点看看 ... 不过不同的是 rA[0]~rProduct[15] 有关的违规报告已经从此消失在这个世界上。
图4.4.15 延迟怪兽有关的节点。
32. 0:
33. begin rA <= A; rB <= B; rProduct <= 16'd0; i <= i + 1'b1; end
34.
35. 1:
36. begin rProduct <= Result_U1; i <= 4'd0; end
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
如果一点一点约束的话,最终会耗死自己而已,所以我们必须寻找捷径 ... 图4.4.15是由 Delay_monster.v 在代码行 32~36 想象出来的关系图,亦即 rA,rB 经即时式 Booth乘法器,致 rProduct 有关的所有节点。因此,我们只要按照图4.4.15的信息来执行约束即可。
From 选项方面选择 rA[0]~rA[7],rB[0]~rB[7](无视feeder);To 选线方面选择 rProduct[0]~[15]即可。
From:
[get_cells {rA[0] rA[1] rA[2] rA[3] rA[4] rA[5] rA[6] rA[7] rB[0] rB[1] rB[2] rB[3] rB[4] rB[5] rB[6] rB[7]}]
To:
[get_cells {rProduct[0] rProduct[1] rProduct[2] rProduct[3] rProduct[4] rProduct[5] rProduct[6] rProduct[7] rProduct[8] rProduct[9] rProduct[10] rProduct[11] rProduct[12] rProduct[13] rProduct[14] rProduct[15]}]
图4.4.16 From选线rA[0]~rA[7],rB[0]~rB[7],To选线rProduct[0]~[15]。
再来重复与图4.4.14的操作,结果如图4.4.16所示,然后点击 Run,相关的sdc指令就会添加进入 sdc 文件内(这回代码因为太过沉长所以被阉掉了)。接着双击 Update Timing Netlist 更新网表,等待一段时间以后双击 Report Top Failing Path 看看,同学们会发现什么也没有,亦即“满江红已经消失了 ... 怪兽不再杀人了 ...”。
很好!我们已经成功使用 Set Multicycle Path 压制怪兽暴动之余,也不改动 100Mhz主时钟频率,真是可喜可贺 ... 不过同学们别天真了,放心还是太早,虽然我们已经解决延迟怪兽的暴动事件,但是更可怕的连锁反应却已经在后头发生了 ... 这段话究竟是怎么一回事呢?(To Be Continued ...)
的噩梦
严格来说这章节所要讨论的充其量只是笔者个人的担忧和多心而已,笔者宁愿自己是想多了,不过执着的念头始终让笔者方不小心来,至于为何呢?让我们先返回《时序篇》的年头开始说起 ...
笔者曾说过每个模块消耗几个时钟完成一次性的操作可有不定也有固定,不过更多时候模块都有固定的时钟消耗数来完成一个周期的操作,为了容易记忆笔者称呼这种操作周期为“模块周期”。一个健全的模块周期不仅拥有固定的时钟消耗数,而且什么时候执行甚麽 —— 这些事实是绝对不可改变。错了会打乱整个设计的执行节奏。
继续使用 delay_monster.v 作为例子:
代码4.5.1 delay_monster 的相关操作。
32. 0:
33. begin rA <= A; rB <= B; rProduct <= 16'd0; i <= i + 1'b1; end
34.
35. 1:
36. begin rProduct <= Result_U1; i <= 4'd0; end
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
上面一段代码告诉我们,T0的时候将 A 和 B载入相对应的 rA 与 rB里。在T1的时候,即时式 Booth 乘法器吐出结果,然后将其寄存到 rProduct里,然后操作结束。换换言之,delay_monster 模块一个模块周期仅消耗2个时钟而已。根据理想时序而言,
即时式 Booth 乘法器是触发即时事件的组合逻辑而已,因此一个时钟已经足够。结果说,从 Verilog 的角度上来看 delay_monster.v这个软模型是正确无误的。
不过,一旦这个软模型准备转换为硬模型,噩梦就开始了。默认网表下,即时式 Booth乘法器的延迟压力大约是 30ns左右,很明显频率100Mhz的主时钟,delay_monster 模块仅用2个时钟,亦即20ns是绝对支付不了该组合逻辑的延迟压力 ... 换之频率为25Mhz作为 delay_monster 模块的主时钟,它就拥有80ns的有余时间,应付30ns的延迟压力却却有余。
嘛 ... 这是理所当然的事实吗,不过上述的事实,Set Multicycle Path 约束命令和笔者的忧虑又有什么关系呢?让笔者还是用图来说话吧 ...
图4.5.1 建模图。
图4.5.1是根据上述代码所创建的建模图,虽然与图4.4.15有点相识,不过为了更能详细说明一切,笔者已经变更一些。如果忽略位宽,基本图4.5.1有两个节点,亦即 rA经即时式 Booth乘法器 致 rProduct;又 rB 经 即时式 Booth乘法器 致 rProduct。然后,经过默认网表的评估,即时式 Booth乘法器大延迟压力大约为31ns,为方便绘图在此取值为30ns。
图4.5.2 代码4.5.1 在频率为100Mhz(左图)和频率(25Mhz)的理想时序活动记录。
图4.5.2 是代码 4.5.1 在两个不同频率下,亦即100Mhz 和 25Mhz 的时序活动记录。
我们先将焦点转向右图,右图是时钟频率为 25Mhz 亦即1个时钟周期为40ns。根据代码4.5.1中的步骤0(32~33行)T0的时候 rA 和 rB 被赋值,同时间即时式 Booth乘法器也开始工作。即时式 Booth乘法器的加工时间大约是30ns,换之T1之前亦即30ns的地方,加工结果 Result就已经被吐出,然后在T1之际 rProduct 就会轻松的读入 Result。
接下来,我们把焦点转向图4.5.2的左图 ... 那是主时钟为100Mhz 的情况下,在T0的时候rA 和 rB被赋值,不过即时式 Booth乘法器需要30ns加工时间,因此在T1之际数据还没加工完成,结果 rProduct 读空。Result 在T3的时候才输出,最快也要在 T4的时候才能读取。
在实验四中,虽然我们为 delay_monster 加入 multicycle path 的约束以致时序合格。事实上那只是 TimeQuest 这个数学工具在数字上的计算成功而已,实际中我们还没有配合模块周期来考虑 multicycle path 为模块周期带来的“噩梦”。
再来我们先用硬件的角度去观察 multicycle path 的行为 ...
图4.5.3 某节点被 multicycle path约束为 Setup Value 1/Hold Value 0。
图4.5.3 是某个节点被 multicyle path 约束为 Setup Value1/ Hold Value 0,multicycle path 在假想里就好比在该节点的 CLK2 路径中添加一个枷锁,不过 setup value = 1 与 hold value = 0这种取值有约束和没约束没什么两样,不管怎么样我们还是来观察它的时序活动。(读者稍微注意一下,这个枷锁只是想象上的东西,实际上不存在硬模型的身上)
图4.5.4 某节点Setup Value 1/Hold Value 0的时序活动。
我们假设 reg1在T0发送仅有1个时钟周期的数据,在T1之际 reg2会轻松的读取从 reg1发送过来的数据,结果如图4.5.4所示。
图4.5.5 某节点被 multicycle path约束为 Setup Value 2/Hold Value 1。
图4.5.5是某节点 multicycle path 的约束结果Setup Value 为2 和 Hold Value为1。如果仔细观察,我们会发掘到图4.5.5 与 图4.5.4 有明显的不同,亦即 reg2 没有吐出任何数据,为什么呢?让我们来观察该节点的时序活动 ...(读者再稍微注意一下,这个枷锁只是TimeQuest的想象而已,实际与HDL的理想时序,或者硬模型本身无关)
图4.5.6 某节点Setup Value 2/Hold Value 1的时序活动。
我们依然假设 reg1 在T0 的时候发送仅有1个时钟周期的数据给 reg2,不过有点遗憾的是 ... 由于该节点受到约束,亦即 Setup Value 为2 和 Hold Value 为1,因此CLK2的锁存沿也随着移动,结果 reg2 在T1的时候却发呆;在T2之际,reg2准备读取数据的时候,可怜的它只能读到“空气”。
set multicycle path 该约束命令实际上它会更改启动沿或者锁存沿的触发时候。为了让同学们更有感的了解,我们再来观察图4.5.6节点的不同时序活动 ...
图4.5.7 某节点Setup Value 2/Hold Value 另一个时序活动。
图4.5.7是某节点的不同时序活动,在T0~T2之际reg1连续发送数据1~3,至此之后一直保持沉默。reg2在T1的时候由于受约束的影响结果在原地发呆。在T2之际,亦即
锁存沿1,对应启动沿1,该读的数据也是数据1才对,可是reg2不小心读到数据2。在T3之际,亦即锁存沿2,对应启动沿2,该读的对象也是数据2才对,却又不小心读到数据3。在T4之际,亦即锁存沿3,正reg2要发愤图强的时候,才发现头上已经没有数据可读了。
当同学们看到这里,隐约中是否已经察觉到笔者的忧虑呢?笔者的忧虑就是,当我们用set multicycle path 告诉 TimeQuest 某对节点用N个时钟工作,可是充其量这也是 TimeQuest的妄想而已,因为实际的HDL行为或者硬模型没有任何改变。
代码4.5.1 delay_monster 的相关操作。
32. 0:
33. begin rA <= A; rB <= B; rProduct <= 16'd0; i <= i + 1'b1; end
34.
35. 1:
36. begin rProduct <= Result_U1; i <= 4'd0; end
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
让我们再仔细分析代码4.5.1的一切 ... 在步骤0的时候 rA与rB以后,注 rA和rB自步骤0之后一直驱动即时式 Booth 乘法器,然后i递增以示下一个步骤。在步骤1的时候,rProdcut 读取该乘法器的输出亦即 Result_U1,此时i不在递增以示永远停留在步骤1。简单说就是在步骤0给即时式booth乘法器给源,然后在步骤1读取结果。
图4.5. 8代码4.5.1 在频率为100Mhz加约束 Setup Value 4/Hold Value 3的详细时序活动。
图4.5.8 我们可以看到,经过 setup value = 4和 hold value = 3 的 set multicycle path 约束以后TimeQuest的妄想是:T0之际 delay monster 模块在步骤0给即时式booth乘法器给袁,然后delay monster 给空3个时钟,亦即T1~T3在打盹(3个空步骤),然后在T4的时候,也是 delay monster 在步骤4读取结果。
笔者需要强调,在TimeQuest的妄想中 delay monster 用5个时钟工作,或者说 delay monster 有5个时钟的模块周期。不过delay monster的实际HDL行为,只用2个时钟工作,或者说实际的 delay monster 有2个时钟的模块周期。
很明显,delay monster实际的模块周期与TimeQuest妄想中的 delay monster 模块周期有很大的出入,前者用2个时钟工作,而TimeQuest受 set multicycle path 冲昏头以后却妄想 delay monster 用5个时钟工作。这就是笔者所谓的忧虑。
根据代码 4.5.1 我们知道该 delay_monster 模块周期总共消耗2个时钟,可是根据图4.5.8的结果TimeQuest妄想该模块一共用了5个时钟周期。这种马头不对狗嘴的情形当然不被我们允许啦 ... 在此我们应该更动一下 delay_monster 的hdl内容才行。
不可理喻的Set False Path
笔者曾在实验四里做过一个非常有趣的举动,亦即有关delay_monster模块:其中一种是将100Mhz的主时钟都约束为 Multicycle Path (Setup Value =4,Hold Value =3);另一种则是直接使用25Mhz的主时钟。然而不同的操作却得出一样的结果 ... 借此,奇怪的念头在笔者的猪脑袋里开始萌生 ...
造就时序违规的凶手就是rA[0]~[7],rB[0]~[7]和rProduct[0]~[15]有关的节点而已,其犯罪动机就是在肚子里养了一只延迟怪兽,造成时钟周期不足够喂养它(时钟周期小于延迟压力)。解决的方法也挺简单,只要给足怪兽食物事情就完事休矣。不过根据实验四的结果,由于 Multicycle Path 约束的原因,却不小心打乱模块周期,造成半得倍失的局面。
未解决这个问题,我们必寻找考其他的出路 ... 为此,我们可以接签一下“25Mhz主时钟”的经验。默认网表下,即时式Booth'乘法器的延迟压力大约30ns,结果40ns的时钟周期足够应付它。如果我们换另一个角度来思考,只要借助一下“黏糊关系”的力量
,即时是 100Mhz 的时钟周期,我们只要给足时钟不是万事Okay吗?
代码4.6.1 “黏糊概念”的给足时钟。
32. 0:
33. begin rA <= A; rB <= B; rProduct <= 16'd0; i <= i + 1'b1; end
34.
35. 1,2,3:
36. i <= i + 1'b1;
37.
38. 4:
39. begin rProduct <= Result_U1; i <= 4'd0; end
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
具体一点让我们悄悄代码行33~34是关键 ... 章节4.5笔者也举例出实验四的2个关键条件,其中 rA和rB供源3个时钟以上是最重要的。换句话说,即使我们为即时式 Booth 乘法器提供足够的时钟,但是驱动源 rA 和 rB 不足也会换取失败的结果。第33行代码执行着为 rA 和 rB供源的角色,然而34~35行则停留3个时钟。
简单点说,33行的操作加上35~36的3个时钟给空,供源一共保持4个时钟 ... 如果主时钟为100Mhz,4个时钟亦即40ns。在此,延迟怪兽已经有足够的食物和时间 ... 随之在步骤4中(第38~39行)rProduct就可以轻松的读取结果。
图4.6.1 代码4.6.1的时序活动。
图4.6.1 是代码4.6.1的时序活动 ... 然而时序活动完全按着步骤i的指向活动着,如T0~T3的时候 rA和rB持续为即时式 Booth 乘法器供源之际,有给足加工时间;在T4的时候,rProduct如步骤i所愿般读取输出 Result。结果而言,TimeQuest的妄想中的delay monster 就完全符合该模块实际的HDL行为了。
延迟怪兽受死吧!
图4.6.2 部分与rA,rB和rProduct有关节点的 Setup报告。
双击 Report Top Failing Path 随便举例 200 个节点的 Setup 报告,在此我们依然看见一片满江红 ... 这是什么一回事?我们不是按照理论给予 rA,rB和rProduct 有关节点足够的供源和时间吗?为何会这样?原因很简单,到目前位置我们还没有告诉 TimeQuest“节点rA~rProduct 或者节点 rB~rProduct 是用4个时钟工作”。不过,实验五笔者不像使用 set multicycle path 这个命令而是其他。
让我们回忆第二章的内容,我们知道 TimeQuest 既是笨蛋又是勤劳的数学工具,它会一点一点检查所有模块的节点,然而 TimeQuest 只会用“1个时钟的情况”去检查节点(Set Multicycle Path约束例外)。
为此HDL行为的“给足时钟”是人为,如果不使用类似 set multicycle path 的约束命令,TimeQuest是不晓得谋对节点用N个时钟工作,可是实验五笔者又不想用它。首先,让我们再一次观察图4.6.2 右边的 Data Delay,平均值为30ns ,而 delay monster的HDL内容已经给足4个时钟的供源和空时间。
虽然我们在心里上十稳九拿该模块不会有问题,不过汪海一片红始终碍眼和碍事。因此
,我们要使用另一个约束命令,亦即 Set False Path 要TimeQuest 无视相关的节点分析。
Set False Path 的用法很简单,之下输入相关的节点,无视操作就会被执行。
图4.6.3 调出 Set False Path 界面。
先从 Constraints 菜单中调出 Set False Path 界面,然后将 From 选项填入 rA[0]~[7] 和 rB[0]~[7],至于 To 选项填入 rProduct[0]~[15],接着点击 Run即可。
set_false_path -from [get_cells {rA[0] rA[1] rA[2] rA[3] rA[4] rA[5] rA[6] rA[7] rB[0] rB[1] rB[2] rB[3] rB[4] rB[5] rB[6] rB[7]}] -to [get_cells {rProduct[0] rProduct[1] rProduct[2] rProduct[3] rProduct[4] rProduct[5] rProduct[6] rProduct[7] rProduct[8] rProduct[9] rProduct[10] rProduct[11] rProduct[12] rProduct[13] rProduct[15]}]
上述代码就会自动加入 sdc 文件中,然后双击 Update Timing Netlist,最后双击 Report Top Failing Path。同学们会发觉到 TimeQuest 不再反馈任何时序的违规信息。
Set False Path 约束命令式一个不可理喻的东西,它会要求 TimeQuest 在分析时无视相关的节点 ... 同时 Set False Path 也是一个危险的约束命令,如果同学们没有足够的信心,千万不要随意使用。
经历许久人与怪兽的战斗终于可以划上句号,为了解决延迟怪事的暴动事件,无论遇上多少失败,身为勇士的同学即屹立不倒,无数次掉落的圣剑也不停举起 ... 如此坚持仅为了我们背后的家园还有家人 ....
嘛 ... 开个玩笑而已,放松点少年。最后让我们来总结一下在实验三提出的问题,即是:“Quartus的编译器有没有足够的智能保证任何节点都合格?如果是又如何优化?如果不是又怎么约束?用什么约束命令?”在此我们可以这样总结 ...
事实上,编译器它不会聪明到可以自动优化延迟怪兽给时序带来的违规,因此我们需要约束相关的节点。用到的命令有 Set Multicycle path ,该功能宛如告诉TimeQuest某对节点用N个时钟工作,实际上是告诉TimeQuest某对节点的启动沿或者锁存沿的触发行为有所更改。虽然使用该约束命令可以使得TimeQuest在分析上足以解决问题,可是却导致TimeQuest的妄想和实际的HDL行为产生出入。所以要小心慎用。
此外,Set False Path 是不可理喻的约束命令,它会要求 TimeQuest 无视相关的节点。使用 Set False Path 之前,最好确认自己是否有足够的理论自己的做法是对的。如果实验五的delay monster 恰恰好仅用2个时钟工作,使用该命令反而会弄巧成拙。
这一章我们讨论了有关内部延迟因数的种种,借此进一步掌握 TimeQuest 的各个操作还有各种微妙的特性,当然,重点部分还是约束命令的了解上。结束之前让我们简要的回顾一下各个约束命令的内容:
derive_pll_clocks
derive_clock_uncertainty
是pll专用的约束命令,其中 derive_pll_clocks 是专门为 pll 打造的懒人版 create clock 约束命令。至于后者就意义不明了 ... 好像是和制作工艺和时钟抖动有关系,嗯 ... 往后有机会再接触也不迟。
set_multicycle_path
这个约束命令粗略点就是告诉TimeQuest某对节点用N个时钟工作,实际上它是告诉TimeQuest某对节点的启动沿和锁存沿的触发行为有所更改。笔者想定读者一定会对
reference clock 的Start选项感到好奇,因为在实验中笔者只顾着用 End,结果没机会讲解。
嘛 ... 同学们就暂时忘了它吧,以目前的实验实在做找不到任何机会,如果同学们有兴趣可以自行查看官方的手册,好像叫做 multicycle path exception,里边有介绍原理和用法。笔者个人更注重实践,往后有机会再做打算。
set_false_path
这个无赖即不可理喻的约束命令基本上没什么好说,该功能是命令 TimeQuest 无视某个节点。嘛 .. 只要用得好它还是大家的好朋友,相反的,如果用不好就要遭殃了。
基本上就是这些而已,同学们可能会为笔者没有提及寄存器相关的延迟因数而感到窝囊
,对吧?这个嘛 ... 下一章一定会有啦,目前本章的目的是让鸭仔适水和作热身而已。学习应该慢慢来,是不是有道理呢?
最后让我们稍微反思一下,TimeQuest 如果没有结合 Verilog 一起探讨的话,基本上 TimeQuest 宛如硬邦邦的数学工具而已。相反,一旦结合 Verilog 各种特质来探讨,意外的我们会发现许多有趣的认识。果然相交一年,完成《整合篇》之后再看回 TimeQuest,让笔者有全新的认识,之前的辛劳都是有价值的。请问同学们是否也有同样的感受呢?