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 | 67 +++++++ target-xtensa/helper.c | 27 +++- target-xtensa/helpers.h | 5 + target-xtensa/op_helper.c | 7 + target-xtensa/translate.c | 452 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 556 insertions(+), 2 deletions(-) create mode 100644 target-xtensa/helpers.h diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index a51ffc8..c323891 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_OPTION_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]; @@ -66,6 +126,13 @@ void do_interrupt(CPUXtensaState *s); int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc); void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf); +#define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt)) + +static inline bool xtensa_option_enabled(const XtensaConfig *config, int opt) +{ + return (config->options & XTENSA_OPTION_BIT(opt)) != 0; +} + static inline int cpu_mmu_index(CPUState *env) { return 0; diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index dc30e00..c119fd0 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 = g_malloc0(sizeof(*env)); + env->config = config; cpu_exec_init(env); if (!tcg_inited) { @@ -59,8 +79,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 aafa33d..0392fbe 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -27,6 +27,7 @@ #include "cpu.h" #include "dyngen-exec.h" +#include "helpers.h" #define MMUSUFFIX _mmu @@ -50,3 +51,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(env); +} diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index e91d8a5..83d8837 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -36,17 +36,469 @@ #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; + uint32_t next_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]); + } +#define GEN_HELPER 2 +#include "helpers.h" +} + +static inline bool option_enabled(DisasContext *dc, int opt) +{ + return xtensa_option_enabled(dc->config, opt); +} + +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_slot(DisasContext *dc, TCGv dest, int slot) +{ + tcg_gen_mov_i32(cpu_pc, dest); + if (dc->singlestep_enabled) { + gen_exception(EXCP_DEBUG); + } else { + if (slot >= 0) { + tcg_gen_goto_tb(slot); + tcg_gen_exit_tb((tcg_target_long)dc->tb + slot); + } else { + tcg_gen_exit_tb(0); + } + } + dc->is_jmp = DISAS_UPDATE; +} + +static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot) +{ + TCGv_i32 tmp = tcg_const_i32(dest); + if (((dc->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + slot = -1; + } + gen_jump_slot(dc, tmp, slot); + tcg_temp_free(tmp); +} + +static void disas_xtensa_insn(DisasContext *dc) +{ +#define HAS_OPTION(opt) do { \ + if (!option_enabled(dc, opt)) { \ + qemu_log("Option %d is not enabled %s:%d\n", \ + (opt), __FILE__, __LINE__); \ + goto invalid_opcode; \ + } \ + } while (0) + +#ifdef TARGET_WORDS_BIGENDIAN +#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) +#else +#define OP0 (((_b0) & 0xf)) +#define OP1 (((_b2) & 0xf)) +#define OP2 (((_b2) & 0xf0) >> 4) +#define RRR_R (((_b1) & 0xf0) >> 4) +#define RRR_S (((_b1) & 0xf)) +#define RRR_T (((_b0) & 0xf0) >> 4) +#endif + +#define RRRN_R RRR_R +#define RRRN_S RRR_S +#define RRRN_T RRR_T + +#define RRI8_R RRR_R +#define RRI8_S RRR_S +#define RRI8_T RRR_T +#define RRI8_IMM8 (_b2) +#define RRI8_IMM8_SE ((((_b2) & 0x80) ? 0xffffff00 : 0) | RRI8_IMM8) + +#ifdef TARGET_WORDS_BIGENDIAN +#define RI16_IMM16 (((_b1) << 8) | (_b2)) +#else +#define RI16_IMM16 (((_b2) << 8) | (_b1)) +#endif + +#ifdef TARGET_WORDS_BIGENDIAN +#define CALL_N (((_b0) & 0xc) >> 2) +#define CALL_OFFSET ((((_b0) & 0x3) << 16) | ((_b1) << 8) | (_b2)) +#else +#define CALL_N (((_b0) & 0x30) >> 4) +#define CALL_OFFSET ((((_b0) & 0xc0) >> 6) | ((_b1) << 2) | ((_b2) << 10)) +#endif +#define CALL_OFFSET_SE \ + (((CALL_OFFSET & 0x20000) ? 0xfffc0000 : 0) | CALL_OFFSET) + +#define CALLX_N CALL_N +#ifdef TARGET_WORDS_BIGENDIAN +#define CALLX_M ((_b0) & 0x3) +#else +#define CALLX_M (((_b0) & 0xc0) >> 6) +#endif +#define CALLX_S RRR_S + +#define BRI12_M CALLX_M +#define BRI12_S RRR_S +#ifdef TARGET_WORDS_BIGENDIAN +#define BRI12_IMM12 ((((_b1) & 0xf) << 8) | (_b2)) +#else +#define BRI12_IMM12 ((((_b1) & 0xf0) >> 4) | ((_b2) << 4)) +#endif +#define BRI12_IMM12_SE (((BRI12_IMM12 & 0x800) ? 0xfffff000 : 0) | BRI12_IMM12) + +#define BRI8_M BRI12_M +#define BRI8_R RRI8_R +#define BRI8_S RRI8_S +#define BRI8_IMM8 RRI8_IMM8 +#define BRI8_IMM8_SE RRI8_IMM8_SE + +#define RSR_SR (_b1) + + uint8_t _b0 = ldub_code(dc->pc); + uint8_t _b1 = ldub_code(dc->pc + 1); + uint8_t _b2 = ldub_code(dc->pc + 2); + + if (OP0 >= 8) { + dc->next_pc = dc->pc + 2; + HAS_OPTION(XTENSA_OPTION_CODE_DENSITY); + } else { + dc->next_pc = dc->pc + 3; + } + + 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->next_pc); + gen_jumpi(dc, (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4, 0); + 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, 0); + 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; + } + + dc->pc = dc->next_pc; + return; + +invalid_opcode: + qemu_log("INVALID(pc = %08x)\n", dc->pc); + dc->pc = dc->next_pc; +#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; + uint32_t pc_start = tb->pc; + uint32_t next_page_start = + (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + + dc.config = env->config; + dc.singlestep_enabled = env->singlestep_enabled; + dc.tb = tb; + dc.pc = pc_start; + 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 && + dc.pc < next_page_start && + gen_opc_ptr < gen_opc_end); + + if (dc.is_jmp == DISAS_NEXT) { + gen_jumpi(&dc, dc.pc, 0); + } + gen_icount_end(tb, insn_count); + *gen_opc_ptr = INDEX_op_end; + + if (!search_pc) { + tb->size = dc.pc - pc_start; + tb->icount = insn_count; + } } 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.6