FPGA開発日記

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

自作CPUにベクトル命令を追加する実装検討 (32. LMUL>1の対応考え直し)

ベクトル命令における、LMUL>1の対応というのは結構難易度の高い問題だ。

今回はその解決方法として、LMULの値がVSETVL命令で変更されると、例外を出して物理レジスタの構成を整列し直す方式を考えたい。

この方式を実際にVerilogで記述して動作をテストしようと思ったのだが、問題にぶつかった。

以下の例外対応コードを使って物理レジスタをLMULの大きさに整列しようとしていた。以下のコードがやっていることは:

  1. まずはすべての現在のアクティブな物理レジスタの内容をメモリにストアする。
  2. LMULを所定の値に変更して、フリーリストから入手できる物理レジスタの値をすべてLMUL0, LMUL1, LMUL*2,... となるようにする(つまりLMULの倍数しか物理レジスタIDを使えないようにする)。
  3. 物理レジスタにデータを書き戻す。
    .section    .text
    csrrw   sp, sscratch, sp
    la      sp, temp_data
    sd      x10,  0(sp)
    sd      x11,  8(sp)
    sd      x12, 16(sp)
    la      x10, vector_stack_data
    csrr    x11, vlenb
    vs1r.v v0 , (x10); add   x10, x10, x11
    vs1r.v v1 , (x10); add   x10, x10, x11
    vs1r.v v2 , (x10); add   x10, x10, x11
    vs1r.v v3 , (x10); add   x10, x10, x11
/* ... 途中省略 ... */
    vs1r.v v27, (x10); add   x10, x10, x11
    vs1r.v v28, (x10); add   x10, x10, x11
    vs1r.v v29, (x10); add   x10, x10, x11
    vs1r.v v30, (x10); add   x10, x10, x11
    vs1r.v v31, (x10); add   x10, x10, x11

    csrr   x12, 0x00b  # vscratch
    srli   x12, x12, 20
    fence.i
    vsetvl x0, x0, x12

    la      x10, vector_stack_data
    vl1r.v v0 , (x10); add   x10, x10, x11
    vl1r.v v1 , (x10); add   x10, x10, x11
    vl1r.v v2 , (x10); add   x10, x10, x11
    vl1r.v v3 , (x10); add   x10, x10, x11
    vl1r.v v4 , (x10); add   x10, x10, x11
    vl1r.v v5 , (x10); add   x10, x10, x11
/* ... 途中省略 ... */
    vl1r.v v28, (x10); add   x10, x10, x11
    vl1r.v v29, (x10); add   x10, x10, x11
    vl1r.v v30, (x10); add   x10, x10, x11
    vl1r.v v31, (x10); add   x10, x10, x11

    ld      x10,  0(sp)
    ld      x11,  8(sp)
    ld      x12, 16(sp)
    csrrw   sp, sscratch, sp

    mret

こうすることにより、レジスタの内容をキープしながら、物理レジスタIDを整列する。 そうすると、LMUL=8とした場合は`vadd v8, v0, v16を実行すると、v8の新たな物理レジスタの先頭をxとすると、x, x+1, x+2, x+3, ..., x+7がv8, v9, v10, ... v15 として扱うことができるようになる(LMUL=8なのでv8の物理レジスタIDから連続した8個の物理レジスタはマイクロアーキテクチャ的に自動的に確保されたものとする)。

その代わりにフリーリストのサイズはLMUL分の1となり、取れる値はLMULの倍数のみとなる。


こうすることでLMULへの簡単な対応を実現しようとしたのだが、問題にぶつかった。 vsetvl命令実行後にフリーリストの大きさが変わり、LMULの倍数しか物理レジスタが取れなくなるのだが、その後にvl1r.vを実行すると、これらの命令はLMULを無視して1レジスタ単位でデータを扱うので、取得できる物理レジスタIDと合わずに矛盾が生じる。 例えば、vl1r.v v7が実行されると、新しい物理レジスタID=32が取得されると同時に、もともとの物理レジスタID=7が解放され、フリーリストにLMULの倍数ではない7というIDが入ってしまい矛盾が生じる。 この時、v7はすでにv0レジスタグループに入っているのだから、物理レジスタID=7を解放せずに、v0の持つID=0を解放すればいいのかもしれない。しかしそうするとv0の物理レジスタIDが0から32に自動的に切り替わってしまい、データの移動が発生しておらずやはり矛盾が生じる。やはりLMULの粒度と異なるベクトル操作命令を何も考えずにぶち込むのは非常に問題となる。 ベクトル・ロードストアはこのようにvsetvl命令と関係なくEMULを変えてくるので結構面倒くさい。演算命令だけならうまくいくはずなのだが...

と、ここまで考えて、これを一時的に解決するためには、LMUL事に通常のUnit Stride命令でデータの書き戻しを行えばいいんじゃないか。

vs1r x 32によるレジスタ・データのバックアップ
vsetvl命令によるLMULの変更
if (LMUL==1)
vle8.v v0, v1, v2, ... v31によるデータ復帰
else if (LMUL==2) 
vle8.v v0, v2, ... v30によるデータ復帰
else if (LMUL==4)
vle8.v v0, v4, ... v28によるデータ復帰
else if (LMUL==8)
vle8.v v0, v8, v16, v24によるデータ復帰

マイクロコードをこのように変更して再実験を行う。