]>
git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/m68hc11/interrupts.c
1 /* interrupts.c -- 68HC11 Interrupts Emulation
2 Copyright 1999-2022 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"
25 #include "sim-signal.h"
27 static const char *interrupt_names
[] = {
63 struct interrupt_def idefs
[] = {
64 /* Serial interrupts. */
65 { M6811_INT_SCI
, M6811_SCSR
, M6811_TDRE
, M6811_SCCR2
, M6811_TIE
},
66 { M6811_INT_SCI
, M6811_SCSR
, M6811_TC
, M6811_SCCR2
, M6811_TCIE
},
67 { M6811_INT_SCI
, M6811_SCSR
, M6811_RDRF
, M6811_SCCR2
, M6811_RIE
},
68 { M6811_INT_SCI
, M6811_SCSR
, M6811_IDLE
, M6811_SCCR2
, M6811_ILIE
},
71 { M6811_INT_SPI
, M6811_SPSR
, M6811_SPIF
, M6811_SPCR
, M6811_SPIE
},
73 /* Realtime interrupts. */
74 { M6811_INT_TCTN
, M6811_TFLG2
, M6811_TOF
, M6811_TMSK2
, M6811_TOI
},
75 { M6811_INT_RT
, M6811_TFLG2
, M6811_RTIF
, M6811_TMSK2
, M6811_RTII
},
77 /* Output compare interrupts. */
78 { M6811_INT_OUTCMP1
, M6811_TFLG1
, M6811_OC1F
, M6811_TMSK1
, M6811_OC1I
},
79 { M6811_INT_OUTCMP2
, M6811_TFLG1
, M6811_OC2F
, M6811_TMSK1
, M6811_OC2I
},
80 { M6811_INT_OUTCMP3
, M6811_TFLG1
, M6811_OC3F
, M6811_TMSK1
, M6811_OC3I
},
81 { M6811_INT_OUTCMP4
, M6811_TFLG1
, M6811_OC4F
, M6811_TMSK1
, M6811_OC4I
},
82 { M6811_INT_OUTCMP5
, M6811_TFLG1
, M6811_OC5F
, M6811_TMSK1
, M6811_OC5I
},
84 /* Input compare interrupts. */
85 { M6811_INT_INCMP1
, M6811_TFLG1
, M6811_IC1F
, M6811_TMSK1
, M6811_IC1I
},
86 { M6811_INT_INCMP2
, M6811_TFLG1
, M6811_IC2F
, M6811_TMSK1
, M6811_IC2I
},
87 { M6811_INT_INCMP3
, M6811_TFLG1
, M6811_IC3F
, M6811_TMSK1
, M6811_IC3I
},
89 /* Pulse accumulator. */
90 { M6811_INT_AINPUT
, M6811_TFLG2
, M6811_PAIF
, M6811_TMSK2
, M6811_PAII
},
91 { M6811_INT_AOVERFLOW
,M6811_TFLG2
, M6811_PAOVF
, M6811_TMSK2
, M6811_PAOVI
},
93 { M6811_INT_COPRESET
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 },
94 { M6811_INT_COPFAIL
, M6811_CONFIG
, M6811_NOCOP
, 0, 0 }
98 #define CYCLES_MAX ((((int64_t) 1) << 62) - 1)
102 OPTION_INTERRUPT_INFO
= OPTION_START
,
103 OPTION_INTERRUPT_CATCH
,
104 OPTION_INTERRUPT_CLEAR
107 static DECLARE_OPTION_HANDLER (interrupt_option_handler
);
109 static const OPTION interrupt_options
[] =
111 { {"interrupt-info", no_argument
, NULL
, OPTION_INTERRUPT_INFO
},
112 '\0', NULL
, "Print information about interrupts",
113 interrupt_option_handler
},
114 { {"interrupt-catch", required_argument
, NULL
, OPTION_INTERRUPT_CATCH
},
116 "Catch interrupts when they are raised or taken\n"
117 "NAME Name of the interrupt\n"
118 "MODE Optional mode (`taken' or `raised')",
119 interrupt_option_handler
},
120 { {"interrupt-clear", required_argument
, NULL
, OPTION_INTERRUPT_CLEAR
},
121 '\0', "NAME", "No longer catch the interrupt",
122 interrupt_option_handler
},
124 { {NULL
, no_argument
, NULL
, 0}, '\0', NULL
, NULL
, NULL
}
127 /* Initialize the interrupts module. */
129 interrupts_initialize (SIM_DESC sd
, sim_cpu
*cpu
)
131 struct interrupts
*interrupts
= &cpu
->cpu_interrupts
;
133 interrupts
->cpu
= cpu
;
135 sim_add_option_table (sd
, 0, interrupt_options
);
138 /* Initialize the interrupts of the processor. */
140 interrupts_reset (struct interrupts
*interrupts
)
144 interrupts
->pending_mask
= 0;
145 if (interrupts
->cpu
->cpu_mode
& M6811_SMOD
)
146 interrupts
->vectors_addr
= 0xbfc0;
148 interrupts
->vectors_addr
= 0xffc0;
149 interrupts
->nb_interrupts_raised
= 0;
150 interrupts
->min_mask_cycles
= CYCLES_MAX
;
151 interrupts
->max_mask_cycles
= 0;
152 interrupts
->last_mask_cycles
= 0;
153 interrupts
->start_mask_cycle
= -1;
154 interrupts
->xirq_start_mask_cycle
= -1;
155 interrupts
->xirq_max_mask_cycles
= 0;
156 interrupts
->xirq_min_mask_cycles
= CYCLES_MAX
;
157 interrupts
->xirq_last_mask_cycles
= 0;
159 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
161 interrupts
->interrupt_order
[i
] = i
;
164 /* Clear the interrupt history table. */
165 interrupts
->history_index
= 0;
166 memset (interrupts
->interrupts_history
, 0,
167 sizeof (interrupts
->interrupts_history
));
169 memset (interrupts
->interrupts
, 0,
170 sizeof (interrupts
->interrupts
));
172 /* In bootstrap mode, initialize the vector table to point
173 to the RAM location. */
174 if (interrupts
->cpu
->cpu_mode
== M6811_SMOD
)
176 bfd_vma addr
= interrupts
->vectors_addr
;
177 uint16_t vector
= 0x0100 - 3 * (M6811_INT_NUMBER
- 1);
178 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
180 memory_write16 (interrupts
->cpu
, addr
, vector
);
188 find_interrupt (const char *name
)
193 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
194 if (strcasecmp (name
, interrupt_names
[i
]) == 0)
201 interrupt_option_handler (SIM_DESC sd
, sim_cpu
*cpu
,
202 int opt
, char *arg
, int is_command
)
207 struct interrupts
*interrupts
;
210 cpu
= STATE_CPU (sd
, 0);
212 interrupts
= &cpu
->cpu_interrupts
;
215 case OPTION_INTERRUPT_INFO
:
216 for (id
= 0; id
< M6811_INT_NUMBER
; id
++)
218 sim_io_eprintf (sd
, "%-10.10s ", interrupt_names
[id
]);
219 switch (interrupts
->interrupts
[id
].stop_mode
)
221 case SIM_STOP_WHEN_RAISED
:
222 sim_io_eprintf (sd
, "catch raised ");
225 case SIM_STOP_WHEN_TAKEN
:
226 sim_io_eprintf (sd
, "catch taken ");
229 case SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
:
230 sim_io_eprintf (sd
, "catch all ");
234 sim_io_eprintf (sd
, " ");
237 sim_io_eprintf (sd
, "%ld\n",
238 interrupts
->interrupts
[id
].raised_count
);
242 case OPTION_INTERRUPT_CATCH
:
243 p
= strchr (arg
, ',');
247 mode
= SIM_STOP_WHEN_RAISED
;
248 id
= find_interrupt (arg
);
250 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
252 if (p
&& strcasecmp (p
, "raised") == 0)
253 mode
= SIM_STOP_WHEN_RAISED
;
254 else if (p
&& strcasecmp (p
, "taken") == 0)
255 mode
= SIM_STOP_WHEN_TAKEN
;
256 else if (p
&& strcasecmp (p
, "all") == 0)
257 mode
= SIM_STOP_WHEN_RAISED
| SIM_STOP_WHEN_TAKEN
;
260 sim_io_eprintf (sd
, "Invalid argument: %s\n", p
);
264 interrupts
->interrupts
[id
].stop_mode
= mode
;
267 case OPTION_INTERRUPT_CLEAR
:
268 mode
= SIM_STOP_WHEN_RAISED
;
269 id
= find_interrupt (arg
);
271 sim_io_eprintf (sd
, "Interrupt name not recognized: %s\n", arg
);
273 interrupts
->interrupts
[id
].stop_mode
= 0;
280 /* Update the mask of pending interrupts. This operation must be called
281 when the state of some 68HC11 IO register changes. It looks the
282 different registers that indicate a pending interrupt (timer, SCI, SPI,
283 ...) and records the interrupt if it's there and enabled. */
285 interrupts_update_pending (struct interrupts
*interrupts
)
289 unsigned long clear_mask
;
290 unsigned long set_mask
;
294 ioregs
= &interrupts
->cpu
->ios
[0];
296 for (i
= 0; i
< ARRAY_SIZE (idefs
); i
++)
298 struct interrupt_def
*idef
= &idefs
[i
];
301 /* Look if the interrupt is enabled. */
302 if (idef
->enable_paddr
)
304 data
= ioregs
[idef
->enable_paddr
];
305 if (!(data
& idef
->enabled_mask
))
308 clear_mask
|= (1 << idef
->int_number
);
313 /* Interrupt is enabled, see if it's there. */
314 data
= ioregs
[idef
->int_paddr
];
315 if (!(data
& idef
->int_mask
))
318 clear_mask
|= (1 << idef
->int_number
);
323 set_mask
|= (1 << idef
->int_number
);
326 /* Some interrupts are shared (M6811_INT_SCI) so clear
327 the interrupts before setting the new ones. */
328 interrupts
->pending_mask
&= ~clear_mask
;
329 interrupts
->pending_mask
|= set_mask
;
331 /* Keep track of when the interrupt is raised by the device.
332 Also implements the breakpoint-on-interrupt. */
335 int64_t cycle
= cpu_current_cycle (interrupts
->cpu
);
338 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
340 if (!(set_mask
& (1 << i
)))
343 interrupts
->interrupts
[i
].cpu_cycle
= cycle
;
344 if (interrupts
->interrupts
[i
].stop_mode
& SIM_STOP_WHEN_RAISED
)
347 sim_io_printf (CPU_STATE (interrupts
->cpu
),
348 "Interrupt %s raised\n",
353 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
355 0, cpu_get_pc (interrupts
->cpu
),
362 /* Finds the current active and non-masked interrupt.
363 Returns the interrupt number (index in the vector table) or -1
364 if no interrupt can be serviced. */
366 interrupts_get_current (struct interrupts
*interrupts
)
370 if (interrupts
->pending_mask
== 0)
373 /* SWI and illegal instructions are simulated by an interrupt.
374 They are not maskable. */
375 if (interrupts
->pending_mask
& (1 << M6811_INT_SWI
))
377 interrupts
->pending_mask
&= ~(1 << M6811_INT_SWI
);
378 return M6811_INT_SWI
;
380 if (interrupts
->pending_mask
& (1 << M6811_INT_ILLEGAL
))
382 interrupts
->pending_mask
&= ~(1 << M6811_INT_ILLEGAL
);
383 return M6811_INT_ILLEGAL
;
386 /* If there is a non maskable interrupt, go for it (unless we are masked
388 if (interrupts
->pending_mask
& (1 << M6811_INT_XIRQ
))
390 if (cpu_get_ccr_X (interrupts
->cpu
) == 0)
392 interrupts
->pending_mask
&= ~(1 << M6811_INT_XIRQ
);
393 return M6811_INT_XIRQ
;
398 /* Interrupts are masked, do nothing. */
399 if (cpu_get_ccr_I (interrupts
->cpu
) == 1)
404 /* Returns the first interrupt number which is pending.
405 The interrupt priority is specified by the table `interrupt_order'.
406 For these interrupts, the pending mask is cleared when the program
407 performs some actions on the corresponding device. If the device
408 is not reset, the interrupt remains and will be re-raised when
409 we return from the interrupt (see 68HC11 pink book). */
410 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
412 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
414 if (interrupts
->pending_mask
& (1 << int_number
))
423 /* Process the current interrupt if there is one. This operation must
424 be called after each instruction to handle the interrupts. If interrupts
425 are masked, it does nothing. */
427 interrupts_process (struct interrupts
*interrupts
)
432 /* See if interrupts are enabled/disabled and keep track of the
433 number of cycles the interrupts are masked. Such information is
434 then reported by the info command. */
435 ccr
= cpu_get_ccr (interrupts
->cpu
);
436 if (ccr
& M6811_I_BIT
)
438 if (interrupts
->start_mask_cycle
< 0)
439 interrupts
->start_mask_cycle
= cpu_current_cycle (interrupts
->cpu
);
441 else if (interrupts
->start_mask_cycle
>= 0
442 && (ccr
& M6811_I_BIT
) == 0)
444 int64_t t
= cpu_current_cycle (interrupts
->cpu
);
446 t
-= interrupts
->start_mask_cycle
;
447 if (t
< interrupts
->min_mask_cycles
)
448 interrupts
->min_mask_cycles
= t
;
449 if (t
> interrupts
->max_mask_cycles
)
450 interrupts
->max_mask_cycles
= t
;
451 interrupts
->start_mask_cycle
= -1;
452 interrupts
->last_mask_cycles
= t
;
454 if (ccr
& M6811_X_BIT
)
456 if (interrupts
->xirq_start_mask_cycle
< 0)
457 interrupts
->xirq_start_mask_cycle
458 = cpu_current_cycle (interrupts
->cpu
);
460 else if (interrupts
->xirq_start_mask_cycle
>= 0
461 && (ccr
& M6811_X_BIT
) == 0)
463 int64_t t
= cpu_current_cycle (interrupts
->cpu
);
465 t
-= interrupts
->xirq_start_mask_cycle
;
466 if (t
< interrupts
->xirq_min_mask_cycles
)
467 interrupts
->xirq_min_mask_cycles
= t
;
468 if (t
> interrupts
->xirq_max_mask_cycles
)
469 interrupts
->xirq_max_mask_cycles
= t
;
470 interrupts
->xirq_start_mask_cycle
= -1;
471 interrupts
->xirq_last_mask_cycles
= t
;
474 id
= interrupts_get_current (interrupts
);
478 struct interrupt_history
*h
;
480 /* Implement the breakpoint-on-interrupt. */
481 if (interrupts
->interrupts
[id
].stop_mode
& SIM_STOP_WHEN_TAKEN
)
483 sim_io_printf (CPU_STATE (interrupts
->cpu
),
484 "Interrupt %s will be handled\n",
485 interrupt_names
[id
]);
486 sim_engine_halt (CPU_STATE (interrupts
->cpu
),
488 0, cpu_get_pc (interrupts
->cpu
),
493 cpu_push_all (interrupts
->cpu
);
494 addr
= memory_read16 (interrupts
->cpu
,
495 interrupts
->vectors_addr
+ id
* 2);
496 cpu_call (interrupts
->cpu
, addr
);
498 /* Now, protect from nested interrupts. */
499 if (id
== M6811_INT_XIRQ
)
501 cpu_set_ccr_X (interrupts
->cpu
, 1);
505 cpu_set_ccr_I (interrupts
->cpu
, 1);
508 /* Update the interrupt history table. */
509 h
= &interrupts
->interrupts_history
[interrupts
->history_index
];
511 h
->taken_cycle
= cpu_current_cycle (interrupts
->cpu
);
512 h
->raised_cycle
= interrupts
->interrupts
[id
].cpu_cycle
;
514 if (interrupts
->history_index
>= MAX_INT_HISTORY
-1)
515 interrupts
->history_index
= 0;
517 interrupts
->history_index
++;
519 interrupts
->nb_interrupts_raised
++;
520 cpu_add_cycles (interrupts
->cpu
, 14);
527 interrupts_raise (struct interrupts
*interrupts
, enum M6811_INT number
)
529 interrupts
->pending_mask
|= (1 << number
);
530 interrupts
->nb_interrupts_raised
++;
534 interrupts_info (SIM_DESC sd
, struct interrupts
*interrupts
)
536 int64_t t
, prev_interrupt
;
539 sim_io_printf (sd
, "Interrupts Info:\n");
540 sim_io_printf (sd
, " Interrupts raised: %lu\n",
541 interrupts
->nb_interrupts_raised
);
543 if (interrupts
->start_mask_cycle
>= 0)
545 t
= cpu_current_cycle (interrupts
->cpu
);
547 t
-= interrupts
->start_mask_cycle
;
548 if (t
> interrupts
->max_mask_cycles
)
549 interrupts
->max_mask_cycles
= t
;
551 sim_io_printf (sd
, " Current interrupts masked sequence: %s\n",
552 cycle_to_string (interrupts
->cpu
, t
,
553 PRINT_TIME
| PRINT_CYCLE
));
555 t
= interrupts
->min_mask_cycles
== CYCLES_MAX
?
556 interrupts
->max_mask_cycles
:
557 interrupts
->min_mask_cycles
;
558 sim_io_printf (sd
, " Shortest interrupts masked sequence: %s\n",
559 cycle_to_string (interrupts
->cpu
, t
,
560 PRINT_TIME
| PRINT_CYCLE
));
562 t
= interrupts
->max_mask_cycles
;
563 sim_io_printf (sd
, " Longest interrupts masked sequence: %s\n",
564 cycle_to_string (interrupts
->cpu
, t
,
565 PRINT_TIME
| PRINT_CYCLE
));
567 t
= interrupts
->last_mask_cycles
;
568 sim_io_printf (sd
, " Last interrupts masked sequence: %s\n",
569 cycle_to_string (interrupts
->cpu
, t
,
570 PRINT_TIME
| PRINT_CYCLE
));
572 if (interrupts
->xirq_start_mask_cycle
>= 0)
574 t
= cpu_current_cycle (interrupts
->cpu
);
576 t
-= interrupts
->xirq_start_mask_cycle
;
577 if (t
> interrupts
->xirq_max_mask_cycles
)
578 interrupts
->xirq_max_mask_cycles
= t
;
580 sim_io_printf (sd
, " XIRQ Current interrupts masked sequence: %s\n",
581 cycle_to_string (interrupts
->cpu
, t
,
582 PRINT_TIME
| PRINT_CYCLE
));
585 t
= interrupts
->xirq_min_mask_cycles
== CYCLES_MAX
?
586 interrupts
->xirq_max_mask_cycles
:
587 interrupts
->xirq_min_mask_cycles
;
588 sim_io_printf (sd
, " XIRQ Min interrupts masked sequence: %s\n",
589 cycle_to_string (interrupts
->cpu
, t
,
590 PRINT_TIME
| PRINT_CYCLE
));
592 t
= interrupts
->xirq_max_mask_cycles
;
593 sim_io_printf (sd
, " XIRQ Max interrupts masked sequence: %s\n",
594 cycle_to_string (interrupts
->cpu
, t
,
595 PRINT_TIME
| PRINT_CYCLE
));
597 t
= interrupts
->xirq_last_mask_cycles
;
598 sim_io_printf (sd
, " XIRQ Last interrupts masked sequence: %s\n",
599 cycle_to_string (interrupts
->cpu
, t
,
600 PRINT_TIME
| PRINT_CYCLE
));
602 if (interrupts
->pending_mask
)
604 sim_io_printf (sd
, " Pending interrupts : ");
605 for (i
= 0; i
< M6811_INT_NUMBER
; i
++)
607 enum M6811_INT int_number
= interrupts
->interrupt_order
[i
];
609 if (interrupts
->pending_mask
& (1 << int_number
))
611 sim_io_printf (sd
, "%s ", interrupt_names
[int_number
]);
614 sim_io_printf (sd
, "\n");
618 sim_io_printf (sd
, "N Interrupt Cycle Taken Latency"
619 " Delta between interrupts\n");
620 for (i
= 0; i
< MAX_INT_HISTORY
; i
++)
623 struct interrupt_history
*h
;
626 which
= interrupts
->history_index
- i
- 1;
628 which
+= MAX_INT_HISTORY
;
629 h
= &interrupts
->interrupts_history
[which
];
630 if (h
->taken_cycle
== 0)
633 dt
= h
->taken_cycle
- h
->raised_cycle
;
634 sim_io_printf (sd
, "%2d %-9.9s %15.15s ", i
,
635 interrupt_names
[h
->type
],
636 cycle_to_string (interrupts
->cpu
, h
->taken_cycle
, 0));
637 sim_io_printf (sd
, "%15.15s",
638 cycle_to_string (interrupts
->cpu
, dt
, 0));
641 dt
= prev_interrupt
- h
->taken_cycle
;
642 sim_io_printf (sd
, " %s",
643 cycle_to_string (interrupts
->cpu
, dt
, PRINT_TIME
));
645 sim_io_printf (sd
, "\n");
646 prev_interrupt
= h
->taken_cycle
;