Verilog HDLによる組合せ論理回路の設計(授業用)

# モジュールの骨格

## 実習1. Half Adderの設計1

halfadder1.v を実習ボードに実装し、動作を確かめること。
入出力の割り当ては表の通りにすること。

``` Verilog:halfadder1.v
// Top-level entity: halfadder

`default_nettype none

module halfadder( // モジュール名とポートリストを定める
  input   wire  a,
  input   wire  b,
  output  wire  c,
  output  wire  s
  );
  
  // 回路の機能を記述する
  assign  c = a & b;    // bitwise AND
  assign  s = a ^ b;    // bitwise XOR

endmodule
```

|ポート|I/O デバイス|
|---|-----|
| a | SA1 (トグルスイッチ) |
| b | SA0 (トグルスイッチ)|
| c | DA1 (個別LED)|
| s | DA0 (個別LED)|

設計する回路のイメージ
![halfaddr.png](https://qiita-image-store.s3.amazonaws.com/0/25656/dfec93e6-d068-8221-3cc5-369583e09e60.png)


### 解説

- Verilog HDLではモジュール単位で回路設計を行う
- モジュール名と、モジュールへの入出力信号となるポートリストを宣言する
- ポートリストでは入出力の方向(input/output)、ビット幅、信号名を定める
- モジュール内で利用する信号を定める
- 信号はwire宣言もしくはreg宣言をする  
  - wire 宣言 ネット型のデータを示す(配線と考えてよい)
  - reg 宣言 レジスタ型のデータを示す

```
// モジュールの骨格
module module_name( // モジュール名
  // ポートリスト
  input   wire        input_signal0,    // 1bitの入力信号
  input   wire  [3:0] input_signal1,    // 4bitの入力信号
  output  wire  [7:0] output_signal,    // 8bitの出力信号
  ); // (セミコロンを忘れずに)
  
  // モジュール内で利用する信号の定義
  wire  [3:0] internal_signal;
  
  // *********************
  // 回路の機能を記述 (略)
  // *********************

endmodule // (ここはセミコロン必要なし)
```


# assign による信号の割り当て

## 実習2. Half Adderの設計2

halfadder2.v を実習ボードに実装し、動作を確かめること。
入出力の割り当ては実習1と同様にすること。

``` Verilog:halfadder2.v
// Top-level entitiy: halfadder

`default_nettype none

module halfadder( 
  input   wire  a,
  input   wire  b,
  output  wire  c,
  output  wire  s);
  // モジュール内信号定義
  wire  [1:0] sum;
  
  assign sum = a + b;   // 算術和
  assign {c, s} = sum;  // ビット連結 
  // sumを使わず以下でも大丈夫
  // assign {c, s} = a + b;
  
endmodule
```

### 解説
- wire 宣言した出力信号、内部信号へは assign 文で配線の割当をおこなう
- 算術演算や論理演算などの式を記述できる [link](http://qiita.com/rikitoro@github/items/9e93b3b9d9c7dd2bd156)

# case 文による組合せ論理回路の設計

## 実習3. Half Adderの設計3
halfadder3.v を実習ボードに実装し、動作を確かめること。
入出力の割り当ては実習1と同様にすること。

```Verilog:halfadder3.v
// Top-level entitiy: halfadder
`default_nettype none

module halfadder( 
  input   wire  a,
  input   wire  b,
  output  wire  c,
  output  wire  s
  );

  reg   [1:0] sum; // always文で代入される信号はreg宣言する

  // always文
  always @ (*) begin // inputのa, bが変化した時の振舞いを記述
    // case文
    case ({a, b})
      2'b00:  sum = 2'b00;
      2'b01:  sum = 2'b01;
      2'b10:  sum = 2'b01;
      2'b11:  sum = 2'b10;
    endcase
    // case文終わり
  end 
  // always文終わり

  assign {c, s} = sum;

endmodule
```

### 解説

- case文はalways文の中で記述する
- case文では()内の信号についてパターンマッチングを行い、一致するところのステートメントを実行する
  - パターンマッチは上から順に行われる
  - どのパターンにも一致しない場合をまとめて記述するときは```default```をもちいる
- always文内での信号の割り当てはブロッキング代入(```=```)もしくはノンブロッキング代入(```<=```)で行う
  - assign文ではないことに注意する
- always文内で代入される信号(```=```, ```<=```の左辺)は、reg宣言されたものである必要がある
- 数値はビット幅と基数を指定して表示する [link](http://qiita.com/rikitoro@github/items/9e93b3b9d9c7dd2bd156)


## 実習4. エンコーダの設計

encoder.v を実習ボードに実装し、動作を確かめること。
入出力の割り当ては下記の通りにすること。

```Verilog:encoder.v
// Top-level entitiy: encoder
`default_nettype none

module encoder( 
  input   wire  [3:0] data,
  output  reg   [2:0] y
  );

  always @ (*) begin
    case (data)
      4'b0001:  y = 3'b000;
      4'b0010:  y = 3'b001;
      4'b0100:  y = 3'b010;
      4'b1000:  y = 3'b011;
      default:  y = 3'b100;
    endcase
  end
endmodule
```

|ポート|I/O デバイス|
|---|-----|
| data[3] | SA3 (トグルスイッチ) |
| data[2] | SA2 (トグルスイッチ)|
| data[1] | SA1 (トグルスイッチ)|
| data[0] | SA0 (トグルスイッチ)|
| y[2]    | DA2 (個別LED) |
| y[1]    | DA1 (個別LED) |
| y[0]    | DA0 (個別LED) |

設計する回路のイメージ
![encoder.png](https://qiita-image-store.s3.amazonaws.com/0/25656/60ccb3a9-c222-c2e8-60f1-f3de02de4957.png)


### 解説

- case文で組合せ論理回路を設計する際は以下のような記述を推奨する
  - 出力に相当する信号をreg宣言する
  - ```always @ (*) begin ``` ~ ```end```内にcase文を記述する
  - case文ではパターンマッチングに漏れが無いようにする(```default```を活用するとよい)
  - ブロッキング代入```=```を使って出力への信号の割り当てを行う


## 実習5. マルチプレクサの設計1
mux.v をカメレオンボードに実装し、動作を確かめること。
入出力の割り当ては表のとおりにすること。

```Verilog:mux1.v
// Top-level entity: mux
`default_nettype none

module mux( 
  input   wire  [2:0] d0,
  input   wire  [2:0] d1,
  input   wire        sel,
  output  reg   [2:0] y
  );

  always @ (*) begin
    case (sel)
      1'b0:  y = d0;
      1'b1:  y = d1;
    endcase
  end
endmodule
```

|ポート|I/O デバイス|
|---|-----|
| d1[2:0] | SA5~SA3 (トグルスイッチ) |
| d0[2:0] | SA2~SA0 (トグルスイッチ) |
| sel     | SA7 (トグルスイッチ)|
| y[2:0]  | DA2~DA0 (個別LED) |

設計する回路のイメージ
![mux.png](https://qiita-image-store.s3.amazonaws.com/0/25656/1684ade7-c6d6-07d2-f381-81d895ccd412.png)


# if 文による組合せ論理回路の設計

## 実習6. マルチプレクサの設計2
mux.v をカメレオンボードに実装し、動作を確かめること。
入出力の割り当ては実習5と同様にすること。

```Verilog:mux2.v
// Top-level entity: mux
`default_nettype none

module mux( 
  input   wire  [2:0] d0,
  input   wire  [2:0] d1,
  input   wire        sel,
  output  reg   [2:0] y
  );

  always @ (*) begin
    if (sel) begin // selが非0の場合が成立, 0の場合が不成立
      y = d1;      //   selが成立のとき、こちらが実行される
    end else begin
      y = d0;      //   selが不成立のとき、こちらが実行される
    end
  end
endmodule
```

### 解説

- if 文はalways文の中で記述する
- if文では条件部()内の信号について成立/不成立を判定する
  - 成立: 非0 (どれか1ビットでも0でない場合)
  - 不成立: 0 (全てのビットが0の場合)
- if文で組合せ論理回路を設計する際は以下のような記述を推奨する
  - 出力に相当する信号をreg宣言する
  - ```always @ (*) begin ``` ~ ```end```内にif文を記述する
  - else節を省略しない(成立/不成立両方の場合の動作を記述する)
  - ブロッキング代入```=```を使って出力への信号の割り当てを行う


## 関連資料

- [Verilog HDLによる論理回路のモジュール設計(授業用)](http://qiita.com/rikitoro@github/items/a2a9c9eadfda878cce69)
- [Verilog HDLによる順序回路の設計(授業用)]
- [Verilog HDLでの回路記述で用いる数値表現と演算子 (授業用)](http://qiita.com/rikitoro@github/items/9e93b3b9d9c7dd2bd156)