1 /* interrupts.c -- 68HC11 Interrupts Emulation
2 Copyright 1999, 2000, 2001, 2002, 2003, 2007, 2008, 2009
3 Free Software Foundation, Inc.
4 Written by Stephane Carrez (stcarrez@nerim.fr)
6 This file is part of GDB, GAS, and the GNU binutils.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 #include "sim-options.h"
24 static const char *interrupt_names
[] = {
60 struct interrupt_def idefs
[] = {
61 /* Serial interrupts. */
62 { M6811_INT_SCI
, M6811_SCSR
, M6811_TDRE
, M6811_SCCR2
, M6811_TIE
},
63 { M6811_INT_SCI
, M6811_SCSR
, M6811_TC
, M6811_SCCR2
, M6811_TCIE
},
64 { M6811_INT_SCI
, M6811_SCSR
, M6811_RDRF
, M6811_SCCR2
, M6811_RIE
},
65 { M6811_INT_SCI
, M6811_SCSR
, M6811_IDLE
, M6811_SCCR2
, M6811_ILIE
},
68 { M6811_INT_SPI
, M6811_SPSR
, M6811_SPIF
, M6811_SPCR
, M6811_SPIE
},
70 /* Realtime interrupts. */
71 { M6811_INT_TCTN
, M6811_TFLG2
, M6811_TOF
, M6811_TMSK2
, M6811_TOI
},
72 { M6811_INT_RT
, M6811_TFLG2
, M6811_RTIF
, M6811_TMSK2
, M6811_RTII
},
74 /* Output compare interrupts. */
75 { M6811_INT_OUTCMP1
, M6811_TFLG1
, M6811_OC1F
, M6811_TMSK1
, M6811_OC1I
},
76 { M6811_INT_OUTCMP2
, M6811_TFLG1
, M6811_OC2F
, M6811_TMSK1
, M6811_OC2I
},
77 { M6811_INT_OUTCMP3
, M6811_TFLG1
, M6811_OC3F
, M6811_TMSK1
, M6811_OC3I
},
78 { M6811_INT_OUTCMP4
, M6811_TFLG1
, M6811_OC4F
, M6811_TMSK1
, M6811_OC4I
},
79 { M6811_INT_OUTCMP5
, M6811_TFLG1
, M6811_OC5F
, M6811_TMSK1
, M6811_OC5I
},
81 /* Input compare interrupts. */
82 { M6811_INT_INCMP1
, M6811_TFLG1
, M6811_IC1F
, M6811_TMSK1
, M6811_IC1I
},
83 { M6811_INT_INCMP2
, M6811_TFLG1
, M6811_IC2F
, M6811_TMSK1
, M6811_IC2I
},
84 { M6811_INT_INCMP3
, M6811_TFLG1
, M6811_IC3F
, M6811_TMSK1
, M6811_IC3I
},
86 /* Pulse accumulator. */
87 { M6811_INT_AINPUT
, M6811_TFLG2
, M6811_PAIF
, M6811_TMSK2
, M6811_PAII
},
88 { M6811_INT_AOVERFLOW
,M6811_TFLG2
, M6811_PAOVF
, M6811_TMSK2
, M6811_PAOVI
},
90 { M6811_INT_COPRESET
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 },
91 { M6811_INT_COPFAIL
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 }
95 #define TableSize(X) (sizeof X / sizeof(X[0]))
96 #define CYCLES_MAX ((((signed64) 1) << 62) - 1)
100 OPTION_INTERRUPT_INFO
= OPTION_START
,
101 OPTION_INTERRUPT_CATCH
,
102 OPTION_INTERRUPT_CLEAR
105 static DECLARE_OPTION_HANDLER (interrupt_option_handler
);
107 static const OPTION interrupt_options
[] =
109 { {"interrupt-info", no_argument
, NULL
, OPTION_INTERRUPT_INFO
},
110 '\0', NULL
, "Print information about interrupts",
111 interrupt_option_handler
},
112 { {"interrupt-catch", required_argument
, NULL
, OPTION_INTERRUPT_CATCH
},
114 "Catch interrupts when they are raised or taken\n"
115 "NAME Name of the interrupt\n"
116 "MODE Optional mode (`taken' or `raised')",
117 interrupt_option_handler
},
118 { {"interrupt-clear", required_argument
, NULL
, OPTION_INTERRUPT_CLEAR
},
119 '\0', "NAME", "No longer catch the interrupt",
120 interrupt_option_handler
},
122 { {NULL
, no_argument
, NULL
, 0}, '\0', NULL
, NULL
, NULL
}
125 /* Initialize the interrupts module. */
127 interrupts_initialize (SIM_DESC sd
, struct _sim_cpu
*proc
)
129 struct interrupts
*interrupts
= &proc
->cpu_interrupts
;
131 interrupts
->cpu
= proc
;
133 sim_add_option_table (sd
, 0, interrupt_options
);
136 /* Initialize the interrupts of the processor. */
138 interrupts_reset (struct interrupts
*interrupts
)
142 interrupts
->pending_mask
= 0;
143 if (interrupts
->cpu
->cpu_mode
& M6811_SMOD
)
144 interrupts
->vectors_addr
= 0xbfc0;
146 interrupts
->vectors_addr
= 0xffc0;
147 interrupts
->nb_interrupts_raised
= 0;
148 interrupts
->min_mask_cycles
= CYCLES_MAX
;
149 interrupts
->max_mask_cycles
= 0;
150 interrupts
->last_mask_cycles
= 0;
151 interrupts
->start_mask_cycle
= -1;
152 interrupts
->xirq_start_mask_cycle
= -1;
153 interrupts
->xirq_max_mask_cycles
= 0;
154 interrupts
->xirq_min_mask_cycles
= CYCLES_MAX
;
155 interrupts
->xirq_last_mask_cycles
= 0;
157 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
159 interrupts
->interrupt_order
[i
] = i
;
162 /* Clear the interrupt history table. */
163 interrupts
->history_index
= 0;
164 memset (interrupts
->interrupts_history
, 0,
165 sizeof (interrupts
->interrupts_history
));
167 memset (interrupts
->interrupts
, 0,
168 sizeof (interrupts
->interrupts
));
170 /* In bootstrap mode, initialize the vector table to point
171 to the RAM location. */
172 if (interrupts
->cpu
->cpu_mode
== M6811_SMOD
)
174 bfd_vma addr
= interrupts
->vectors_addr
;
175 uint16 vector
= 0x0100 - 3 * (M6811_INT_NUMBER
- 1);
176 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
178 memory_write16 (interrupts
->cpu
, addr
, vector
);
186 find_interrupt (const char *name
)
191 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
192 if (strcasecmp (name
, interrupt_names
[i
]) == 0)
199 interrupt_option_handler (SIM_DESC sd
, sim_cpu
*cpu
,
200 int opt
, char *arg
, int is_command
)
205 struct interrupts
*interrupts
;
208 cpu
= STATE_CPU (sd
, 0);
210 interrupts
= &cpu
->cpu_interrupts
;
213 case OPTION_INTERRUPT_INFO
:
214 for (id
= 0; id
< M6811_INT_NUMBER
; id
++)
216 sim_io_eprintf (sd
, "%-10.10s ", interrupt_names
[id
]);
217 switch (interrupts
->interrupts
[id
].stop_mode
)
219 case SIM_STOP_WHEN_RAISED
:
220 sim_io_eprintf (sd
, "catch raised ");
223 case SIM_STOP_WHEN_TAKEN
:
224 sim_io_eprintf (sd
, "catch taken ");
227 case SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
:
228 sim_io_eprintf (sd
, "catch all ");
232 sim_io_eprintf (sd
, " ");
235 sim_io_eprintf (sd
, "%ld\n",
236 interrupts
->interrupts
[id
].raised_count
);
240 case OPTION_INTERRUPT_CATCH
:
241 p
= strchr (arg
, ',');
245 mode
= SIM_STOP_WHEN_RAISED
;
246 id
= find_interrupt (arg
);
248 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
250 if (p
&& strcasecmp (p
, "raised") == 0)
251 mode
= SIM_STOP_WHEN_RAISED
;
252 else if (p
&& strcasecmp (p
, "taken") == 0)
253 mode
= SIM_STOP_WHEN_TAKEN
;
254 else if (p
&& strcasecmp (p
, "all") == 0)
255 mode
= SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
;
258 sim_io_eprintf (sd
, "Invalid argument: %s\n", p
);
262 interrupts
->interrupts
[id
].stop_mode
= mode
;
265 case OPTION_INTERRUPT_CLEAR
:
266 mode
= SIM_STOP_WHEN_RAISED
;
267 id
= find_interrupt (arg
);
269 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
271 interrupts
->interrupts
[id
].stop_mode
= 0;
278 /* Update the mask of pending interrupts. This operation must be called
279 when the state of some 68HC11 IO register changes. It looks the
280 different registers that indicate a pending interrupt (timer, SCI, SPI,
281 ...) and records the interrupt if it's there and enabled. */
283 interrupts_update_pending (struct interrupts
*interrupts
)
287 unsigned long clear_mask
;
288 unsigned long set_mask
;
292 ioregs
= &interrupts
->cpu
->ios
[0];
294 for (i
= 0; i
< TableSize(idefs
); i
++)
296 struct interrupt_def
*idef
= &idefs
[i
];
299 /* Look if the interrupt is enabled. */
300 if (idef
->enable_paddr
)
302 data
= ioregs
[idef
->enable_paddr
];
303 if (!(data
& idef
->enabled_mask
))
306 clear_mask
|= (1 << idef
->int_number
);
311 /* Interrupt is enabled, see if it's there. */
312 data
= ioregs
[idef
->int_paddr
];
313 if (!(data
& idef
->int_mask
))
316 clear_mask
|= (1 << idef
->int_number
);
321 set_mask
|= (1 << idef
->int_number
);
324 /* Some interrupts are shared (M6811_INT_SCI) so clear
325 the interrupts before setting the new ones. */
326 interrupts
->pending_mask
&= ~clear_mask
;
327 interrupts
->pending_mask
|= set_mask
;
329 /* Keep track of when the interrupt is raised by the device.
330 Also implements the breakpoint-on-interrupt. */
333 signed64 cycle
= cpu_current_cycle (interrupts
->cpu
);
336 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
338 if (!(set_mask
& (1 << i
)))
341 interrupts
->interrupts
[i
].cpu_cycle
= cycle
;
342 if (interrupts
->interrupts
[i
].stop_mode
& SIM_STOP_WHEN_RAISED
)
345 sim_io_printf (CPU_STATE (interrupts
->cpu
),
346 "Interrupt %s raised\n",
351 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
353 0, cpu_get_pc (interrupts
->cpu
),
360 /* Finds the current active and non-masked interrupt.
361 Returns the interrupt number (index in the vector table) or -1
362 if no interrupt can be serviced. */
364 interrupts_get_current (struct interrupts
*interrupts
)
368 if (interrupts
->pending_mask
== 0)
371 /* SWI and illegal instructions are simulated by an interrupt.
372 They are not maskable. */
373 if (interrupts
->pending_mask
& (1 << M6811_INT_SWI
))
375 interrupts
->pending_mask
&= ~(1 << M6811_INT_SWI
);
376 return M6811_INT_SWI
;
378 if (interrupts
->pending_mask
& (1 << M6811_INT_ILLEGAL
))
380 interrupts
->pending_mask
&= ~(1 << M6811_INT_ILLEGAL
);
381 return M6811_INT_ILLEGAL
;
384 /* If there is a non maskable interrupt, go for it (unless we are masked
386 if (interrupts
->pending_mask
& (1 << M6811_INT_XIRQ
))
388 if (cpu_get_ccr_X (interrupts
->cpu
) == 0)
390 interrupts
->pending_mask
&= ~(1 << M6811_INT_XIRQ
);
391 return M6811_INT_XIRQ
;
396 /* Interrupts are masked, do nothing. */
397 if (cpu_get_ccr_I (interrupts
->cpu
) == 1)
402 /* Returns the first interrupt number which is pending.
403 The interrupt priority is specified by the table `interrupt_order'.
404 For these interrupts, the pending mask is cleared when the program
405 performs some actions on the corresponding device. If the device
406 is not reset, the interrupt remains and will be re-raised when
407 we return from the interrupt (see 68HC11 pink book). */
408 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
410 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
412 if (interrupts
->pending_mask
& (1 << int_number
))
421 /* Process the current interrupt if there is one. This operation must
422 be called after each instruction to handle the interrupts. If interrupts
423 are masked, it does nothing. */
425 interrupts_process (struct interrupts
*interrupts
)
430 /* See if interrupts are enabled/disabled and keep track of the
431 number of cycles the interrupts are masked. Such information is
432 then reported by the info command. */
433 ccr
= cpu_get_ccr (interrupts
->cpu
);
434 if (ccr
& M6811_I_BIT
)
436 if (interrupts
->start_mask_cycle
< 0)
437 interrupts
->start_mask_cycle
= cpu_current_cycle (interrupts
->cpu
);
439 else if (interrupts
->start_mask_cycle
>= 0
440 && (ccr
& M6811_I_BIT
) == 0)
442 signed64 t
= cpu_current_cycle (interrupts
->cpu
);
444 t
-= interrupts
->start_mask_cycle
;
445 if (t
< interrupts
->min_mask_cycles
)
446 interrupts
->min_mask_cycles
= t
;
447 if (t
> interrupts
->max_mask_cycles
)
448 interrupts
->max_mask_cycles
= t
;
449 interrupts
->start_mask_cycle
= -1;
450 interrupts
->last_mask_cycles
= t
;
452 if (ccr
& M6811_X_BIT
)
454 if (interrupts
->xirq_start_mask_cycle
< 0)
455 interrupts
->xirq_start_mask_cycle
456 = cpu_current_cycle (interrupts
->cpu
);
458 else if (interrupts
->xirq_start_mask_cycle
>= 0
459 && (ccr
& M6811_X_BIT
) == 0)
461 signed64 t
= cpu_current_cycle (interrupts
->cpu
);
463 t
-= interrupts
->xirq_start_mask_cycle
;
464 if (t
< interrupts
->xirq_min_mask_cycles
)
465 interrupts
->xirq_min_mask_cycles
= t
;
466 if (t
> interrupts
->xirq_max_mask_cycles
)
467 interrupts
->xirq_max_mask_cycles
= t
;
468 interrupts
->xirq_start_mask_cycle
= -1;
469 interrupts
->xirq_last_mask_cycles
= t
;
472 id
= interrupts_get_current (interrupts
);
476 struct interrupt_history
*h
;
478 /* Implement the breakpoint-on-interrupt. */
479 if (interrupts
->interrupts
[id
].stop_mode
& SIM_STOP_WHEN_TAKEN
)
481 sim_io_printf (CPU_STATE (interrupts
->cpu
),
482 "Interrupt %s will be handled\n",
483 interrupt_names
[id
]);
484 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
486 0, cpu_get_pc (interrupts
->cpu
),
491 cpu_push_all (interrupts
->cpu
);
492 addr
= memory_read16 (interrupts
->cpu
,
493 interrupts
->vectors_addr
+ id
* 2);
494 cpu_call (interrupts
->cpu
, addr
);
496 /* Now, protect from nested interrupts. */
497 if (id
== M6811_INT_XIRQ
)
499 cpu_set_ccr_X (interrupts
->cpu
, 1);
503 cpu_set_ccr_I (interrupts
->cpu
, 1);
506 /* Update the interrupt history table. */
507 h
= &interrupts
->interrupts_history
[interrupts
->history_index
];
509 h
->taken_cycle
= cpu_current_cycle (interrupts
->cpu
);
510 h
->raised_cycle
= interrupts
->interrupts
[id
].cpu_cycle
;
512 if (interrupts
->history_index
>= MAX_INT_HISTORY
-1)
513 interrupts
->history_index
= 0;
515 interrupts
->history_index
++;
517 interrupts
->nb_interrupts_raised
++;
518 cpu_add_cycles (interrupts
->cpu
, 14);
525 interrupts_raise (struct interrupts
*interrupts
, enum M6811_INT number
)
527 interrupts
->pending_mask
|= (1 << number
);
528 interrupts
->nb_interrupts_raised
++;
532 interrupts_info (SIM_DESC sd
, struct interrupts
*interrupts
)
534 signed64 t
, prev_interrupt
;
537 sim_io_printf (sd
, "Interrupts Info:\n");
538 sim_io_printf (sd
, " Interrupts raised: %lu\n",
539 interrupts
->nb_interrupts_raised
);
541 if (interrupts
->start_mask_cycle
>= 0)
543 t
= cpu_current_cycle (interrupts
->cpu
);
545 t
-= interrupts
->start_mask_cycle
;
546 if (t
> interrupts
->max_mask_cycles
)
547 interrupts
->max_mask_cycles
= t
;
549 sim_io_printf (sd
, " Current interrupts masked sequence: %s\n",
550 cycle_to_string (interrupts
->cpu
, t
,
551 PRINT_TIME
| PRINT_CYCLE
));
553 t
= interrupts
->min_mask_cycles
== CYCLES_MAX
?
554 interrupts
->max_mask_cycles
:
555 interrupts
->min_mask_cycles
;
556 sim_io_printf (sd
, " Shortest interrupts masked sequence: %s\n",
557 cycle_to_string (interrupts
->cpu
, t
,
558 PRINT_TIME
| PRINT_CYCLE
));
560 t
= interrupts
->max_mask_cycles
;
561 sim_io_printf (sd
, " Longest interrupts masked sequence: %s\n",
562 cycle_to_string (interrupts
->cpu
, t
,
563 PRINT_TIME
| PRINT_CYCLE
));
565 t
= interrupts
->last_mask_cycles
;
566 sim_io_printf (sd
, " Last interrupts masked sequence: %s\n",
567 cycle_to_string (interrupts
->cpu
, t
,
568 PRINT_TIME
| PRINT_CYCLE
));
570 if (interrupts
->xirq_start_mask_cycle
>= 0)
572 t
= cpu_current_cycle (interrupts
->cpu
);
574 t
-= interrupts
->xirq_start_mask_cycle
;
575 if (t
> interrupts
->xirq_max_mask_cycles
)
576 interrupts
->xirq_max_mask_cycles
= t
;
578 sim_io_printf (sd
, " XIRQ Current interrupts masked sequence: %s\n",
579 cycle_to_string (interrupts
->cpu
, t
,
580 PRINT_TIME
| PRINT_CYCLE
));
583 t
= interrupts
->xirq_min_mask_cycles
== CYCLES_MAX
?
584 interrupts
->xirq_max_mask_cycles
:
585 interrupts
->xirq_min_mask_cycles
;
586 sim_io_printf (sd
, " XIRQ Min interrupts masked sequence: %s\n",
587 cycle_to_string (interrupts
->cpu
, t
,
588 PRINT_TIME
| PRINT_CYCLE
));
590 t
= interrupts
->xirq_max_mask_cycles
;
591 sim_io_printf (sd
, " XIRQ Max interrupts masked sequence: %s\n",
592 cycle_to_string (interrupts
->cpu
, t
,
593 PRINT_TIME
| PRINT_CYCLE
));
595 t
= interrupts
->xirq_last_mask_cycles
;
596 sim_io_printf (sd
, " XIRQ Last interrupts masked sequence: %s\n",
597 cycle_to_string (interrupts
->cpu
, t
,
598 PRINT_TIME
| PRINT_CYCLE
));
600 if (interrupts
->pending_mask
)
602 sim_io_printf (sd
, " Pending interrupts : ");
603 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
605 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
607 if (interrupts
->pending_mask
& (1 << int_number
))
609 sim_io_printf (sd
, "%s ", interrupt_names
[int_number
]);
612 sim_io_printf (sd
, "\n");
616 sim_io_printf (sd
, "N Interrupt Cycle Taken Latency"
617 " Delta between interrupts\n");
618 for (i
= 0; i
< MAX_INT_HISTORY
; i
++)
621 struct interrupt_history
*h
;
624 which
= interrupts
->history_index
- i
- 1;
626 which
+= MAX_INT_HISTORY
;
627 h
= &interrupts
->interrupts_history
[which
];
628 if (h
->taken_cycle
== 0)
631 dt
= h
->taken_cycle
- h
->raised_cycle
;
632 sim_io_printf (sd
, "%2d %-9.9s %15.15s ", i
,
633 interrupt_names
[h
->type
],
634 cycle_to_string (interrupts
->cpu
, h
->taken_cycle
, 0));
635 sim_io_printf (sd
, "%15.15s",
636 cycle_to_string (interrupts
->cpu
, dt
, 0));
639 dt
= prev_interrupt
- h
->taken_cycle
;
640 sim_io_printf (sd
, " %s",
641 cycle_to_string (interrupts
->cpu
, dt
, PRINT_TIME
));
643 sim_io_printf (sd
, "\n");
644 prev_interrupt
= h
->taken_cycle
;
This page took 0.042948 seconds and 4 git commands to generate.