Commit e96e2044a14340bf1e612b7f046093495c10a06f

Authored by ths
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
cpu-exec.c
... ... @@ -511,7 +511,10 @@ int cpu_exec(CPUState *env1)
511 511 BREAK_CHAIN;
512 512 }
513 513 #elif defined(TARGET_SH4)
514   - /* XXXXX */
  514 + if (interrupt_request & CPU_INTERRUPT_HARD) {
  515 + do_interrupt(env);
  516 + BREAK_CHAIN;
  517 + }
515 518 #elif defined(TARGET_ALPHA)
516 519 if (interrupt_request & CPU_INTERRUPT_HARD) {
517 520 do_interrupt(env);
... ...
hw/sh7750.c
... ... @@ -551,6 +551,8 @@ SH7750State *sh7750_init(CPUSH4State * cpu)
551 551 _INTC_ARRAY(vectors),
552 552 _INTC_ARRAY(groups));
553 553  
  554 + cpu->intc_handle = &s->intc;
  555 +
554 556 sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0]);
555 557 sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF,
556 558 s->periph_freq, serial_hds[1]);
... ...
hw/sh_intc.c
... ... @@ -14,10 +14,91 @@
14 14 #include "sh.h"
15 15  
16 16 //#define DEBUG_INTC
  17 +//#define DEBUG_INTC_SOURCES
17 18  
18 19 #define INTC_A7(x) ((x) & 0x1fffffff)
19 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 102 #define INTC_MODE_NONE 0
22 103 #define INTC_MODE_DUAL_SET 1
23 104 #define INTC_MODE_DUAL_CLR 2
... ... @@ -94,42 +175,24 @@ static void sh_intc_locate(struct intc_desc *desc,
94 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 181 struct intc_source *source = desc->sources + id;
101   - int old = source->enable_count;
102 182  
103 183 if (!id)
104 184 return;
105 185  
106 186 if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
107   -#ifdef DEBUG_INTC
  187 +#ifdef DEBUG_INTC_SOURCES
108 188 printf("sh_intc: reserved interrupt source %d modified\n", id);
109 189 #endif
110 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 196 #ifdef DEBUG_INTC
134 197 else {
135 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 200 #endif
138 201  
139 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 206 #ifdef DEBUG_INTC
... ... @@ -200,7 +263,7 @@ static void sh_intc_write(void *opaque, target_phys_addr_t offset,
200 263 printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
201 264 k, first, enum_ids[k], (unsigned int)mask);
202 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 269 *valuep = value;
... ... @@ -309,7 +372,7 @@ void sh_intc_register_sources(struct intc_desc *desc,
309 372 if (s)
310 373 s->vect = vect->vect;
311 374  
312   -#ifdef DEBUG_INTC
  375 +#ifdef DEBUG_INTC_SOURCES
313 376 printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
314 377 vect->enum_id, s->vect, s->enable_count, s->enable_max);
315 378 #endif
... ... @@ -330,7 +393,7 @@ void sh_intc_register_sources(struct intc_desc *desc,
330 393 s->next_enum_id = gr->enum_ids[k];
331 394 }
332 395  
333   -#ifdef DEBUG_INTC
  396 +#ifdef DEBUG_INTC_SOURCES
334 397 printf("sh_intc: registered group %d (%d/%d)\n",
335 398 gr->enum_id, s->enable_count, s->enable_max);
336 399 #endif
... ... @@ -347,6 +410,7 @@ int sh_intc_init(struct intc_desc *desc,
347 410 {
348 411 unsigned int i;
349 412  
  413 + desc->pending = 0;
350 414 desc->nr_sources = nr_sources;
351 415 desc->mask_regs = mask_regs;
352 416 desc->nr_mask_regs = nr_mask_regs;
... ... @@ -359,6 +423,11 @@ int sh_intc_init(struct intc_desc *desc,
359 423 return -1;
360 424  
361 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 432 desc->iomemtype = cpu_register_io_memory(0, sh_intc_readfn,
364 433 sh_intc_writefn, desc);
... ...
hw/sh_intc.h
... ... @@ -35,9 +35,11 @@ struct intc_source {
35 35 unsigned short vect;
36 36 intc_enum next_enum_id;
37 37  
38   - int asserted;
  38 + int asserted; /* emulates the interrupt signal line from device to intc */
39 39 int enable_count;
40 40 int enable_max;
  41 + int pending; /* emulates the result of signal and masking */
  42 + struct intc_desc *parent;
41 43 };
42 44  
43 45 struct intc_desc {
... ... @@ -49,9 +51,13 @@ struct intc_desc {
49 51 int nr_prio_regs;
50 52  
51 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 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 62 void sh_intc_register_sources(struct intc_desc *desc,
57 63 struct intc_vect *vectors,
... ...
target-sh4/cpu.h
... ... @@ -121,6 +121,7 @@ typedef struct CPUSH4State {
121 121 int exception_index;
122 122 CPU_COMMON tlb_t utlb[UTLB_SIZE]; /* unified translation table */
123 123 tlb_t itlb[ITLB_SIZE]; /* instruction translation table */
  124 + void *intc_handle;
124 125 } CPUSH4State;
125 126  
126 127 CPUSH4State *cpu_sh4_init(const char *cpu_model);
... ...
target-sh4/helper.c
... ... @@ -27,6 +27,7 @@
27 27  
28 28 #include "cpu.h"
29 29 #include "exec-all.h"
  30 +#include "hw/sh_intc.h"
30 31  
31 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 75  
75 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 103 if (loglevel & CPU_LOG_INT) {
78 104 const char *expname;
79 105 switch (env->exception_index) {
... ... @@ -117,32 +143,47 @@ void do_interrupt(CPUState * env)
117 143 expname = "trapa";
118 144 break;
119 145 default:
120   - expname = "???";
121   - break;
  146 + expname = do_irq ? "interrupt" : "???";
  147 + break;
122 148 }
123 149 fprintf(logfile, "exception 0x%03x [%s] raised\n",
124   - env->exception_index, expname);
  150 + irq_vector, expname);
125 151 cpu_dump_state(env, logfile, fprintf, 0);
126 152 }
127 153  
128 154 env->ssr = env->sr;
129   - env->spc = env->spc;
  155 + env->spc = env->pc;
130 156 env->sgr = env->gregs[15];
131 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 419  
420 420 void OPPROTO op_trapa(void)
421 421 {
422   - env->tra = PARAM1 * 2;
  422 + env->tra = PARAM1 << 2;
423 423 env->exception_index = 0x160;
424 424 do_raise_exception();
425 425 RETURN();
... ...