]>
git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/m68hc11/interrupts.c
1 /* interrupts.c -- 68HC11 Interrupts Emulation
2 Copyright 1999-2021 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/>. */
20 /* This must come before any other includes. */
24 #include "sim-options.h"
26 static const char *interrupt_names
[] = {
62 struct interrupt_def idefs
[] = {
63 /* Serial interrupts. */
64 { M6811_INT_SCI
, M6811_SCSR
, M6811_TDRE
, M6811_SCCR2
, M6811_TIE
},
65 { M6811_INT_SCI
, M6811_SCSR
, M6811_TC
, M6811_SCCR2
, M6811_TCIE
},
66 { M6811_INT_SCI
, M6811_SCSR
, M6811_RDRF
, M6811_SCCR2
, M6811_RIE
},
67 { M6811_INT_SCI
, M6811_SCSR
, M6811_IDLE
, M6811_SCCR2
, M6811_ILIE
},
70 { M6811_INT_SPI
, M6811_SPSR
, M6811_SPIF
, M6811_SPCR
, M6811_SPIE
},
72 /* Realtime interrupts. */
73 { M6811_INT_TCTN
, M6811_TFLG2
, M6811_TOF
, M6811_TMSK2
, M6811_TOI
},
74 { M6811_INT_RT
, M6811_TFLG2
, M6811_RTIF
, M6811_TMSK2
, M6811_RTII
},
76 /* Output compare interrupts. */
77 { M6811_INT_OUTCMP1
, M6811_TFLG1
, M6811_OC1F
, M6811_TMSK1
, M6811_OC1I
},
78 { M6811_INT_OUTCMP2
, M6811_TFLG1
, M6811_OC2F
, M6811_TMSK1
, M6811_OC2I
},
79 { M6811_INT_OUTCMP3
, M6811_TFLG1
, M6811_OC3F
, M6811_TMSK1
, M6811_OC3I
},
80 { M6811_INT_OUTCMP4
, M6811_TFLG1
, M6811_OC4F
, M6811_TMSK1
, M6811_OC4I
},
81 { M6811_INT_OUTCMP5
, M6811_TFLG1
, M6811_OC5F
, M6811_TMSK1
, M6811_OC5I
},
83 /* Input compare interrupts. */
84 { M6811_INT_INCMP1
, M6811_TFLG1
, M6811_IC1F
, M6811_TMSK1
, M6811_IC1I
},
85 { M6811_INT_INCMP2
, M6811_TFLG1
, M6811_IC2F
, M6811_TMSK1
, M6811_IC2I
},
86 { M6811_INT_INCMP3
, M6811_TFLG1
, M6811_IC3F
, M6811_TMSK1
, M6811_IC3I
},
88 /* Pulse accumulator. */
89 { M6811_INT_AINPUT
, M6811_TFLG2
, M6811_PAIF
, M6811_TMSK2
, M6811_PAII
},
90 { M6811_INT_AOVERFLOW
,M6811_TFLG2
, M6811_PAOVF
, M6811_TMSK2
, M6811_PAOVI
},
92 { M6811_INT_COPRESET
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 },
93 { M6811_INT_COPFAIL
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 }
97 #define CYCLES_MAX ((((signed64) 1) << 62) - 1)
101 OPTION_INTERRUPT_INFO
= OPTION_START
,
102 OPTION_INTERRUPT_CATCH
,
103 OPTION_INTERRUPT_CLEAR
106 static DECLARE_OPTION_HANDLER (interrupt_option_handler
);
108 static const OPTION interrupt_options
[] =
110 { {"interrupt-info", no_argument
, NULL
, OPTION_INTERRUPT_INFO
},
111 '\0', NULL
, "Print information about interrupts",
112 interrupt_option_handler
},
113 { {"interrupt-catch", required_argument
, NULL
, OPTION_INTERRUPT_CATCH
},
115 "Catch interrupts when they are raised or taken\n"
116 "NAME Name of the interrupt\n"
117 "MODE Optional mode (`taken' or `raised')",
118 interrupt_option_handler
},
119 { {"interrupt-clear", required_argument
, NULL
, OPTION_INTERRUPT_CLEAR
},
120 '\0', "NAME", "No longer catch the interrupt",
121 interrupt_option_handler
},
123 { {NULL
, no_argument
, NULL
, 0}, '\0', NULL
, NULL
, NULL
}
126 /* Initialize the interrupts module. */
128 interrupts_initialize (SIM_DESC sd
, sim_cpu
*cpu
)
130 struct interrupts
*interrupts
= &cpu
->cpu_interrupts
;
132 interrupts
->cpu
= cpu
;
134 sim_add_option_table (sd
, 0, interrupt_options
);
137 /* Initialize the interrupts of the processor. */
139 interrupts_reset (struct interrupts
*interrupts
)
143 interrupts
->pending_mask
= 0;
144 if (interrupts
->cpu
->cpu_mode
& M6811_SMOD
)
145 interrupts
->vectors_addr
= 0xbfc0;
147 interrupts
->vectors_addr
= 0xffc0;
148 interrupts
->nb_interrupts_raised
= 0;
149 interrupts
->min_mask_cycles
= CYCLES_MAX
;
150 interrupts
->max_mask_cycles
= 0;
151 interrupts
->last_mask_cycles
= 0;
152 interrupts
->start_mask_cycle
= -1;
153 interrupts
->xirq_start_mask_cycle
= -1;
154 interrupts
->xirq_max_mask_cycles
= 0;
155 interrupts
->xirq_min_mask_cycles
= CYCLES_MAX
;
156 interrupts
->xirq_last_mask_cycles
= 0;
158 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
160 interrupts
->interrupt_order
[i
] = i
;
163 /* Clear the interrupt history table. */
164 interrupts
->history_index
= 0;
165 memset (interrupts
->interrupts_history
, 0,
166 sizeof (interrupts
->interrupts_history
));
168 memset (interrupts
->interrupts
, 0,
169 sizeof (interrupts
->interrupts
));
171 /* In bootstrap mode, initialize the vector table to point
172 to the RAM location. */
173 if (interrupts
->cpu
->cpu_mode
== M6811_SMOD
)
175 bfd_vma addr
= interrupts
->vectors_addr
;
176 uint16 vector
= 0x0100 - 3 * (M6811_INT_NUMBER
- 1);
177 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
179 memory_write16 (interrupts
->cpu
, addr
, vector
);
187 find_interrupt (const char *name
)
192 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
193 if (strcasecmp (name
, interrupt_names
[i
]) == 0)
200 interrupt_option_handler (SIM_DESC sd
, sim_cpu
*cpu
,
201 int opt
, char *arg
, int is_command
)
206 struct interrupts
*interrupts
;
209 cpu
= STATE_CPU (sd
, 0);
211 interrupts
= &cpu
->cpu_interrupts
;
214 case OPTION_INTERRUPT_INFO
:
215 for (id
= 0; id
< M6811_INT_NUMBER
; id
++)
217 sim_io_eprintf (sd
, "%-10.10s ", interrupt_names
[id
]);
218 switch (interrupts
->interrupts
[id
].stop_mode
)
220 case SIM_STOP_WHEN_RAISED
:
221 sim_io_eprintf (sd
, "catch raised ");
224 case SIM_STOP_WHEN_TAKEN
:
225 sim_io_eprintf (sd
, "catch taken ");
228 case SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
:
229 sim_io_eprintf (sd
, "catch all ");
233 sim_io_eprintf (sd
, " ");
236 sim_io_eprintf (sd
, "%ld\n",
237 interrupts
->interrupts
[id
].raised_count
);
241 case OPTION_INTERRUPT_CATCH
:
242 p
= strchr (arg
, ',');
246 mode
= SIM_STOP_WHEN_RAISED
;
247 id
= find_interrupt (arg
);
249 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
251 if (p
&& strcasecmp (p
, "raised") == 0)
252 mode
= SIM_STOP_WHEN_RAISED
;
253 else if (p
&& strcasecmp (p
, "taken") == 0)
254 mode
= SIM_STOP_WHEN_TAKEN
;
255 else if (p
&& strcasecmp (p
, "all") == 0)
256 mode
= SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
;
259 sim_io_eprintf (sd
, "Invalid argument: %s\n", p
);
263 interrupts
->interrupts
[id
].stop_mode
= mode
;
266 case OPTION_INTERRUPT_CLEAR
:
267 mode
= SIM_STOP_WHEN_RAISED
;
268 id
= find_interrupt (arg
);
270 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
272 interrupts
->interrupts
[id
].stop_mode
= 0;
279 /* Update the mask of pending interrupts. This operation must be called
280 when the state of some 68HC11 IO register changes. It looks the
281 different registers that indicate a pending interrupt (timer, SCI, SPI,
282 ...) and records the interrupt if it's there and enabled. */
284 interrupts_update_pending (struct interrupts
*interrupts
)
288 unsigned long clear_mask
;
289 unsigned long set_mask
;
293 ioregs
= &interrupts
->cpu
->ios
[0];
295 for (i
= 0; i
< ARRAY_SIZE (idefs
); i
++)
297 struct interrupt_def
*idef
= &idefs
[i
];
300 /* Look if the interrupt is enabled. */
301 if (idef
->enable_paddr
)
303 data
= ioregs
[idef
->enable_paddr
];
304 if (!(data
& idef
->enabled_mask
))
307 clear_mask
|= (1 << idef
->int_number
);
312 /* Interrupt is enabled, see if it's there. */
313 data
= ioregs
[idef
->int_paddr
];
314 if (!(data
& idef
->int_mask
))
317 clear_mask
|= (1 << idef
->int_number
);
322 set_mask
|= (1 << idef
->int_number
);
325 /* Some interrupts are shared (M6811_INT_SCI) so clear
326 the interrupts before setting the new ones. */
327 interrupts
->pending_mask
&= ~clear_mask
;
328 interrupts
->pending_mask
|= set_mask
;
330 /* Keep track of when the interrupt is raised by the device.
331 Also implements the breakpoint-on-interrupt. */
334 signed64 cycle
= cpu_current_cycle (interrupts
->cpu
);
337 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
339 if (!(set_mask
& (1 << i
)))
342 interrupts
->interrupts
[i
].cpu_cycle
= cycle
;
343 if (interrupts
->interrupts
[i
].stop_mode
& SIM_STOP_WHEN_RAISED
)
346 sim_io_printf (CPU_STATE (interrupts
->cpu
),
347 "Interrupt %s raised\n",
352 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
354 0, cpu_get_pc (interrupts
->cpu
),
361 /* Finds the current active and non-masked interrupt.
362 Returns the interrupt number (index in the vector table) or -1
363 if no interrupt can be serviced. */
365 interrupts_get_current (struct interrupts
*interrupts
)
369 if (interrupts
->pending_mask
== 0)
372 /* SWI and illegal instructions are simulated by an interrupt.
373 They are not maskable. */
374 if (interrupts
->pending_mask
& (1 << M6811_INT_SWI
))
376 interrupts
->pending_mask
&= ~(1 << M6811_INT_SWI
);
377 return M6811_INT_SWI
;
379 if (interrupts
->pending_mask
& (1 << M6811_INT_ILLEGAL
))
381 interrupts
->pending_mask
&= ~(1 << M6811_INT_ILLEGAL
);
382 return M6811_INT_ILLEGAL
;
385 /* If there is a non maskable interrupt, go for it (unless we are masked
387 if (interrupts
->pending_mask
& (1 << M6811_INT_XIRQ
))
389 if (cpu_get_ccr_X (interrupts
->cpu
) == 0)
391 interrupts
->pending_mask
&= ~(1 << M6811_INT_XIRQ
);
392 return M6811_INT_XIRQ
;
397 /* Interrupts are masked, do nothing. */
398 if (cpu_get_ccr_I (interrupts
->cpu
) == 1)
403 /* Returns the first interrupt number which is pending.
404 The interrupt priority is specified by the table `interrupt_order'.
405 For these interrupts, the pending mask is cleared when the program
406 performs some actions on the corresponding device. If the device
407 is not reset, the interrupt remains and will be re-raised when
408 we return from the interrupt (see 68HC11 pink book). */
409 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
411 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
413 if (interrupts
->pending_mask
& (1 << int_number
))
422 /* Process the current interrupt if there is one. This operation must
423 be called after each instruction to handle the interrupts. If interrupts
424 are masked, it does nothing. */
426 interrupts_process (struct interrupts
*interrupts
)
431 /* See if interrupts are enabled/disabled and keep track of the
432 number of cycles the interrupts are masked. Such information is
433 then reported by the info command. */
434 ccr
= cpu_get_ccr (interrupts
->cpu
);
435 if (ccr
& M6811_I_BIT
)
437 if (interrupts
->start_mask_cycle
< 0)
438 interrupts
->start_mask_cycle
= cpu_current_cycle (interrupts
->cpu
);
440 else if (interrupts
->start_mask_cycle
>= 0
441 && (ccr
& M6811_I_BIT
) == 0)
443 signed64 t
= cpu_current_cycle (interrupts
->cpu
);
445 t
-= interrupts
->start_mask_cycle
;
446 if (t
< interrupts
->min_mask_cycles
)
447 interrupts
->min_mask_cycles
= t
;
448 if (t
> interrupts
->max_mask_cycles
)
449 interrupts
->max_mask_cycles
= t
;
450 interrupts
->start_mask_cycle
= -1;
451 interrupts
->last_mask_cycles
= t
;
453 if (ccr
& M6811_X_BIT
)
455 if (interrupts
->xirq_start_mask_cycle
< 0)
456 interrupts
->xirq_start_mask_cycle
457 = cpu_current_cycle (interrupts
->cpu
);
459 else if (interrupts
->xirq_start_mask_cycle
>= 0
460 && (ccr
& M6811_X_BIT
) == 0)
462 signed64 t
= cpu_current_cycle (interrupts
->cpu
);
464 t
-= interrupts
->xirq_start_mask_cycle
;
465 if (t
< interrupts
->xirq_min_mask_cycles
)
466 interrupts
->xirq_min_mask_cycles
= t
;
467 if (t
> interrupts
->xirq_max_mask_cycles
)
468 interrupts
->xirq_max_mask_cycles
= t
;
469 interrupts
->xirq_start_mask_cycle
= -1;
470 interrupts
->xirq_last_mask_cycles
= t
;
473 id
= interrupts_get_current (interrupts
);
477 struct interrupt_history
*h
;
479 /* Implement the breakpoint-on-interrupt. */
480 if (interrupts
->interrupts
[id
].stop_mode
& SIM_STOP_WHEN_TAKEN
)
482 sim_io_printf (CPU_STATE (interrupts
->cpu
),
483 "Interrupt %s will be handled\n",
484 interrupt_names
[id
]);
485 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
487 0, cpu_get_pc (interrupts
->cpu
),
492 cpu_push_all (interrupts
->cpu
);
493 addr
= memory_read16 (interrupts
->cpu
,
494 interrupts
->vectors_addr
+ id
* 2);
495 cpu_call (interrupts
->cpu
, addr
);
497 /* Now, protect from nested interrupts. */
498 if (id
== M6811_INT_XIRQ
)
500 cpu_set_ccr_X (interrupts
->cpu
, 1);
504 cpu_set_ccr_I (interrupts
->cpu
, 1);
507 /* Update the interrupt history table. */
508 h
= &interrupts
->interrupts_history
[interrupts
->history_index
];
510 h
->taken_cycle
= cpu_current_cycle (interrupts
->cpu
);
511 h
->raised_cycle
= interrupts
->interrupts
[id
].cpu_cycle
;
513 if (interrupts
->history_index
>= MAX_INT_HISTORY
-1)
514 interrupts
->history_index
= 0;
516 interrupts
->history_index
++;
518 interrupts
->nb_interrupts_raised
++;
519 cpu_add_cycles (interrupts
->cpu
, 14);
526 interrupts_raise (struct interrupts
*interrupts
, enum M6811_INT number
)
528 interrupts
->pending_mask
|= (1 << number
);
529 interrupts
->nb_interrupts_raised
++;
533 interrupts_info (SIM_DESC sd
, struct interrupts
*interrupts
)
535 signed64 t
, prev_interrupt
;
538 sim_io_printf (sd
, "Interrupts Info:\n");
539 sim_io_printf (sd
, " Interrupts raised: %lu\n",
540 interrupts
->nb_interrupts_raised
);
542 if (interrupts
->start_mask_cycle
>= 0)
544 t
= cpu_current_cycle (interrupts
->cpu
);
546 t
-= interrupts
->start_mask_cycle
;
547 if (t
> interrupts
->max_mask_cycles
)
548 interrupts
->max_mask_cycles
= t
;
550 sim_io_printf (sd
, " Current interrupts masked sequence: %s\n",
551 cycle_to_string (interrupts
->cpu
, t
,
552 PRINT_TIME
| PRINT_CYCLE
));
554 t
= interrupts
->min_mask_cycles
== CYCLES_MAX
?
555 interrupts
->max_mask_cycles
:
556 interrupts
->min_mask_cycles
;
557 sim_io_printf (sd
, " Shortest interrupts masked sequence: %s\n",
558 cycle_to_string (interrupts
->cpu
, t
,
559 PRINT_TIME
| PRINT_CYCLE
));
561 t
= interrupts
->max_mask_cycles
;
562 sim_io_printf (sd
, " Longest interrupts masked sequence: %s\n",
563 cycle_to_string (interrupts
->cpu
, t
,
564 PRINT_TIME
| PRINT_CYCLE
));
566 t
= interrupts
->last_mask_cycles
;
567 sim_io_printf (sd
, " Last interrupts masked sequence: %s\n",
568 cycle_to_string (interrupts
->cpu
, t
,
569 PRINT_TIME
| PRINT_CYCLE
));
571 if (interrupts
->xirq_start_mask_cycle
>= 0)
573 t
= cpu_current_cycle (interrupts
->cpu
);
575 t
-= interrupts
->xirq_start_mask_cycle
;
576 if (t
> interrupts
->xirq_max_mask_cycles
)
577 interrupts
->xirq_max_mask_cycles
= t
;
579 sim_io_printf (sd
, " XIRQ Current interrupts masked sequence: %s\n",
580 cycle_to_string (interrupts
->cpu
, t
,
581 PRINT_TIME
| PRINT_CYCLE
));
584 t
= interrupts
->xirq_min_mask_cycles
== CYCLES_MAX
?
585 interrupts
->xirq_max_mask_cycles
:
586 interrupts
->xirq_min_mask_cycles
;
587 sim_io_printf (sd
, " XIRQ Min interrupts masked sequence: %s\n",
588 cycle_to_string (interrupts
->cpu
, t
,
589 PRINT_TIME
| PRINT_CYCLE
));
591 t
= interrupts
->xirq_max_mask_cycles
;
592 sim_io_printf (sd
, " XIRQ Max interrupts masked sequence: %s\n",
593 cycle_to_string (interrupts
->cpu
, t
,
594 PRINT_TIME
| PRINT_CYCLE
));
596 t
= interrupts
->xirq_last_mask_cycles
;
597 sim_io_printf (sd
, " XIRQ Last interrupts masked sequence: %s\n",
598 cycle_to_string (interrupts
->cpu
, t
,
599 PRINT_TIME
| PRINT_CYCLE
));
601 if (interrupts
->pending_mask
)
603 sim_io_printf (sd
, " Pending interrupts : ");
604 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
606 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
608 if (interrupts
->pending_mask
& (1 << int_number
))
610 sim_io_printf (sd
, "%s ", interrupt_names
[int_number
]);
613 sim_io_printf (sd
, "\n");
617 sim_io_printf (sd
, "N Interrupt Cycle Taken Latency"
618 " Delta between interrupts\n");
619 for (i
= 0; i
< MAX_INT_HISTORY
; i
++)
622 struct interrupt_history
*h
;
625 which
= interrupts
->history_index
- i
- 1;
627 which
+= MAX_INT_HISTORY
;
628 h
= &interrupts
->interrupts_history
[which
];
629 if (h
->taken_cycle
== 0)
632 dt
= h
->taken_cycle
- h
->raised_cycle
;
633 sim_io_printf (sd
, "%2d %-9.9s %15.15s ", i
,
634 interrupt_names
[h
->type
],
635 cycle_to_string (interrupts
->cpu
, h
->taken_cycle
, 0));
636 sim_io_printf (sd
, "%15.15s",
637 cycle_to_string (interrupts
->cpu
, dt
, 0));
640 dt
= prev_interrupt
- h
->taken_cycle
;
641 sim_io_printf (sd
, " %s",
642 cycle_to_string (interrupts
->cpu
, dt
, PRINT_TIME
));
644 sim_io_printf (sd
, "\n");
645 prev_interrupt
= h
->taken_cycle
;