デコーダとエンコーダを書く

📌 このページの概要と目標

概要: always文を使ったデコーダとエンコーダの記述を学び、if文・case文の使い方を理解する。

目標: 以下の回路を記述できる

  • always文を用いたデコーダ、エンコーダ
  • 等号演算、if文、case文を用いたデコーダ
  • casex、casez文を用いたデコーダ
  • if文、case文を用いたエンコーダ

修了判定:

  • 修了判定1:3to8デコーダをif文で記述する
  • 修了判定2:3to8デコーダをcasex文で記述する
  • 修了判定3:8to3エンコーダをif文で記述する

1. 等号演算によるデコーダ

もふねこ

ここでは「デコーダ」と「エンコーダ」という、信号を翻訳する回路を作っていくよ🐾
前回のセレクタと同じで色々な書き方があるから、どれが一番読みやすいか考えながら見てみてね!

==(等号演算子)は、2つの値を比較し、一致すれば1(真)、不一致なら0(偽)を返す1ビットの値です。この性質を利用してデコーダを記述できます。

2to4デコーダ 真理値表

din[1:0]dout[3:0]
000001
010010
100100
111000
module decoder_cond( din, dout );
input  [1:0] din;
output [3:0] dout;

assign dout[0] = (din==2'b00);
assign dout[1] = (din==2'b01);
assign dout[2] = (din==2'b10);
assign dout[3] = (din==2'b11);

endmodule
⚠️ 等号演算によるデコーダの注意点
  • 入出力のビット数が少ない場合は簡潔に記述できる
  • 入出力のビット数が多くなると記述量が増え、読みにくくなる

2. if文によるデコーダ

等号演算によるデコーダと同じ動作を、always文+if文で記述します。always文を使った組み合わせ回路では、出力信号をreg型として宣言し、センシティビティリストに入力信号を指定します。

module decoder_if( din, dout );
input  [1:0] din;
output [3:0] dout;

reg    [3:0] dout;

always @( din )
begin
    if ( din==2'b00 )
        dout <= 4'b0001;
    else if ( din==2'b01 )
        dout <= 4'b0010;
    else if ( din==2'b10 )
        dout <= 4'b0100;
    else
        dout <= 4'b1000;
end
endmodule
💡 always文による組み合わせ回路の書き方
  • 出力信号をreg型として宣言する
  • always @(din):括弧内の信号(din)が変化したときにbegin〜endを実行
  • 代入にはノンブロッキング代入(<=)を使う
  • 入出力ビット数が多くなると記述量が増え読みにくくなる(等号演算と同様)

3. case文によるデコーダ

if文によるデコーダと比較して、always文のbegin〜endの間だけが異なります。case文は真理値表をそのまま記述できるため、最もシンプルで読みやすい記述方法です。

module decoder_case( din, dout );
input  [1:0] din;
output [3:0] dout;

reg    [3:0] dout;

always @( din )
begin
    case ( din )
        2'b00:   dout <= 4'b0001;
        2'b01:   dout <= 4'b0010;
        2'b10:   dout <= 4'b0100;
        2'b11:   dout <= 4'b1000;
        default: dout <= 4'bxxxx;
    endcase
end
endmodule
💡 case文によるデコーダの特長
  • 真理値表をそのままcase文で記述するだけで完成
  • default:上記4つ以外の値(不定値xやzなど)のときの処理
  • defaultでdoutに不定値を代入 → 論理合成ツールがゲート数最小の回路を生成
  • 入出力ビット数が多くても記述量が少なく読みやすい(if文・等号演算より優れる)

4. casex文・casez文によるデコーダ

もふねこ

「0でも1でもどっちでもいいよ(Don't Care)」っていうときは、casex文が便利!🐾
xを使うことで、論理合成ツールが一番小さい回路になるよう最適化してくれるんだ!

case文の仲間としてcasex文とcasez文があります。

動作
case1, 0, 不定値x, ハイインピーダンスz のすべてを一致比較
casex比較する値にxまたはzを使用すると、そのビットの比較を行わず一致とみなす
casez比較する値にzを使用すると、そのビットの比較を行わず一致とみなす

casexの使用例

din[1]が0のとき、din[0]の値にかかわらずdoutは4'b0001とする場合:

真理値表(din[1]=0 のとき din[0] は無関係)

din[1:0]dout[3:0]
000001
010001
100100
111000
module decoder_casex( din, dout );
input  [1:0] din;
output [3:0] dout;

reg    [3:0] dout;

always @( din )
begin
    casex ( din )
        2'b0x:   dout <= 4'b0001;  // din[1]=0 ならビット0は無視
        2'b10:   dout <= 4'b0100;
        2'b11:   dout <= 4'b1000;
        default: dout <= 4'bxxxx;
    endcase
end
endmodule
⚠️ casex と casez の使い分け
  • casex:回路記述によく使用する
  • casez:主にシミュレーション用。回路記述ではあまり使用しない

5. if文によるエンコーダ

エンコーダはデコーダとは逆で、入力ビット数が出力ビット数より多くなります。ここでは入力4ビット・出力2ビットの4to2エンコーダをif文で記述します。

このエンコーダはdinの「1になる場所(ビット位置)」を出力doutで表します。

4to2エンコーダ 真理値表

din[3:0]dout[1:0]
000100
001001
010010
100011
module encoder_if( din, dout );
input  [3:0] din;
output [1:0] dout;

reg    [1:0] dout;

always @( din )
begin
    if ( din == 4'b0001 )
        dout <= 2'b00;
    else if ( din == 4'b0010 )
        dout <= 2'b01;
    else if ( din == 4'b0100 )
        dout <= 2'b10;
    else if ( din == 4'b1000 )
        dout <= 2'b11;
    else
        dout <= 2'bxx;
end
endmodule
💡 else(デフォルト処理)について
  • 4to2エンコーダで有効な入力は4通り(0001, 0010, 0100, 1000)のみ
  • それ以外の12通りの入力値やx/z入力はelse以降が実行される
  • elseでdoutに不定値xを代入 → 論理合成ツールが最小回路規模で合成
  • 00や11などの固定値を代入することも可能だが、回路サイズ最小化は期待できない

6. case文によるエンコーダ

if文によるエンコーダと比較して、always文のbegin〜endの間のみが異なります。case文によるエンコーダも、真理値表をそのままcase文で記述すれば完成です。

module encoder_case( din, dout );
input  [3:0] din;
output [1:0] dout;

reg    [1:0] dout;

always @(din)
begin
    case(din)
        4'b0001: dout <= 2'b00;
        4'b0010: dout <= 2'b01;
        4'b0100: dout <= 2'b10;
        4'b1000: dout <= 2'b11;
        default: dout <= 2'bxx;
    endcase
end
endmodule
💡 case文によるエンコーダの特長 defaultはif文のelseと同様。真理値表以外の入力に対する出力を記述します。不定値xを指定することで、論理合成ツールは回路が最も小さくなるように合成します。

7. 修了判定(練習問題)

⚠️ 修了判定とは 以下の設問に自分で解答を書いてから、答え合わせをしてください。

修了判定1:3to8デコーダをif文で記述する

設問: 3to8出力デコーダを記述する。if文で記述。

din[2:0]dout[7:0]
00000000001
00100000010
01000000100
01100001000
10000010000
10100100000
11001000000
11110000000
▶ 解答を見る
module decoder_if( din, dout );
input  [2:0] din;
output [7:0] dout;

reg    [7:0] dout;

always @( din )
begin
    if ( din == 3'b000 )
        dout <= 8'b00000001;
    else if ( din == 3'b001 )
        dout <= 8'b00000010;
    else if ( din == 3'b010 )
        dout <= 8'b00000100;
    else if ( din == 3'b011 )
        dout <= 8'b00001000;
    else if ( din == 3'b100 )
        dout <= 8'b00010000;
    else if ( din == 3'b101 )
        dout <= 8'b00100000;
    else if ( din == 3'b110 )
        dout <= 8'b01000000;
    else
        dout <= 8'b10000000;
end
endmodule

修了判定2:3to8デコーダをcasex文で記述する

設問: 3to8出力デコーダを記述する。casex文で記述。(真理値表は修了判定1と同じ)

▶ 解答を見る
module decoder_case( din, dout );
input  [2:0] din;
output [7:0] dout;

reg    [7:0] dout;

always @( din )
begin
    casex ( din )
        3'b00x:  dout <= 8'b00000001;
        3'b010:  dout <= 8'b00000100;
        3'b011:  dout <= 8'b00001000;
        3'b100:  dout <= 8'b00010000;
        3'b101:  dout <= 8'b00100000;
        3'b110:  dout <= 8'b01000000;
        3'b111:  dout <= 8'b10000000;
        default: dout <= 8'bxxxxxxxx;
    endcase
end
endmodule

修了判定3:8to3エンコーダをif文で記述する

設問: 8to3エンコーダを記述する。if文で記述。

din[7:0]dout[2:0]
00000001000
00000010001
00000100010
00001000011
00010000100
00100000101
01000000110
10000000111
▶ 解答を見る
module encoder_if( din, dout );
input  [7:0] din;
output [2:0] dout;

reg    [2:0] dout;

always @(din)
begin
    if ( din == 8'h1 )
        dout <= 3'h0;
    else if ( din == 8'h2 )
        dout <= 3'h1;
    else if ( din == 8'h4 )
        dout <= 3'h2;
    else if ( din == 8'h8 )
        dout <= 3'h3;
    else if ( din == 8'h10 )
        dout <= 3'h4;
    else if ( din == 8'h20 )
        dout <= 3'h5;
    else if ( din == 8'h40 )
        dout <= 3'h6;
    else if ( din == 8'h80 )
        dout <= 3'h7;
    else
        dout <= 3'hxx;
end
endmodule

📝 C2 まとめ: デコーダとエンコーダ 記述スタイル比較

記述スタイル特徴推奨場面
等号演算(assign)各出力ビットを独立したassign文で記述。ビット数増加で記述量増大入出力ビット数が少ない場合
if文(always)条件分岐で記述。ビット数増加で記述量増大条件が複雑なとき
case文(always)真理値表をそのまま記述可能。最もシンプル・読みやすいデコーダ・エンコーダ全般(推奨)
casex文(always)xビットを「どちらでもよい」として比較を省略できるドントケア条件がある回路記述