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
を評価するまではTestLazyModule
のmodule
が評価されないので、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
- ちゃんとT
TestLazyModule
がモジュールとして実体化している
- ちゃんとT
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
でここからどう使っていけばいいんだろー??って話になるのだが、上記のコードと前回のConfig
やParameters
クラス等の仕組みだけで、中で生成されるシステムが変わるので、そのへんに着目して解析やらトライアルを続けていきたい。
まずはさっきのコードに前回のConfig
とかを追加して見るところから、、、かなぁ。