前回の続き。
Chipyardのリファレンスは比較的詳しく書いてあると思うので、この資料を読みながらDiplomacyの勉強をしていこうと思う。
9.1.4. 識別ノード
これまでのノードは入力もしくは出力が定義されていたが、識別ノードは両方を持っている。名前が示しているように、入力と出力を単純に接続するだけである。このノードは複数のノードを複数のエッジをもつ単一のノードに接続するために使用される。例えば、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.node
はmanager1.node
に接続され、client2.node
はmanager2.node
に接続される。
識別ノードの入力ノードと出力ノードの数は一致していなければならない。そうでなければ、エラボレーション時にエラーが発生する。
9.1.5. アダプタノード
識別ノードと同様に、アダプタノードは複数の入力を受け付け、同じだけの出力を生成する。しかし識別ノードと異なるのはアダプタノードは単純にノードを接続するわけではない。入力と出力の論理と物理的なインタフェースを変更し、メッセージを変更することができる。RocketChipはアダプタのライブラリをDiplomaticウィジェットで提供している。
アダプタノードを自分で作成する必要性はあまりないが、必要であれば以下のようにして作成できる。
val node = TLAdapterNode( clientFn = { cp => // .. }, managerFn = { mp => // .. })
clientFn
は入力のTLClientPortParameters
を受け付ける関数であり、出力のための該当するパラメータを生成する。managerFn
は出力のTLManagerPortParameters
を受け付ける関数であり、入力のための該当するパラメータを生成する。
9.1.6. ネクサスノード
ネクサスノードはアダプタノードと似ているが出力インタフェースが異なる。しかし同様に出力ノードの数と入力ノードの数は異なっていても良い。このノードタイプはTileLinkのクロスバージェネレータを提供しているTLXbar
ウィジェットに使用される。このノードを手動で作成する必要性はあまりないが、必要に応じて以下のように生成できる。
val node = TLNexusNode( clientFn = { seq => // .. }, managerFn = { seq => // .. })
アダプタノードのコンストラクタと似ているが、単一のパラメータオブジェクトを渡して単一の結果を返すのではなく、複数のパラメータを受け取る関数を取る。さらに、入力シーケンスの数と出力シーケンスの数は同一でなくても良い。