見出し画像

異なるクロック間の信号乗せ換え:バス編

 前回の記事で「異なるクロック間の信号乗せ換えの基本」と題して1bitの信号のクロック乗せ換えについて説明しました。
 そんなわけで今回は複数bitの信号の場合です。

 クロックが違うとセットアップタイム、ホールドタイムの違反が必ず発生します。1bitであれば、変化点にあたっても変化前を取ったのか、変化後を取ったのかくらいの話で済みますから、変な中間電位が伝わることだけ防げれば問題ありませんでした。
 複数bitの場合には、この、「変化前を取ったのか、変化後を取ったのか」が重要になります。1bit目は変化前を、2bit目は変化後を取ってしまうと、バス信号として見たときに出鱈目なデータになってしまうからです。

 そのため、複数bitのバス信号を乗せ換える場合には、変化していないタイミングで乗せ換えるか、非同期のFIFOを使用します。

 変化していないタイミングで乗せ換える回路の例です。

module DiffClkCnvBus(
      RESETn, CLK_A, DATA_A, CLK_B, DATA_B
);

input  RESETn;       // system reset
input  CLK_A;        // system clock A
input  [7:0] DATA_A; // input data A
input  CLK_B;        // system clock B
output [7:0] DATA_B; // output data B

reg    [7:0] tmp_A;  // tmp register A clock
reg    [7:0] tmp_B1; // tmp register B clock
reg    [7:0] tmp_B2; // tmp register B clock
reg    [7:0] tmp_B3; // tmp register B clock
reg    [7:0] tmp_B4; // tmp register B clock

always @ ( posedge CLK_A or negedge RESETn ) begin
         if( !RESETn )
                tmp_A <= 8'h0;
         else
   	            tmp_A <= DATA_A;
end

always @ ( posedge CLK_B or negedge RESETn ) begin
         if( !RESETn ) begin
                tmp_B1 <= 8'h0;
                tmp_B2 <= 8'h0;
                tmp_B3 <= 8'h0;
         end
         else begin
                tmp_B1 <= tmp_A;
                tmp_B2 <= tmp_B1;
                tmp_B3 <= tmp_B2;
         end
end

always @ ( posedge CLK_B or negedge RESETn ) begin
         if( !RESETn )
                tmp_B4 <= 8'h0;
	     else if( tmp_B2 == tmp_B3 )
                tmp_B4 <= tmp_B3;
         else
   	            tmp_B3 <= tmp_B3;
end

assign DATA_B = tmp_B4;


 まずは、1bitの時と同様に2段のフリップフロップを使ってCLK_Bに乗せ換えます。
 複数bitの場合には、その後にもう一段、フリップフロップtmp_B3にも代入し、前段のtmp_B2と等しい値かどうかをチェックします。これは変化していないタイミングを検知するためのものです。
 tmp_B2とtmp_B3の値が等しければ、入力が変化していないと判断し、最後のtmp_B4に代入します。

 変化していないタイミングを計ることで「変化前を取ったのか、変化後を取ったのか」という問題を回避する方法です。

 しかし、この方法は、入力が絶えず変化している場合には適しません。
 この方法で絶えず変化する入力信号の、変化しないタイミングを計るためには、CLK_Aの数倍の速度がCLK_Bに必要になります。

 入力が絶えず変化し、入出力のクロック周波数に大きな差がない場合には、非同期のFIFOを使用します。
 非同期のFIFOは、FPGA自体が非同期FIFOに対応した構造を持っている必要があります。

 FPGAの機能としての非同期FIFOですので、使う際にはメーカーのIPを使います。
 Vivadoの場合ですとIP Catalogから「FIFO Generator」を呼び出して使用します。

画像1

 非同期FIFOを使えば、入出力クロックに差があまりない環境で、入力データが常に変化していても対応出来ます。
 ただし、入力クロックの周波数が高かった場合にはFIFOが頻繁に溢れますし、出力クロックの周波数が高かった場合にはFIFOは常に空の状態に近いでしょう。

 そのあたりの制御はFullやEmptyのフラグを管理して行う必要があります。制御回路が複雑になりがちです。必要な条件を精査してからロジックを組むことをお勧めします。

  二つの方法を説明しました。

 いずれも、入出力のクロック周波数の差、入力信号を全て伝えたいのか、最新のデータだけが見えればいいのか、そういったいくつかの条件で細かい手法が変わってきます。

 出力クロックが入力クロックよりも明らかに遅いのに、全ての入力データを出力で見たい、というのは不可能です。繰り返しになりますが、必要な条件を精査してからロジックを組むことをお勧めします。

この記事が気に入ったらサポートをしてみませんか?