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

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

Chisel Bootcamp - Module3.1(1) - パラメタライズを使ったジェネレータ

スポンサーリンク

前回の記事ではChisel BootcampのModule2.5でFIRフィルタのジェネレータの実装について勉強した。

www.tech-diningyo.info

Module2.5の残りのトピックがあるのだが、一旦後回しにして今回からModule3.1に入っていく。

Module3からはジェネレータについてより深く学んでいくことになっているようだ。Module3.1の内容は「パラメータ」となる。

Module 3.1: ジェネレータ:パラメータ

モチベーション

これまでと同様にモチベーションから。

コード・ジェネレータになるためのChiselモジュールはどのようにしてその仕事成すべきかということを指定できる仕組みでなければならない。このセクションではモジュールのパラメタライズについてを取り扱い、その中で様々なメソドロジやScalaの仕組みを見ていく。パラメータを受け渡す豊富な機能はそのまま豊富な回路を生成できることに比例する。パラメータは以下の機能を提供するべきだ。

  • 有用なデフォルトパラメータ
  • 容易にパラメータがセットできること
  • 無効な、または意味のない値から保護できること

複雑なシステムにおいて、うっかり他のモジュールに影響を及ぼすことのないパラメータのオーバライドを許可する際にこれらの機能が非常に役に立つものになる。

パラメータの受け渡し

この項では改めて、「Chiselのジェネレータがどのようにパラメータを取得するか」ということについて見ていく

例題:パラメタライズされたScalaのオブジェクト

まずはScalaのクラスでパラメタライズされたオブジェクトを見ていく。

class ParameterizedScalaObject(param1: Int, param2: String) {
  println(s"I have parameters: param1 = $param1 and param2 = $param2")
}
val obj1 = new ParameterizedScalaObject(4,     "Hello")
val obj2 = new ParameterizedScalaObject(4 + 2, "World")

今更、何故??となるかもしれないが、ChiselのModuleScalaclassを元にしているので、まずは”改めて”パラメタライズということについて考えてみようという意図があるようだ。

実行結果は載せるまで無いとは思うが、以下の様になる。

I have parameters: param1 = 4 and param2 = Hello
I have parameters: param1 = 6 and param2 = World

例題:パラメタライズされたChiselのオブジェクト

では、Chiselにおいて”パラメタライズされたオブジェクト”とは??ということになるが、それがModuleになる。

class ParameterizedWidthAdder(in0Width: Int, in1Width: Int, sumWidth: Int) extends Module {
  require(in0Width >= 0)
  require(in1Width >= 0)
  require(sumWidth >= 0)
  val io = IO(new Bundle {
    val in0 = Input(UInt(in0Width.W))
    val in1 = Input(UInt(in1Width.W))
    val sum = Output(UInt(sumWidth.W))
  })
  // a +& b includes the carry, a + b does not
  io.sum := io.in0 +& io.in1
}

println(getVerilog(new ParameterizedWidthAdder(1, 4, 6)))

モジュールの行っている処理自体は単純なものになっている。ただ単に入力に与えられたデータを加算して出力するというだけのものだ。

require

ここで、これまでと異なるのはModuleクラスを継承する際に使用されているrequireになる。

このrequireはエラボレーションの前にチェックされるアサーションでジェネレータが特定のパラメータの中で動作する場合や、排他のパラメータが存在しておりそれをチェックする場合、意味のないパラメータをチェックする場合に有用な文法となる。

上記をそのまま実行すると、以下の様に何事もなくエラボレーションが終了しモジュールが定義される。

[info] [0.001] Elaborating design...
[info] [0.683] Done elaborating.
Total FIRRTL Compile Time: 246.8 ms
module cmd3HelperParameterizedWidthAdder( // @[:@3.2]
  input        clock, // @[:@4.4]
  input        reset, // @[:@5.4]
  input        io_in0, // @[:@6.4]
  input  [3:0] io_in1, // @[:@6.4]
  output [5:0] io_sum // @[:@6.4]
);
  wire [3:0] _GEN_0; // @[cmd3.sc 11:20:@8.4]
  wire [4:0] _T_11; // @[cmd3.sc 11:20:@8.4]
  assign _GEN_0 = {{3'd0}, io_in0}; // @[cmd3.sc 11:20:@8.4]
  assign _T_11 = _GEN_0 + io_in1; // @[cmd3.sc 11:20:@8.4]
  assign io_sum = {{1'd0}, _T_11};
endmodule

では、ジェネレータでモジュールをインスタンスする際のパラメータをrequire文の条件を満たさないものに変更してみよう。

require文の条件を満たさない場合

今回の説明用のモジュールは入力/出力のそれぞれがビット幅を指定できるようになっており、それぞれrequire文でのチェックが行われている。

そのため、どれかのパラメータを0以下にするとrequireの条件を満たせない。

println(getVerilog(new ParameterizedWidthAdder(-1, 4, 6)))

このコードを実行すると、以下の様に例外が発生して終了する。

[info] [0.000] Elaborating design...

java.lang.IllegalArgumentException: requirement failed
  scala.Predef$.require(Predef.scala:212)
  ammonite.$sess.cmd3$Helper$ParameterizedWidthAdder.<init>(cmd3.sc:2)
  ammonite.$sess.cmd4$Helper$$anonfun$1.apply(cmd4.sc:1)
  ammonite.$sess.cmd4$Helper$$anonfun$1.apply(cmd4.sc:1)
  chisel3.core.Module$.do_apply(Module.scala:49)
  chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:93)
  chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:93)
  chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:297)
  chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:295)
  scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
  chisel3.internal.Builder$.build(Builder.scala:295)
  chisel3.Driver$.elaborate(Driver.scala:93)
  chisel3.Driver$.execute(Driver.scala:140)
  chisel3.Driver$.execute(Driver.scala:202)
  ammonite.$file.dummy.source.load$minusivy$Helper.getVerilog(Main.sc:9)
  ammonite.$sess.cmd4$Helper.<init>(cmd4.sc:1)
  ammonite.$sess.cmd4$.<init>(cmd4.sc:7)
  ammonite.$sess.cmd4$.<clinit>(cmd4.sc:-1)

色々メッセージが出ているし、jupyte-notebookで走らせているため行数の対応が取りにくくわかりにくいが、以下の部分が発火したrequireになっている。ParameterizeWidthAdderinitでfailが発生しておりcmd3.scの2行目というが実際の行になる。なお、このcmd3.scはjupyte-notebook上のコードセルに対応しているものになっている。そのため、実際に通常のscalaのファイルとしてモジュールを作っていく場合にはもう少しわかり易くなるはず。

ammonite.$sess.cmd3$Helper$ParameterizedWidthAdder.<init>(cmd3.sc:2)

このrequireによるチェックはテスターを使ったシミュレーション実行時のassertとは別の機能となっている。

今日はここまでにして、また次回引き続きModule3.1を見ていくことにする。