Set up disas_xtensa_insn switch structure, mark required options on high level groups. Implement arithmetic/bit logic/jump/call0.
Implement code generation loop with single step/breakpoint checking. Signed-off-by: Max Filippov <jcmvb...@gmail.com> --- target-xtensa/cpu.h | 60 +++++++ target-xtensa/helper.c | 27 +++- target-xtensa/helpers.h | 5 + target-xtensa/op_helper.c | 7 + target-xtensa/translate.c | 374 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 target-xtensa/helpers.h diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index ef6881a..cf19fce 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -45,7 +45,67 @@ #define TARGET_VIRT_ADDR_SPACE_BITS 32 #define TARGET_PAGE_BITS 12 +enum { + /* Additional instructions */ + XTENSA_OPTION_CODE_DENSITY, + XTENSA_OPTION_LOOP, + XTENSA_OPTION_EXTENDED_L32R, + XTENSA_OPTION_16_BIT_IMUL, + XTENSA_OPTION_32_BIT_IMUL, + XTENSA_OPTION_32_BIT_IDIV, + XTENSA_OPTION_MAC16, + XTENSA_OPTION_MISC_OP, + XTENSA_OPTION_COPROCESSOR, + XTENSA_OPTION_BOOLEAN, + XTENSA_OPTION_FP_COPROCESSOR, + XTENSA_OPTION_MP_SYNCHRO, + XTENSA_OPTION_CONDITIONAL_STORE, + + /* Interrupts and exceptions */ + XTENSA_OPTION_EXCEPTION, + XTENSA_OPTION_RELOCATABLE_VECTOR, + XTENSA_OPTION_UNALIGNED_EXCEPTION, + XTENSA_OPTION_INTERRUPT, + XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT, + XTENSA_OPTION_TIMER_INTERRUPT, + + /* Local memory */ + XTENSA_OPTION_ICACHE, + XTENSA_OPTION_ICACHE_TEST, + XTENSA_OPTION_ICACHE_INDEX_LOCK, + XTENSA_OPTION_DCACHE, + XTENSA_OPTION_DCACHE_TEST, + XTENSA_OPTION_DCACHE_INDEX_LOCK, + XTENSA_OPTION_IRAM, + XTENSA_OPTION_IROM, + XTENSA_OPTION_DRAM, + XTENSA_OPTION_DROM, + XTENSA_OPTION_XLMI, + XTENSA_OPTION_HW_ALIGNMENT, + XTENSA_OPTION_MEMORY_ECC_PARITY, + + /* Memory protection and translation */ + XTENSA_OPTION_REGION_PROTECTION, + XTENSA_OPTION_REGION_TRANSLATION, + XTENSA_OPTION_MMU, + + /* Other */ + XTENSA_OPTION_WINDOWED_REGISTER, + XTENSA_OPTION_PROCESSOR_INTERFACE, + XTENSA_OPTION_MISC_SR, + XTENSA_OPTION_THREAD_POINTER, + XTENSA_OPTION_PROCESSOR_ID, + XTENSA_OPTION_DEBUG, + XTENSA_OPTIN_TRACE_PORT, +}; + +typedef struct XtensaConfig { + const char *name; + uint64_t options; +} XtensaConfig; + typedef struct CPUXtensaState { + const XtensaConfig *config; uint32_t regs[16]; uint32_t pc; uint32_t sregs[256]; diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index 6b6e17b..6f374d3 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -39,12 +39,32 @@ void cpu_reset(CPUXtensaState *env) env->pc = 0; } +static const XtensaConfig core_config[] = { + { + .name = "sample-xtensa-core", + .options = -1, + }, +}; + CPUXtensaState *cpu_xtensa_init(const char *cpu_model) { static int tcg_inited; CPUXtensaState *env; + const XtensaConfig *config = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(core_config); ++i) + if (strcmp(core_config[i].name, cpu_model) == 0) { + config = core_config + i; + break; + } + + if (config == NULL) { + return NULL; + } env = qemu_mallocz(sizeof(*env)); + env->config = config; cpu_exec_init(env); if (!tcg_inited) { @@ -60,8 +80,11 @@ CPUXtensaState *cpu_xtensa_init(const char *cpu_model) void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf) { - cpu_fprintf(f, "Available CPUs:\n" - " Xtensa core\n"); + int i; + cpu_fprintf(f, "Available CPUs:\n"); + for (i = 0; i < ARRAY_SIZE(core_config); ++i) { + cpu_fprintf(f, " %s\n", core_config[i].name); + } } target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h new file mode 100644 index 0000000..c298d74 --- /dev/null +++ b/target-xtensa/helpers.h @@ -0,0 +1,5 @@ +#include "def-helper.h" + +DEF_HELPER_1(exception, void, i32) + +#include "def-helper.h" diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 5a0e068..d1e620c 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -26,6 +26,7 @@ */ #include "exec.h" +#include "helpers.h" #define MMUSUFFIX _mmu @@ -49,3 +50,9 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, TARGET_PAGE_SIZE); } + +void HELPER(exception)(uint32_t excp) +{ + env->exception_index = excp; + cpu_loop_exit(); +} diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index c47feab..16efcfd 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -36,17 +36,391 @@ #include "tcg-op.h" #include "qemu-log.h" +#include "helpers.h" +#define GEN_HELPER 1 +#include "helpers.h" + +typedef struct DisasContext { + const XtensaConfig *config; + TranslationBlock *tb; + uint32_t pc; + int is_jmp; + int singlestep_enabled; +} DisasContext; + +static TCGv_ptr cpu_env; +static TCGv_i32 cpu_pc; +static TCGv_i32 cpu_R[16]; + +#include "gen-icount.h" void xtensa_translate_init(void) { + static const char * const regnames[] = { + "ar0", "ar1", "ar2", "ar3", + "ar4", "ar5", "ar6", "ar7", + "ar8", "ar9", "ar10", "ar11", + "ar12", "ar13", "ar14", "ar15", + }; + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + cpu_pc = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUState, pc), "pc"); + + for (i = 0; i < 16; i++) { + cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUState, regs[i]), + regnames[i]); + } +} + +static void gen_exception(int excp) +{ + TCGv_i32 tmp = tcg_const_i32(excp); + gen_helper_exception(tmp); + tcg_temp_free(tmp); +} + +static void gen_jump(DisasContext *dc, TCGv dest) +{ + tcg_gen_mov_i32(cpu_pc, dest); + if (dc->singlestep_enabled) { + gen_exception(EXCP_DEBUG); + } + tcg_gen_exit_tb(0); + dc->is_jmp = DISAS_UPDATE; +} + +static void gen_jumpi(DisasContext *dc, uint32_t dest) +{ + TCGv_i32 tmp = tcg_const_i32(dest); + gen_jump(dc, tmp); + tcg_temp_free(tmp); +} + +static void disas_xtensa_insn(DisasContext *dc) +{ +#define HAS_OPTION(opt) do { \ + if (!(dc->config->options & (((uint64_t)1) << (opt)))) { \ + goto invalid_opcode; \ + } \ + } while (0) + +#define _OP0 (((_b0) & 0xf0) >> 4) +#define _OP1 (((_b2) & 0xf0) >> 4) +#define _OP2 ((_b2) & 0xf) +#define RRR_R ((_b1) & 0xf) +#define RRR_S (((_b1) & 0xf0) >> 4) +#define RRR_T ((_b0) & 0xf) + +#define RRRN_R RRR_R +#define RRRN_S RRR_S +#define RRRN_T RRR_T + +#define RI16_IMM16 (((_b1) << 8) | (_b2)) + +#define CALL_N (((_b0) & 0xc) >> 2) +#define CALL_OFFSET ((((_b0) & 0x3) << 16) | ((_b1) << 8) | (_b2)) +#define CALL_OFFSET_SE (((_b0 & 0x2) ? 0xfffc0000 : 0) | CALL_OFFSET) + + uint8_t _b0 = ldub_code(dc->pc); + uint8_t _b1 = ldub_code(dc->pc + 1); + uint8_t _b2 = ldub_code(dc->pc + 2); + + switch (_OP0) { + case 0: /*QRST*/ + switch (_OP1) { + case 0: /*RST0*/ + switch (_OP2) { + case 0: /*ST0*/ + if ((RRR_R & 0xc) == 0x8) { + HAS_OPTION(XTENSA_OPTION_BOOLEAN); + } + + switch (RRR_R) { + case 0: /*SNM0*/ + break; + + case 1: /*MOVSPw*/ + HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); + break; + + case 2: /*SYNC*/ + break; + + case 3: + break; + + } + break; + + case 1: /*AND*/ + tcg_gen_and_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + break; + + case 2: /*OR*/ + tcg_gen_or_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + break; + + case 3: /*XOR*/ + tcg_gen_xor_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + break; + + case 4: /*ST1*/ + break; + + case 5: /*TLB*/ + break; + + case 6: /*RT0*/ + break; + + case 7: /*reserved*/ + break; + + case 8: /*ADD*/ + tcg_gen_add_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + break; + + case 9: /*ADD**/ + case 10: + case 11: + { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, cpu_R[RRR_S], _OP2 - 8); + tcg_gen_add_i32(cpu_R[RRR_R], tmp, cpu_R[RRR_T]); + tcg_temp_free(tmp); + } + break; + + case 12: /*SUB*/ + tcg_gen_sub_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]); + break; + + case 13: /*SUB**/ + case 14: + case 15: + { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, cpu_R[RRR_S], _OP2 - 12); + tcg_gen_sub_i32(cpu_R[RRR_R], tmp, cpu_R[RRR_T]); + tcg_temp_free(tmp); + } + break; + } + break; + + case 1: /*RST1*/ + break; + + case 2: /*RST2*/ + break; + + case 3: /*RST3*/ + break; + + case 4: /*EXTUI*/ + case 5: + break; + + case 6: /*CUST0*/ + break; + + case 7: /*CUST1*/ + break; + + case 8: /*LSCXp*/ + HAS_OPTION(XTENSA_OPTION_COPROCESSOR); + break; + + case 9: /*LSC4*/ + break; + + case 10: /*FP0*/ + HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR); + break; + + case 11: /*FP1*/ + HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR); + break; + + default: /*reserved*/ + break; + } + break; + + case 1: /*L32R*/ + { + TCGv_i32 tmp = tcg_const_i32( + (0xfffc0000 | (RI16_IMM16 << 2)) + + ((dc->pc + 3) & ~3)); + + /* no ext L32R */ + + tcg_gen_qemu_ld32u(cpu_R[RRR_T], tmp, 0); + tcg_temp_free(tmp); + } + break; + + case 2: /*LSAI*/ + break; + + case 3: /*LSCIp*/ + HAS_OPTION(XTENSA_OPTION_COPROCESSOR); + break; + + case 4: /*MAC16d*/ + HAS_OPTION(XTENSA_OPTION_MAC16); + break; + + case 5: /*CALLN*/ + switch (CALL_N) { + case 0: /*CALL0*/ + tcg_gen_movi_i32(cpu_R[0], dc->pc + 3); + gen_jumpi(dc, (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4); + break; + + case 1: /*CALL4w*/ + case 2: /*CALL8w*/ + case 3: /*CALL12w*/ + HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); + break; + } + break; + + case 6: /*SI*/ + switch (CALL_N) { + case 0: /*J*/ + gen_jumpi(dc, dc->pc + 4 + CALL_OFFSET_SE); + break; + + } + break; + + case 7: /*B*/ + break; + + case 8: /*L32I.Nn*/ + break; + + case 9: /*S32I.Nn*/ + break; + + case 10: /*ADD.Nn*/ + break; + + case 11: /*ADDI.Nn*/ + break; + + case 12: /*ST2n*/ + break; + + case 13: /*ST3n*/ + break; + + default: /*reserved*/ + break; + } + + if (_OP0 >= 8) { + dc->pc += 2; + HAS_OPTION(XTENSA_OPTION_CODE_DENSITY); + } else { + dc->pc += 3; + } + return; + +invalid_opcode: + printf("INVALID(pc = %08x): %s:%d\n", dc->pc, __FILE__, __LINE__); + dc->pc += (_OP0 >= 8) ? 2 : 3; +#undef HAS_OPTION +} + +static void check_breakpoint(CPUState *env, DisasContext *dc) +{ + CPUBreakpoint *bp; + + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc->pc) { + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_exception(EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + } + } + } +} + +static void gen_intermediate_code_internal( + CPUState *env, TranslationBlock *tb, int search_pc) +{ + DisasContext dc; + int insn_count = 0; + int j, lj = -1; + uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + int max_insns = tb->cflags & CF_COUNT_MASK; + + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + + dc.config = env->config; + dc.singlestep_enabled = env->singlestep_enabled; + dc.tb = tb; + dc.pc = env->pc; + dc.is_jmp = DISAS_NEXT; + + gen_icount_start(); + + do { + check_breakpoint(env, &dc); + + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) { + gen_opc_instr_start[lj++] = 0; + } + } + gen_opc_pc[lj] = dc.pc; + gen_opc_instr_start[lj] = 1; + gen_opc_icount[lj] = insn_count; + } + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(dc.pc); + } + + disas_xtensa_insn(&dc); + ++insn_count; + if (env->singlestep_enabled) { + tcg_gen_movi_i32(cpu_pc, dc.pc); + gen_exception(EXCP_DEBUG); + break; + } + } while (dc.is_jmp == DISAS_NEXT && + insn_count < max_insns && + gen_opc_ptr < gen_opc_end); + + if (dc.is_jmp == DISAS_NEXT) { + tcg_gen_movi_i32(cpu_pc, dc.pc); + tcg_gen_exit_tb(0); + } + gen_icount_end(tb, insn_count); + *gen_opc_ptr = INDEX_op_end; + } void gen_intermediate_code(CPUState *env, TranslationBlock *tb) { + gen_intermediate_code_internal(env, tb, 0); } void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) { + gen_intermediate_code_internal(env, tb, 1); } void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, -- 1.7.3.4