このページは、タイミングに関する連載ページの一部です。これまでのページでは、タイミング計算の背後にある理論を説明し、クロック周期制約とタイミングクロージャ(timing closure)の原則について述べてきました。そろそろ、タイミング制約(timing constraints)の技術的な詳細に入る頃です。
Tcl コマンドとしての create_clock の意味
これまでのページは、すべて次のタイミング制約を中心に展開してきました。
create_clock -period 4 -name clk [get_ports clk]
この行の構文について、あえてここまで説明を避けてきました。そろそろ、これが実際に何を意味するのかを説明しましょう。
このタイミング制約は SDC(Synopsys Design Constraints)形式で書かれています。これはタイミング制約の最も一般的な形式です。Vivado と Quartus は、他のいくつかの FPGA ツールと同様に、この形式を使用します。
SDC ファイルは、本質的には Tcl で書かれたスクリプト(script)です。したがって、SDC ファイルの内容は情報の集合ではなく、短いコンピュータプログラムです。ただし、SDC ファイルがスクリプトとして持つ機能は、制約を書くための小さなコマンド群に限定されています。Tcl スクリプトで許されるすべてが SDC ファイルでできるわけではありません。
create_clock コマンドはタイミング制約を定義するために使われます。しかし実際には、このコマンドは FPGA ツールに新しいクロックオブジェクトを作成するように指示します。「オブジェクト」という言葉は、ソフトウェア工学の分野で通常意味するものと同じ意味です。新しく作成されたクロックオブジェクトは、Tcl インタープリタのメモリ内に、独自のプロパティを持つオブジェクトとして保存されます。
たとえば、create_clock コマンドの「-name clk」という部分は、「name」というプロパティに値「clk」を与えます。前のページの 1 つで述べたとおり、この名前はタイミングレポートで使われました。この制約(より正確には、このクロックオブジェクト)のために計算されたタイミングパス(path)に沿って、「clk」という名前が現れたのです。
後のページでは、clk_out1_clk_wiz_1 や clk_out2_clk_wiz_1 といった他のクロック名があることも見ました。これらは実際には、ツールによって自動的に作成された他のクロックオブジェクトの名前です。
すべてのクロックをリストするための Tcl コマンドがあります。それは get_clocks です。前のページの 2 つのクロックを使った例で、Vivado の Tcl コンソールで実行すると、次のようになります。
> get_clocks clk clkfbout_clk_wiz_1 clk_out1_clk_wiz_1 clk_out2_clk_wiz_1
get_clocks および類似のコマンドについては、次のページで詳しく説明します。
これらのオブジェクトのプロパティを表示することもできます。すべてのプロパティを理解する必要はありません。ここで示すのは、クロックがオブジェクトであるという点を強調するためです。私自身、オブジェクトのプロパティを直接操作する必要が生じたことは一度もありません。
> report_property [get_clocks clk] Property Type Read-only Value CLASS string true clock INPUT_JITTER double true 0.040 IS_GENERATED bool true 0 IS_PROPAGATED bool true 1 IS_USER_GENERATED bool true 0 IS_VIRTUAL bool true 0 NAME string true clk PERIOD double true 4.000 SOURCE_PINS string* true clk SYSTEM_JITTER double true 0.050 WAVEFORM double* true 0.000 2.000 > report_property [get_clocks clk_out1_clk_wiz_1] Property Type Read-only Value CLASS string true clock EDGES int* true 1 2 3 EDGE_SHIFT double* true 0.000 2.000 4.000 INPUT_JITTER double true 0.000 IS_GENERATED bool true 1 IS_INVERTED bool true 0 IS_PROPAGATED bool true 1 IS_RENAMED bool true 0 IS_USER_GENERATED bool true 0 IS_VIRTUAL bool true 0 MASTER_CLOCK clock true clk NAME string true clk_out1_clk_wiz_1 PERIOD double true 8.000 SOURCE pin true pll_i/inst/mmcme3_adv_inst/CLKIN1 SOURCE_PINS string* true pll_i/inst/mmcme3_adv_inst/CLKOUT0 SYSTEM_JITTER double true 0.050 WAVEFORM double* true 0.000 4.000
ここで認識すべき重要なことは、create_clock は単にオブジェクトを作成するだけだということです。このコマンドのパラメータは、そのオブジェクトのプロパティをどのように設定するかを決めるだけです。たとえば、(これまで繰り返し示してきたタイミング制約の中の)「-period 4」という部分は、「PERIOD」というプロパティに値 4 を設定することを意味するにすぎません。
これらの Tcl コマンドを自分で試してみたい場合、FPGA ツールごとに違いがあることに注意してください。
Vivado では、これらのコマンドは Implemented Design を開いた後でのみ使用できます。
Quartus では、まず TimeQuest Timing Analyzer を開き、Create Timing Netlist、Read SDC File、Update Timing Netlist の順にクリックします。次に、Tcl コンソールで例えば次のようなコマンドを試してください。
> join [ query_collection -all [ get_clocks ] ] "\n" > get_clock_info -waveform [get_clocks clk]
「クロック」という言葉の意味
FPGA ツールが「クロック」という言葉を使うとき、それは通常クロックオブジェクトを意味し、FPGA 内部の物理的な信号を指すわけではありません。これは特にタイミングレポートで当てはまります。
前のページで「理論上のクロック(theoretical clocks)」という言葉を何度か使いました。これらは実際にはクロックオブジェクトです。タイミングレポートでは「クロック」と呼ばれますが、タイミング解析での使われ方を見れば、それらが単なる情報の入れ物であることがわかります。
では、これらのクロックオブジェクトと実際の信号との間には、どのような関係があるのでしょうか。タイミング解析ではクロックオブジェクトの名前が何度も現れました。これらはどのように連携しているのでしょうか。
ツールが設計の静的タイミング解析を実行するとき、すべてのパスが調べられます。パスがフリップフロップから始まる場合、ツールはそのフリップフロップのクロック入力に接続されている信号(つまりネット)を調べます。この信号に関連するクロックオブジェクトはあるでしょうか。たとえば、信号が @clk の場合、関連するクロックオブジェクトは create_clock コマンドで「clk」という名前を与えられたものです。該当するクロックオブジェクトを見つけたら、ツールはそのオブジェクトのプロパティから必要な情報を取得できます。
同じことがパスの終点にあるフリップフロップにも行われます。これで、ツールはパスに対応する 2 つのクロックオブジェクトを手にします。これらのオブジェクトからの情報を使って、ツールはタイミング解析を実行します。
もちろん、同じ手順はフリップフロップだけでなく、あらゆる順序エレメントに適用されます。
これを理解することがなぜ重要なのでしょうか。その理由の 1 つは、タイミングレポートに「クロックのないレジスタがある」というエラーメッセージが出ることがあるからです。通常、これはフリップフロップのクロック入力に何も接続されていないという意味ではありません。むしろ、ツールがこのクロック入力に関連するクロックオブジェクトを見つけられなかったことを意味します。言い換えれば、ツールはこのフリップフロップのクロック入力に関する情報を見つけられなかったのです。したがって、問題は通常ロジック設計側ではなく、タイミング制約が欠けている(または誤って書かれている)ことにあります。
もう一度言っておきます。タイミングレポートで「クロック」と書かれていても、ロジック設計にその名前の信号があるという意味ではなく、その名前でクロックオブジェクトが作成されているということです。それでは、どの信号に対応するのかをどうやって知るのでしょうか。それが次の話題です。
このクロックはどの信号のもの?
タイミングレポートを読みにくくしているものの 1 つが、クロックの名前です。論理設計のクロック信号のほとんどは PLL によって生成され、タイミングレポートに現れる名前がほとんど役に立たないことがよくあります。多くの FPGA ツールでは SDC ファイルにコマンドを追加することでクロックオブジェクトの名前を変更できますが、ほとんどのプロジェクトではこれを行いません。そして、黄金律 #4 は、プロジェクト固有のことをするのを避けることです。
クロックのソースが IP ブロック(たとえばギガビットトランシーバー、PCIe ブロック、オンチッププロセッサコアなど)である場合、名前の問題はさらに厄介になります。その場合、クロック名から、それがどこから来て何に関係しているのかを読み取るのは難しいことがよくあります。
では、この問題はどのように解決するのでしょうか。最も単純な状況、つまりクロック名が自分たちの SDC ファイル内の create_clock コマンドに由来する場合から始めましょう。同じタイミング制約をもう一度示します。
create_clock -period 4 -name clk [get_ports clk]
このコマンドの最後の部分は「[get_ports clk]」です。Tcl 言語では、角括弧はその中身を Tcl コマンドとして実行し、そのコマンドの結果を角括弧の場所に使うことを意味します。
get_ports コマンドは、「clk」という名前の I/O ポートを見つけます。このコマンドの結果は、そのポートを表すオブジェクトです。したがって、上の create_clock コマンドでは、このオブジェクトがコマンドの引数になります。これが、create_clock がクロックオブジェクトと実信号を結び付ける仕組みです。
ポートの名前もオブジェクトの名前も「clk」であることに注意してください。これらが同じである必要はありませんが、同じにすることをお勧めします。オブジェクトの名前はタイミングレポートに表示されます。したがって、通常はポートの名前が最良の選択です。
また、信号を識別するためにネットオブジェクトやピンオブジェクトを使うことも可能です。これは、IP ブロックのために自動生成されるタイミング制約でよく使われます。しかし、自分の制約でこれをやりたくなった場合、何か間違ったことをしている可能性が高いです。
つまり、SDC ファイルで create_clock コマンドを使ったのであれば、クロックオブジェクトがどの信号に対応するのかを知るのは簡単です。しかし、ツールが自動的に作成したクロックオブジェクトはどうでしょうか。
その場合は、タイミングレポートを見るのが最も良い方法です。たとえば、前のページの例で、@pll_clk_8 に関連するクロックオブジェクトはどれでしょうか。簡単な方法は、タイミングレポート内でテキスト検索をすることです。「pll_clk_8」を検索すると、次の部分が見つかります。
Location Delay type Incr(ns) Path(ns) Netlist Resource(s)
-------------------------------------------------------------- -------------------
(clock clk_out1_clk_wiz_1 rise edge)
16.000 16.000
AG12 0.000 16.000 clk (IN)
net (fo=0) 0.000 16.000 pll_i/inst/clkin1_ibuf/I
AG12 INBUF (Prop_INBUF_HRIO_PAD_O)
0.738 16.738 pll_i/inst/clkin1_ibuf/INBUF_INST/O
net (fo=1, routed) 0.105 16.843 pll_i/inst/clkin1_ibuf/OUT
AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
0.049 16.892 pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
net (fo=1, routed) 0.975 17.867 pll_i/inst/clk_in1_clk_wiz_1
MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
-4.438 13.429 pll_i/inst/mmcme3_adv_inst/CLKOUT0
net (fo=1, routed) 0.501 13.930 pll_i/inst/clk_out1_clk_wiz_1
BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O)
0.101 14.031 pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=1, routed) 1.369 15.400 pll_clk_8
SLICE_X49Y58 FDRE foo_reg_reg/C
これは clk_out1_clk_wiz_1 のソースクロックパスであり、ここに答えがあります。
もう 1 つの方法は、Tcl コマンドで情報を取得することです。その正確な方法は FPGA ツールごとに異なります。Vivado では、Implemented Design を開いた後に、次のようなコマンドを使用できます。
> get_clocks -of_objects [ get_nets pll_clk_8 ] clk_out1_clk_wiz_1
この方法では、ネットの名前を知っている必要があります。時にはこの例のように単純なこともありますが、時にはこのネットの名前を見つける作業が必要です。FPGA ツールには通常 GUI でこれを行う方法があります。また、この目的に Tcl コマンドを使うこともできます。
実際、Tcl の例をいくつか見ていただいたことで、Tcl をきちんと扱う方法を知ることの重要性を納得していただけたのではないでしょうか。次のページはまさにその話題についてです。
get_ports を使うことの重要性
上の例では、create_clock コマンドは、クロックオブジェクトと物理的な入力ピンを結び付けるために get_ports に依存しています。上で述べたとおり、この結び付けは、どの論理エレメントがこのクロック(またはそこから生成されるクロック)に接続されているかを知るために必要です。
しかし、get_ports を使うことだけが唯一の可能性ではありません。たとえば、グローバルクロックバッファの出力ピンを参照することもできます。たとえば、次のようなものです。
create_clock -name clk -period 4 [get_pins my_BUFG_inst/O]
違いは、ツールがグローバルクロックバッファの出力ピンをクロックの発生元と見なすことです。つまり、クロックパスの計算はこの位置から始まります。この発生元での最初のクロックエッジは 0 ns に発生するため、この出力ピンが時刻の基準になります。
これは正当なタイミング制約ですが、2 つの重要な欠点があります。
- このクロックを、他のすべてのクロックに対して無関係クロック(unrelated clocks)と見なすよう、ツールに要求する必要があります。これは、同じ PLL で生成されたクロックについても当てはまります。その理由は、ツールが出力ピンを時刻の基準と考えるからです。したがって、クロック間のクロックパス遅延の違い、つまりクロックスキュー(clock skew)は補償されません。
- FPGA の外部に見えるクロックに関連するI/O タイミング制約を定義することは、不可能ではないにしても非常に困難です。そのような制約は、クロックオブジェクトを時刻の基準として頼るからです。しかし繰り返しますが、ここでの時刻の基準はグローバルクロックバッファの出力ピンです。外部クロックからグローバルクロックバッファまでのクロックスキューは未知です(たとえば温度によって変化します)。
したがって、可能な限り get_ports を使うべきです。そうしないと、そのクロックのタイミングは、それ自身以外の何に対しても未知と見なされるべきです。
このページでは、多くの Tcl コマンドを紹介しましたが、十分には説明しませんでした。次のページでそのギャップを埋めます。