Verilogで組み合わせ回路を記述する:条件演算子とAlwaysブロック
Verilogで組み合わせロジックをモデル化する方法を解説。真理値表実装のための条件演算子と、簡潔な設計を可能にする強力な「always」ブロックをカバーします。
このガイドでは、Verilogで組み合わせ回路を記述する主要技術を歩いていきます。まずassignキーワードを用いた継続的代入構文を再訪し、次に条件演算子(?:)を多重化器ロジックを簡潔に表現する手段として導入します。最後に、alwaysブロックがさらに明確な手続き的記述を提供することを示します。
以前、継続的代入のためのassignキーワードを扱いました。これらのステートメントは常にアクティブで、回路のゲートレベル記述を得られます。例えば、次のコードはANDゲートをモデル化します:
assign out1 = a & b;
Verilogの条件演算子を使うと、単一の代入で条件に基づいて値を変更できます。構文は:
assign [signal_name] = [conditional_expression] ? [value_if_true] : [value_if_false];
conditional_expressionが最初に評価されます。真ならvalue_if_trueがsignal_nameに代入され、偽ならvalue_if_falseが使用されます。例:
assign out1 = (sel) ? (a & b) : (a | b);
selが真ならaとbのANDがout1を駆動し、偽ならaとbのORが選択されます。この1行で2対1多重化器を実装します。
条件演算子は従来のプログラミングにおけるif文のように振る舞うため、組み合わせロジックをより抽象的に表現できます。この演算子をネストすることで、例1のように複雑な構造を構築できます。
例1:ネストした条件演算子
以下はネストした条件演算子で実装された4対2優先エンコーダです。真理値表は図1に示します。

Verilogコードは:
module Prio_4_to_2 ( input wire [3:0] x, output wire [1:0] y, output wire v ); assign y = x[3] ? 2'b11 : x[2] ? 2'b10 : x[1] ? 2'b01 : 2'b00; assign v = ( x[3] | x[2] | x[1] | x[0] ) ? 1'b1 : 1'b0; endmodule
7〜10行目がネストした条件演算子を示します。各条件が順次評価され、早い項に高い優先度が与えられます。これは優先ロジックに最適です。vの最終assignはビット単位ORを用いていずれかの入力がハイかどうかを示します。
Xilinx ISEによるシミュレーション結果が動作を確認しています(図2)。

図2. 優先エンコーダのXilinx ISEシミュレーション。
条件式は真の条件が見つかるまで順番に評価され、対応する値が代入されることに注意が必要です。したがって、この構造はバランスの取れた多重化器(図4)ではなく優先ネットワーク(図3)に最適です。

図3. 優先ネットワーク。

図4. 入力優先度のないn対1多重化器。
以前の記事でVHDLの並行代入について議論しましたが、概念はVerilogでも同様に適用されます。
Verilog手続き文
assignと条件演算子で多くの設計に対応できますが、alwaysブロックはより表現力豊かな手続き的アプローチを提供します。alwaysブロック内ではステートメントが順次実行され、ifやcaseなどの高水準構造を含めることができ、人間の推論の自然な流れを反映します。
例2は基本的なalwaysブロックを用いてシンプルなANDゲートを実装するものです。
例2:「Always」ブロック文
alwaysブロックの簡略構文は:
always @(sensitivity_list) begin sequential_statements; end
sensitivity_listはブロックが再評価されるタイミングを指定します。純粋な組み合わせ回路の場合、すべての入力信号をこのリストに含める必要があります。
図5の回路を考えます。出力はaまたはbがトグルするたびに変化します。

図5. Circuit_1
以下のalwaysブロックはブロッキング代入を用いてAND演算を実装します:
always @(a, b) begin out1 = a & b; end
ブロッキング代入(=)は左辺を即座に更新しますが、ノンブロッキング代入(<=)はブロック終了まで更新を遅延させます。組み合わせロジックでは、意図しないレースコンディションを避けるためブロッキング代入を使用するのが最も安全です。
手続きブロックはwireを駆動できないため、出力はregで宣言する必要があります。これはストレージ要素を推論するかもしれませんし、しないかもしれません。完全なモジュールは以下の通り:
module Circuit_1 ( input wire a, input wire b, output reg out1 ); always @(a, b) begin out1 = a & b; end endmodule
まとめると、この記事では真理値表ロジックの簡潔な表現のための条件演算子、優先エンコーディングのためのネスト演算子、組み合わせ記述をより明確にする手続き的alwaysブロックを扱いました。今後の投稿ではalwaysを順序ロジックに使用する方法を探ります。
埋め込み