優先エンコーダで if/case を理解する

このページで学ぶ内容

  • 概要:プライオリティエンコーダを例にして、優先順位つきの回路記述を学ぶ
  • 目標:プライオリティエンコーダとは何か/if文を使った記述/casex文を使った記述
  • 修了判定:各種エンコーダの記述を論理合成し、結果を比較する

まず実験:要求が重なったとき、3つの書き方は割れる

このページでは、同じエンコーダを if 文と casex 文で書き分けて比べます。先に、要求が複数同時に立ったときに何が起きるかを動かして確かめておきます。

3通りの書き方と自動判定テストベンチをダウンロード(ZIP)

並べているもの

4ビットの要求を2ビットの番号に変える
出力書き方意味
y_ifif / else if の連鎖先に書いた条件が勝つ(優先順位あり)
y_caseone-hotパターンの caseちょうど1本だけ立っているときに答える
y_casexdon't careパターンの casexif連鎖と同じ優先順位
回路本体 priority_styles.v を見る
`timescale 1ns/1ps
`default_nettype none
// Three ways to turn a 4-bit request into a 2-bit number.
// They agree while only one bit is set, and split apart when several are.
module priority_styles (
    input  wire [3:0] din,
    output reg  [1:0] y_if,
    output reg        v_if,
    output reg  [1:0] y_case,
    output reg        v_case,
    output reg  [1:0] y_casex,
    output reg        v_casex
);
    // if / else if chain: the first condition that holds wins.
    always @* begin
        v_if = 1'b1;
        if      (din[3]) y_if = 2'd3;
        else if (din[2]) y_if = 2'd2;
        else if (din[1]) y_if = 2'd1;
        else if (din[0]) y_if = 2'd0;
        else begin
            y_if = 2'd0;
            v_if = 1'b0;
        end
    end

    // case with one-hot patterns: an exact match or nothing.
    always @* begin
        v_case = 1'b1;
        case (din)
            4'b1000: y_case = 2'd3;
            4'b0100: y_case = 2'd2;
            4'b0010: y_case = 2'd1;
            4'b0001: y_case = 2'd0;
            default: begin
                y_case = 2'd0;
                v_case = 1'b0;
            end
        endcase
    end

    // casex with don't-care patterns: the same priority as the if chain.
    always @* begin
        v_casex = 1'b1;
        casex (din)
            4'b1xxx: y_casex = 2'd3;
            4'b01xx: y_casex = 2'd2;
            4'b001x: y_casex = 2'd1;
            4'b0001: y_casex = 2'd0;
            default: begin
                y_casex = 2'd0;
                v_casex = 1'b0;
            end
        endcase
    end
endmodule
`default_nettype wire
テストベンチ tb_priority_styles.v を見る
`timescale 1ns/1ps
`default_nettype none
// All 16 request patterns. Counts where the three styles agree and differ.
module tb_priority_styles;
    reg [3:0] din;
    wire [1:0] y_if, y_case, y_casex;
    wire v_if, v_case, v_casex;
    reg [1:0] want;
    integer checks = 0;
    integer differ = 0;
    integer i;

    priority_styles dut(.din(din),
                        .y_if(y_if), .v_if(v_if),
                        .y_case(y_case), .v_case(v_case),
                        .y_casex(y_casex), .v_casex(v_casex));

    task check;
        input [2:0] actual, expected;
        input [8*12-1:0] label;
        begin
            checks = checks + 1;
            if (actual !== expected)
                $fatal(1, "FAIL: check=%0d %0s din=%b actual=%b expected=%b",
                       checks, label, din, actual, expected);
        end
    endtask

    initial begin
        $dumpfile("priority_styles.vcd");
        $dumpvars(0, tb_priority_styles);
        $display("  din  | if    case   casex");
        for (i = 0; i < 16; i = i + 1) begin
            din = i[3:0];
            #1;
            // The specification for a priority encoder: highest set bit wins.
            want = din[3] ? 2'd3 : din[2] ? 2'd2 : din[1] ? 2'd1 : 2'd0;
            check({v_if, y_if}, {|din, want}, "if chain");
            check({v_casex, y_casex}, {|din, want}, "casex");
            // The one-hot case only answers for exactly one request.
            if (din == 4'b1000 || din == 4'b0100 || din == 4'b0010 || din == 4'b0001)
                check({v_case, y_case}, {1'b1, want}, "case onehot");
            else
                check({v_case, y_case}, 3'b000, "case default");
            if ({v_if, y_if} !== {v_case, y_case}) differ = differ + 1;
            if (i == 4'b1010 || i == 4'b0110 || i == 4'b1111)
                $display("  %b | %b %b   %b %b   %b %b", din,
                         v_if, y_if, v_case, y_case, v_casex, y_casex);
            #1;
        end
        $display("  if chain and one-hot case differ on %0d of 16 inputs", differ);
        $display("PASS: priority styles (%0d checks)", checks);
        $finish;
    end

    initial begin
        #2000;
        $fatal(1, "FAIL: testbench timeout");
    end
endmodule
`default_nettype wire

実行方法

iverilog -g2012 -Wall -s tb_priority_styles -o sim.vvp priority_styles.v tb_priority_styles.v
vvp sim.vvp

全16通りを与えます。Windows / Icarus Verilog 13.0で2026年10月28日に実行した結果の抜粋です(v y の順で表示)。

  din  | if    case   casex
  0110 | 1 10   0 00   1 10
  1010 | 1 11   0 00   1 11
  1111 | 1 11   0 00   1 11
  if chain and one-hot case differ on 11 of 16 inputs
PASS: priority styles (48 checks)

読み取れること

要求が1本だけのときは、3つとも同じ答えを返します。複数が同時に立つと、if 連鎖と casex は上位を選び、one-hotの case はどれにも一致せず default に落ちました。16通りのうち11通りで、両者は違う答えになります。

どれが正しいかは仕様しだいだよ。「同時に立ったら上位を優先する」のか、「同時には立たないことを上流で保証する」のか、「同時に立ったらエラーにする」のか。先に決めて、それをコードで表そう。決めずに書くと、記述の順序でたまたま決まった動作になってしまうんだ。

要求を1本ずつしか試さないと、見つからない

if 連鎖の順序を逆(下位優先)にした回路に、要求を1本ずつしか立てないテストベンチを実行すると、こうなります。

PASS: partial check (4 one-hot requests only)

合格表示です。1本ずつなら優先順位は関係ないので、順序を逆にしても同じ答えになるんだ。複数同時の入力を入れて初めて、優先順位が合っているかを確かめられます。※この簡易テストベンチは、サイト側の検証スクリプトで実行したもので、配布ZIPには入っていません。

わざと壊して、テストの働きも確かめる

2026年10月28日 Windows / Icarus Verilog 13.0での実行結果
壊した場所結果
if 連鎖の順序を逆にするcheck=10 if chain din=0011 actual=100 expected=101
casex のパターンを間違えるcheck=5 casex din=0001 actual=110 expected=100

確認問題

要求が同時に立たないことが仕様で保証されているなら、if 連鎖と one-hot の case はどちらで書いても同じでしょうか。

答え

その入力範囲では同じ答えになります。上の実験でも、1本だけ立っている4通りでは3つとも一致しました。違うのは「保証が破れたときにどうなるか」です。if 連鎖は上位を選び、one-hotの case は default に落ちます。保証が本当に成り立つのかを確かめること、破れたときの動きを決めておくことの両方が要ります。casex を使うときは、入力に不定値が含まれる場合の振る舞いにも注意してね(O6の実習)。

この後の節では、プライオリティエンコーダの考え方と、if文・casex文それぞれの記述を順に見ていきます。

(2)プライオリティエンコーダとは

エンコード回路にも優先順位があります。これをプライオリティエンコーダといいます。

プライオリティエンコーダを簡単な例で説明します。A・B・C の 3 つのキーを入力し、2 ビットの信号を出力するエンコーダを考えます。

O1 図1:プライオリティエンコーダのブロック図。A、B、CがENCODERに入り、2ビットのENC_OUT 2bitが出る。
O1 図1:プライオリティエンコーダのブロック図。横に長い図はスクロールして確認できます。
基本エンコーダの端子構成

入力:A, B, C(3本) → 出力:ENC_OUT(2bit)

キーが押されていない状態(A=B=C=0)では出力は 00。A が押されると 01、B が押されると 10、C が押されると 11 になります。

もふねこ

もふねこ:
「優先順位」ってなんだか難しそうに聞こえるけど、要は「同時にボタンが押されたとき、どっちを優先するか」を決めるだけだよ🐾!
if文と casex文、最終的な回路は同じになるけど、シミュレーションでの「不定値(x)」の扱いが違うから、トラブルシュートの時は気をつけてね!

ABCENC_OUT
00000
10001
01010
00111

ところがこの真理値表では、複数の入力が同時に 1 になった場合に出力が決まりません。そこでキーに優先順位をつけて真理値表を作り直します。

ここでは A の優先順位が最も高く、LSB 側に行くほど優先順位が低くなるように設計します。

  • A が押されているとき、B と C の値は出力に影響しない
  • B と C が同時に押されても、出力は 10(B が優先)

このように入力に優先順位のあるエンコーダをプライオリティエンコーダと呼びます。

出力に影響を与えない入力部分を don't care(x) で表すと、優先順位を真理値表でシンプルに表現できます。

ABCENC_OUT備考
00000入力なし
1xx01A が最優先
01x10B が次に優先
00111C は最低優先
x: don't care(1 でも 0 でもかまわない)

don't care の入力は出力に影響しないため、どちらの値でも同じ出力が得られます。

(3)if 文によるエンコーダ記述

入力が 8 本、出力が 3 本のプライオリティエンコーダを考えます。MSB 側(DIN[7])の優先順位が最も高く、LSB 側に行くほど低くなります。

DIN[7:0]DOUT[2:0]優先順位
1xxxxxxx7高(最優先)
01xxxxxx6↑
001xxxxx5|
0001xxxx4|
00001xxx3|
000001xx2|
0000001x1↓
000000010低(最低)
if 文の優先順位ルール

if・else if の構造は先に書いた条件が優先されます。優先順位の高い条件から順に記述します。

// if文によるプライオリティエンコーダ
module ENCODER_IF( DIN, DOUT );
    input  [7:0] DIN;
    output [2:0] DOUT;
    reg    [2:0] DOUT;

    always @( DIN ) begin
        if      ( DIN[7]==1'b1 )  DOUT <= 3'h7;
        else if ( DIN[6]==1'b1 )  DOUT <= 3'h6;
        else if ( DIN[5]==1'b1 )  DOUT <= 3'h5;
        else if ( DIN[4]==1'b1 )  DOUT <= 3'h4;
        else if ( DIN[3]==1'b1 )  DOUT <= 3'h3;
        else if ( DIN[2]==1'b1 )  DOUT <= 3'h2;
        else if ( DIN[1]==1'b1 )  DOUT <= 3'h1;
        else                      DOUT <= 3'h0;
    end
endmodule

(4)casex 文によるエンコーダ記述

同じ真理値表を、今度は casex 文を使って表現します。

casex 文の特徴

casex 文は不定値 x やハイインピーダンス z を don't care として扱います。真理値表の x をそのまま記述でき、非常にわかりやすい記述になります。

// casex文によるプライオリティエンコーダ
module encoder_casex( DIN, DOUT );
    input  [7:0] DIN;
    output [2:0] DOUT;
    reg    [2:0] DOUT;

    always @( DIN ) begin
        casex( DIN )
            8'b1xxxxxxx:  DOUT <= 3'h7;
            8'b01xxxxxx:  DOUT <= 3'h6;
            8'b001xxxxx:  DOUT <= 3'h5;
            8'b0001xxxx:  DOUT <= 3'h4;
            8'b00001xxx:  DOUT <= 3'h3;
            8'b000001xx:  DOUT <= 3'h2;
            8'b0000001x:  DOUT <= 3'h1;
            8'b0000000x:  DOUT <= 3'h0;  // 末尾が x なので DIN=0 のときも 0(if文版の else と同じ動き)
            default:      DOUT <= 3'hx;
        endcase
    end
endmodule

(5)優先順位のないエンコーダ

今度は優先順位のないエンコーダを記述します。このエンコーダは「入力 DIN のいずれか 1 ビットだけが常に 1」という前提のエンコーダです。複数の信号が同時に 1 になることはないものとして考えます。

DIN[7:0]DOUT[2:0]
100000007
010000006
001000005
000100004
000010003
000001002
000000101
000000010
// 優先順位のないエンコーダ
module enc_nonprior( DIN, DOUT );
    input  [7:0] DIN;
    output [2:0] DOUT;
    reg    [2:0] DOUT;

    always @( DIN ) begin
        case( DIN )
            8'b1000_0000:  DOUT <= 3'h7;
            8'b0100_0000:  DOUT <= 3'h6;
            8'b0010_0000:  DOUT <= 3'h5;
            8'b0001_0000:  DOUT <= 3'h4;
            8'b0000_1000:  DOUT <= 3'h3;
            8'b0000_0100:  DOUT <= 3'h2;
            8'b0000_0010:  DOUT <= 3'h1;
            8'b0000_0001:  DOUT <= 3'h0;
            default:       DOUT <= 3'hx;
        endcase
    end
endmodule
⚠️ default の記述を忘れずに!
  • default を省略するとラッチが生成されてしまいます
  • default の出力値は 3'hx(不定値)にしてください
  • 固定値(例: 3'h0)を書くと、その入力でも動作が決まる代わりに、その値を出力する回路が必要になります。面積にどれだけ効くかは同じ条件で合成してレポートを比べないと分かりません
優先順位回路を式で書くと

上の真理値表(Aが最優先、次にB、最後にC)から、2つの出力はこうなります。

assign ENC_OUT_1 = ~A & (B | C);
assign ENC_OUT_0 = A | (~A & ~B & C);

Aが押されていれば ENC_OUT_1 は必ず0で、出力は 01。AがなくBがあれば 10、AもBも無くCだけなら 11 です。式の中に ~A が入ることが、そのまま「Aが優先」という意味になります。

※以前ここに置いていたゲート図は、配線をたどると ENC_OUT_1 = A | B となり、上の真理値表と合っていませんでした。下の図2がその差し替えだよ。式が真理値表と一致することは、入力8通りをすべて Icarus Verilog 13.0 でためして確認してあるよ(2026-10-29 実施)。

O1 図2:優先順位回路のゲート構成。NOT A と OR(B,C) を AND でまとめた出力が ENC_OUT_1、A と「NOT A・NOT B・C の3入力 AND」を OR でまとめた出力が ENC_OUT_0。
O1 図2:優先順位回路のゲート構成。A が1なら ~A が0になるので、ENC_OUT_1 は必ず0になります。

(6)ワンポイント・アドバイス:if 文と casex 文の動作の違い

本文中の 2 つのプライオリティエンコーダ(if 文版・casex 文版)は、0/1 の入力に対して同じ優先動作を表します。下に載せたレポート例でも規模・遅延は同じ値になっています(対象のツールとライブラリで同じ回路になるかは、実際に合成して確かめてください)。しかし、シミュレーションの結果は必ずしも同じではありません。

if 文(ENCODER_IF)

入力のすべてのビットが不定値になると → 0 が出力

(else 節が実行されるため)

casex 文(encoder_casex)

入力のすべてのビットが不定値になると → 7 が出力

(casex は x をdon't care として扱い、最初のパターン 8'b1xxxxxxx にマッチするため)

まとめ

if 文と casex 文による記述は基本的な動作は同じでも、不定値入力時の細かい挙動が異なります。シミュレーションの正確性が重要な場面では、この違いを意識して記述方法を選んでください。

(7)修了判定

設問

本文の各記述を論理合成する。論理合成後のレポートで結果を比較する。

  • if 文を使ったプライオリティエンコーダ:encoder_if.v
  • casex 文を使ったプライオリティエンコーダ:encoder_casex.v
  • 優先順位の無いエンコーダ:enc_nonprior.v

論理合成結果

モジュール回路規模(ゲート数)遅延時間(ns)
encoder_if.v(ENCODER_IF)172.51
encoder_casex.v(ENCODER_CASEX)172.51
enc_nonprior.v(ENC_NONPRIOR)91.35

ENCODER_IF 論理合成レポート

************************************
レポート : area
回路    : ENCODER_IF
************************************
ポート数:           11
ネット数:           19
セル数:             12
セル種類:            7
組み合わせ回路:     17
非組み合わせ回路:    0
合計:               17

************************************
レポート : timing
回路    : ENCODER_IF
************************************
ライブラリ:         hd350s
配線遅延モデル:     hd350s_05k
コンディション:     MAX567
--------------------------------------
DIN[2] (in)      0.00    0.00
DOUT[0] (out)    0.00    2.51
データ到着時刻          2.51

ENCODER_CASEX 論理合成レポート

************************************
レポート : area
回路    : ENCODER_CASEX
************************************
ポート数:           11
ネット数:           19
セル数:             12
セル種類:            7
組み合わせ回路:     17
非組み合わせ回路:    0
合計:               17

************************************
レポート : timing
回路    : ENCODER_CASEX
************************************
ライブラリ:         hd350s
配線遅延モデル:     hd350s_05k
コンディション:     MAX567
--------------------------------------
DIN[2] (in)      0.00    0.00
DOUT[0] (out)    0.00    2.51
データ到着時刻          2.51

ENC_NONPRIOR 論理合成レポート

************************************
レポート : area
回路    : ENC_NONPRIOR
************************************
ポート数:           11
ネット数:           10
セル数:              3
セル種類:            1
組み合わせ回路:      9
非組み合わせ回路:    0
合計:                9

************************************
レポート : timing
回路    : ENC_NONPRIOR
************************************
ライブラリ:         hd350s
配線遅延モデル:     hd350s_05k
コンディション:     MAX567
--------------------------------------
DIN[5] (in)      0.00    0.00
DOUT[2] (out)    0.00    1.35
データ到着時刻          1.35

O1 まとめ

  • プライオリティエンコーダ:複数の入力が 1 になったとき、優先順位の高い入力のみを有効にするエンコーダ
  • if 文:優先順位の高い条件を先に書く → if/else if の構造が優先順位を表現
  • casex 文:don't care(x/z)を使った真理値表をそのまま記述できる → 可読性が高い
  • このページの if 版と casex 版は、0/1 の入力に対して同じ優先動作を表す。下のレポート例では規模・遅延も同じだけれど、自分の環境で同じ回路になるかは実際に合成して確かめる
  • 下のレポート例では、優先順位なしエンコーダ(case 文)のほうが規模・遅延の値が小さい。ただし許容する入力と仕様がそもそも違うので、置き換えてよいかは仕様から判断する
  • case 文は全経路で出力が決まるように書くこと。その入力で出力が任意でよい仕様なら 3'hx(don't care)を書くと最適化の余地が生まれ、決まった動作が要る仕様なら既知の値を書く(S3)
  • 不定値入力時のシミュレーション挙動は if 文と casex 文で異なるため注意が必要
  • ※この章に出てくる規模17・遅延2.51ns などの数値は、この教材にもとから載っているレポート例の値だよ。ライブラリ名(hd350s)やコンディションは載っているけれど、ツールの版や設定一式までは分からないので、こちらでは再現していない。元の出典も確認できていないから、比べ方の例として読んでね

(8)追加演習

⚠️ 修了判定:8-to-3 プライオリティエンコーダ

設問:8本の入力信号 DIN[7:0] を受け取り、3ビットの出力 DOUT[2:0] にエンコードする回路を記述せよ。MSB(DIN[7])が最も優先度が高いものとする。if文 または casex文のいずれかを用いて記述すること。

解答例:casex文を用いた記述

module PRI_ENC8 ( DIN, DOUT );
input  [7:0] DIN;
output [2:0] DOUT;
reg    [2:0] DOUT;

always @( DIN ) begin
    casex ( DIN )
        8'b1xxx_xxxx : DOUT <= 3'd7;
        8'b01xx_xxxx : DOUT <= 3'd6;
        8'b001x_xxxx : DOUT <= 3'd5;
        8'b0001_xxxx : DOUT <= 3'd4;
        8'b0000_1xxx : DOUT <= 3'd3;
        8'b0000_01xx : DOUT <= 3'd2;
        8'b0000_001x : DOUT <= 3'd1;
        8'b0000_0001 : DOUT <= 3'd0;
        default      : DOUT <= 3'dX;
    endcase
end
endmodule
⚠️ もふねこの現場メモ:なぜ実務では casex ではなく casez が推奨されるのか?

プライオリティエンコーダの記述に `casex` 文を使う例が教科書によく載っていますが、実際の製品設計(実務)では `casez` 文を使うのが鉄則だよ。

  • casex: 不定値 `x` とハイインピーダンス `z` の両方を don't care(どちらでも良い)として扱います。
  • casez: ハイインピーダンス `z` のみを don't care として扱い、不定値 `x` は don't care にしません。

もし `casex` を使うと、回路内の別の場所でバグが発生して入力信号が不定値 `x` になったとき、`casex` がそれを don't care としてマッチさせてしまい、シミュレーションが「正常」に動いているように見えてバグ(不定値の伝搬)を見逃してしまう原因になるんだよ。だから、実務では `z` だけを don't care にする `casez` を使い、`?` 記法(例: `8'b1???_????`)で記述するのが標準的だよ!