FPGA開発日記

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

TileLinkの勉強 (4. Diplomacyがサポートするウィジェットについて)

前回の続き。

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

chipyard.readthedocs.io

今回は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のキューが生成され、flowpipeは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リードレスポンスはインタリーブされる可能性がある。このウィジェットはスレーブからのリードレスポンスを並び替え、すべてのビートが単一のトランザクションになるように調整する。

引数:

使用例:

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を使用する必要がある。