ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

Chiselのハードウェアを型のインスタンスに変換(chiselTypeOf)

スポンサーリンク

Chiselのutil以下に存在するモジュールに自分のモジュールのIOを接続しようとした時に遭遇したエラーとその解決法であるchiselTypeOfについて簡単にまとめておく。

QueueにモジュールのIOをつなぐときにエラー

ざっくりと以下のようなコードを作成してエラボレートしたところエラーが発生した。

import chisel3._
import chisel3.util._

class MyIO extends Bundle {
  val strb = UInt(4.W)
  val data = UInt(32.W)
}

class MyModule extends Module {
  val io = IO(new Bundle {
    val in = Flipped(Decoupled(new MyIO))
    val out = Decoupled(new MyIO)
  })

  val q = Module(new Queue(io.in.bits, 4)) // L.53:ここがエラー発生行

  q.io.deq <> io.out
}

発生するエラーの内容は以下のようなもの

[info] [0.002] Elaborating design...
[error] (run-main-0) chisel3.core.Binding$ExpectedChiselTypeException: 'MyIO@37' must be a Chisel type, not hardware
[error] chisel3.core.Binding$ExpectedChiselTypeException: 'MyIO@37' must be a Chisel type, not hardware
[error]     at chisel3.core.requireIsChiselType$.apply(Binding.scala:42)
[error]     at chisel3.util.Queue.<init>(Decoupled.scala:203)
[error]     at MyModule$$anonfun$2.apply(QueueError.scala:53)
[error]     at MyModule$$anonfun$2.apply(QueueError.scala:53)
[error]     at chisel3.core.Module$.do_apply(Module.scala:49)
[error]     at MyModule.<init>(QueueError.scala:53)
[error]     at ElaborateTypeError$$anonfun$3.apply(QueueError.scala:63)
[error]     at ElaborateTypeError$$anonfun$3.apply(QueueError.scala:63)

エラーの内容としては「new Queueの第一引数に指定するのはChiselのハードウェアではなく型でなければならない」というもの。 確かにio.in.bitsはChiselのIOでくるまれているため、Chiselのハードウェアになっている。

対処法1:MyIOのインスタンスを渡す

ということはここの宣言をChiselのハードウェアではなく型にしてあげればいいので、以下のような修正が考えれられる。

class MyModule extends Module {
  val io = IO(new Bundle {
    val in = Flipped(Decoupled(new MyIO))
    val out = Decoupled(new MyIO)
  })

  val q = Module(new Queue(new MyIO, 4)) // MyIOのインスタンスを渡す。

  q.io.enq <> io.in // インスタンスを渡しただけなので別途接続が必要
  io.out <> q.io.deq
}

上記の様に修正してやると、エラボレートは下記のように正常にPASSするようになる。

[info] Running ElaborateTypeError
[info] [0.001] Elaborating design...
[info] [0.899] Done elaborating.
Total FIRRTL Compile Time: 510.0 ms

対処法2:chiselTypeOfを使う

でもなんかわざわざ元の型を指定してやるのが野暮ったい。 そう思って再度ChiselのQueueのソースコードを眺めていたところ、Queueのclassではなくobjectの方に答えが載っていた。。。 その答えというのがchiselTypeOfというChiselのハードウェアを型のインスタンスに変換する処理だ。

  • chisel3/core/Data.scala : L.149 @ Chisel 3.1.7
/** Returns the chisel type of a hardware object, allowing other hardware to be constructed from it.
  */
object chiselTypeOf {
  def apply[T <: Data](target: T): T = {
    requireIsHardware(target)
    target.cloneTypeFull.asInstanceOf[T]
  }
}

やってる内容自体は割と単純で

  1. 引数のtargetがChiselのハードウェアであるかをチェック
  2. cloneTypeFull.asInstanceOfで変換

となっている。

早速先ほどのコードに適用してみよう。

class MyModule extends Module {
  val io = IO(new Bundle {
    val in = Flipped(Decoupled(new MyIO))
    val out = Decoupled(new MyIO)
  })

  val q = Module(new Queue(chiselTypeOf(io.in.bits), 4))

  q.io.enq <> io.in
  io.out <> q.io.deq
}
  • エラボレート結果
[info] Done packaging.
[info] Running ElaborateTypeError
[info] [0.001] Elaborating design...
[info] [0.889] Done elaborating.
Total FIRRTL Compile Time: 516.2 ms

正常にエラボレートが通った。 ということで何かChsielのハードウェアから別のインスタンスを作りたいなーという時にはchiselTypeOfを使ってやるのが簡単でいい、という話でした。 通常はオブジェクトのapplyを使って作ることが多いのでそれに倣ってればハマらなかったってことなんだけど、これの存在に気づけたのでよしとしよう。。