このページは、 clock domainsに関する 3 つのページのシリーズの最後のページです。
bit 1台では足りない場合
多くの場合、 clock domains を通過するために必要な信号はデータ ワードであり、単一の bitではありません。この場合の簡単な解決策は、すでに提案されているように、 FPGAのベンダーが提供する dual-clock FIFO です。しかし、時にはこれはオプションではありません。その上、過去に誰かがその FIFO を実装しなければなりませんでした。
したがって、目標は、 vector signal が別の clock domainで正しく表示されるようにすることです。なぜそれほど簡単ではないのかを説明するために、まず clock domain crossingの素朴で間違った例から始めましょう。
reg [7:0] foo, bar, bar_metaguard;
always @(posedge clk1)
foo <= foo + 1;
always @(posedge clk2)
begin
bar_metaguard <= foo; // This will fail sometimes!
bar <= bar_metaguard;
end
これは、前のページの単純な metastability guard の例とまったく同じですが、 registers は 8-bit vectorsであり、 @foo は '0' と '1'の間で変更する代わりに counter です。
では、なぜこれが間違っているのでしょうか。問題は、 @foo から @bar_metaguardまでの paths 間の routing delays の違いです。 8 つの bits には、それぞれ異なる delayがあります。 @foo が変更されると、これらの bits の変更の一部は、合法的な timing から @bar_metaguardの flip-flopsに到達する可能性があり、他の変更は間に合わない可能性があります。
したがって、 @bar_metaguardを構成する 8 つの flip-flops のいずれにも metastability がなくても、 @foo が変更され、 @bar_metaguard の一部の bits のみが新しい値を取得し、残りは古い値のままになるという状況が発生する可能性があります。したがって、 @foo が 0xff から 0x00に変更された場合、 @bar_metaguard の次の値は何でもかまいません。これは、変更前に @fooの値を取得した bits と、変更後に @fooの値を取得した bits があるためです。この誤った値は、 @clk2の clock cycle の後、 @bar で表示されます。
この問題を解決するには、まずニーズを定義する必要があります。 @bar には常に有効な値が含まれている必要がありますか? それとも、ある clock domain から別の clock domain に時折情報を渡すことを意図していますか?これら 2 つのオプションについては、個別に説明します。
オプション1: 連続サンプリング
目的の単語 (この例では@bar ) が、他の clock domain (@foo) で単語を継続的にサンプリングする必要があり、常に正当で意味のある値を含んでいる場合、これを確実にする方法は 1 つしかありません。 上記の単純な方法を clock domain crossing に使用しますが、 @clk1の各 clock cycle で、 @fooの bits の 1 つだけが変更される (または変更されない) ことを確認してください。つまり、 vector signalで metastability guard を使用しますが、同じ clock cycleで複数の bits が変更された場合の問題を回避するようにしてください。
各 clock cycleで変更できるのは 1 つの bit だけであるため、各変更は @bar_metaguardによってサンプリングされるか見逃され、いずれにしても @foo が持っていた値の 1 つを反映します。
したがって、上記の例の @foo が、単純な binary codeではなく Gray code を使用する counter である場合、完全に正常に動作します。 Gray coding の本質は、単語がカウントアップするたびに 1 つの bit のみが変化することです。そのため、 @bar は常に意味のある値を保持することが保証されます。
しかし、待ってください。 @clk1 の周波数が @clk2よりも高かったらどうなるでしょうか。 @bar が @fooの値の一部をスキップしても問題ない場合は問題ありません。たとえば、 @foo が Gray code 形式を使用する counter である場合、 @barを確認するときにカウントされた数値の一部がスキップされる可能性があります。それでも、 @bar に表示されるすべての値は、ある時点で @foo に表示されたという意味で正しいです。
このメソッドの最も一般的な使用法は dual-clock FIFOs内であり、 Gray code は FIFOの RAM のアドレスを 2 つの clock domainsに転送するために使用されます。 FIFOの書き込み側は、 RAM に書き込んだ最後のワードの address を Gray codeにエンコードします。エンコードされたワードは、ワードの metastability guards を使用して、別の clock domainにある読み取り側に転送されます。同じ方法が逆方向にも使用されます。
それぞれの側は、もう一方の側の更新された address ( metastability guardsの遅延後) を認識しているため、それぞれの側は FIFOにいくつの要素があるかを計算し、 empty や fullのような信号を生成することもできます。
より高いレートでイベントを伝える
前のページから、単一の bit を備えた metastability guard には、ソースの clock domainの clock の周波数に制限があることを思い出してください。この bit の各変更が相手にイベントを知らせる方法である場合、これらのイベントが頻繁に発生すると、受信側がイベントを見逃すリスクがあります。
解決策は、 Gray code でエンコードされた counter を clock domainsに転送することです。このようにして、受信側は発生したイベントの数を認識できるため、情報が失われることはありません。この counter の bits の数は、イベントが @clk1の clock cycle ごとに発生したとしても、受信側が発生したイベントの数を推測できるように選択されます。
したがって、この意味では、単一の bitよりも vector を使用する方が簡単です。 単一の bit が変更され、宛先の clock が遅いためにその事実が見逃された場合、結果として、何かが起こったことを見逃すことになります。しかし、 clock domains を介して (たとえば Gray codeを使用して) 正しく転送されるワードでは、情報が失われることはありません。
この目的に bit が 1 つで十分な場合、 Gray code counter はイベントごとに値を変更する 1 つのビットにすぎません。つまり、単一の bitでは、 Gray code counter を使用したソリューションは単純な metastability guardとまったく同じです。
pathsの timing についての些細なコメント
このセクションのタイトルが示すように、次のセクションにスキップすることをお勧めします。
vector signals用の metastability guard に関する基本的な仮定があります。 これらの paths の delays の差が、ソースの clockの clock cycle を超えないこと。
この仮定は、特別なことをしなくてもほぼ確実に満たされます。それでも、次の理論的な例を考えてみましょう。 @clk1 の周波数が 500 MHzで、 @fooの paths から @bar_metaguard のいずれかが 1 nsの routing delay であるとします。また、別の path が 4 nsの routing delay を持っているとしましょう。もちろん、これが発生する可能性は非常に低いですが、何が起こるか見てみましょう。
bits の 1 つがその値を変更し、変更は 4 nsを取る旅を開始します。次の clock cycle、 2 ns で、もう一方の bit が変化し、 1 nsの時間後に @bar_metaguard に到達します。しかし、それは最初の bitの到着よりも早い 1 ns です。したがって、 @bar_metaguard は、 @foo が持っていなかった値で単語全体をサンプリングできます。
routing delays は通常、この例よりもはるかに短いため、これは実際には発生しないと予想されます。それにもかかわらず、理論的にはどの routing delay も可能です。この可能性を完全に排除するには、次のような timing constraint を使用できます ( Vivado 形式で記述)。
set_max_delay -datapath_only -from [ get_pins -hier -filter {name=~*/C} ] -to [ get_pins -hier -filter {name=~*_metaguard*/D} ] 1.5
この constraint は、前のページで説明した set_max_delay を搭載したものと似ています。ただし、前のページでは metastability guard が「-from」の部分にあり、ここでは「-to」の部分にあることに注意してください。したがって、 constraints は同じ pathsには適用されません。前のページの constraint の目的は、 metastability guard に metastabilityから回復する時間を与えることでした。したがって、その constraint は、同じ clock間の paths に適用されます。一方、上記の constraint は clock domain crossing 自体に関連しています。
したがって、この constraint は別の方法で記述されます。 関連する paths は unrelated clocksの clock domains 間を接続するため、これらの clocks の skews とこれらの clocks の jitters を考慮しても意味がありません。これは -datapath_only 部分が言うことです: clocks が flip-flopsに到達するのにかかる時間は気にしないでください。 pathを測定するだけです。
この constraint が紛らわしいのは、 path がソースの flip-flopの clock pin から始まり、宛先の data input pin (D) で終了することです。したがって、ストップウォッチはソースの flip-flop が clock を取得したときに開始し、更新された信号が destinationに到着したときに終了します。これは、 setup timeを満たすために必要です。したがって、この path には両側の timing requirementsが含まれます。
この timing constraintのように、これらすべての paths を 1.5 nsに制限することにより、 path はこの制限時間を超えることができず、したがって path delays 間の skew もこの数に制限されます。したがって、 @clk1 に 2 nsの clock period があったとしても、 paths が間違った順序で到着することはありえません。繰り返しになりますが、いずれにせよ非常にありそうもないことですが、これがそれを確実にする方法です。
path から metastability guard への変換が false path constraint (例えば set_false_paths または set_clock_groups) の影響を受ける場合、 set_max_delay はおそらく効果がないことに注意してください。 false path constraint が優先される可能性があります。そのため、ツールが constraints を希望どおりに解釈することを確認するために、常に timing report内の paths を確認してください。 timingに関する別のページで、これについて説明しています。
オプション #2: registerの不定期更新
各 clock cycle で 1 つの bit のみを変更できるという制限は、多くの場合、制限が厳しすぎます。データが時々更新される場合は、別の手法を使用できます。次の例では、 @do_update がアクティブ (つまり、値 '1'を持つ) は、複数の clock cyclesで 1 回だけであると想定しています。また、この register を使用して、 @foo の値を @new_valueで更新する必要があることを示すと仮定しましょう。
reg [7:0] foo, bar;
reg toggle, toggle_metaguard, toggle_a, toggle_b;
reg new_value_bar;
always @(posedge clk1)
if (do_update)
begin
foo <= new_value;
toggle <= !toggle;
end
always @(posedge clk2)
begin
toggle_metaguard <= toggle;
toggle_a <= toggle_metaguard;
toggle_b <= toggle_a;
if (toggle_a != toggle_b)
bar <= foo; // No metastability guard, because foo is stable
new_bar <= (toggle_a != toggle_b); // Not necessary, just side info
end
今のところ、 @new_barは無視してください。それについては後で説明します。
これがどのように機能するかです: @foo は、 @do_update がアクティブな場合にのみ更新されます。その場合、 @toggle は同じ clock cycleで反対の値に変更されます。
@clk2の clock domainでは、 @toggle_metaguard は @toggle の値を metastability guardとして取得します。次の clock cycleでは、この値が @toggle_aにコピーされます。その後の clock cycle では、 @foo の値が直接 @barにコピーされます。これは、 @toggle_a と @toggle_b がちょうど 1 つの clock cycleの間に異なる値を持つためです。
@bar と @foo が異なる clock domains にあるという事実は重要ではありません。なぜなら、 @foo はタイミング要件を満たすのに十分な時間にわたって安定しているためです。
なぜ私はそれについてとても確信しているのですか?今回は正当な理由があり、次のようになります。 @toggle が変更したため、 @toggle_metaguard が値を変更したときに、手順全体が開始されます。 @bar が同じ @clk2 サイクルで @foo をサンプリングしていたら、安全ではなかったでしょうが、運が良ければ問題なかったかもしれません。しかし、 @toggle_metaguardの新しい値が @toggle_aになるまで、 @clk2 の別の clock cycle があります。 @bar はその後も更新されず、 @clk2の次の clock cycle でのみ更新されます。
したがって、 @foo が変化した瞬間から @foo が @bar によってサンプリングされるまでの期間があり、これは @clk2の少なくとも 2 つの clock cycles に相当します。 flip-flopの setup timeと比較すると、それは永遠です。そうは言っても、前のページで @toggle_metaguardに示したように、 set_max_delay を適用することは理にかなっています。 paths から @barでも同じことができますが、前述の永遠性のために必要になる可能性はほとんどありません。
この方法のアキレス腱は、 @barによってサンプリングされたときに @foo が安定したままであることを保証するために、 @do_update がアクティブになることはめったにないことです。このような更新間の合理的な最小時間は、 @clk2の 4 つの clock cycles に対応する時間です。したがって、計算は、 @clk1 の clock cycles が @clk2の 4 つの clock cycles に対応し、最も近い整数に切り上げられます。 @clk1 が @clk2 よりも 4 倍遅い場合 (または遅い場合)、それはまったく制限ではありません。それ以外の場合は、 @do_update が許可されているよりも頻繁にアクティブにならないようにするメカニズムが logic にある必要があります。
実のところ、実際の designsでは、更新速度が非常に遅い場合、 @toggle が提供する種類の保護なしで clock domains が不用意に交差することがあります。このようにすると、 @foo が @bar に連続してコピーされます。 @foo がときどき変更されると、 @bar は 1 つの clock cycleの間に誤った値を含むことがありますが、誰が気にしますか?多くの場合、この間違いは clock domainsの問題全体を無視した結果です。しなくなるまで、たまに。
ずさんなことと言えば、上記の例では、 @toggle もそれに関連する registers もリセットされておらず、初期値が割り当てられていないことに注意してください。 synthesizer はそれらすべてに初期値 0 を割り当てる可能性が高いため、これは通常は問題ありません。また、これらの registers が最初は同じ値を持っていなくても、 @fooの不要なサンプリングが 1 回発生し、それ以上は発生しません。それにもかかわらず、これらの registers をリセットすることをお勧めします。
より高度な亜種
これまで、3 つの簡単な例を紹介してきました。
- metastability guard のおかげで、単一の bitで clock domains を通過します (前のページで)。
- 複数の bitsでも同じですが、各 clock cycleを変更する bit は 1 つだけという制限があります。
- clock domains では単語に制限はありませんが、この単語を変更できる頻度には制限があります。 toggle bit は、単語が安定している場合にのみサンプリングされるようにするために使用されました。
これらの単純な例は、他のいくつかのメカニズムの基礎となっています。
まず、上記の例で @new_bar について何か言うことを約束しました。したがって、 @bar が新しい値を持つとき、1 つの clock cycleの間に高いのは register だけです。これは特別なことではありませんが、 @bar と @new_bar は、他の clock domainの @foo と @do_update を反映していることに注意してください。したがって、これは clock domain を介してコマンドとステータス メッセージを渡す方法です (可能であれば、代わりに FIFO を使用する必要があると言いましたか?)。
最後の例のもう 1 つの興味深い展開は次のとおりです。 registers、 @foo 、 @barのペアの代わりに dual port RAM を配置します。これは、 clock domainsを介して buffers のデータを伝達する方法です。 @clk1 の clock domain 内の logic が RAMにデータを書き込み、しばらくしてこの RAM の半分を満たすとします。 logic が RAMの後半を埋めていくにつれて、 @toggleの値が変化します。この register は、上記のとおり、 @clk2の clock domain にコピーされます。ただし、例のように @bar を更新する代わりに、 logic は RAMの前半のデータを消費します。
これは、この単純な register が double-buffer メカニズムを同期できる方法であり、一方の側が RAM からデータを読み取り、もう一方の側がこの RAMからデータを読み取ります。実際、 @toggle の役割は単に値を変更することではなく、 RAM のどの半分が現在書き込まれているのかを反対側に通知することでもあります。
それでも、可能であれば FIFO を使用することをお勧めします。この double-buffer メカニズムは魅力的に聞こえるかもしれませんが、より良い代替手段がない場合にのみ使用する必要があります。たとえば、 RAM からのデータが、データが書き込まれる順序とは異なる順序で読み取られる場合です。
概要
最終的には、次のようになります。 unrelated clocksから clock domains への移行では、常に resynchronization logic が関与します。この resynchronization を渡すデータ ワードは限られているため、ソースの clock (例では@clk1 ) の各 clock cycle の値を変更できる bit は 1 つだけです。送信先に不正なデータが届く可能性があります。
一部のアプリケーションではこれで十分ですが、この制限があまりにも制約的である場合、代わりに、データ自体で resynchronization logic を使用せずに、 clock domains と vector register の間、または RAMを介してデータを移動できます。これは、データの書き込み操作と読み取り操作の間の最小限の時間差を維持する logic のおかげで機能します。このタイム ギャップにより、宛先でサンプリングされるときにデータ ワードが安定することが保証されます。それにもかかわらず、この logic は clock domain crossingと同じ技術に基づいています。したがって、このソリューションには、おそらく Gray codeを使用して、一度に 1 つの bit を変更することに制限されている resynchronization logic が含まれます。
したがって、 unrelated clocks が関係している場合、 resynchronization logic とこの 1 つの bit のルールは常に存在します。それは、それらがどのように適用されるかの問題です。