diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2011-09-06 03:55:48 +0400 |
---|---|---|
committer | Blue Swirl <blauwirbel@gmail.com> | 2011-09-10 16:57:39 +0000 |
commit | b994e91b00cce463bfd306482dfe21630e11bf68 (patch) | |
tree | 097173c01d0e318e0201f30c877927193fce0e57 | |
parent | 1ddeaa5d4277af76679d02bc59b08657c357aee6 (diff) | |
download | qemu-b994e91b00cce463bfd306482dfe21630e11bf68.tar.gz qemu-b994e91b00cce463bfd306482dfe21630e11bf68.tar.bz2 qemu-b994e91b00cce463bfd306482dfe21630e11bf68.zip |
target-xtensa: implement interrupt option
See ISA, 4.4.6 (interrupt option), 4.4.7 (high priority interrupt
option) and 4.4.8 (timer interrupt option) for details.
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
-rw-r--r-- | hw/xtensa_pic.c | 96 | ||||
-rw-r--r-- | target-xtensa/cpu.h | 45 | ||||
-rw-r--r-- | target-xtensa/helper.c | 98 | ||||
-rw-r--r-- | target-xtensa/helpers.h | 5 | ||||
-rw-r--r-- | target-xtensa/op_helper.c | 46 | ||||
-rw-r--r-- | target-xtensa/translate.c | 153 |
6 files changed, 431 insertions, 12 deletions
diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c index 91a5445f42..3033ae214a 100644 --- a/hw/xtensa_pic.c +++ b/hw/xtensa_pic.c @@ -27,6 +27,8 @@ #include "hw.h" #include "pc.h" +#include "qemu-log.h" +#include "qemu-timer.h" /* Stub functions for hardware that doesn't exist. */ void pic_info(Monitor *mon) @@ -36,3 +38,97 @@ void pic_info(Monitor *mon) void irq_info(Monitor *mon) { } + +void xtensa_advance_ccount(CPUState *env, uint32_t d) +{ + uint32_t old_ccount = env->sregs[CCOUNT]; + + env->sregs[CCOUNT] += d; + + if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { + int i; + for (i = 0; i < env->config->nccompare; ++i) { + if (env->sregs[CCOMPARE + i] - old_ccount <= d) { + xtensa_timer_irq(env, i, 1); + } + } + } +} + +void check_interrupts(CPUState *env) +{ + int minlevel = xtensa_get_cintlevel(env); + uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; + int level; + + /* If the CPU is halted advance CCOUNT according to the vm_clock time + * elapsed since the moment when it was advanced last time. + */ + if (env->halted) { + int64_t now = qemu_get_clock_ns(vm_clock); + + xtensa_advance_ccount(env, + muldiv64(now - env->halt_clock, + env->config->clock_freq_khz, 1000000)); + env->halt_clock = now; + } + for (level = env->config->nlevel; level > minlevel; --level) { + if (env->config->level_mask[level] & int_set_enabled) { + env->pending_irq_level = level; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + qemu_log_mask(CPU_LOG_INT, + "%s level = %d, cintlevel = %d, " + "pc = %08x, a0 = %08x, ps = %08x, " + "intset = %08x, intenable = %08x, " + "ccount = %08x\n", + __func__, level, xtensa_get_cintlevel(env), + env->pc, env->regs[0], env->sregs[PS], + env->sregs[INTSET], env->sregs[INTENABLE], + env->sregs[CCOUNT]); + return; + } + } + env->pending_irq_level = 0; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); +} + +static void xtensa_set_irq(void *opaque, int irq, int active) +{ + CPUState *env = opaque; + + if (irq >= env->config->ninterrupt) { + qemu_log("%s: bad IRQ %d\n", __func__, irq); + } else { + uint32_t irq_bit = 1 << irq; + + if (active) { + env->sregs[INTSET] |= irq_bit; + } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) { + env->sregs[INTSET] &= ~irq_bit; + } + + check_interrupts(env); + } +} + +void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active) +{ + qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); +} + +static void xtensa_ccompare_cb(void *opaque) +{ + CPUState *env = opaque; + xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); +} + +void xtensa_irq_init(CPUState *env) +{ + env->irq_inputs = (void **)qemu_allocate_irqs( + xtensa_set_irq, env, env->config->ninterrupt); + if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT) && + env->config->nccompare > 0) { + env->ccompare_timer = + qemu_new_timer_ns(vm_clock, &xtensa_ccompare_cb, env); + } +} diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 1283fd904d..474466cc35 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -116,10 +116,16 @@ enum { WINDOW_START = 73, EPC1 = 177, DEPC = 192, + EPS2 = 194, EXCSAVE1 = 209, + INTSET = 226, + INTCLEAR = 227, + INTENABLE = 228, PS = 230, EXCCAUSE = 232, + CCOUNT = 234, EXCVADDR = 238, + CCOMPARE = 240, }; #define PS_INTLEVEL 0xf @@ -141,6 +147,10 @@ enum { #define PS_WOE 0x40000 #define MAX_NAREG 64 +#define MAX_NINTERRUPT 32 +#define MAX_NLEVEL 6 +#define MAX_NNMI 1 +#define MAX_NCCOMPARE 3 enum { /* Static vectors */ @@ -190,6 +200,17 @@ enum { COPROCESSOR0_DISABLED = 32, }; +typedef enum { + INTTYPE_LEVEL, + INTTYPE_EDGE, + INTTYPE_NMI, + INTTYPE_SOFTWARE, + INTTYPE_TIMER, + INTTYPE_DEBUG, + INTTYPE_WRITE_ERR, + INTTYPE_MAX +} interrupt_type; + typedef struct XtensaConfig { const char *name; uint64_t options; @@ -197,6 +218,18 @@ typedef struct XtensaConfig { int excm_level; int ndepc; uint32_t exception_vector[EXC_MAX]; + unsigned ninterrupt; + unsigned nlevel; + uint32_t interrupt_vector[MAX_NLEVEL + MAX_NNMI + 1]; + uint32_t level_mask[MAX_NLEVEL + MAX_NNMI + 1]; + uint32_t inttype_mask[INTTYPE_MAX]; + struct { + uint32_t level; + interrupt_type inttype; + } interrupt[MAX_NINTERRUPT]; + unsigned nccompare; + uint32_t timerint[MAX_NCCOMPARE]; + uint32_t clock_freq_khz; } XtensaConfig; typedef struct CPUXtensaState { @@ -207,6 +240,12 @@ typedef struct CPUXtensaState { uint32_t uregs[256]; uint32_t phys_regs[MAX_NAREG]; + int pending_irq_level; /* level of last raised IRQ */ + void **irq_inputs; + QEMUTimer *ccompare_timer; + uint32_t wake_ccount; + int64_t halt_clock; + int exception_taken; CPU_COMMON @@ -222,6 +261,10 @@ CPUXtensaState *cpu_xtensa_init(const char *cpu_model); void xtensa_translate_init(void); int cpu_xtensa_exec(CPUXtensaState *s); void do_interrupt(CPUXtensaState *s); +void check_interrupts(CPUXtensaState *s); +void xtensa_irq_init(CPUState *env); +void xtensa_advance_ccount(CPUState *env, uint32_t d); +void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active); int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc); void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf); void xtensa_sync_window_from_phys(CPUState *env); @@ -298,7 +341,7 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, static inline int cpu_has_work(CPUState *env) { - return 1; + return env->pending_irq_level; } static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index bcd6c773e2..c24a38a99a 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -39,7 +39,10 @@ void cpu_reset(CPUXtensaState *env) env->exception_taken = 0; env->pc = env->config->exception_vector[EXC_RESET]; env->sregs[LITBASE] &= ~1; - env->sregs[PS] = 0x1f; + env->sregs[PS] = xtensa_option_enabled(env->config, + XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10; + + env->pending_irq_level = 0; } static const XtensaConfig core_config[] = { @@ -63,6 +66,31 @@ static const XtensaConfig core_config[] = { [EXC_USER] = 0x5fff863c, [EXC_DOUBLE] = 0x5fff865c, }, + .ninterrupt = 13, + .nlevel = 6, + .interrupt_vector = { + 0, + 0, + 0x5fff857c, + 0x5fff859c, + 0x5fff85bc, + 0x5fff85dc, + 0x5fff85fc, + }, + .level_mask = { + [4] = 1, + }, + .interrupt = { + [0] = { + .level = 4, + .inttype = INTTYPE_TIMER, + }, + }, + .nccompare = 1, + .timerint = { + [0] = 0, + }, + .clock_freq_khz = 912000, }, }; @@ -92,6 +120,7 @@ CPUXtensaState *cpu_xtensa_init(const char *cpu_model) xtensa_translate_init(); } + xtensa_irq_init(env); qemu_init_vcpu(env); return env; } @@ -111,8 +140,63 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) return addr; } +/*! + * Handle penging IRQ. + * For the high priority interrupt jump to the corresponding interrupt vector. + * For the level-1 interrupt convert it to either user, kernel or double + * exception with the 'level-1 interrupt' exception cause. + */ +static void handle_interrupt(CPUState *env) +{ + int level = env->pending_irq_level; + + if (level > xtensa_get_cintlevel(env) && + level <= env->config->nlevel && + (env->config->level_mask[level] & + env->sregs[INTSET] & + env->sregs[INTENABLE])) { + if (level > 1) { + env->sregs[EPC1 + level - 1] = env->pc; + env->sregs[EPS2 + level - 2] = env->sregs[PS]; + env->sregs[PS] = + (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; + env->pc = env->config->interrupt_vector[level]; + } else { + env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; + + if (env->sregs[PS] & PS_EXCM) { + if (env->config->ndepc) { + env->sregs[DEPC] = env->pc; + } else { + env->sregs[EPC1] = env->pc; + } + env->exception_index = EXC_DOUBLE; + } else { + env->sregs[EPC1] = env->pc; + env->exception_index = + (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; + } + env->sregs[PS] |= PS_EXCM; + } + env->exception_taken = 1; + } +} + void do_interrupt(CPUState *env) { + if (env->exception_index == EXC_IRQ) { + qemu_log_mask(CPU_LOG_INT, + "%s(EXC_IRQ) level = %d, cintlevel = %d, " + "pc = %08x, a0 = %08x, ps = %08x, " + "intset = %08x, intenable = %08x, " + "ccount = %08x\n", + __func__, env->pending_irq_level, xtensa_get_cintlevel(env), + env->pc, env->regs[0], env->sregs[PS], + env->sregs[INTSET], env->sregs[INTENABLE], + env->sregs[CCOUNT]); + handle_interrupt(env); + } + switch (env->exception_index) { case EXC_WINDOW_OVERFLOW4: case EXC_WINDOW_UNDERFLOW4: @@ -123,6 +207,10 @@ void do_interrupt(CPUState *env) case EXC_KERNEL: case EXC_USER: case EXC_DOUBLE: + qemu_log_mask(CPU_LOG_INT, "%s(%d) " + "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n", + __func__, env->exception_index, + env->pc, env->regs[0], env->sregs[PS], env->sregs[CCOUNT]); if (env->config->exception_vector[env->exception_index]) { env->pc = env->config->exception_vector[env->exception_index]; env->exception_taken = 1; @@ -132,5 +220,13 @@ void do_interrupt(CPUState *env) } break; + case EXC_IRQ: + break; + + default: + qemu_log("%s(pc = %08x) unknown exception_index: %d\n", + __func__, env->pc, env->exception_index); + break; } + check_interrupts(env); } diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h index 6e02d26dfd..28689c3088 100644 --- a/target-xtensa/helpers.h +++ b/target-xtensa/helpers.h @@ -17,4 +17,9 @@ DEF_HELPER_1(wsr_lend, void, i32) DEF_HELPER_1(simcall, void, env) DEF_HELPER_0(dump_state, void) +DEF_HELPER_2(waiti, void, i32, i32) +DEF_HELPER_2(timer_irq, void, i32, i32) +DEF_HELPER_1(advance_ccount, void, i32) +DEF_HELPER_1(check_interrupts, void, env) + #include "def-helper.h" diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index d37f70dda5..0047fa3c22 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -338,3 +338,49 @@ void HELPER(dump_state)(void) { cpu_dump_state(env, stderr, fprintf, 0); } + +void HELPER(waiti)(uint32_t pc, uint32_t intlevel) +{ + env->pc = pc; + env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | + (intlevel << PS_INTLEVEL_SHIFT); + check_interrupts(env); + if (env->pending_irq_level) { + cpu_loop_exit(env); + return; + } + + if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { + int i; + uint32_t wake_ccount = env->sregs[CCOUNT] - 1; + + for (i = 0; i < env->config->nccompare; ++i) { + if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] < + wake_ccount - env->sregs[CCOUNT]) { + wake_ccount = env->sregs[CCOMPARE + i]; + } + } + env->wake_ccount = wake_ccount; + qemu_mod_timer(env->ccompare_timer, qemu_get_clock_ns(vm_clock) + + muldiv64(wake_ccount - env->sregs[CCOUNT], + 1000000, env->config->clock_freq_khz)); + } + env->halt_clock = qemu_get_clock_ns(vm_clock); + env->halted = 1; + HELPER(exception)(EXCP_HLT); +} + +void HELPER(timer_irq)(uint32_t id, uint32_t active) +{ + xtensa_timer_irq(env, id, active); +} + +void HELPER(advance_ccount)(uint32_t d) +{ + xtensa_advance_ccount(env, d); +} + +void HELPER(check_interrupts)(CPUState *env) +{ + check_interrupts(env); +} diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index 1598e27440..d75e780201 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -58,6 +58,8 @@ typedef struct DisasContext { bool sar_m32_5bit; bool sar_m32_allocated; TCGv_i32 sar_m32; + + uint32_t ccount_delta; } DisasContext; static TCGv_ptr cpu_env; @@ -78,11 +80,36 @@ static const char * const sregnames[256] = { [WINDOW_BASE] = "WINDOW_BASE", [WINDOW_START] = "WINDOW_START", [EPC1] = "EPC1", + [EPC1 + 1] = "EPC2", + [EPC1 + 2] = "EPC3", + [EPC1 + 3] = "EPC4", + [EPC1 + 4] = "EPC5", + [EPC1 + 5] = "EPC6", + [EPC1 + 6] = "EPC7", [DEPC] = "DEPC", + [EPS2] = "EPS2", + [EPS2 + 1] = "EPS3", + [EPS2 + 2] = "EPS4", + [EPS2 + 3] = "EPS5", + [EPS2 + 4] = "EPS6", + [EPS2 + 5] = "EPS7", [EXCSAVE1] = "EXCSAVE1", + [EXCSAVE1 + 1] = "EXCSAVE2", + [EXCSAVE1 + 2] = "EXCSAVE3", + [EXCSAVE1 + 3] = "EXCSAVE4", + [EXCSAVE1 + 4] = "EXCSAVE5", + [EXCSAVE1 + 5] = "EXCSAVE6", + [EXCSAVE1 + 6] = "EXCSAVE7", + [INTSET] = "INTSET", + [INTCLEAR] = "INTCLEAR", + [INTENABLE] = "INTENABLE", [PS] = "PS", [EXCCAUSE] = "EXCCAUSE", + [CCOUNT] = "CCOUNT", [EXCVADDR] = "EXCVADDR", + [CCOMPARE] = "CCOMPARE0", + [CCOMPARE + 1] = "CCOMPARE1", + [CCOMPARE + 2] = "CCOMPARE2", }; static const char * const uregnames[256] = { @@ -188,9 +215,20 @@ static void gen_left_shift_sar(DisasContext *dc, TCGv_i32 sa) tcg_temp_free(tmp); } -static void gen_exception(int excp) +static void gen_advance_ccount(DisasContext *dc) +{ + if (dc->ccount_delta > 0) { + TCGv_i32 tmp = tcg_const_i32(dc->ccount_delta); + dc->ccount_delta = 0; + gen_helper_advance_ccount(tmp); + tcg_temp_free(tmp); + } +} + +static void gen_exception(DisasContext *dc, int excp) { TCGv_i32 tmp = tcg_const_i32(excp); + gen_advance_ccount(dc); gen_helper_exception(tmp); tcg_temp_free(tmp); } @@ -199,6 +237,7 @@ static void gen_exception_cause(DisasContext *dc, uint32_t cause) { TCGv_i32 tpc = tcg_const_i32(dc->pc); TCGv_i32 tcause = tcg_const_i32(cause); + gen_advance_ccount(dc); gen_helper_exception_cause(tpc, tcause); tcg_temp_free(tpc); tcg_temp_free(tcause); @@ -209,6 +248,7 @@ static void gen_exception_cause_vaddr(DisasContext *dc, uint32_t cause, { TCGv_i32 tpc = tcg_const_i32(dc->pc); TCGv_i32 tcause = tcg_const_i32(cause); + gen_advance_ccount(dc); gen_helper_exception_cause_vaddr(tpc, tcause, vaddr); tcg_temp_free(tpc); tcg_temp_free(tcause); @@ -225,8 +265,9 @@ 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); + gen_exception(dc, EXCP_DEBUG); } else { + gen_advance_ccount(dc); if (slot >= 0) { tcg_gen_goto_tb(slot); tcg_gen_exit_tb((tcg_target_long)dc->tb + slot); @@ -323,10 +364,17 @@ static void gen_brcondi(DisasContext *dc, TCGCond cond, tcg_temp_free(tmp); } +static void gen_rsr_ccount(DisasContext *dc, TCGv_i32 d, uint32_t sr) +{ + gen_advance_ccount(dc); + tcg_gen_mov_i32(d, cpu_SR[sr]); +} + static void gen_rsr(DisasContext *dc, TCGv_i32 d, uint32_t sr) { static void (* const rsr_handler[256])(DisasContext *dc, TCGv_i32 d, uint32_t sr) = { + [CCOUNT] = gen_rsr_ccount, }; if (sregnames[sr]) { @@ -372,6 +420,34 @@ static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v) gen_helper_wsr_windowbase(v); } +static void gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + tcg_gen_andi_i32(cpu_SR[sr], v, + dc->config->inttype_mask[INTTYPE_SOFTWARE]); + gen_helper_check_interrupts(cpu_env); + gen_jumpi_check_loop_end(dc, 0); +} + +static void gen_wsr_intclear(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_andi_i32(tmp, v, + dc->config->inttype_mask[INTTYPE_EDGE] | + dc->config->inttype_mask[INTTYPE_NMI] | + dc->config->inttype_mask[INTTYPE_SOFTWARE]); + tcg_gen_andc_i32(cpu_SR[INTSET], cpu_SR[INTSET], tmp); + tcg_temp_free(tmp); + gen_helper_check_interrupts(cpu_env); +} + +static void gen_wsr_intenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + tcg_gen_mov_i32(cpu_SR[sr], v); + gen_helper_check_interrupts(cpu_env); + gen_jumpi_check_loop_end(dc, 0); +} + static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) { uint32_t mask = PS_WOE | PS_CALLINC | PS_OWB | @@ -381,10 +457,23 @@ static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) mask |= PS_RING; } tcg_gen_andi_i32(cpu_SR[sr], v, mask); - /* This can change mmu index, so exit tb */ + gen_helper_check_interrupts(cpu_env); + /* This can change mmu index and tb->flags, so exit tb */ gen_jumpi_check_loop_end(dc, -1); } +static void gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + uint32_t id = sr - CCOMPARE; + if (id < dc->config->nccompare) { + uint32_t int_bit = 1 << dc->config->timerint[id]; + gen_advance_ccount(dc); + tcg_gen_mov_i32(cpu_SR[sr], v); + tcg_gen_andi_i32(cpu_SR[INTSET], cpu_SR[INTSET], ~int_bit); + gen_helper_check_interrupts(cpu_env); + } +} + static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) { static void (* const wsr_handler[256])(DisasContext *dc, @@ -394,7 +483,13 @@ static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) [SAR] = gen_wsr_sar, [LITBASE] = gen_wsr_litbase, [WINDOW_BASE] = gen_wsr_windowbase, + [INTSET] = gen_wsr_intset, + [INTCLEAR] = gen_wsr_intclear, + [INTENABLE] = gen_wsr_intenable, [PS] = gen_wsr_ps, + [CCOMPARE] = gen_wsr_ccompare, + [CCOMPARE + 1] = gen_wsr_ccompare, + [CCOMPARE + 2] = gen_wsr_ccompare, }; if (sregnames[sr]) { @@ -425,6 +520,16 @@ static void gen_load_store_alignment(DisasContext *dc, int shift, } } +static void gen_waiti(DisasContext *dc, uint32_t imm4) +{ + TCGv_i32 pc = tcg_const_i32(dc->next_pc); + TCGv_i32 intlevel = tcg_const_i32(imm4); + gen_advance_ccount(dc); + gen_helper_waiti(pc, intlevel); + tcg_temp_free(pc); + tcg_temp_free(intlevel); +} + static void disas_xtensa_insn(DisasContext *dc) { #define HAS_OPTION(opt) do { \ @@ -561,6 +666,7 @@ static void disas_xtensa_insn(DisasContext *dc) HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); { TCGv_i32 tmp = tcg_const_i32(dc->pc); + gen_advance_ccount(dc); gen_helper_retw(tmp, tmp); gen_jump(dc, tmp); tcg_temp_free(tmp); @@ -606,6 +712,7 @@ static void disas_xtensa_insn(DisasContext *dc) HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); { TCGv_i32 pc = tcg_const_i32(dc->pc); + gen_advance_ccount(dc); gen_helper_movsp(pc); tcg_gen_mov_i32(cpu_R[RRR_T], cpu_R[RRR_S]); tcg_temp_free(pc); @@ -653,6 +760,7 @@ static void disas_xtensa_insn(DisasContext *dc) case 0: /*RFEx*/ gen_check_privilege(dc); tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); + gen_helper_check_interrupts(cpu_env); gen_jump(dc, cpu_SR[EPC1]); break; @@ -686,6 +794,7 @@ static void disas_xtensa_insn(DisasContext *dc) } gen_helper_restore_owb(); + gen_helper_check_interrupts(cpu_env); gen_jump(dc, cpu_SR[EPC1]); tcg_temp_free(tmp); @@ -700,7 +809,16 @@ static void disas_xtensa_insn(DisasContext *dc) case 1: /*RFIx*/ HAS_OPTION(XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT); - TBD(); + if (RRR_S >= 2 && RRR_S <= dc->config->nlevel) { + gen_check_privilege(dc); + tcg_gen_mov_i32(cpu_SR[PS], + cpu_SR[EPS2 + RRR_S - 2]); + gen_helper_check_interrupts(cpu_env); + gen_jump(dc, cpu_SR[EPC1 + RRR_S - 1]); + } else { + qemu_log("RFI %d is illegal\n", RRR_S); + gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); + } break; case 2: /*RFME*/ @@ -746,14 +864,16 @@ static void disas_xtensa_insn(DisasContext *dc) HAS_OPTION(XTENSA_OPTION_INTERRUPT); gen_check_privilege(dc); tcg_gen_mov_i32(cpu_R[RRR_T], cpu_SR[PS]); + tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_INTLEVEL); tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], RRR_S); - tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], - RRR_S | ~PS_INTLEVEL); + gen_helper_check_interrupts(cpu_env); + gen_jumpi_check_loop_end(dc, 0); break; case 7: /*WAITIx*/ HAS_OPTION(XTENSA_OPTION_INTERRUPT); - TBD(); + gen_check_privilege(dc); + gen_waiti(dc, RRR_S); break; case 8: /*ANY4p*/ @@ -1637,6 +1757,7 @@ static void disas_xtensa_insn(DisasContext *dc) TCGv_i32 pc = tcg_const_i32(dc->pc); TCGv_i32 s = tcg_const_i32(BRI12_S); TCGv_i32 imm = tcg_const_i32(BRI12_IMM12); + gen_advance_ccount(dc); gen_helper_entry(pc, s, imm); tcg_temp_free(imm); tcg_temp_free(s); @@ -1823,6 +1944,7 @@ static void disas_xtensa_insn(DisasContext *dc) HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER); { TCGv_i32 tmp = tcg_const_i32(dc->pc); + gen_advance_ccount(dc); gen_helper_retw(tmp, tmp); gen_jump(dc, tmp); tcg_temp_free(tmp); @@ -1876,7 +1998,7 @@ static void check_breakpoint(CPUState *env, DisasContext *dc) QTAILQ_FOREACH(bp, &env->breakpoints, entry) { if (bp->pc == dc->pc) { tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_exception(EXCP_DEBUG); + gen_exception(dc, EXCP_DEBUG); dc->is_jmp = DISAS_UPDATE; } } @@ -1908,6 +2030,7 @@ static void gen_intermediate_code_internal( dc.lbeg = env->sregs[LBEG]; dc.lend = env->sregs[LEND]; dc.is_jmp = DISAS_NEXT; + dc.ccount_delta = 0; init_litbase(&dc); init_sar_tracker(&dc); @@ -1917,7 +2040,7 @@ static void gen_intermediate_code_internal( if (env->singlestep_enabled && env->exception_taken) { env->exception_taken = 0; tcg_gen_movi_i32(cpu_pc, dc.pc); - gen_exception(EXCP_DEBUG); + gen_exception(&dc, EXCP_DEBUG); } do { @@ -1940,11 +2063,17 @@ static void gen_intermediate_code_internal( tcg_gen_debug_insn_start(dc.pc); } + ++dc.ccount_delta; + + if (insn_count + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + disas_xtensa_insn(&dc); ++insn_count; if (env->singlestep_enabled) { tcg_gen_movi_i32(cpu_pc, dc.pc); - gen_exception(EXCP_DEBUG); + gen_exception(&dc, EXCP_DEBUG); break; } } while (dc.is_jmp == DISAS_NEXT && @@ -1955,6 +2084,10 @@ static void gen_intermediate_code_internal( reset_litbase(&dc); reset_sar_tracker(&dc); + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + if (dc.is_jmp == DISAS_NEXT) { gen_jumpi(&dc, dc.pc, 0); } |