声明:本文为黑金动力社区(http://www.heijin.org)原创教程,如需转载请注明出处,谢谢!
黑金动力社区2013年原创教程连载计划:
http://www.cnblogs.com/alinx/p/3362790.html
《FPGA那些事儿--TimeQuest 静态时序分析》完整版下载地址:
http://www.heijin.org/forum.php?mod=viewthread&tid=25284&extra=page%3D1
第七章:供源时钟与其他
7.1 供源时钟①
7.2 供源时钟②
7.3 供源时钟③
7.4 引脚电容性与上下坡信号
7.5 介电参数与延迟时间
7.6 IO口
7.7 SDRAM实例
总结:
第七章:供源时钟与其他
7.1 供源时钟①
图7.1.1 供源时钟的外部模型。
什么是供源时钟?想必同学们一定不陌生,fpga是一个无私的孩子,有时候会牺牲一个管脚,将自己的时钟源透过该管脚,输出给外部ic,如图7.1.1所示 ... 频率为100Mhz的fpga_clk 作为fpga的时钟信号,该fpga_clk经过 fpga内部以后,再路由某个管脚将新命名的 ext1_clk与ext2_clk输出给外部ic,100Mhz的ext1_clk与ext2_clk因此称为供源时钟。(我们先假设任何时钟信号都没有发生抖动)
供源时钟既是内部时钟信号,也是外部时钟信号,因此它夹杂了内部延迟因数,与外部延迟因数。到底为什么供源时钟会那么大牌,以至于我们要特别关注它呢?同学们有所不知了,图7.1.1中的 fpga2ext1与fpga2ext2 是提供源,事实上它们的存在感很弱,如果我们不强调它们的存在,TimeQuest 不容易发现它们。
供源时钟基本的问题是如何将它的存在感加强以至于TimeQuest认识到呢?这时候我们就要使用 create_generated_clock 这个约束命令了。
实验十一 供源时钟与 Create Generated Clock
实验十一是基于实验七,两者之间的区别就在于,实验十一的ext1_clk 与 ext2_clk是供源时钟而实验七不是。先让我们分析一下实验十一的 HDL 代码:
center_module.v
1. module center_module 2. ( 3. input CLK, 4. input RSTn, 5. input [3:0]Din, 6. output [3:0]Dout, 7. output ext1_clk,ext2_clk 8. ); 9. /********************************************/ 10. 11. reg [3:0]rData; 12. 13. always @ ( posedge CLK or negedge RSTn ) 14. if( !RSTn ) 15. begin 16. rData <= 4'd0; 17. end 18. else 19. begin 20. rData <= Din; 21. end 22. 23. /********************************************/ 24. 25. assign Dout = rData; 26. assign ext1_clk = CLK; 27. assign ext2_clk = CLK; 28. 29. /********************************************/ 30. 31. endmodule
第7行声明 ext1_clk 与 ext2_clk 的输出,然后在第26~27行设置 ext1_clk 与 ext2_clk的驱动源,亦即 CLK。
再者求出图7.11的各种延迟信息:
fpga_clk delay 2ns ext1_clk delay 1ns ext2_clk delay 3ns data delay min 2ns data delay max 4ns Tsu/Th/Tsu 0.5ns fpga2ic1 ? fpga2ic2 ? input max = <ext2fpga delay max> + ext_Tco = 4ns + 0.5 = 4.5ns input min = <fpga2ext delay min> + ext_Tco(或者 ext_minTco) = 2ns + 0.5ns = 2.5ns output max = <fpga2ext delay max> + ext_Tsu = 4ns + 0.5ns = 4.5ns output min = < fpga2ext delay min > - ext_Th = 2ns - 0.5ns = 1.5ns
寻找fpga2ext1 与 fpga2ext2的延迟信息是我们在实验十一的首要任务,实际上fpga2ext1 是 ext1的一部分,换之 fpga2ext2 是 ext2_clk 的一部分,不过fpga2ext1 与 fpga2ext2 是内部时钟信号,也是内部延迟因数,而ext1_clk 与 ext_clk 是外部时钟信号,也是外部延迟因数。外部延迟信息我们可以直观晓得,反之内部延迟信息必须透过 TimeQuest计算。
结果而言,如何告诉 TimeQuest 该 fpga2ext1 与 fpga2ext2 是存在,我们就要透过约束命令create_generated_clock 的力量,然后再寻找它们的延迟值。首先打开实验十一,然后建立同名的 sdc 文件,再者将下述代码拷贝进去:
#************************************************************** # Create Clock #************************************************************** create_clock -name {fpga_clk} -period 10.000 -waveform { 0.000 5.000 } [get_ports {CLK}] #************************************************************** # Set Clock Latency #************************************************************** set_clock_latency -source 2.000 [get_clocks {fpga_clk}] set_clock_latency -source 1.000 [get_clocks {ext1_clk}] set_clock_latency -source 3.000 [get_clocks {ext2_clk}] #************************************************************** # Set Input Delay #************************************************************** set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[0]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[0]}] set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[1]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[1]}] set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[2]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[2]}] set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[3]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[3]}] #************************************************************** # Set Output Delay #************************************************************** set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[0]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[0]}] set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[1]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[1]}] set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[2]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[2]}] set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[3]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[3]}]
上述的代码行基本上和实验七差不多,区别仅是实验十一将其中ext1_clk与ext2_clk的虚拟时钟拿掉而已。接下来打开TimeQuest ,手动建立 model fast 的网表,然后就是惯例的双击 Read SDC File,还有 Update Timing Netlist。
图7.1.2 create generated clock 界面。
从 Constraint 菜单下打开 create generated clock 界面 ... Clock name 是供源时钟的命名,在此是 ext1_clk与 ext2_clk;source对应时钟源,在此是CLK,余下的 Relationship to source 选项是改变供源时钟的行为,我们暂时先不管。然而 Targets 是供源时钟的输出口,在此是 ext1_clk 与 ext_clk2。
#************************************************************** # Create Generated Clock #************************************************************** create_generated_clock -name {ext1_clk} -source [get_ports {CLK}] -master_clock {fpga_clk} [get_ports {ext1_clk}] create_generated_clock -name {ext2_clk} -source [get_ports {CLK}] -master_clock {fpga_clk} [get_ports {ext2_clk}]
完后,上述代码会自添加在 sdc 文件下,然后双击 Update Timing Netlist 来更新网表。
图7.1.3 Report Path界面(左),路径结果(右)。
接下来双击 Report Path快捷,然后会跳出图7.1.3左图的界面,任所有选项放空,而Path 深度设置1000,上述的设置亦即将所有路径列出。完后会跳出右图,其中CLK~ext1_clk 与 CLK~ext2_clk 路径就是我们要寻找的结果。两者路径有2.562ns的延迟,不过这是内部数据路径与内部时钟路径总和值,实际的 setup 余量与 hold 余量计算中,它们会个别列出。
图7.1.4 实验十一(左)与实验七(右)的setup时序报告。
图7.1.4是实验实验十一与实验七的 setup报告比较,同学们请注意Data Delay,很明显左图的结果必右图的结果还要大上记号,为什么会这样呢?原因是供源时钟 ext1_clk与 ext2_clk 的内部延迟信息已经计算进去。不管怎么样,我们还是将先展开节点 Din[0]~rData[0] 的详细信息,看看里边发生什么了。
图7.1.4 实验十一(左)与实验七(右)节点Din[0]~rData[0]的setup详细信息。
实验十一与实验七之间的区别就在于实验十一的 ext1_clk 与 ext2_clk 是供源时钟,而实验七不是。同学们请注意一下 clock network delay,左图是3ns右图是1ns,其中的差别是2ns,这是供源时钟造就而外的时钟路径延迟。再者是 data path,左图是3.543,而右图是1.898ns,其中有1.645ns的差别,这是供源时钟造就而外的数据延迟。接着将焦点切换到 Data Required Path 下,很明显左图的各种延迟数值比起右图高出一点,这一切也是供源时钟造成的而外延迟。
图7.1.5 实验十一(左)与实验七(右)节点rData[0]~Dout[0]的setup详细信息。
图7.1.5 是节点 rData[0]~Dout[0]的详细setup信息,其中 Data Arrival Path的各种信息,很明显左图比右图更多一些,这些都是供源时钟的错。换之,Data Required Path 的 clock network delay ,左图有5ns延迟,而右图有3ns延迟,其中有2ns的差别,这也是供源时钟造就的而外时钟路径延迟。
图7.1.6 实验十一(左)与实验七(右)的hold时序报告。
图7.1.6是实验十一与实验七的hold报告比较,至于 data delay 左图比起右图高出不少,不用说都是供源时钟惹的祸。
图7.1.7 实验十一(左)与实验七(右)节点Din[0]~rData[0]的Hold详细信息。
图7.1.7是节点 Din[0]~rData[0]详细的hold信息比较,先是 Data Arrival Path 的 clock network delay,左图有3ns延迟,而右图有1ns延迟,其中有2ns的时钟路径的差别。此外左图的 data path 比起右图的data path 同样也增加不少。
图7.1.8 实验十一(左)与实验七(右)节点rData[0]~Dout[0]的Hold详细信息。
图7.1.8 是节点 rData[0]~Dout[0]详细的hold信息比较,同学们注意 Data Required Path的 clock network delay ,左图有5ns延迟,而右图有3ns延迟,其中有2ns的时钟路径延迟差别。至于 Data Arrival path 的 data path,左图比右图多出一点。
经过上述的内容,我们可以这样结论:
实验十一与实验七相比较,实验十一的 ext1_clk 与 ext2_clk 是供源时钟,而实验七却不是。供源时钟造就了而外的 data path 延迟,此外实验十一的 ext1_clk 与 ext2_clk与实验七相比多出2ns的时钟路径延迟。因此我们可以说,图7.1.1中的fpga2ext1与fpga2ext2都有2ns的延迟。
同学们请注意,实验十一的重点再也不是时序是否合格,也不是 fpga2ext1 与 fpga2ext2的延迟信息,而是:当我们使用 create_generated_clock 约束命令告诉TimeQuest相关供源时钟的信息以后,TimeQuest就会将供源时钟造成的而外延迟信息也计算进去。结果而言,实验十一与实验七会取得不同的结果,那怕 ext1_clk 与 ext2_clk的角色改变一点点而已。
7.2 供源时钟②
图7.2.1 供源时钟的外部模型。
图7.2.1与图7.1.1的外部模型相较起来,图7.2.1的fpga2ext1与fpga2ext2多出了减频1/2的模块。结果,这一回我们必须更动一下 create_generated_clock 的用法,不然的话TimeQuest是无法知晓 fpga2ext1 与 fpga2ext2 是否存在。让我们先瞧一瞧到底,究竟center_module 更动了什么?
center_module.v
1. module center_module 2. ( 3. input CLK, 4. input RSTn, 5. input [3:0]Din, 6. output [3:0]Dout, 7. output ext1_clk,ext2_clk 8. ); 9. /********************************************/ 10. 11. reg ext_div2; 12. 13. always @ ( posedge CLK or negedge RSTn ) 14. if( !RSTn ) 15. begin 16. ext_div2 <= 1'b0; 17. end 18. else 19. begin 20. ext_div2 <= ~ext_div2; 21. end 22. /********************************************/ 23. 24. reg [3:0]rData; 25. 26. always @ ( posedge CLK or negedge RSTn ) 27. if( !RSTn ) 28. begin 29. rData <= 4'd0; 30. end 31. else 32. begin 33. rData <= Din; 34. end 35. 36. /********************************************/ 37. 38. assign Dout = rData; 39. assign ext1_clk = ext_div2; 40. assign ext2_clk = ext_div2; 41. 42. /********************************************/ 43. 44. endmodule
第7行声明了 ext1_clk 与 ext2_clk 的输出口,然而第11~21行描述了ext_div2寄存器的1/2分频行为,最后在第40~41行将 ext_div2 驱动 ext1_clk 与 ext2_clk。
实验十二: 供源时钟与寄存器与 create_generated_clock
打开实验是十二,然后建立同名的sdc文件,接着将下面的代码拷贝进去。
#**************************************************************
# Create Clock
#**************************************************************
create_clock -name {fpga_clk} -period 10.000 -waveform { 0.000 5.000 } [get_ports {CLK}]
再者,打开TimeQuest手动建立 model fast 网表,然后惯例的连打 Read SDC File 与 Update Timing Netlist。在此我们只有 fpga_clk 的系统时钟而已,我们必须经 create generated clock 告诉TimeQuest还有ext1_clk 与 ext2_clk 这两个时钟。
图7.2.2 模糊的供源时钟 ext1_clk 与 ext2_clk声明。
一般上我们都会直接从 create generated clock 界面下,命名 ext1_clk 与 ext2_clk,时钟源为 CLK,然后在Divide by 的选项下输入2,最后的 Targets 分别对应 ext1_clk 与 ext2_clk输出口,如图7.2.2所示。
Warning: No paths exist between clock target "ext1_clk" of clock "ext1_clk" and its clock source. Assuming zero source clock latency.
Warning: No paths exist between clock target "ext2_clk" of clock "ext2_clk" and its clock source. Assuming zero source clock latency.
不过当我们再度双击 Update Timing Netlist 更新网表,此时 TimeQuest就会反馈找不到 ext1_clk 与 ext2_clk 驱动源的信息,如上述信息所示。这就俗称模糊供源时钟声明,因为图7.2.2的告知表现让TimeQuest这个笨蛋仅晓得 ext1_clk 有1/2分频,时钟源是 CLK,对应输出口是 ext1_clk;或者,ext2_clk 有1/2 分频,时钟源也是 CLK,对应输出口则是 ext2_clk。,可是TimeQuest却不清楚中间的经过,也不会自己去寻找 ... 真是蠢死了!因此,我们必须手动建立 ext1_clk 与 ext2_clk 的完全经过 ...
CLK输入口 → ext_div2寄存器 → ext1_clk 输出口
→ ext2_clk输出口
在这里,我们知道 ext1_clk 与 ext2_clk 供源时钟的声明,各自都有两个箭头,然而每一个箭头就代表一次 create generate clock 声明。ext1_clk供源时钟的声明先是 CLK输入口到 ext_div2寄存器,然后ext_div2到 ext1_clk输出口;而 ext2_clk 供源时钟的声明可是ext_div寄存器致 ext2_clk 输出口。这里有3个箭头,亦即我们需要声明 create generate clock 三次。
图7.2.3 清晰的供源时钟 ext1_clk 与 ext2_clk声明。
不保存之前的约束过程,然后重新打开TimeQuest再手动建立 model fast 网表,随之惯例的连打操作,亦即先双击 Read SDC File,然后又在双击 Update Timing Netlist。接下来从 Constraint 菜单下调出 create generated clock 界面,按图7.2.3声明。左图是声明名为CLK2ext_div2的时钟路径,时钟源为 CLK,对应目标是 ext_div2寄存器。
中图是声明名为 ext1_clk 的供源时钟,时钟源为 ext_div2寄存器,对应输出口是ext1_clk;至于右图是声明名为 ext2_clk 的供源时钟,时钟源为 ext_div2寄存器,对应输出口是 ext2_clk。完后下列代码会自动添加在 sdc文件下。
#**************************************************************
# Create Generated Clock
#**************************************************************
create_generated_clock -name {CLK2ext_div2} -source [get_ports {CLK}] -master_clock {fpga_clk} [get_registers {ext_div2}]
create_generated_clock -name {ext1_clk} -source [get_registers {ext_div2}] -divide_by 2 -master_clock {CLK2ext_div2} [get_ports {ext1_clk}]
create_generated_clock -name {ext2_clk} -source [get_registers {ext_div2}] -divide_by 2 -master_clock {CLK2ext_div2} [get_ports {ext2_clk}]
图7.2.4 时钟简报。
然后双击 Update Timing Netlist 更新网表,接着顺便双击 Report All Summaries调出所有相关的时钟声明,结果如图7.2.4所示。其中 CLK2ext_div2 的作用仅告知 TimeQuest 有一个频率为100Mhz的时钟路径从 CLK 输入口连接至 ext_div2寄存器。至于 ext1_clk供源时钟则是 50Mhz的频率,从 ext_div2寄存器连接至 ext1_clk输出口;换之 50Mhz频率的供源时钟也是从 ext_div2寄存器连接至 ext2_clk 输出口。
#************************************************************** # Set Clock Latency #************************************************************** set_clock_latency -source 2.000 [get_clocks {fpga_clk}] set_clock_latency -source 1.000 [get_clocks {ext1_clk}] set_clock_latency -source 3.000 [get_clocks {ext2_clk}] #************************************************************** # Set Input Delay #************************************************************** set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[0]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[0]}] set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[1]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[1]}] set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[2]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[2]}] set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[3]}] set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[3]}] #************************************************************** # Set Output Delay #************************************************************** set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[0]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[0]}] set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[1]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[1]}] set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[2]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[2]}] set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[3]}] set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[3]}]
完后将上述各种约束信息手动添加致 sdc 文件下,然后重新打开 TimeQuest。继续惯例的连打操作,知道网表更新。
图7.2.5 路径报告。
接下来双击 Report Path,就会弹出图7.2.5的左图,选项任空,深度为1000 ... 执行以后就会弹出右图。同学们稍微注意第3路径CLK~ex_div,和15路径ext_div2~ext2_clk还有第16路径 ext_div2~ext1_clk,都有延迟信息。结果而言,TimeQuest已经知晓 ext1_clk 与 ext2_clk 供源时钟的存在。
到目前位置实验十一的目的已经完成,我们先用 create generated clock 将 CLK输入口连接至 ext_div2寄存器,声明为 CLK2ext_div2。接着再用同样的约束命令将 ext_div2寄存器连接至 ext1_clk输出口,还有ext2_clk输出口。然后再经过 Report Path 取得相关路径的延迟信息,亦即TimeQuest已经认识 ext1_clk 与 ext2_clk供源时钟。
7.3 供源时钟③
图7.3.1 供源时钟的外部模型。
最后一种供源时钟就是借助pll 的力量,这也是供源时钟最常的产生方法。如图7.3.1所示,fpga_clk 进入fpga内部,接着经过pll后产生 ext1_clk 与 ext2_clk供源时钟。让我们先瞧瞧 hdl 的描述内容:
center_module.v
1. module center_module 2. ( 3. input CLK, 4. input RSTn, 5. input [3:0]Din, 6. output [3:0]Dout, 7. output ext1_clk,ext2_clk 8. ); 9. /********************************************/ 10. 11. wire ext_CLK; 12. 13. pll_module pll 14. ( 15. .inclk0 ( CLK ), 16. .c0 ( ext_CLK ) 17. ); 18. 19. /********************************************/ 20. 21. reg [3:0]rData; 22. 23. always @ ( posedge CLK or negedge RSTn ) 24. if( !RSTn ) 25. begin 26. rData <= 4'd0; 27. end 28. else 29. begin 30. rData <= Din; 31. end 32. 33. /********************************************/ 34. 35. assign Dout = rData; 36. assign ext1_clk = ext_CLK; 37. assign ext2_clk = ext_CLK; 38. 39. /********************************************/ 40. 41. endmodule
第7行声明ext1_clk 与 ext2_clk的输出口,第11~17行实例化pll模块,而pll的输入是 CLK输出是 ext_clk。最后在第36~37行将 ext_clk驱动 ext1_clk 与 ext2_clk。
图7.3.2 pll设置。
图7.3.2是pll的设置内容,inclk0是100Mhz的时钟信号,而c0也是100Mhz的时钟信号。
实验十三 供源时钟与pll与 create generated clock
首先打开实验十三,然后建立同名 sdc 文件,接着将下面的约束代码拷贝进去。
#************************************************************** # Create Clock #************************************************************** create_clock -name {fpga_clk} -period 10.000 -waveform { 0.000 5.000 } [get_ports {CLK}] #************************************************************** # Create Generated Clock #************************************************************** derive_pll_clocks
接下来打开 TimeQuest,手动建立 model fast 网表,随之执行惯例的连打操作,亦即双击 Read SDC File 与 Update Timing Netlist。
图7.3.3 时间简报。
完后双击Report All Summaries,就会弹出图7.3.3的时间简报,此时只有pll的clk[0],和fpga_clk,却没有供源时钟 ext1_clk与ext2_clk。
图7.3.4 路径报告。
再者双击 Report Path任选项放空,执行以后就会出现图7.3.4右图的情形 ... 亦即第5与第6的路径,同学们有没有看到呢?pll致 ext1_clk 还有 ext2_clk,至于第19的路径则是CLK致pll。
图7.3.5 声明 ext1_clk与 ext2_clk供源时钟。
接着从 Constraint 菜单下打开 create generated clock,然后根据图7.3.5的选择设置并且声明 ext1_clk 与 ext2_clk供源时钟,不过此时的source对应 net 资源内的 pll_clk[0]。
完后,下面的代码会自动添加在 sdc文件下 ...
#**************************************************************
# Create Generated Clock
#**************************************************************
create_generated_clock -name {ext1_clk} -source [get_nets {pll|altpll_component|_clk0}] -master_clock {pll|altpll_component|pll|clk[0]} [get_ports {ext1_clk}]
create_generated_clock -name {ext2_clk} -source [get_nets {pll|altpll_component|_clk0}] -master_clock {pll|altpll_component|pll|clk[0]} [get_ports {ext2_clk}]
图7.3.6 时间简报。
同学们让我们乘胜追击,再双击 Report All Summaries 调出时间简报,结果如图7.3.6所示,此时已经出现 ext1_clk 与 ext2_clk的供源时钟,话句话说 ... TimeQuest已经认识成功,接下来我们就可以进行其他约束了 ... 同学们麻烦将下列的代码拷贝进去sdc 文件里。
#**************************************************************
# Set Clock Latency
#**************************************************************
set_clock_latency -source 2.000 [get_clocks {fpga_clk}]
set_clock_latency -source 1.000 [get_clocks {ext1_clk}]
set_clock_latency -source 3.000 [get_clocks {ext2_clk}]
#**************************************************************
# Set Input Delay
#**************************************************************
set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[0]}]
set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[0]}]
set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[1]}]
set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[1]}]
set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[2]}]
set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[2]}]
set_input_delay -add_delay -max -clock [get_clocks {ext1_clk}] 4.500 [get_ports {Din[3]}]
set_input_delay -add_delay -min -clock [get_clocks {ext1_clk}] 2.500 [get_ports {Din[3]}]
#**************************************************************
# Set Output Delay
#**************************************************************
set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[0]}]
set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[0]}]
set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[1]}]
set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[1]}]
set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[2]}]
set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[2]}]
set_output_delay -add_delay -max -clock [get_clocks {ext2_clk}] 4.500 [get_ports {Dout[3]}]
set_output_delay -add_delay -min -clock [get_clocks {ext2_clk}] 1.500 [get_ports {Dout[3]}]
最后使出决定性攻击,亦即双击 Update Timing Netlist 更新网表。
图7.3.7 实验十三(左)与实验十一(右)的setup时序报告。
图7.3.8 实验十三(左)与实验十一(右)的hold时序报告。
图7.3.7是实验十三与实验十一之间的 setup时序报告比较,反之图7.3.8是hold时序报告的比较,笔者虽然知道此刻的比较没有任何实际的意义,不过我们看看也无妨。
到目前为止,我们已经知道分别有3个情况的供源时钟会出现在TimeQuest里,亦即CLK直接驱动供源时钟;CLK驱动寄存器,寄存器再驱动供源时钟;或者CLK驱动PLL,再由PLL驱动供源时钟。不过不管哪一样,将供源时钟告诉TimeQuest的方法都大同小异。
7.4 引脚电容性与上下坡信号
Warning: Found N output pins without output pin load capacitance assignment
每当我们编译完毕某个模块的时候,Quartus 就会反馈给我们知道,某个输出管脚忘记设置电容性,如上所示。事实上电容性,比起延迟特性,它更接近电器特性,不过本身也夹杂一些延迟特性 ... 用语言描述会很抽闲,笔者还是用图来说话吧。
图7.4.1 1pf输出口的寄存器(左),有tRise与tFall的时序图(右)。
为了方面解释我们先假设 1pf ≈1ns,如图7.4.1的左图所示有一对节点,reg1向reg2先发送一个时钟周期的拉高,然后有发送一个周期的拉低,然而右图就是时序图。对TimeQuest而言,它是没有办法绘出斜坡的黑色虚线,而是红色的实线。只有电器特性的信号才能使信号上坡或者下坡,虽然TimeQuest是针对物理时序的数学工具,但它本身却不参与电器特性的活动。
虽说如此,信号的上下坡却会影响物理时序,因此TimeQuest必须采取特殊的手段。tRise与tFall延迟信息,事实上它们与实际的延迟信息却有不同的行为,这话何解呢?一般的延迟信息会使信号向右移动,然而 tRise 与 tFall 却像寄生虫一样寄生在数据里,使部分数据无效化。如右图所示,数据D有10ns的建立时间,不过tRise却吃掉开始的1ns,结果为9ns。
此外,图7.4.1 还隐藏一个重要信息,亦即 tRise与tFall仅对建立时间有效,换句话说 tRise 与 tFall 也只是针对发送方,如左图的reg1。至于reg2作接收方,它会免疫 tRise 与 tFall 的寄生攻击。
图7.4.3 clk1有8ns延迟的时序图。
我们再假设 clk1有8ns的延迟,自然而然数据Q也会向右边移动8ns,从而导致建立时间为2ns,不过再加上 tRise 的1ns寄生攻击,数据前面的1ns部分已经失效,因此建立时间为1ns。在第18ns的位置,数据Q由高变低,此时tFall寄生物攻击数据,数据失去1ns的建立时间,让原本2ns的建立时间成为1ns。
图7.4.4 建立关系的等价图。
图7.4.4是建立关系的等价图,我们知道数据有延迟,启动沿也会跟着移动,结果说 ... 原本在0ns的启动沿受8ns的延迟后,落在8ns的位置,不过再加上tRise 或者 tFall的寄生攻击,启动沿最后倒在9ns的位置,导致建立关系为1ns。然而时间为10ns~20ns的时候,数据Q也遇见同样的状况。
认真思考一下,图7.4.3与图7.4.4它们之间的建立时间与建立关系都为1ns。结果我们可以如此断定,tRise或者tFall的寄生攻击,针对建立时间也针对建立关系,换之它们同样也针对启动沿和下一个启动沿(但不是与保持关系有关的下一个启动沿)。只要理清上述的重点,我们变可以采取措施。
图7.4.5 假想外部模型。
假设有一个假想的外部模型,其中ic1的io口有1pf的电容性,而fpga的io口也有1pf的电容性。此外我们也知道 ic1致fpga是ic2fpga的外部模型,亦即ic1发送fpga接收;换之fpga致ic2是fpga2ic的外部模型,亦即fpga发送ic2读取。而fpga的hdl内容如下:
1. module center_module 2. ( 3. input CLK, 4. input RSTn, 5. input [3:0]Din, 6. output [3:0]Dout 7. ); 8. /********************************************/ 9. 10. reg [3:0]rData; 11. 12. always @ ( posedge CLK or negedge RSTn ) 13. if( !RSTn ) 14. begin 15. rData <= 4'd0; 16. end 17. else 18. begin 19. rData <= Din; 20. end 21. 22. /********************************************/ 23. 24. assign Dout = rData; 25. 26. /********************************************/ 27. 28. endmodule
在第5行声明的 Din是ic1给fpga的输入口,而第6行声明的Dout则是fpga给ic2的输出口。换句话说,Din~rData有4个节点,而rData~Dout有又4个节点。首先我们可以使用 set_clock_uncertainly 的约束命令告诉 TimeQuest相关的电容性信息 ...
图7.4.6 使用 set_clock_uncertainly确保电容性。
如图7.4.6所示,左图是ic2fpga的外部模型,亦即针对节点Din~rData,我们在from clock 选择 ext1_clk 然后 to clock 选择 fpga_clk,至于analysis type 使能 Setup 最后在 Uncertainty 中输入1ns的值。同样,右图是fpga2ic的外部模型,亦即针对节点rData~Dout,而from clock 选择 fpga_clk,to clock 则是 ext2_clk2,analysis type也是 setup,uncertainty也是1ns。
完后,节点Din~rData与节点rData~Dout的setup分析都会保留1ns的setup余量,只要余量一天为正直,tRise与tFall的寄生攻击也不会坏掉数据的有效性。
除了使用 set_clock_uncertainly 约束命令以外,我们还可以使用 input/output公式来确保 tRise 和 tFall ... 首先我们必须知道电容性仅影响建立关系,而 input/output max也是针对建立关系而已,因此我们可以这样更改 input/output 公式。
input max = <ext2fpga delay max> - < destination reg clk delay - source reg clk delay > + ext_Tco
= <ext2fpga delay max> - < clock skew > + ext_Tco + < tRise/tFall fpga io >
output max = <fpga2ext delay max> - < destination reg clk delay - source reg clk delay > + ext_Tsu
= <fpga2ext delay max> - < clock skew > + ext_Tsu + < tRise/tFall ic io >
input 是指 fpga2ic外部模型,亦即 fpga输出数据而ic读取,其中fpga的io口的电容性是关键,所以 input max 加上 tRise 或者 tFall 的时间;而output是指 ic2fpga 外部模型,亦即ic输出数据而fpga读取,其中ic的io口电容性是关键,所以 output max 加上 tRise 或者 tFall 的时间。
同学们一定觉得很好奇 ... 到底fpga引脚是如何换取电容性的 tRise或者tFall呢?在这里有一个非常粗略也很简单的办法。
图7.4.5 Cyclone II 的引脚电容性。
图7.4.5所示是Cyclone II 家族 fpga 的 IO引脚有 6pF,而 Time Constant 的公式下:
t = RC
t 固定时间
r 引脚阻值
c 引脚电容
一般上IO的阻值非常小 ... 都是1 Ohm 以下,不过为了方便计算,也为了增加保险余量,IO的阻值都取为 1 Ohm,此外IO电容性的 F 单位直接对应 s(秒),因此:
t = RC = 1(6pf) = 6ps tRise/tFall = 6ps
以上纯属粗略的算法而已,实际的 tRise 与 tFall 取值还要根据相关的手册。虽说信号的上下山会影响时序的合格化,不过在默认的情况下我们一般都会无视它们,因为它们实在小到可怜。除非是时序非常挑剔的设计,否则我们看也不看它们一眼。
7.5 介电参数与延迟时间
笔者曾在第三章说过,粗略点认为介电参数就像PCB大道上的柏油路,还是沙石路 ... 如果一台汽车在这条大道上行驶,很明显泊油路比起砂石路更顺畅,因为砂石路给予更大的行驶阻碍。介电参数就是这么一回事,不同材料的PCB,会有不同数值的介电参数,越高的数值,走线就有越高的阻值。
一般经济型的PCB,原材料都是FR4,而FR4的介电参数时4.4~5.2之间,至于详细的介电参数信息可以向PCB厂商要。笔者自认是懒人,所以笔者也不打算解释介电参数换算延迟时间的过程,换之我们要懂得享用前人种好的大树。
图7.5.1 传输线延迟计算器。
图7.5.1出现的小软件是网友 Aaron Lee“开发”的超方便应用程序,只要根据走线的长度,再输入相关的介电参数,然后轻轻点击一下《计算》就会自动求出走线的延迟时间。看吧,是不是很傻瓜?是不是很方便?至少笔者是这么认为 ...
7.6 IO口
图7.6.1 IO口的外部模型(右)。
有关IO口的外部模型有如图7.6.1所示,实际上它就是输出方的ic1与输入方的ic2结为一体,此时的外部模型既是fpga2ic也是ic2fpga。然而在约束的执行上倒是没有巨大不同,不同只是就在于针对对象已经二合为一,不管怎么说,我们还是做实验比较实际。
实验十四 分析和约束io口外部模型
图7.6.2 供源时钟与io口的外部模型。
如图7.6.2所示,图中是一个有供源时钟与io口的外部模型,它虽然看似有点难度的样子,不过不用担心,我们会一步步分析它。事不宜迟,让我们先瞧瞧fpga的hdl模块如何描述呢?
center_module.v
1. module center_module 2. ( 3. input CLK, 4. input RSTn, 5. inout [3:0]Data, 6. output ext_clk 7. ); 8. /********************************************/ 9. 10. reg [3:0]rData; 11. reg isOut; 12. reg i; 13. 14. always @ ( posedge CLK or negedge RSTn ) 15. if( !RSTn ) 16. begin 17. rData <= 4'd0; 18. isOut <= 1'b0; 19. i <= 1'b0; 20. end 21. else 22. case( i ) 23. 24. 0: 25. begin isOut = 1'b0; rData <= Data; i <= 1'b1; end 26. 27. 1: 28. begin isOut = 1'b1; i <= 1'b0; end 29. 30. endcase 31. 32. /********************************************/ 33. 34. assign Data = isOut ? rData : 4'dz; 35. assign ext_clk = CLK; 36. 37. /********************************************/ 38. 39. endmodule
上述是一个没什么大不了的模块,第5行声明了名为 Data的io口,而第6行声明 ext_clk输出时钟。第22~30行是该模块的核心操作 ... 现在步骤0从ic读取数据,然后在步骤1将数据输出,其中isOut寄存器成为io口开关的关键。再者计算和举例相关的延迟信息:
data delay min 2ns data delay max 4ns fpga_clk delay 1ns ext_clk delay 2ns Tsu/Th/Tco 0.5ns input max = <ext2fpga delay max> + ext_Tco = 4ns + 0.5 = 4.5ns input min = <fpga2ext delay min> + ext_Tco(或者 ext_minTco) = 2ns + 0.5ns = 2.5ns output max = <fpga2ext delay max> + ext_Tsu = 4ns + 0.5ns = 4.5ns output min = < fpga2ext delay min > - ext_Th = 2ns - 0.5ns = 1.5ns
接着为实验十四建立同名 sdc 文件,然后将下列代码拷贝进去:
#**************************************************************
# Create Clock
#**************************************************************
create_clock -name {fpga_clk} -period 10.000 -waveform { 0.000 5.000 } [get_ports {CLK}]
打开TimeQuest手动建立 model fast网表,然后执行管理的连打 ...
图7.6.3 建立 ext_clk供源时钟。
先透过 create generated clock 建立 ext_clk 供源时钟,操作如图7.6.3所示,完后请记得更新网表,让ext_clk生效。
图7.6.4 为 io的input方设置约束。
图7.6.5 为 io的output方设置约束。
不管 input 方或者 output 方,是 io 都有同样的 clock name 对象,与 Targets 对象,不同的只是 input时的约束行为,或者 output时的约束行为。如图7.6.4与图7.6.5所示
,各种 input/output 的 min/max设置够根据之前都计算得到的延迟信息。完后再更新一下网表,以便生效。
图7.6.5 设置外部时钟路径的延迟。
接下来设置 fpga_clk 与 ext_clk的外部路径的延迟信息,操作如图7.6.5所示。完后也记得更新一下网表,就这样,实验十四的约束过程就完结了,看不看时序结果一点都不重要。
实验十四的要点除了让同学们认识io口的约束方法以外,笔者也先想让同学们热身热身一下 ... 因为从下一章开始,就是我们久违的目标,亦即sdram实例。实际上sdram的约束设置和实验十四差不了多少而已,不过不同的是实验十四时钟是假想实验而已,换之sdram是真实的实例,其中有许多细节是假想实验不能体现的。
7.7 SDRAM实例
假如笔者说,我们之前的努力都是为了挑战 sdram而作准备,同学们可否相信吗?笔者就是这样的男人,为了一件不解的小事,就会从最基本做起 ... 虽然旁人会笑过程慢,耗精力。不过让笔者告诉同学们,这一切的付出一定是十分有价值 ... 感动的言辞先放在一旁,让我们开始步入冒险!
有关sdram的驱动原理,麻烦同学回顾《Verilog HDL那些事儿》—《整合篇》,在这里我们要将全部精力放在约束的操作上。
图7.7.1 sdram的建模图。
但是看图7.7.1,笔者想是会吓跑一群小朋友吧,实际上模块的内容不是那么复杂,只是sdram的驱动原理烦人一点。让我们看看模块的内容:
1. module sdram_demo3 2. ( 3. input CLK, 4. input RSTn, 5. 6. output SDRAM_CLK, 7. output [4:0]SDRAM_CMD, 8. output [13:0]SDRAM_BA, 9. inout [15:0]SDRAM_DATA, 10. 11. output SDRAM_UDQM, 12. output SDRAM_LDQM 13. ); 14. 15. /********************************/ 16. 17. wire CLK_100Mhz; 18. wire CLK_100Mhz2; 19. 20. pll_module U1 21. ( 22. .inclk0 ( CLK ), 23. .c0 ( CLK_100Mhz ), 24. .c1 ( CLK_100Mhz2 ) 25. ); 26. 27. /********************************/ 28. 29. wire Done_Sig; 30. wire Busy_Sig; 31. wire [15:0]RdData; 32. 33. sdram_module3 U2 34. ( 35. .CLK( CLK_100Mhz ), 36. .RSTn( RSTn ), 37. .WrEN_Sig( 1'b1 ), 38. .RdEN_Sig( 1'b1 ), 39. .Done_Sig( Done_Sig ), 40. .Busy_Sig( Busy_Sig ), 41. .BRC_Addr( 22'd1 ), 42. .WrData( 16'd1 ), 43. .RdData( RdData ), 44. .SDRAM_CMD( SDRAM_CMD ), 45. .SDRAM_BA( SDRAM_BA ), 46. .SDRAM_DATA( SDRAM_DATA ), 47. .SDRAM_UDQM( SDRAM_UDQM ), 48. .SDRAM_LDQM( SDRAM_LDQM ) 49. ); 50. 51. /***********************************/ 52. 53. assign SDRAM_CLK = CLK_100Mhz2; 54. 55. /****************************/ 56. 57. endmodule
第6行声明 SDRAM_CLK的供源时钟,它主要由pll的c1在24行经由 CLK_100Mhz连线,最后在第53行驱动。此外第7~8行是5位的 SDRAM_CMD输出,还有14为的SDRAM_BA输出;第9行是inout的SDRAM_DATA;第11~12行则是SDRAM_LQM与SDRAM_UQM输出声明。 至于其他的内容不关紧要 ...
实验十五 SDRAM约束实例
图7.7.2 fpga与sdram的外部模型。
图7.7.2是fpga与sdram的外部模型,其中fpga有20Mhz的fpga_clk时钟信号;而sdram是由fpga_clk经pll的c1输出而成,有-75度位移100Mhz频率的供源时钟SDRAM_CLK驱动。此外还有fpga输出的5位宽SDRAM_CMD,14位宽SDRAM_BA,各自1位宽的 SDRAM_LDQM 还有 SDRAM_UDQM。接下来,我们则要取出相关的延迟信息 ...
图7.7.3 SDRAM的各种走线长度。
图7.7.3 是从核心版的原理图中取得的相关路径信息,由于商业关系笔者就不公开相关的原理图文件了,有望同学们见谅。其中CLOCK对应fpga_clk,S_CLK对应 SDRAM_CLK,B_Ax对应SDRAM_BA,S_DBx对应SDRAM_DATA,紧接着的S_CKE与S_Nx对应SDRAM_CMD,S_UDQM对应SDRAM_UQDM,S_LDQM对应SDRAM_LQDM。
除了 fpga_clk,SDRAM_CLK,还有 SDRAM_UDQM与SDRAM_LQDM以外的路径都是多位宽数据。前面我们曾经学过,对多位宽的数据而言,我们仅取最大延迟(max)和最小延迟(min)的路径即可。然后,用章节7.5的小软件,介电参数设置为4.4,将路径长度换算为延迟时间,结果如下:
fpga_clk delay 0.022ns
SDRAM_CLK delay 0.158ns (-75度)
SDRAM_BA max delay 0.218ns
SDRAM_BA min delay 0.172ns
SDRAM_DATA max delay 0.136ns
SDRAM_DATA min delay 0.068ns
SDRAM_CMD max delay 0.227ns
SDRAM_CDM min delay 0.156ns
SDRAM_UQDM delay 0.152ns
SDRAM_LQDM delay 0.149ns
SDRAM相关的寄存器特性,相关手册查不到 ...
其中让读者觉得困惑的就是附有-75°的SDRAM_CLK路径,关于这点同学们就不用多心了,SDRAM_CLK的供源是pll的c1,而且c1原有-75度位移的设置。接着打开实验十五的 sdram_demo3,然后建立同名的 sdc 文件,然后先将下列的代码拷贝进去:
#**************************************************************
# Create Clock
#**************************************************************
create_clock -name {CLK} -period 50.000 -waveform { 0.000 25.000 } [get_ports {CLK}]
#**************************************************************
# Create Generated Clock
#**************************************************************
derive_pll_clocks
原时钟是频率20Mhz的时钟信号,对应CLK输入口,而derive_pll_clocks就是声明pll的时钟源。接着打开TimeQuest,然后手动建立 create_timing_netlist -model fast 网表,然后惯例的执行双击 Read SDC File和Update Timing Netlist。
图7.7.4 建立SDRAM_CLK供源时钟。
打开 create generated clock 界面,声明供源时钟SDRAM_CLK,源对象是net资源的 pll|clk1,而目前是 SDRAM_CLK输出口,如图7.7.4所示。
图7.7.5 声明外部时钟信号的延迟。
打开 set clock latency 界面,声明CLK时钟路径有0.022ns延迟,SDRAM_CLK时钟路径有0.158ns延迟,如图7.7.5所示。
图7.7.7 声明 SDRAM_BA的输出延迟。
打开set output delay 界面,对象是 SDRAM_BA输出口而最大延迟是0.218ns,最小延迟是0.172ns,如图7.7.7所示。
图7.7.8 声明 SDRAM_CMD的输出延迟。
打开 set output delay 界面,对象是 SDRAM_CMD输出口而最大延迟是0.227ns,最小延迟是0.156ns,如图7.7.8所示。
图7.7.9 声明 SDRAM_xDQM的输出延迟。
打开set output delay界面,对象是SDRAM_UQDM与SDRAM_LUDQM
,它两都是单位宽信号,所以为Both ... 前者的延迟是0.152ns,后者的延迟是0.149ns,如图7.7.9所示。
图7.7.9声明 SDRAM_DATA的输入延迟。
打开 set input delay 界面,对象是 SDRAM_DATA的输入口而最大延迟是0.136ns,最小延迟是0.068ns,如图7.7.9所示。
图7.7.10声明 SDRAM_DATA的输出延迟。
打开set output delay 界面,对象是 SDRAM_DATA的输出口而最大延迟是0.136ns,最小延迟是0.068ns,如图7.7.10所示。同学们用不着怀疑,为什么图7.7.9和图7.7.10的约束行为都针对 SDRAM_DATA,因为SDRAM_DATA是 IO口。
完后,sdc文件打开是这个样子:
#**************************************************************
# Create Clock
#**************************************************************
create_clock -name {CLK} -period 50.000 -waveform { 0.000 25.000 } [get_ports {CLK}]
#**************************************************************
# Create Generated Clock
#**************************************************************
derive_pll_clocks
create_generated_clock -name SDRAM_CLK -source [get_nets {U1|altpll_component|_clk1}] [get_ports {SDRAM_CLK}]
#**************************************************************
# Set Clock Latency
#**************************************************************
set_clock_latency -source 0.022 [get_clocks {CLK}]
set_clock_latency -source 0.158 [get_clocks {SDRAM_CLK}]
**************************************************************
# Set Input Delay
#**************************************************************
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[0]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[0]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[1]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[1]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[2]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[2]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[3]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[3]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[4]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[4]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[5]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[5]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[6]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[6]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[7]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[7]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[8]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[8]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[9]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[9]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[10]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[10]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[11]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[11]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[12]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[12]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[13]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[13]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[14]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[14]}]
set_input_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[15]}]
set_input_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[15]}]
#**************************************************************
# Set Output Delay
#**************************************************************
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[0]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[0]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[1]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[1]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[2]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[2]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[3]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[3]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[4]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[4]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[5]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[5]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[6]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[6]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[7]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[7]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[8]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[8]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[9]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[9]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[10]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[10]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[11]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[11]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[12]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[12]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.218 [get_ports {SDRAM_BA[13]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.172 [get_ports {SDRAM_BA[13]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.227 [get_ports {SDRAM_CMD[0]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.156 [get_ports {SDRAM_CMD[0]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.227 [get_ports {SDRAM_CMD[1]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.156 [get_ports {SDRAM_CMD[1]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.227 [get_ports {SDRAM_CMD[2]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.156 [get_ports {SDRAM_CMD[2]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.227 [get_ports {SDRAM_CMD[3]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.156 [get_ports {SDRAM_CMD[3]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.227 [get_ports {SDRAM_CMD[4]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.156 [get_ports {SDRAM_CMD[4]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[0]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[0]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[1]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[1]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[2]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[2]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[3]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[3]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[4]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[4]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[5]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[5]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[6]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[6]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[7]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[7]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[8]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[8]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[9]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[9]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[10]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[10]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[11]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[11]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[12]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[12]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[13]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[13]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[14]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[14]}]
set_output_delay -add_delay -max -clock [get_clocks {SDRAM_CLK}] 0.136 [get_ports {SDRAM_DATA[15]}]
set_output_delay -add_delay -min -clock [get_clocks {SDRAM_CLK}] 0.068 [get_ports {SDRAM_DATA[15]}]
set_output_delay -add_delay -clock [get_clocks {SDRAM_CLK}] 0.149 [get_ports {SDRAM_LDQM}]
set_output_delay -add_delay -clock [get_clocks {SDRAM_CLK}] 0.152 [get_ports {SDRAM_UDQM}]
图7.7.11 best case网表造就的setup结果。
完后,当我们再次更新网表的时候 ... 一切都是预期的合格结果,最差的建立余量也有 3.814ns,不过 best case网表的保险余量很小而已 ... 让我们重新打开 TimeQuest,然后使用默认网表亦即 create_timing_netlist -model slow,再次分析看看 ... 想定会看见 TimeQuest 反馈红色警报!如图7.7.12所示,最差建立余量既然是 -1.244ns。
图7.7.12 worst case 网表导致setup不足的红色警报(编译器优化之前)。
让我们仔细思考看看 ... worst case 网表比起 best case 网表,前者的保险余量虽然比后者高出许多,但是性能限制也因此高出不少,结果而言 best case 网表可以合格的时序,却在 worst case 网表无法合格。不过只要 best case 网表的执行环境不极端的话,想必运行不会崩溃,但是 worst case 网表无法合格,这点多少要人放不下心 ... 如果希望 worst case 网表可以合格我们又该如何呢?
在此,我们需要用到第五章,关于 Fmax的优化设置了,虽说是优化设置 ... 但是优化对象是编译器而不是hdl内容本身,不管怎么样!同学将所有编译器的优化选项开启,然后重新编译,然后打开TimeQuest又一次选择 create_timing_netlist -model slow 默认网表看看 ....
图7.7.13 worst case 网表导致setup不足的红色警报(编译器优化之后)。
与优化之前的结果相比,图7.7.13所示的最差建立余量已经减少许多,亦即-0.791ns。事实上,如果实验对时序不挑剔的话,这种结果已经是可以接受了,毕竟建立余量不足在容差在±1.0ns的范围,不过红色始终是红色,完美主义的我们又该怎么办呢?
图7.7.14 不同grade等级的worst case 网表。
根据TimeQuest 还有 altera 的fpga而言,不同家族的 fpga 都有可支持 grade等级,以cyclone II为例,它可以支持的 grade 等级如图7.7.14所示。笔者曾说过,越极端的网表质量保险余量越大,亦即内部延迟因数的取值也会越大,同时越是限制性能。反之,越好的网表质量保险余量越小,亦即内部延迟因数的取值越接近实体,同时性能限制越小。
图7.7.15 worst case 网表grade 7。
实验十五告诉我们,即使是优化以后的 worst case 网表,时序也不一定合格 ... 此外我们也没有理由选择best case网表,因为保险余量很小,因此我们可以选择 worst case,grade 7 看看(ep2c8q208c8 不支持 grade 6),如图7.7.15所示。
图7.7.15 worst case 网表grade 7造就的setup结果(编译器优化之后)。
完后,我们会发现TimeQuest不再爆红,而最差的建立余量也是0.142ns,如图7.7.15所示。就这样我们可以安心结束分析了。
实验十五既是一个综合练习,也是一个总结,为了分析sdram模块的时序是否合格,首先我们收集各种外部延期信息,然后将它们转换为延迟时间。过后再用各种约束命令告诉TimeQuest相关的延迟信息。虽说优化编译器的设置会稍微提高时序的合格率,不过最关键的还是不同质量的网表,没有 grade 等级的worst case网表是最保险的网表,但是时就是不容易合格,不过我们不一定要选择 best case 网表,结果最佳的选择自然而然就是是含有 grade等级的 worst case网表,最终我们可以取得五十五十的平衡结果。
总结:
这一章的前面虽大部分都在讨论供源时钟的声明方法 ... 笔者认为供源时钟是学习TimeQuest的其中一个重点吧,因为很多时候为了经济,外部模型的时钟源都由 fpga提供,不过以笔者看来供源时钟至少有3个种类,亦即直接驱动,寄存器驱动,还是pll驱动。
嘛 ... TimeQuest本来就是笨蛋,如果不告诉他有关供源时钟的存在,它就会看不见 ... 除了直接驱动的供源时钟以外,寄存器驱动或者pll驱动的供源时钟,声明过程会比较猥琐一点,而声明供源时钟的相关约束命令就是 create_generated_clock。
除此之外,我们还学习到一些零碎的内容,如tRise与tFall或者有关io口的电容性。一般上相关的硬件手册取出引脚的电容性之余,同时也会取出 tRise 与 tFall的时间。有tRise和tFall的实际时间当然是最好不过的,不过也有例外的时候 ... 那时候,我们就要自己手动将电容性计算出tRise与tFall的实际时间。不过笔记取出的粗略方法是没有任何正确性可言,呵呵呵!
这一章重头戏当然是sdram的约束实例了。菜鸟时候的笔者,这关是跨不了的一道巨门,没办法之下笔者也只能一步一步往上爬,最后发觉到 ... 原来也不过如此而已。做人最重要就是要有耐性,耐性才是成功的关键,呵呵!有点老王卖瓜了。实验十五有一点遗憾,那就是没有对tRise与tFall进行约束,不这样做是有诸多原因的 ... 不过一般上,tRise与tFall 都不会过度考虑在约束中,即使有,也是像笔者这种贱骨头才会行动而已。
基本上从第一章到第七篇为止,TimeQuest的正篇我们已经学习完毕,所谓正篇除了TimeQuset的基本知识以外,还有就是Constraint 菜单下的相关约束命令,不过笔者*除去 set_clock_group,没法子呀 ... 就是没有可举例的实例,因为 set_clock_group 必须关系到多时钟设计。
后语
为时数年的学习旅行终于告一段落了,为了理解TimeQuest笔者不知道走过多少知识的城镇小乡,入眠时也不知看无数次夜中漫天的概念星星,它们不停一眨一闪,耀眼的星光不停诱出心底深处的思路与念头。电脑为伴的旅程,它会帮助笔者记录所有灵感,就这样字字成行,行行成段,段段成页,页页成章,章章成本,这本笔记就这样编辑而成。
完后,巨大的疲惫感虽然胜过一时的兴奋感,不过很欣慰自己想法自然以黑字白字储存着,这是真实的成长记录,笔者感觉自己不知不觉之间更加接近FPGA的世界,因为“静态时序分析”这块遮盖眼前的面纱已经脱落,因此视觉又一步扩大和清晰。笔者真想大声叫喊,然后将心中所有的感动,经由大气传达致世界各处。
呵呵!笔者偶尔也想这样表达心情,读者看着笑笑就好。这本笔记终于编辑完毕了,笔记的内容虽然只包含TimeQuest的篇内容,不过已经足够了,因为我们已经经由TimeQuest去了解物理时序的种种,好使自己更进一步触及FPGA。这本笔记的前生是当初一时心血来潮所写过的瞎搞TimeQuest。瞎搞TimeQuest事实上称不上学习笔记,顶多只是笔者的涂鸦笔记而已。
笔者经过思虑以后,总觉得 ... 既然有闲情写涂鸦笔记,还不如又认真一回写出像样一点的学习笔记。这本笔记有许多内容都是笔者在一年前累计的涂鸦和素描,经过重新编辑一番以后,再稍微添加几笔,有模有样的学习笔记就出来了,不过笔记的内容完全不按官方的资料去编辑,呵呵!管它什么马,只要跑得快就是好马,不是吗?
不管读者讨厌或者喜欢,想必与笔者无关吧?笔者完全也是按照自己的心情去编辑而已,简单说笔记是为自己而编辑。内容喜欢与否都请笑笑而过吧,笔者的所作所为一切都是为了Verilog HDL的宏愿而已,所以说TimeQuest不过是在学习Verilog HDL的路途中,顺手牵一把的东西而已。不知这是不是所谓的醉翁之意吗?
好了,好了 ... 感叹就到此为止吧!这本笔记只是描述 TimeQuest的正篇内容而已,不过对付一般的应用已经却却有余了。看见后语这也意味着笔记的完结,想必读者会感觉一丝寂寞和舍不得吧?读者千万别这样感触。笔者只是一支指明灯,作用就是照耀迷途的路人还有为他们指向大概的方位而已,未来的路上读者还要靠自己努力。
往后如果足够的资料的话,再考虑编辑副篇也不迟 ... 不过未来的事情不是人人都可以预测的,借老子的话说:“让河水顺其自然的流动吧,少年!”。有机会的话,再实现当初的想法也不迟。
就这样,有缘再见。