1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-09-16

記事の概要

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は以下のようにコーディングできます。

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は以下のようにコーディングできます。

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で、以下のようにコーディングできます。

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は以下のようにコーディングできます。

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 finite 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状態へ遷移
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信号を送信する。
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において##の定義が解説されていました。2つのwireを連結する演算子でした。結合後のビット幅は、両者のビット幅の和になります。
"dataReg := 0.U ## dataReg(7, 1)"により、dataRegは1ビットずつ右シフトしていきます。

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

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

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章の勉強記録
Chisel入門書「Digital Design with Chisel」2章の勉強記録
Chisel入門書「Digital Design with Chisel」3章の勉強記録
Chisel入門書「Digital Design with Chisel」4章の勉強記録
Chisel入門書「Digital Design with Chisel」5章の勉強記録
Chisel入門書「Digital Design with Chisel」6章の勉強記録
Chisel入門書「Digital Design with Chisel」7章の勉強記録
Chisel入門書「Digital Design with Chisel」9章の勉強記録

修正履歴

  • 2019年9月27日修正
    • ##の使用方法について削除と加筆。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?