Diplomacyの勉強をしていて、さてRocket-Chipの場合はどのようにして外部インタフェースと接続しているのか、具体的にはどのようにELFファイルなどのテストパタンをロードしているのか気になってきたので調査してみた。
- Verilatorの場合
VerilatorはシミュレーションのためのTopファイルとしてC++ファイルが必要となる。これはRocket-Chipのディレクトリでは以下に配置してある。
rocket-chip/src/main/resources/csrc/emulator.cc
int main(int argc, char** argv) { unsigned random_seed = (unsigned)time(NULL) ^ (unsigned)getpid(); uint64_t max_cycles = -1; int ret = 0; bool print_cycles = false; ... done_processing: if (optind == argc) { std::cerr << "No binary specified for emulator\n"; usage(argv[0]); return 1; } int htif_argc = 1 + argc - optind; htif_argv = (char **) malloc((htif_argc) * sizeof (char *)); htif_argv[0] = argv[0]; for (int i = 1; optind < argc;) htif_argv[i++] = argv[optind++]; if (verbose) fprintf(stderr, "using random seed %u\n", random_seed); ... jtag = new remote_bitbang_t(rbb_port); dtm = new dtm_t(htif_argc, htif_argv);
で、具体的にはhtif_argv
に使用するELFファイルが指定されて、これがdtm_t
に引数として指定されインスタンス化される。
このdtmを操作しているのが、SimDTM
というモジュールのようだった。
module TestHarness( // @[freechips.rocketchip.system.DefaultConfig.fir 238618:2] input clock, // @[freechips.rocketchip.system.DefaultConfig.fir 238619:4] input reset, // @[freechips.rocketchip.system.DefaultConfig.fir 238620:4] output io_success // @[freechips.rocketchip.system.DefaultConfig.fir 238621:4] ); ... SimDTM SimDTM ( // @[Periphery.scala 257:25 freechips.rocketchip.system.DefaultConfig.fir 238700:4] .clk(SimDTM_clk), .reset(SimDTM_reset), .debug_req_ready(SimDTM_debug_req_ready), .debug_req_valid(SimDTM_debug_req_valid), .debug_req_bits_addr(SimDTM_debug_req_bits_addr), .debug_req_bits_data(SimDTM_debug_req_bits_data), .debug_req_bits_op(SimDTM_debug_req_bits_op), .debug_resp_ready(SimDTM_debug_resp_ready), .debug_resp_valid(SimDTM_debug_resp_valid), .debug_resp_bits_data(SimDTM_debug_resp_bits_data), .debug_resp_bits_resp(SimDTM_debug_resp_bits_resp), .exit(SimDTM_exit) );
これは、Scalaのモジュールには直接インスタンス化されているようではないらしい。
rocket-chip/src/main/scala/system/TestHarness.scala
class TestHarness()(implicit p: Parameters) extends Module { val io = IO(new Bundle { val success = Output(Bool()) }) val ldut = LazyModule(new ExampleRocketSystem) val dut = Module(ldut.module) // Allow the debug ndreset to reset the dut, but not until the initial reset has completed dut.reset := (reset.asBool | dut.debug.map { debug => AsyncResetReg(debug.ndreset) }.getOrElse(false.B)).asBool dut.dontTouchPorts() dut.tieOffInterrupts() SimAXIMem.connectMem(ldut) SimAXIMem.connectMMIO(ldut) ldut.l2_frontend_bus_axi4.foreach(_.tieoff) Debug.connectDebug(dut.debug, dut.resetctrl, dut.psd, clock, reset.asBool, io.success) }
rocket-chip/src/main/scala/devices/debug/Periphery.scala
class SimDTM(implicit p: Parameters) extends BlackBox with HasBlackBoxResource { val io = IO(new Bundle { val clk = Input(Clock()) val reset = Input(Bool()) val debug = new DMIIO val exit = Output(UInt(32.W)) }) def connect(tbclk: Clock, tbreset: Bool, dutio: ClockedDMIIO, tbsuccess: Bool) = { io.clk := tbclk io.reset := tbreset dutio.dmi <> io.debug dutio.dmiClock := tbclk dutio.dmiReset := tbreset tbsuccess := io.exit === 1.U when (io.exit >= 2.U) { printf("*** FAILED *** (exit code = %d)\n", io.exit >> 1.U) stop(1) } } addResource("/vsrc/SimDTM.v") addResource("/csrc/SimDTM.cc") }
最後のaddResource
でVerilogファイルとC言語のDPI関数を追加している。
rocket-chip/src/main/resources/vsrc/SimDTM.v
// See LICENSE.SiFive for license details. //VCS coverage exclude_file import "DPI-C" function int debug_tick ( output bit debug_req_valid, input bit debug_req_ready, output int debug_req_bits_addr, output int debug_req_bits_op, output int debug_req_bits_data, input bit debug_resp_valid, output bit debug_resp_ready, input int debug_resp_bits_resp, input int debug_resp_bits_data ); module SimDTM( input clk, input reset, output debug_req_valid, input debug_req_ready, output [ 6:0] debug_req_bits_addr, output [ 1:0] debug_req_bits_op, output [31:0] debug_req_bits_data, input debug_resp_valid, output debug_resp_ready, input [ 1:0] debug_resp_bits_resp, input [31:0] debug_resp_bits_data, output [31:0] exit );
つまり、Diplomacyで外部C言語関数とやり取りをしたい場合、このようなラッパーになるような関数を書いて、DPI関数を通じて直接接続しなければならないということかな。