前回のChiselの記事ではChisel-Bootcampを進めてModule3.6のジェネリクス型を学んでいった。
今回はChiselのTips的な話で、ネタになるのは自分がChiselを使って実装をしていた際に出くわしたVerilog HDLで実装されたモジュールをブラックボックスとして接続する方法についてだ。
Chiselのモジュールのブラックボックス化
まずはBlackBox
の紹介から始めて行く。これはChiselのWikiにも記載されているので、そちらも必要に応じて参照してもらうとよいです。
あとmsyksphinzさんのFPGA開発日記の方でも既に取り上げてくださってますのでこちらもどうぞ。
埋め込むサンプルのVerilog HDLコード
まずは先に今回の記事でサンプルとして使用する埋め込みたいVerilogのデザインを準備する。 今回は簡単にするために、以下のように入力したデータが加算されるだけのモジュール。 ファイル名はそのまま"adder.v"としてます。
module AdderBB #( parameter in0_bits = 'd10 ,parameter in1_bits = 'd10 ,parameter out_bits = in0_bits + 1 ) ( input clk ,input rst_n ,input [in0_bits-1:0] in0 ,input [in1_bits-1:0] in1 ,output reg [out_bits-1:0] out ); always @(posedge clk or negedge rst_n) begin if (!rst_n) begin out <= 'h0; end else begin out <= in0 + in1; end end endmodule
BlackBox
を使ったブラックボックス化
ChiselではBlackBox
というクラスを継承してモジュールを作成すると、そのクラスをブラックボックス化することが出来る。
クラス名がそのままズバリBlackBox
。
モジュールの作り方は通常のモジュールと全く一緒で以下のようにすればOK。
class <モジュール名> extends BlackBox(Map[String, Param]) { val io = IO(<IOをインスタンスする>) }
注意点としてはBlackBox
の内部ではクロックとリセットが定義されないため、クロック/リセットが必要な場合は
明示的にIO()
の中にそれらを含めなければならない。クロックは専用のオブジェクトが用意されているのでそれを使う。
- クロック →
Clock()
→ 入力ポートならInput(Clock())
- リセット → リセットは通常の
Bool()
でOK →Input(Bool())
ブラックボックス化の示す通りBlackBox
を継承して作ったモジュール自体はIOのみが定義されて、中は完全に空のモジュールになる。
そのためBlackBox
を継承したモジュール内部でChiselのハードウェア記述を行うとエラボレートの際にエラーになる。
class AdderBB(in0Bits: Int, in1Bits: Int) extends BlackBox(Map( "in0_bits" -> IntParam(in0Bits), "in1_bits" -> IntParam(in1Bits), "out_bits" -> IntParam(in0Bits+1))) { val io = IO(new Bundle { val clk = Input(Clock()) val rst_n = Input(Bool()) val in0 = Input(UInt(in0Bits.W)) val in1 = Input(UInt(in0Bits.W)) val out = Output(UInt((in0Bits+1).W)) }) // io.out := io.in0 + io.in1 // この行を有効にするとエラーが発生! }
その際のエラーは以下のようなものだ。 要はChiselのユーザーモジュールではないよーということか。
[info] [0.001] Elaborating design... [error] (run-main-2) chisel3.internal.ChiselException: Error: Not in a UserModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox. 〜長いので省略〜 [error] at chisel3.core.Bits.binop(Bits.scala:175) [error] at chisel3.core.UInt.do_$plus$amp(Bits.scala:462) [error] at chisel3.core.UInt.do_$plus$percent(Bits.scala:464) [error] at chisel3.core.UInt.do_$plus(Bits.scala:444) // ユーザーモジュールじゃないのに足し算がある。というのが理由 [error] at AdderBB.<init>(Top.scala:14) // エラーの発生元は14行目(上記コードのio.out := ...の行) 〜長いので省略〜
Verilogのパラメータの伝搬
Verilogのモジュールインスタンス時にパラメータを指定するようなケースがあると思うが冒頭の作り方や例の部分で書いているように
継承したBlackBox
のパラメータにMap
を入れることでこれにも対応が可能だ。
その部分だけを切り出したのが以下の部分だ。
class AdderBB(in0Bits: Int, in1Bits: Int) extends BlackBox(Map( "in0_bits" -> IntParam(in0Bits), // Verilogのパラメータを必要な分だけ "in1_bits" -> IntParam(in1Bits), // "パラメータ名" -> Param(パラメータ) "out_bits" -> IntParam(in0Bits+1))) { // で定義
実はこの部分は記事を書いている2019/02/22時点ではWikiの記述と違っている。最初はWikiの記載のとおりにMap
の中身のタプル指定を[String, String]
で指定したのだが以下のように要求される型が違うというエラーが発生した。
[error] /media/diningyo/5B14-9AF3/blackbox/src/main/scala/Top.scala:6:37: type mismatch; [error] found : String("in0Bits") [error] required: chisel3.core.Param [error] extends BlackBox(Map("in0Bits" -> "in0Bits",
そこで現在のChiselのBlackBox
の実装を確認してみたのだが以下のように[String, Params]
に変更されていた。
#なお手元の環境のChiselは3.1.6
abstract class BlackBox( val params: Map[String, Param] = Map.empty[String, Param]) // Mapの指定は[String, Params] (implicit compileOptions: CompileOptions) extends BaseBlackBox {
またこのParams
は以下のような定義になっている。
/** Parameters for BlackBoxes */ sealed abstract class Param case class IntParam(value: BigInt) extends Param case class DoubleParam(value: Double) extends Param case class StringParam(value: String) extends Param /** Unquoted String */ case class RawParam(value: String) extends Param
ベースをsealed abstract class Param
としてInt
/Double
/String
/Raw
の4つが準備されている。
Paramsの種類 | Paramsの引数の型 | Verilogのパラメータのマッピング | 例 |
---|---|---|---|
IntParam | Int | 整数 | "A" -> IntParams(10) => .A(10) |
DoubleParam | Double | 浮動小数点 | "A" -> DoubleParams(10.toDouble) => .A(10.0) |
StringParam | String | 文字列 | "A" -> StringParam("a") => .A("a") |
RawParam | String | 文字列 | "A" -> RawParam("a") => .A(a) |
ただ単にブラックボックス化したいだけなら、これでも十分使える。
このBlackBox
を使ったモジュールをインスタンスしたChiselコードを見てみよう。
#今回はあえてこれ単体でRTLの生成も可能な完全なコードを載せてみる(あんまり見かけなかったから。。)
class Top(in0Bits: Int, in1Bits: Int) extends Module { val io = IO(new Bundle { val in0 = Input(UInt(in0Bits.W)) val in1 = Input(UInt(in0Bits.W)) val out = Output(UInt((in0Bits + 1).W)) }) val adder = Module(new AdderBB(in0Bits, in1Bits)) adder.io.in0 := io.in0 adder.io.in1 := io.in1 io.out := adder.io.out adder.io.clk := clock adder.io.rst_n := !reset.asTypeOf(Bool()) // reset != Boolなので変換しないとダメ } // エラボレート用のメイン object Elaborate extends App { chisel3.Driver.execute(args, () => new Top(32, 32)) }
上記のコードを実行すると生成されるブラックボックス化したモジュールのインスタンス部分のRTLは以下のようになる。
IOポートの名前は通常のModule
を使った場合とは異なり、IO()
内で宣言した変数名がそのまま使用される。
module Top( // @[:@13.2] input clock, // @[:@14.4] input reset, // @[:@15.4] input [31:0] io_in0, // @[:@16.4] input [31:0] io_in1, // @[:@16.4] output [32:0] io_out // @[:@16.4] ); wire [32:0] AdderBB_out; // @[Top.scala 34:23:@18.4] wire [31:0] AdderBB_in1; // @[Top.scala 34:23:@18.4] wire [31:0] AdderBB_in0; // @[Top.scala 34:23:@18.4] wire AdderBB_rst_n; // @[Top.scala 34:23:@18.4] wire AdderBB_clk; // @[Top.scala 34:23:@18.4] // ここがインスタンス部分 AdderBB AdderBB ( // @[Top.scala 34:23:@18.4] .out(AdderBB_out), .in1(AdderBB_in1), .in0(AdderBB_in0), .rst_n(AdderBB_rst_n), .clk(AdderBB_clk) ); assign io_out = AdderBB_out; // @[Top.scala 37:12:@26.4] assign AdderBB_in1 = io_in1; // @[Top.scala 36:18:@25.4] assign AdderBB_in0 = io_in0; // @[Top.scala 35:18:@24.4] assign AdderBB_rst_n = reset; // @[Top.scala 39:20:@28.4] assign AdderBB_clk = clock; // @[Top.scala 38:18:@27.4] endmodule
因みにBlackBox
と似たようなモジュールにExtModule
ってのもある。
こちらを使うとIO()
を複数個宣言することが可能になるが、それ以外には特に差分は無いっぽいので、あるよーというお知らせだけ。今後使ってて何か発見があれば別の記事で書くかも。
ブラックボックス化したモジュールを埋め込む方法
既存のVerilog HDLコードをブラックボックスとして埋めるという点だけみれば、上記のBlackBox
を使ったモジュールのインスタンスを方法を知っていればそれでOKだったりもする。要はブラックボックス部分以外はChiselで生成したRTLにしておいて、実際のシミュレータ環境側でブラックボックス部分のRTLを別途読みこめばそれでよい。
一方でChiselにはiotester
をいうモジュールを使うことで、Scala&Chiselで書いたテストコードをそのままFIRRTLレベルや他のシミュレータ(普通はverilatorになる)を使ってテストすることが可能だ。
そのような場合にこのブラックボックスとして定義したモジュールの中身をiotester
に認識させるための仕組みが用意されているので、ここからはそれを見ていこうと思う。
まずはテストコードの準備
ということで、まずはテスト用のメインオブジェクトを準備する。 こんなの↓
object Test extends App { chisel3.iotesters.Driver.execute( args, () => new Top(32, 32)) { c => new chisel3.iotesters.PeekPokeTester(c) { for (i <- 0 until 10) { val in0 = i val in1 = i * 2 poke(c.io.in0, in0) poke(c.io.in1, in1) expect(c.io.out, in0 + in1) } } } }
iotester.Driver
を使ってテストを実行する形。テストの中身は今回はどうでも良いので適当にfor
ループ回して1step後の出力を期待値と比較して確認するだけのものにした。
そして先ほどBlackBox
を内部でインスタンスして作ったTop
モジュールのコードに上記のテストコードを追加してverilatorで実行してみる。
$ sbt "runMain Test --backend-name verilator" [info] Loading global plugins from /home/diningyo/.sbt/1.0/plugins [info] Loading project definition from /home/diningyo/prj/study/2000_chisel/blackbox/project [info] Loading settings for project blackbox from build.sbt ... [info] Set current project to chisel-module-template (in build file:/home/diningyo/prj/study/2000_chisel/blackbox/) [warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list [info] Running Test --backend-name verilator [info] [0.002] Elaborating design... [info] [0.774] Done elaborating. Total FIRRTL Compile Time: 302.7 ms cd /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931 && verilator --cc Top.v --assert -Wno-fatal -Wno-WIDTH -Wno-STMTDLY -O1 --top-module Top +define+TOP_TYPE=VTop +define+PRINTF_COND=!Top.reset +define+STOP_COND=!Top.reset -CFLAGS "-Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h" -Mdir /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931 --exe /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/Top-harness.cpp --trace %Error: Top.v:13: Cannot find file containing module: AdderBB %Error: Top.v:13: This may be because there's no search path specified with -I<dir>. %Error: Top.v:13: Looked in: %Error: Top.v:13: AdderBB %Error: Top.v:13: AdderBB.v %Error: Top.v:13: AdderBB.sv %Error: Top.v:13: /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/AdderBB %Error: Top.v:13: /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/AdderBB.v %Error: Top.v:13: /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/AdderBB.sv %Error: Exiting due to 9 error(s) %Error: Command Failed /usr/bin/verilator_bin --cc Top.v --assert -Wno-fatal -Wno-WIDTH -Wno-STMTDLY -O1 --top-module Top '+define+TOP_TYPE=VTop' '+define+PRINTF_COND=!Top.reset' '+define+STOP_COND=!Top.reset' -CFLAGS '-Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h' -Mdir /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931 --exe /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/Top-harness.cpp --trace [error] (run-main-0) java.lang.AssertionError: assertion failed: [error] java.lang.AssertionError: assertion failed: [error] at scala.Predef$.assert(Predef.scala:170) [error] at chisel3.core.assert$.apply(Assert.scala:76) [error] at chisel3.iotesters.setupVerilatorBackend$.apply(VerilatorBackend.scala:262) [error] at chisel3.iotesters.Driver$$anonfun$execute$1$$anonfun$apply$mcZ$sp$1.apply$mcZ$sp(Driver.scala:56) [error] at chisel3.iotesters.Driver$$anonfun$execute$1$$anonfun$apply$mcZ$sp$1.apply(Driver.scala:39) [error] at chisel3.iotesters.Driver$$anonfun$execute$1$$anonfun$apply$mcZ$sp$1.apply(Driver.scala:39) [error] at logger.Logger$$anonfun$makeScope$1.apply(Logger.scala:138) [error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) [error] at logger.Logger$.makeScope(Logger.scala:136) [error] at chisel3.iotesters.Driver$$anonfun$execute$1.apply$mcZ$sp(Driver.scala:39) [error] at chisel3.iotesters.Driver$$anonfun$execute$1.apply(Driver.scala:39) [error] at chisel3.iotesters.Driver$$anonfun$execute$1.apply(Driver.scala:39) [error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58) [error] at chisel3.iotesters.Driver$.execute(Driver.scala:38) [error] at chisel3.iotesters.Driver$.execute(Driver.scala:100) [error] at Test$.delayedEndpoint$Test$1(Top.scala:65) [error] at Test$delayedInit$body.apply(Top.scala:63) [error] at scala.Function0$class.apply$mcV$sp(Function0.scala:34) [error] at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) [error] at scala.App$$anonfun$main$1.apply(App.scala:76) [error] at scala.App$$anonfun$main$1.apply(App.scala:76) [error] at scala.collection.immutable.List.foreach(List.scala:392) [error] at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35) [error] at scala.App$class.main(App.scala:76) [error] at Test$.main(Top.scala:63) [error] at Test.main(Top.scala) [error] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [error] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) [error] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [error] at java.lang.reflect.Method.invoke(Method.java:498) [error] Nonzero exit code: 1 [error] (Compile / runMain) Nonzero exit code: 1 [error] Total time: 3 s, completed 2019/03/17 23:12:46
まあ、なんか盛大に怒られるんですよ。 ただエラーの内容はとても単純でverilatorで実行しようとした時に
- モジュール"AdderBB"が見つからなかった
というもの。 verilatorは親切にも"モジュール名.[v|sv]"といった名称のファイルの探索までやってくれたうえでエラーになってるのが分かると思います。 なので、上記の探索パス部分にそのファイルがあればシミュレーションは実行可能だったりします。上記の例だと、
- /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/AdderBB.v
が存在していればシミュレーションは正常に実施できます。
Chiselの機能を使って解決
でも冒頭に書いたとおり、こんなことしなくても良い仕組みがちゃんと準備されてるので、それを使ってスマートに解決してみましょう! #上記の方法だと、シミュレーション実行するまでverilatorのディレクトリも出来ないしね。
実際にどうするかというと、以下の2つの方法があります。
1. BlackBox
にVerilog HDLのコードを埋め込む(setInline
)
この場合はtrait HasBlackBoxInline
をミックスインすると使用可能になるメソッドsetInline
を使用する。
ということで先のAdderBB
を以下の様に書き換える。
class AdderBB(in0Bits: Int, in1Bits: Int) extends BlackBox(Map( "in0_bits" -> IntParam(in0Bits), "in1_bits" -> IntParam(in1Bits), "out_bits" -> IntParam(in0Bits+1))) with HasBlackBoxInline { val io = IO(new Bundle { val clk = Input(Clock()) val rst_n = Input(Bool()) val in0 = Input(UInt(in0Bits.W)) val in1 = Input(UInt(in0Bits.W)) val out = Output(UInt((in0Bits+1).W)) }) setInline("adder.v", s""" |module AdderBB | #( | parameter in0_bits = 'd10 | ,parameter in1_bits = 'd10 | ,parameter out_bits = in0_bits + 1 | ) | ( | input clk | ,input rst_n | ,input [in0_bits-1:0] in0 | ,input [in1_bits-1:0] in1 | ,output reg [out_bits-1:0] out | ); | | always @(posedge clk or negedge rst_n) begin | if (!rst_n) begin | out <= 'h0; | end | else begin | out <= in0 + in1; | end | end |endmodule """.stripMargin) }
上記の様に変更したあとに再度バックエンドをverilatorにしてシミュレーションを実行すると以下のようにテストがパスするようになる。
[info] Loading global plugins from /home/diningyo/.sbt/1.0/plugins [info] Loading project definition from /home/diningyo/prj/study/2000_chisel/blackbox/project [info] Loading settings for project blackbox from build.sbt ... [info] Set current project to chisel-module-template (in build file:/home/diningyo/prj/study/2000_chisel/blackbox/) [warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list [info] Running Test --backend-name verilator [info] [0.002] Elaborating design... [info] [0.812] Done elaborating. Total FIRRTL Compile Time: 311.4 ms cd /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931 && verilator --cc Top.v --assert -Wno-fatal -Wno-WIDTH -Wno-STMTDLY -O1 --top-module Top +define+TOP_TYPE=VTop +define+PRINTF_COND=!Top.reset +define+STOP_COND=!Top.reset -CFLAGS "-Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h" -Mdir /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931 -f /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/black_box_verilog_files.f --exe /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/Top-harness.cpp --trace make: ディレクトリ '/home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931' に入ります g++ -I. -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_TRACE=1 -DVM_COVERAGE=0 -Wno-char-subscripts -Wno-parentheses-equality -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h -c -o Top-harness.o /home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931/Top-harness.cpp g++ -I. -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_TRACE=1 -DVM_COVERAGE=0 -Wno-char-subscripts -Wno-parentheses-equality -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h -c -o verilated.o /usr/share/verilator/include/verilated.cpp g++ -I. -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_TRACE=1 -DVM_COVERAGE=0 -Wno-char-subscripts -Wno-parentheses-equality -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h -c -o verilated_vcd_c.o /usr/share/verilator/include/verilated_vcd_c.cpp /usr/bin/perl /usr/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include VTop.cpp > VTop__ALLcls.cpp /usr/bin/perl /usr/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include VTop__Trace.cpp VTop__Syms.cpp VTop__Trace__Slow.cpp > VTop__ALLsup.cpp g++ -I. -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_TRACE=1 -DVM_COVERAGE=0 -Wno-char-subscripts -Wno-parentheses-equality -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h -c -o VTop__ALLcls.o VTop__ALLcls.cpp g++ -I. -MMD -I/usr/share/verilator/include -I/usr/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_TRACE=1 -DVM_COVERAGE=0 -Wno-char-subscripts -Wno-parentheses-equality -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VTop -DVL_USER_FINISH -include VTop.h -c -o VTop__ALLsup.o VTop__ALLsup.cpp Archiving VTop__ALL.a ... ar r VTop__ALL.a VTop__ALLcls.o VTop__ALLsup.o ranlib VTop__ALL.a g++ Top-harness.o verilated.o verilated_vcd_c.o VTop__ALL.a -o VTop -lm -lstdc++ 2>&1 | c++filt make: ディレクトリ '/home/diningyo/prj/study/2000_chisel/blackbox/test_run_dir/Test107190931' から出ます sim start on diningyo-pc at Sun Mar 17 23:48:43 2019 inChannelName: 00014751.in outChannelName: 00014751.out cmdChannelName: 00014751.cmd STARTING test_run_dir/Test107190931/VTop [info] [0.002] SEED 1552834121357 Enabling waves.. Exit Code: 0 [info] [0.023] RAN 10 CYCLES PASSED ## 全部の期待値が一致した [success] Total time: 4 s, completed 2019/03/17 23:48:43
HasBlackBoxInline
を使ってブラックボックスの中身を解決した際にはverilatorの実行ディレクトリに以下の2つのファイルが生成される。
setInline
の第一引数で指定したファイル:中身は第二引数に記載したVerilog HDLのコード- black_box_verilog_files.fというファイルパスが記載されたファイル
- このファイルがverilator実行時に読み込まれ、ブラックボックスのモジュールの実体が解決される
1. BlackBox
にVerilog HDLのパスを埋め込む(setResource
)
こちらはVeilog HDLのコードを埋める代わりに、ファイルへのパスを埋め込んでブラックボックスモジュールの依存関係を解決する方法。
先ほどのtrait HasBlackBoxInline
の代わりにtrait HasBlackBoxResource
をミックスインしてsetResource
メソッドにファイルパスを引き渡す。
こちらを使った例が以下。
class AdderBB(in0Bits: Int, in1Bits: Int) extends BlackBox(Map( "in0_bits" -> IntParam(in0Bits), "in1_bits" -> IntParam(in1Bits), "out_bits" -> IntParam(in0Bits+1))) with HasBlackBoxResource { val io = IO(new Bundle { val clk = Input(Clock()) val rst_n = Input(Bool()) val in0 = Input(UInt(in0Bits.W)) val in1 = Input(UInt(in0Bits.W)) val out = Output(UInt((in0Bits+1).W)) }) setResource("/adder.v") } class AdderEM(in0Bits: Int, in1Bits: Int) extends ExtModule(Map( "in0_bits" -> IntParam(in0Bits), "in1_bits" -> IntParam(in1Bits), "out_bits" -> IntParam(in0Bits+1))) { val clk = IO(Input(Clock())) val rst_n = IO(Input(Bool())) val io = IO(new Bundle { val in0 = Input(UInt(in0Bits.W)) val in1 = Input(UInt(in0Bits.W)) val out = Output(UInt((in0Bits+1).W)) }) }
setResource
メソッドに指定するパスは"src/{main, test}/resources"の下に置くことが決まりとなっており、ファイル探索に置いてもこのディレクトリ以下が探索対象となる。
つまり上記の様に"/adder.v"を指定した場合にはファイルは"src/main/resources/AdderBB.v"に配置する必要がある。
あと、ファイルパス指定の"/adder.v"の"/"は必須、無いと以下のようなエラーが発生する
[error] (run-main-0) firrtl.transforms.BlackBoxNotFoundException: BlackBox 'AdderBB.v' not found. Did you misspell it? Is it in src/{main,test}/resources?
またsetInline
を使う方法、setResource
を使う方法のどちらともファイル名は任意の名前を付けることができるが、Verilog HDLのモジュール名はChiselでBlackBox
を継承したモジュールと同名である必要があるので注意が必要だ。
テストの実行結果はsetInline
を使った場合と一緒なので割愛して、これでBlackBoxの使い方まとめはお終い。