Menu

Verilog If-Else:手続きブロック内の条件ロジック

alwaysブロック内でif/elseがどう動くか、初心者を引っかけるラッチの罠、そしてチェーンしたelse ifが生成するpriority-encoderハードウェア。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

馴染みの構文、異なる発想

if/elseはCのように見えます:

if (condition) begin
    // ... 文 ...
end else begin
    // ... 文 ...
end

しかしVerilogはソフトウェアではないので、ルールが違います。心に留めておくべきこと2つ:

  1. if/elseは手続きブロック内にしか存在しません。moduleレベルで独立したifは書けません。
  2. if/elseが何に合成されるかは ブロックの型 に依存します。組み合わせブロックではmultiplexerまたはpriority encoderになります。クロック同期ブロックでは条件付き更新ロジック付きのflip-flopになります。

組み合わせブロック内

組み合わせalways @(*)ブロックは、abcが変わるたびに再実行します。if/elseチェーンが1つの分岐を選び、maxを代入し、ブロックが終わります。max常に 代入される(すべてのパスに代入がある)ので、合成ツールは純粋な組み合わせロジックを生成します。ラッチなしです。

ハードウェアにflip-flopがなくてもmaxregと宣言されていることに注意してください。同じルールが常に適用されます。always内で代入されるものはすべてregでなければなりません。

ラッチの罠

これが初心者の組み合わせコードで最も一般的なバグです:

// 間違い - ラッチを推論する
always @(*) begin
    if (enable)
        out = data;
    // elseなし! enableが低のとき、outはどうなる?
end

合成ツールは「enableが低のとき、outは代入されない」を読み、out前の値を覚える 必要があると判断します。値を覚えるにはストレージセルが必要なので、ツールはラッチを挿入します。同期設計でのラッチはタイミング問題を引き起こし、resetが困難で、ほぼ意図したものではありません。

修正方法は2つ:

両方とも同じ組み合わせハードウェア、2-to-1 muxを生成します。「先頭でデフォルト」パターンは、同じsignalへの条件付き代入が多いときによりスケールします。

クロック同期ブロック内

組み合わせ版との違いに注目してください:

  • ブロックがalways @(posedge clk) - flip-flopの領域。
  • 代入が<=(非blocking)を使う。
  • 「resetでもenableでもない」ケース用のelseがない。 それで構いません。クロック同期ブロックでは、どの分岐も発火しないとき、flip-flopは前の値を保持します。これがflip-flopが物理的に行うことそのものです。signalがすでに registerである ので、ラッチは推論されません。

これがelseを省略しても安全な唯一の場所です。クロック同期ブロックの外では、常にすべてのパスを処理してください。

チェーンしたelse if:priority encoder

else ifのチェーンは暗黙の優先順位を持ち、早い条件が後の条件を上回ります:

requests[0]が最高優先度です。セットされていれば、上位ビットが何をしてもgrant0になります。合成ツールはチェーンをカスケードmuxに変換します。最初にビット0をチェック、次にビット1、次にビット2、次にビット3。各レベルが少しずつ遅延を加えます。

条件が 相互に排他的 なら(例えば、one-hot入力のデコード)、case文(次のドキュメント)の方がelse ifチェーンよりフラットで速いハードウェアを生成します。真の優先順位要件がないときはcase形式を使ってください。

クロック同期コードでのelseなしif

クロック同期ブロックは「前の値を保持」がデフォルトなのでelseが必要ありません。これで イネーブル を構築します:

always @(posedge clk) begin
    if (load) target <= incoming;
    // elseなし: loadが低のとき、targetは値を保持
end

これがロード可能registerです。ほとんどのパイプラインレジスタ、カウンタ、設定registerがこのパターンを使います。

begin/endと単一文

Cと同様に、単一文ではbegin/endを省略できます:

if (a) out = 1;
else   out = 0;

1文を超えるなら、ブロックを使ってください:

if (a) begin
    out = 1;
    flag = 1;
end else begin
    out = 0;
    flag = 0;
end

2つのパターンは自由に混在できます。スタイルガイドは、2つ目の文を追加するのを苦痛なくするため、常にbegin/endを使うことを一般に推奨します。

次に読むもの

次のドキュメントCase文は、多分岐デコード(状態機械、opcode dispatch、ROMテーブル)の正しいツールであるcaseを扱います。その後、For Loopsは、elaboration時に展開されるためソフトウェアの兄弟分とは微妙に異なります。

よくある質問

Verilogのif文はどう動きますか?

if (cond) statement;condが非0のときstatementを実行します。begin ... endで複数の文を包めます。代替分岐にはelse statement;を追加するか、else if (other_cond) ...でチェーンできます。if/elseは手続きブロック(initialまたはalways)内にしか存在しません。moduleのトップレベルではありません。

Verilogの推論されるラッチとは?

あなたが頼んでいないのに合成ツールが作ったラッチです。組み合わせalwaysブロックがすべてのパスでsignalを代入していないからです。ツールは「aならout = 1、elseなし」を見て、未代入のケースは前の値を覚えなければならないと判断し、ラッチを生成します。ラッチはほぼ常に間違いです。修正は、すべてのsignalにブロック先頭でデフォルト値を与えるか、明示的なelseを与えることです。

Verilogで推論されるラッチを避けるには?

組み合わせalways @(*)ブロックで、すべての出力regがすべてのコードパスで代入されるようにしてください。最もきれいなパターンは、ブロックの先頭でデフォルトを設定し、条件的にオーバーライドすることです。コンパイラは通常ラッチを推論したときに警告します。警告はエラーとして扱ってください。

Verilogでif-elseチェーンから何が合成されますか?

priority encoderです。最初のifが最高優先度、次のelse ifは最初が偽のときだけチェックされ、というふうに続きます。ハードウェアではこれが優先順序が焼き付けられたmuxのチェーンになります。条件が相互に排他的なら、同じロジックを持つcase文がよりフラットなハードウェアに合成され、より明確に読めます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める