diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c index 3033ae214a..e5085ea9e7 100644 --- a/hw/xtensa_pic.c +++ b/hw/xtensa_pic.c @@ -116,10 +116,35 @@ void xtensa_timer_irq(CPUState *env, uint32_t id, uint32_t active) qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); } +void xtensa_rearm_ccompare_timer(CPUState *env) +{ + 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, env->halt_clock + + muldiv64(wake_ccount - env->sregs[CCOUNT], + 1000000, env->config->clock_freq_khz)); +} + static void xtensa_ccompare_cb(void *opaque) { CPUState *env = opaque; - xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); + + if (env->halted) { + env->halt_clock = qemu_get_clock_ns(vm_clock); + xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); + if (!cpu_has_work(env)) { + env->sregs[CCOUNT] = env->wake_ccount + 1; + xtensa_rearm_ccompare_timer(env); + } + } } void xtensa_irq_init(CPUState *env) diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 339075dda1..966f515249 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -313,6 +313,7 @@ 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); +void xtensa_rearm_ccompare_timer(CPUState *env); 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); diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 64847fc8e6..0605611031 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -370,23 +370,11 @@ void HELPER(waiti)(uint32_t pc, uint32_t intlevel) 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; + if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { + xtensa_rearm_ccompare_timer(env); + } HELPER(exception)(EXCP_HLT); } diff --git a/tests/xtensa/test_timer.S b/tests/xtensa/test_timer.S index ede63955cc..1041cc6658 100644 --- a/tests/xtensa/test_timer.S +++ b/tests/xtensa/test_timer.S @@ -14,6 +14,7 @@ test ccompare wsr a2, intenable rsr a2, interrupt wsr a2, intclear + movi a2, 0 wsr a2, ccompare1 wsr a2, ccompare2 @@ -37,6 +38,7 @@ test ccompare0_interrupt wsr a2, intenable rsr a2, interrupt wsr a2, intclear + movi a2, 0 wsr a2, ccompare1 wsr a2, ccompare2 @@ -66,6 +68,7 @@ test ccompare1_interrupt wsr a2, intenable rsr a2, interrupt wsr a2, intclear + movi a2, 0 wsr a2, ccompare0 wsr a2, ccompare2 @@ -92,6 +95,7 @@ test ccompare2_interrupt wsr a2, intenable rsr a2, interrupt wsr a2, intclear + movi a2, 0 wsr a2, ccompare0 wsr a2, ccompare1 @@ -112,4 +116,63 @@ test ccompare2_interrupt 2: test_end +test ccompare_interrupt_masked + set_vector kernel, 2f + movi a2, 0 + wsr a2, intenable + rsr a2, interrupt + wsr a2, intclear + movi a2, 0 + wsr a2, ccompare2 + + movi a3, 40 + rsr a2, ccount + addi a2, a2, 20 + wsr a2, ccompare1 + addi a2, a2, 20 + wsr a2, ccompare0 + rsync + rsr a2, interrupt + assert eqi, a2, 0 + + movi a2, 0x40 + wsr a2, intenable + rsil a2, 0 + loop a3, 1f + nop +1: + test_fail +2: + rsr a2, exccause + assert eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */ +test_end + +test ccompare_interrupt_masked_waiti + set_vector kernel, 2f + movi a2, 0 + wsr a2, intenable + rsr a2, interrupt + wsr a2, intclear + movi a2, 0 + wsr a2, ccompare2 + + movi a3, 40 + rsr a2, ccount + addi a2, a2, 20 + wsr a2, ccompare1 + addi a2, a2, 20 + wsr a2, ccompare0 + rsync + rsr a2, interrupt + assert eqi, a2, 0 + + movi a2, 0x40 + wsr a2, intenable + waiti 0 + test_fail +2: + rsr a2, exccause + assert eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */ +test_end + test_suite_end