LoginSignup
2
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-09-14

記事の概要

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を持つレジスタは次のように定義されます。

val q = RegNext(d)

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

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

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

delayReg := delayIn

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

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

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

valReg := inVal

イネーブル付きレジスタ

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

when (enable) { 
  enableReg := inVal 
}

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

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

when (enable) { 
  resetEnableReg := inVal 
}

6.2 Counters

  • 4ビットカウンター
val cntReg = RegInit(0.U(4.W))

cntReg := cntReg + 1.U

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

  • イベント発生時のみカウントするカウンター
val cntEventsReg = RegInit(0.U(4.W)) 
when(event) { 
  cntEventsReg := cntEventsReg + 1.U 
}
  • 最大値に到達した時にカウントリセットするカウンター
val cntReg = RegInit(0.U(8.W))

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

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

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

cntReg := Mux(cntReg === N, 0.U, cntReg + 1.U)
  • カウントダウンするカウンター

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

val cntReg = RegInit(N)

cntReg := cntReg - 1.U 
when(cntReg === 0.U) { 
  cntReg := N 
}
  • カウンター関数

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

// 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エラーを考慮して10にしてあります。

6.2.2 Generating Timing with Counters

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

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

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

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

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

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を引いています。

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を示しています。

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ビットカウンターのイネーブル信号に使用してカウントします。

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章の記事の方法でvivadoからFPGAボードに書き込みます。
制約ファイルと使用している7セグ表示器は5章の記事と同じになります。

関連記事

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

2
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
2
0