diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2020-07-05 17:31:59 -0700 |
---|---|---|
committer | Max Filippov <jcmvbkbc@gmail.com> | 2020-08-21 12:48:14 -0700 |
commit | a7d479ee51c5911c93ee931efe4c2413b54c8756 (patch) | |
tree | b20ef102ab90ee39b39f71df5208f08899bf5c42 /target/xtensa/exc_helper.c | |
parent | 91dc2b2d12a0343f37a3ab425f2912ae0ce18187 (diff) |
target/xtensa: implement NMI support
When NMI is configured it is taken regardless of INTENABLE SR contents,
PS.INTLEVEL or PS.EXCM. It is cleared automatically once it's taken.
Add nmi_level to XtensaConfig, puth there NMI level from the overlay or
XCHAL_NUM_INTLEVELS + 1 when NMI is not configured. Add NMI mask to
INTENABLE SR and limit CINTLEVEL to nmi_level - 1 when determining
pending IRQ level in check_interrupt(). Always take and clear pending
interrupt at nmi_level in the handle_interrupt().
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Diffstat (limited to 'target/xtensa/exc_helper.c')
-rw-r--r-- | target/xtensa/exc_helper.c | 23 |
1 files changed, 15 insertions, 8 deletions
diff --git a/target/xtensa/exc_helper.c b/target/xtensa/exc_helper.c index 601341d13a..58a64e6d62 100644 --- a/target/xtensa/exc_helper.c +++ b/target/xtensa/exc_helper.c @@ -132,11 +132,15 @@ void HELPER(intset)(CPUXtensaState *env, uint32_t v) v & env->config->inttype_mask[INTTYPE_SOFTWARE]); } +static void intclear(CPUXtensaState *env, uint32_t v) +{ + atomic_and(&env->sregs[INTSET], ~v); +} + void HELPER(intclear)(CPUXtensaState *env, uint32_t v) { - atomic_and(&env->sregs[INTSET], - ~(v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | - env->config->inttype_mask[INTTYPE_EDGE]))); + intclear(env, v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | + env->config->inttype_mask[INTTYPE_EDGE])); } static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector) @@ -159,11 +163,11 @@ static void handle_interrupt(CPUXtensaState *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 > xtensa_get_cintlevel(env) && + level <= env->config->nlevel && + (env->config->level_mask[level] & + env->sregs[INTSET] & env->sregs[INTENABLE])) || + level == env->config->nmi_level) { CPUState *cs = env_cpu(env); if (level > 1) { @@ -173,6 +177,9 @@ static void handle_interrupt(CPUXtensaState *env) (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; env->pc = relocated_vector(env, env->config->interrupt_vector[level]); + if (level == env->config->nmi_level) { + intclear(env, env->config->inttype_mask[INTTYPE_NMI]); + } } else { env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; |