From patchwork Wed May 4 00:59:25 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Max Filippov X-Patchwork-Id: 93955 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 9FB73B6F3A for ; Wed, 4 May 2011 11:07:51 +1000 (EST) Received: from localhost ([::1]:39227 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QHQZA-0000MP-PQ for incoming@patchwork.ozlabs.org; Tue, 03 May 2011 21:07:48 -0400 Received: from eggs.gnu.org ([140.186.70.92]:53306) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QHQSe-0005hN-6F for qemu-devel@nongnu.org; Tue, 03 May 2011 21:01:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QHQSc-0000ud-Kb for qemu-devel@nongnu.org; Tue, 03 May 2011 21:01:04 -0400 Received: from mail-ew0-f45.google.com ([209.85.215.45]:56465) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QHQSc-0000ZL-7g for qemu-devel@nongnu.org; Tue, 03 May 2011 21:01:02 -0400 Received: by mail-ew0-f45.google.com with SMTP id 24so221435ewy.4 for ; Tue, 03 May 2011 18:01:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references; bh=j1/qnkiE2/bYfCOSMve+6dZcaeSVu/pAhPMyTSxF1tk=; b=PKtsNbxcGzmmB5g7NyItNqe1vwIh0RNiFE8vGQCwLRpMFWTsLK2EOGoFFgXSX8p615 VMzvbpK+BkStR/FUA9auhLeeQAuk1CJyANoBYBq4VEqfMUMpgGsa+75DCa/RGEoIACZI xI/FXGeuVhtwbylwMA7H5kpSBTgeULLQG8xG0= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=m05DHooJq/3LT+6vogRzYdqYmEkk472NRX+0D+FpfUzrRCmYZl2Edp0dOtMkg60H9+ TQ9iYtXeSN3zFi8ewhyZjK/AxdXvlwAjjT03f66LiUTMikd2z0i4fBVu/K0j/6QNPT5H 3JFYM1jDevwb7jh5H3BKZpDTD0ujeZFe2lt0w= Received: by 10.213.103.82 with SMTP id j18mr1625587ebo.64.1304470861787; Tue, 03 May 2011 18:01:01 -0700 (PDT) Received: from octofox.metropolis ([188.134.19.124]) by mx.google.com with ESMTPS id u16sm441016eei.9.2011.05.03.18.00.59 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 03 May 2011 18:01:01 -0700 (PDT) Received: by octofox.metropolis (sSMTP sendmail emulation); Wed, 4 May 2011 05:00:59 +0400 From: Max Filippov To: qemu-devel@nongnu.org Date: Wed, 4 May 2011 04:59:25 +0400 Message-Id: <1304470768-16924-25-git-send-email-jcmvbkbc@gmail.com> X-Mailer: git-send-email 1.7.3.4 In-Reply-To: <1304470768-16924-1-git-send-email-jcmvbkbc@gmail.com> References: <1304470768-16924-1-git-send-email-jcmvbkbc@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) X-Received-From: 209.85.215.45 Cc: Max Filippov Subject: [Qemu-devel] [RFC 25/28] target-xtensa: implement interrupt option X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org See ISA, 4.4.6 (interrupt option), 4.4.7 (high priority interrupt option) and 4.4.7 (timer interrupt option) for details. CCOUNT SR is incremented before every command and timer interrupt checking is inserted before every command. It may be done on TB boundary for optimization. WAITI doesn't go to sleep, but just sets PS.INTLEVEL. Timer may be used to avoid busy looping. Signed-off-by: Max Filippov --- hw/xtensa_pic.c | 43 ++++++++++++++++++++ target-xtensa/cpu.h | 33 +++++++++++++++ target-xtensa/helper.c | 79 +++++++++++++++++++++++++++++++++++- target-xtensa/helpers.h | 4 ++ target-xtensa/op_helper.c | 18 ++++++++ target-xtensa/translate.c | 98 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 272 insertions(+), 3 deletions(-) diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c index 5ff1697..aa1ae3b 100644 --- a/hw/xtensa_pic.c +++ b/hw/xtensa_pic.c @@ -9,3 +9,46 @@ void pic_info(Monitor *mon) void irq_info(Monitor *mon) { } + +void check_interrupts(CPUState *env) +{ + int minlevel = xtensa_get_cintlevel(env); + int level; + + for (level = env->config->nlevel; level > minlevel; --level) { + if (env->config->level_mask[level] & + env->sregs[INTSET] & + env->sregs[INTENABLE]) { + env->pending_irq_level = level; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + 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) { + printf("%s: bad IRQ %d\n", __func__, irq); + } else { + uint32_t irq_bit = 1 << irq; + + if (active) { + env->sregs[INTSET] |= irq_bit; + } else { + env->sregs[INTSET] &= ~irq_bit; + } + + check_interrupts(env); + } +} + +void xtensa_irq_init(CPUState *env) +{ + env->irq_inputs = (void **)qemu_allocate_irqs( + xtensa_set_irq, env, env->config->ninterrupt); +} diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 8110665..f3028a7 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,16 @@ enum { COPROCESSOR0_DISABLED = 32, }; +typedef enum { + INTTYPE_LEVEL, + INTTYPE_EDGE, + INTTYPE_NMI, + INTTYPE_SOFTWARE, + INTTYPE_TIMER, + INTTYPE_DEBUG, + INTTYPE_WRITE_ERR, +} interrupt_type_t; + typedef struct XtensaConfig { const char *name; uint64_t options; @@ -197,6 +217,14 @@ 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[MAX_NINTERRUPT]; + uint32_t level_mask[MAX_NLEVEL + MAX_NNMI + 1]; + interrupt_type_t inttype[MAX_NINTERRUPT]; + unsigned nccompare; + uint32_t timerint[MAX_NCCOMPARE]; } XtensaConfig; typedef struct CPUXtensaState { @@ -207,6 +235,9 @@ 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; + int exception_taken; CPU_COMMON @@ -222,6 +253,8 @@ 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); int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc); void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf); diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index 6df2e50..0325b03 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -40,6 +40,8 @@ void cpu_reset(CPUXtensaState *env) env->pc = env->config->exception_vector[EXC_RESET]; env->sregs[LITBASE] &= ~1; env->sregs[PS] = 0x1f; + + env->pending_irq_level = 0; } static const XtensaConfig core_config[] = { @@ -61,6 +63,30 @@ 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 = { + [0] = 4, + }, + .level_mask = { + [4] = 1, + }, + .inttype = { + [0] = INTTYPE_TIMER, + }, + .nccompare = 1, + .timerint = { + [0] = 0, + }, }, }; @@ -90,6 +116,7 @@ CPUXtensaState *cpu_xtensa_init(const char *cpu_model) xtensa_translate_init(); } + xtensa_irq_init(env); cpu_reset(env); qemu_init_vcpu(env); return env; @@ -110,9 +137,55 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) return addr; } +static int handle_interrupt(CPUState *env) +{ + int handled = 1; + 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->pc = env->config->interrupt_vector[level]; + } else { + handled = 0; + 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] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | level; + env->exception_taken = 1; + env->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + return handled; +} + void do_interrupt(CPUState *env) { switch (env->exception_index) { + case EXC_IRQ: + if (handle_interrupt(env)) { + break; + } + /* not handled interrupt falls through, + * env->exception_index is updated + */ + case EXC_WINDOW_OVERFLOW4: case EXC_WINDOW_UNDERFLOW4: case EXC_WINDOW_OVERFLOW8: @@ -125,12 +198,16 @@ void do_interrupt(CPUState *env) if (env->config->exception_vector[env->exception_index]) { env->pc = env->config->exception_vector[env->exception_index]; env->exception_taken = 1; + env->interrupt_request |= CPU_INTERRUPT_EXITTB; } else { printf("%s(pc = %08x) bad exception_index: %d\n", __func__, env->pc, env->exception_index); } break; + default: + printf("%s(pc = %08x) unknown exception_index: %d\n", + __func__, env->pc, env->exception_index); + break; } - env->interrupt_request |= CPU_INTERRUPT_EXITTB; } diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h index 55eb0d8..3f492e3 100644 --- a/target-xtensa/helpers.h +++ b/target-xtensa/helpers.h @@ -14,4 +14,8 @@ DEF_HELPER_1(wsr_lend, void, i32) DEF_HELPER_0(simcall, void) DEF_HELPER_0(dump_state, void) +DEF_HELPER_0(check_interrupts, void) +DEF_HELPER_2(waiti, void, i32, i32) +DEF_HELPER_2(timer_irq, void, i32, i32) + #include "def-helper.h" diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 68b1526..022dbb9 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -27,6 +27,7 @@ #include "exec.h" #include "helpers.h" +#include "hw/irq.h" #define MMUSUFFIX _mmu @@ -275,3 +276,20 @@ void HELPER(dump_state)(void) { cpu_dump_state(env, stderr, fprintf, 0); } + +void HELPER(check_interrupts)(void) +{ + check_interrupts(env); +} + +void HELPER(waiti)(uint32_t pc, uint32_t intlevel) +{ + env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | + (intlevel << PS_INTLEVEL_SHIFT); + check_interrupts(env); +} + +void HELPER(timer_irq)(uint32_t id, uint32_t active) +{ + qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); +} diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index ab86db8..ce1fbf6 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -67,11 +67,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] = { @@ -122,6 +147,12 @@ static inline int option_enabled(DisasContext *dc, int opt) return xtensa_option_enabled(dc->config, opt); } +static void gen_check_interrupts(DisasContext *dc) +{ + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_helper_check_interrupts(); +} + static void gen_rsr(TCGv_i32 d, int sr) { if (sregnames[sr]) { @@ -141,12 +172,32 @@ static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v) gen_helper_wsr_windowbase(v); } +static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + tcg_gen_mov_i32(cpu_SR[sr], v); + gen_check_interrupts(dc); +} + +static void gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v) +{ + TCGv_i32 id = tcg_const_i32(sr - CCOMPARE); + TCGv_i32 active = tcg_const_i32(0); + tcg_gen_mov_i32(cpu_SR[sr], v); + gen_helper_timer_irq(id, active); + tcg_temp_free(id); + tcg_temp_free(active); +} + static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) { static void (* const wsr_handler[256])(DisasContext *dc, uint32_t sr, TCGv_i32 v) = { [LEND] = gen_wsr_lend, [WINDOW_BASE] = gen_wsr_windowbase, + [PS] = gen_wsr_ps, + [CCOMPARE] = gen_wsr_ccompare, + [CCOMPARE + 1] = gen_wsr_ccompare, + [CCOMPARE + 2] = gen_wsr_ccompare, }; if (sregnames[sr]) { @@ -273,6 +324,15 @@ static void gen_load_store_alignment(DisasContext *dc, int shift, TCGv_i32 addr) tcg_temp_free(tmp); } +static void gen_waiti(DisasContext *dc, uint32_t imm4) +{ + TCGv_i32 pc = tcg_const_i32(dc->pc); + TCGv_i32 intlevel = tcg_const_i32(imm4); + 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 { \ @@ -539,7 +599,9 @@ static void disas_xtensa_insn(DisasContext *dc) case 1: /*RFIx*/ HAS_OPTION(XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT); - TBD(); + gen_check_privilege(dc); + tcg_gen_mov_i32(cpu_SR[PS], cpu_SR[EPS2 + RRR_S - 2]); + gen_jump(dc, cpu_SR[EPC1 + RRR_S - 1]); break; case 2: /*RFME*/ @@ -582,11 +644,13 @@ static void disas_xtensa_insn(DisasContext *dc) 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_check_interrupts(dc); break; case 7: /*WAITIx*/ HAS_OPTION(XTENSA_OPTION_INTERRUPT); - TBD(); + gen_check_privilege(dc); + gen_waiti(dc, RRR_S); break; case 8: /*ANY4p*/ @@ -1765,6 +1829,26 @@ static void check_breakpoint(CPUState *env, DisasContext *dc) } } +static void gen_timer(CPUState *env, DisasContext *dc) +{ + int id; + tcg_gen_addi_i32(cpu_SR[CCOUNT], cpu_SR[CCOUNT], 1); + for (id = 0; id < env->config->nccompare; ++id) { + int label = gen_new_label(); + tcg_gen_brcond_i32( + TCG_COND_NE, cpu_SR[CCOUNT], cpu_SR[CCOMPARE + id], label); + { + TCGv_i32 tid = tcg_const_i32(id); + TCGv_i32 active = tcg_const_i32(1); + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_helper_timer_irq(tid, active); + tcg_temp_free(tid); + tcg_temp_free(active); + } + gen_set_label(label); + } +} + static void gen_intermediate_code_internal( CPUState *env, TranslationBlock *tb, int search_pc) { @@ -1813,6 +1897,12 @@ static void gen_intermediate_code_internal( tcg_gen_debug_insn_start(dc.pc); } + gen_timer(env, &dc); + + if (insn_count + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + disas_xtensa_insn(&dc); ++insn_count; if (env->singlestep_enabled) { @@ -1824,6 +1914,10 @@ static void gen_intermediate_code_internal( insn_count < max_insns && gen_opc_ptr < gen_opc_end); + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + if (dc.is_jmp == DISAS_NEXT) { tcg_gen_movi_i32(cpu_pc, dc.pc); tcg_gen_exit_tb(0);