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
が生成された。lw
とsw
のデコーダが作られている。
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_lw
とtrans_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, ...