Rocket Chip環境の仕組み解析メモの垂れ流し記事。前回のgenerateFirrtl
で実際にChiselのモジュールとして回路化されるTestHarness
を見ていく。
TestHarness
前回の記事の終わりの方で書いたとおり"emulator"ディレクトリの下でmake
コマンドを実行した際に構築されるChiselのモジュールは
であった。
でこのクラスの中身だが、以下のような物になっている。
class TestHarness()(implicit p: Parameters) extends Module { val io = new Bundle { val success = Bool(OUTPUT) } val dut = Module(LazyModule(new ExampleRocketSystem).module) dut.reset := reset | dut.debug.ndreset dut.dontTouchPorts() dut.tieOffInterrupts() dut.connectSimAXIMem() dut.connectSimAXIMMIO() dut.l2_frontend_bus_axi4.foreach(_.tieoff) Debug.connectDebug(dut.debug, clock, reset, io.success) }
ものすごく、、、、、シンプル。とてもこれだけであのRTLが生成されるとは思えない。 またRocket Chipの売りである”Genratorへ渡すパラメータクラスだけで、構成が変更できる”という部分が単純に見ただけだとわからない。
そのカスタマイズ性を作り出しているのがLazyModule
の力ということになる。とりあえずLazyModule
自体については置いておくことにする。。
で上記の中で実際にやっているのは、以下のような処理だけ。
// ExampleRocketSystemのインスタンス val dut = Module(LazyModule(new ExampleRocketSystem).module) // リセットの接続 dut.reset := reset | dut.debug.ndreset // dutのDon't touch指定 dut.dontTouchPorts() // 割り込みの0クリップ dut.tieOffInterrupts() // シミュレーション用のAXIメモリの接続 dut.connectSimAXIMem() // シミュレーション用のAXI-MMIOメモリの接続 dut.connectSimAXIMMIO() // L2 Frontendバスとnodeの接続 dut.l2_frontend_bus_axi4.foreach(_.tieoff) // デバッグ関連の接続 Debug.connectDebug(dut.debug, clock, reset, io.success)
正直、まだ追いきれてないけど上記の各接続処理の中身も見ておく。
dontTouchPorts
このメソッドはtrait DontTouch
の中に定義されているもの。
/** Marks every port as don't touch * * @note This method can only be called after the Module has been fully constructed * (after Module(...)) */ def dontTouchPorts(): this.type = { self.getModulePorts.foreach(dontTouch(_)) self }
self.dontTouchPorts
とした際のself
はTestHarness
のdut
になる。
dut内部のモジュールのポートをforeach
で処理してdontTouch
に渡して処理する。最終的にはChiselのdontTouch
が呼ばれるようになっている。
tieOffInterrupts
/** Common io name and methods for propagating or tying off the port bundle */ trait HasExtInterruptsBundle { val interrupts: UInt def tieOffInterrupts(dummy: Int = 1) { interrupts := UInt(0) } }
interrupts
を宣言だけしておいて、それにUInt(0)
を設定している。
connectSimAXIMem
このメソッドはtrait CanHaveMasterAXI4MemPortModuleImp
の中に定義されているもの。
val mem_axi4 = outer.memAXI4Node.map(x => IO(HeterogeneousBag.fromNode(x.in))) (mem_axi4 zip outer.memAXI4Node) foreach { case (io, node) => (io zip node.in).foreach { case (io, (bundle, _)) => io <> bundle } } def connectSimAXIMem() { (mem_axi4 zip outer.memAXI4Node).foreach { case (io, node) => (io zip node.in).foreach { case (io, (_, edge)) => val mem = LazyModule(new SimAXIMem(edge, size = p(ExtMem).get.master.size)) Module(mem.module).io.axi4.head <> io } } }
val mem_axi4
にRocket Chip側のIO情報が入っていて、それをconnectSimAXIMem
で接続しているような感じに見える。
connectSimAXIMMIO
このメソッドはtrait CanHaveMasterAXI4MMIOPortModuleImp
の中に定義されているもの。
val mmio_axi4 = IO(HeterogeneousBag.fromNode(outer.mmioAXI4Node.in)) (mmio_axi4 zip outer.mmioAXI4Node.in) foreach { case (io, (bundle, _)) => io <> bundle } def connectSimAXIMMIO() { (mmio_axi4 zip outer.mmioAXI4Node.in) foreach { case (io, (_, edge)) => // test harness size capped to 4KB (ignoring p(ExtMem).get.master.size) val mmio_mem = LazyModule(new SimAXIMem(edge, size = 4096)) Module(mmio_mem.module).io.axi4.head <> io } }
これに関しては先程のconnectSimAXIMem
とほぼ一緒。
dut.l2_frontend_bus_axi4.foreach(_.tieoff)
この処理の中のl2_frontend_bus_axi4
は以下。
/** Actually generates the corresponding IO in the concrete Module */ trait CanHaveSlaveAXI4PortModuleImp extends LazyModuleImp { val outer: CanHaveSlaveAXI4Port val l2_frontend_bus_axi4 = IO(HeterogeneousBag.fromNode(outer.l2FrontendAXI4Node.out).flip) (outer.l2FrontendAXI4Node.out zip l2_frontend_bus_axi4) foreach { case ((bundle, _), io) => bundle <> io } }
l2_frontend_bus_axi4
自体は先程のconnectSimAXIMem
のmem_axi4
と同じ形に見える。
実際にはouter
のl2FrontendAXI4Node.out
というポートを反転したものに対して_.tieoff
を実施、なのだがtieoff
が見つからない。
名前からだと先ほどのtieOffInterrupts
と一緒な気がするのだが。。。
Debug.connectDebug
これは引数の各種ポートと各種テストモジュールを接続する感じの処理。
- src/main/scla/devices/debug/Periphery.scala
def connectDebug( debug: DebugIO, c: Clock, r: Bool, out: Bool, tckHalfPeriod: Int = 2, cmdDelay: Int = 2, psd: PSDTestMode = new PSDTestMode().fromBits(0.U)) (implicit p: Parameters): Unit = { debug.clockeddmi.foreach { d => val dtm = Module(new SimDTM).connect(c, r, d, out) } debug.systemjtag.foreach { sj => val jtag = Module(new SimJTAG(tickDelay=3)).connect(sj.jtag, c, r, ~r, out) sj.reset := r sj.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W) } debug.psd.foreach { _ <> psd } debug.disableDebug.foreach { x => x := Bool(false) } }
ということでざっとTestHarness
の中身について、でした。Rocket Chip自体はExampleRocketSystem
で完結していて、それにシミュレーションようにメモリやらデバッグ用のモジュールを接続する処理をしたもの、という感じ。