ブロッキング代入とノンブロッキング代入の違い:評価のタイミング・順序・使う場所を比較

どちらもVerilogのalwaysブロックの中で使う代入ですが、左辺が書き換わるタイミングが違います。ブロッキング代入(=)は、その行を実行した時点で左辺を更新し、次の行では新しい値が使われます。ノンブロッキング代入(<=)は、同じタイミングのすべての右辺を先に評価し、そのあとでまとめて左辺を更新します。クロックで動く順序回路には<=、組み合わせ回路のalwaysには=、というのが基本のガイドラインです。

項目ブロッキング代入(=)ノンブロッキング代入(<=)
書き方a = b;a <= b;
左辺を更新するタイミングその行を実行したとき、すぐに更新する同じタイミングの右辺をすべて評価した後、まとめて更新する
次の行から見える値更新後の新しい値更新前の古い値
記述の順序への依存あり。行の順番を入れ替えると結果が変わるほぼなし。ただし同じ変数に2回代入すると、後の代入が残る
対応する回路の動き上から順に途中の値を計算していく組み合わせ回路クロックのエッジで一斉に値を取り込むフリップフロップ
主に使う場所組み合わせ回路のalways @(*)、テストベンチの手続きクロックで動く順序回路のalways @(posedge clk)
2段のシフトレジスタ(クロックで動くブロック)q1 = d; q2 = q1; では、q2にも同じエッジでdが入り、1段しか遅れないq1 <= d; q2 <= q1; では、q2にエッジの前のq1が入り、2段になる
値の入れ替え(クロックで動くブロック)a = b; b = a; では、aとbが同じ値になるa <= b; b <= a; では、aとbが入れ替わる
複数のalwaysブロックの間同じエッジで動くブロックどうしで、実行の順番によって結果が変わるレースが起きやすい右辺の評価が先にそろうので、レースが起きにくい
使い方を誤ったとき順序回路に使うと、結果がブロックの実行順に左右され、合成した回路と動きが食い違うことがある組み合わせ回路に使うと、途中の値が古いまま読まれ、シミュレーションが合成した回路と食い違ったり、余分に再実行されたりする

数値は代表的な目安で、世代や製品によって異なります。

どんなときに使うか

ブロッキング代入(=)
組み合わせ回路のalways @(*)の中で、途中の値を順に計算するとき。テストベンチで手順を順番に書くとき。
ノンブロッキング代入(<=)
always @(posedge clk)など、クロックで動くフリップフロップを書くとき。レジスタ、カウンタ、状態機械の状態の更新。
混ぜない
1つのalwaysブロックの中で=と<=を混ぜず、同じ変数を複数のalwaysブロックから代入しないようにします。SystemVerilogでは、always_ffとalways_combで意図をツールに伝えられます。

よくある質問

順序回路にブロッキング代入を使うと、どうなりますか?

行の順番によっては、同じエッジで更新した値が次の行にそのまま渡り、意図より段数の少ない回路になります。また、同じクロックで動くほかのalwaysブロックとの間で、実行の順番によって結果が変わるレースが起き、シミュレーションと合成した回路が食い違う原因になります。

ノンブロッキング代入で、a <= b; b <= a; と書くと値が入れ替わるのはなぜですか?

2つの右辺を古い値のまま先に評価してから、左辺をまとめて更新するからです。2つのフリップフロップの出力を、互いの入力につないだ回路と同じ動きになります。

テストベンチでは、どちらを使えばよいですか?

手順を順に書く部分はブロッキング代入で構いません。ただし、回路への入力をクロックのエッジと同じ時刻に変えると、古い値と新しい値のどちらを取り込むかが実行の順番で変わるので、エッジから少しずらすか、ノンブロッキング代入で変えます。

理解度チェック

Verilogのノンブロッキング代入(<=)の動作として正しいものはどれか。

  1. A代入した瞬間に左辺が更新され、次の行ではその新しい値が使われる
  2. B同じタイミングのすべての右辺を先に評価し、その後でまとめて左辺を更新する
  3. C代入の結果を捨てて、何も変更しない
答えと解説を見る

正解:B. 同じタイミングのすべての右辺を先に評価し、その後でまとめて左辺を更新する

ノンブロッキング代入では、クロックのエッジで、すべての<=の右辺をまず古い値のまま評価し、そのあとで一斉に左辺を書き換えます。これは、同じクロックで動く多数のフリップフロップが、エッジの直前の値を同時に取り込む実際の回路と同じ動きです。そのため、クロックで動く順序回路の記述にはノンブロッキング代入を使うのが原則です。

Verilogで代入の種類を使い分けるときの一般的なガイドラインとして正しいものはどれか。

  1. Aどこでもブロッキング代入(=)だけを使う
  2. Bクロックで動く順序回路にはノンブロッキング代入(<=)、組み合わせ回路のalwaysにはブロッキング代入(=)を使う
  3. C1つのalwaysの中で、2つをでたらめに混ぜて使う
答えと解説を見る

正解:B. クロックで動く順序回路にはノンブロッキング代入(<=)、組み合わせ回路のalwaysにはブロッキング代入(=)を使う

順序回路をノンブロッキング代入で書くと、すべてのフリップフロップが同時に値を取り込む回路の動きと一致し、記述の順番や、ほかのalwaysブロックとの実行の順番に結果が左右されません。組み合わせ回路では、上から順に途中の値を計算していく書き方が自然なので、ブロッキング代入を使います。1つのalwaysブロックの中で両方を混ぜないことも大切です。

テストベンチで、回路への入力をクロックの立ち上がりと同じ時刻にブロッキング代入で変えると、問題が起きることがある。主な理由と対策の組み合わせとして正しいものはどれか。

  1. A回路が変わる前と後のどちらの値を取り込むかが実行の順番で変わる(競合)ため。入力はクロックのエッジから少しずらすか、ノンブロッキング代入で変える
  2. Bクロックの周波数が自動で下がるため。クロックを速くする
  3. Cブロッキング代入は、テストベンチでは使えないため。すべてassign文にする
答えと解説を見る

正解:A. 回路が変わる前と後のどちらの値を取り込むかが実行の順番で変わる(競合)ため。入力はクロックのエッジから少しずらすか、ノンブロッキング代入で変える

同じ時刻に、テストベンチが入力を書き換える処理と、回路のフリップフロップが入力を取り込む処理が起こると、シミュレータがどちらを先に実行するかで、取り込まれる値が古い値にも新しい値にもなります。これはシミュレーションの競合(レース)で、シミュレータによって結果が変わることもあります。入力を立ち下がりで変える、少し遅らせる、ノンブロッキング代入やSystemVerilogのクロッキングブロックを使う、などで防ぎます。

  1. 上の記述で、クロックの立ち上がりの直前にa=0、b=1だった。立ち上がりの後のaとbの値はどうなるか。
  2. 2段のシフトレジスタのつもりで、上のようにブロッキング代入(=)で書いた。起こる問題として正しいものはどれか。
  3. 組み合わせ回路のつもりで上のように書いた。起こりうる問題として正しいものはどれか。

ハードウェア記述言語と検証の練習問題へ

関連する用語の解説

ブロッキング代入
Verilogの「=」の代入。その場で左辺が更新され、次の行ではその新しい値が使われる。組み合わせ回路のalwaysやテストベンチで使う。
ノンブロッキング代入
Verilogの「<=」の代入。同じタイミングのすべての右辺を先に評価してからまとめて左辺を更新する。フリップフロップの動きと一致するので、順序回路に使う。
センシティビティリスト
Verilogのalwaysの後の@( )に書く、ブロックを実行するきっかけとなる信号の並び。組み合わせ回路で書き漏れるとシミュレーションと回路が食い違う。
Verilog
デジタル回路の設計と検証に広く使われるハードウェア記述言語。moduleを単位に回路を書き、assignやalwaysで動作を表す。

関連する比較