前回の記事ではChisel BootcampのModule2.5でFIRフィルタのジェネレータの実装について勉強した。
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のModule
もScalaのclass
を元にしているので、まずは”改めて”パラメタライズということについて考えてみようという意図があるようだ。
実行結果は載せるまで無いとは思うが、以下の様になる。
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
になっている。ParameterizeWidthAdder
のinit
でfailが発生しておりcmd3.scの2行目というが実際の行になる。なお、このcmd3.scはjupyte-notebook上のコードセルに対応しているものになっている。そのため、実際に通常のscalaのファイルとしてモジュールを作っていく場合にはもう少しわかり易くなるはず。
ammonite.$sess.cmd3$Helper$ParameterizedWidthAdder.<init>(cmd3.sc:2)
このrequire
によるチェックはテスターを使ったシミュレーション実行時のassert
とは別の機能となっている。
今日はここまでにして、また次回引き続きModule3.1を見ていくことにする。