FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Diplomacyを使って独自のTileLinkスレーブノードを作成してみる

ここらで、Diplomacyを使った独自のTileLinkスレーブノードを作成してみよう。作成するのはTileLinkのスレーブノードで、書き込みを行うとそのデータの分だけ加算するレジスタを持つ。とりあえずRTLを生成できるように持って行きたい。

f:id:msyksphinz:20200113231438p:plain:w400
作成するノード形態とTLSlaveRegノードの接続形態

Diplomacyを使ったTileLinkスレーブノードの作成

Diplomacyを使ったスレーブノードには、以下の2レイヤが必要となる。

  1. LazyModuleを使ったDiplomacyノードを持つクラス
  2. Moduleを使ったDiplomacyノードに接続される実際のクラス

  3. 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を作成して接続してみる。

slavereg0slavereg1を作成した。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ファイルを生成できた。一応シミュレーションも動いている。もう少しデバッグを進めたいかな。

f:id:msyksphinz:20200113231558p:plain
TLSlaveRegsを使ったデザインのシミュレーション波形