デコーダとエンコーダを書く
概要: 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] |
|---|---|
| 00 | 0001 |
| 01 | 0010 |
| 10 | 0100 |
| 11 | 1000 |
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
- 出力信号を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文で記述するだけで完成
default:上記4つ以外の値(不定値xやzなど)のときの処理- defaultでdoutに不定値を代入 → 論理合成ツールがゲート数最小の回路を生成
- 入出力ビット数が多くても記述量が少なく読みやすい(if文・等号演算より優れる)
4. casex文・casez文によるデコーダ
「0でも1でもどっちでもいいよ(Don't Care)」っていうときは、casex文が便利!🐾
xを使うことで、論理合成ツールが一番小さい回路になるよう最適化してくれるんだ!
case文の仲間としてcasex文とcasez文があります。
| 文 | 動作 |
|---|---|
case | 1, 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] |
|---|---|
| 00 | 0001 |
| 01 | 0001 |
| 10 | 0100 |
| 11 | 1000 |
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:主にシミュレーション用。回路記述ではあまり使用しない
5. if文によるエンコーダ
エンコーダはデコーダとは逆で、入力ビット数が出力ビット数より多くなります。ここでは入力4ビット・出力2ビットの4to2エンコーダをif文で記述します。
このエンコーダはdinの「1になる場所(ビット位置)」を出力doutで表します。
4to2エンコーダ 真理値表
| din[3:0] | dout[1:0] |
|---|---|
| 0001 | 00 |
| 0010 | 01 |
| 0100 | 10 |
| 1000 | 11 |
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
- 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
7. 修了判定(練習問題)
修了判定1:3to8デコーダをif文で記述する
設問: 3to8出力デコーダを記述する。if文で記述。
| din[2:0] | dout[7:0] |
|---|---|
| 000 | 00000001 |
| 001 | 00000010 |
| 010 | 00000100 |
| 011 | 00001000 |
| 100 | 00010000 |
| 101 | 00100000 |
| 110 | 01000000 |
| 111 | 10000000 |
▶ 解答を見る
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] |
|---|---|
| 00000001 | 000 |
| 00000010 | 001 |
| 00000100 | 010 |
| 00001000 | 011 |
| 00010000 | 100 |
| 00100000 | 101 |
| 01000000 | 110 |
| 10000000 | 111 |
▶ 解答を見る
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ビットを「どちらでもよい」として比較を省略できる | ドントケア条件がある回路記述 |
