* [Qemu-devel] [PATCH sh4: system emulator interrupt update
@ 2007-11-29 11:06 Magnus Damm
0 siblings, 0 replies; only message in thread
From: Magnus Damm @ 2007-11-29 11:06 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1038 bytes --]
Hi everyone,
This patch updates the interrupt and exception code for the sh4 system
emulator. Together with the delay slot patch posted yesterday this
patch implements working interrupts for the sh4 system emulator. There
are unfortunately no sources that actually hook up to the interrupt
controller yet - will post a patch for sh_timer in the near future.
Changes included in this patch:
- sh_intc now fully emulates intc registers and deliver interrupts
- do_interrupt() is updated to hook in sh_intc interrupts
- code to call do_interrupt() for sh4 is added in cpu-exec.c
- exception updates for do_interrupt() - masking etc
- trapa instruction immediate data fix
cpu-exec.c | 5 +-
hw/sh7750.c | 2
hw/sh_intc.c | 123 +++++++++++++++++++++++++++++++++++++++------------
hw/sh_intc.h | 8 ++-
target-sh4/cpu.h | 1
target-sh4/helper.c | 75 ++++++++++++++++++++++++-------
target-sh4/op.c | 2
7 files changed, 169 insertions(+), 47 deletions(-)
Please apply.
/ magnus
[-- Attachment #2: qemu-cvs-20071129-sh-irq.patch --]
[-- Type: application/octet-stream, Size: 10946 bytes --]
--- 0002/cpu-exec.c
+++ work/cpu-exec.c 2007-11-28 21:24:06.000000000 +0900
@@ -511,7 +511,10 @@ int cpu_exec(CPUState *env1)
BREAK_CHAIN;
}
#elif defined(TARGET_SH4)
- /* XXXXX */
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ do_interrupt(env);
+ BREAK_CHAIN;
+ }
#elif defined(TARGET_ALPHA)
if (interrupt_request & CPU_INTERRUPT_HARD) {
do_interrupt(env);
--- 0001/hw/sh7750.c
+++ work/hw/sh7750.c 2007-11-28 21:24:06.000000000 +0900
@@ -551,6 +551,8 @@ SH7750State *sh7750_init(CPUSH4State * c
_INTC_ARRAY(vectors),
_INTC_ARRAY(groups));
+ cpu->intc_handle = &s->intc;
+
sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0]);
sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF,
s->periph_freq, serial_hds[1]);
--- 0001/hw/sh_intc.c
+++ work/hw/sh_intc.c 2007-11-29 17:48:51.000000000 +0900
@@ -14,10 +14,91 @@
#include "sh.h"
//#define DEBUG_INTC
+//#define DEBUG_INTC_SOURCES
#define INTC_A7(x) ((x) & 0x1fffffff)
#define INTC_ARRAY(x) (sizeof(x) / sizeof(x[0]))
+void sh_intc_toggle_source(struct intc_source *source,
+ int enable_adj, int assert_adj)
+{
+ int enable_changed = 0;
+ int pending_changed = 0;
+ int old_pending;
+
+ if ((source->enable_count == source->enable_max) && (enable_adj == -1))
+ enable_changed = -1;
+
+ source->enable_count += enable_adj;
+
+ if (source->enable_count == source->enable_max)
+ enable_changed = 1;
+
+ source->asserted += assert_adj;
+
+ old_pending = source->pending;
+ source->pending = source->asserted &&
+ (source->enable_count == source->enable_max);
+
+ if (old_pending != source->pending)
+ pending_changed = 1;
+
+ if (pending_changed) {
+ if (source->pending) {
+ source->parent->pending++;
+ if (source->parent->pending == 1)
+ cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+ }
+ else {
+ source->parent->pending--;
+ if (source->parent->pending == 0)
+ cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+ }
+ }
+
+ if (enable_changed || assert_adj || pending_changed) {
+#ifdef DEBUG_INTC_SOURCES
+ printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n",
+ source->parent->pending,
+ source->asserted,
+ source->enable_count,
+ source->enable_max,
+ source->vect,
+ source->asserted ? "asserted " :
+ assert_adj ? "deasserted" : "",
+ enable_changed == 1 ? "enabled " :
+ enable_changed == -1 ? "disabled " : "",
+ source->pending ? "pending" : "");
+#endif
+ }
+}
+
+int sh_intc_get_pending_vector(struct intc_desc *desc, int imask)
+{
+ unsigned int i;
+
+ /* slow: use a linked lists of pending sources instead */
+ /* wrong: take interrupt priority into account (one list per priority) */
+
+ if (imask == 0x0f) {
+ return -1; /* FIXME, update code to include priority per source */
+ }
+
+ for (i = 0; i < desc->nr_sources; i++) {
+ struct intc_source *source = desc->sources + i;
+
+ if (source->pending) {
+#ifdef DEBUG_INTC_SOURCES
+ printf("sh_intc: (%d) returning interrupt source 0x%x\n",
+ desc->pending, source->vect);
+#endif
+ return source->vect;
+ }
+ }
+
+ assert(0);
+}
+
#define INTC_MODE_NONE 0
#define INTC_MODE_DUAL_SET 1
#define INTC_MODE_DUAL_CLR 2
@@ -94,42 +175,24 @@ static void sh_intc_locate(struct intc_d
assert(0);
}
-static void sh_intc_toggle(struct intc_desc *desc, intc_enum id,
- int enable, int is_group)
+static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id,
+ int enable, int is_group)
{
struct intc_source *source = desc->sources + id;
- int old = source->enable_count;
if (!id)
return;
if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
-#ifdef DEBUG_INTC
+#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: reserved interrupt source %d modified\n", id);
#endif
return;
}
- if (source->vect) {
- if (enable)
- source->enable_count++;
- else
- source->enable_count--;
+ if (source->vect)
+ sh_intc_toggle_source(source, enable ? 1 : -1, 0);
- if (source->enable_count == source->enable_max) {
-#ifdef DEBUG_INTC
- printf("sh_intc: enabling interrupt source %d -> 0x%04x\n",
- id, source->vect);
-#endif
- }
-
- if (old == source->enable_max) {
-#ifdef DEBUG_INTC
- printf("sh_intc: disabling interrupt source %d -> 0x%04x\n",
- id, source->vect);
-#endif
- }
- }
#ifdef DEBUG_INTC
else {
printf("setting interrupt group %d to %d\n", id, !!enable);
@@ -137,7 +200,7 @@ static void sh_intc_toggle(struct intc_d
#endif
if ((is_group || !source->vect) && source->next_enum_id) {
- sh_intc_toggle(desc, source->next_enum_id, enable, 1);
+ sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1);
}
#ifdef DEBUG_INTC
@@ -200,7 +263,7 @@ static void sh_intc_write(void *opaque,
printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
k, first, enum_ids[k], (unsigned int)mask);
#endif
- sh_intc_toggle(desc, enum_ids[k], value & mask, 0);
+ sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0);
}
*valuep = value;
@@ -309,7 +372,7 @@ void sh_intc_register_sources(struct int
if (s)
s->vect = vect->vect;
-#ifdef DEBUG_INTC
+#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
vect->enum_id, s->vect, s->enable_count, s->enable_max);
#endif
@@ -330,7 +393,7 @@ void sh_intc_register_sources(struct int
s->next_enum_id = gr->enum_ids[k];
}
-#ifdef DEBUG_INTC
+#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: registered group %d (%d/%d)\n",
gr->enum_id, s->enable_count, s->enable_max);
#endif
@@ -347,6 +410,7 @@ int sh_intc_init(struct intc_desc *desc,
{
unsigned int i;
+ desc->pending = 0;
desc->nr_sources = nr_sources;
desc->mask_regs = mask_regs;
desc->nr_mask_regs = nr_mask_regs;
@@ -359,6 +423,11 @@ int sh_intc_init(struct intc_desc *desc,
return -1;
memset(desc->sources, 0, i);
+ for (i = 0; i < desc->nr_sources; i++) {
+ struct intc_source *source = desc->sources + i;
+
+ source->parent = desc;
+ }
desc->iomemtype = cpu_register_io_memory(0, sh_intc_readfn,
sh_intc_writefn, desc);
--- 0001/hw/sh_intc.h
+++ work/hw/sh_intc.h 2007-11-29 17:12:08.000000000 +0900
@@ -35,9 +35,11 @@ struct intc_source {
unsigned short vect;
intc_enum next_enum_id;
- int asserted;
+ int asserted; /* emulates the interrupt signal line from device to intc */
int enable_count;
int enable_max;
+ int pending; /* emulates the result of signal and masking */
+ struct intc_desc *parent;
};
struct intc_desc {
@@ -49,9 +51,13 @@ struct intc_desc {
int nr_prio_regs;
int iomemtype;
+ int pending; /* number of interrupt sources that has pending set */
};
+int sh_intc_get_pending_vector(struct intc_desc *desc, int imask);
struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id);
+void sh_intc_toggle_source(struct intc_source *source,
+ int enable_adj, int assert_adj);
void sh_intc_register_sources(struct intc_desc *desc,
struct intc_vect *vectors,
--- 0002/target-sh4/cpu.h
+++ work/target-sh4/cpu.h 2007-11-28 21:24:06.000000000 +0900
@@ -121,6 +121,7 @@ typedef struct CPUSH4State {
int exception_index;
CPU_COMMON tlb_t utlb[UTLB_SIZE]; /* unified translation table */
tlb_t itlb[ITLB_SIZE]; /* instruction translation table */
+ void *intc_handle;
} CPUSH4State;
CPUSH4State *cpu_sh4_init(const char *cpu_model);
--- 0003/target-sh4/helper.c
+++ work/target-sh4/helper.c 2007-11-29 17:33:01.000000000 +0900
@@ -27,6 +27,7 @@
#include "cpu.h"
#include "exec-all.h"
+#include "hw/sh_intc.h"
#if defined(CONFIG_USER_ONLY)
@@ -74,6 +75,31 @@ target_phys_addr_t cpu_get_phys_page_deb
void do_interrupt(CPUState * env)
{
+ int do_irq = env->interrupt_request & CPU_INTERRUPT_HARD;
+ int do_exp, irq_vector = env->exception_index;
+
+ /* prioritize exceptions over interrupts */
+
+ do_exp = env->exception_index != -1;
+ do_irq = do_irq && (env->exception_index == -1);
+
+ if (env->sr & SR_BL) {
+ if (do_exp && env->exception_index != 0x1e0) {
+ env->exception_index = 0x000; /* masked exception -> reset */
+ }
+ if (do_irq) {
+ return; /* masked */
+ }
+ }
+
+ if (do_irq) {
+ irq_vector = sh_intc_get_pending_vector(env->intc_handle,
+ (env->sr >> 4) & 0xf);
+ if (irq_vector == -1) {
+ return; /* masked */
+ }
+ }
+
if (loglevel & CPU_LOG_INT) {
const char *expname;
switch (env->exception_index) {
@@ -117,32 +143,47 @@ void do_interrupt(CPUState * env)
expname = "trapa";
break;
default:
- expname = "???";
- break;
+ expname = do_irq ? "interrupt" : "???";
+ break;
}
fprintf(logfile, "exception 0x%03x [%s] raised\n",
- env->exception_index, expname);
+ irq_vector, expname);
cpu_dump_state(env, logfile, fprintf, 0);
}
env->ssr = env->sr;
- env->spc = env->spc;
+ env->spc = env->pc;
env->sgr = env->gregs[15];
env->sr |= SR_BL | SR_MD | SR_RB;
- env->expevt = env->exception_index & 0x7ff;
- switch (env->exception_index) {
- case 0x040:
- case 0x060:
- case 0x080:
- env->pc = env->vbr + 0x400;
- break;
- case 0x140:
- env->pc = 0xa0000000;
- break;
- default:
- env->pc = env->vbr + 0x100;
- break;
+ if (do_exp) {
+ env->expevt = env->exception_index;
+ switch (env->exception_index) {
+ case 0x000:
+ case 0x020:
+ case 0x140:
+ env->sr &= ~SR_FD;
+ env->sr |= 0xf << 4; /* IMASK */
+ env->pc = 0xa0000000;
+ break;
+ case 0x040:
+ case 0x060:
+ env->pc = env->vbr + 0x400;
+ break;
+ case 0x160:
+ env->spc += 2; /* special case for TRAPA */
+ /* fall through */
+ default:
+ env->pc = env->vbr + 0x100;
+ break;
+ }
+ return;
+ }
+
+ if (do_irq) {
+ env->intevt = irq_vector;
+ env->pc = env->vbr + 0x600;
+ return;
}
}
--- 0002/target-sh4/op.c
+++ work/target-sh4/op.c 2007-11-29 16:55:24.000000000 +0900
@@ -419,7 +419,7 @@ void OPPROTO op_subv_T0_T1(void)
void OPPROTO op_trapa(void)
{
- env->tra = PARAM1 * 2;
+ env->tra = PARAM1 << 2;
env->exception_index = 0x160;
do_raise_exception();
RETURN();
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2007-11-29 11:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-29 11:06 [Qemu-devel] [PATCH sh4: system emulator interrupt update Magnus Damm
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).