FPGA開発日記

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

TileLinkの勉強 (2. Chipyardのリファレンスを読む)

前回の続き。

Chipyardのリファレンスは比較的詳しく書いてあると思うので、この資料を読みながらDiplomacyの勉強をしていこうと思う。

chipyard.readthedocs.io

9.1.4. 識別ノード

f:id:msyksphinz:20191221225444p:plain
識別ノード

これまでのノードは入力もしくは出力が定義されていたが、識別ノードは両方を持っている。名前が示しているように、入力と出力を単純に接続するだけである。このノードは複数のノードを複数のエッジをもつ単一のノードに接続するために使用される。例えば、2つのクライアントLazyモジュールがあったとして、これを1つのクライアントノードに接続している。

class MyClient1(implicit p: Parameters) extends LazyModule {
  val node = TLHelper.makeClientNode("my-client1", IdRange(0, 1))

  lazy val module = new LazyModuleImp(this) {
    // ...
  }
}

class MyClient2(implicit p: Parameters) extends LazyModule {
  val node = TLHelper.makeClientNode("my-client2", IdRange(0, 1))

  lazy val module = new LazyModuleImp(this) {
    // ...
  }
}

2つの異なるLazyモジュールをインスタンスして、この2つのノードを1つのノードに見せている。

class MyClientGroup(implicit p: Parameters) extends LazyModule {
  val client1 = LazyModule(new MyClient1)
  val client2 = LazyModule(new MyClient2)
  val node = TLIdentityNode()

  node := client1.node
  node := client2.node

  lazy val module = new LazyModuleImp(this) {
    // Nothing to do here
  }
}

マネージャに対しても同様の処理が可能である。

class MyManager1(beatBytes: Int)(implicit p: Parameters) extends LazyModule {
  val node = TLHelper.makeManagerNode(beatBytes, TLManagerParameters(
    address = Seq(AddressSet(0x0, 0xfff))))

  lazy val module = new LazyModuleImp(this) {
    // ...
  }
}

class MyManager2(beatBytes: Int)(implicit p: Parameters) extends LazyModule {
  val node = TLHelper.makeManagerNode(beatBytes, TLManagerParameters(
    address = Seq(AddressSet(0x1000, 0xfff))))

  lazy val module = new LazyModuleImp(this) {
    // ...
  }
}

class MyManagerGroup(beatBytes: Int)(implicit p: Parameters) extends LazyModule {
  val man1 = LazyModule(new MyManager1(beatBytes))
  val man2 = LazyModule(new MyManager2(beatBytes))
  val node = TLIdentityNode()

  man1.node := node
  man2.node := node

  lazy val module = new LazyModuleImp(this) {
    // Nothing to do here
  }
}

クライアントとマネージャのグループを接続したい場合、以下のように接続できる。

class MyClientManagerComplex(implicit p: Parameters) extends LazyModule {
  val client = LazyModule(new MyClientGroup)
  val manager = LazyModule(new MyManagerGroup(8))

  manager.node :=* client.node

  lazy val module = new LazyModuleImp(this) {
    // Nothing to do here
  }
}

:=*演算子の意味についてはDiplomacy コネクタの章を確認してほしい。つまり、複数のエッジを持つノードをたがいに接続したのである。識別ノードのエッジは順番に接続され、従ってこの場合はclient1.nodemanager1.nodeに接続され、client2.nodemanager2.nodeに接続される。

識別ノードの入力ノードと出力ノードの数は一致していなければならない。そうでなければ、エラボレーション時にエラーが発生する。

9.1.5. アダプタノード

f:id:msyksphinz:20191221225509p:plain
アダプタノード

識別ノードと同様に、アダプタノードは複数の入力を受け付け、同じだけの出力を生成する。しかし識別ノードと異なるのはアダプタノードは単純にノードを接続するわけではない。入力と出力の論理と物理的なインタフェースを変更し、メッセージを変更することができる。RocketChipはアダプタのライブラリをDiplomaticウィジェットで提供している。

アダプタノードを自分で作成する必要性はあまりないが、必要であれば以下のようにして作成できる。

val node = TLAdapterNode(
  clientFn = { cp =>
    // ..
  },
  managerFn = { mp =>
    // ..
  })

clientFn は入力のTLClientPortParameters を受け付ける関数であり、出力のための該当するパラメータを生成する。managerFn は出力のTLManagerPortParameters を受け付ける関数であり、入力のための該当するパラメータを生成する。

9.1.6. ネクサスノード

f:id:msyksphinz:20191221225530p:plain
ネクサスノード

ネクサスノードはアダプタノードと似ているが出力インタフェースが異なる。しかし同様に出力ノードの数と入力ノードの数は異なっていても良い。このノードタイプはTileLinkのクロスバージェネレータを提供しているTLXbarウィジェットに使用される。このノードを手動で作成する必要性はあまりないが、必要に応じて以下のように生成できる。

val node = TLNexusNode(
  clientFn = { seq =>
    // ..
  },
  managerFn = { seq =>
    // ..
  })

アダプタノードのコンストラクタと似ているが、単一のパラメータオブジェクトを渡して単一の結果を返すのではなく、複数のパラメータを受け取る関数を取る。さらに、入力シーケンスの数と出力シーケンスの数は同一でなくても良い。