前回の続き。
Chipyardのリファレンスは比較的詳しく書いてあると思うので、この資料を読みながらDiplomacyの勉強をしていこうと思う。
今回はDiplomacyがサポートするウィジェットについて。AXI4とTileLinkについて様々なウィジェットがサポートされており、これらの部品を組み合わせることで任意のバスを構成していくことになる。
Diplomaticウィジェット
RocketChipはDiplomaticなTileLinkとAXI4のウィジェットをライブラリとして提供している。共通部品として最も多く使用されるウィジェットを以下に示す。TileLinkのウィジェットはfreechips.rocketchip.tilelink
から入手できる。また、AXI4のウィジェットはfreechips.rocketchip.amba.axi4
から入手できる。
TLBuffer
TileLinkのトランザクションをバッファリングするためのウィジェットである。TL-C以外の場合は2つのキューをインスタンス化し、TL-Cの場合は5つのキューをインスタンス化するだけである。各チャンネルのキューを柔軟に設定するために、freechips.rocketchip.diplomacy.BufferParams
をコンストラクタに渡すことができる。case classの引数は以下のとおりである。
depth: Int
- キューのエントリ数flow: Boolean
- Trueの場合、Valid信号は組み合わせ回路で構成されるためエンキューと同じサイクルで消費される。pipe: Boolean
- Trueの場合、Ready信号は組み合わせ回路で構成されるため、1エントリのキューは最大のレートで使用される。
コンストラクタにはInt
型の整数のみを渡すことができる。BufferParams
オブジェクトの代わりに整数を渡すことによって、整数で渡されたdepthのキューが生成され、flow
とpipe
はfalseに設定される。
また、以下のあらかじめ定義されたBufferParams
オブジェクトを使用することができる。
BufferParams.default
=BufferParams(2, false, false)
BufferParams.none
=BufferParams(0, false, false)
BufferParams.flow
=BufferParams(1, true, false)
BufferParams.pipe
=BufferParams(1, false, true)
引数:
4種類のコンストラクタがある。引数が0, 1, 2, 5つ取るバリエーションである。
0個の引数を持つコストラクタはすべてのチャネルでBufferParams.default
を使用する。
1つの引数を持つコンストラクタはすべてのチャネルでBufferParams
オブジェクトを使用する。
2つの引数を持つコンストラクタは、それぞれ以下のように引数が適用される。
ace: BufferParams
- A, C, Eチャネルに使用されるパラメータである。bd: BufferParams
- B, Dチャネルに使用されるパラメータである。
5つの引数を持つコンストラクタはそれぞれ以下のように引数が適用される。
a: BufferParams
- Aチャネルに使用されるパラメータである。b: BufferParams
- Bチャネルに使用されるパラメータである。c: BufferParams
- Cチャネルに使用されるパラメータである。d: BufferParams
- Dチャネルに使用されるパラメータである。e: BufferParams
- Eチャネルに使用されるパラメータである。
使用例:
// デフォルトの設定 manager0.node := TLBuffer() := client0.node // 暗黙的にチャネル当たり8エントリのキューを挿入する。 manager1.node := TLBuffer(8) := client1.node // Aチャネルではデフォルト設定を使用し、Dチャネルではパイプを使用する。 manager2.node := TLBuffer(BufferParams.default, BufferParams.pipe) := client2.node // AチャネルとDチャネルにキューを挿入する。 manager3.node := TLBuffer( BufferParams.default, BufferParams.none, BufferParams.none, BufferParams.default, BufferParams.none) := client3.node
AXI4Buffer
TLBuffer
と同様だがAXI4用である。BufferParams
オブジェクトを引数に取る。
引数:
TLBufferと同様に、AXI4Bufferにも4種類のコンストラクタがある。引数が0, 1, 2, 5つ取るバリエーションである。
0個の引数を持つコストラクタはすべてのチャネルでBufferParams.default
を使用する。
1つの引数を持つコンストラクタはすべてのチャネルでBufferParams
オブジェクトを使用する。
2つの引数を持つコンストラクタは、それぞれ以下のように引数が適用される。
aw: BufferParams
- AR, AW, Wチャネルに使用されるパラメータである。br: BufferParams
- B, Rチャネルに使用されるパラメータである。
5つの引数を持つコンストラクタはそれぞれ以下のように引数が適用される。
aw: BufferParams
- AWチャネルに使用されるパラメータである。w: BufferParams
- Wチャネルに使用されるパラメータである。b: BufferParams
- Bチャネルに使用されるパラメータである。ar: BufferParams
- ARチャネルに使用されるパラメータである。r: BufferParams
- Rチャネルに使用されるパラメータである。
使用例:
// デフォルト設定 slave0.node := AXI4Buffer() := master0.node // 暗黙的にチャネル当たり8エントリのキューを挿入する slave1.node := AXI4Buffer(8) := master1.node // AW/W/ARチャネルではデフォルト設定を使用し、B/Rチャネルではパイプを使用する。 slave2.node := AXI4Buffer(BufferParams.default, BufferParams.pipe) := master2.node // AW/B/ARチャネルに1エントリのキューを挿入し、W/Rチャネルにキューを挿入する。 // Single-entry queues for aw, b, and ar but two-entry queues for w and r slave3.node := AXI4Buffer(1, 2, 1, 1, 2) := master3.node
AXI4UserYanker
個のウィジェットはユーザフィールドを持つAXIポートを受け取り、そのユーザフィールドを取り払う。ARとAWリクエストチャネルのユーザフィールドは内部のキューによりARID/AWIDに関連付けられて保持される。このユーザフィールドはレスポンス時に付け加えられる。
引数:
capMaxFlight: Option[Int]
- (任意) インフライトなIDを保持することのできるリクエストの数。None(デフォルト)の場合、UserYankerはインフライトなリクエストの最大数までサポートする。
使用例:
nouser.node := AXI4UserYanker(Some(1)) := hasuser.node
AXI4Deinterleaver
異なるIDを持つ複数ビートのAXIリードレスポンスはインタリーブされる可能性がある。このウィジェットはスレーブからのリードレスポンスを並び替え、すべてのビートが単一のトランザクションになるように調整する。
引数:
maxReadBytes: Int
- 単一のトランザクションでの最大バイト数。
使用例:
interleaved.node := AXI4Deinterleaver() := consecutive.node
TLFragmenter
TLFragmenterウィジェットはTileLinkインタフェースの最大論理転送サイズを縮小するために、大きな大きなトランザクションをより小さなトランザクションに分割する。
引数:
minSize: Int
- 接続されるマネージャの最小転送サイズmaxSize: Int
- Fragmenterが適用された後の最大転送サイズalwaysMin: Boolean
- (オプション)すべてのリクエストをminSizeに分割するか(そうでない場合、マネージャでサポートされる最大サイズに分割する) (デフォルトではfalse)。earlyAck: EarlyAck.T
- (オプション)複数ビートのPutコマンドにおいて、最初のビートでAcknowledgeするか、最後のビットでAckowledgeするか?取りうる値は以下である(デフォルト :EarlyAck.None
)EarlyAck.AllPuts
- 常に最初のビートでAckowledgeを返す。EarlyAck.PutFulls
- PutFullの場合は最初のビートでAckowledgeを返す。そうでなければ最後のビートでAckowledgeを返す。EarlyAck.None
- 常に最後のビートでAckowledgeを返す。
holdFirstDenied: Boolean
- (optional) Allow the Fragmenter to unsafely combine multibeat Gets by taking the first denied for the whole burst. (default: false)
使用例:
val beatBytes = 8 val blockBytes = 64 single.node := TLFragmenter(beatBytes, blockBytes) := multi.node axi4lite.node := AXI4Fragmenter() := axi4full.node
Additional Notes
- TLFragmenterは PutFull, PutPartial, LogicalData, Get, Hint に対して変更を加える。
- TLFragmenterはArithmeticDataはそのまま通過させる(alwaysMinが有効の場合は
minSize
まで縮小させる) - TLFragmenterはacqureコマンドを変更できない(livelockを発生させる可能性がある)。したがって、両サイドにキャッシュを配置するのは危険である。
AXI4Fragmenter
AXI4FragmenterはTLFragmenter
と似ているが、TileLinkではなく複数ビートのAXI4トランザクションを分割する。これはAXI4からAXI4-Liteへの効率的な変換の機能も持っている。このウィジェットのコンストラクタは引数を取らない。
使用例:
axi4lite.node := AXI4Fragmenter() := axi4full.node
TLSourceShrinker
マネージャが管理するソースIDは、通常は接続されるクライアントの情報により計算される。いくつかの場合には、ソースのIDの数を固定したいときがある。例えばTileLinkのポートをVerilogのブラックボックスにエクスポートしたいときなどが挙げられる。このような問題を解決するために、クライアントがより多くのソースIDを必要とする場合にはTLSourceShrinkerを使用できる。
引数:
maxInFlight: Int
- The maximum number of source IDs that will be sent from the TLSourceShrinker to the manager.maxInFlight: Int
- TLSourceShrinkerからマネージャに転送されるソースIDの最大数。
使用例:
// クライアントノードはおそらくソースIDを16以上持っている。 // マネージャノードはIDを16個までしか管理できない。 manager.node := TLSourceShrinker(16) := client.node
AXI4IdIndexer
TLSourceShrinker
のAXI4番である。スレーブAXI4インタフェースのAWID/ARIDのビット数を制限する。AXI4ポートを外部インタフェース・もしくはブラックボックスに接続する場合に有益である。
引数:
idBits: Int
- スレーブインタフェース上のIDビット数
使用例:
// マスターノードはおおそらく16以上のIDを持っている。 // スレーブノードは4つのIDまでしか管理できない。 slave.node := AXI4IdIndexer(4) := master.node
注意:
AXI4IDIndexerはスレーブインタフェースにuser
フィールドを追加し、マスターリクエストのIDをフィールドにストアする。user
フィールドを持たないAXI4インタフェースに接続する場合、AXI4UserYanker
を使用する必要がある。