Chisel入門書「Digital Design with Chisel」5章の勉強記録

# 記事の概要
Chiselの入門書「Digital Design with Chisel」の5章の勉強記録です。
本文の概要を備忘録として整理し、また実際に行った演習を紹介します。

本のpdfデータとプログラム一式は無料で以下から入手できます。
https://raw.githubusercontent.com/wiki/schoeberl/chisel-book/chisel-book.pdf
https://github.com/schoeberl/chisel-book

## 5.1 Combinational Circuits
組み合わせ回路をChiselで表現する方法を説明しています。
例えば、val変数eで以下の組み合わせ回路を作成したとします。

```Scala
val e = (a & b) | c
```

eは別の式に代入することができます。

```Scala
val f = ˜e
```

ですが、valはScalaの固定変数なので、eに他の式を再代入するとScalaコンパイラエラー：reassignment to val　が発生します。
また、以下のようにChisel演算子:=を使用しようとすると、

```Scala
e := c & b
```

RuntimeException：Cannot reassign to read-only　が発生します


### 条件付き組み合わせ回路
条件付き組み合わせ回路、つまりマルチプレクサ回路は、if/elseif/elseに相当するwhen/.elsewhen/otherwiseを用いて作成できます。
if/elseif/elseを使用しないのは、ハードウェア生成をしない単なる条件式と区別する為です。（例えば、ハードウェア作成するかしないか条件付きで判定する場合、if文を用いる）
.elsewhenは最初に"."が必要なので注意してください。Scalaでは演算のメソッド呼び出しに"."をつけないといけないからです。

```Scala
val w = WireInit(0.U)

when (cond) { 
  w := 1.U 
} .elsewhen (cond2) { 
  w := 2.U 
} otherwise { 
  w := 3.U 
}
```

wは以下のようにWireにより作成することもできますが、初期値を定義できるWireInitの使用が推奨されます。

```Scala
val w = Wire(UInt())
```

when文は幾らでも深くできますが、条件式が1つの信号にしか依存しないようならば、switch文の使用が推奨されます。

## 5.2 Decoder
デコーダーは、nビットの2進数をmビット信号に変換します（ただし$m≤2n$を満たす）

```Scala
result := 0.U

switch(sel) { 
  is (0.U) { result := 1.U} 
  is (1.U) { result := 2.U} 
  is (2.U) { result := 4.U} 
  is (3.U) { result := 8.U} 
}
```

ここで条件信号selの全ての組み合わせを網羅しています。
Chiselでは、選択信号の全ての組み合わせに対応する出力信号を決めないとエラーになります。
意図しないラッチが発生する組み合わせ回路の不完全な割り当てを回避するためです。

4章のALUでも説明したように、switch/isを使用するには以下をインポートする必要があります。

```Scala
import chisel3.util._
```

デコーダはバイナリで表現した方が分かりやすくなります。

```Scala
switch (sel) { 
  is ("b00".U) { result := "b0001".U} 
  is ("b01".U) { result := "b0010".U} 
  is ("b10".U) { result := "b0100".U} 
  is ("b11".U) { result := "b1000".U} 
}
```

また、上記のデコーダのresultは規則的な構造を持っているので、左シフトを使用した方が適切かもしれません。

```Scala
switch (sel) { 
  is ("b00".U) { result := 1.U << sel} 
  is ("b01".U) { result := 1.U << sel} 
  is ("b10".U) { result := 1.U << sel} 
  is ("b11".U) { result := 1.U << sel} 
}
```

なお、デコーダを使用してマルチプレクサーを作成できますが、ChiselのコアライブラリにはMuxがあるので、わざわざそのような作り方をする必要はありません。

## 5.3 Encoder
エンコーダはデコーダの逆のことをしています。

```Scala
b := "b00".U 
switch (a) { 
  is ("b0001".U) { b := "b00".U} 
  is ("b0010".U) { b := "b01".U} 
  is ("b0100".U) { b := "b10".U} 
  is ("b1000".U) { b := "b11".U} 
}
```

もし出力信号bを初期化していない場合、このエンコーダはエラーになります。
switch文は選択信号aの全ての組み合わせを網羅していない為、switch文から漏れたパターンの出力は未定義になるからです。
デコーダで説明したように、未定義の出力を持つ関数はエラーになります。

そこで全ての未定義の入力パターンを網羅する為に、あらかじめ出力bは00に初期化してあります。それによりswitch文から漏れたパターンには00が出力されます。

## 5.4 Exercise

4ビットバイナリ入力を7セグメントディスプレイのエンコードに変換する組み合わせ回路を記述します。
4つのスイッチまたはボタンを回路の入力に接続し、出力を7セグメントディスプレイに接続します。

以下のをverilogに変換します。

```Scala
import chisel3._
import chisel3.Driver
import chisel3.util._

class Encoder_7seg extends Module {
  val io = IO(new Bundle {
    val sw = Input(UInt(4.W))
    val seg = Output(UInt(8.W))
    val dig = Output(UInt(4.W))
  })

  io.dig := "b1111".U

  io.seg := "b11111111".U 
  switch (io.sw) { 
    is (0.U) { io.seg := "b00000011".U} 
    is (1.U) { io.seg := "b10011111".U} 
    is (2.U) { io.seg := "b00100101".U} 
    is (3.U) { io.seg := "b00001101".U} 
    is (4.U) { io.seg := "b10011001".U} 
    is (5.U) { io.seg := "b01001001".U} 
    is (6.U) { io.seg := "b01000001".U} 
    is (7.U) { io.seg := "b00011111".U} 
    is (8.U) { io.seg := "b00000001".U} 
    is (9.U) { io.seg := "b00001001".U} 
  }
}

/**
 * An object extending App to generate the Verilog code.
 */
object Encoder_7seg extends App {
  chisel3.Driver.execute(Array[String](), () => new Encoder_7seg())
}
```

[市販の4桁7セグ表示器キット](http://www.aitendo.com/product/14393)をArty S7に接続して、以下のようにスイッチにより7セグの値が0から9まで変化することを確認しました。
<img width="50%" alt="7seg.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/5470bf6e-bc57-119c-0326-5d1a6bee1e28.jpeg">


制約ファイルは以下を用いました。
7セグ付きのFPGAボード、もしくはご自身で7セグを取り付けて使用する場合のご参考にしてください。

```
## This file is a general .xdc for the Arty S7-25 Rev. E
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock Signals
set_property -dict { PACKAGE_PIN F14   IOSTANDARD LVCMOS33 } [get_ports { clock }]; #IO_L13P_T2_MRCC_15 Sch=uclk
create_clock -add -name sys_clk_pin -period 83.333 -waveform {0 41.667} [get_ports { clock }];

## Switches
set_property -dict { PACKAGE_PIN H14   IOSTANDARD LVCMOS33 } [get_ports { io_sw[0] }]; #IO_L20N_T3_A19_15 Sch=sw[0]
set_property -dict { PACKAGE_PIN H18   IOSTANDARD LVCMOS33 } [get_ports { io_sw[1] }]; #IO_L21P_T3_DQS_15 Sch=sw[1]
set_property -dict { PACKAGE_PIN G18   IOSTANDARD LVCMOS33 } [get_ports { io_sw[2] }]; #IO_L21N_T3_DQS_A18_15 Sch=sw[2]
set_property -dict { PACKAGE_PIN M5    IOSTANDARD SSTL135 } [get_ports { io_sw[3] }]; #IO_L6N_T0_VREF_34 Sch=sw[3]

## Buttons
set_property -dict { PACKAGE_PIN G15   IOSTANDARD LVCMOS33 } [get_ports { reset }]; #IO_L18N_T2_A23_15 Sch=btn[0]

## ChipKit Outer Digital Header
set_property -dict { PACKAGE_PIN L13   IOSTANDARD LVCMOS33 } [get_ports { io_seg[0] }]; #IO_0_14 Sch=ck_io[0] 
set_property -dict { PACKAGE_PIN N13   IOSTANDARD LVCMOS33 } [get_ports { io_seg[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=ck_io[1] F
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { io_seg[3] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=ck_io[2] E
set_property -dict { PACKAGE_PIN R14   IOSTANDARD LVCMOS33 } [get_ports { io_seg[4] }]; #IO_L13P_T2_MRCC_14 Sch=ck_io[3] D
set_property -dict { PACKAGE_PIN T14   IOSTANDARD LVCMOS33 } [get_ports { io_seg[5] }]; #IO_L13N_T2_MRCC_14 Sch=ck_io[4] C
set_property -dict { PACKAGE_PIN R16   IOSTANDARD LVCMOS33 } [get_ports { io_seg[6] }]; #IO_L14P_T2_SRCC_14 Sch=ck_io[5] B

## ChipKit Inner Digital Header
## NOTE: these pins are shared with PMOD Headers JC and JD and cannot be used at the same time as the applicable PMOD interface(s)
set_property -dict { PACKAGE_PIN U11   IOSTANDARD LVCMOS33 } [get_ports { io_dig[3] }]; #IO_L24P_T3_A01_D17_14        Sch=jd10/ck_io[26]
set_property -dict { PACKAGE_PIN T11   IOSTANDARD LVCMOS33 } [get_ports { io_dig[2] }]; #IO_L23N_T3_A02_D18_14        Sch=jd9/ck_io[27]
set_property -dict { PACKAGE_PIN R11   IOSTANDARD LVCMOS33 } [get_ports { io_dig[1] }]; #IO_L23P_T3_A03_D19_14        Sch=jd8/ck_io[28]
set_property -dict { PACKAGE_PIN T13   IOSTANDARD LVCMOS33 } [get_ports { io_dig[0] }]; #IO_L22N_T3_A04_D20_14        Sch=jd7/ck_io[29]
set_property -dict { PACKAGE_PIN V13   IOSTANDARD LVCMOS33 } [get_ports { io_seg[7] }]; #IO_L21N_T3_DQS_A06_D22_14    Sch=jd3/ck_io[31] A
set_property -dict { PACKAGE_PIN U12   IOSTANDARD LVCMOS33 } [get_ports { io_seg[1] }]; #IO_L21P_T3_DQS_14            Sch=jd2/ck_io[32] G
```

## 参考
- [Chiselの文法 - 入門編 〜その5：Chiselの制御構文〜](https://www.tech-diningyo.info/entry/2019/06/02/102230)
    - 本記事で使われているwhen、switch、Muxなどについて詳細で分かりやすい説明がされています。


## 関連記事

[Chisel入門書「Digital Design with Chisel」1章の勉強記録](https://qiita.com/Kosuke_Matsui/items/80dde1219e3ce02c4b66)
[Chisel入門書「Digital Design with Chisel」2章の勉強記録](https://qiita.com/Kosuke_Matsui/items/be68930d121b13402e2a)
[Chisel入門書「Digital Design with Chisel」3章の勉強記録](https://qiita.com/Kosuke_Matsui/items/b82edca18089084350c0)
[Chisel入門書「Digital Design with Chisel」4章の勉強記録](https://qiita.com/Kosuke_Matsui/items/23800cf34fb435225ec9)
[Chisel入門書「Digital Design with Chisel」6章の勉強記録](https://qiita.com/Kosuke_Matsui/items/dd526ae4d59f30d1f586)
[Chisel入門書「Digital Design with Chisel」7章の勉強記録](https://qiita.com/Kosuke_Matsui/items/53a9e61facbc1cc79025)
[Chisel入門書「Digital Design with Chisel」8章の勉強記録](https://qiita.com/Kosuke_Matsui/items/3ab4be6bdba6d94be10b)
[Chisel入門書「Digital Design with Chisel」9章の勉強記録](https://qiita.com/Kosuke_Matsui/items/4c11d32c799800680f5c)