前回の続き。Diplomacyを使って実際にいくつかデザインを作ってみようと思った。Diplomacyを使えばデバッグユニットをその場で生成して、さらに外部と接続して制御したい。そんなデザインを作ってみよう。
CoreComplexモジュールの作成
まずはコアの部分を作る、CoreComplexというモジュールを作り、そこにIFUとメモリをLazyModuleとして配置した。
class core_complex(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule { val ifu = LazyModule(new ifu("ifu")) val xbar = LazyModule(new TLXbar) val memory = LazyModule(new TLRAM(AddressSet(0x020000000, 0x0ffff), beatBytes = ramBeatBytes)) xbar.node := TLDelayer(0.0001) := ifu.node memory.node := xbar.node
上記の記述では、ifu
とxbar
とmemory
の3つをLazyModuleとして宣言し、これを接続する。見てわかる通りcore_complex
モジュールはLazyModuleとして宣言されており、これはその名の通りDiplomacyによるパス探索で見つからなければ自動的に削除される。またDiplomacyによる「外交」実行中にパラメータの調整が行われる。
さらにこれに対して外部からメモリアクセスするための専用モジュールとしてtest_loader
を宣言し接続した。
class core_complex(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule { val loader = LazyModule(new loader("loader")) val ifu = LazyModule(new ifu("ifu")) val xbar = LazyModule(new TLXbar) val memory = LazyModule(new TLRAM(AddressSet(0x020000000, 0x0ffff), beatBytes = ramBeatBytes)) xbar.node := loader.node xbar.node := TLDelayer(0.0001) := ifu.node memory.node := xbar.node
CoreComplexをインスタンス化するTestHarnessモジュールを作成
次にこのCoreComplexをインスタンス化するTestHarnessモジュールを作成する。このTestHarnessモジュールはシミュレーション用で、実際にチップを起こす場合には使用されない。従ってここに外部プログラムローダなどを配置することになる。
class TestHarness()(implicit p: Parameters) extends Module { val io = IO(new Bundle { val success = Output(Bool()) }) val ldut = LazyModule(new core_complex(4, 5000)) val dut = Module(ldut.module) val dtm = Module(new sim_dtm); ldut.loader.module.io.req := dtm.io.req ldut.loader.module.io.addr := dtm.io.addr ldut.loader.module.io.data := dtm.io.data dtm.io.ready := ldut.loader.module.io.ready } class DefaultConfig extends Config(new BaseSubsystemConfig)
この時にLazyModuleとしてcore_complex
をインスタンス化しており、さらにcore_complex
の内部インスタンスのmodule
をdut
として引っ張り出している。core_complex
にはLazyではないモジュールとしてmodule
を宣言していた。
class core_complex(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule { val loader = LazyModule(new loader("loader")) ... lazy val module = new LazyModuleImp(this) with UnitTestModule { // TLPatternPusher ifu.module.io.run := true.B io.finished := ifu.module.io.done when (ifu.module.io.error) { printf("Error is detected") } }
さらに、dtm
として外部との接続用にデバッグユニットをインスタンス化している。このsim_dtm
は実体はBlackBox
モジュールでVerilogとして実装されている。DPIを経由してVerilogモジュールと通信をする仕組みになっている。
sim_dtm.v
module sim_dtm( 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 ); bit r_reset; wire __debug_req_ready = debug_req_ready; wire __debug_resp_valid = debug_resp_valid; wire [31:0] __debug_resp_bits_resp = {30'b0, debug_resp_bits_resp}; ...
この状態でVerilogを生成してみたのだが、FIRRTLの段階で失敗してしまった。ldut.loader
が存在しないらしい。確かにLazyModuleなのでタッチしないとインスタンス化されないと思うのだが、そうはいってもどうやってインスタンス化すればいいんだ?
firrtl.passes.CheckHighFormLike$UndeclaredReferenceException: @[test_harness.scala 25:30]: [module TestHarness] Reference loader is not declared. firrtl.passes.CheckHighFormLike$UndeclaredReferenceException: @[test_harness.scala 26:30]: [module TestHarness] Reference loader is not declared. firrtl.passes.CheckHighFormLike$UndeclaredReferenceException: @[test_harness.scala 27:30]: [module TestHarness] Reference loader is not declared. firrtl.passes.CheckHighFormLike$UndeclaredReferenceException: @[test_harness.scala 28:16]: [module TestHarness] Reference loader is not declared.
これは、一度接続先をLazyModuleの内部にインスタンスされているModuleに接続することで解決できる。以下のように、LazyModule内部のmodule
に対して接続することでモジュール間を接続できる。
class TestHarness()(implicit p: Parameters) extends Module { val io = IO(new Bundle { val success = Output(Bool()) }) val ldut = LazyModule(new core_complex(4, 5000)) val dut = Module(ldut.module) val dtm = Module(new sim_dtm); dut.io.req := dtm.io.req dut.io.addr := dtm.io.addr dut.io.data := dtm.io.data dtm.io.ready := dut.io.ready }
module TestHarness : input clock : Clock input reset : UInt<1> output io : {success : UInt<1>} clock is invalid reset is invalid io is invalid inst ldut of core_complex @[test_harness.scala 22:19] ldut.clock is invalid ldut.reset is invalid ldut.auto is invalid ldut.io is invalid ldut.clock <= clock ldut.reset <= reset inst dtm of sim_dtm @[test_harness.scala 24:19] dtm.ready is invalid dtm.data is invalid dtm.addr is invalid dtm.req is invalid ldut.io.req <= dtm.req @[test_harness.scala 25:15] ldut.io.addr <= dtm.addr @[test_harness.scala 26:15] ldut.io.data <= dtm.data @[test_harness.scala 27:15] dtm.ready <= ldut.io.ready @[test_harness.scala 28:16]
class core_complex(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule { val loader = LazyModule(new loader("loader")) lazy val module = new LazyModuleImp(this) { val io = IO(new Bundle { val req = Input(Bool()) val addr = Input(UInt(32.W)) val data = Input(UInt(32.W)) val ready = Output(Bool()) }) loader.module.io.req := io.req loader.module.io.addr := io.addr loader.module.io.data := io.data io.ready := loader.module.io.ready