fc2ブログ

[Quartus]Timing Analyzer Tclコマンド

以前にも書いたような気がするが、再度メモ。
 ※ 箇条書きだけのメモにしようと思ってたけど、需要あるかなーと思って、詳しく書いてみた。

INTEL QuartusのTiming Analyzerタイミング制約ファイルは.sdcは、EDAツール業界No.1の米Synopsys社sdc(Synopsys Design Constraint)フォーマットから来ている。
残念ながらコマンド体系が完全に一致してないので、以前Synopsys社のタイミング解析ツールPrimeTimeを覚えた人としては、
いちいちコマンドやオプションを覚え直さないといけないのが残念だが。
あとは(仕方ないが)完全に同じコマンドが使える訳ではないので、あのコマンドがあれば、ともどかしい気持ちになる。
でもget_pinsとか回路を追っかけるのに便利なコマンドがある程度はそろってる。
コマンドラインで自分が作った回路(ネットリスト)を追っかけていくためのtclコマンドをメモしておく。

■まずはTiming Analyzerをひらく
Quartusでプロジェクトを開いた状態で以下でひらく。
Quartus > Tools > Timing Analyzer


■タイミング解析を始めるための前準備
Timing Analyzerを開くと、下段にtclプロンプト(tcl>)が見えるはず。
マウスポインタでそこをクリックすると入力可能な状態になるはず。
そこでまずは以下コマンドを打とう。

もしプロジェクトを開いてない状態から始める時は以下コマンドも必要だが、既にQuartusで開いている場合は不要らしい。
よって、今回これは省略。

tcl> project_open -force "C:/xxx/DE2_115.qpf" -revision DE2_115

なので以下から実行しよう。

tcl> create_timing_netlist -model slow
  → タイミング解析するには、専用ネットリスト?を作るって事かな。遅延設定はslowにしてる。
     slow(温度高、電圧低、プロセス遅(だよね?))って事はSetup解析に使うべき遅延モデル。
     逆にfast(温度低、電圧高、プロセス早)って事はHold解析に使うべき遅延モデル。

tcl> read_sdc
  → プロジェクトに登録しているsdcファイルを読み込んでくれる。
  → これやらなくても、自分で一個一個制約コマンドを打っていけば良いんだけどね。

tcl> update_timing_netlist
  → 何かタイミング制約を与えた場合は、その後上記コマンドでネットリストをupdateすべきらしい。


■本題のtclコマンドメモ
では、sdcファイルを用意してない事を前提にやってみよう。
こちら使っている評価ボードはTerasic社DE2-115だ。(厳密にはVEEKとかtPADと呼ばれるディスプレイ付のボード)

最初にやるべき制約はクロック制約だ。
DE2-115ボードには50MHzのクロック(クリスタル?)がのっている。
Terasic社が提供しているSystem Builder(だったかな?)を使うとトップのVerilogファイルを自動生成してくれるが、
そのVerilogでは、ポート名がCLOCK_50となってる。
まずはそのポートが存在するか確認しよう。
ポートをゲットするためのコマンドがget_portsだ。

tcl> get_ports CLOCK_50
_col8

Timing Analyzerツールでは、ゲットしたオブジェクトはコレクションという単位で返す。
プログラムでいうArrayというものかな。
これ、CLOCK_50というポートは1個しかないのが分かっているのに、いちいちコレクションで返されてポート名が表示されないのも面倒だが。

コレクションに何が入っているか確認しよう。
この方法探すのにちょっと苦労した。PrimeTimeの時はforeachで回したよなーと思ってぐぐったら見つかった
http://billauer.co.il/blog/2013/11/sdc-tcl-quartus-wildcards/


tcl> foreach_in_collection port [get_ports CLOCK_50] { puts [get_port_info -name $port] }
CLOCK_50
  → 一行に書いてるが、やってる事は[get_ports CLOCK_50]で取得したコレクションを順番にportという変数に入れてforeachで回してるって事。
    foreachで回したオブジェクトを[get_port_info -name $port]で名前(厳密にはフルインスタンス名と呼ぶべきか)を表示してる。
これで無事に目で見える表示に出来た。
クロック制約を与えるべきポート名が分かったので、そこにクロック制約を付けてみよう。
50MHz(=20ns)なので、以下のように書く。

tcl> create_clock -period 20.000ns [get_ports CLOCK_50] -name CLOCK_50
  → 後で扱いやすいようにクロック制約名のラベルをCLOCK_50とつけておいた。

ちゃんとクロック制約CLOCK_50が存在するか確認してみる。

tcl> get_clocks CLOCK_50
_col13

またもコレクションで返るから名前が見えないが、先のコマンド使えば確認できる。ここでは省略。
また、名前はCLOCK_50で同じだが、さっきのはポートオブジェクト、今度はクロックオブジェクトなのでget_clocksを使うべき。

さらに、クリスタルがいかに正確なパルスだとしても、必ず何らかの振れ幅(ジッタ)は存在する。
その制約を実現するには以下コマンドを使う。

tcl> set_clock_uncertainty -from CLOCK_50 -to CLOCK_50 0.3ns
   → ここで書いたCLOCK_50はもちろんクロックオブジェクトの名前。
   → もしクロックの立上りと立下りのジッタがそれぞれ別の場合は、-rise_from/-rise_to、-fall_from/-fall_toオプションを使って、それぞれ別々に制約を付けても良い。
ここまでで作った制約をタイミングネットリストへ反映させてみよう。

tcl> update_timing_netlist

アップデートしたら、最初はFlipFlop間のセットアップタイミングを見てみよう。

tcl> report_timing -setup
Report Timing: Found 1 setup paths (0 violated). Worst case slack is 6.258
Path #1: Setup slack is 6.258
1 6.258

slackとはタイミングの余裕度の事。プラスならタイミングは問題無し(=MET)。
マイナスならタイミング違反だから、回路構成を見直したり、Quartusで(必要に応じてオプションを変えて)Place&Routeをやり直したり、と改善する必要がある。

セットアップタイミングでslackが+6.258って事は、今はクロック周波数が50MHz(20ns)だが、回路の実力的には72.7696MHz=13.742ns(20-6.258)まで周波数を上げられる、という事。

次はホールドタイミングを見てみよう。

tcl> report_timing -hold
Report Timing: Found 1 hold paths (0 violated). Worst case slack is 0.342
Path #1: Hold slack is 0.342
1 0.342

こちらもプラスなので問題無し。ただしホールドタイミング解析で一番悲観的ケース(ワーストケース)は半導体の遅延が速い場合なので、fast設定でやるべきだ。

ここで一つ疑問が沸く。
はたして、自分の回路内の全てのFlipFlopにクロック制約が反映されたのだろうか?
シンプルな回路なら外部クロックポート1個で、そのクロック設定をすれば全てのFlipFlopに制約が付与される、というケースもあるだろうが、えてして、クロックが複数あったり、内部で分周クロックを生成していたり、する。

そんな時に便利なコマンドが以下だ。

tcl> check_timing
Check Timing:
No Clock
:

と出る。
No Clockをクリックすると、ドロップダウン形式でクロック制約が付与されてないFlipFlopのリストが表示される。
これで、まだクロック設定がされてないFlipFlopが確認出来る。
STA(Static Timing Analysis)は、そもそも制約が当たってない回路は何もチェックしてくれないので、
クロック制約が当たってないFlipFlopはタイミングを考慮しないレイアウトになり、実際FPGAを動かすと不具合の原因になる。
論理シミュレーションでは動作するのに、実機動作がおかしい、といった場合はSTAのタイミング制約漏れ、を疑おう。

しかし、自分が設計してないような回路をインプリする場合もあるだろう。
その場合は、自動的にクロック設定をやってくれる便利なコマンドがある。

tcl> derive_clocks -period 20ns
Deriving Clocks
create_clock -period 20.000 -name xxx:xxx1|CLOCK_25 xxx:xxx1|CLOCK_25
create_clock -period 20.000 -name xxx:xxx1|spi:spi|clock_cnt[0] xxx:xxx1|spi:spi|clock_cnt[0]

自分の場合は2カ所クロック設定が自動的にされた。
ただし、全て20ns設定になってるので、違う場合は個別に修正する必要がある。
では、実際全てのFlipFlopにクロック設定が反映されたかどうか、再度確認してみよう。
今度は、no_clockチェックだけを指定してやってみる。

tcl> check_timing -include no_clock
Check Timing:
Summary
no_clock 0

これで制約無しFlipFlopが0個になったので、全てのFlipFlopにクロック設定が反映された。
これで少なくともFlipFlop間(FF -> FF)のタイミングチェックは漏れが無くなった。
尚、異なるクロック間がfalse(タイミング不問)の場合は、set_false_pathコマンドを使おう。

このように、derive_clocksは非常に便利なのだが、注意点がある。
derive_clocksで自動的に設定されたクロック設定は、対象のピン(上記の例だとxxx:xxx1|CLOCK_25とか)に設定されたクロックと外部ポートCLOCK_50に設定したクロック、どちらも「基準時間0ns」からスタートして、それぞれの周波数設定で動作する、という設定になってしまっている。
以下に簡易図を示す。

CLOCK_50 >----+------FF1---(CLOCK_25)----FF2
           +------FF3

上図で、外部ポートのCLOCK_50は基準時間0nsで動いてOK。
問題はCLOCK_25の方。
上図で分かる通り、CLOCK_50ポートのクロックが基準時間0nsとした場合、CLOCK_25クロックは「FF1の遅延時間」の後にパルスが出る。
以下のようなデータ転送があった場合、FF1遅延時間が考慮されず、正しいタイミング解析になってない。
CLOCK_50で動作するFF ---> CLOCK_25で動作するFF

このような問題を解決するためには、create_generated_clockコマンドを使おう。
このコマンドを使えば、ソースクロック(上図の例ではCLOCK_50)の遅延関係を考慮して、CLOCK_25を設定してくれる。

Usage: create_generated_clock [-h] [-help] [-long_help] [-add] [-divide_by ] [-duty_cycle ] [-edge_shift ] [-edges ] [-invert] [-master_clock ] [-multiply_by ] [-name ] [-offset


だんだん疲れて来たので、本日はこの辺で。
もっと知りたい人がいたらもっと書こうかな。

(追記)使えそうなコマンド

■foreachで1行づつ表示

http://billauer.co.il/blog/2013/11/sdc-tcl-quartus-wildcards/

階層になってるピンを全て取得して、それをquery_collectionで目に見える表示にして、さらにforeachで1行づつ表示させる。
tcl> foreach i [ query_collection -all [ get_pins -hierarchical * ] ] { puts "Pin: $i" }

■no_clock原因調査

次に、no_clock箇所の解析。
tcl> check_timing -include no_clock
こうやるとクロック制約が伝搬してないレジスタが表示される。
表示されたうちの1つのレジスタの存在を確認してみる。

tcl> query_collection -all [get_registers cq_vga2:cq_vga2_vga|valr4[10] ]
cq_vga2:cq_vga2_vga|valr4[10]

確かにレジスタは存在する。
このレジスタのピンを確認する。

tcl> foreach i [query_collection -all [get_pins cq_vga2:cq_vga2_vga|valr4[10]|*] ] { puts $i}
cq_vga2_vga|valr4[10]|ena
cq_vga2_vga|valr4[10]|asdata
cq_vga2_vga|valr4[10]|clrn
cq_vga2_vga|valr4[10]|clk
cq_vga2_vga|valr4[10]|q

query_collectionで表示させても良いが見難いので、さらにforeachで1行づつ表示させてる。

■Quartusで使えるコマンド調査

ここでfanin関連コマンドが存在すればclkピンへ伝搬する元を追えるはずだが。
quartus_staで使えるコマンドを調べる。
https://cdrdv2-public.intel.com/666794/ug-qpp-scripting-j-683432-666794.pdf
ここを見ると、以下コマンドで使用可能なtclコマンドが調べられるとの事。

tcl> help
これでQuartusのTclパッケージ一覧を調べる。

----------------------------------
Available Quartus Prime Tcl Packages:
----------------------------------

Loaded Not Loaded
------------------ ----------------------------------
::quartus::device ::quartus::design
::quartus::flow ::quartus::report
::quartus::misc ::quartus::names
::quartus::project ::quartus::incremental_compilation
::quartus::sdc
::quartus::sdc_ext
::quartus::sta
::quartus::tdc



tcl> help -pkg ::quartus::sdc
次にこのコマンドでsdcパッケージに入ってるコマンドリストを取得。

-------------
Tcl Commands:
-------------

all_clocks
all_inputs
all_outputs
all_registers
create_clock
create_generated_clock
derive_clocks
get_cells
get_clocks
get_nets
get_pins
get_ports
remove_clock_groups
remove_clock_latency
remove_clock_uncertainty
remove_disable_timing
remove_input_delay
remove_output_delay
reset_design
set_clock_groups
set_clock_latency
set_clock_uncertainty
set_disable_timing
set_false_path
set_input_delay
set_input_transition
set_max_delay
set_min_delay
set_multicycle_path
set_output_delay



tcl> help -pkg ::quartus::sdc_ext
さらにsdc_extも調べる。

-------------
Tcl Commands:
-------------

derive_clock_uncertainty
derive_pll_clocks
get_active_clocks
get_assignment_groups
get_fanins
get_fanouts
get_keepers
get_nodes
get_partitions
get_registers
remove_annotated_delay
remove_clock
reset_timing_derate
set_active_clocks
set_annotated_delay
set_max_skew
set_net_delay
set_scc_mode
set_time_format
set_timing_derate


なんか目的のコマンドあるっぽい。

実はfaninコマンドが無いと思っていて、先に以下コマンドでクロック設定すべき箇所の正解を調べてしまっていたので、自動的にcreate_clockされた状態。
tcl> derive_clocks -period 40ns
Info (332105): Deriving Clocks
Info (332105): create_clock -period 40.000 -name CLOCK_25 CLOCK_25

一旦クロックを削除してやり直してみる。
tcl> remove_clock CLOCK_25

tcl> check_timing -include no_clock
ここで表示されたクロック制約がまだ伝搬してない以下を使ってfaninコマンドやってみる。
cq_vga2:cq_vga2_vga|valr3[14]

■get_faninsコマンド使い方

tcl> query_collection -all [get_fanins [get_pins cq_vga2:cq_vga2_vga|valr3[14]|clk]]
CLOCK_25

これでderive_clocksでやった時と同じ、CLOCK_25がクロック設定すべき根本だと分かった。

ここで出来ればCLOCK_25が何者か(pin、cell、net、register、port)わかるようにコレクションタイプを表示させるコマンドを探したが見つからず。
自分で設計してるからCLOCK_25が何者か分かるが、他人の膨大なRTLを解析する場合は、コレクションタイプも調べられた方が良いかと。
仕方ないので総当たりで調べてみる。

tcl> query_collection -all [get_cells CLOCK_25]
CLOCK_25

tcl> query_collection -all [get_pins CLOCK_25]
Warning (332173): Ignored filter: CLOCK_25 could not be matched with a pin

tcl> query_collection -all [get_nets CLOCK_25]
CLOCK_25

tcl> query_collection -all [get_registers CLOCK_25]
CLOCK_25

tcl> query_collection -all [get_ports CLOCK_25]
Warning (332173): Ignored filter: CLOCK_25 could not be matched with a port

つまり、CLOCK_25というcell、net、registerオブジェクトは存在するが、pinとportは存在しない、と。

■レジスタにcreate_generated_clockを設定

CLOCK_25がregisterオブジェクトである事が分かったので、以下コマンドでpinを調べる。既に知ってるのだが一応。
tcl> query_collection -all [get_pins CLOCK_25|*]
CLOCK_25|d CLOCK_25|clrn CLOCK_25|clk CLOCK_25|q

これでCLOCK_25は、clkピンに元のクロックがやってきて、分周か何かがされてqピンから出力される回路と予想が付く。

CLOCK_25レジスタにクロックを供給している元を調べる。
tcl> query_collection -all [get_fanins [get_pins CLOCK_25|clk]]
CLOCK_50

CLOCK_50である事が分かった。(RTL設計者なので、これがportであり、clockオブジェクトである事は知ってる)

-sourceオプションにclockを設定したらエラー出たので、portにした。
tcl> create_generated_clock -divide_by 2 -source [get_ports CLOCK_50] -name CLOCK_25 [get_pins CLOCK_25|q]

設定したクロックが存在するか確認すると、以下の通り設定効いてるようだ。
tcl> report_clocks
Info (332111): Found 4 clocks
Info (332111): Clock Name Period Rise Time Fall Time Clock Type Targets
Info (332111): ============== ======== ========= ========= ============ =============
Info (332111): CLOCK2_50 20.000 0.000 10.000 Base CLOCK2_50
Info (332111): CLOCK3_50 20.000 0.000 10.000 Base CLOCK3_50
Info (332111): CLOCK_25 40.000 0.000 20.000 Generated CLOCK_25|q
Info (332111): CLOCK_50 20.000 0.000 10.000 Base CLOCK_50
Info (332111): Generated Clock Source Node Master Clock Waveform Attributes
Info (332111): ================ ============= ============ ==========================
Info (332111): CLOCK_25 CLOCK_50 CLOCK_50 divide by( 2 )


あらためてno_clockを調べてみると0になってるので、これで全てのregisterにクロック伝搬がされた。
tcl> check_timing -include no_clock
Info (332106): Check Timing:
Info (332106): Summary
Info (332106): no_clock 0


■set_clock_uncertaintyでばらつきを制約を設定

実は前のcheck_timingでuncertainty制約無いぞ!と指摘を受けていたので設定してみる。
tcl> set_clock_uncertainty -from [get_clocks CLOCK_50] -to [get_clocks CLOCK_50] 0.3ns
確認コマンドは・・・調査中。
スポンサーサイト



カレンダー
09 | 2022/10 | 11
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 - - - - -
累積訪問者
現在の訪問者
現在の閲覧者数:
最新記事
最新トラックバック
最新コメント
月別アーカイブ
カテゴリ
プロフィール

bobgosso

Author:bobgosso
FPGAのブログへようこそ!

検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード