FPGA開発日記

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

rv8を解析してサイクル精度シミュレータSniperのログ表示を見やすくする

rv8はRISC-VのシミュレータやJITの機能を持つ豊富なツール群だ。命令デコーダも搭載されており、Sniperもrv8のデコーダ機能を使って命令デコードを行っている。

が、実はこの機能が少しややこしい。正しく命令追加をしようとすると、rv8の中に仕込まれているmetaのフォーマットを理解する必要があり詰まっていた。 最近改めて読み直すとその意味が理解できて来たので、調整を行っていく。

まず、命令のオペコードを示すopecodesを参照したとき、codecというものを理解する必要がある。 これは命令フォーマット時にどの命令フォーマットを使用するかというもので、例えばロード命令とストア命令のフォーマットは異なる。

lb         rd rs1 oimm12             14..12=0 6..2=0x00 1..0=3            i+l   rv32i rv64i rv128i
lh         rd rs1 oimm12             14..12=1 6..2=0x00 1..0=3            i+l   rv32i rv64i rv128i
lw         rd rs1 oimm12             14..12=2 6..2=0x00 1..0=3            i+l   rv32i rv64i rv128i
lbu        rd rs1 oimm12             14..12=4 6..2=0x00 1..0=3            i+l   rv32i rv64i rv128i
lhu        rd rs1 oimm12             14..12=5 6..2=0x00 1..0=3            i+l   rv32i rv64i rv128i
sb         rs1 rs2 simm12            14..12=0 6..2=0x08 1..0=3            s     rv32i rv64i rv128i
sh         rs1 rs2 simm12            14..12=1 6..2=0x08 1..0=3            s     rv32i rv64i rv128i
sw         rs1 rs2 simm12            14..12=2 6..2=0x08 1..0=3            s     rv32i rv64i rv128i

このi+lとかsとかがコーデックを示すもので、とりあえず+lはおいておいてiとかsとかが意味することは、どのような命令フォーマットで命令を成形し、どのオペランdおを使用するか、ということを示している。

  • codecs
i          rd,rs1,imm             rd rs1 imm12

s          rs2,offset(rs1)        rs1 rs2 simm12

さらにややこしいのが、このcodecは+もしくは· (. = ドットではないので注意!) でバリエーションを持つことができる。 +は本質は変えずに表現方法のみを変える。·はデコードの方式も変えてしまう。

例えば、ii·sh5は本質的に何が違うのかというと、decode()やり方が変わる。

/* Decode I */
template <typename T> inline void decode_i(T &dec, inst_t inst)
{
        dec.rd = operand_rd::decode(inst);
        dec.rs1 = operand_rs1::decode(inst);
        dec.rs2 = rv_ireg_zero;
        dec.imm = operand_imm12::decode(inst);
}

/* Decode I sh5 */
template <typename T> inline void decode_i_sh5(T &dec, inst_t inst)
{
        dec.rd = operand_rd::decode(inst);
        dec.rs1 = operand_rs1::decode(inst);
        dec.rs2 = rv_ireg_zero;
        dec.imm = operand_shamt5::decode(inst);
}

というわけでデコーダの本質ができたところで、ベクトル命令のオペランド定義を変えていく。つまり、以下のようなデコードフォーマットを詰め込んでいく。

/* Decode Vector Unit-Stride Load */
template <typename T> inline void decode_i_lv(T &dec, inst_t inst)
{
        dec.rd = operand_rd::decode(inst);
        dec.rs1 = operand_rs1::decode(inst);
        dec.rs2 = rv_ireg_zero;
        dec.imm = 0;
}

/* Decode Vector Unit-Stride Store */
template <typename T> inline void decode_s_v(T &dec, inst_t inst)
{
        dec.rd = rv_ireg_zero;
        dec.rs1 = operand_rs1::decode(inst);
        dec.rs2 = rv_ireg_zero;
        dec.rs3 = operand_rd::decode(inst);
        dec.imm = 0;
}

これで、とりあえず命令デコード情報がまともに表示できるようになった。