FPGA開発日記

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

サイクル精度シミュレータSniperにRISC-Vのベクトル命令の情報を追加する

サイクル精度シミュレータSniperは、トレースファイルをベースとしたサイクル精度解析シミュレータだ。 RISC-Vのサポートにおいては、SpikeからSIFTファイルを生成し、それをSniperに加えることでサイクル計算を行う。 これだけだとSniper側にRISC-Vのデコード情報は必要ないように思われるが、実際にはSniper側にデコーダを用意し、それに基づいてサイクル計算のルーチンを動かす必要がある。

SniperRISC-Vサポートにおいては、RV8のデコードエンジンが採用されている。

https://github.com/michaeljclark/rv8

これは基本命令とFPU命令は備わっているが、ベクトル命令は備わっていない。したがって、ベクトル命令サポートを追加する必要がある。

  1. meta/opcodesの改造

といっても、meta/opcodesを直接編集してしまうと、生成される命令IDがずれてしまい面倒なので、別のファイルを用意してこちらにベクトル命令のオペコードを追加していく。

  • meta/vector_opcodes
# format of a line in this file:
# <instruction name> [<args> ...] <opcode> <codec> <extension>
#
# <args> is one of rd, rs1, rs2, frd, frs1, frs2, frs3, imm20, imm12,
# sbimm12, simm12, shamt5, shamt6, rm, aq, rl, pred, succ
#
# <opcode> is given by specifying one or more range/value pairs:
# hi..lo=value or bit=value or arg=value (e.g. 6..2=0x45 10=1)
#
# <codec> is one of r, i, s, sb, u, uj, ...
#
# <extension> is one of { rv32, rv64, rv128 } \u00B7 { i, m, a, f, d, s, c }


# RVV (actually its rvv but temporary assigned to F)
# lumop = 24..20
vsetvli    rd rs1 oimm12       31..31=0  14..12=7 6..2=21 1..0=3 i    rv64v
vsetivli   rd imm12 oimm12     31..30=3  14..12=7 6..2=21 1..0=3 i+ii rv64v
vsetvl     rd rs1 rs2          31..25=64 14..12=7 6..2=21 1..0=3 r    rv64v

vle8.v     vd rs1              31..29=0 28..28=0 27..26=0 24..20=0 14..12=0 6..2=1 1..0=3 i+v  rv64v
vse8.v     vs3 rs1             31..29=0 28..28=0 27..26=0 24..20=0 14..12=0 6..2=9 1..0=3 s+v  rv64v
vle16.v    vd rs1              31..29=0 28..28=0 27..26=0 24..20=0 14..12=5 6..2=1 1..0=3 i+v  rv64v
vse16.v    vs3 rs1             31..29=0 28..28=0 27..26=0 24..20=0 14..12=5 6..2=9 1..0=3 s+v  rv64v
vle32.v    vd rs1              31..29=0 28..28=0 27..26=0 24..20=0 14..12=6 6..2=1 1..0=3 i+v  rv64v
vse32.v    vs3 rs1             31..29=0 28..28=0 27..26=0 24..20=0 14..12=6 6..2=9 1..0=3 s+v  rv64v
vle64.v    vd rs1              31..29=0 28..28=0 27..26=0 24..20=0 14..12=7 6..2=1 1..0=3 i+v  rv64v
vse64.v    vs3 rs1             31..29=0 28..28=0 27..26=0 24..20=0 14..12=7 6..2=9 1..0=3 s+v  rv64v
# Fault-only first load instruction
vleff8.v   vd rs1              31..29=0 28..28=0 27..26=0 24..20=16 14..12=0 6..2=1 1..0=3 i+v  rv64v
vleff16.v  vd rs1              31..29=0 28..28=0 27..26=0 24..20=16 14..12=5 6..2=1 1..0=3 i+v  rv64v
vleff32.v  vd rs1              31..29=0 28..28=0 27..26=0 24..20=16 14..12=6 6..2=1 1..0=3 i+v  rv64v
vleff64.v  vd rs1              31..29=0 28..28=0 27..26=0 24..20=16 14..12=7 6..2=1 1..0=3 i+v  rv64v
# Whole Register load instruction
vl1re8.v   vd rs1              31..29=0 28..28=0 27..26=0 24..20=8 14..12=0 6..2=1 1..0=3 i+v  rv64v
vl1re16.v  vd rs1              31..29=0 28..28=0 27..26=0 24..20=8 14..12=5 6..2=1 1..0=3 i+v  rv64v
vl1re32.v  vd rs1              31..29=0 28..28=0 27..26=0 24..20=8 14..12=6 6..2=1 1..0=3 i+v  rv64v

この情報から、src/asm/switch.hというデコード関数が作られることになり、これがSniperのデコードエンジンに輸入される。

template <bool rv32, bool rv64, bool rv128, bool rvi, bool rvm, bool rva, bool rvs, bool rvf, bool rvd, bool rvq, bool rvc, bool rvv>
inline opcode_t decode_inst_op(riscv::inst_t inst)
{
    opcode_t op = rv_op_illegal;
    switch (((inst >> 0) & 0b11) /* inst[1:0] */) {
        case 0:
            // c.addi4spn c.fld c.lw c.flw c.fsd c.sw c.fsw c.ld c.sd c.lq c.sq
            switch (((inst >> 13) & 0b111) /* inst[15:13] */) {
                case 0: if (rvc) op = rv_op_c_addi4spn; break;
                case 1: if (rvc) op = rv_op_c_fld; break; // c.fld c.lq
                case 2: if (rvc) op = rv_op_c_lw; break;
                case 3:
                    if (rvc && rv32) op = rv_op_c_flw;
                    else if (rvc && rv64) op = rv_op_c_ld;
                    break;
                case 5: if (rvc) op = rv_op_c_fsd; break; // c.fsd c.sq
                case 6: if (rvc) op = rv_op_c_sw; break;
                case 7:
                    if (rvc && rv32) op = rv_op_c_fsw;
                    else if (rvc && rv64) op = rv_op_c_sd;
                    break;
            }
            break;
        case 1:
            // c.nop c.addi c.jal c.li c.addi16sp c.lui c.srli c.srai c.andi c.sub c.xor c.or ...
            switch (((inst >> 13) & 0b111) /* inst[15:13] */) {
                case 0:
                    // c.nop c.addi
                    switch (((inst >> 2) & 0b11111111111) /* inst[12:2] */) {
                        case 0: if (rvc) op = rv_op_c_nop; break;
                        default: if (rvc) op = rv_op_c_addi; break;
                    }
                    break;
  1. meta/codecの追加

上記のmeta/opcodesでは、i+vなどという記法が追加されているが、これはmeta/codecsに定義されている。

  • meta/codecs
i+v        vd,rs1                 vd rs1
s+v        vs3,rs1                vs3 rs1
i+vs       vd,rs1,rs2             vd rs1 rs2
s+vs       vs3,rs1,rs2            vs3 rs1 rs2
i+vv       vd,vs2,vs1             vd vs2 vs1
i+vr       vd,vs2,rs1             vd vs2 rs1
i+vi       vd,vs2,simm5           vd vs2 simm5
i+v        vd,vs2                 vd vs2
i+ii       rd,imm12,oimm12        rd imm12 oimm12

まあこの記法はよくわからないのだが、要するに命令毎のオペランドリストに対して固有のコーデックを記述するらしい。 それぞれのオペランドリスト形式については、meta/formatsに記述しておく。

  • meta/formats
# Vector
vd,rs1
vs3,rs1
vd,rs1,rs2
vs3,rs1,rs2
vd,vs2,vs1
vd,vs2,rs1
vd,vs2,simm5
vd,vs2,simm5
vd,vs2
rd,imm12,oimm12
  1. meta/registersの追加

ベクトルレジスタを追加する。

v0  v0  vreg  caller  "Vector Registers"
v1  v1  vreg  caller  "Vector Registers"
v2  v2  vreg  caller  "Vector Registers"
v3  v3  vreg  caller  "Vector Registers"
v4  v4  vreg  caller  "Vector Registers"
v5  v5  vreg  caller  "Vector Registers"
v6  v6  vreg  caller  "Vector Registers"
v7  v7  vreg  caller  "Vector Registers"
v8  v8  vreg  callee  "Vector Registers"
v9  v9  vreg  callee  "Vector Registers"
v10 v10 vreg  caller  "Vector Registers"
v11 v11 vreg  caller  "Vector Registers"
v12 v12 vreg  caller  "Vector Registers"
v13 v13 vreg  caller  "Vector Registers"
v14 v14 vreg  caller  "Vector Registers"
v15 v15 vreg  caller  "Vector Registers"
v16 v16 vreg  caller  "Vector Registers"
v17 v17 vreg  caller  "Vector Registers"
v18 v18 vreg  callee  "Vector Registers"
v19 v19 vreg  callee  "Vector Registers"
v20 v20 vreg  callee  "Vector Registers"
v21 v21 vreg  callee  "Vector Registers"
v22 v22 vreg  callee  "Vector Registers"
v23 v23 vreg  callee  "Vector Registers"
v24 v24 vreg  callee  "Vector Registers"
v25 v25 vreg  callee  "Vector Registers"
v26 v26 vreg  callee  "Vector Registers"
v27 v27 vreg  callee  "Vector Registers"
v28 v28 vreg  caller  "Vector Registers"
v29 v29 vreg  caller  "Vector Registers"
v30 v30 vreg  caller  "Vector Registers"
v31 v31 vreg  caller  "Vector Registers"

ここまでで、RV8に対する基本的な追加は完了となる。次は、Sniperに対してデコード情報を追加で記述していくことになる。