Verilog のノンブロッキング代入(<=)とブロッキング代入(=)の違い

hardware verilog hdl

概要

Verilog では、プログラミング言語のような逐次実行ではなく、ハードウェア(回路)の並行動作を記述する。そのため代入の意味も一般的な言語とは異なり、<= は「以下かどうかの比較」ではなく「ノンブロッキング代入」という代入演算子として使われる。

module d_flip_flop (
    input wire clk,
    input wire d,
    output reg q
);

    always @(posedge clk) begin
        q <= d;
    end

endmodule

このコードは D フリップフロップ(クロックの立ち上がりエッジで入力 d の値を出力 q に取り込む回路)を表している。module でポート(clk、d、q)を定義し、clk の立ち上がり(posedge clk)のたびに always ブロックの中身が実行される。

Verilog の代入には 2 種類ある。

  • ブロッキング代入(=): 右辺を評価した直後に、その場で左辺へ代入する。記述順に逐次実行されるような挙動になる。
  • ノンブロッキング代入(<=): 右辺の値を評価はするが、実際の左辺への代入は、同じシミュレーション時刻のタイミング(そのクロックエッジでの更新サイクルの最後)でまとめて行われる。

この違いが重要になるのは、複数のフリップフロップが同じクロックで同時に値を更新するような回路を記述するとき。もしブロッキング代入を使うと、記述した順番によって「先に更新された新しい値」を後続の代入が読んでしまい、実際のハードウェアの並行動作とシミュレーション結果がずれてしまうことがある。ノンブロッキング代入であれば、そのクロックエッジでの全ての右辺評価を先に済ませてから、まとめて左辺を更新するため、複数のフリップフロップが「同時に」値を更新する実際の回路の動作を正しく表現できる。

手順

  1. 順序回路(クロックエッジで状態を更新するフリップフロップ・レジスタ)を記述する際は、always @(posedge clk) ブロック内の代入にノンブロッキング代入(<=)を使う。
  2. 組み合わせ回路(always @(*) などクロックに同期しないロジック)を記述する際は、ブロッキング代入(=)を使うのが一般的な使い分け。
  3. 同じ always ブロック内でブロッキングとノンブロッキングを混在させない。

注意点

  • <= を見て「比較演算子では」と早合点しないこと。文脈(always ブロック内の代入位置)で判断する必要がある。
  • 順序回路にブロッキング代入を使ってしまうと、シミュレータ上ではたまたま意図通りに動いて見えても、実際の論理合成後の回路では意図しないタイミングの差異が生じることがあるため、「順序回路は <=、組み合わせ回路は =」という使い分けの原則を徹底するのが安全。