DATE2022に上がっていたChiselの拡張言語Twineというものについて、論文を読んでみることにした。
しかしこれ系の拡張言語はふわっとしている印象が多くて嫌な予感がする。大丈夫か?(DATEにAcceptしているのだから大丈夫なのかなあ)
前回の記事
Twineの概要
Twineを使用した例を示すために、以下の計算をするモジュールをTwineで実装することを考える。
このときのTwineの記述は以下のようになる。
class TopLevelDesign extends TwineModule{ ③ val in = Input(new FPFMAOps(32,8)) val out = Output(new FPSqrtOps(16,4)) val ctrl = new DecoupledIOCtrl(3,2) val fma1 = Module(new FPFMA(32,4)) val fma2 = Module(new FPFMA(32,2)) val sqrt1 = Module(new FPSqrt(16,2)) val sqrt2 = Module(new FPSqrt(16,2)) ④ in >>> fma1 >>> sqrt1 ⑤ in.op1 >>> sqrt2 ⑥ TwineBundle(sqrt1, sqrt2, sqrt2) >>> fma2 ⑦ fma2 >>> ctrl
トップレベルデザインは、図1に示すように、fma1
、fma2
、sqrt1
、sqrt2
の4つのコンポーネントで構成されている。
- fma1: 32ビットの浮動小数点演算器。4入力受け取り
- fma2 : 32ビットの浮動小数点演算器。2入力受け取り
- sqrt1 : 16ビットの浮動小数点演算SQRT。2入力受け取り
- sqrt2 : 16ビットの浮動小数点演算SQRT。2入力受け取り
従来のHDLを使用する場合、コンポーネント間のインタフェースの型変換などに余分なコードが必要になるため、Twineではインタフェースを以下のように標準化することでこれらの問題を解決する。
TightlyCoupledIOCtrl
ValidIOCtrl
DecoupledIOCtrl
OutOfOrderIOCtrl
リスト1の1、2、3はTwineのインターフェースの例となっている。
class FPFMA(val numBit:Int, val entries:Int) extends TwineModule{ ① val in = Input(new FPFMAOps(numBit, entries)) val out = Output(new FPResult(numBit, entries) val ctrl = new DecoupledIOCtrl(1,2) /*Computation logic for FMA(fused multiply-add) */ } class FPSqrt(val numBit:Int, val entries:Int) extends TwineModule{ ② val in = Input(new FPSqrtOp(numBit, entries)) val out = Output(new FPResult(numBit, entries)) val ctrl = new ValidIOCtrl(2) /*Computation logic for Sqrt */ } class TopLevelDesign extends TwineModule{ ③ val in = Input(new FPFMAOps(32,8)) val out = Output(new FPSqrtOps(16,4)) val ctrl = new DecoupledIOCtrl(3,2)
Twineでは、各ハードウェアコンポーネントは3つの変数を定義したTwineModule
となる。
Twineでは、プロデューサとコンシューマの関係を明確にするために新しい演算子を導入する。新しい演算子>>>
により、コンポーネントとデータポート間の関係性を直接記述することができるようになっている。
特にリコンフィギャラブルアクセラレータについては、以下の2つの重要な課題がある。
- 制御信号の調整
- コンポーネント間でのデータ変換
これらを解決するために、プロデューサ・コンシューマの関係やインタフェースに基づき、各コンポーネント間の中間コーポ―ネントとして必要なバッファやコンバータを挿入する。
次に、データポートに対応するコンポーネントまたは新たに生成された中間コンポーネントを接続する。
A. 制御インタフェースの抽象化
本節では、Twineの4つの標準的なインタフェースについて説明する。
標準インタフェースの使用を強制することで、ハードウェアコンポーネントを高水準設計で再利用する際に、より予測しやすくなる。Twineは4つの標準制御インタフェースを提供し、開発者は各コンポーネントを詳細に理解する必要はない。以下、4つのインターフェースについて説明し、セクションVでこれらのインターフェースのさらなる拡張について議論する。
TightlyCoupledIOCtrl
:TightlyCoupledIOCtrl
は,レイテンシが一定でタイミング動作が単純 なコンポーネント(例えば,常に 4 サイクルで計算するパイプラ イン乗算器)のために設計されたインターフェースである.最もシンプルなインタフェースで、最もオーバヘッドが低い。このインタフェースは入力と出力が密に結合されているため、可変レイテンシに対応できない。図2aに示すように、TightlyCoupledIOCtrl
は、stallとstuckという2つの制御信号を持っている。このコンポーネントは、stallまたはstuck信号がアサートされると消費も生産もしません。そうでない場合は、毎サイクル入力を受け取り、出力を生成します。
ValidIOCtrl
:ValidIOCtrl
は、TightlyCoupledIOCtrl
よりも柔軟性が高く、実行中の非決定的なレイテンシをサポートしながら、低いオーバーヘッドを維持するように設計されている。図2bに示すように、ValidIOCtrl
は両端に有効ビットのペアを追加実装し、入力側と出力側を緩く結合することだけを可能にします。ValidIOCtrl
で導入された2つの新しい信号は、有効な信号です。入力有効ビットは、新しい利用可能な入力をコンポーネントに通知し、出力有効ビットは、コンポーネントによって生成された新しい結果を処理するために外部システムに信号を送ります。
DecoupledIOCtrl
。ValidIOCtrl
は柔軟性が高い反面、下流のコンポーネントからのBackpressueをローカルにバッファリングできないため、backpressueがさらに上流に伝わり、より多くのコンポーネントを停止させることになる。DecoupledIOCtrl
は、入出力が完全に分離されたコントロールインターフェースである。DecoupledIOCtrl
は、コンポーネントの両端でFIFOバッファをサポートし、backpressueをローカルに対応させます。これらのバッファは、宣言時にパラメータで簡単にカスタマイズ、生成することができます。図2c に示すように、DecoupledIOCtrl
は、コンポーネントの両端にvalid/ready ペアを実装しています。Valid/readyペアは、より複雑な制御ロジックに適応するためのハンドシェイクメカニズムを提供する。リスト1 の①は、コンポーネントの入力側と出力側にそれぞれエントリ数1とエントリ数2のバッファを持つDecoupledIOCtrl
の宣言を示しています。
OutOfOrderIOCtrl
。メモリアクセスなどレイテンシのばらつきが大きい処理では、並列性を生かすためにアウトオブオーダー実行が必要です。図2dに示すOutOfOrderIOCtrl
は、このようなユースケースのために設計されたインタフェースである。これは最も柔軟なインターフェースの一つであるが、最も複雑なインターフェースでもある。アウトオブオーダーコンポーネントに入るリクエストには、チケット番号tick_num
が割り当てられ、アウトオブオーダー処理と完了が可能になる。実行終了後、リクエストは自動的にtick_num
で並べ替えられるか、他のアウトオブオーダーコンポーネントに渡される。バッファ管理と順序変更は開発者にとって透過的なので、開発者は入ってくるリクエストの処理にだけ集中すればよい。
インターフェイス間の比較 表1は、インターフェースの品質比較です。経験則として、素朴なタイミング動作をするコンポーネントはオーバーヘッドの少ないインターフェイスを使用し、複雑な機能や長いレイテンシの操作をするコンポーネントは、より柔軟なインターフェイスを実装する必要があります。
TightlyCoupledIOCtrl | ValidIOCtrl | DecoupledIOCtrl | OutOfOrderIOCtrl | |
---|---|---|---|---|
柔軟性 | 非常に低い | 低い | 高い | 高い |
オーバヘッド | 低い | 低い | 高い | 高い |
固定レイテンシ | Yes | No | No | No |
アウトオブオーダ | No | モジュール内 | モジュール内 | モジュール間 |
バックプレッシャー | Yes | Yes | No | No |