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

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

ChiselのBlackboxで複数のファイルから構成されるVerilog-HDLのモジュールを読み込む

スポンサーリンク

ふと気になったのでChiselのBlackboxで複数のファイルから構成されたVerilogのRTLを指定できるのかを試してみた。

Blackboxのおさらい

Chiselには既存のVerilog-HDLのRTLをModuleに組み込む方法としてBlackboxが存在している。 これを使用することでChiselのモジュールのクラス名と一致するRTLのモジュールをChiselのモジュールでインスタンスして使うことが可能だ。

簡単に使い方をおさらいしておく。

module Test
  (
    input in,
    output out
  );

  assign out = in;
endmodule

上記のTestモジュールを含むファイルをChiselのプロジェクトの以下の場所に保存しておく。

  • src/main/resources/Test.v

TestモジュールをChiselのモジュール内でインスタンスして使う場合にはBlackboxを継承したクラスを作成してそのクラスを通常のモジュールと同様にインスタンスすればいい。

  • Testを使用するChiselモジュール
class Test extends Blackbox with HasBlackBoxResource {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  // setResourceにファイルを指定することで、シミュレーション実行時に"Test.v"が
  // テストの実行時に使用されるようになる。
  setResource("/Test.v") // "/"が無いとエラー
}

class Top extends Module {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  // Blackboxで作成したラッパークラスをインスタンス
  val m_test = Module(new Test)

  // 通常のモジュールと同様に接続
  m_test.io.in := io.in
  io.out := m_test.io.out
}

Blackboxで指定するモジュールが複数のファイルで構成される場合

FPGAで使用するようなRAM推定のファイルならひとつのファイルにひとつのモジュールが書かれていることが多いので、setResourceに指定するファイルは自ずと一つになる。

では、既存の資産を流用するケースで複数のRTLファイルから構成されるモジュールをBlackboxモジュールに指定してChiselのモジュール内で使用するにはどうればいいのだろうか?ということを考えたことが無かったので試してみた。

文章で書くといまいちイメージしにくいかもしれないが例えば以下のような感じのことを指している。

  • a.sv : module aが実装されている
  • b.sv : module bが実装されている

というケースに於いて、モジュール"a"/"b"が以下のように階層化されているような場合だ。

  • a.svのモジュール"a"
module a
    (
        input in,
        output out
    );

    b b (.in(in), .out(out));
endmodule : a
  • b.svのモジュール"b"
module b
    (
        input in,
        output out
    );

    assign out = in;
endmodule : b

この場合にモジュール"a"をChiselのBlackboxに指定した際に"a.sv"/"b.sv"の2つのファイルが必要になるのだが、setResourceの指定はどうすればいいかについて幾つか試してみる。

まずは"a.sv"のみを指定。

class a extends BlackBox with HasBlackBoxResource {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  setResource("/a.sv")
}

class BlackBoxTop extends Module {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  val m_a = Module(new a)

  m_a.io.in := io.in
  io.out := m_a.io.out
}

テストを作ってverilatorで上記のモジュールをテストすると以下のようにエラーが発生する。エラーの中身はモジュール"b"が実装されているファイルが見つからない、という内容だ。

Total FIRRTL Compile Time: 465.0 ms
cd /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop && verilator --cc BlackBoxTop.v --assert -Wno-fatal -Wno-WIDTH -Wno-STMTDLY -O1 --top-module BlackBoxTop +define+TOP_TYPE=VBlackBoxTop +define+PRINTF_COND=!BlackBoxTop.reset +define+STOP_COND=!BlackBoxTop.reset -CFLAGS "-Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VBlackBoxTop -DVL_USER_FINISH -include VBlackBoxTop.h" -Mdir /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop -f /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/firrtl_black_box_resource_files.f --exe /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/BlackBoxTop-harness.cpp --trace
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7: Cannot find file containing module: b
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7: This may be because there's no search path specified with -I<dir>.
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7: Looked in:
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7:       b
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7:       b.v
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7:       b.sv
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7:       /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/b
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7:       /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/b.v
%Error: /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/a.sv:7:       /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/b.sv
%Error: Exiting due to 9 error(s)
%Error: Command Failed /usr/local/bin/verilator_bin --cc BlackBoxTop.v --assert -Wno-fatal -Wno-WIDTH -Wno-STMTDLY -O1 --top-module BlackBoxTop \+define\+TOP_TYPE\=VBlackBoxTop \+define\+PRINTF_COND\=\!BlackBoxTop.reset \+define\+STOP_COND\=\!BlackBoxTop.reset -CFLAGS -Wno-undefined-bool-conversion\ -O1\ -DTOP_TYPE\=VBlackBoxTop\ -DVL_USER_FINISH\ -include\ VBlackBoxTop.h -Mdir /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop -f /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/firrtl_black_box_resource_files.f --exe /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/BlackBoxTop-harness.cpp --trace

Chiselのテストフレームワークのシミュレータのバックエンドとしてverilatorを指定した場合に、モジュール内にsetResourceが使用されると、シミュレーションの実行ディレクトリに"firrtl_black_box_resource_files.f"というファイルが出来ており、この中にsetResourceで指定したファイルのパスが記載され、モジュールの解決が行われる仕組みになっており、今回のケースだと"a.sv"のみが指定されたため、ファイルが見つからないということになっている。

ここまで書くともう察した方もいるだろうが、解決方法は単純で必要なファイルを全てsetResourceを使って指定してやればOKだ。

先ほどのモジュール"a"の場合はBlackboxを継承したクラスの実装は以下のようになる。

class a extends BlackBox with HasBlackBoxResource {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  setResource("/a.sv")
  setResource("/b.sv") // "b.sv"も追加する
}

この状態でテストを再度実行すると以下のようにVerilatorのエラーは発生せずにシミュレーションが開始される。

Total FIRRTL Compile Time: 425.4 ms
cd /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop && verilator --cc BlackBoxTop.v --assert -Wno-fatal -Wno-WIDTH -Wno-STMTDLY -O1 --top-module BlackBoxTop +define+TOP_TYPE=VBlackBoxTop +define+PRINTF_COND=!BlackBoxTop.reset +define+STOP_COND=!BlackBoxTop.reset -CFLAGS "-Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VBlackBoxTop -DVL_USER_FINISH -include VBlackBoxTop.h" -Mdir /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop -f /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/firrtl_black_box_resource_files.f --exe /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/BlackBoxTop-harness.cpp --trace
make: ディレクトリ '/home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop' に入ります
g++  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=1 -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow     -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VBlackBoxTop -DVL_USER_FINISH -include VBlackBoxTop.h   -c -o BlackBoxTop-harness.o /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop/BlackBoxTop-harness.cpp
g++  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=1 -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow     -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VBlackBoxTop -DVL_USER_FINISH -include VBlackBoxTop.h   -c -o verilated.o /usr/local/share/verilator/include/verilated.cpp
g++  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=1 -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow     -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VBlackBoxTop -DVL_USER_FINISH -include VBlackBoxTop.h   -c -o verilated_vcd_c.o /usr/local/share/verilator/include/verilated_vcd_c.cpp
/usr/bin/perl /usr/local/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include VBlackBoxTop.cpp > VBlackBoxTop__ALLcls.cpp
/usr/bin/perl /usr/local/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include VBlackBoxTop__Trace.cpp VBlackBoxTop__Syms.cpp VBlackBoxTop__Trace__Slow.cpp > VBlackBoxTop__ALLsup.cpp
g++  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=1 -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow     -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VBlackBoxTop -DVL_USER_FINISH -include VBlackBoxTop.h   -c -o VBlackBoxTop__ALLcls.o VBlackBoxTop__ALLcls.cpp
g++  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVL_PRINTF=printf -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=1 -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow     -Wno-undefined-bool-conversion -O1 -DTOP_TYPE=VBlackBoxTop -DVL_USER_FINISH -include VBlackBoxTop.h   -c -o VBlackBoxTop__ALLsup.o VBlackBoxTop__ALLsup.cpp
      Archiving VBlackBoxTop__ALL.a ...
ar r VBlackBoxTop__ALL.a VBlackBoxTop__ALLcls.o VBlackBoxTop__ALLsup.o
ar: VBlackBoxTop__ALL.a を作成しています
ranlib VBlackBoxTop__ALL.a
g++    BlackBoxTop-harness.o verilated.o verilated_vcd_c.o VBlackBoxTop__ALL.a    -o VBlackBoxTop -lm -lstdc++
make: ディレクトリ '/home/diningyo/prj/study/2000_chisel/500_learning-chisel3/test_run_dir/BlackboxTop' から出ます
sim start on diningyo-pc at Mon Oct  7 23:48:19 2019
inChannelName: 00011230.in
outChannelName: 00011230.out
cmdChannelName: 00011230.cmd
STARTING test_run_dir/BlackboxTop/VBlackBoxTop

ということで、Chiselのテストフレームワーク経由で複数のファイルからなるRTLをブラックボックス化して使う際には必要なファイルを全てsetResourceで列挙すればOKだった。

setResourceを何個も列挙するのはめんどくさいので、以下のような感じでSeqとかに適当に入れてforeachとかで処理するのが良いと思う。

class a extends BlackBox with HasBlackBoxResource {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
  })

  val rtlList = Seq("a.sv", "b.sv")

  rtlList.foreach( f => setResource(s"/$f"))
}

ということで、複数のRTLファイルから構成されるVerilog-HDLのモジュールをBlackbox化して使う方法でした。