FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

AWSで動作するRISC-VデザインFireSimのカスタマイズ : オリジナルデバイスをVerilog付きでシミュレーションする

前回の記事で、どうにかFixedInputStreamが実行できるようになったが、これはテストベンチまですべてScalaで記述して常に同じ動作を繰り返す環境になっている。 これは嫌なので、Verilogなどを接続して外部から引数を渡し、自由にシミュレーションを実行できるようにしたい。

というわけで、以下のチュートリアルを進めて、さらにFireChipのオリジナルデザインを拡張していく。

f:id:msyksphinz:20180628023357p:plain

Creating Simulation Model — FireSim documentation

まずは、ChiselのデザインとVerilogのデザインを接続するインタフェースを作らなければならない。 そこで、「実際にはVerilogのデザイン」なのだけれども、まずはScalaで型を作る。これにはBlackBoxというクラスを使用する。

class SimInputStream(w: Int) extends BlackBox(Map("DATA_BITS" -> IntParam(w))) {
  val io = IO(new Bundle {
    val clock = Input(Clock())
    val reset = Input(Bool())
    val out = Decoupled(UInt(w.W))
  })
}

このインタフェースを接続するためにSimInputStreamConfigコンフィグレーションを作成する。 さらにconnectSimInputを定義し、VerilogScalaをインタフェースするためのしくみを作る。

  • src/main/scala/example/Config.scala
class WithSimInputStream extends Config((site, here, up) => {
  case BuildTop => (clock: Clock, reset: Bool, p: Parameters) => {
    val top = Module(LazyModule(new ExampleTopWithInputStream()(p)).module)
    top.connectSimInput(clock, reset)
    top
  }
})

class SimInputStreamConfig extends Config(
  new WithSimInputStream ++ new BaseExampleConfig)
  • src/main/scala/example/InputStream.scala
 trait HasPeripheryInputStream { this: BaseSubsystem =>
   private val portName = "input-stream"
@@ -30,6 +41,13 @@ trait HasPeripheryInputStreamModuleImp extends LazyModuleImp {
     val fixed = Module(new FixedInputStream(data, outer.streamWidth))
     stream_in <> fixed.io.out
   }
+
+  def connectSimInput(clock: Clock, reset: Bool) {
+    val sim = Module(new SimInputStream(outer.streamWidth))
+    sim.io.clock := clock
+    sim.io.reset := reset
+    stream_in <> sim.io.out
+  }
 }

そしてVerilog/C++のインタフェースを作成して接続していく。これは長いしCopy&Pasteなので割愛。

要点としては、Verilogで定義されたSimInputStreamScalaで定義されたSimInputStreamconnectSimInputで接続する、ということ。

これができればあとはデータをC++インタフェース側からひたすら流し込むだけである。

  • src/main/resources/vsrc/SimInputStream.v
module SimInputStream #(DATA_BITS=64) 
   (
    input                    clock,
    input                    reset,
    output                   out_valid,
    input                    out_ready,
    output [DATA_BITS-1:0] out_bits
    );
   
   bit                     __out_valid;
...

これでコンパイルを行い、シミュレーションを行った。

$ make CONFIG=SimInputStreamConfig
$ dd if=/dev/urandom of=instream.img bs=32 count=1
1+0 records in
1+0 records out
32 bytes copied, 0.000306216 s, 105 kB/s
$ hexdump instream.img
0000000 1d2b e182 6629 006e 46be 3532 06c6 f1f2
0000010 2d00 aece 4ef7 d17b 4500 513a fd1e ca96
0000020
$ ./simulator-example-SimInputStreamConfig +instream=instream.img ../tests/input-stream.riscv
006e6629e1821d2b
f1f206c6353246be
d17b4ef7aece2d00
ca96fd1e513a4500