1 /* interrupts.c -- 68HC11 Interrupts Emulation
2 Copyright 1999-2016 Free Software Foundation, Inc.
3 Written by Stephane Carrez (stcarrez@nerim.fr)
5 This file is part of GDB, GAS, and the GNU binutils.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "sim-options.h"
23 static const char *interrupt_names
[] = {
59 struct interrupt_def idefs
[] = {
60 /* Serial interrupts. */
61 { M6811_INT_SCI
, M6811_SCSR
, M6811_TDRE
, M6811_SCCR2
, M6811_TIE
},
62 { M6811_INT_SCI
, M6811_SCSR
, M6811_TC
, M6811_SCCR2
, M6811_TCIE
},
63 { M6811_INT_SCI
, M6811_SCSR
, M6811_RDRF
, M6811_SCCR2
, M6811_RIE
},
64 { M6811_INT_SCI
, M6811_SCSR
, M6811_IDLE
, M6811_SCCR2
, M6811_ILIE
},
67 { M6811_INT_SPI
, M6811_SPSR
, M6811_SPIF
, M6811_SPCR
, M6811_SPIE
},
69 /* Realtime interrupts. */
70 { M6811_INT_TCTN
, M6811_TFLG2
, M6811_TOF
, M6811_TMSK2
, M6811_TOI
},
71 { M6811_INT_RT
, M6811_TFLG2
, M6811_RTIF
, M6811_TMSK2
, M6811_RTII
},
73 /* Output compare interrupts. */
74 { M6811_INT_OUTCMP1
, M6811_TFLG1
, M6811_OC1F
, M6811_TMSK1
, M6811_OC1I
},
75 { M6811_INT_OUTCMP2
, M6811_TFLG1
, M6811_OC2F
, M6811_TMSK1
, M6811_OC2I
},
76 { M6811_INT_OUTCMP3
, M6811_TFLG1
, M6811_OC3F
, M6811_TMSK1
, M6811_OC3I
},
77 { M6811_INT_OUTCMP4
, M6811_TFLG1
, M6811_OC4F
, M6811_TMSK1
, M6811_OC4I
},
78 { M6811_INT_OUTCMP5
, M6811_TFLG1
, M6811_OC5F
, M6811_TMSK1
, M6811_OC5I
},
80 /* Input compare interrupts. */
81 { M6811_INT_INCMP1
, M6811_TFLG1
, M6811_IC1F
, M6811_TMSK1
, M6811_IC1I
},
82 { M6811_INT_INCMP2
, M6811_TFLG1
, M6811_IC2F
, M6811_TMSK1
, M6811_IC2I
},
83 { M6811_INT_INCMP3
, M6811_TFLG1
, M6811_IC3F
, M6811_TMSK1
, M6811_IC3I
},
85 /* Pulse accumulator. */
86 { M6811_INT_AINPUT
, M6811_TFLG2
, M6811_PAIF
, M6811_TMSK2
, M6811_PAII
},
87 { M6811_INT_AOVERFLOW
,M6811_TFLG2
, M6811_PAOVF
, M6811_TMSK2
, M6811_PAOVI
},
89 { M6811_INT_COPRESET
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 },
90 { M6811_INT_COPFAIL
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 }
94 #define TableSize(X) (sizeof X / sizeof(X[0]))
95 #define CYCLES_MAX ((((signed64) 1) << 62) - 1)
99 OPTION_INTERRUPT_INFO
= OPTION_START
,
100 OPTION_INTERRUPT_CATCH
,
101 OPTION_INTERRUPT_CLEAR
104 static DECLARE_OPTION_HANDLER (interrupt_option_handler
);
106 static const OPTION interrupt_options
[] =
108 { {"interrupt-info", no_argument
, NULL
, OPTION_INTERRUPT_INFO
},
109 '\0', NULL
, "Print information about interrupts",
110 interrupt_option_handler
},
111 { {"interrupt-catch", required_argument
, NULL
, OPTION_INTERRUPT_CATCH
},
113 "Catch interrupts when they are raised or taken\n"
114 "NAME Name of the interrupt\n"
115 "MODE Optional mode (`taken' or `raised')",
116 interrupt_option_handler
},
117 { {"interrupt-clear", required_argument
, NULL
, OPTION_INTERRUPT_CLEAR
},
118 '\0', "NAME", "No longer catch the interrupt",
119 interrupt_option_handler
},
121 { {NULL
, no_argument
, NULL
, 0}, '\0', NULL
, NULL
, NULL
}
124 /* Initialize the interrupts module. */
126 interrupts_initialize (SIM_DESC sd
, struct _sim_cpu
*proc
)
128 struct interrupts
*interrupts
= &proc
->cpu_interrupts
;
130 interrupts
->cpu
= proc
;
132 sim_add_option_table (sd
, 0, interrupt_options
);
135 /* Initialize the interrupts of the processor. */
137 interrupts_reset (struct interrupts
*interrupts
)
141 interrupts
->pending_mask
= 0;
142 if (interrupts
->cpu
->cpu_mode
& M6811_SMOD
)
143 interrupts
->vectors_addr
= 0xbfc0;
145 interrupts
->vectors_addr
= 0xffc0;
146 interrupts
->nb_interrupts_raised
= 0;
147 interrupts
->min_mask_cycles
= CYCLES_MAX
;
148 interrupts
->max_mask_cycles
= 0;
149 interrupts
->last_mask_cycles
= 0;
150 interrupts
->start_mask_cycle
= -1;
151 interrupts
->xirq_start_mask_cycle
= -1;
152 interrupts
->xirq_max_mask_cycles
= 0;
153 interrupts
->xirq_min_mask_cycles
= CYCLES_MAX
;
154 interrupts
->xirq_last_mask_cycles
= 0;
156 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
158 interrupts
->interrupt_order
[i
] = i
;
161 /* Clear the interrupt history table. */
162 interrupts
->history_index
= 0;
163 memset (interrupts
->interrupts_history
, 0,
164 sizeof (interrupts
->interrupts_history
));
166 memset (interrupts
->interrupts
, 0,
167 sizeof (interrupts
->interrupts
));
169 /* In bootstrap mode, initialize the vector table to point
170 to the RAM location. */
171 if (interrupts
->cpu
->cpu_mode
== M6811_SMOD
)
173 bfd_vma addr
= interrupts
->vectors_addr
;
174 uint16 vector
= 0x0100 - 3 * (M6811_INT_NUMBER
- 1);
175 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
177 memory_write16 (interrupts
->cpu
, addr
, vector
);
185 find_interrupt (const char *name
)
190 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
191 if (strcasecmp (name
, interrupt_names
[i
]) == 0)
198 interrupt_option_handler (SIM_DESC sd
, sim_cpu
*cpu
,
199 int opt
, char *arg
, int is_command
)
204 struct interrupts
*interrupts
;
207 cpu
= STATE_CPU (sd
, 0);
209 interrupts
= &cpu
->cpu_interrupts
;
212 case OPTION_INTERRUPT_INFO
:
213 for (id
= 0; id
< M6811_INT_NUMBER
; id
++)
215 sim_io_eprintf (sd
, "%-10.10s ", interrupt_names
[id
]);
216 switch (interrupts
->interrupts
[id
].stop_mode
)
218 case SIM_STOP_WHEN_RAISED
:
219 sim_io_eprintf (sd
, "catch raised ");
222 case SIM_STOP_WHEN_TAKEN
:
223 sim_io_eprintf (sd
, "catch taken ");
226 case SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
:
227 sim_io_eprintf (sd
, "catch all ");
231 sim_io_eprintf (sd
, " ");
234 sim_io_eprintf (sd
, "%ld\n",
235 interrupts
->interrupts
[id
].raised_count
);
239 case OPTION_INTERRUPT_CATCH
:
240 p
= strchr (arg
, ',');
244 mode
= SIM_STOP_WHEN_RAISED
;
245 id
= find_interrupt (arg
);
247 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
249 if (p
&& strcasecmp (p
, "raised") == 0)
250 mode
= SIM_STOP_WHEN_RAISED
;
251 else if (p
&& strcasecmp (p
, "taken") == 0)
252 mode
= SIM_STOP_WHEN_TAKEN
;
253 else if (p
&& strcasecmp (p
, "all") == 0)
254 mode
= SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
;
257 sim_io_eprintf (sd
, "Invalid argument: %s\n", p
);
261 interrupts
->interrupts
[id
].stop_mode
= mode
;
264 case OPTION_INTERRUPT_CLEAR
:
265 mode
= SIM_STOP_WHEN_RAISED
;
266 id
= find_interrupt (arg
);
268 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
270 interrupts
->interrupts
[id
].stop_mode
= 0;
277 /* Update the mask of pending interrupts. This operation must be called
278 when the state of some 68HC11 IO register changes. It looks the
279 different registers that indicate a pending interrupt (timer, SCI, SPI,
280 ...) and records the interrupt if it's there and enabled. */
282 interrupts_update_pending (struct interrupts
*interrupts
)
286 unsigned long clear_mask
;
287 unsigned long set_mask
;
291 ioregs
= &interrupts
->cpu
->ios
[0];
293 for (i
= 0; i
< TableSize(idefs
); i
++)
295 struct interrupt_def
*idef
= &idefs
[i
];
298 /* Look if the interrupt is enabled. */
299 if (idef
->enable_paddr
)
301 data
= ioregs
[idef
->enable_paddr
];
302 if (!(data
& idef
->enabled_mask
))
305 clear_mask
|= (1 << idef
->int_number
);
310 /* Interrupt is enabled, see if it's there. */
311 data
= ioregs
[idef
->int_paddr
];
312 if (!(data
& idef
->int_mask
))
315 clear_mask
|= (1 << idef
->int_number
);
320 set_mask
|= (1 << idef
->int_number
);
323 /* Some interrupts are shared (M6811_INT_SCI) so clear
324 the interrupts before setting the new ones. */
325 interrupts
->pending_mask
&= ~clear_mask
;
326 interrupts
->pending_mask
|= set_mask
;
328 /* Keep track of when the interrupt is raised by the device.
329 Also implements the breakpoint-on-interrupt. */
332 signed64 cycle
= cpu_current_cycle (interrupts
->cpu
);
335 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
337 if (!(set_mask
& (1 << i
)))
340 interrupts
->interrupts
[i
].cpu_cycle
= cycle
;
341 if (interrupts
->interrupts
[i
].stop_mode
& SIM_STOP_WHEN_RAISED
)
344 sim_io_printf (CPU_STATE (interrupts
->cpu
),
345 "Interrupt %s raised\n",
350 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
352 0, cpu_get_pc (interrupts
->cpu
),
359 /* Finds the current active and non-masked interrupt.
360 Returns the interrupt number (index in the vector table) or -1
361 if no interrupt can be serviced. */
363 interrupts_get_current (struct interrupts
*interrupts
)
367 if (interrupts
->pending_mask
== 0)
370 /* SWI and illegal instructions are simulated by an interrupt.
371 They are not maskable. */
372 if (interrupts
->pending_mask
& (1 << M6811_INT_SWI
))
374 interrupts
->pending_mask
&= ~(1 << M6811_INT_SWI
);
375 return M6811_INT_SWI
;
377 if (interrupts
->pending_mask
& (1 << M6811_INT_ILLEGAL
))
379 interrupts
->pending_mask
&= ~(1 << M6811_INT_ILLEGAL
);
380 return M6811_INT_ILLEGAL
;
383 /* If there is a non maskable interrupt, go for it (unless we are masked
385 if (interrupts
->pending_mask
& (1 << M6811_INT_XIRQ
))
387 if (cpu_get_ccr_X (interrupts
->cpu
) == 0)
389 interrupts
->pending_mask
&= ~(1 << M6811_INT_XIRQ
);
390 return M6811_INT_XIRQ
;
395 /* Interrupts are masked, do nothing. */
396 if (cpu_get_ccr_I (interrupts
->cpu
) == 1)
401 /* Returns the first interrupt number which is pending.
402 The interrupt priority is specified by the table `interrupt_order'.
403 For these interrupts, the pending mask is cleared when the program
404 performs some actions on the corresponding device. If the device
405 is not reset, the interrupt remains and will be re-raised when
406 we return from the interrupt (see 68HC11 pink book). */
407 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
409 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
411 if (interrupts
->pending_mask
& (1 << int_number
))
420 /* Process the current interrupt if there is one. This operation must
421 be called after each instruction to handle the interrupts. If interrupts
422 are masked, it does nothing. */
424 interrupts_process (struct interrupts
*interrupts
)
429 /* See if interrupts are enabled/disabled and keep track of the
430 number of cycles the interrupts are masked. Such information is
431 then reported by the info command. */
432 ccr
= cpu_get_ccr (interrupts
->cpu
);
433 if (ccr
& M6811_I_BIT
)
435 if (interrupts
->start_mask_cycle
< 0)
436 interrupts
->start_mask_cycle
= cpu_current_cycle (interrupts
->cpu
);
438 else if (interrupts
->start_mask_cycle
>= 0
439 && (ccr
& M6811_I_BIT
) == 0)
441 signed64 t
= cpu_current_cycle (interrupts
->cpu
);
443 t
-= interrupts
->start_mask_cycle
;
444 if (t
< interrupts
->min_mask_cycles
)
445 interrupts
->min_mask_cycles
= t
;
446 if (t
> interrupts
->max_mask_cycles
)
447 interrupts
->max_mask_cycles
= t
;
448 interrupts
->start_mask_cycle
= -1;
449 interrupts
->last_mask_cycles
= t
;
451 if (ccr
& M6811_X_BIT
)
453 if (interrupts
->xirq_start_mask_cycle
< 0)
454 interrupts
->xirq_start_mask_cycle
455 = cpu_current_cycle (interrupts
->cpu
);
457 else if (interrupts
->xirq_start_mask_cycle
>= 0
458 && (ccr
& M6811_X_BIT
) == 0)
460 signed64 t
= cpu_current_cycle (interrupts
->cpu
);
462 t
-= interrupts
->xirq_start_mask_cycle
;
463 if (t
< interrupts
->xirq_min_mask_cycles
)
464 interrupts
->xirq_min_mask_cycles
= t
;
465 if (t
> interrupts
->xirq_max_mask_cycles
)
466 interrupts
->xirq_max_mask_cycles
= t
;
467 interrupts
->xirq_start_mask_cycle
= -1;
468 interrupts
->xirq_last_mask_cycles
= t
;
471 id
= interrupts_get_current (interrupts
);
475 struct interrupt_history
*h
;
477 /* Implement the breakpoint-on-interrupt. */
478 if (interrupts
->interrupts
[id
].stop_mode
& SIM_STOP_WHEN_TAKEN
)
480 sim_io_printf (CPU_STATE (interrupts
->cpu
),
481 "Interrupt %s will be handled\n",
482 interrupt_names
[id
]);
483 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
485 0, cpu_get_pc (interrupts
->cpu
),
490 cpu_push_all (interrupts
->cpu
);
491 addr
= memory_read16 (interrupts
->cpu
,
492 interrupts
->vectors_addr
+ id
* 2);
493 cpu_call (interrupts
->cpu
, addr
);
495 /* Now, protect from nested interrupts. */
496 if (id
== M6811_INT_XIRQ
)
498 cpu_set_ccr_X (interrupts
->cpu
, 1);
502 cpu_set_ccr_I (interrupts
->cpu
, 1);
505 /* Update the interrupt history table. */
506 h
= &interrupts
->interrupts_history
[interrupts
->history_index
];
508 h
->taken_cycle
= cpu_current_cycle (interrupts
->cpu
);
509 h
->raised_cycle
= interrupts
->interrupts
[id
].cpu_cycle
;
511 if (interrupts
->history_index
>= MAX_INT_HISTORY
-1)
512 interrupts
->history_index
= 0;
514 interrupts
->history_index
++;
516 interrupts
->nb_interrupts_raised
++;
517 cpu_add_cycles (interrupts
->cpu
, 14);
524 interrupts_raise (struct interrupts
*interrupts
, enum M6811_INT number
)
526 interrupts
->pending_mask
|= (1 << number
);
527 interrupts
->nb_interrupts_raised
++;
531 interrupts_info (SIM_DESC sd
, struct interrupts
*interrupts
)
533 signed64 t
, prev_interrupt
;
536 sim_io_printf (sd
, "Interrupts Info:\n");
537 sim_io_printf (sd
, " Interrupts raised: %lu\n",
538 interrupts
->nb_interrupts_raised
);
540 if (interrupts
->start_mask_cycle
>= 0)
542 t
= cpu_current_cycle (interrupts
->cpu
);
544 t
-= interrupts
->start_mask_cycle
;
545 if (t
> interrupts
->max_mask_cycles
)
546 interrupts
->max_mask_cycles
= t
;
548 sim_io_printf (sd
, " Current interrupts masked sequence: %s\n",
549 cycle_to_string (interrupts
->cpu
, t
,
550 PRINT_TIME
| PRINT_CYCLE
));
552 t
= interrupts
->min_mask_cycles
== CYCLES_MAX
?
553 interrupts
->max_mask_cycles
:
554 interrupts
->min_mask_cycles
;
555 sim_io_printf (sd
, " Shortest interrupts masked sequence: %s\n",
556 cycle_to_string (interrupts
->cpu
, t
,
557 PRINT_TIME
| PRINT_CYCLE
));
559 t
= interrupts
->max_mask_cycles
;
560 sim_io_printf (sd
, " Longest interrupts masked sequence: %s\n",
561 cycle_to_string (interrupts
->cpu
, t
,
562 PRINT_TIME
| PRINT_CYCLE
));
564 t
= interrupts
->last_mask_cycles
;
565 sim_io_printf (sd
, " Last interrupts masked sequence: %s\n",
566 cycle_to_string (interrupts
->cpu
, t
,
567 PRINT_TIME
| PRINT_CYCLE
));
569 if (interrupts
->xirq_start_mask_cycle
>= 0)
571 t
= cpu_current_cycle (interrupts
->cpu
);
573 t
-= interrupts
->xirq_start_mask_cycle
;
574 if (t
> interrupts
->xirq_max_mask_cycles
)
575 interrupts
->xirq_max_mask_cycles
= t
;
577 sim_io_printf (sd
, " XIRQ Current interrupts masked sequence: %s\n",
578 cycle_to_string (interrupts
->cpu
, t
,
579 PRINT_TIME
| PRINT_CYCLE
));
582 t
= interrupts
->xirq_min_mask_cycles
== CYCLES_MAX
?
583 interrupts
->xirq_max_mask_cycles
:
584 interrupts
->xirq_min_mask_cycles
;
585 sim_io_printf (sd
, " XIRQ Min interrupts masked sequence: %s\n",
586 cycle_to_string (interrupts
->cpu
, t
,
587 PRINT_TIME
| PRINT_CYCLE
));
589 t
= interrupts
->xirq_max_mask_cycles
;
590 sim_io_printf (sd
, " XIRQ Max interrupts masked sequence: %s\n",
591 cycle_to_string (interrupts
->cpu
, t
,
592 PRINT_TIME
| PRINT_CYCLE
));
594 t
= interrupts
->xirq_last_mask_cycles
;
595 sim_io_printf (sd
, " XIRQ Last interrupts masked sequence: %s\n",
596 cycle_to_string (interrupts
->cpu
, t
,
597 PRINT_TIME
| PRINT_CYCLE
));
599 if (interrupts
->pending_mask
)
601 sim_io_printf (sd
, " Pending interrupts : ");
602 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
604 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
606 if (interrupts
->pending_mask
& (1 << int_number
))
608 sim_io_printf (sd
, "%s ", interrupt_names
[int_number
]);
611 sim_io_printf (sd
, "\n");
615 sim_io_printf (sd
, "N Interrupt Cycle Taken Latency"
616 " Delta between interrupts\n");
617 for (i
= 0; i
< MAX_INT_HISTORY
; i
++)
620 struct interrupt_history
*h
;
623 which
= interrupts
->history_index
- i
- 1;
625 which
+= MAX_INT_HISTORY
;
626 h
= &interrupts
->interrupts_history
[which
];
627 if (h
->taken_cycle
== 0)
630 dt
= h
->taken_cycle
- h
->raised_cycle
;
631 sim_io_printf (sd
, "%2d %-9.9s %15.15s ", i
,
632 interrupt_names
[h
->type
],
633 cycle_to_string (interrupts
->cpu
, h
->taken_cycle
, 0));
634 sim_io_printf (sd
, "%15.15s",
635 cycle_to_string (interrupts
->cpu
, dt
, 0));
638 dt
= prev_interrupt
- h
->taken_cycle
;
639 sim_io_printf (sd
, " %s",
640 cycle_to_string (interrupts
->cpu
, dt
, PRINT_TIME
));
642 sim_io_printf (sd
, "\n");
643 prev_interrupt
= h
->taken_cycle
;