Commit e96e2044a14340bf1e612b7f046093495c10a06f
1 parent
823029f9
SH4: system emulator interrupt update, by Magnus Damm.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3762 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
7 changed files
with
169 additions
and
47 deletions
cpu-exec.c
| @@ -511,7 +511,10 @@ int cpu_exec(CPUState *env1) | @@ -511,7 +511,10 @@ int cpu_exec(CPUState *env1) | ||
| 511 | BREAK_CHAIN; | 511 | BREAK_CHAIN; |
| 512 | } | 512 | } |
| 513 | #elif defined(TARGET_SH4) | 513 | #elif defined(TARGET_SH4) |
| 514 | - /* XXXXX */ | 514 | + if (interrupt_request & CPU_INTERRUPT_HARD) { |
| 515 | + do_interrupt(env); | ||
| 516 | + BREAK_CHAIN; | ||
| 517 | + } | ||
| 515 | #elif defined(TARGET_ALPHA) | 518 | #elif defined(TARGET_ALPHA) |
| 516 | if (interrupt_request & CPU_INTERRUPT_HARD) { | 519 | if (interrupt_request & CPU_INTERRUPT_HARD) { |
| 517 | do_interrupt(env); | 520 | do_interrupt(env); |
hw/sh7750.c
| @@ -551,6 +551,8 @@ SH7750State *sh7750_init(CPUSH4State * cpu) | @@ -551,6 +551,8 @@ SH7750State *sh7750_init(CPUSH4State * cpu) | ||
| 551 | _INTC_ARRAY(vectors), | 551 | _INTC_ARRAY(vectors), |
| 552 | _INTC_ARRAY(groups)); | 552 | _INTC_ARRAY(groups)); |
| 553 | 553 | ||
| 554 | + cpu->intc_handle = &s->intc; | ||
| 555 | + | ||
| 554 | sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0]); | 556 | sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0]); |
| 555 | sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF, | 557 | sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF, |
| 556 | s->periph_freq, serial_hds[1]); | 558 | s->periph_freq, serial_hds[1]); |
hw/sh_intc.c
| @@ -14,10 +14,91 @@ | @@ -14,10 +14,91 @@ | ||
| 14 | #include "sh.h" | 14 | #include "sh.h" |
| 15 | 15 | ||
| 16 | //#define DEBUG_INTC | 16 | //#define DEBUG_INTC |
| 17 | +//#define DEBUG_INTC_SOURCES | ||
| 17 | 18 | ||
| 18 | #define INTC_A7(x) ((x) & 0x1fffffff) | 19 | #define INTC_A7(x) ((x) & 0x1fffffff) |
| 19 | #define INTC_ARRAY(x) (sizeof(x) / sizeof(x[0])) | 20 | #define INTC_ARRAY(x) (sizeof(x) / sizeof(x[0])) |
| 20 | 21 | ||
| 22 | +void sh_intc_toggle_source(struct intc_source *source, | ||
| 23 | + int enable_adj, int assert_adj) | ||
| 24 | +{ | ||
| 25 | + int enable_changed = 0; | ||
| 26 | + int pending_changed = 0; | ||
| 27 | + int old_pending; | ||
| 28 | + | ||
| 29 | + if ((source->enable_count == source->enable_max) && (enable_adj == -1)) | ||
| 30 | + enable_changed = -1; | ||
| 31 | + | ||
| 32 | + source->enable_count += enable_adj; | ||
| 33 | + | ||
| 34 | + if (source->enable_count == source->enable_max) | ||
| 35 | + enable_changed = 1; | ||
| 36 | + | ||
| 37 | + source->asserted += assert_adj; | ||
| 38 | + | ||
| 39 | + old_pending = source->pending; | ||
| 40 | + source->pending = source->asserted && | ||
| 41 | + (source->enable_count == source->enable_max); | ||
| 42 | + | ||
| 43 | + if (old_pending != source->pending) | ||
| 44 | + pending_changed = 1; | ||
| 45 | + | ||
| 46 | + if (pending_changed) { | ||
| 47 | + if (source->pending) { | ||
| 48 | + source->parent->pending++; | ||
| 49 | + if (source->parent->pending == 1) | ||
| 50 | + cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD); | ||
| 51 | + } | ||
| 52 | + else { | ||
| 53 | + source->parent->pending--; | ||
| 54 | + if (source->parent->pending == 0) | ||
| 55 | + cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + if (enable_changed || assert_adj || pending_changed) { | ||
| 60 | +#ifdef DEBUG_INTC_SOURCES | ||
| 61 | + printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n", | ||
| 62 | + source->parent->pending, | ||
| 63 | + source->asserted, | ||
| 64 | + source->enable_count, | ||
| 65 | + source->enable_max, | ||
| 66 | + source->vect, | ||
| 67 | + source->asserted ? "asserted " : | ||
| 68 | + assert_adj ? "deasserted" : "", | ||
| 69 | + enable_changed == 1 ? "enabled " : | ||
| 70 | + enable_changed == -1 ? "disabled " : "", | ||
| 71 | + source->pending ? "pending" : ""); | ||
| 72 | +#endif | ||
| 73 | + } | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) | ||
| 77 | +{ | ||
| 78 | + unsigned int i; | ||
| 79 | + | ||
| 80 | + /* slow: use a linked lists of pending sources instead */ | ||
| 81 | + /* wrong: take interrupt priority into account (one list per priority) */ | ||
| 82 | + | ||
| 83 | + if (imask == 0x0f) { | ||
| 84 | + return -1; /* FIXME, update code to include priority per source */ | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + for (i = 0; i < desc->nr_sources; i++) { | ||
| 88 | + struct intc_source *source = desc->sources + i; | ||
| 89 | + | ||
| 90 | + if (source->pending) { | ||
| 91 | +#ifdef DEBUG_INTC_SOURCES | ||
| 92 | + printf("sh_intc: (%d) returning interrupt source 0x%x\n", | ||
| 93 | + desc->pending, source->vect); | ||
| 94 | +#endif | ||
| 95 | + return source->vect; | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + assert(0); | ||
| 100 | +} | ||
| 101 | + | ||
| 21 | #define INTC_MODE_NONE 0 | 102 | #define INTC_MODE_NONE 0 |
| 22 | #define INTC_MODE_DUAL_SET 1 | 103 | #define INTC_MODE_DUAL_SET 1 |
| 23 | #define INTC_MODE_DUAL_CLR 2 | 104 | #define INTC_MODE_DUAL_CLR 2 |
| @@ -94,42 +175,24 @@ static void sh_intc_locate(struct intc_desc *desc, | @@ -94,42 +175,24 @@ static void sh_intc_locate(struct intc_desc *desc, | ||
| 94 | assert(0); | 175 | assert(0); |
| 95 | } | 176 | } |
| 96 | 177 | ||
| 97 | -static void sh_intc_toggle(struct intc_desc *desc, intc_enum id, | ||
| 98 | - int enable, int is_group) | 178 | +static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, |
| 179 | + int enable, int is_group) | ||
| 99 | { | 180 | { |
| 100 | struct intc_source *source = desc->sources + id; | 181 | struct intc_source *source = desc->sources + id; |
| 101 | - int old = source->enable_count; | ||
| 102 | 182 | ||
| 103 | if (!id) | 183 | if (!id) |
| 104 | return; | 184 | return; |
| 105 | 185 | ||
| 106 | if (!source->next_enum_id && (!source->enable_max || !source->vect)) { | 186 | if (!source->next_enum_id && (!source->enable_max || !source->vect)) { |
| 107 | -#ifdef DEBUG_INTC | 187 | +#ifdef DEBUG_INTC_SOURCES |
| 108 | printf("sh_intc: reserved interrupt source %d modified\n", id); | 188 | printf("sh_intc: reserved interrupt source %d modified\n", id); |
| 109 | #endif | 189 | #endif |
| 110 | return; | 190 | return; |
| 111 | } | 191 | } |
| 112 | 192 | ||
| 113 | - if (source->vect) { | ||
| 114 | - if (enable) | ||
| 115 | - source->enable_count++; | ||
| 116 | - else | ||
| 117 | - source->enable_count--; | 193 | + if (source->vect) |
| 194 | + sh_intc_toggle_source(source, enable ? 1 : -1, 0); | ||
| 118 | 195 | ||
| 119 | - if (source->enable_count == source->enable_max) { | ||
| 120 | -#ifdef DEBUG_INTC | ||
| 121 | - printf("sh_intc: enabling interrupt source %d -> 0x%04x\n", | ||
| 122 | - id, source->vect); | ||
| 123 | -#endif | ||
| 124 | - } | ||
| 125 | - | ||
| 126 | - if (old == source->enable_max) { | ||
| 127 | -#ifdef DEBUG_INTC | ||
| 128 | - printf("sh_intc: disabling interrupt source %d -> 0x%04x\n", | ||
| 129 | - id, source->vect); | ||
| 130 | -#endif | ||
| 131 | - } | ||
| 132 | - } | ||
| 133 | #ifdef DEBUG_INTC | 196 | #ifdef DEBUG_INTC |
| 134 | else { | 197 | else { |
| 135 | printf("setting interrupt group %d to %d\n", id, !!enable); | 198 | printf("setting interrupt group %d to %d\n", id, !!enable); |
| @@ -137,7 +200,7 @@ static void sh_intc_toggle(struct intc_desc *desc, intc_enum id, | @@ -137,7 +200,7 @@ static void sh_intc_toggle(struct intc_desc *desc, intc_enum id, | ||
| 137 | #endif | 200 | #endif |
| 138 | 201 | ||
| 139 | if ((is_group || !source->vect) && source->next_enum_id) { | 202 | if ((is_group || !source->vect) && source->next_enum_id) { |
| 140 | - sh_intc_toggle(desc, source->next_enum_id, enable, 1); | 203 | + sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1); |
| 141 | } | 204 | } |
| 142 | 205 | ||
| 143 | #ifdef DEBUG_INTC | 206 | #ifdef DEBUG_INTC |
| @@ -200,7 +263,7 @@ static void sh_intc_write(void *opaque, target_phys_addr_t offset, | @@ -200,7 +263,7 @@ static void sh_intc_write(void *opaque, target_phys_addr_t offset, | ||
| 200 | printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", | 263 | printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", |
| 201 | k, first, enum_ids[k], (unsigned int)mask); | 264 | k, first, enum_ids[k], (unsigned int)mask); |
| 202 | #endif | 265 | #endif |
| 203 | - sh_intc_toggle(desc, enum_ids[k], value & mask, 0); | 266 | + sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0); |
| 204 | } | 267 | } |
| 205 | 268 | ||
| 206 | *valuep = value; | 269 | *valuep = value; |
| @@ -309,7 +372,7 @@ void sh_intc_register_sources(struct intc_desc *desc, | @@ -309,7 +372,7 @@ void sh_intc_register_sources(struct intc_desc *desc, | ||
| 309 | if (s) | 372 | if (s) |
| 310 | s->vect = vect->vect; | 373 | s->vect = vect->vect; |
| 311 | 374 | ||
| 312 | -#ifdef DEBUG_INTC | 375 | +#ifdef DEBUG_INTC_SOURCES |
| 313 | printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n", | 376 | printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n", |
| 314 | vect->enum_id, s->vect, s->enable_count, s->enable_max); | 377 | vect->enum_id, s->vect, s->enable_count, s->enable_max); |
| 315 | #endif | 378 | #endif |
| @@ -330,7 +393,7 @@ void sh_intc_register_sources(struct intc_desc *desc, | @@ -330,7 +393,7 @@ void sh_intc_register_sources(struct intc_desc *desc, | ||
| 330 | s->next_enum_id = gr->enum_ids[k]; | 393 | s->next_enum_id = gr->enum_ids[k]; |
| 331 | } | 394 | } |
| 332 | 395 | ||
| 333 | -#ifdef DEBUG_INTC | 396 | +#ifdef DEBUG_INTC_SOURCES |
| 334 | printf("sh_intc: registered group %d (%d/%d)\n", | 397 | printf("sh_intc: registered group %d (%d/%d)\n", |
| 335 | gr->enum_id, s->enable_count, s->enable_max); | 398 | gr->enum_id, s->enable_count, s->enable_max); |
| 336 | #endif | 399 | #endif |
| @@ -347,6 +410,7 @@ int sh_intc_init(struct intc_desc *desc, | @@ -347,6 +410,7 @@ int sh_intc_init(struct intc_desc *desc, | ||
| 347 | { | 410 | { |
| 348 | unsigned int i; | 411 | unsigned int i; |
| 349 | 412 | ||
| 413 | + desc->pending = 0; | ||
| 350 | desc->nr_sources = nr_sources; | 414 | desc->nr_sources = nr_sources; |
| 351 | desc->mask_regs = mask_regs; | 415 | desc->mask_regs = mask_regs; |
| 352 | desc->nr_mask_regs = nr_mask_regs; | 416 | desc->nr_mask_regs = nr_mask_regs; |
| @@ -359,6 +423,11 @@ int sh_intc_init(struct intc_desc *desc, | @@ -359,6 +423,11 @@ int sh_intc_init(struct intc_desc *desc, | ||
| 359 | return -1; | 423 | return -1; |
| 360 | 424 | ||
| 361 | memset(desc->sources, 0, i); | 425 | memset(desc->sources, 0, i); |
| 426 | + for (i = 0; i < desc->nr_sources; i++) { | ||
| 427 | + struct intc_source *source = desc->sources + i; | ||
| 428 | + | ||
| 429 | + source->parent = desc; | ||
| 430 | + } | ||
| 362 | 431 | ||
| 363 | desc->iomemtype = cpu_register_io_memory(0, sh_intc_readfn, | 432 | desc->iomemtype = cpu_register_io_memory(0, sh_intc_readfn, |
| 364 | sh_intc_writefn, desc); | 433 | sh_intc_writefn, desc); |
hw/sh_intc.h
| @@ -35,9 +35,11 @@ struct intc_source { | @@ -35,9 +35,11 @@ struct intc_source { | ||
| 35 | unsigned short vect; | 35 | unsigned short vect; |
| 36 | intc_enum next_enum_id; | 36 | intc_enum next_enum_id; |
| 37 | 37 | ||
| 38 | - int asserted; | 38 | + int asserted; /* emulates the interrupt signal line from device to intc */ |
| 39 | int enable_count; | 39 | int enable_count; |
| 40 | int enable_max; | 40 | int enable_max; |
| 41 | + int pending; /* emulates the result of signal and masking */ | ||
| 42 | + struct intc_desc *parent; | ||
| 41 | }; | 43 | }; |
| 42 | 44 | ||
| 43 | struct intc_desc { | 45 | struct intc_desc { |
| @@ -49,9 +51,13 @@ struct intc_desc { | @@ -49,9 +51,13 @@ struct intc_desc { | ||
| 49 | int nr_prio_regs; | 51 | int nr_prio_regs; |
| 50 | 52 | ||
| 51 | int iomemtype; | 53 | int iomemtype; |
| 54 | + int pending; /* number of interrupt sources that has pending set */ | ||
| 52 | }; | 55 | }; |
| 53 | 56 | ||
| 57 | +int sh_intc_get_pending_vector(struct intc_desc *desc, int imask); | ||
| 54 | struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id); | 58 | struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id); |
| 59 | +void sh_intc_toggle_source(struct intc_source *source, | ||
| 60 | + int enable_adj, int assert_adj); | ||
| 55 | 61 | ||
| 56 | void sh_intc_register_sources(struct intc_desc *desc, | 62 | void sh_intc_register_sources(struct intc_desc *desc, |
| 57 | struct intc_vect *vectors, | 63 | struct intc_vect *vectors, |
target-sh4/cpu.h
| @@ -121,6 +121,7 @@ typedef struct CPUSH4State { | @@ -121,6 +121,7 @@ typedef struct CPUSH4State { | ||
| 121 | int exception_index; | 121 | int exception_index; |
| 122 | CPU_COMMON tlb_t utlb[UTLB_SIZE]; /* unified translation table */ | 122 | CPU_COMMON tlb_t utlb[UTLB_SIZE]; /* unified translation table */ |
| 123 | tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ | 123 | tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ |
| 124 | + void *intc_handle; | ||
| 124 | } CPUSH4State; | 125 | } CPUSH4State; |
| 125 | 126 | ||
| 126 | CPUSH4State *cpu_sh4_init(const char *cpu_model); | 127 | CPUSH4State *cpu_sh4_init(const char *cpu_model); |
target-sh4/helper.c
| @@ -27,6 +27,7 @@ | @@ -27,6 +27,7 @@ | ||
| 27 | 27 | ||
| 28 | #include "cpu.h" | 28 | #include "cpu.h" |
| 29 | #include "exec-all.h" | 29 | #include "exec-all.h" |
| 30 | +#include "hw/sh_intc.h" | ||
| 30 | 31 | ||
| 31 | #if defined(CONFIG_USER_ONLY) | 32 | #if defined(CONFIG_USER_ONLY) |
| 32 | 33 | ||
| @@ -74,6 +75,31 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) | @@ -74,6 +75,31 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) | ||
| 74 | 75 | ||
| 75 | void do_interrupt(CPUState * env) | 76 | void do_interrupt(CPUState * env) |
| 76 | { | 77 | { |
| 78 | + int do_irq = env->interrupt_request & CPU_INTERRUPT_HARD; | ||
| 79 | + int do_exp, irq_vector = env->exception_index; | ||
| 80 | + | ||
| 81 | + /* prioritize exceptions over interrupts */ | ||
| 82 | + | ||
| 83 | + do_exp = env->exception_index != -1; | ||
| 84 | + do_irq = do_irq && (env->exception_index == -1); | ||
| 85 | + | ||
| 86 | + if (env->sr & SR_BL) { | ||
| 87 | + if (do_exp && env->exception_index != 0x1e0) { | ||
| 88 | + env->exception_index = 0x000; /* masked exception -> reset */ | ||
| 89 | + } | ||
| 90 | + if (do_irq) { | ||
| 91 | + return; /* masked */ | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + if (do_irq) { | ||
| 96 | + irq_vector = sh_intc_get_pending_vector(env->intc_handle, | ||
| 97 | + (env->sr >> 4) & 0xf); | ||
| 98 | + if (irq_vector == -1) { | ||
| 99 | + return; /* masked */ | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 77 | if (loglevel & CPU_LOG_INT) { | 103 | if (loglevel & CPU_LOG_INT) { |
| 78 | const char *expname; | 104 | const char *expname; |
| 79 | switch (env->exception_index) { | 105 | switch (env->exception_index) { |
| @@ -117,32 +143,47 @@ void do_interrupt(CPUState * env) | @@ -117,32 +143,47 @@ void do_interrupt(CPUState * env) | ||
| 117 | expname = "trapa"; | 143 | expname = "trapa"; |
| 118 | break; | 144 | break; |
| 119 | default: | 145 | default: |
| 120 | - expname = "???"; | ||
| 121 | - break; | 146 | + expname = do_irq ? "interrupt" : "???"; |
| 147 | + break; | ||
| 122 | } | 148 | } |
| 123 | fprintf(logfile, "exception 0x%03x [%s] raised\n", | 149 | fprintf(logfile, "exception 0x%03x [%s] raised\n", |
| 124 | - env->exception_index, expname); | 150 | + irq_vector, expname); |
| 125 | cpu_dump_state(env, logfile, fprintf, 0); | 151 | cpu_dump_state(env, logfile, fprintf, 0); |
| 126 | } | 152 | } |
| 127 | 153 | ||
| 128 | env->ssr = env->sr; | 154 | env->ssr = env->sr; |
| 129 | - env->spc = env->spc; | 155 | + env->spc = env->pc; |
| 130 | env->sgr = env->gregs[15]; | 156 | env->sgr = env->gregs[15]; |
| 131 | env->sr |= SR_BL | SR_MD | SR_RB; | 157 | env->sr |= SR_BL | SR_MD | SR_RB; |
| 132 | 158 | ||
| 133 | - env->expevt = env->exception_index & 0x7ff; | ||
| 134 | - switch (env->exception_index) { | ||
| 135 | - case 0x040: | ||
| 136 | - case 0x060: | ||
| 137 | - case 0x080: | ||
| 138 | - env->pc = env->vbr + 0x400; | ||
| 139 | - break; | ||
| 140 | - case 0x140: | ||
| 141 | - env->pc = 0xa0000000; | ||
| 142 | - break; | ||
| 143 | - default: | ||
| 144 | - env->pc = env->vbr + 0x100; | ||
| 145 | - break; | 159 | + if (do_exp) { |
| 160 | + env->expevt = env->exception_index; | ||
| 161 | + switch (env->exception_index) { | ||
| 162 | + case 0x000: | ||
| 163 | + case 0x020: | ||
| 164 | + case 0x140: | ||
| 165 | + env->sr &= ~SR_FD; | ||
| 166 | + env->sr |= 0xf << 4; /* IMASK */ | ||
| 167 | + env->pc = 0xa0000000; | ||
| 168 | + break; | ||
| 169 | + case 0x040: | ||
| 170 | + case 0x060: | ||
| 171 | + env->pc = env->vbr + 0x400; | ||
| 172 | + break; | ||
| 173 | + case 0x160: | ||
| 174 | + env->spc += 2; /* special case for TRAPA */ | ||
| 175 | + /* fall through */ | ||
| 176 | + default: | ||
| 177 | + env->pc = env->vbr + 0x100; | ||
| 178 | + break; | ||
| 179 | + } | ||
| 180 | + return; | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + if (do_irq) { | ||
| 184 | + env->intevt = irq_vector; | ||
| 185 | + env->pc = env->vbr + 0x600; | ||
| 186 | + return; | ||
| 146 | } | 187 | } |
| 147 | } | 188 | } |
| 148 | 189 |
target-sh4/op.c
| @@ -419,7 +419,7 @@ void OPPROTO op_subv_T0_T1(void) | @@ -419,7 +419,7 @@ void OPPROTO op_subv_T0_T1(void) | ||
| 419 | 419 | ||
| 420 | void OPPROTO op_trapa(void) | 420 | void OPPROTO op_trapa(void) |
| 421 | { | 421 | { |
| 422 | - env->tra = PARAM1 * 2; | 422 | + env->tra = PARAM1 << 2; |
| 423 | env->exception_index = 0x160; | 423 | env->exception_index = 0x160; |
| 424 | do_raise_exception(); | 424 | do_raise_exception(); |
| 425 | RETURN(); | 425 | RETURN(); |