FPGA開発日記

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

RISC-Vベクトル拡張を使ったstrlenの実装

RISC-Vのベクトル拡張について、いくつかのサンプルプログラムを用いて使い方を見ていく。 次はstrlen()について。これはmemcpy()とは違って、メモリアクセスするサイズが良く分からない場合のもの。

この場合には、ベクトルロードはFault-only-Firstロード命令を用いる。 ちなみにこれに類似した命令はArmのA64FXにも実装されている。

github.com

この命令のモチベーションは、ベクトル長が分からない場合に「どこまでアクセスできるのか」が分からない場合において有効な命令である。

例えばVLEN=1024のベクトル拡張の場合はvle8.v(8ビット単位の粒度でのベクトルロード命令)では、1024ビットのメモリに対してロードを実行する。 もしこの中にPMAやPMPによりメモリアクセス不可能な領域がある場合、ベクトルロード中にメモリアクセス例外が発生する。

ページ単位のメモリアクセス制御を行っている場合、一般的にページサイズは4Kであるが、ページの概念が存在しないシステムではアクセスできないページにアクセスしてしまう可能性がある。

この時に、Fault-only-firstロード命令は「どの領域までアクセスするのか分からない」場合に有効な命令となる。 Fault-only-firstロード命令は、「先頭のメモリアクセス要素」に対して例外が発生した場合には例外が発生し、それ以外のメモリアクセス要素に対して例外が発生する場合には例外を発生せずにその場で命令を終了しvlをアップデートする。 この命令を用いることで、strlenのようにアクセスしてはならない場所までアクセスする可能性のある命令でも、例外を発生せずにループを終了することができる。

f:id:msyksphinz:20210728214921p:plain

この前提を元に、strlenのサンプルコードを見ていく。

# size_t strlen(const char *str)
# a0 holds *str

strlen:
    mv a3, a0             # Save start
loop:
    vsetvli a1, x0, e8, m8, ta, ma  # Vector of bytes of maximum length
    vle8ff.v v8, (a3)      # Load bytes
    csrr a1, vl           # Get bytes read
    vmseq.vi v0, v8, 0    # Set v0[i] where v8[i] = 0
    vfirst.m a2, v0       # Find first set bit
    add a3, a3, a1        # Bump pointer
    bltz a2, loop         # Not found?

    add a0, a0, a1        # Sum start + bump
    add a3, a3, a2        # Add index
    sub a0, a3, a0        # Subtract start address+bump

    ret

vsetvliの引数はx0が設定され、取れる限り最大長のベクトル長が設定される(VLMAX)。 次にvle8ff.vを用いてバイト単位でベクトルロードを行い、アクセス不許可の領域に対する配慮も行う。

そしてvmseq.viを用いて、メモリ要素中の0の要素の場所置見つける。そしてvfirst.mによりそのインデックスを見つけ、そうでなかったらループを繰り返す。