FPGA開発日記

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

QEMUに入門してみる(7. translateに必要な関数の確認)

QEMUの続き。独自ターゲットでビルドしてみる。前回の続き。

QEMUのデコーダを作っていく。QEMUのデコーダはDSLを使って記述するらしい。translate.cで呼び出されているecode_insn32()を作っていく。

  • qemu/target/myriscvx/translate.c
static void decode_opc(CPUMYRISCVXState *env,
                       DisasContext *ctx,
                       uint16_t opcode)
{
  uint32_t opcode32 = opcode;
  opcode32 = deposit32(opcode32, 16, 16,
                       translator_lduw(env, ctx->base.pc_next + 2));
  ctx->pc_succ_insn = ctx->base.pc_next + 4;
  if (!decode_insn32(ctx, opcode32)) {
    gen_exception_illegal(ctx);
  }
}

まずはinsn32.decodeを定義する。qemu/target/myriscvx/insn32.decodeに定義する。

  • qemu/target/myriscvx/insn32.decode
# Fields:
%rs3       27:5
%rs2       20:5
%rs1       15:5
%rd        7:5

# immediates:
%imm_i    20:s12
%imm_s    25:s7 7:5

# Argument sets:
&empty
&i    imm rs1 rd
&s    imm rs1 rs2

# Formats
@i       ............    ..... ... ..... ....... &i      imm=%imm_i     %rs1 %rd
@s       .......   ..... ..... ... ..... ....... &s      imm=%imm_s %rs2 %rs1

# *** RV32I Base Instruction Set ***
lw       ............     ..... 010 ..... 0000011 @i
sw       .......  .....   ..... 010 ..... 0100011 @s

lw / swの命令を定義している。必要なフィールドは01で記述であり、それ以外の領域は.で記述しているようだ。それぞれのフォーマットは@i, @sで定義されている。@i@sはそれぞれのフィールドを定義しているようだ。

# *** RV32I Base Instruction Set ***
lw       ............     ..... 010 ..... 0000011 @i
sw       .......  .....   ..... 010 ..... 0100011 @s
# Formats
@i       ............    ..... ... ..... ....... &i      imm=%imm_i     %rs1 %rd
@s       .......   ..... ..... ... ..... ....... &s      imm=%imm_s %rs2 %rs1

&i&sというのが良く分からないのだが、Argument setsとして定義されている。

# Argument sets:
&empty
&i    imm rs1 rd
&s    imm rs1 rs2

各フィールドの定義については一番先頭で記述されている。

# Fields:
%rs3       27:5
%rs2       20:5
%rs1       15:5
%rd        7:5

# immediates:
%imm_i    20:s12
%imm_s    25:s7 7:5

これをコンパイルターゲットに追加して、decode_insn32.inc.cを生成するようなルールを追加する。

  • qemu/target/myriscvx/Makefile.objs
decode32-y = $(SRC_PATH)/target/myriscvx/insn32.decode
# decode32-$(TARGET_MYRISCVX64) += $(SRC_PATH)/target/myriscvx/insn32-64.decode

target/myriscvx/decode_insn32.inc.c: $(decode32-y) $(DECODETREE)
    $(call quiet-command, \
      $(PYTHON) $(DECODETREE) -o $@ --static-decode decode_insn32 \
          $(decode32-y), "GEN", $(TARGET_DIR)$@)

target/myriscvx/translate.o: target/myriscvx/decode_insn32.inc.c

これでtarget/myriscvx/decode_insn32.inc.cが生成された。lwswのデコーダが作られている。

static bool decode_insn32(DisasContext *ctx, uint32_t insn)
{
    union {
        arg_empty f_empty;
        arg_i f_i;
        arg_s f_s;
    } u;

    switch (insn & 0x0000707f) {
    case 0x00002003:
        /* ........ ........ .010.... .0000011 */
        /* /home/msyksphinz/work/riscv/qemu/target/myriscvx/insn32.decode:25 */
        decode_insn32_extract_i(ctx, &u.f_i, insn);
        if (trans_lw(ctx, &u.f_i)) return true;
        return false;
    case 0x00002023:
        /* ........ ........ .010.... .0100011 */
        /* /home/msyksphinz/work/riscv/qemu/target/myriscvx/insn32.decode:26 */
        decode_insn32_extract_s(ctx, &u.f_s, insn);
        if (trans_sw(ctx, &u.f_s)) return true;
        return false;
    }
    return false;
}

ここまでくれば、trans_lwtrans_swを実装すれば完成する。

  • qemu/target/myriscvx/insn_trans/trans_rvi.inc.c
static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop)
{
    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();
    gen_get_gpr(t0, a->rs1);
    tcg_gen_addi_tl(t0, t0, a->imm);

    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, memop);
    gen_set_gpr(a->rd, t1);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
    return true;
}


static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop)
{
    TCGv t0 = tcg_temp_new();
    TCGv dat = tcg_temp_new();
    gen_get_gpr(t0, a->rs1);
    tcg_gen_addi_tl(t0, t0, a->imm);
    gen_get_gpr(dat, a->rs2);

    tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx, memop);
    tcg_temp_free(t0);
    tcg_temp_free(dat);
    return true;
}


static bool trans_lw(DisasContext *ctx, arg_lw *a)
{
  return gen_load(ctx, a, MO_TESL);
}


static bool trans_sw(DisasContext *ctx, arg_sw *a)
{
  return gen_store(ctx, a, MO_TESL);
}

あとはtranslate.cにこれをインクルードする。

/* Include the auto-generated decoder for 32 bit insn */
#include "decode_insn32.inc.c"

/* Include insn module translation function */
#include "insn_trans/trans_rvi.inc.c"

static void decode_opc(CPUMYRISCVXState *env,
                       DisasContext *ctx,
                       uint16_t opcode)
{
  uint32_t opcode32 = opcode;
  opcode32 = deposit32(opcode32, 16, 16,
...