久しぶりにChiselで何か作ってみたくなった。Chiselを使うならDiplomacyを使わないと意味ないだろ!ということで久しぶりにDiplomacyの資料を取り出して読み直している。少し新しいデザインを作りながら、Diplomacyの作り方を復習している。
Diplomacyを使ったTileLinkスレーブノードの作成
Diplomacyを使ったスレーブノードには、以下の2レイヤが必要となる。
- LazyModuleを使ったDiplomacyノードを持つクラス
Moduleを使ったDiplomacyノードに接続される実際のクラス
chisel-hw/src/main/scala/TLUnitTest/TLSlaveReg.scala
class TLSlaveReg( address: AddressSet, parentLogicalTreeNode: Option[LogicalTreeNode] = None, beatBytes: Int = 4, val devName: Option[String] = None, val dtsCompat: Option[Seq[String]] = None )(implicit p: Parameters) extends LazyModule ...
実際のノードは以下のように作成する。TLManagerNode
なので、スレーブデバイスとして動作させるわけだ。
chisel-hw/src/main/scala/TLUnitTest/TLSlaveReg.scala
val node = TLManagerNode(Seq(TLManagerPortParameters( Seq(TLManagerParameters( address = List(address), resources = device.reg("TLSlaveReg"), regionType = RegionType.IDEMPOTENT, executable = false, supportsGet = TransferSizes(1, beatBytes), ...
実際のノードはLazyModuleImpl
を使って実装される。LazyModuleImplには、node
からのDiplomacyノードを引き込む形になっている。
lazy val module = new LazyModuleImp(this) { val (in, edge) = node.in(0)
in
は実際のノードの配線を示し、edge
はノードのパラメータなどの情報が入っている。
in
の使い方は、実際にTileLinkのノードを引き込んで制御するために使う。
lazy val module = new LazyModuleImp(this) { val (in, edge) = node.in(0) ... val a = in.a val d = in.d ... val a_read = a.bits.opcode === TLMessages.Get val a_write = a.bits.opcode =/= TLMessages.Get val a_extra = Cat(a.bits.source, a.bits.size) in.a.ready := true.B when (a.fire && a_write) { printf("A.Write Addr = %x, Data = %x", a.bits.address, a.bits.data) } when (a.fire && a_read ) { printf("A.Read Addr = %x", a.bits.address) } when (a_write) { counter := counter + a.bits.data }
edge
の使い方は、TileLink接続ノードのパラメータを参照するときに使う。
lazy val module = new LazyModuleImp(this) { val (in, edge) = node.in(0) val baseEnd = 0 val (sizeEnd, sizeOff) = (edge.bundle.sizeBits + baseEnd, baseEnd) val (sourceEnd, sourceOff) = (edge.bundle.sourceBits + sizeEnd, sizeEnd) ... d.bits := edge.AccessAck( toSource = a_extra(sourceEnd-1, sourceOff), lgSize = a_extra(sizeEnd-1, sizeOff))
実際に接続してみよう。TLOriginalSlave.scala
を作成して接続してみる。
slavereg0
とslavereg1
を作成した。LazyModule
をインスタンス化して、AddressSet
をそれぞれ0x000, 0x400
, 0x400, 0x800
を作成した。
chisel-hw/src/main/scala/TLUnitTest/TLOriginalSlave.scala
class TLOriginalSlave(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule { ... val xbar = LazyModule(new TLXbar) val slavereg0 = LazyModule(new TLSlaveReg(AddressSet(0x000, 0x3ff), beatBytes = ramBeatBytes)) val slavereg1 = LazyModule(new TLSlaveReg(AddressSet(0x400, 0x3ff), beatBytes = ramBeatBytes)) pushers.zip(model).map{ case (pusher, model) => xbar.node := model.node := pusher.node } slavereg0.node := xbar.node slavereg1.node := xbar.node ...
これでVerilogを生成すると、オリジナルのスレーブノードが接続されたVerilogファイルを生成できた。一応シミュレーションも動いている。もう少しデバッグを進めたいかな。
以下のようにしてMakefileを作成し、自動的にVerilogが生成されるようにした。
PROJECT ?= freechips.rocketchip.unittest CONFIG ?= TLOriginalUnitTestConfig CONFIG_FIR ?= $(PROJECT).$(CONFIG).fir JAVA_HEAP_SIZE ?= 8G JAVA_ARGS ?= -Xmx$(JAVA_HEAP_SIZE) -Xss8M -XX:MaxPermSize=256M export JAVA_ARGS include ../../Makefrag-verilator tilelink: TestHarness.sv mkdir -p $(generated_dir_debug)/$(long_name) $(VERILATOR) $(VERILATOR_FLAGS) -Mdir $(generated_dir_debug)/$(long_name) \ -o $(abspath $(sim_dir))/$@ $(verilog) $(cppfiles) -LDFLAGS "$(LDFLAGS)" \ -CFLAGS "-I$(generated_dir_debug) -include $(model_header_debug)" $(MAKE) VM_PARALLEL_BUILDS=1 -C $(generated_dir_debug)/$(long_name) -f V$(MODEL).mk ./$@
$ make tilelink CONFIG=TLOriginalSlaveTestConfig