このページは、タイミングに関する連載ページの一部です。これまでのページでは、タイミング計算の背後にある理論を説明し、いくつかのタイミング制約(timing constraints)の書き方を示し、タイミングクロージャ(timing closure)の原則について述べました。このページでは、クロックドメイン(clock domain)に関連するタイミング制約の定義方法を説明します。
はじめに
特定の論理エレメントを見つける方法と、パス(path)を定義する方法を身につけたところで、この知識を活用するタイミング制約について見ていきましょう。
まず最初に見ていくのは、タイミング制約とクロックドメインの関係です。この 2 つのトピックは密接に関連しており、どちらか一方だけを論じることはできません。このトピックに馴染みがない場合は、クロックドメインに関するこの連載ページに目を通すことをお勧めします。これは、以下で使う用語を理解するためにも必要です。
クロック間の関係はロジックが決める
次の Verilog コードの例を見てみましょう。
module top(
input clk,
input foo,
output reg bar_reg,
output reg baz
);
reg foo_reg;
reg bar;
reg baz_metaguard;
wire pll_clk_8, pll_clk_6;
clk_wiz_1 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk_8),
.clk_out2(pll_clk_6));
always @(posedge pll_clk_8)
foo_reg <= foo;
always @(posedge pll_clk_6)
begin
bar <= !foo_reg;
bar_reg <= bar;
end
always @(posedge clk)
begin
baz_metaguard <= bar;
baz <= baz_metaguard;
end
これは、以前に説明したPLL を使った例とほぼ同じです。違いは、新しいクロックドメイン横断(clock domain crossing)があることです。@bar の値は、メタスタビリティガード(metastability guard)を通じて @baz にコピーされます。
この例には、2 種類のクロックドメイン横断があります。
- @foo_reg から @bar へ。これは 2 つの関連クロック(related clocks)の間です(メタスタビリティガードなし)。
- @bar から @baz へ。これは 2 つの無関係クロック(unrelated clocks)の間です(@baz_metaguard はメタスタビリティガードです)。
クロックドメイン横断のタイプを判断するために、私は 1 つの基準だけを見ました。メタスタビリティガードが存在するかどうかです。ロジックが無関係クロックを想定しているなら、必要な安全機構を使います。したがって、他のことは重要ではありません。たとえこれらのクロック間のパスでタイミング要件を満たすことが可能であっても、そうする理由はないのです。
逆も同様です。ロジックが関連クロックを想定しているなら、タイミング違反に対する保護はありません。したがって、これらのクロック間のすべてのパスでタイミング要件を満たさなければなりません。
結論として、ロジックは常に、どのクロックが関連クロックであり、どれがそうでないかについて仮定を置いています。これらの仮定は、保護機構の有無に反映されます。しかし、これらの仮定を実際のものにするのは FPGA ツールです。特に、ツールは関連クロック間のパスでタイミング要件が満たされることを保証します。
したがって、ロジックがクロックについて置いている仮定を、ツールが認識していることを確認する必要があります。
上の例では、@pll_clk_6 と @clk に関するロジックの期待をツールが知る方法はないことに注意してください。ロジックはこれらを関連クロックと見なすかもしれないし、その逆と見なすかもしれません。実際、私は以前に似たような例を使いました(「アイデア #9」を参照)。関連クロック間のパスを示すためでした。上の例では、これらの 2 つのクロックは無関係クロックとして扱われます。
もしかしたらツールは @baz_metaguard という名前から賢い推測ができるかもしれません。メタスタビリティガードの典型的な構造がヒントになるかもしれません。しかし、それだけではこの重要な問題について判断を下すには不十分です。
クロックについてツールに伝える
これまでの例では、タイミング制約は 1 つだけでした。
create_clock -period 4.000 -name clk [get_ports clk]
最初に浮かぶ疑問は、ツールが @pll_clk_6 から @clk へのクロックドメイン横断をデフォルトでどのように扱うかです。これが唯一のタイミング制約である場合、ツールは @bar と @baz_metaguard の間のパスに何かを適用するでしょうか。
答えは「使用する FPGA ツールに依存する」です。事実上すべての FPGA ツールが @pll_clk_6 と @pll_clk_8 を関連クロックと見なしますが、他のクロックの組み合わせで何が起こるかは明らかではありません。ツールは通常、デフォルトですべてのクロックを関連クロックとして扱いますが、それに依存するのは安全ではありません。
多くの FPGA ツールは set_clock_groups コマンドをサポートしています。これはクロック間の関係を定義するための最良の選択肢です。このコマンドの重要性のため、設計で使う前にツールのドキュメントを参照することをお勧めします。
上の例では、このコマンドを Vivado(または他のツールでも同様)で次のように使えます。
set_clock_groups -asynchronous \
-group [list \
[get_clocks -of_objects [get_pins pll_i/clk_out1] ] \
[get_clocks -of_objects [get_pins pll_i/clk_out2] ] ] \
-group [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]
get_clocks と get_pins のこの使い方は前に説明しました。各 get_clocks コマンドは、clk_wiz_1 の該当するポート名に応じてクロックオブジェクトを見つけます(clk_wiz_1 のインスタンシエーション(instantiation)名は pll_i であることに注意)。たとえば、最後の get_clocks コマンドは @clk のクロックオブジェクトを取得します。
この例は、set_clock_groups がクロックのグループをどのように定義するかを示しています。この場合、1 つのグループは @pll_clk_6 と @pll_clk_8 で構成され、2 つ目のグループは @clk だけです。この制約は、同じグループに属するすべてのクロックが関連クロックであることをツールに伝えます。同様に、2 つのクロックが異なるグループに属する場合、ツールはそれらを無関係クロックと見なします。
言い換えれば、ツールは、パスの両側のクロックが同じグループに属する場合にのみ、そのパスにタイミング制約を適用します。
設計のタイミング制約には、複数の set_clock_groups コマンドを記述できます。しかし、すべてのクロックをグループに分割するには、1 つの set_clock_groups コマンドを使うのが最善です。これにより、矛盾を避けられ、混乱の防止にも役立ちます。このコマンドの主な利点は、クロック間の関係を簡潔に記述できることです。短く、簡潔で、数学的。それが私たちが望む方法です。
set_clock_groups は、すべての create_clock コマンドの後に使うべきことに注意してください。言及するクロックオブジェクトが既に存在している必要があるからです。
クロック間の矛盾した関係
設計のある場所ではクロックが関連クロックと見なされ、別の場所では無関係クロックと見なされる場合、set_clock_groups を使うことはできません。
たとえば、上記の Verilog コードでは、@clk と @pll_clk_6 は無関係クロックとして扱われています。しかし、理論的には、それらを関連クロックとして扱う追加ロジックが存在することもあり得ました。それは良い考えではありませんが、この追加ロジックのために @pll_clk_6 と @clk の間にタイミング制約を適用することは可能です。
この理由で set_clock_groups が使えないなら、まず、set_clock_groups を使えるようにロジックを変更したいかどうかを自問してください。これはこのコマンドがとても優れているからだけではありません。どのクロックが関連クロックで、どれがそうでないかを示す単純な規則がなければ、クロックドメイン横断で誤りを犯しやすくなります。それでもロジックを変更したくない場合は、フォールスパス(false path)を定義することが解決策です。
set_false_path コマンドについて簡単に
フォールスパスを宣言するための主要なコマンドは 2 つあります。set_clock_groups とset_false_path です。
set_clock_groups コマンドは上で説明しました。異なるグループに属する 2 つのクロック間のパスは、すべてフォールスパスと見なされます。しかし、フォールスパスとして宣言すべきすべてのパスに対して、set_clock_groups が常に使えるとは限りません。パスの選択が、クロックのグループを定義することよりも具体的でなければならない場合があります。set_false_path コマンドは、パスを特定して選択できるようにすることで、この問題を解決します。
たとえば、上記の set_clock_groups コマンドを set_false_path コマンドに置き換えてみましょう。修正が必要なのは、@bar から @baz_metaguard へのパスだけです。他のすべてのパスのタイミング要件は、現状のままで正しく適用されます。したがって、これがこのパスに対する set_false_path コマンドの 1 つの書き方です。ただし、この例から学ぼうとしないでください。
set_false_path -from [get_cells bar_reg__0] -to [get_cells baz_metaguard_reg]
このコマンドは、get_cells を使ってパスの始点と終点を選択しています。これには、両側のセルオブジェクトの名前を知っている必要があります。この場合、「bar_reg__0」という名前がありますが、これは名前に依存することの問題を示しています。この名前は「bar_reg」であるはずでしたが、すでに説明したように、偶然によって「bar_reg__0」になってしまいました。
つまり、この例は set_false_path の問題の 1 つを示しています。設計内の論理エレメントを詳細に選択するには、オブジェクトの名前に頼らざるを得ないことがよくあります。この問題と可能な解決策については以前に説明しました。
このコマンドは簡略化できます。@baz_metaguard はメタスタビリティガードなので、このレジスタに至るパスがどこから来るかは関係ありません。では、このレジスタへのすべてのパスを無視してはどうでしょうか?
set_false_path -to [get_cells baz_metaguard_reg]
このコマンドの効果はまったく同じです。
set_clock_groups の代わりに set_false_path を使う
もう 1 つの可能性として、クロックに従ってパスを定義する方法があります。実際、上記の set_clock_groups コマンドは、次の制約に置き換えることができます。
set_false_path -from [get_clocks -of_objects [get_pins pll_i/clk_out1]] \
-to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]
set_false_path -from [get_clocks -of_objects [get_pins pll_i/clk_out2] ] \
-to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]
set_false_path -from [get_clocks -of_objects [ get_pins pll_i/clk_in1] ] \
-to [get_clocks -of_objects [get_pins pll_i/clk_out1] ]
set_false_path -from [get_clocks -of_objects [ get_pins pll_i/clk_in1] ] \
-to [get_clocks -of_objects [get_pins pll_i/clk_out2] ]
これは、同じフォールスパスを作成する、長くて詳細な方法です。クロックをグループに分割する代わりに、無関係クロックの各ペアに対して 2 つの set_false_path コマンドがあります。1 つは各方向に対してです。これだけ多くの制約があると、誤りを犯しやすいのは明らかです。しかもこれは、3 つのクロックによる単純な例です。
それにもかかわらず、set_false_path は、set_clock_groups の代わりにこのように使われることがよくあります。これはおそらく、誰かがどこかからタイミング制約をコピーしたためでしょう。
誤りがどのように発生し得るかの例
発生し得る誤りの単純な例を見てみましょう。上の議論から、フォールスパスとして宣言する必要があるパスは 1 つだけであることは明らかです。@bar から @baz_metaguard へのパスです。したがって、最後の例にある 4 つの set_false_path コマンドのうち、意味があるのは次の 1 つだけです。
set_false_path -from [get_clocks -of_objects [get_pins pll_i/clk_out2] ] \
-to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]
他の 3 つの set_false_path コマンドは、どのパスも対象にしていません。しかし待ってください。制約をもっと単純化してみてはどうでしょうか。@clk で終わるすべてのパスをフォールスパスにしてはどうでしょうか? 例えばこれでは?
set_false_path -to [get_clocks -of_objects [ get_pins pll_i/clk_in1] ]
実際には、「clk」を定義する create_clock コマンドがすぐ上にあるため、これは次のように書けます。
set_false_path -to [get_clocks clk]
この制約は短くて簡潔です。残念ながら、これはひどく間違っています。@clk で終わるすべてのパスをフォールスパスにすることを提案しました。しかし、@baz_metaguard から @baz へのパスはどうでしょうか。これは @clk から @clk へのパスです。もちろん、このパスはフォールスパスであるべきではありません。しかし、最後の 2 つの set_false_path コマンドは、パスが @clk で始まる場合でも、@clk で終わるすべてのパスを含みます。
この種の誤りは、特にフォールスパス用の制約を、数学的な考え方の精度なしに書くと、簡単に発生します。どのパスがフォールスパスで、どれがそうでないかを明らかにする専用のタイミングレポートを作成すると役立つかもしれません。
これで、FPGA 内部のパスに対するタイミング制約についての実践的な議論は終わりです。次のページでは、このトピックに属するマルチサイクルパス(multicycle path)について説明します。ただし、マルチサイクルパス制約は通常推奨されません。したがって、代わりにI/O 制約の入門に進んでも構いません。