RISC-V Vector命令の仕様がかなり固まってきた。いつの間にかVer 0.8が公開され、命令のニーモニックも安定しつつある。
https://riscv.github.io/documents/riscv-v-spec/riscv-v-spec.pdf
ついでに、自分で日本語訳したRISC-V Vector命令仕様書がまだVer.0.7のままなのでアップデートしなければ。
RVV (RISC-V)の最も基本的なパラメータ
RISC-Vベクトル拡張の最も基本的なパラメータは以下の3つである。
- 1ベクトル要素の最大ビットサイズELEN。2の累乗の必要がある。
- ベクトルレジスタのビットサイズVLEN ≥ ELEN, 2の累乗の必要がある。
- ストリップビット距離SLEN。VLEN ≥ SLEN ≥ 32 である必要がある。それぞれは2の累乗の必要がある。
それぞれ、自作RISC-Vシミュレータでそれぞれ以下のように表現した。
class RiscvPeThread : public EnvBase { ... /* * Vector Constant Parameters */ const int32_t RVV_ELEN = 32; // The maximum size of a single vector element in bits, ELEN, which must be a power of 2. const int32_t RVV_VLEN = 512; // The number of bits in a vector register, VLEN ≥ ELEN, which must be a power of 2. const int32_t RVV_SLEN = 32; // The striping distance in bits, SLEN, which must be VLEN ≥ SLEN ≥ 32, and which must be a power of 2. ... public: int32_t get_ELEN() { return RVV_ELEN; } int32_t get_VLEN() { return RVV_VLEN; } int32_t get_SLEN() { return RVV_SLEN; } ...
RVVサポートに必要なレジスタ
RVVのサポートには以下のシステムレジスタが新たに追加される。
Address | Privilege | Name | Description |
---|---|---|---|
0x008 | URW | vstart | ベクトルスタート位置を示すレジスタ |
0x009 | URW | vxsat | 浮動小数点飽和フラグレジスタ |
0x00A | URW | vxrm | 浮動小数点丸めモードレジスタ |
0xC20 | URO | vl | ベクトル長レジスタ |
0xC21 | URO | vtype | ベクトルデータ型レジスタ |
それぞれ自作RISC-Vシミュレータに以下のように実装した。これらのCSRはすべてRubyのスクリプトで自動生成するように設定している。
$sysreg_table.push(Array[0x008, 'URW', 'vstart' , Array[Array[xlen-1, 0, 'vstart' , 'RW', 0]]]) $sysreg_table.push(Array[0x009, 'URW', 'vxsat' , Array[Array[xlen-1, 0, 'vxsat' , 'RW', 0]]]) $sysreg_table.push(Array[0x00A, 'URW', 'vxrm' , Array[Array[xlen-1, 0, 'vxrm' , 'RW', 0]]]) ... $sysreg_table.push(Array[0xC20, 'URO', 'vl', Array[Array[xlen-1, 0, 'vl', 'RO', 0]]]) $sysreg_table.push(Array[0xC21, 'URO', 'vtype', Array[Array[xlen-1, xlen-1, 'vill', 'RO', 0], Array[6, 5, 'vediv', 'RO', 0], Array[4, 2, 'vsew', 'RO', 0], Array[1, 0, 'vlmul', 'RO', 0]]]) $sysreg_table.push(Array[0xC22, 'URO', 'vlenb', Array[Array[xlen-1, 0, 'vlenb', 'RO', 0]]])
上記の記述で、例えばvtype
については以下のような構造体が自動的に生成される。
union { struct { uint64_t vlmul : 2; uint64_t vsew : 3; uint64_t vediv : 2; uint64_t dummy_0 : 56; uint64_t vill : 1; } bit_vtype; uint64_t vtype; } vtype;
これに対して、Read/Writeの実装を付け加えていく。
template <typename Xlen_t> CsrAccResult CsrEnv::Read_VTYPE (Xlen_t *data, PrivMode mode) { *data = vtype.vtype; return CsrAccResult::Normal; } template <typename Xlen_t> CsrAccResult CsrEnv::Write_VTYPE (Xlen_t data, PrivMode mode) { vtype.vtype = data; DWord_t vl = m_pe_thread->get_VLEN() * vtype.bit_vtype.vlmul / vtype.bit_vtype.vsew; Write_VL (vl, mode); return CsrAccResult::Normal; }
vsetvli, vsetvl命令の初期実装
まずは、RVVの初期設定を行うための基本的な命令を作り込んで行こう。
それぞれ、vsetvli
, vsetvl
という命令が定義されている。
それぞれ、JSONファイルを定義して命令のデコーダとテンプレートを作成する。
// // RVV : Vector Extension // { "name":"vsetvli r[11:7],r[19:15],h[30:20]", "length":"32", "dlength":"32", "field":["0XXXX", "XX", "XXXXX", "XXXXX", "111", "XXXXX", "10101", "11"], "category":"VECTOR", "func_suffix":"", "impl":[""], "inst_ctrl":[] }, { "name":"vsetvl r[11:7],r[19:15],r[24:20]", "length":"32", "dlength":"32", "field":["10000", "00", "XXXXX", "XXXXX", "111", "XXXXX", "10101", "11"], "category":"VECTOR", "func_suffix":"", "impl":[""], "inst_ctrl":[] },
さらに、実際の実装を作り込んで行く。
void InstEnv::RISCV_INST_VSETVLI (InstWord_t inst_hex) { if (!m_pe_thread->IsVECAvailable ()) { m_pe_thread->GenerateException (ExceptCode::Except_IllegalInst, 0); return; } RegAddr_t rs1_addr = ExtractR1Field (inst_hex); RegAddr_t rd_addr = ExtractRDField (inst_hex); DWord_t rs1_val = m_pe_thread->ReadGReg<DWord_t> (rs1_addr); m_pe_thread->CSRWrite (static_cast<Addr_t>(SYSREG_ADDR_VTYPE), ExtractBitField (inst_hex, 26, 20)); }
とりあえず、これで基本的な命令の初期実装は完了だ。