aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/xtensa_pic.c96
-rw-r--r--target-xtensa/cpu.h45
-rw-r--r--target-xtensa/helper.c98
-rw-r--r--target-xtensa/helpers.h5
-rw-r--r--target-xtensa/op_helper.c46
-rw-r--r--target-xtensa/translate.c153
6 files changed, 431 insertions, 12 deletions
diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c
index 91a5445..3033ae2 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 1283fd9..474466c 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 bcd6c77..c24a38a 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 6e02d26..28689c3 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 d37f70d..0047fa3 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 1598e27..d75e780 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);
}