優先エンコーダで if/case を理解する
このページで学ぶ内容
- 概要:プライオリティエンコーダを例にして、優先順位つきの回路記述を学ぶ
- 目標:プライオリティエンコーダとは何か/if文を使った記述/casex文を使った記述
- 修了判定:各種エンコーダの記述を論理合成し、結果を比較する
まず実験:要求が重なったとき、3つの書き方は割れる
このページでは、同じエンコーダを if 文と casex 文で書き分けて比べます。先に、要求が複数同時に立ったときに何が起きるかを動かして確かめておきます。
3通りの書き方と自動判定テストベンチをダウンロード(ZIP)
並べているもの
| 出力 | 書き方 | 意味 |
|---|---|---|
y_if | if / else if の連鎖 | 先に書いた条件が勝つ(優先順位あり) |
y_case | one-hotパターンの case | ちょうど1本だけ立っているときに答える |
y_casex | don't careパターンの casex | if連鎖と同じ優先順位 |
回路本体 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には入っていません。
わざと壊して、テストの働きも確かめる
| 壊した場所 | 結果 |
|---|---|
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 ビットの信号を出力するエンコーダを考えます。
入力:A, B, C(3本) → 出力:ENC_OUT(2bit)
キーが押されていない状態(A=B=C=0)では出力は 00。A が押されると 01、B が押されると 10、C が押されると 11 になります。
もふねこ:
「優先順位」ってなんだか難しそうに聞こえるけど、要は「同時にボタンが押されたとき、どっちを優先するか」を決めるだけだよ🐾!
if文と casex文、最終的な回路は同じになるけど、シミュレーションでの「不定値(x)」の扱いが違うから、トラブルシュートの時は気をつけてね!
| A | B | C | ENC_OUT |
|---|---|---|---|
| 0 | 0 | 0 | 00 |
| 1 | 0 | 0 | 01 |
| 0 | 1 | 0 | 10 |
| 0 | 0 | 1 | 11 |
ところがこの真理値表では、複数の入力が同時に 1 になった場合に出力が決まりません。そこでキーに優先順位をつけて真理値表を作り直します。
ここでは A の優先順位が最も高く、LSB 側に行くほど優先順位が低くなるように設計します。
- A が押されているとき、B と C の値は出力に影響しない
- B と C が同時に押されても、出力は
10(B が優先)
このように入力に優先順位のあるエンコーダをプライオリティエンコーダと呼びます。
出力に影響を与えない入力部分を don't care(x) で表すと、優先順位を真理値表でシンプルに表現できます。
| A | B | C | ENC_OUT | 備考 |
|---|---|---|---|---|
| 0 | 0 | 0 | 00 | 入力なし |
| 1 | x | x | 01 | A が最優先 |
| 0 | 1 | x | 10 | B が次に優先 |
| 0 | 0 | 1 | 11 | C は最低優先 |
don't care の入力は出力に影響しないため、どちらの値でも同じ出力が得られます。
(3)if 文によるエンコーダ記述
入力が 8 本、出力が 3 本のプライオリティエンコーダを考えます。MSB 側(DIN[7])の優先順位が最も高く、LSB 側に行くほど低くなります。
| DIN[7:0] | DOUT[2:0] | 優先順位 |
|---|---|---|
| 1xxxxxxx | 7 | 高(最優先) |
| 01xxxxxx | 6 | ↑ |
| 001xxxxx | 5 | | |
| 0001xxxx | 4 | | |
| 00001xxx | 3 | | |
| 000001xx | 2 | | |
| 0000001x | 1 | ↓ |
| 00000001 | 0 | 低(最低) |
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 文は不定値 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] |
|---|---|
| 10000000 | 7 |
| 01000000 | 6 |
| 00100000 | 5 |
| 00010000 | 4 |
| 00001000 | 3 |
| 00000100 | 2 |
| 00000010 | 1 |
| 00000001 | 0 |
// 優先順位のないエンコーダ
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 の出力値は
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 実施)。
~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) | 17 | 2.51 |
| encoder_casex.v(ENCODER_CASEX) | 17 | 2.51 |
| enc_nonprior.v(ENC_NONPRIOR) | 9 | 1.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本の入力信号 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: 不定値 `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???_????`)で記述するのが標準的だよ!