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>
This commit is contained in:
parent
1ddeaa5d42
commit
b994e91b00
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user