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,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();