FPGA開発日記

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

RISC-Vベクトル拡張仕様書 v1.0 を読み直す (9. ベクトルの各要素の定義)

RISC-Vベクトル拡張仕様書の読み直し。ベクトルレジスタの要素の定義

ベクトルレジスタはマスクしたりプリスタートで実行を省略したりと、さまざまなモードがある。 それらの要素の意味を厳密に定義するのが本章の目的。

github.com

github.com


6. コンフィグレーション設定命令 (vsetvli/vsetivl/vsetvl)

多数の要素を処理するための一般的なアプローチの一つに、ループの各イタレーションでいくつかの要素を処理し、 すべての要素が処理されるまでイタレーションを続ける"ストリップマイニング"があります。 RISC-Vのベクトル仕様では、この手法を直接かつポータブルにサポートしています。 アプリケーションは、処理する要素の総数(アプリケーションベクトル長、AVL)を vl の候補値として指定し、 ハードウェアは、マイクロアーキテクチャの実装と vtype の設定に基づいて、 ハードウェアが反復ごとに処理する要素数( vl に格納されている) を、汎用レジスタを介して応答します。 [example-stripmin-sew] に示されている簡単なループ構造は、コードが残りの要素数とハードウェアが処理する1回あたりの量を簡単に追跡していることを示しています。

アプリケーションのニーズに合わせて vl と vtype の値を迅速に設定できるように、一連の命令が提供されています。 vset{i}vl{i} 命令は、その引数に基づいて、vtype と vl の CSR を設定し、rd に vl の新しい値を書き込みます。

 vsetvli rd, rs1, vtypei   # rd = new vl, rs1 = AVL, vtypei = new vtype setting
 vsetivli rd, uimm, vtypei # rd = new vl, uimm = AVL, vtypei = new vtype setting
 vsetvl  rd, rs1, rs2      # rd = new vl, rs1 = AVL, rs2 = new vtype value

6.1. vtype エンコーディング

新しいvtype の設定は、 vsetvli とvsetivli の即時フィールド、および vsetvl の rs2 レジスタエンコードされます。

 vset{i}vli の即値vtipeiで提案されるアセンブラ名

 e8    # SEW=8b
 e16   # SEW=16b
 e32   # SEW=32b
 e64   # SEW=64b
 e128  # SEW=128b
 e256  # SEW=256b
 e512  # SEW=512b
 e1024 # SEW=1024b

 mf8  # LMUL=1/8
 mf4  # LMUL=1/4
 mf2  # LMUL=1/2
 m1   # LMUL=1, mの設定を省略した場合
 m2   # LMUL=2
 m4   # LMUL=4
 m8   # LMUL=8

例:
    vsetvli t0, a0, e8          # SEW= 8, LMUL=1
    vsetvli t0, a0, e8, m2      # SEW= 8, LMUL=2
    vsetvli t0, a0, e32, mf2    # SEW=32, LMUL=1/2

vsetvl の動作は、rs2 から vtype の値を受け取ることと、 コンテキストの復元に使えることを除いて、vsetvli と同様になります。

vtype の設定が実装でサポートされていない場合には、vtype に vill ビットが設定され、 vtype の残りのビットは 0 に設定され、vl レジスタも 0 に設定されます。

Note: 以前のドラフトでは、vtype に不正な値を設定した場合、トラップが必要でした。 しかし、これはISAへのCSRの書き込み時に最初のデータ依存のトラップを追加することになりました。 実装では、 vill を設定する代わりに、 vtype に不正な値が書き込まれたときにトラップすることを選択して、 エミュレーションで新しい構成をサポートできるようにして、将来の互換性を確保することができます。 現在のスキームは、ある設定に対して vill がクリアされているかどうかをチェックすることで、 サポートされているベクトルユニットの設定をランタイムに軽く照会することをサポートしています。

6.2. AVLのエンコーディング

新しいベクトル長の設定はAVLに基づいており、 vsetvli とvsetvl では、 rs1 とrd のフィールドに以下のようにエンコードされます。

rs1 が x0 でない場合,AVL は rs1 で指定された x レジスタに保持される符号なし整数となり、 新しい vl 値も rd で指定された x レジスタに書き込まれます。

rs1=x0 であるが、 rd!=x0 のときは、符号なし整数の最大値 (~0) が AVL として使用され、 結果として VLMAX が vl に書き込まれ、 rd で指定された x レジスタにも書き込まれます。

rs1=x0 かつ rd=x0 の場合、この命令は vl の現在のベクトル長をAVLとして使用し、 結果の値はvlに書き込まれますが、書き込みレジスタには書き込まれません。 この形式はVLMAXの場合にのみ使用でき、したがって vl は新しいSEW/LMUL比によって実際には変更されません。 VLMAXの変更をもたらすような新しいSEW/LMUL比率の命令の使用は予約済みです。 この場合、実装では vill を設定することができます。

Note: この命令の最後の形式では、VLMAXが減少しないことを条件に、現在の vl を維持しながら vtype レジスタを変更することができます。 この設計は、現在の vtype 設定に対して vl が常に正当な値を保持するように選択されました。 現在の vl 値は vl CSR から読み取ることができます。 新しいSEW/LMUL比率によってVLMAXが縮小する場合、この命令によって vl 値が減少する可能性がありますが、 これが一般的に有用な動作であることは明らかではないため、このケースは留保されています。 また、実装は、マイクロアーキテクチャを最適化するために、この命令によって vl が変更されないと仮定することができます。 vsetivli 命令では、AVLは rs1 フィールドの 5ビットのゼロ拡張即時値(0~31)としてエンコードされます。

Note: vsetivli のAVLのエンコーディングは、通常のCSRの即値と同じです。 vsetivli 命令は、ベクトルサイズが小さく、ベクトルレジスタ内に収まることがわかっているため、 ストリップマイニングのオーバーヘッドが不要な場合に、よりコンパクトなコードを提供します。

6.3. vl 設定の制約

vset{i}vl{i} 命令は、まず引数の vtype にしたがってVLMAXを設定し、 次に以下の制約にしたがって vl を設定します。

  1. AVL ≤ VLMAX のときは vl = AVL
  2. AVL W (2 * VLMAX) のときは ceil(AVL / ) ≤ vl ≤ VLMAX
  3. AVL ≥ (2 * VLMAX) のときは vl = VLMAX
  4. 同一の入力AVLおよびVLMAX値に対して、任意の実装で決定される
  5. これらの具体的な特性は、事前のルールから導かれます。
    1. AVL = 0 なら vl = 0
    2. AVL > 0 なら vl > 0
    3. vl ≤ VLMAX
    4. vl ≤ AVL
    5. vl から読み込んだ値を vset{i}vl{i} の AVL 引数として使用すると、vl 内の同じ値になります。 ただし、結果として得られる VLMAX が vl が読み込まれた時点での VLMAX の値と同じであることが条件です。

Note: vlの設定ルールは、AVL ≤ VLMAX では、レジスタの流出やコンテキストの入れ替えがあっても vl の動作を維持できるよう、 十分に厳密に設計されています。しかし、 AVL > VLMAX の場合は、ベクトルレーンの使用率を向上させることができる柔軟性を備えています。 例えば、 VLMAX < AVL < 2*VLMAX の場合、vl = ceil(AVL / 2) と設定することで、ストリップマイニングループの最後の2回の反復に作業を均等に分散させることができます。 要件2は、 AVL < 2*VLMAX の場合であっても、リダクションループの最初のストリップミン反復では、 すべての反復の中で最大のベクトル長を使用することを保証する。 これにより、ソフトウェアは、ストリップマイニングループで観測されたベクトル長の実行最大値を明示的に計算する必要がなくなります。 また、要件2では、 VLMAX < AVL < 2*VLMAX の場合、vlをVLMAXに設定することができます。

6.4. ストリップマイニングとSEW変更の例

SEWとLMULの設定を動的に変更することで、1つのループで幅が混在するオペレーションでも高い処理能力を発揮します。

# 例: 16ビットの値をロードし、32ビットに拡張し乗算を行い、
# 結果の32ビットを3ビット右にシフトし、結果の32ビットをストアする
# プログラムの先頭では:
#  a0は処理を行う全体の要素数を保持している
#  a1はソース配列のアドレスを保持している
#  a2は書き込み配列のアドレスを保持している
loop:
    vsetvli a3, a0, e16, m4, ta, ma  # vtype = 16-bit 整数ベクトル
                                     # a3をvlに更新する(個のイタレーションにおける要素の個数)
    vle16.v v4, (a1)        # 16ビットのベクトルを取得する
    slli t1, a3, 1          # このイタレーションで処理する要素の個数から要素の2バイト数を計算する
    add a1, a1, t1          # ポインタを進める
    vwmul.vx v8, v4, x10    # v4の値を32ビットに拡張して<v8--v15>に格納する

    vsetvli x0, x0, e32, m8, ta, ma  # 32ビット操作に変更する
    vsrl.vi v8, v8, 3
    vse32.v v8, (a2)        # 32ビットの値をベクトルにストアする
    slli t1, a3, 2          # このイタレーションで処理する要素の個数から要素の4バイト数を計算する
    add a2, a2, t1          # ポインタを進める
    sub a0, a0, a3          # vlだけカウンタを減少させる
    bnez a0, loop           # これ以上処理する?