ベクトル命令の全命令をリストアップしたので、次はいよいよベクトル命令のデコーダ生成に入る。デコーダの生成を人力で頑張る必要はなく、私のRISC-Vシミュレータはすべて自動的にデコーダを吐き出す仕組みが搭載されている。したがって、ひたすらベクトル命令のエンコーディングの定義を書いていけばよい。
[
/* BEGIN OPIVV */
{"name":"vadd.vv v[11:7],v[24:20],v[19:15][,vm[25]]", "length":"32", "dlength":"128", "field": ["00000", "0X", "XXXXX", "XXXXX", "000", "XXXXX", "10101", "11"], "category":"VEC", "func_suffix":"", "impl":[""], "inst_ctrl":[""]},
{"name":"vsub.vv v[11:7],v[24:20],v[19:15][,vm[25]]", "length":"32", "dlength":"128", "field": ["00001", "0X", "XXXXX", "XXXXX", "000", "XXXXX", "10101", "11"], "category":"VEC", "func_suffix":"", "impl":[""], "inst_ctrl":[""]},
{"name":"vrsub.vv v[11:7],v[24:20],v[19:15][,vm[25]]", "length":"32", "dlength":"128", "field": ["00001", "1X", "XXXXX", "XXXXX", "000", "XXXXX", "10101", "11"], "category":"VEC", "func_suffix":"", "impl":[""], "inst_ctrl":[""]},
{"name":"vminu.vv v[11:7],v[24:20],v[19:15][,vm[25]]", "length":"32", "dlength":"128", "field": ["00010", "0X", "XXXXX", "XXXXX", "000", "XXXXX", "10101", "11"], "category":"VEC", "func_suffix":"", "impl":[""], "inst_ctrl":[""]},
{"name":"vmin.vv v[11:7],v[24:20],v[19:15][,vm[25]]", "length":"32", "dlength":"128", "field": ["00010", "1X", "XXXXX", "XXXXX", "000", "XXXXX", "10101", "11"], "category":"VEC", "func_suffix":"", "impl":[""], "inst_ctrl":[""]},
...
今回はまだすべての命令定義の項目を埋め切っていないので、すべて横一列に書くことで後で一気に修正が行いやすい状態を保っている。この形式で合計300を超えるRISC-Vの命令リストをすべて並びあげた。
私のシミュレータはこのデコードテーブルをすべて舐めたうえで、適切なデコードジャンプテーブルを生成していく。例えば、vadd.vv
命令に到達するまでには以下のテーブルをジャンプしていく。
vadd.vv 命令
命令エンコーディング ["00000", "0X", "XXXXX", "XXXXX", "000", "XXXXX", "10101", "11"]
swimmer_riscv/src/inst_decoder_riscv.cpp
InstId_t RiscvDec::DecodeInst (InstWord_t inst) {
InstWord_t field_LD = ExtractLDField (inst);
switch (field_LD) {
case 0x03 :
...
...
return DecodeInst_LD_11 (inst); break;
InstId_t RiscvDec::DecodeInst_LD_11 (InstWord_t inst) {
InstWord_t field_OP = ExtractOPField (inst);
switch (field_OP) {
case 0x15 :
...
return DecodeInst_LD_11_OP_10101 (inst); break;
InstId_t RiscvDec::DecodeInst_LD_11_OP_10101 (InstWord_t inst) {
InstWord_t field_F3 = ExtractF3Field (inst);
switch (field_F3) {
...
case 0x00 :
...
return DecodeInst_LD_11_OP_10101_F3_000 (inst); break;
InstId_t RiscvDec::DecodeInst_LD_11_OP_10101_F3_000 (InstWord_t inst) {
InstWord_t field_R3 = ExtractR3Field (inst);
switch (field_R3) {
case 0x00 :
return InstId_t::INST_ID_VADD_VV;
これが例えばさらに難しくてvsub.vv
とvrsub.vv
を見分ける場合、
InstWord_t field_R3 = ExtractR3Field (inst);
switch (field_R3) {
case 0x00 :
return InstId_t::INST_ID_VADD_VV;
case 0x01 :
return DecodeInst_LD_11_OP_10101_F3_000_R3_00001 (inst); break;
さらにDecodeInst_LD_11_OP_10101_F3_000_R3_00001()
の中ですべての要素を洗い出して分類する。
InstId_t RiscvDec::DecodeInst_LD_11_OP_10101_F3_000_R3_00001 (InstWord_t inst) {
InstWord_t field_F2 = ExtractF2Field (inst);
switch (field_F2) {
case 0x00 :
case 0x01 :
return InstId_t::INST_ID_VSUB_VV;
case 0x02 :
case 0x03 :
return InstId_t::INST_ID_VRSUB_VV;
default : return InstId_t::INST_ID_SENTINEL_MAX;
}
return InstId_t::INST_ID_SENTINEL_MAX;
}
あとはすべての命令向けの関数を定義すれば完了だ。これで命令のスケルトンがすべて完成した。
swimmer_riscv/src/inst_riscv_vector.cpp
#include <stdint.h>
#include <gmpxx.h>
...
void InstEnv::RISCV_INST_VADD_VV(InstWord_t inst_hex) {}
void InstEnv::RISCV_INST_VSUB_VV(InstWord_t inst_hex) {}
void InstEnv::RISCV_INST_VRSUB_VV(InstWord_t inst_hex) {}
...
void InstEnv::RISCV_INST_VFWNMACC_VF(InstWord_t inst_hex) {}
void InstEnv::RISCV_INST_VFWMSAC_VF(InstWord_t inst_hex) {}
void InstEnv::RISCV_INST_VFWNMSAC_VF(InstWord_t inst_hex) {}