FPGA開発日記

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

RISC-Vベクトル拡張仕様書 v1.0 を読み直す (16. 整数算術演算命令)

RISC-Vベクトル拡張仕様書の読み直し。基本的な算術演算系のベクトル命令の説明。

github.com

github.com


11. ベクトル整数算術演算命令

整数ベクトル算術演算命令が提供されています。

11.1. ベクトル単一幅整数加算減算命令

ベクトル整数加減算命令が提供されています。 ベクトル・スカラ形式においては逆減算命令も提供されています。

# 整数加算
vadd.vv vd, vs2, vs1, vm   # ベクトル-ベクトル
vadd.vx vd, vs2, rs1, vm   # ベクトル-スカラ
vadd.vi vd, vs2, imm, vm   # ベクトル-即値

# 整数減算
vsub.vv vd, vs2, vs1, vm   # ベクトル-ベクトル
vsub.vx vd, vs2, rs1, vm   # ベクトル-スカラ

# 整数逆減算
vrsub.vx vd, vs2, rs1, vm   # vd[i] = x[rs1] - vs2[i]
vrsub.vi vd, vs2, imm, vm   # vd[i] = imm - vs2[i]
ベクトル内の整数値は、x0 とのスカラ逆減算によって符号を反転することができます。 これは vneg vd,vs = vrsub.vx vd,vs,x0 としてアセンブリ疑似命令を定義することができます。

11.2. ベクトル幅拡張整数加減算命令

符号付きと符号なしの両方で、幅拡張加減算命令が定義されています。 これらは幅の小さいオペランドが最初に符号拡張かゼロ拡張され、 倍幅での加減算が行われます。

# 符号なし整数の幅拡張加減算命令, 2*SEW = SEW +/- SEW
vwaddu.vv  vd, vs2, vs1, vm  # vector-vector
vwaddu.vx  vd, vs2, rs1, vm  # vector-scalar
vwsubu.vv  vd, vs2, vs1, vm  # vector-vector
vwsubu.vx  vd, vs2, rs1, vm  # vector-scalar

# 符号付き整数の幅拡張加減算命令, 2*SEW = SEW +/- SEW
vwadd.vv  vd, vs2, vs1, vm  # vector-vector
vwadd.vx  vd, vs2, rs1, vm  # vector-scalar
vwsub.vv  vd, vs2, vs1, vm  # vector-vector
vwsub.vx  vd, vs2, rs1, vm  # vector-scalar

# 符号なし整数の幅拡張加減算命令, 2*SEW = 2*SEW +/- SEW
vwaddu.wv  vd, vs2, vs1, vm  # vector-vector
vwaddu.wx  vd, vs2, rs1, vm  # vector-scalar
vwsubu.wv  vd, vs2, vs1, vm  # vector-vector
vwsubu.wx  vd, vs2, rs1, vm  # vector-scalar

# 符号付き整数の幅拡張加減算命令, 2*SEW = 2*SEW +/- SEW
vwadd.wv  vd, vs2, vs1, vm  # vector-vector
vwadd.wx  vd, vs2, rs1, vm  # vector-scalar
vwsub.wv  vd, vs2, vs1, vm  # vector-vector
vwsub.wx  vd, vs2, rs1, vm  # vector-scalar

Note: スカラオペランド x0 との幅拡張加算命令を使用することにより整数値の幅を2倍に拡張することができます。 vwcvt.x.x.v vd,vs,vm = vwadd.vx vd,vs,x0,vm と vwcvtu.x.x.v vd,vs,vm = vwaddu.vx vd,vs,x0,vm により 疑似アセンブリ命令を定義することができます。

11.3. ベクトル整数拡張

ゼロ拡張と符号拡張のベクトル整数拡張命令は、SEWよりも小さいEEWの整数オペランドを SEWの幅まで拡張して書き込みレジスタに転送します。 ソースのEEWはSEWの1/2, 1/4, 1/8であり、ソースのEMULは(EEW/SEW)*LMULです。 書き込み側のEEWはSEWと同一であり、EMULはLMULと同一です。

vzext.vf2 vd, vs2, vm  # SEW/2のソースオペランドをゼロ拡張してSEW幅化し書き込む
vsext.vf2 vd, vs2, vm  # SEW/2のソースオペランドを符号拡張してSEW幅化し書き込む
vzext.vf4 vd, vs2, vm  # SEW/4のソースオペランドをゼロ拡張してSEW幅化し書き込む
vsext.vf4 vd, vs2, vm  # SEW/4のソースオペランドを符号拡張してSEW幅化し書き込む
vzext.vf8 vd, vs2, vm  # SEW/8のソースオペランドをゼロ拡張してSEW幅化し書き込む
vsext.vf8 vd, vs2, vm  # SEW/8のソースオペランドを符号拡張してSEW幅化し書き込む

ソースのEEWがサポートされていない場合もしくはEMULがLMULの最小値よりも小さい場合、 命令エンコーディングは予約されています。

11.4. ベクトル整数 キャリー付き加算 / ボロー付き減算命令

複数ワードの整数演算をサポートするために、キャリービットを操作する命令が用意されています。 各演算(加算または減算)には2つの命令が用意されています。 1つは演算結果(SEW幅)を提供し、 もう1つはキャリー出力(マスク・ブールとしてエンコードされた1ビット)を生成します。

キャリー入出力は、 <sec-mask-register-layout> 節で説明したマスクレジスタのレイアウトを用いて表現されます。 エンコーディングの制約により,キャリー入力は暗黙の v0 レジスタから出力されなければなりませんが、 キャリー出力はソース/書き込みのオーバーラップの制約を満たす任意のベクトルレジスタに書き込むことができます.

vadc と vsbc は、ソースオペランドとキャリーインまたはボローインの加算または減算を行い、 その結果をベクトルレジスタ vd に書き込みます。 これらの命令は、マスクされた命令 (vm=0) としてエンコードされていますが、すべてのボディ要素を操作し、書き戻します。 マスクされていないバージョン(vm=1)に対応するエンコーディングは予約されています。

vvmadc と vmsbc は、ソースオペランドを加算または減算し、マスクされている (vm=0) 場合にはオプションでキャリーインを加算またはボローインを減算し、 その結果をマスクレジスタ vd に書き戻します。 マスクされていない場合 (vm=1)、キャリーインやボローインはありません。 これらの命令は、マスクされていても、すべてのボディ要素を操作し、書き戻します。 これらの命令はマスク値を生成するため、常に末尾Agnosticポリシで動作します。

 # キャリー付き加算命令

 # vd[i] = vs2[i] + vs1[i] + v0.mask[i]
 vadc.vvm   vd, vs2, vs1, v0  # ベクトル-ベクトル

 # vd[i] = vs2[i] + x[rs1] + v0.mask[i]
 vadc.vxm   vd, vs2, rs1, v0  # ベクトル-スカラ

 # vd[i] = vs2[i] + imm + v0.mask[i]
 vadc.vim   vd, vs2, imm, v0  # ベクトル-即値

 # マスクレジスタフォーマットにキャリーアウトを生成する

 # vd.mask[i] = carry_out(vs2[i] + vs1[i] + v0.mask[i])
 vmadc.vvm   vd, vs2, vs1, v0  # ベクトル-ベクトル

 # vd.mask[i] = carry_out(vs2[i] + x[rs1] + v0.mask[i])
 vmadc.vxm   vd, vs2, rs1, v0  # ベクトル-スカラ

 # vd.mask[i] = carry_out(vs2[i] + imm + v0.mask[i])
 vmadc.vim   vd, vs2, imm, v0  # ベクトル-即値

 # vd.mask[i] = carry_out(vs2[i] + vs1[i])
 vmadc.vv    vd, vs2, vs1      # ベクトル-ベクトル, no carry-in

 # vd.mask[i] = carry_out(vs2[i] + x[rs1])
 vmadc.vx    vd, vs2, rs1      # ベクトル-スカラ, no carry-in

 # vd.mask[i] = carry_out(vs2[i] + imm)
 vmadc.vi    vd, vs2, imm      # ベクトル-即値, no carry-in

キャリー伝搬を実装するには、入力が変更されていない状態で2つの命令を実行する必要があるため、 破壊的な蓄積を行うと、正しい結果を得るために追加の移動が必要になります。

  # v4に値を蓄積する複数ワード算術演算命令列
  vmadc.vvm v1, v4, v8, v0  # 一時レジスタv1にキャリーを格納する
  vadc.vvm v4, v4, v8, v0   # 加算を行う
  vmmv.m v0, v1             # 次のワードのために一時キャリーをv0に移動する

ボロー付き減算命令vsbcは、減算のためのロングワード演算をサポートするための機能を果たします。 即値での減算命令はありません。

 # ボロー付きの差分を計算する

 # vd[i] = vs2[i] - vs1[i] - v0.mask[i]
 vsbc.vvm   vd, vs2, vs1, v0  # Vector-vector

 # vd[i] = vs2[i] - x[rs1] - v0.mask[i]
 vsbc.vxm   vd, vs2, rs1, v0  # Vector-scalar

 # マスクレジスタフォーマットでボロー出力を生成する

 # vd.mask[i] = borrow_out(vs2[i] - vs1[i] - v0.mask[i])
 vmsbc.vvm   vd, vs2, vs1, v0  # Vector-vector

 # vd.mask[i] = borrow_out(vs2[i] - x[rs1] - v0.mask[i])
 vmsbc.vxm   vd, vs2, rs1, v0  # Vector-scalar

 # vd.mask[i] = borrow_out(vs2[i] - vs1[i])
 vmsbc.vv    vd, vs2, vs1      # Vector-vector, no borrow-in

 # vd.mask[i] = borrow_out(vs2[i] - x[rs1])
 vmsbc.vx    vd, vs2, rs1      # Vector-scalar, no borrow-in

vmsbc では,切り捨て前の差が負であるときに限りボローは1と定義される。

vadcvsbcでは書き込みベクトルレジスタv0の場合、命令エンコーディングが予約されます。

Note: この制約は、マスクレジスタを上書きするマスク付きベクトル演算の制約に相当します。