verilog記述のポイント:グレイコードカウンタで多ビット値をドメイン間へ渡す
| 設計内容分類 | FPGA |
|---|---|
| 解決できる課題 | CPU負荷低減 |
実現したい仕様・課題
FIFOのポインタのように連続的に変化する多ビットカウンタ値を別クロックドメインで参照したい場合、バイナリカウンタをビットごとに2段同期化すると、複数ビットが同時に変化する瞬間の取り込みで各ビットの新旧が混ざり、実際には存在しない値として読めてしまいます。多ビットの変化値を安全にドメインをまたいで観測したいというのが課題です。
設計のポイント
連続カウンタのドメイン間受け渡しには、グレイコード(隣接する値の間で1ビットしか変化しない符号)を使用します。バイナリコードでは、たとえば7→8(0111→1000)で4ビットが同時に変化し、同期化のタイミングによっては新旧ビットが混在した無関係な値(0000や1111など)が読めてしまいます。グレイコードなら値の遷移で変化するのは常に1ビットだけなので、同期化タイミングがどうであれ、読める値は「変化前」か「変化後」のいずれかに限定され、偽の中間値が原理的に発生しません。実装は、カウンタ本体はバイナリで保持し、bin ^ (bin >> 1) でグレイコードに変換した値をレジスタで受けてから相手ドメインへ渡し、受け側で2段同期化します。受け側で数値として使う場合はグレイ→バイナリ変換(上位から順次XOR)を行います。この手法は非同期FIFO(DCFIFO)の内部でポインタ比較に使われている標準技術であり、自前で非同期FIFOやドメインまたぎの進捗カウンタを設計する際の必須要素です。適用条件として、カウンタが±1ずつ連続的に変化することが前提であり、ジャンプする値には適用できません。また、グレイ変換後の値は必ずレジスタ経由で渡し、組み合わせ出力のまま渡してグリッチを拾わないようにすることが実装上の注意点です。
【コード例】
// バイナリ→グレイ変換(レジスタで受けて渡す)
always @(posedge clk_a) gray_a <= bin_a ^ (bin_a >> 1);
// 受け側:2段同期化
always @(posedge clk_b) begin g1 <= gray_a; g2 <= g1; end
// グレイ→バイナリ変換(4bitの例)
wire [3:0] bin_b;
assign bin_b[3] = g2[3];
assign bin_b[2] = bin_b[3] ^ g2[2];
assign bin_b[1] = bin_b[2] ^ g2[1];
assign bin_b[0] = bin_b[1] ^ g2[0];

補足情報
設計のポイント 一覧
FPGA設計のポイント
- verilog記述のポイント:always内の出力はできる限り少なくする
- 定数のビット幅明示
- FPGAによるスキャンコンバータの実装
- ストリーミングデータ参照による高速演算処理
- FPGAによるLCDコントローラの実装
- USB HS FIFOによる高速計測アプリケーションの実装
- verilog記述のポイント:連接演算子を使う
- verilog記述のポイント:複雑な組み合わせ回路にfunctionを使用する
- verilog記述のポイント:wireとregの違い
- verilog記述のポイント:双方向ピン(inout)を使用する
- verilog記述のポイント:モジュールの接続
- verilog記述のポイント:output regを使用する
- verilog記述のポイント:定数にparameterを使う
- verilog記述のポイント:二次元配列
- verilog記述のポイント:ブロッキング・ノンブロッキング
- DCFIFOによりクロックが異なる回路を接続し、開発リードタイム短縮
- PC搭載システムのダウンサイジング
- 複数のSPIデバイスの同時制御
- ソフトコアプロセッサ使用によるマイコンとゲートアレイのワンチップ化
- verilog記述のポイント:case文にはdefaultを必ず記述する
- verilog記述のポイント:クロック分周ではなくクロックイネーブルを使う
- verilog記述のポイント:generate文による繰り返し回路の生成
- verilog記述のポイント:signedを使った符号付き演算
- verilog記述のポイント:除算・剰余演算を避ける
- verilog記述のポイント:演算結果のビット幅と桁あふれに注意する
- verilog記述のポイント:casex・casezの多用を避ける
- verilog記述のポイント:初期値に頼らずリセットで初期化する
- verilog記述のポイント:シフトレジスタの記述
- verilog記述のポイント:エッジ検出回路の記述
- verilog記述のポイント:チャタリング除去(デバウンス)回路
- verilog記述のポイント:パイプライン化による動作周波数の向上
- verilog記述のポイント:演算子の優先順位は括弧で明示する
- verilog記述のポイント:`defineとparameterの使い分け
- VHDL記述のポイント:signal代入はプロセス終了時に反映される
- VHDL記述のポイント:processの感度リスト漏れに注意する
- VHDL記述のポイント:型変換の作法(std_logic_vector・unsigned・signed・integer)
- VHDL記述のポイント:others => ‘0’ を活用する
- VHDL記述のポイント:rising_edge(clk)を使う
- VHDL記述のポイント:列挙型でステートマシンの状態を定義する
- VHDL記述のポイント:record型で関連信号をまとめる
- VHDL記述のポイント:packageで共通定義を集約する
- VHDL記述のポイント:数値リテラルの記述と型の修飾
- verilog記述のポイント:taskで手続きをまとめる(functionとの違い)
- verilog記述のポイント:for文による組み合わせ回路の記述
- verilog記述のポイント:インデックス付きパートセレクト(+:)でビットフィールドを扱う
- verilog記述のポイント:valid/readyハンドシェイクによるデータ受け渡し
- verilog記述のポイント:同期FIFOの設計
- VHDL記述のポイント:subtypeで意味のある型名を定義する
- VHDL記述のポイント:std_ulogicでマルチドライバを検出する
- FPGA利用によるCPU負荷の削減
- VHDL記述のポイント:after句・wait文は合成対象外と心得る
- VHDL記述のポイント:booleanとstd_logicの使い分け
- VHDL記述のポイント:aliasで信号やスライスに別名を付ける
- VHDL記述のポイント:shift_left・shift_right関数によるシフト演算
- VHDL記述のポイント:resize関数でビット幅を変換する
- VHDL記述のポイント:functionとprocedureの使い分け
- verilog記述のポイント:$readmemhによるメモリ・ROMの初期化
- verilog記述のポイント:グレイコードカウンタで多ビット値をドメイン間へ渡す
- verilog記述のポイント:トグル同期によるクロックドメイン間のパルス受け渡し
- VHDL記述のポイント:port mapは名前による結合を使う
- VHDL記述のポイント:直接インスタンス化とcomponent宣言の使い分け
- VHDL記述のポイント:constantで定数を定義する
- VHDL記述のポイント:integerにはrange指定を付ける
- VHDL記述のポイント:属性(’range・’length)で幅に依存しない記述
- VHDL記述のポイント:配列型の定義と活用
- VHDL記述のポイント:genericによるモジュールのパラメータ化
- VHDL記述のポイント:同時処理文(when-else/with-select)とprocessの使い分け
- VHDL記述のポイント:numeric_stdを使う(std_logic_arithを避ける)
- VHDL記述のポイント:signalとvariableの違いと使い分け
- VHDL記述のポイント:std_logicとstd_logic_vectorを使う
- verilog記述のポイント:トライステートはトップレベルのみで使用する
- verilog記述のポイント:ブロックRAMが推論される記述パターン
- verilog記述のポイント:ステートマシンの記述パターン
- verilog記述のポイント:2段フリップフロップによる非同期信号の同期化
- verilog記述のポイント:非同期リセットと同期リセットの使い分け
- verilog記述のポイント:組み合わせ回路のalwaysはalways @(*)で記述する



