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

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

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

## 6.1 Registers
### レジスタ
レジスタは、Dフリップフロップの集合です。
Dフリップフロップは、クロックの立ち上がりエッジでの入力値を取り込み、出力値として保存します。

Chiselでは、入力dと出力qを持つレジスタは次のように定義されます。

```Scala
val q = RegNext(d)
```

クロックは暗黙裡に設定されているので明示する必要はありません。

最初にレジスタ名を定義してから、入力信号をレジスタの入力に接続することでレジスタを定義することもできます。

```Scala
val delayReg = Reg(UInt(4.W))

delayReg := delayIn
```

記法として、レジスタにはRegを含め、変数名は小文字から始めて、クラス名は大文字から始めるという規則に従います。

同期リセットによる初期化を行うレジスタを作成するのならば、以下のようにします。

```Scala
val valReg = RegInit(0.U(4.W))

valReg := inVal
```

### イネーブル付きレジスタ

```Scala
val enableReg = Reg(UInt(4.W))

when (enable) { 
  enableReg := inVal 
}
```

同期リセットによる初期化を行うイネーブル付きレジスタは、以下のように作成します

```Scala
val resetEnableReg = RegInit(0.U(4.W))

when (enable) { 
  resetEnableReg := inVal 
}
```

## 6.2 Counters
- 4ビットカウンター

```Scala
val cntReg = RegInit(0.U(4.W))

cntReg := cntReg + 1.U
```

0から4ビットの最大値である15までカウントしてから、再び0に戻るのを繰り返します。

- イベント発生時のみカウントするカウンター

```Scala
val cntEventsReg = RegInit(0.U(4.W)) 
when(event) { 
  cntEventsReg := cntEventsReg + 1.U 
}
```

- 最大値に到達した時にカウントリセットするカウンター

```Scala
val cntReg = RegInit(0.U(8.W))

cntReg := cntReg + 1.U 
when(cntReg === N) { 
  cntReg := 0.U 
}
```

マルチプレクサーを利用して同様のカウンターを作ることもできます。

```Scala
val cntReg = RegInit(0.U(8.W))

cntReg := Mux(cntReg === N, 0.U, cntReg + 1.U)
```

- カウントダウンするカウンター

最大値からカウントダウンを開始し、0に達したときにカウンターを最大値に戻します。

```Scala
val cntReg = RegInit(N)

cntReg := cntReg - 1.U 
when(cntReg === 0.U) { 
  cntReg := N 
}
```

- カウンター関数

パラメーターを使用して関数を定義し、複数のカウンターを量産できます。

```Scala
// This function returns a counter 
def genCounter(n: Int) = { 
  val cntReg = RegInit(0.U(8.W)) 
  cntReg := Mux(cntReg === n.U, 0.U, cntReg + 1.U) 
  cntReg 
}

// now we can easily create many counters 
val count10 = genCounter(10) 
val count99 = genCounter(99)
```

関数genCounterの最後のcntReg は、関数の戻り値がcntReg であることを示しています。

カウンタは0から始まるので10を数えるには引数は9でいいのですが、[Off-by-oneエラー](https://ja.wikipedia.org/wiki/Off-by-one%E3%82%A8%E3%83%A9%E3%83%BC)を考慮して10にしてあります。

## 6.2.2 Generating Timing with Counters 
マイコンのクロックは早すぎるので、数百ミリ秒単位や秒単位のカウントをする場合などには、nクロックサイクルごとに1カウントするようにします。

以下では、レジスタtickCounterReg がNカウントするごとに1クロックサイクルの間、tick信号がアサートします。

```Scala
val tickCounterReg = RegInit(0.U(4.W)) 
val tick = tickCounterReg === (N-1).U

tickCounterReg := tickCounterReg + 1.U 
when (tick) { 
  tickCounterReg := 0.U 
}
```

すると、レジスタlowFrequCntReg は、tick信号がアサートしているのを検知して、カウンタを加算します。

```Scala
val lowFrequCntReg = RegInit(0.U(4.W)) 
when (tick) { 
  lowFrequCntReg := lowFrequCntReg + 1.U 
}
```

カウンタ値の初期化では、値が0.Uに設定されている場合の混乱を回避する為に、データ幅を明示することを推奨します。

## 6.2.3 The Nerd Counter

負の値の最上位ビットが1になることを利用したカウントダウン方式のカウンターです。
カウンタとレジスタの最上位ビットcntReg(7)が1の時は、値が負になっているのでカウンタを最大値に戻し、tick信号をアサートします。
1クロックサイクルが経過すると、tick信号はネゲートします。
-1まで数えた時にカウンタリセットするので、MAXはカウント設定値から2を引いています。

```Scala
val MAX = (N - 2).S(8.W) 
val cntReg = RegInit(MAX) 
io.tick := false.B

cntReg := cntReg - 1.S when(cntReg(7)) { 
  cntReg := MAX 
  io.tick := true.B 
}
```

## 6.3 Memory
レジスタからメモリを作成することもできますが、レジスタは高価な
ので、メモリにはSRAMを使用します。
FPGAには、ブロックRAMとも呼ばれるオンチップメモリ​​ブロックが含まれており、これらを組み合わせてメモリを作ります。

FPGA（およびASIC）は通常、同期メモリをサポートします。同期メモリの入力には、読み取りアドレス、書き込みアドレス、書き込みデータ、書き込みイネーブル用のレジスタを使用します。
読み取りアドレスには、1つの読み取りデータ出力があります。

以下は1KBのメモリを実装するコンポーネントMemoryを示しています。

```Scala
class Memory() extends Module { 
  val io = IO(new Bundle { 
    val rdAddr = Input(UInt(10.W)) 
    val rdData = Output(UInt(8.W)) 
    val wrEna = Input(Bool()) 
    val wrData = Input(UInt(8.W)) 
    val wrAddr = Input(UInt(10.W)) 
  })
  
  val mem = SyncReadMem(1024, UInt(8.W))
  
  io.rdData := mem.read(io.rdAddr)
  
  when(io.wrEna) { 
    mem.write(io.wrAddr, io.wrData) 
  }
}
```

オンチップメモリ​​をサポートするために、ChiselはメモリコンストラクタSyncReadMemを提供しています。

Chiselは、同期書き込みと非同期読み取りを備えたメモリコンストラクタMemも提供します。
ですが、MemはFPGAでは直接利用できないため、合成ツールはレジスタを用いてメモリを構築してしまいます。 
したがって、FPGAではSyncReadMemの使用を推奨します。

## 6.4 Exercise

7セグメントエンコーダーを使用し、4ビットカウンターの0からFを7セグメントに表示させます。

500ミリ秒ごとにtick信号をアサートし、 その信号を4ビットカウンターのイネーブル信号に使用してカウントします。

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


class Counter_7seg extends Module {
  val io = IO(new Bundle {
    val seg = Output(UInt(8.W))
    val dig = Output(UInt(4.W))
  })
 
  val tickCounterReg = RegInit(0.U(23.W)) 
  val tick = tickCounterReg === (6000000 -1).U

  tickCounterReg := tickCounterReg + 1.U 
  when (tick) { 
    tickCounterReg := 0.U 
  }

  val cntSegReg  = RegInit(0.U(4.W)) 
  when (tick) { 
    cntSegReg := cntSegReg + 1.U 
    when(cntSegReg === 16.U) { 
      cntSegReg := 0.U 
    }
  }

  io.dig := "b1111".U

  io.seg := "b11111111".U 
  switch (cntSegReg) { 
    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} 
    is (10.U) { io.seg := "b00010001".U} 
    is (11.U) { io.seg := "b11000001".U} 
    is (12.U) { io.seg := "b11100101".U} 
    is (13.U) { io.seg := "b10000101".U} 
    is (14.U) { io.seg := "b01100001".U} 
    is (15.U) { io.seg := "b01110001".U} 
  }
}

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

build.sbtのある階層で以下を実行してverilogに変換します。

```
sbt "runMain Counter_7seg"
```

[1章の記事の方法](https://qiita.com/Kosuke_Matsui/items/80dde1219e3ce02c4b66)でvivadoからFPGAボードに書き込みます。
制約ファイルと使用している7セグ表示器は[5章の記事](https://qiita.com/Kosuke_Matsui/items/847665e371185f14e735)と同じになります。



## 関連記事

[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」5章の勉強記録](https://qiita.com/Kosuke_Matsui/items/847665e371185f14e735)
[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)