RISC-Vのベクトル拡張の理解に当たり、複雑怪奇なシステムレジスタを理解するのは大変だ。ここではRISC-Vベクトル拡張が備える謎のシステムレジスタについて一気に解説していきたい。
ちなみに最新のRISC-V Vector Extension 0.9をベースに解説している。
RISC-V Vector Extension 0.9では以下に示すCSRを新たに定義している。
Vector Extensionを理解するにあたりとりあえず無視していいのが、"vxsat", "vxrm", "vcsr"である。これらのCSRはFixed Point命令のためのレジスタなので本質にあまり関係しない。
そしていくつかVector Extension固有の定数について理解しておこう。以下に示す数値は「実装に固有の数値」であり、ハードウェアにより定義されるものだ。これらは命令の実行中に変更することはできない。
- VLEN : ベクトルレジスタ1本が持つビット長。例えばベクトルレジスタ1本が512ビットであれば、VLEN=512である。
- ELEN : ベクトルレジスタ内の1つの要素を示す最大のビット数。例えばベクトルレジスタで最大64ビットのデータを1要素として扱うことができるとすると、ELEN=64となる。当然、ELENはXLEN or FLENよりも大きな数値である必要がある。
- SLEN : ベクトルレジスタ内のインデックス参照方法をストリップして表現することができる。これは複雑なので必要になれば理解すれば良い。
これを踏まえ、システムレジスタでまず理解しなければならないのはvtype
CSRだ。このCSRはいろんなフィールドを持っており、理解するのが難しい。
vsew
から始める。これは現在のベクトルレジスタの1要素を示すビット長だ。RISC-V Vector Extensionでは、1命令で複数のデータ長を扱う。例えば32ビットのベクトル加算も、64ビットのベクトル加算も両方ともvadd.vv
で表現されるため、今自分がどのビット長で計算しているのかが分からない。このためvtype.vsew
で現在実行中のデータ長を把握する。
もう一つ、vlmul
というフィールドがある。これは、1つのレジスタインデックスで複数のベクトルレジスタを取り扱うことができるようになる。vlmul
の値に伴いLMUL
というパラメータがエンコードされ、一度に何個のレジスタを扱うかを指定できる。大量のデータを扱い、同じ操作を複数のレジスタに対して実行する必要があるときに便利だ。
具体的には、通常はvtype.vlmul=0(LMUL=1)
の時、以下の命令は、v8
レジスタとv16
レジスタを読み取り、加算を行いその結果をv0
レジスタに格納する。
vadd.vv v0, v8, v16 # v0 <= v8 + v16
ところが、vtype.vlmul=1(LMUL=2)
とすると、vNレジスタ(Nは2の倍数でなければならない、それ以外は命令例外が発生する)とvN+1レジスタに対して操作が適用される。つまり上記のvadd.vv
命令は、
vadd.vv v0, v8, v16 # {v1, v2} <= {v9,v8} + {v17,v16}
となり、1命令で複数のレジスタを扱うことができる。命令フェッチ幅を抑えるための巨大なベクトルレジスタのような使い方ができるようになる。これは最大でLMUL=8まで指定することができ、最大でvN~vN+7のレジスタを同時に扱うことができるようになる。
これを踏まえ、現在ベクトル命令が実行されると何個の要素が1つのレジスタとして扱うことができるようになるのだろうか?これを示しているのがvl
レジスタである。
vlレジスタの値は:「VLEN / SEW * LMUL」で表現される。SEW
はvtype.sew
をエンコードした値、LMUL
はvtype.vlmul
をエンコードした値である。
具体例を見る。VLEN=512
でSEW=64
、LMUL=1
である場合、vl = 512 / 64 * 1 = 8
であり、64ビットの値が8個まとまったものが1つのベクトルレジスタに集約されている。これは簡単。
では次に、VLEN=512
でSEW=32
、LMUL=1
である場合、vl = 512 / 32 * 1 = 16
となり、32ビットの値が16個まとまったものが1つのベクトルレジスタに集約されていると考えられる。つまり、SEW
とLMUL
の値によってvl
の値は逐次変化していく!
さらにややこしいことに、VLEN=512
でSEW=64
、LMUL=4
とすると、vl=512 / 64* 4 = 32
となり、1つのレジスタインデックスを参照することで32個の要素に対する処理が行われることを意味する。このように、コンフィグレーションによりレジスタの定義範囲が大きく変わるので、現在の状態が把握しにくいのがRISC-V Vector Extensionの特徴である。
このシステムレジスタの設定方法だが、多くの場合はvsetvli
命令によって設定される。vsetvli
の命令仕様についてはあまり詳細は説明しないが、Spikeの実装を参考にするのが良いと思う。
vsetvli rd, rs1, vtypei # rd = new vl, rs1 = AVL, vtypei = new vtype setting vsetvl rd, rs1, rs2 # rd = new vl, rs1 = AVL, rs2 = new vtype value
// set vl if (vlmax == 0) { vl = 0; } else if (rd == 0 && rs1 == 0) { vl = vl > vlmax ? vlmax : vl; } else if (rd != 0 && rs1 == 0) { vl = vlmax; } else if (rs1 != 0) { vl = reqVL > vlmax ? vlmax : reqVL; }