01signal.com

ソース同期入力に対する 01 信号サンプリングの利用

はじめに

このページでは、ソース同期入力 (source-synchronous input) とのインターフェース方法として、01 信号サンプリング (01-signal sampling) を説明します。この種のデータソースとインターフェースするための他の戦略については、ソース同期入力全般に関するページで概説しています。そのページでは、ソース同期入力とは何かについても説明しています。

01 信号サンプリングの背後にある考え方は、外部クロックをデータ信号として扱うことです。したがって、そのクロックはレジスタによってサンプリング (sampling) されます。このレジスタは、外部クロックから独立した安定した内部クロックを使用します。

データ信号は追加のレジスタによってサンプリングされます。同じ内部クロックがこの目的に使われます。この内部クロックは、01 信号サンプリングを実装するすべてのロジックでも使われます。

このロジックは、外部クロックをサンプリングしているレジスタの値の変化を検出します。このレジスタが '0' から '1' に変わったとき、それは外部クロックに立ち上がりエッジがあったことを意味します。ロジックはこのイベントに応答して、他のレジスタの値を FIFO に書き込みます。これらのレジスタには、外部クロックの立ち上がりエッジが発生した時点に存在していたデータ信号の値が格納されています。

このロジックは、FIFO のクロックが外部クロックであり、FIFO のデータ入力がデータ信号に直接接続されている場合と同じ結果を達成します。違いは、ロジックが使うクロックが外部クロックか内部クロックかという点だけです。01 信号サンプリングの利点は、すべてのロジックが内部クロックのみに依存することです。このクロックは安定かつ信頼できます。外部クロックが誤動作しても、ロジックは妥当な動作を続けます。

次の図は 01 信号サンプリングを示しています。

Example of 01-signal sampling

この図では、@stable_clk は FPGA の内部クロックです。@data_clk と @data は FPGA に到達する信号です。@data_clk_samp と @data_samp は FPGA 内部のレジスタです。図において、外部信号 @data_clk は @data_clk_samp に、@data は @data_samp にそれぞれ対応します。

この図は、@data_clk_samp に "0 1" パターンが現れると、@data_samp の値が FIFO に書き込まれることを示しています。これが、この方式が「01 信号サンプリング」と呼ばれる理由です。

ここでは、到着したデータをどう処理するかの一例として FIFO を示しています。内部クロックの周波数はデータレートよりもかなり高いため、これはしばしば適切な解決策となります。そのため、その後はより低いクロック周波数を基準とするロジックでデータを処理する方が便利なことがよくあります。FIFO は、データを別のクロックドメイン (clock domain) のロジックへ引き渡すための便利な方法です。

とはいえ、残りのロジックを 01 信号サンプリングに使ったものと同じ内部クロックで実装することも可能です。FIFO を使うのは、あくまでも 1 つの選択肢にすぎません。

Verilog による例

次の Verilog コードはその考え方を示しています。外部クロックは @data_clk です。

module top (
   input stable_clk,

   input data_clk,
   input [7:0] data
);

   reg [7:0] data_guard, data_samp;
   reg       data_clk_guard, data_clk_samp, data_clk_samp_d;
   wire      fifo_wr_en;

   always @(posedge stable_clk)
     begin
        data_guard <= data;
        data_clk_guard <= data_clk;

        data_samp <= data_guard;
        data_clk_samp <= data_clk_guard;

        data_clk_samp_d <= data_clk_samp;
     end

   assign fifo_wr_en = data_clk_samp && !data_clk_samp_d;

   data_fifo fifo_i
     (
      .wr_clk(stable_clk),
      .din(data_samp),
      .wr_en(fifo_wr_en),

       [ ... other ports connected here ... ]
      );
endmodule

重要なのは FIFO の wr_en、すなわち @fifo_wr_en です。この信号は "data_clk_samp && !data_clk_samp_d" に等しいので、@data_clk_samp において "0 1" パターンを検出した結果として High になります。それにより、@data_samp の値が FIFO に書き込まれます。

@stable_clk だけがロジックのクロックとして使われることに注意してください。@data_clk は通常の I/O 入力として扱われます。

@data_guard と @data_clk_guard のタイミング要件は保証されません。入力ポートは @stable_clk に対して非同期だからです。したがって @data_guard と @data_clk_guard は メタステイビリティガード (metastability guard) です。ロジックはこれら 2 つのレジスタの値に直接依存しません。一方、@data_samp と @data_clk_samp は、そのガードの 2 段目にあたるため、ロジックから直接使用されます。

しかし、これで本当に機能するのでしょうか? その答えはタイミング解析にあります。

タイミング解析

01 信号サンプリングのタイミング解析は通常の方法とは異なります。通常は、クロックエッジとデータ信号がサンプリングされる時点の間には一定の時間差があります。これは、サンプリングに使うクロックがデータ信号と同期しているためです。しかし 01 信号サンプリングでは、@data のサンプリングは @stable_clk で行われます。したがって、サンプリングのタイミングはデータ信号自身のタイミングとは無関係です。その代わりにロジックは、@data_clk_samp が "0 1" と変化したとき、つまり外部クロックの立ち上がりエッジがあったときに得られた値だけを選び出します。

つまり、データクロックの立ち上がりエッジの時点と、実際にサンプリングが行われる時点の間には、ランダムな時間差があります。それなのに、なぜこの方法は信頼できるのでしょうか?

ロジック設計におけるタイミングの基本については、専用ページがあります。そのページでは tsu と thold の意味を説明しています。要点を短く言えば、フリップフロップの入力ポートは、クロックの立ち上がりエッジの前後で安定していなければなりません(フリップフロップが立ち上がりエッジで動作すると仮定します)。tsu は、入力ポートが立ち上がりエッジのに安定していなければならない時間を定義します。thold は、入力ポートが立ち上がりエッジのに安定していなければならない時間を定義します。これらの条件のいずれかが満たされない場合、立ち上がりエッジに対するフリップフロップの応答は予測不能になります。

これは別の見方もできます。入力ポートは、立ち上がりエッジの周辺の特定の時間帯にわたって安定している必要があります。この時間帯を Δt = tsu + thold と呼ぶことにしましょう。ここからは、Δt と他のパラメータに基づいて、確実な動作を保証するタイミング要件を求めます。

タイミング解析はすべて、@data_clk_guard が High で @data_clk_samp が Low である状況を前提にします。この状況が起きると、"0 1" パターンは 1 クロックサイクル後に検出されます。言い換えると、次のクロックサイクルでは @data_clk_samp_d が '0'、@data_clk_samp が '1' になります。その結果 @fifo_wr_en は High になるので、@data_samp にはデータソースが意図した値が入っていなければなりません。

したがって解析は次の問いに焦点を当てます。@data_clk_guard が High で @data_clk_samp が Low のとき、情報が正しく届くために @data に要求される条件は何でしょうか?

解析は 2 つの部分に分けて行います。どちらの部分でも、@data_clk_guard が High で @data_clk_samp が Low であると仮定します。最初の部分での問いは、この仮定を崩さずに @data_clk が Low から High へ変化できる最も遅いタイミングはいつか、というものです。次に、@data_samp が正しい値を含むことを保証する、@data に関するタイミング要件を求めます。

解析の 2 番目の部分では、逆の問いをします。@data_clk_guard と @data_clk_samp に関する仮定を崩さずに、@data_clk が Low から High へ変化できる最も早いタイミングはいつか、というものです。そして、タイミング要件に関して同様の解析を行います。

その前に、いくつかの記号を定義しましょう。

解析の第 1 部

このタイミング図は @stable_clk の 2 クロックサイクルを示しています。以下の説明では、@data_clk_guard は右側の立ち上がりエッジで @data_clk から新しい値を取り込みます。同様に、@data_guard は同じクロックエッジで @data から新しい値を取り込みます。

同じ原理により、@data_clk_samp と @data_samp は、左側にある @stable_clk の立ち上がりエッジに対応しています。

Timing diagram for late @data_clk scenario

このシナリオでは、@data_clk は黄色い領域の終わりにちょうど High に変化します。フリップフロップのタイミング要件が満たされないため、結果は予測不能です。しかし、@data_clk_guard が High で @data_clk_samp が Low になることはあり得ます

しかし、@data_clk がもう少し遅れて値を変えたなら、@data_clk_guard は確実に Low になります。このシナリオでは、黄色い領域の全体にわたって入力ポートが Low だからです。したがって次のように言えます。@data_clk_guard が High で @data_clk_samp が Low なら、@data_clk は上のタイミング図に示された時点より早く Low から High へ変化しています。つまり、そのタイミング図は、@data_clk が最も遅い可能な時点で変化したシナリオを示しています。

@data_guard が信頼できる値を保持するには、@data は黄色い領域のから安定していなければなりません。これが最初のタイミング要件です。すなわち、@data は @data_clk の立ち上がりエッジの前の Δt の間、安定していなければなりません。

@data_clk が Low から High へより早く変化した場合でも、このタイミング要件によって @data_guard の tsu 要件は依然として満たされることに注意してください。したがって、@data_clk_guard が High で @data_clk_samp が Low となるすべてのシナリオで、フリップフロップの tsu が保証されます。

上のタイミング図には、スキューに関係するものは何も示されていません。スキューを考慮に入れると、タイミング要件は次のようになります。@data は @data_clk の立ち上がりエッジの前の Δt + tskew の間、安定していなければなりません。

このシナリオでは、議論に関わるクロックエッジが 1 つだけなので、ジッタを考慮する必要はありません。

解析の第 2 部

次は、このシナリオのタイミング図です。

Timing diagram for early @data_clk scenario

このシナリオでは、@data_clk は、@stable_clk の前のクロックエッジにある黄色い領域のちょうど先頭で High に変化します。

この状況では @data_clk_guard が High になることは間違いありません。しかし、前のクロックサイクルでタイミング違反があったため、@data_clk_samp の値は予測できません。前と同様、@data_clk_samp が Low になることはあり得ます。しかし、@data_clk がこれより早く値を変えた場合、@data_clk_samp は確実に High になります。

したがって、@data_clk_guard が High で @data_clk_samp が Low の場合、@data_clk は上のタイミング図に示された時点より遅く Low から High へ変化しています。つまり、このタイミング図は、@data_clk が最も早い可能な時点で変化したシナリオを示しています。

@data_guard が正しい値を保持するには、@data は右側の黄色い領域のまで安定していなければなりません。

上のタイミング図によれば、@data_clk の立ち上がりエッジから 2 番目の黄色い領域の終わりまでの時間差は Δt + tclk です。しかし、スキューとジッタも考慮する必要があります。したがって、@data は @data_clk の立ち上がりエッジの後の Δt + tclk + tskew + tj の間、安定していなければなりません。

@data_clk が Low から High へより早く変化した場合でも、このタイミング要件によって @data_guard のホールド要件が満たされることに注意してください。したがって、@data_clk_guard が High で @data_clk_samp が Low となるすべてのシナリオで、フリップフロップの thold が保証されます。

タイミング要件

上記のタイミング解析の結論として、01 信号サンプリングの確実な動作を保証するためのタイミング要件は次の 2 つです。

これらの要件は複雑に見えるかもしれませんが、満たすのはたいてい簡単です。例えば、@data が @data_clk の立ち下がりエッジに合わせて変化するなら、これらの要件を満たすのは通常容易です。また、@stable_clk が @data_clk の 3 倍の周波数であれば、それで十分なことがよくあります。

多くの場合、Δt、tskew、tj の正確な値を知る必要はありません。上記の 2 つの要件に基づいて、Δt と Δt + tskew + tj がそれぞれどれだけの値まで許容されるかを計算すれば十分なことが多いです。@stable_clk の周波数が十分に高ければ、これら 2 つの式の値は、FPGA で現実的に起こり得る値よりも大きくなることがよくあります。

IOB レジスタは、FPGA の入力ポート間のタイミング差(tskew)を最小限にするために使うべきです。また、IOB レジスタが使われることを保証するために、これらの入力ポートに関してタイミング制約 (timing constraints) を書くべきです。

01 信号サンプリングの変形

ここまでの説明では、@data は @data_clk の立ち下がりエッジに合わせて変化すると仮定していました。もし @data が @data_clk の立ち上がりエッジに合わせて変化するなら、ロジックを調整して、@data_clk_guard が Low で @data_clk_samp が High のときに有効になるようにすべきです。言い換えると、ロジックは "1 0" パターンを探すことによって @data_clk の立ち下がりエッジを検出しなければなりません。

@data の値を取り込むタイミングの基準を変えることも可能です。例えば、@fifo_wr_en を数クロックサイクル遅らせた方がよい場合もあります。また、上記で示したタイミングより早く @fifo_wr_en をアクティブにした方がよい場合もあります。これは、@data_clk と @data の間のタイミング関係に依存します。信号の供給元のデータシートを参照し、上で示したようにタイミングを解析してください。

@data_clk の周波数が比較的高い場合は、DDR レジスタを使って @data_clk と @data をサンプリングすることが可能です。この方法を実装するロジックは少し複雑になりますが、同じ原理が適用されます。

@data_guard は本当にメタステイビリティガードなのか?

この問いに対する短い答えは「はい」です。@data は @stable_clk に対して非同期なので、@data_guard のタイミング要件は保証されません。

しかし、@data_guard の内容が実際に使われるクロックサイクルに議論を絞ってみましょう。前述の 2 つのタイミング要件により、@data_guard を実装するフリップフロップが確実に動作することが保証されていることに注目してください。これらのフリップフロップの tsu と thold は両方とも保証されています。

したがって、@data_guard は実際にはメタステイビリティガードとしては使われていません。このレジスタが使われる方法では、単なる遅延レジスタとして機能します。しかし、物理信号(@data)が誤動作した場合に起こり得るタイミング違反から追加のレジスタが保護してくれるのは、害にはなりません。これは、信号がコネクタを介して FPGA に接続されている場合に特に重要です。

実際の例

OV7670 カメラセンサと接続するロジックの例が、別のページにあります。そのロジックは、01 信号サンプリングによってこのカメラセンサからピクセルデータを取得しています。カメラセンサからの入力は次のとおりです。

   input       pclk_in;
   input [7:0] D_in;
   input       hsync_in, vsync_in;

01 信号サンプリングを行うロジックは次のとおりです。

   (* IOB = "TRUE" *) reg [7:0] D_guard;
   (* IOB = "TRUE" *) reg       pclk_guard, hsync_guard, vsync_guard;

   reg [7:0]  D;
   reg 	      pclk, hsync, vsync;

   wire       sample_valid;
   reg 	      previous_pclk;

   always @(posedge stable_clk)
     begin
	// Metastability guards on asynchronous inputs
	D_guard <= D_in;
	pclk_guard <= pclk_in;
	hsync_guard <= hsync_in;
	vsync_guard <= vsync_in;

	D <= D_guard;
	pclk <= pclk_guard;
	hsync <= hsync_guard;
	vsync <= vsync_guard;

        previous_pclk <= pclk;
     end

   assign sample_valid = pclk && !previous_pclk;

ここで示した Verilog コードと、OV7670 のページの Verilog コードには、わかりやすくするために若干の違いがあります。これらの違いは、ロジックの動作には影響しません。

この Verilog コードを、このページの最初に示したコードと比較してみましょう。信号の名前は異なりますが、意味は同じです。@data の代わりに @D_in、@hsync_in、@vsync_in があります。@data_clk の代わりに @pclk_in があります。また、@fifo_wr_en の代わりに @sample_valid があります。名前の変更は紛らわしいかもしれませんが、上記の Verilog コードと機能的に違いはありません。

レジスタの宣言の前にある "(* IOB = "TRUE" *)" の部分に注目してください。Vivado を使用する場合、これはレジスタを IOB に挿入するよう要求する方法の 1 つです。

この例では FIFO は示されていません。すべてのデータを FIFO に書き込みたいわけではないからです。@sample_valid が High のとき、@D、@hsync、@vsync にはカメラセンサからの正しい値が入っています。しかし、それは @D を FIFO に書き込みたいという意味ではありません。@hsync と @vsync に依存します。そのため、OV7670 の例では、ピクセルのみが FIFO に書き込まれるようにする追加ロジックがあります。

さて、興味深い部分であるタイミング解析に移りましょう。

この例の @stable_clk の周波数は 100 MHz です。@pclk_in の周波数は 25 MHz です。OV7670 のデータシートによれば、@D_in、@hsync_in、@vsync_in は、@pclk_in が High から Low に変化した(立ち下がりエッジの)後の 5 ns の間、安定していることが保証されています。

@pclk_in のクロック周期は 40 ns です。したがって、立ち下がりエッジから立ち上がりエッジまでの距離は 20 ns です。それでは、これをタイミング要件と比較してみましょう。

最初のタイミング要件は、@D_in、@hsync_in、@vsync_in が @pclk_in の立ち上がりエッジのに Δt の間安定していることでした。実際には、これらの信号は立ち下がりエッジの 5 ns 後から安定します。したがって、これらの信号は次の立ち上がりエッジの少なくとも 15 ns 前から安定しています。Δt = tsu + thold であることを思い出してください。つまり、実際のタイミング要件は、tsu + thold が 15 ns より小さいことです。これはすべての FPGA に当てはまります。

2 番目の要件は、これらの信号が @pclk_in の立ち上がりエッジのに、少なくとも Δt + tclk + tskew + tj の間安定していることでした。しかし、これらの信号が変化するのは立ち下がりエッジのときだけです。したがって、要件は Δt + tclk + tskew + tj が 20 ns より小さいことになります。@stable_clk の周波数が 100 MHz なので、tclk は 10 ns です。したがって実際の要件は、Δt + tskew + tj が 10 ns より小さいことです。繰り返しますが、これはすべての FPGA で明らかに成り立ちます。

この例は、FPGA の正確なタイミングパラメータを知らなくても、タイミング要件を容易に保証できることを示しています。

まとめ

01 信号サンプリングは、FPGA がサポートできるクロック周波数に比べてデータレートが低い場合、優れた解決策です。データクロックが安定している必要はありません。また、データクロックの正確な周波数を事前に知っている必要もありません。2 つのタイミング要件を保証すれば十分です。

この方法にはさらに利点があります。このクロックが一時的に停止しても、その時間帯にデータが収集されないだけです。このクロックの誤動作による損害は、データフローの中断だけに限られます。それはシステムの目に見える誤動作につながりますが、その誤動作はクロックの問題らしく見えます(FPGA が幽霊に取り憑かれたようには見えません)。

したがって、データ信号のサンプリングには本質的なランダム性があるにもかかわらず、01 信号サンプリングはソース同期入力に対する信頼性が高く堅牢な解決策です。唯一の実際の欠点は、データレートの制限です。

このページは英語から機械翻訳されたものです。不明な点があれば、原文を参照してください。
Copyright © 2021-2026. All rights reserved. (dcc38493)