FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chiselを使ってMNISTハードウェアアクセラレータを実装(ReorderQueue実装)

前回の続き。TileLinkがデータをインオーダで返してこない件、受信側でデータの並べ替えを担保しなければならない。 これには根本的な実装の変更が必要だなあ。

これまでは、データを受け取った順番に乗算器に流し込んでいた。 これは、リクエストしたデータがそのまま帰ってくるという前提で設計していた。 これを変更しなければならない。このためには、リオーダバッファでデータを並び替えなければならない。

f:id:msyksphinz:20180127012841p:plain

このためには、Chiselで記述されたReorderQueueというものを使わせてもらった。 これは、Tagを使ってバラバラに入ってきたデータを並び替えるためのモジュールらしい。

  • src/main/scala/util/ReorderQueue.scala
class ReorderQueue[T <: Data](dType: T, tagWidth: Int, size: Option[Int] = None)
    extends Module {
  val io = new Bundle {
    val enq = new ReorderEnqueueIO(dType, tagWidth).flip
    val deq = new ReorderDequeueIO(dType, tagWidth)
  }

  val tagSpaceSize = 1 << tagWidth
  val actualSize = size.getOrElse(tagSpaceSize)

  if (tagSpaceSize > actualSize) {
    val roq_data = Reg(Vec(actualSize, dType))
    val roq_tags = Reg(Vec(actualSize, UInt(width = tagWidth)))

Chiselでのモジュールインスタンス方法

例えば、ログを格納するためにブロックRAMを配置するために以下のLoggerRAMモジュールを定義したとする。

class LoggerRAM (DataWidth: Int, Length: Int) extends Module {
  val io = IO(new Bundle {
    val WData_In = Input(UInt(width = DataWidth))
    val WAddr_In = Input(UInt(width = log2Ceil(Length)))
    val We_In    = Input(Bool())

    val RAddr_In = Input(UInt(width = log2Ceil(Length)))
    val Data_Out = Output(UInt(width = DataWidth))
  })

  val memory = Mem(1024, UInt(width = DataWidth))

  when (io.We_In) {
    memory(io.WAddr_In) := io.WData_In
  }

  val w_data_out = Reg(UInt(width = DataWidth))
  w_data_out := memory(io.RAddr_In)

  io.Data_Out := w_data_out
}

この時に、モジュールのインタフェースはioとして定義されている。ここでは、

  • io.WData_In : 入力データ。DataWidth幅
  • io.WAddr_In : 入力データアドレス。
  • io.We_In : 書き込み信号
  • io.RAddr_In : 読み込みデータアドレス
  • io.Data_Out : 読み込みデータ

としている。これらを上位のモジュールで定義するためには以下のようにして定義する。 Verilogではインタフェースを接続するが、Chiselではwireをassignしていくようなイメージだ。

  val ResultRAM = Module (new LoggerRAM(32, 1024))

  ResultRAM.io.WData_In := (r_total + w_result).asUInt()
  ResultRAM.io.We_In    := we_resultram
  ResultRAM.io.WAddr_In := r_result_log_count
  ResultRAM.io.RAddr_In := r_log_addr
  w_result_MemOutput    := ResultRAM.io.Data_Out

これでLogger用のブロックRAMと、ReorderQueueによるデータ整列、さらに固定小数点加算回路をブロック化したものを配置し、シミュレーションと論理合成を行ってみた。

f:id:msyksphinz:20180127014751p:plain

シミュレーションはうまく動作したようだ。次に実機に持っていこう。

f:id:msyksphinz:20180127014822p:plain

うーん、精度は多少良くなったけど、それでもまだオールSWに比べて精度が低い。性能はソフトウェアに比べて大体4倍くらいだ。 どこかにまだ問題があるはずだ。引き続きデバッグしていく。