ここらで、Diplomacyを使った独自のTileLinkスレーブノードを作成してみよう。作成するのはTileLinkのスレーブノードで、書き込みを行うとそのデータの分だけ加算するレジスタを持つ。とりあえずRTLを生成できるように持って行きたい。
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ファイルを生成できた。一応シミュレーションも動いている。もう少しデバッグを進めたいかな。