多分岐
caseはVerilogのフラットなディスパッチ構造です。式を与え、一致する分岐を選びます:
case (expression)
pattern_1: statement_1;
pattern_2: statement_2;
pattern_3: begin
statement_3a;
statement_3b;
end
default: default_statement;
endcase
構造的にはCのswitchに似ていますが:
breakはありません。各分岐は次の分岐で暗黙に終了します。- パターンはintegerだけでなくvectorも可能。
- 合成ツールは全体をフラットなmux(パターンが許せばone-hotデコーダ)に変換します。
動作する例:4-to-1 mux
case本体は4つの明示パターンに加えてdefaultを持ちます。合成ツールは2ビット入力を4つの値の1つにマッピングして、4-to-1 multiplexerを生成します。きれいで、フラットで、速い。
なぜdefaultが必要か
組み合わせcaseでは、defaultを省略するのはifにelseがないのと同じ罠です。一致しない入力値でoutが未代入のままになり、合成ツールがラッチを推論します。
2ビットselでは、上のパターンが4つの可能な値をすべてカバーするので、理論上はdefaultは冗長です。実際には:
- 合成ツールが常にcaseが網羅的であることを証明するわけではない。
- シミュレーションでセレクタが
xやzになる可能性があり、明示caseの どれにも 一致しません。 - 後で新しいcaseを追加すると、default動作が未指定のままになるかもしれません。
常にdefaultを書いてください。defaultが到達不能と分かっている状態機械やmuxロジックでは、'xを代入:
default: out = 8'bx;
…と書くと、合成ツールに「これはdon't-care、自由に最適化して」と伝え、到達不能ケースがどうにか到達されたら、シミュレーションで真っ赤なxが出ます。両方の世界の良いとこ取りです。
caseの状態機械
caseの古典的な使い方は、有限状態機械の状態遷移ロジックです:
case (state)ブロックが状態遷移ロジックです。各分岐が次のstateを決定し、そこに留まる時間を決めます。defaultはここでは到達不能(RED/GREEN/YELLOWを2ビット空間で網羅)ですが、安全ネットとしてあります。stateがどうにか2'd3になっても、FSMはラッチではなくREDにきれいにresetします。
有限状態機械でこのパターンを深掘りします。
分岐ごとに複数パターン
複数のパターンが1つの文を共有することを、カンマ区切りでリストできます:
case (opcode)
4'h0, 4'h1, 4'h2: result = a + b;
4'h3, 4'h4: result = a - b;
4'h8: result = a & b;
default: result = 8'd0;
endcase
これは2つのopcodeが「減算」、3つが「加算」を意味します。合成ツールはコンパレータのためにパターンをORします。
casezとcasex:don't-careマッチング
時にはいくつかのビットを指定せずにパターンに一致させたいことがあります - 「010で始まる任意のopcode」:
casez (opcode)
8'b010?_????: instruction = ALU_OP;
8'b110?_????: instruction = LOAD_OP;
8'b1110_????: instruction = JUMP_OP;
default: instruction = UNKNOWN;
endcasez
casezはパターン内の?(とz)をdon't-careとして扱います。各?は0または1に一致します。一部のopcodeクラスで一部のビット位置が未使用の命令フォーマットをデコードするのに有用です。
casexはこれをxもdon't-careとして扱うように拡張します。casexは危険 です。未初期化signal(シミュレーションでxになる)が すべての caseに一致してしまい、驚くべき振る舞いを生成するからです。ほとんどのモダンなスタイルガイドはcasezを推奨し、casexを禁じます。
SystemVerilogではcase insideもあり、すべての中で最もきれいなバージョンです(範囲とリストを受け入れる)。プレーンVerilogはcasezで止まります。
case vs if/else ifチェーン
両方の構造は多分岐決定を表現できますが、異なるハードウェアに合成されます:
caseはフラットなディスパッチ。合成ツールはone-hotデコーダ、バランスされたmux tree、その他のフラット構造を生成できます。評価は定数時間。if/else ifは優先順位チェーン。合成ツールは各レベルが遅延を加えるカスケードを生成します。対数的に遅くなります。
機能的には重なります。スタイル的には:単一の式の値についての条件ならcaseを使い、真の優先順位があるか条件が異なるsignalを含むならif/else ifを使ってください。
// caseの方が良い:
if (sel == 2'd0) out = a;
else if (sel == 2'd1) out = b;
else if (sel == 2'd2) out = c;
else out = d;
// if/else ifの方が良い:
if (urgent_event) next_state = HANDLE_URGENT;
else if (timer_expired) next_state = TIMEOUT;
else if (data_ready) next_state = PROCESS;
else next_state = state;
最初の3つの条件はすべて「selは何か?」 - caseの方が自然に読めて、よりフラットに合成されます。後の3つは明らかな優先順位を持つ独立イベントで、if/else ifの方が適合します。
次に読むもの
この章の最後のドキュメントFor Loopsは、Verilogのforと、合成可能コードで使うときに起こる驚くべきことを扱います。その後、本格的に順序ロジックとFSMに移ります。
よくある質問
Verilogのcase文とは?
case (expr) ... endcaseはVerilogの多分岐構造です。式を1回評価し、一致した分岐にディスパッチします。状態機械、opcodeデコーダ、muxセレクタ、いくつかの互いに排他的な選択肢の中から選ぶあらゆるものの慣用句的な選択です。
case、casex、casezの違いは?
caseはxとz値を含めてビット精密に一致します。casezはcase項のz(と?)をdon't-careとして扱います。casexはxとzの両方をdon't-careとして扱います。Don't-careマッチングは、一部のビット位置が無関係なopcodeパターンに有用ですが、casexはシミュレーションで危険です。未初期化signal(x)が偶然すべてのcaseに一致してしまうからです。
Verilogのcase文でdefaultが必要な理由は?
defaultなしだと、合成ツールはどのcaseも一致しない可能性を見て、出力signalが前の値を保持しなければならないと判断し、意図しないラッチを推論します。default分岐は一致しないすべての値を処理します。通常は出力を安全値にセットするか、x代入でcaseが到達不能であることを示します。常に入れてください。
Verilogでcaseとif-elseはどう使い分け?
条件が相互に排他的で、単一の式の値でディスパッチするとき(状態機械、opcodeデコーダ、muxセレクタ)にはcaseを使ってください。真の優先順序があるか、条件が異なるsignalを含むときはif/elseを使ってください。caseは長いelse ifチェーンよりフラットで速いハードウェアに合成されます。