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

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

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

## 8.1 A Light Flasher Example
FSMを複数の小さなFSMに切り分ける例として点滅装置の例を考えます。

点滅装置の仕様は以下とします。

- 1つの入力信号startを持つ
- 1つの出力信号lightを持つ
- startが1クロックサイクルの間Highになると、点滅シーケンスが開始
- シーケンスは点滅を3回繰り返す
- 1回の点滅では、lightは6クロックサイクル点灯し、4クロックサイクル消灯
- シーケンスの後、FSMはlightをオフにし、次のstartを待機

このFSMには27個の状態があります。

- 初期状態：1個
- light ON状態：3回の点灯期間×6クロックサイクル　18個
- light OFF状態：2回の消灯期間×4クロックサイクル　8個

このFSMをMaster FSMとTimer FSMに切り分けます。
Timer FSMは6または4クロックサイクルの間カウントダウンします。
Timer FSMの仕様は以下になります。

- timerLoadがアサートされると、タイマーは状態に関係なく、ダウンカウンターに値をロードする。
- timerSelectは、ロード用に5または3を選択する。
- timerDoneは、カウンターがカウントダウンを完了するとアサートされ、アサートを維持する。
- それ以外の場合、タイマーはカウントダウンする

Timer FSMは以下のようにコーディングできます。

```Scala
val timerReg = RegInit(0.U)
timerDone := timerReg === 0.U

// Timer FSM (down counter)
when(!timerDone) {
  timerReg := timerReg - 1.U
}
when (timerLoad) {
  when (timerSelect) {
    timerReg := 5.U
  } .otherwise {
    timerReg := 3.U
  }
}
```

Master FSMは以下のようにコーディングできます。

```Scala
val off :: flash1 :: space1 :: flash2 :: space2 :: flash3 :: Nil = Enum(6)
val stateReg = RegInit(off)

val light = WireInit(false.B) // FSM output

// Timer connection
val timerLoad = WireInit(false.B) // start timer with a load
val timerSelect = WireInit(true.B) // select 6 or 4 cycles
val timerDone = Wire(Bool())

timerLoad := timerDone

// Master FSM
switch(stateReg) {
  is(off) {
    timerLoad := true.B
    timerSelect := true.B
  when (start) { stateReg := flash1 }
  }
  is (flash1) {
    timerSelect := false.B
    light := true.B
    when (timerDone) { stateReg := space1 }
  }
  is (space1) {
    when (timerDone) { stateReg := flash2 }
  }
  is (flash2) {
    timerSelect := false.B
    light := true.B
    when (timerDone) { stateReg := space2 }
  }
  is (space2) {
    when (timerDone) { stateReg := flash3 }
  }
  is (flash3) {
    timerSelect := false.B
    light := true.B
    when (timerDone) { stateReg := off }
  }
}
```

このマスターFSMには、まだ冗長性があります。
点灯状態flash1、flash2、およびflash3は同じ機能を実行しており、消灯状態space1およびspace2も同様です。

そこでFSMをMaster FSMとCounter FSMとTimer FSMに切り分けます。
Timer FSMに変更はありません。
Counter FSMは残りの点滅回数をカウントするFSMで、以下のようにコーディングできます。

```Scala
val cntReg = RegInit(0.U)
cntDone := cntReg === 0.U

// Down counter FSM
when(cntLoad) { cntReg := 2.U }
when(cntDecr) { cntReg := cntReg - 1.U }
```

残り点滅回数を数えるので、3回点滅の場合、カウントダウンは2から始まることに注意してください。

これによりMaster FSMは以下のようにコーディングできます。

```Scala
val off :: flash :: space :: Nil = Enum(3)
val stateReg = RegInit(off)

val light = WireInit(false.B) // FSM output

// Timer connection
val timerLoad = WireInit(false.B) // start timer with a load
val timerSelect = WireInit(true.B) // select 6 or 4 cycles
val timerDone = Wire(Bool())
// Counter connection
val cntLoad = WireInit(false.B)
val cntDecr = WireInit(false.B)
val cntDone = Wire(Bool())

timerLoad := timerDone

switch(stateReg) {
  is(off) {
    timerLoad := true.B
    timerSelect := true.B
    cntLoad := true.B
    when (start) { stateReg := flash }
  }
  is (flash) {
    timerSelect := false.B
    light := true.B
    when (timerDone & !cntDone) { stateReg := space }
    when (timerDone & cntDone) { stateReg := off }
  }
  is (space) {
    cntDecr := timerDone
    when (timerDone) { stateReg := flash }
  }
}
```
## 8.2 State Machine with Datapath
通信状態マシンの典型的な例の1つは、データパスと状態マシンを組み合わせます。これをFSMD(a ﬁnite state machine with datapath)と呼びます。


## 8.2.1 Popcount Example
Popcountを例として取り上げます。
Popcountは、ある整数値に対して、それを2進数で表現した場合のbit1の数を数えます。例えば、7ならb111なので3になります。
Popcountはハミング距離の計算に使われます。
ハミング距離とは、2つの同じ桁数の2進数を比較した場合の異なる桁数を数えたものです。例えば、b111とb101のハミング距離は1になります。
つまり、2つの2進数のxorを取って、Popcountを計算したものがハミング距離です。

通信有限状態マシンは、受信と送信の制御信号を持つ有限状態マシンです。
p74の図8.3ではデータパスユニットは入力dinと出力popCntを持ちます。

FSMはこの入出力信号に対してハンドシェイク通信を使用します。 
ハンドシェイク通信は、データが利用可能な場合、valid信号をアサートします。
そしてデータ受信者が可能になった時に、Ready信号を受け取ります。
validとReadyの両方の信号がアサートされると、転送が行われる仕組みです。

p74の図8.3ではFSMはdinに対するハンドシェイク信号、dinValid入力信号とdinReady出力信号を持ち、popCntに対するハンドシェイク信号、popCntValid出力信号とpopCntReady入力信号を持っています。

FSMは3つの状態Idle、Count、Doneを持ちます

- Idle
    -  初期状態
    -  入力データが受信可能であることを通知するdinReady信号を出力
    -  有効な入力データがあることを通知するdinValid信号を受信すると、load信号を出力し、Count状態へ遷移
- Count
    - 計算が完了したことを通知するdone信号を受信すると、Don状態へ遷移
- Done
    - 有効なカウント値があることを通知するpopCntValid信号を出力
    - カウント値を受信したことを通知するpopCntReady信号を受信すると、Idle状態へ遷移

```Scala
class PopCountFSM extends Module {
  val io = IO(new Bundle {
    val dinValid = Input(Bool())
    val dinReady = Output(Bool())
    val popCntValid = Output(Bool())
    val popCntReady = Input(Bool())
    val load = Output(Bool())
    val done = Input(Bool())
  })

  val idle :: count :: done :: Nil = Enum(3)
  val stateReg = RegInit(idle)

  io.load := false.B

  io.dinReady := false.B
  io.popCntValid := false.B

  switch(stateReg) {
    is(idle) {
      io.dinReady := true.B
      when(io.dinValid) {
        io.load := true.B
        stateReg := count
      }
    }
    is(count) {
      when(io.done) {
        stateReg := done
      }
    }
    is(done) {
      io.popCntValid := true.B
      when(io.popCntReady) {
        stateReg := idle
      }
    }
  }
}
```

データパスは、FSMにおいてdin信号を受信完了したことを通知するload信号がアサートした時にbit1の数を計算します。
そして計算完了時に、それをFSMへそれを通知するdone信号を送信します。

データパスにおいてpopCntの計算は、以下のように行います。

- load信号受信
    - データをレジスタにロード
    - popCntRegを0にリセット
    - 入力データを8ビットとして、counterRegの初期値を8に設定
- 入力データを1ビットずつシフトしbit1をカウント、counterRegも1ずつ減算する
- counterRegが0になったら、done信号を送信する。

```Scala
class PopCountDataPath extends Module {
  val io = IO(new Bundle {
    val din = Input(UInt(8.W))
    val load = Input(Bool())
    val popCnt = Output(UInt(4.W))
    val done = Output(Bool())
  })

  val dataReg = RegInit(0.U(8.W))
  val popCntReg = RegInit(0.U(8.W))
  val counterReg= RegInit(0.U(4.W))

  dataReg := 0.U ## dataReg(7, 1)
  popCntReg := popCntReg + dataReg(0)

  val done = counterReg === 0.U
  when (!done) {
    counterReg := counterReg - 1.U
  }

  when(io.load) {
    dataReg := io.din
    popCntReg := 0.U
    counterReg := 8.U
  }

  // debug output
  printf("%x %d\n", dataReg , popCntReg)

  io.popCnt := popCntReg
  io.done := done
}
```

~~本では"dataReg := 0.U ## dataReg(7, 1)"となっていた個所の\##を、連結演算子の::に置き換えました。
\##という記法は調べましたが、何を意味しているか分かりませんでした。
::を使用することで、dataRegは1ビットずつシフトしていきます。~~

[final macro def ##(that: Bits): UInt](https://www.chisel-lang.org/api/3.1.0/index.html#chisel3.core.UInt)において\##の定義が解説されていました。2つのwireを連結する演算子でした。結合後のビット幅は、両者のビット幅の和になります。
"dataReg := 0.U ## dataReg(7, 1)"により、dataRegは1ビットずつ右シフトしていきます。

なおdataReg が更新されるのは次のクロック立ち上がりなので、popCntRegの計算には現在のdataReg(0)が使用されます。

データパスとFSMはTOP階層で以下のように接続されます。

```Scala
class PopCount extends Module {
  val io = IO(new Bundle {
    val dinValid = Input(Bool())
    val dinReady = Output(Bool())
    val din = Input(UInt(8.W))
    val popCntValid = Output(Bool())
    val popCntReady = Input(Bool())
    val popCnt = Output(UInt(4.W))
  })
  
  val fsm = Module(new PopCountFSM)
  val data = Module(new PopCountDataPath)

  fsm.io.dinValid := io.dinValid
  io.dinReady := fsm.io.dinReady
  io.popCntValid := fsm.io.popCntValid
  fsm.io.popCntReady := io.popCntReady

  data.io.din := io.din
  io.popCnt := data.io.popCnt
  data.io.load := fsm.io.load
  fsm.io.done := data.io.done
}
```

## 8.3 Ready-Valid Interface
ハンドシェイク通信についての説明をしています。


## 関連記事

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

# 修正履歴
- 2019年9月27日修正
    - \##の使用方法について削除と加筆。