3章 組み合わせ論理回路の設計

本章では、always_comb 文を用いて加算器やエンコーダ、マルチプレクサなどの組み合わせ論理回路を設計する方法を学びます。

always_comb 文はいずれかの信号に変化があったときに起動し、always_comb 文の内部の処理が実行されるような回路が構成されます。 これによって組合せ回路を設計することができます。

3.1 4ビット加算器

4ビット加算器 adder

<図 3.1 4ビット加算器 adder>

2章では図 3.1 に示す4ビット加算器を、算術演算子 + と assign 文を用いて設計しました(リスト 2.6)。

同じ回路を以下のリスト 3.1 のように always_comb 文を用いて記述することもできます。

<リスト3.1 adder モジュール(4ビット加算器)>

adder.sv
module adder(
  input   logic [3:0] a,
  input   logic [3:0] b,
  output  logic [3:0] sum,
  output  logic       carry
);

  always_comb begin
    {carry, sum} = a + b;
  end

endmodule

この例では、信号 a もしくは b に変化があったときに always_comb 文が起動し、 a と b の加算結果を sum に出力し、その繰り上がりを carry に出力しています。 すなわち curry 付きの加算回路が実現されます。

always_comb 文の内部で信号の代入を行うには、ブロッキング代入 = を用います。 ノンブロッキング代入と呼ばれる <= を使うこともできますが、 両者には評価方法についての挙動の違いがあり、 always_comb 文で組み合わせ回路を設計する場合には、 原則としてブロッキング代入 = を用いることを推奨します。

なお、always_comb 文や、次章以降で学ぶ always_ff 文などの always 文の内部では assign 文による代入を行うことはできません。 また、always 文の外部ではブロッキング代入やノンブロッキング代入を行うことはできません。

演習

リスト3.1の adder モジュールを実習ボード DE0-CV に実装してその動作を確認しましょう。 adder モジュールの入出力信号は表3.1のように DE0-CV の入出力デバイスに割り当ててください。

<表3.1 adder モジュールの入出力のデバイスへの割り当て>

信号名割り当てデバイス入出力
a[3:0]SW7-SW4input
b[3:0]SW3-SW0input
carryLEDR9output
sum[3:0]LEDR3-LEDR0output

3.2 7セグメントデコーダ

DE0-CV には 7 セグメント LED ディスプレイが 6 個 (HEX0~HEX5) 搭載されています。 それぞれのディスプレイは 7 個の LED を用いて数字やアルファベットを表示することができます。

7セグメント LED ディスプレイ (HEX0)

7セグメント LED ディスプレイの各 LED は、対応する入力端子に 0 を入力すると点灯し、 1 を入力すると消灯します。 例えば HEX0 の場合、HEX06~HEX00 に 0110000 を入力すると 番号 6, 3, 2, 1, 0 のLEDが点灯し "3" のパターンを表示できます。

では、DE0-CV に搭載されている 7 セグメント LED ディスプレイを用いて、 4-bit の入力信号を16進数のパターン (0,1,2,3,4,5,6,7,8,9,A,b,c,d,E,F) に変換して表示する回路を設計します。

この回路は、4-bit の入力信号 num[3:0] と 7-bit の出力信号 hex[6:0] の関係が、 表3.2の真理値表で与えられる組み合わせ論理回路 sseg_decoder (図3.2)によって実現できます。

<表3.2 sseg_decoderモジュールの真理値表>

入力 num[3:0]出力 hex[6:0]表示パターン
000010000000
000111110011
001001001002
001101100003
010000110014
010100100105
011000000106
011110110007
100000000008
100100100009
10100001000A
10110000011b
11000100111c
11010100001d
11100000110E
11110001110F

7セグメントデコーダ <図3.2 7セグメントデコーダ sseg_decoder>

このような回路の機能が真理値表で与えられる場合は、case 文を用いて設計することができます。 case 文は入力信号の値に応じて出力信号の値を決定するための文です。 case 文は always 文の中で使用します。

リスト3.2に sseg_decoder モジュールの記述例を示します。

<リスト3.2 sseg_decoder モジュール(7セグメントデコーダ)>

sseg_decoder.sv
module sseg_decoder(
  input   logic [3:0]   num,
  output  logic [6:0]   hex
);

  always_comb begin
    case (num)
      4'h0  : hex = 7'b100_0000;
      4'h1  : hex = 7'b111_1001;
      4'h2  : hex = 7'b010_0100;
      4'h3  : hex = 7'b011_0000;
      4'h4  : hex = 7'b001_1001;
      4'h5  : hex = 7'b001_0010;
      4'h6  : hex = 7'b000_0010;
      4'h7  : hex = 7'b101_1000;
      4'h8  : hex = 7'b000_0000;
      4'h9  : hex = 7'b001_0000;
      4'ha  : hex = 7'b000_1000;
      4'hb  : hex = 7'b000_0011;
      4'hc  : hex = 7'b100_0110;
      4'hd  : hex = 7'b010_0001;
      4'he  : hex = 7'b000_0110;
      4'hf  : hex = 7'b000_1110;
      default  : hex = 7'b111_1111; 
      // 上記で全パターン尽くされているのでこのdefaultは実際は不要
    endcase
  end

endmodule

表 3.2 の真理値表がそのまま case 文の中に示されています。 上記の case 文では、信号 num の値について上からパターンマッチが行われ、 パターンが一致するところの文が実行されます。 例えば、num = 4'b1010 (= 4'hA)の時は、hex に 7'b0001000 が代入されます。

case 文を用いて組み合わせ論理回路を設計する場合は、すべての入力パターンを尽くすように記述しなければいけません。 必要に応じて default 文を用い、すべての入力パターンが尽くされるようにしましょう。

演習

リスト 3.2 の sseg_decoder モジュールを実習ボード DE0-CV に実装してその動作を確認しましょう。 sseg_decoder モジュールの入出力信号は表 3.2 のように DE0-CV の入出力デバイスに割り当ててください。

<表 3.2 sseg_decoder モジュールの入出力のデバイスへの割り当て>

信号名割り当てデバイス入出力
num[3:0]SW3-SW0input
hex[6:0]HEX06-HEX00output

3.3 マルチプレクサ

図3.3に示す4入力マルチプレクサを設計することを考えます。 選択信号 sel の値によって 4 つの入力信号 d0, d1, d2, d3 のいずれか一つが選ばれ、 その信号が出力されます。

4入力マルチプレクサ

<図 3.3 4入力マルチプレクサ mux4>

表 3.3 に 4入力マルチプレクサ mux4 の機能表を示します。

<表3.3 mux4 モジュールの機能表>

選択信号 sel出力 y
00d0
01d1
10d2
11d3

mux4 回路は表3.3に従って選択信号の値により出力 y の値が切り替わるので 先ほどと同様に case 文を用いて記述することができます。 リスト 3.3 に mux4 モジュールの記述例を示します。

<リスト 3.3 mux4 モジュール>

mux4.sv
module mux4 (
  input   logic [1:0] sel, // 選択信号
  input   logic [1:0] d0,
  input   logic [1:0] d1,
  input   logic [1:0] d2,
  input   logic [1:0] d3,
  output  logic [1:0] y
);

  always_comb begin
    case (sel)
      2'b00   : y = d0;
      2'b01   : y = d1;
      2'b10   : y = d2;
      2'b11   : y = d3;
      default : y = 2'b00; 
    endcase
  end

endmodule

演習

リスト3.3の mux4 モジュールを実習ボード DE0-CV に実装してその動作を確認しましょう。 mux4 モジュールの入出力信号は表3.3aのように DE0-CV の入出力デバイスに割り当ててください。

<表3.3a mux4 モジュールの入出力のデバイスへの割り当て>

信号名割り当てデバイス入出力
sel[1:0]SW9-SW8input
d0[1:0]SW7-SW6input
d1[1:0]SW5-SW4input
d2[1:0]SW3-SW2input
d3[1:0]SW1-SW0input
y[1:0]LEDR1-LEDR0output

3.4 プライオリティエンコーダ

図3.4に示すような 4 入力のプライオリティエンコーダ priotiry_encoder を設計します。

プライオリティエンコーダ

このエンコーダは 4 つの各 1 ビットの入力信号 d[3], d[2], d[1], d[0] のうち、 1 が入力されている最も上位のビット位置を 2 進数表示で y に出力します。 出力 en は入力が有効かどうかを示す信号で、 d[3] ~ d[0] のいずれかに 1 が入力されているときには 1 を出力し、 どれにも 1 が入力されていない(つまりすべて 0 が入力されている)ときは 0 を出力します。 例えば d[3:0] として 0101 が入力されたときは y = 10, en = 1 が出力されます。

表3.4にこの 4 入力のプライオリティエンコーダ priority_encoder の機能表を示します。

<表 3.4 priority_decoder モジュールの機能表 >

入力 d[3:0]出力 y[1:0]出力 en
1***111
01**101
001*011
0001001
上記以外000

表の入力 d[3:0] の欄での * は、そのビット位置の信号が 0, 1 どちらの場合でも 対応する行で出力が決定されることを示しています。

このような回路は casez 文を用いると簡単に記述することができます。

priority_encoder.sv
module priority_encoder(
  input   logic [3:0] d,  // 入力信号
  output  logic [1:0] y,  // 出力信号
  output  logic       en  // 有効信号
);

  assign en = |d; // d[3] | d[2] | d[1] | d[0] と同じ
  
  always_comb begin
    casez (d)
      4'b1??? : y = 2'b11;
      4'b01?? : y = 2'b10;
      4'b001? : y = 2'b01;
      4'b0001 : y = 2'b00;
      default : y = 2'b00;
     endcase
  end
  
endmodule

このコードでは casez 文で d の値でパターンマッチングを行っていますが、 機能表 3.4 において * で表した部分は 0 にも 1 にも対応するワールドカードとして ? を用いてパターンを記述しています。

出力 en への assign 文においては、 信号 d の 4 ビットすべて OR をとるリダクション演算子による記法 |d を用いています。 これは d[3] | d[2] | d[1] | d[0] と同じ意味になります。 なお、すべての AND をとりたいときは &d と記述します。

演習

リスト 3.4 の を実習ボード DE0-CV に実装してその動作を確認しましょう。 priority_encoder モジュール入出力信号は表 3.4a のように DE0-CV の入出力デバイスに割り当ててください。

<表3.4a priority_encoder モジュールの入出力のデバイスへの割り当て>

信号名割り当てデバイス入出力
d[3:0]SW3-SW0input
y[1:0]LEDR1-LEDR0output
enLEDR9output