前回の続き。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