时序分析是基于源reg的Tco、目的reg的Tsu,源reg到目的reg的Tdelay路径延迟以及到两个reg的clk skew计算出来的,现在Timequest不知道外设接收reg和时钟输入端的延迟参数,无法分析,还需分若干步配置:
$ _* }3 A0 O/ k- _) S7 x
1.使用Create Clocks建立系统时钟sysclk
% d& O" w$ z; c$ F xcreate_clock -name {sysclk} -period 20.000 -waveform { 0.000 10.000 } [get_ports {sysclk}]
Timequset里的所有时钟都需要手动设置,首先设置系统时钟,后面的时钟都要基于这个时钟才能生成。
2 n5 n% }! |4 A' F2 o
2.使用Create Generated Clocks建立输出时钟clkout
9 e' g0 B; c/ z& l& n! P外设的时钟源于FPGA的输出port clkout,如果不建立时钟,timequest只会把clkout当作一个普通的输出引脚,时序分析器认为目的reg缺少驱动时钟,无法分析。Timequest中将通过倍频、分频或者移相等生成的时钟都归为Generated Clocks,你可以使用Create Clocks创建试一下,不会提示创建失败,但是在最后的时序分析里不会加入clkout的clock network delay,Timequest没有你想象的那么智能,知道clkout是从一个分频模块输出,自动加入模块延迟分析,它不知道这些。所以还是使用Create Generated Clocks来创建吧,先填写源时钟sysclk,再填写生成时钟和源时钟的关系,2分频,最后指定target 生成时钟到clkout引脚,这三个步骤看似没问题,结果却是创建失败,提示找不到sysclk到clkout之间的路径,如下所示。
$ A3 X9 b7 e0 M
Warning: No paths exist between clock target "clk_out" of clock "clkout" and its clock source. Assuming zero source clock latency.
/ z, [0 t, _0 r4 U+ s1 P9 V
但是sysclk到clkout明明是有路径的啊,之间的逻辑也很简单,就是一个二分频关系,bdf图如下所示:
6 {2 w1 @% ]3 @4 M) s4 I$ d
. Z; t8 w. X7 h
3 S8 S: I; E/ U! K" X* J
& e& y' [- `) |: A+ u) \
: r2 B7 [2 i7 Z% F; F3 g; v
clk_div模块的代码如下:
1 c! m' N9 P# P6 p
# P" V: E+ z" G9 S1 T5 S( x( w
0 U0 ~6 e& n( r" Z# n5 c0 D# [
% N! ^* f* E# s/ s+ ^/ M
尝试了很多方法都无效,最终实验成功了一种自认为很别扭的方法,“曲线救国”分两步走:
0 h6 H7 B7 H- Y: O8 ^; Z
首先Create Generated Clocks 从引脚sysclk 到 寄存器clk_div
create_generated_clock -name {clk_div_r} -source [get_ports {sysclk}] -divide_by 2 -master_clock {sysclk} [get_registers {clk_div:inst4|clk_div}]
再Create Generated Clocks 从寄存器clk_div到输出引脚clk_out
create_generated_clock -name {clkout} -source [get_registers {clk_div:inst4|clk_div}] -master_clock {clk_div_r} [get_ports {clk_out}]
' b/ }# s% L3 k ^# B4 c" m6 N
这才把clk_out设置为和sysclk关联的输出时钟。
& a8 H: g/ h0 S b, W; w5 O
3. 用set output delay 设置基于clkout时钟的输出dout延迟
set_output_delay -add_delay -clock [get_clocks {clkout}] 5.000 [get_ports {dout}],这句话在源reg和目的reg之间搭建起一座桥梁,实现两个作用:
1.告诉分析工具,clkout是目的reg的驱动时钟(但是前提是clkout需要被Timequest认为是个时钟),dout是目的reg的数据源,分析工具才能计算出路径的clk skew(clkout的clock network delay - sysclk的clock network delay);
2.告诉了分析工具外设reg的建立时间Tsu和路径延迟Tdelay,这两个参数之和其实就是输出延迟,分别源于外设数据手册和PCB板的走线延迟。
; F- Z# s, j% h7 E/ A3 k; y
4.设置set max delay和set min delay
如果不需要更严苛的约束,不设置这两个参数也可以。经过前三步,时序分析器已经知道了源reg和目的reg的时钟频率,系统会默认使用时钟频率分析,既最大延迟为时钟周期Tclk,最小延迟为0。这里的set max delay约束Tco和clk skew为了满足外设的建立时间,使(Tclk + clk skew)-(Tco+Tdelay) > Tsu,set min delay 约束Tco和clk skew满足外设的保持时间,(Tco+Tdelay) - clk skew > Th。
a% X+ L+ r; a
5.设置多周期约束
由于本例的特殊性,源时钟是目的时钟的两倍,需要多时钟约束设置,由篇幅所限,下次再细讲。最后可以在Report Timing的Registers to Outputs的setup和hold里看到时序余量分析了。
回顾约束的整个过程,最闹心的就是第二步Create Generated Clocks了,可能一直受altera里的pll设置影响,只要设置源时钟,填好输出输入时钟关系,pll就可以用了,到了Timequest里的生成时钟设置,我也想当然的认为,clkout是由sysclk生成变化而来,源里面只要填sysclk,关系填好就肯定没问题了,但是结果并非这么简单。
再回到第二步,再尝试从sysclk直接Create Generated Clocks指定到输出时钟clkout,发现除了找不到sysclk和clkout之间的路径之外还有一个警告
Warning: Node: clk_div:inst|clk_div was determined to be a clock but was found without an associated clock assignment。
警告clk_div被设置为了时钟,但是没有指定关联的源时钟即没指定clk_div是由哪个时钟生成的,从这个警告推理:我设置的Generated 时钟是clkout,而clkout是由clk_div驱动的,所以Timequest也将clk_div认为是一个时钟路径node,但是我只告诉了Timequest clkout的源时钟是sysclk,而没有告诉它clk_div的源时钟是哪个,所以创建失败,虽然从程序显而易见源也是sysclk,看来我高估了TimeQuest的智商,按照上述的“两步走”方案,第一步就是告知clk_div的源时钟是sysclk,第二步再生成基于clk_div的clkout就ok了
+ m% F8 i+ I* R/ j m推测Timequest中对Creat Generated Clocks分析过程是这样的:从最终的target Clock端口往回分析,寻找这个时钟路径上的net寄存器,看其是否有关联时钟,如果有,看是否已经指定了,如果指定了则继续反向分析直到源时钟,如果没有则提示创建失败。为了验证这个推测,再级联一个分频模块clk_div如下图所示:
& S- t. ^- M+ p
4 _. y, K1 t# h3 D; h/ G4 @! \
; g! e4 i% |& ]0 N2 E b
+ M7 B+ ^% v6 J4 i Y+ c
7 M% p e$ c5 i6 V { e% P% v1 I
此时该如何建立基于sysclk的输出时钟clk_out呢,从clk_out管脚返回分析,clk_out由ints5|clk_div驱动,ints5|clk_div的关联时钟是inst4|clk_div,inst4|clk_div的关联时钟是sysclk,所以要分三步创建:
1、首先创建基于sysclk的inst4|clk_div。
create_generated_clock -name {clk_div_r} -source [get_ports {sysclk}] -divide_by 2 -master_clock {sysclk} [get_registers {clk_div:inst4|clk_div}]
2、再创建基于inst4|clk_div的inst5|clk_div。 m t/ b) E3 g- {
create_generated_clock -name {clk_div_rr} -source [get_registers {clk_div:inst4|clk_div}] -divide_by 2 -master_clock {clk_div_r} [get_registers {clk_div:inst5|clk_div}]
3、最后创建基于inst5|clk_div的clk_out: u+ q4 z' Y' { @
create_generated_clock -name {clkout} -source [get_registers {clk_div:inst5|clk_div}] -master_clock {clk_div_rr} [get_ports {clk_out}]
验证结果,少了上述任何一步都无法得到正确的输出时钟,比如用Create clk 创建inst4|clk_div 取代第一步,虽然也可以生成clk_out,但最终的输出延迟会减小。
多数工程中,使用分频模块输出时钟的做法并不多,更多的是使用pll,我又尝试了下使用PLL产生一个外部时钟,PLL的源为sysclk,仍然无法直接Create Generated Clocks从sysclk到clk_out,而是首先derive_pll_clocks将所有的PLL输出都设为时钟,再Create Generated Clocks从PLL的时钟输出寄存器到clk_out才可以。