FPGA開発日記

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

RISC-Vベクトル拡張仕様書 v1.0 を読み直す (13. ベクトルロード命令のバリエーション2)

RISC-Vベクトル拡張仕様書の読み直し。ベクトルロード命令の続き。 セグメントアクセスとかが増えてきてこの辺は非常にグロい部分。そもそもEEWとSEWでメモリアクセスは挙動が違うのに、非常に複雑になってしまっている... 他の算術演算と一緒でEEWとSEWを統一してほしいな。

github.com

github.com


7.8.1. ベクトルユニットストライドセグメントロードストア命令

ベクトルユニットストライドのセグメントロードおよびセグメントストア命令は、 パックされた連続したセグメント("array-of-structures")を 複数の書き込みベクトルレジスタグループに移動させます。

Note 異なるサイズのフィールドを持つ構造体の場合、セグメント・ロードでデータをベクトルレジスタに取り込んだ後、 ソフトウェアは追加の命令を使ってセグメントから構造体フィールドをアンパックすることができます。 ユニットストライドのセグメントロードとストアには、 それぞれ vlseg/vsseg というアセンブラプレフィックスが使われます。

    # フォーマット
    vlseg<nf>e<eew>.v vd, (rs1), vm       # ユニットストライドセグメントロードのテンプレート
    vsseg<nf>e<eew>.v vs3, (rs1), vm       # ユニットストライドセグメントストア命令のテンプレート

    # Examples
    vlseg8e8.v vd, (rs1), vm   # 1バイトフィールドを8つ持つ要素を8つのベクトルレジスタにロードする

    vsseg3e32.v vs3, (rs1), vm  # 3*4バイトセグメントの要素をvs3,vs3+1,vs3+2からメモリにストアする
ロードの場合、vd レジスタはセグメントからロードされる最初のフィールドを保持します。 ストアの場合は、`vs3`レジスタが読み込まれ、各セグメントに格納される最初のフィールドが提供されます。

    # 例1
    # パッキングされたRGBピクセル(8bppの24ビットデータ構造)
    vsetvli a1, t0, e8, ta, ma
    vlseg3e8.v v8, (a0), vm
    # v8 は赤ピクセルを持っている
    # v9 は緑ピクセルを持っている
    # v10 青ピクセルを持っている

    # 例2
    # メモリ構造は複素数を持っている。32ビットの実数と32ビットの虚数を持っている
    vsetvli a1, t0, e32, ta, ma
    vlseg2e32.v v8, (a0), vm
    # v8 は実数を持っている
    # v9 虚数を持っている
ユニットストライド命令については、fault-only-first版も定義されている。

    # ベクトルfault-only-firstユニットストライドセグメントロード命令の例
    vlseg<nf>e<eew>ff.v vd, (rs1),  vm          # ユニットストライドfault-only-firstセグメントロード

fault-only-firstセグメントのロードでは、セグメントへのアクセスの途中で例外が検出された場合、 要素のインデックスがゼロであるかどうかにかかわらず、セグメントのサブセットをロードするかどうかは実装で定義されます。

これらの命令は、トラップが報告された時点や、ベクトル長がトリムされた時点を過ぎても、 書き込みベクトルレジスタグループの要素を上書きすることがあります。

7.8.2. ベクトルストライドセグメントロードストア命令

ベクターストライドセグメントは、GPRの引数 rs2 で指定されたバイトストライドの オフセットで区切られた連続したセグメントを移動してロード、ストアします。

Note: 負のストライドとゼロのストライドがサポートされています。

    # フォーマット
    vlsseg<nf>e<eew>.v vd, (rs1), rs2, vm          # ストライドセグメントロード
    vssseg<nf>e<eew>.v vs3, (rs1), rs2, vm         # ストライドセグメントストア

    # 例
    vsetvli a1, t0, e8, ta, ma
    vlsseg3e8.v v4, (x5), x6   # アドレスx5+i*x6をv4[i]に格納する
                              #  x5+i*x6+1をv5[i]に格納する
                              #  x5+i*x6+2をv6[i]に格納する

    # 例
    vsetvli a1, t0, e32, ta, ma
    vssseg2e32.v v2, (x5), x6   # v2[i]のワードをアドレスx5+i*x6にストアする
                                #   また、v3[i]のワードをx5+i*x6+4にストアする

各セグメント内のフィールドへのアクセスは、 セグメントがメモリ内で重なるようなバイトストライドの場合も含め、 どのような順序でも行うことができます。

7.8.3. ベクトルインデックスセグメントロードストア命令

各セグメントは、rs1 フィールドのスカラのベースアドレスと、ベクトルレジスタ vs2 のバイトオフセットを足したアドレスに位置します。 Index-Ordered形式とIndex-Unordered形式の両方が提供されており、 Index-Ordered形式はセグメントを要素順にアクセスします。 しかし、順序付けられた形式でも、個々のセグメント内のフィールドへのアクセスは、お互いに順序付けられていません。

データベクトルレジスタ群はEEW=SEW、EMUL=LMUL、 インデックスベクトルレジスタ群はEEWがEMUL=(EEW/SEW)*LMULで命令にエンコードされています。

    # 例
    vluxseg<nf>ei<eew>.v vd, (rs1), vs2, vm   # Indexed-unorderedセグメントロード
    vloxseg<nf>ei<eew>.v vd, (rs1), vs2, vm   # Indexed-orderedセグメントロード
    vsuxseg<nf>ei<eew>.v vs3, (rs1), vs2, vm  # Indexed-unorderedセグメントストア
    vsoxseg<nf>ei<eew>.v vs3, (rs1), vs2, vm  # Indexed-orderedセグメントストア

    # 例
    vsetvli a1, t0, e8, ta, ma
    vluxseg3ei32.v v4, (x5), v3   # アドレスx5+v3[i]のバイトデータをv4[i]にロードする
                              #  アドレスx5+v3[i]+1のバイトデータをv5[i]にロードする
                              #  アドレスx5+v3[i]+2のバイトデータをv6[i]にロードする

    # 例
    vsetvli a1, t0, e32, ta, ma
    vsuxseg2ei32.v v2, (x5), v5   # v2[i]中のワードをアドレスx5+v5[i]にストアする
                              #   v3[i]中のワードをアドレスx5+v5[i]+4にストアする

ベクトルインデックスセグメントロードでは、 書き込みベクトルレジスタグループはソースベクトルレジスタグループ(vs2で指定)と重なることはできません。

Note: この制約は、構造体のロードの途中で例外が発生したインデックス付きセグメント・ロードの再起動をサポートします。

7.9. ベクトル全体レジスタロードストア命令

LOAD-FPメジャーオペコードのベクターロードホールレジスタ命令のフォーマット

{reg: [
  {bits: 7, name: 0x07, attr: 'VL*R*'},
  {bits: 5, name: 'vd', attr: 'destination of load', type: 2},
  {bits: 3, name: 'width'},
  {bits: 5, name: 'rs1', attr: 'base address', type: 4},
  {bits: 5, name: 8, attr: 'lumop'},
  {bits: 1, name: 1, attr: 'vm'},
  {bits: 2, name: 0x10000, attr: 'mop'},
  {bits: 1, name: 'mew'},
  {bits: 3, name: 'nf'},
]}
{reg: [
  {bits: 7, name: 0x27, attr: 'VS*R*'},
  {bits: 5, name: 'vs3', attr: 'store data', type: 2},
  {bits: 3, name: 0x1000},
  {bits: 5, name: 'rs1', attr: 'base address', type: 4},
  {bits: 5, name: 8, attr: 'sumop'},
  {bits: 1, name: 1, attr: 'vm'},
  {bits: 2, name: 0x100, attr: 'mop'},
  {bits: 1, name: 0x100, attr: 'mew'},
  {bits: 3, name: 'nf'},
]}

これらの命令は、ベクターレジスタグループ全体をロードおよびストアします。

Note: これらの命令は、ベクタ・レジスタの現在の内容のタイプや長さがわからない場合や、 vlvtypeを変更するとコストがかかる場合に、ベクタ・レジスタの保存や復元に使用することを目的としています。 例えば、コンパイラレジスタ・スピル、ベクタ・レジスタで値が渡されるベクタ関数呼び出し、割り込みハンドラ、OSのコンテキスト・スイッチなどです。 ソフトウェアは、vlenbレジスタを読むことで、転送されたバイト数を知ることができます。 ロード命令では、通常のユニットストライド・ロードのパターンに従って、 mewおよびwidthフィールドにEEWがエンコードされています。

Note: レジスタ内ののバイトレイアウトはメモリ内のバイトレイアウトと同じなので、 EEWに関係なく同じデータが出力先のレジスタ群に書き込まれます。 そのため,EEW=8 のバリエーションだけを用意すれば十分でした。 EEWのバリエーションをすべて用意したのは、エンコードされたEEWをヒントにして、 次にこのEEWでアクセスされる書き込みレジスタグループを示すことができるようにするためで、 これは内部的にデータを再配置する実装の助けになります。 ベクトル全体レジスタストア命令は、EEW=8 のマスクされていない ユニットストライドストアと同様にエンコードされます。

フィールド nf は、ロードおよびストアするベクタレジスタの数をエンコードします。 符号化されたレジスタ数は2の累乗でなければならず、 ベクタレジスタ番号はベクタレジスタグループと同様にアラインメントされていなければならず、 そうでなければ命令の符号化は予約されます。 nf フィールドは、転送するベクターレジスターの数をエンコードするもので、 ベースの後に連続して番号が付けられます。 nf の値は1,2,4,8のみがサポートされており、その他の値は予約されています。 複数のレジスタを転送する場合、最も低い番号のベクトルレジスタは最も低い番号のメモリアドレスに保持され、 連続したベクトルレジスタ番号はメモリに連続して配置されます。

この命令は、 vtype や vl の現在の設定にかかわらず、 evl=nf *VLEN/EEWという実効ベクター長で動作します。 vstart ≥ vl の場合には要素が書き込まれないという通常の特性は、 vtype や vl の現在の設定に関係なく動作します。 vstart ≥ vl の場合には要素が書き込まれないという通常の特性は、 これらの命令には適用されません。 代わりに、vstart ≥ evl の場合には要素は書き込まれません。

この命令は、要素のマスクされていないユニットストライドロードおよびストア命令と同様に動作し、 ベースアドレスは rs1 で指定されるスカラ x レジスタで渡されます。

実装では,ベースアドレスが、エンコードされたEEWのサイズ(バイト)(EEW/8)と実装でサポートされている SEWの最小サイズ(バイト)(SEWMIN/8)のいずれか大きい方に自然にアラインされていない場合、 レジスタ全体のロードとストアでアドレスのずれの例外を発生させることができます。

Note: エンコードされたEEWへの非アラインメントに基づいてミスアラインの例外を発生させることができるため、 これらの命令の実装が簡素化されます。 サブセットの実装によっては、より小さなSEW幅をサポートしていない場合があるため、 エンコードされたEEWよりも大きくても、サポートされている最小のSEWに対してミスアラインド例外を報告することが許可されています。 極端な実装では、例えば SEWMIN>XLEN のようになります。 ソフトウェア環境は、ABIをサポートするための最小アラインメント要件を義務付けることができます。

   # 全体レジスタロードストア命令のフォーマット
   vl1r.v v3, (a0)       # vl1re8.vの疑似命令

   vl1re8.v    v3, (a0)  # a0に保持されているアドレスからVLEN/8バイトだけロードしv3に格納する
   vl1re16.v   v3, (a0)  # a0に保持されているアドレスからVLEN/16バイトだけロードしv3に格納する
   vl1re32.v   v3, (a0)  # a0に保持されているアドレスからVLEN/32バイトだけロードしv3に格納する
   vl1re64.v   v3, (a0)  # a0に保持されているアドレスからVLEN/64バイトだけロードしv3に格納する
   # vl1re128.v  v3, (a0)
   # vl1re256.v  v3, (a0)
   # vl1re512.v  v3, (a0)
   # vl1re1024.v v3, (a0)

   vl2r.v v2, (a0)       # vl2re8.v v2, (a0)の疑似命令

   vl2re8.v    v2, (a0)  # a0に保持されているアドレスから2*VLEN/8バイトだけロードしv2-v3に格納する
   vl2re16.v   v2, (a0)  # a0に保持されているアドレスから2*VLEN/16バイトだけロードしv2-v3に格納する
   vl2re32.v   v2, (a0)  # a0に保持されているアドレスから2*VLEN/32バイトだけロードしv2-v3に格納する
   vl2re64.v   v2, (a0)  # a0に保持されているアドレスから2*VLEN/64バイトだけロードしv2-v3に格納する
   # vl2re128.v  v2, (a0)
   # vl2re256.v  v2, (a0)
   # vl2re512.v  v2, (a0)
   # vl2re1024.v v2, (a0)

   vl4r.v v4, (a0)       # vl4re8.vの疑似命令

   vl4re8.v    v4, (a0)  # a0に保持されているアドレスから4*VLEN/8バイトだけロードしv4-v7に格納する
   vl4re16.v   v4, (a0)
   vl4re32.v   v4, (a0)
   vl4re64.v   v4, (a0)
   # vl4re128.v  v4, (a0)
   # vl4re256.v  v4, (a0)
   # vl4re512.v  v4, (a0)
   # vl4re1024.v v4, (a0)

   vl8r.v v8, (a0)       # vl8re8.vの疑似命令

   vl8re8.v    v8, (a0)  # a0に保持されているアドレスから8*VLEN/8バイトだけロードしv8-v15に格納する
   vl8re16.v   v8, (a0)
   vl8re32.v   v8, (a0)
   vl8re64.v   v8, (a0)
   # vl8re128.v  v8, (a0)
   # vl8re256.v  v8, (a0)
   # vl8re512.v  v8, (a0)
   # vl8re1024.v v8, (a0)

   vs1r.v v3, (a1)      # v3をa1に保持されているアドレスにストアする
   vs2r.v v2, (a1)      # v2-v3をa1に保持されているアドレスにストアする
   vs4r.v v4, (a1)      # v4-v7をa1に保持されているアドレスにストアする
   vs8r.v v8, (a1)      # v8-v15をa1に保持されているアドレスにストアする

Note: サポートされていないEEWの値に対する vlr 命令では、 実装上、不正命令例外を発生させる必要があります。

Note: マスクヒントとして、レジスタ全体のマスクロード命令(vl1re1.vd, (rs1))を追加することを検討しましたが、 これは現在PoRにはありません。