RISC-Vベクトル拡張命令は非常に複雑なことで有名だが、命令を読み込んでISSの実装、RTLの実装をしているなかで非常に仕様として分かりにくい、複雑な命令があるのでメモしておく。
ロードストア命令には、大きく分けて4つのアドレッシングモードがある。
- ユニットストライドアドレッシング(Unit Stride): 単純に各要素のアドレスがメモリ上で隣接している
- ストライドアドレッシング(Strided) : ストライドのアドレスが汎用レジスタに格納されており、各要素は線形に飛び飛びになっている
- オーダー付きインデックスアドレッシング(Index Ordered): 各要素のアドレスは汎用レジスタ+ベクトルレジスタの各要素となり、各要素のアドレスは完全にランダムとなる。デバイス向けアクセスなど、安全にアクセスされることを保証しなければならない。
- アンオーダーインデックスアドレッシング(Index Unordered)s: 各要素のアドレスは汎用レジスタ+ベクトルレジスタの各要素となり、各要素のアドレスは完全にランダムとなる
ちなみにこれだけではなくて、ベクトルメモリアクセスには"Segmented"と呼ばれるアドレッシングモードもあるので、話はさらにややこしくなる。
代表的なこれらの命令を並べてみるとこんな感じだ。<EEW>
の部分には8,16,32,64などのアクセスサイズが入る。
<2-8>
の部分は1命令で同時にアップデートできるレジスタの数を指す(8つのベクトルレジスタを同時にアップデートできる命令が存在しているなんてとてもRISCとは思いたくないが...)
Non-segment | Segment | |
---|---|---|
Unit-Stride | VLE |
VLSEG<2-8>E |
Strided | VLSE |
VLSSEG<2-8>E |
Index Ordered | VLOXEI |
VLOXSEG<2-8>EI |
Index Unordered | VLUXEI |
VLOXSEG<2-8>EI |
上記を見ればわかる通り、普通RISC-Vのベクトル拡張命令はvtype
レジスタに格納されているデータ幅の単位に従って演算が行われるが、ロードストア命令はそうではない。ロードストア命令はそれぞれ、
- VLE8.v (8ビットデータアクセス)
- VLE16.v (16ビットデータアクセス)
- VLE32.v (32ビットデータアクセス)
などが定義されており、vtype
レジスタの値を切り替えなくても任意のサイズのデータにアクセス可能である。
逆に言うとvtype
の設定は無視されるということになる。これが後々非常に厄介ということになるのだが...
ちなみにベクトル長は変わらないので8/16/32の違いは何だと思われるかもしれないが、これはアドレスアライン例外の発生条件が異なる。
- VLUXEI命令とVLOXEI命令の違い
VLUXEI命令は"Vector Load with Unordered with Indexed EI"と考えることができ、ベクトルレジスタにロードされるデータのアドレスは、ベースレジスタアドレス+ベクトルレジスタvs2の各要素とすることができる。
上記ではベクトルメモリアクセス命令はvtype
を無視するといったが、実はこのVLUXEI
, VLOXEI
(そして同じ種類のストア命令)はvtype.sew
を使用する。この辺からとんでもなく分かりにくくなる。
つまり、これらのインデックスメモリアクセス命令は、アクセスサイズはvtype.sew
, インデックスのサイズは<EEW>
から切り取られる。
通常の命令がVLE<EEW>
としてEEW
の前に"E"という接頭語が付くのに対して、VLUXEI<EEW>
はEEW
の前にEI
という接頭語が付くのは、まさしくこの違いを表現している。
疑似コードにしてみると以下のような感じになるだろうか。このときにさらにややこしくなるのが、書き込みレジスタVPR[vs3]
は(vtype.sew=16
なので)16ビット毎に処理するのに対して、VPR[vs2]
は(VLUXEI32
なので) 32ビット毎に進められることだ。つまり、半分までの値をロードするとVPR[vs2]
の値がなくなってしまう。そこで、VPR[vs2+1]
の値をさらに読みながらインデックスを生成し続けることになる。
// VLUXEI32.v の場合 (vtype.sew=16) for (i = 0; VLEN/16; i++) { VA = GPR[rs1] + VPR[vs2][i * 32 +: 32]; PA = MMU(VA); VPR[vs3][i * 16 +: 16] = MEM[PA]; }
VLEN=128のときに具体的なアドレス生成方法を示す。この表から分かる通り、VPR[v10]
の後半は、インデックスレジスタとしてVPR[v24]
ではなくVPR[v25]
を使う必要があり、アドレス生成がさらにややこしいものとなっている。
VLUXEI32.v v10, x10, v24 (vtype.sew=16, VLEN=128)
Vector Register Write | Memory Address |
---|---|
VPR[v10][ 15: 0] |
GPR[x10] + VPR[v24][ 31: 0] |
VPR[v10][ 31: 16] |
GPR[x10] + VPR[v24][ 63: 32] |
VPR[v10][ 47: 32] |
GPR[x10] + VPR[v24][ 95: 64] |
VPR[v10][ 63: 48] |
GPR[x10] + VPR[v24][127: 96] |
VPR[v10][ 79: 64] |
GPR[x10] + VPR[v25][ 31: 0] |
VPR[v10][ 94: 80] |
GPR[x10] + VPR[v25][ 63: 32] |
VPR[v10][111: 96] |
GPR[x10] + VPR[v25][ 95: 64] |
VPR[v10][127:112] |
GPR[x10] + VPR[v25][127: 96] |