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

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

Rocket ChipのGeneratorのソースの解析メモ(5) - LazyModuleを使った最小??のモジュールを作る

スポンサーリンク

Rocket Chipの解析の話の5回目。 今日はソースコードの解析の傍らで行っていたLazyModuleの使い方を探っていた結果についてを少し。

LazyModule

そもそもLazyModuleって?という話になるのだが、このLazyModuleはRocket ChipのDiplomatic Design Patternを構成する重要なモジュールの一つのようだ。Diplomatic Design Patternについてはこちらの論文をどうぞ

Rocket Chipでの使われ方が自分には複雑すぎて詳しくはまだ追いきれていない部分が多いが、クラスパラメータの評価と実際のモジュールの実体化のタイミングをバラすことが出来るように見えている(まだあやふやな理解です)。

LazyModuleを使った最小??のモジュール

LazyModuleの実装もだしRocket Chipにおけるこのモジュールの使われ方も高度すぎて何がなんだかわからんので、とりあえずLazyModuleを使ったモジュールでエラボレートを通そうと試していた。 まだ全然使いこなせる感じでは無いのだが、とりあえずLazyModuleを使って作ったモジュールのエラボレートを通すことが出来たのでそのコードを載せておこうと思う。

とりあえず自分が書いた最小と思われるLazyModuleのコードは以下。

package trial

import chisel3._
import chisel3.util._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp}

/**
 * こっちは***Impを実体化するクラス
 * イメージ的には実装本体のラッパー
 */
class TestLazyModule(implicit p: Parameters) extends LazyModule {
  override lazy val module = new TestLazyModuleImp(this)
}

/**
 * こっちが実装をする方のモジュール
 */
class TestLazyModuleImp[+L <: TestLazyModule](_outer: TestLazyModule) extends LazyModuleImp(_outer) {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val finished = Output(Bool())
  })

  io.finished := io.in
}

/**
 * LazyModuleはDriver.executeに渡せないのでModuleの皮をかぶせる
 */
class TestTop()(implicit p: Parameters) extends Module {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val finished = Output(Bool())
  })

  val dut = Module(LazyModule(new TestLazyModule()).module)

  dut.io.in := io.in
  io.finished := dut.io.finished
}

/**
 * エラボレート
 */
object Elaborate extends App {

  implicit val p = Parameters.empty

  Driver.execute(Array(
    "-tn=TestLazyModule",
    "-td=rtl"
  ),

  () => new TestTop())
}

コメントを追加したとおりなのだがLazyModuleを使って実装をする際には、以下の2つが必要になるようだ。

  • LazyModule継承したクラス:イメージ的には実際のモジュールの皮って感じ。
    • 上位のTestTop階層で.moduleを評価するまではTestLazyModulemoduleが評価されないので、TestLazyModuleインスタンスmoduleの実体化が分離されてるっぽい。
  • LazyModuleはDriverを継承したクラス:こっちがモジュールの実装を受け持つみたい。
    • 上記の例だとTestLazyModuleImpは見た目は普通のChiselのモジュール

上記のTestTopモジュールからRTLを生成すると以下のようになった。

[info] Running trial.Elaborate
[info] [0.004] Elaborating design...
[info] [1.602] Done elaborating.
Total FIRRTL Compile Time: 657.9 ms
[success] Total time: 89 s, completed 2019/08/21 23:30:16
  • 生成されたRTL
    • ちゃんとTTestLazyModuleがモジュールとして実体化している
module TestLazyModule(
  input   io_in,
  output  io_finished
);
  assign io_finished = io_in; // @[TestLazyModule.scala 25:15]
endmodule
module TestTop(
  input   clock,
  input   reset,
  input   io_in,
  output  io_finished
);
  wire  dut_io_in; // @[TestLazyModule.scala 37:19]
  wire  dut_io_finished; // @[TestLazyModule.scala 37:19]
  TestLazyModule dut ( // @[TestLazyModule.scala 37:19]
    .io_in(dut_io_in),
    .io_finished(dut_io_finished)
  );
  assign io_finished = dut_io_finished; // @[TestLazyModule.scala 40:15]
  assign dut_io_in = io_in; // @[TestLazyModule.scala 39:13]
endmodule

このLazyModuleの仕組みは実際には以下のような感じで使われている。

/** Example Top with periphery devices and ports, and a Rocket subsystem */
class ExampleRocketSystem(implicit p: Parameters) extends RocketSubsystem
    with HasAsyncExtInterrupts
    with CanHaveMasterAXI4MemPort
    with CanHaveMasterAXI4MMIOPort
    with CanHaveSlaveAXI4Port
    with HasPeripheryBootROM {

  // ここで実装本体を作る
  override lazy val module = new ExampleRocketSystemModuleImp(this)

  // この辺はバスの変換とか??
  // The sbus masters the cbus; here we convert TL-UH -> TL-UL
  sbus.crossToBus(cbus, NoCrossing)

  // The cbus masters the pbus; which might be clocked slower
  cbus.crossToBus(pbus, SynchronousCrossing())

  ~略~

// こっちに至ってはtraitをペタペタくっつけるだけ。。
class ExampleRocketSystemModuleImp[+L <: ExampleRocketSystem](_outer: L) extends RocketSubsystemModuleImp(_outer)
    with HasRTCModuleImp
    with HasExtInterruptsModuleImp
    with CanHaveMasterAXI4MemPortModuleImp
    with CanHaveMasterAXI4MMIOPortModuleImp
    with CanHaveSlaveAXI4PortModuleImp
    with HasPeripheryBootROMModuleImp
    with DontTouch

でここからどう使っていけばいいんだろー??って話になるのだが、上記のコードと前回のConfigParametersクラス等の仕組みだけで、中で生成されるシステムが変わるので、そのへんに着目して解析やらトライアルを続けていきたい。 まずはさっきのコードに前回のConfigとかを追加して見るところから、、、かなぁ。