diff options
Diffstat (limited to 'sim/m68hc11')
-rw-r--r-- | sim/m68hc11/ChangeLog | 21 | ||||
-rw-r--r-- | sim/m68hc11/interrupts.c | 302 | ||||
-rw-r--r-- | sim/m68hc11/interrupts.h | 44 | ||||
-rw-r--r-- | sim/m68hc11/m68hc11_sim.c | 11 |
4 files changed, 354 insertions, 24 deletions
diff --git a/sim/m68hc11/ChangeLog b/sim/m68hc11/ChangeLog index e4cc73f..f0ab92c 100644 --- a/sim/m68hc11/ChangeLog +++ b/sim/m68hc11/ChangeLog @@ -1,3 +1,24 @@ +2002-03-07 Stephane Carrez <Stephane.Carrez@worldnet.fr> + + * interrupts.c (interrupts_reset): New function, setup interrupt + vector address according to cpu mode. + (interrupts_initialize): Move reset portion to the above. + (interrupt_names): New table to give a name to interrupts. + (idefs): Handle pulse accumulator interrupts. + (interrupts_info): Print the interrupt history. + (interrupt_option_handler): New function. + (interrupt_options): New table of options. + (interrupts_update_pending): Keep track of when interrupts are + raised and implement breakpoint-on-raise-interrupt. + (interrupts_process): Keep track of when interrupts are taken + and implement breakpoint-on-interrupt. + * interrupts.h (struct interrupt_history): Define. + (struct interrupt): Keep track of the interrupt history. + (interrupts_reset): Declare. + (interrupts_initialize): Update prototype. + * m68hc11_sim.c (cpu_reset): Reset interrupts. + (cpu_initialize): Cleanup. + 2001-07-28 Stephane Carrez <Stephane.Carrez@worldnet.fr> * dv-m68hc11eepr.c (m68hc11eepr_info): Fix print of current write diff --git a/sim/m68hc11/interrupts.c b/sim/m68hc11/interrupts.c index 5c7f5e2..5844c74 100644 --- a/sim/m68hc11/interrupts.c +++ b/sim/m68hc11/interrupts.c @@ -1,5 +1,5 @@ /* interrupts.c -- 68HC11 Interrupts Emulation - Copyright 1999, 2000, 2001 Free Software Foundation, Inc. + Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Written by Stephane Carrez (stcarrez@worldnet.fr) This file is part of GDB, GAS, and the GNU binutils. @@ -19,6 +19,43 @@ along with this file; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sim-main.h" +#include "sim-options.h" + +static const char *interrupt_names[] = { + "R1", + "R2", + "R3", + "R4", + "R5", + "R6", + "R7", + "R8", + "R9", + "R10", + "R11", + + "SCI", + "SPI", + "AINPUT", + "AOVERFLOW", + "TOVERFLOW", + "OUT5", + "OUT4", + "OUT3", + "OUT2", + "OUT1", + "INC3", + "INC2", + "INC1", + "RT", + "IRQ", + "XIRQ", + "SWI", + "ILL", + "COPRESET", + "COPFAIL", + "RESET" +}; struct interrupt_def idefs[] = { /* Serial interrupts. */ @@ -45,6 +82,10 @@ struct interrupt_def idefs[] = { { M6811_INT_INCMP1, M6811_TFLG1, M6811_IC1F, M6811_TMSK1, M6811_IC1I }, { M6811_INT_INCMP2, M6811_TFLG1, M6811_IC2F, M6811_TMSK1, M6811_IC2I }, { M6811_INT_INCMP3, M6811_TFLG1, M6811_IC3F, M6811_TMSK1, M6811_IC3I }, + + /* Pulse accumulator. */ + { M6811_INT_AINPUT, M6811_TFLG2, M6811_PAIF, M6811_TMSK2, M6811_PAII }, + { M6811_INT_AOVERFLOW,M6811_TFLG2, M6811_PAOVF, M6811_TMSK2, M6811_PAOVI}, #if 0 { M6811_INT_COPRESET, M6811_CONFIG, M6811_NOCOP, 0, 0 }, { M6811_INT_COPFAIL, M6811_CONFIG, M6811_NOCOP, 0, 0 } @@ -54,16 +95,55 @@ struct interrupt_def idefs[] = { #define TableSize(X) (sizeof X / sizeof(X[0])) #define CYCLES_MAX ((((signed64) 1) << 62) - 1) -/* Initialize the interrupts of the processor. */ -int -interrupts_initialize (struct _sim_cpu *proc) +enum +{ + OPTION_INTERRUPT_INFO = OPTION_START, + OPTION_INTERRUPT_CATCH, + OPTION_INTERRUPT_CLEAR +}; + +static DECLARE_OPTION_HANDLER (interrupt_option_handler); + +static const OPTION interrupt_options[] = +{ + { {"interrupt-info", no_argument, NULL, OPTION_INTERRUPT_INFO }, + '\0', NULL, "Print information about interrupts", + interrupt_option_handler }, + { {"interrupt-catch", required_argument, NULL, OPTION_INTERRUPT_CATCH }, + '\0', "NAME[,MODE]", + "Catch interrupts when they are raised or taken\n" + "NAME Name of the interrupt\n" + "MODE Optional mode (`taken' or `raised')", + interrupt_option_handler }, + { {"interrupt-clear", required_argument, NULL, OPTION_INTERRUPT_CLEAR }, + '\0', "NAME", "No longer catch the interrupt", + interrupt_option_handler }, + + { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL } +}; + +/* Initialize the interrupts module. */ +void +interrupts_initialize (SIM_DESC sd, struct _sim_cpu *proc) { struct interrupts *interrupts = &proc->cpu_interrupts; - int i; interrupts->cpu = proc; + + sim_add_option_table (sd, 0, interrupt_options); +} + +/* Initialize the interrupts of the processor. */ +void +interrupts_reset (struct interrupts *interrupts) +{ + int i; + interrupts->pending_mask = 0; - interrupts->vectors_addr = 0xffc0; + if (interrupts->cpu->cpu_mode & M6811_SMOD) + interrupts->vectors_addr = 0xbfc0; + else + interrupts->vectors_addr = 0xffc0; interrupts->nb_interrupts_raised = 0; interrupts->min_mask_cycles = CYCLES_MAX; interrupts->max_mask_cycles = 0; @@ -78,12 +158,111 @@ interrupts_initialize (struct _sim_cpu *proc) { interrupts->interrupt_order[i] = i; } - return 0; + + /* Clear the interrupt history table. */ + interrupts->history_index = 0; + memset (interrupts->interrupts_history, 0, + sizeof (interrupts->interrupts_history)); + + memset (interrupts->interrupts, 0, + sizeof (interrupts->interrupts)); +} + +static int +find_interrupt (const char *name) +{ + int i; + + if (name) + for (i = 0; i < M6811_INT_NUMBER; i++) + if (strcasecmp (name, interrupt_names[i]) == 0) + return i; + + return -1; } +static SIM_RC +interrupt_option_handler (SIM_DESC sd, sim_cpu *cpu, + int opt, char *arg, int is_command) +{ + char *p; + int mode; + int id; + struct interrupts *interrupts; + + if (cpu == 0) + cpu = STATE_CPU (sd, 0); + + interrupts = &cpu->cpu_interrupts; + switch (opt) + { + case OPTION_INTERRUPT_INFO: + for (id = 0; id < M6811_INT_NUMBER; id++) + { + sim_io_eprintf (sd, "%-10.10s ", interrupt_names[id]); + switch (interrupts->interrupts[id].stop_mode) + { + case SIM_STOP_WHEN_RAISED: + sim_io_eprintf (sd, "catch raised "); + break; + + case SIM_STOP_WHEN_TAKEN: + sim_io_eprintf (sd, "catch taken "); + break; + + case SIM_STOP_WHEN_RAISED | SIM_STOP_WHEN_TAKEN: + sim_io_eprintf (sd, "catch all "); + break; + + default: + sim_io_eprintf (sd, " "); + break; + } + sim_io_eprintf (sd, "%ld\n", + interrupts->interrupts[id].raised_count); + } + break; + + case OPTION_INTERRUPT_CATCH: + p = strchr (arg, ','); + if (p) + *p++ = 0; + + mode = SIM_STOP_WHEN_RAISED; + id = find_interrupt (arg); + if (id < 0) + sim_io_eprintf (sd, "Interrupt name not recognized: %s\n", arg); + + if (p && strcasecmp (p, "raised") == 0) + mode = SIM_STOP_WHEN_RAISED; + else if (p && strcasecmp (p, "taken") == 0) + mode = SIM_STOP_WHEN_TAKEN; + else if (p && strcasecmp (p, "all") == 0) + mode = SIM_STOP_WHEN_RAISED | SIM_STOP_WHEN_TAKEN; + else if (p) + { + sim_io_eprintf (sd, "Invalid argument: %s\n", p); + break; + } + if (id >= 0) + interrupts->interrupts[id].stop_mode = mode; + break; + + case OPTION_INTERRUPT_CLEAR: + mode = SIM_STOP_WHEN_RAISED; + id = find_interrupt (arg); + if (id < 0) + sim_io_eprintf (sd, "Interrupt name not recognized: %s\n", arg); + else + interrupts->interrupts[id].stop_mode = 0; + break; + } + + return SIM_RC_OK; +} /* Update the mask of pending interrupts. This operation must be called - when the state of some 68HC11 IO registers changes. It looks the + when the state of some 68HC11 IO register changes. It looks the different registers that indicate a pending interrupt (timer, SCI, SPI, ...) and records the interrupt if it's there and enabled. */ void @@ -132,6 +311,35 @@ interrupts_update_pending (struct interrupts *interrupts) the interrupts before setting the new ones. */ interrupts->pending_mask &= ~clear_mask; interrupts->pending_mask |= set_mask; + + /* Keep track of when the interrupt is raised by the device. + Also implements the breakpoint-on-interrupt. */ + if (set_mask) + { + signed64 cycle = cpu_current_cycle (interrupts->cpu); + int must_stop = 0; + + for (i = 0; i < M6811_INT_NUMBER; i++) + { + if (!(set_mask & (1 << i))) + continue; + + interrupts->interrupts[i].cpu_cycle = cycle; + if (interrupts->interrupts[i].stop_mode & SIM_STOP_WHEN_RAISED) + { + must_stop = 1; + sim_io_printf (CPU_STATE (interrupts->cpu), + "Interrupt %s raised\n", + interrupt_names[i]); + } + } + if (must_stop) + sim_engine_halt (CPU_STATE (interrupts->cpu), + interrupts->cpu, + 0, cpu_get_pc (interrupts->cpu), + sim_stopped, + SIM_SIGTRAP); + } } @@ -251,7 +459,21 @@ interrupts_process (struct interrupts *interrupts) if (id >= 0) { uint16 addr; - + struct interrupt_history *h; + + /* Implement the breakpoint-on-interrupt. */ + if (interrupts->interrupts[id].stop_mode & SIM_STOP_WHEN_TAKEN) + { + sim_io_printf (CPU_STATE (interrupts->cpu), + "Interrupt %s will be handled\n", + interrupt_names[id]); + sim_engine_halt (CPU_STATE (interrupts->cpu), + interrupts->cpu, + 0, cpu_get_pc (interrupts->cpu), + sim_stopped, + SIM_SIGTRAP); + } + cpu_push_all (interrupts->cpu); addr = memory_read16 (interrupts->cpu, interrupts->vectors_addr + id * 2); @@ -267,6 +489,17 @@ interrupts_process (struct interrupts *interrupts) cpu_set_ccr_I (interrupts->cpu, 1); } + /* Update the interrupt history table. */ + h = &interrupts->interrupts_history[interrupts->history_index]; + h->type = id; + h->taken_cycle = cpu_current_cycle (interrupts->cpu); + h->raised_cycle = interrupts->interrupts[id].cpu_cycle; + + if (interrupts->history_index >= MAX_INT_HISTORY-1) + interrupts->history_index = 0; + else + interrupts->history_index++; + interrupts->nb_interrupts_raised++; cpu_add_cycles (interrupts->cpu, 14); return 1; @@ -281,12 +514,11 @@ interrupts_raise (struct interrupts *interrupts, enum M6811_INT number) interrupts->nb_interrupts_raised ++; } - - void interrupts_info (SIM_DESC sd, struct interrupts *interrupts) { signed64 t; + int i; sim_io_printf (sd, "Interrupts Info:\n"); sim_io_printf (sd, " Interrupts raised: %lu\n", @@ -300,21 +532,21 @@ interrupts_info (SIM_DESC sd, struct interrupts *interrupts) if (t > interrupts->max_mask_cycles) interrupts->max_mask_cycles = t; - sim_io_printf (sd, " Current interrupts masked sequence: %s\n", + sim_io_printf (sd, " Current interrupts masked sequence: %s\n", cycle_to_string (interrupts->cpu, t)); } t = interrupts->min_mask_cycles == CYCLES_MAX ? interrupts->max_mask_cycles : interrupts->min_mask_cycles; - sim_io_printf (sd, " Shortest interrupts masked sequence: %s\n", + sim_io_printf (sd, " Shortest interrupts masked sequence: %s\n", cycle_to_string (interrupts->cpu, t)); t = interrupts->max_mask_cycles; - sim_io_printf (sd, " Longest interrupts masked sequence: %s\n", + sim_io_printf (sd, " Longest interrupts masked sequence: %s\n", cycle_to_string (interrupts->cpu, t)); t = interrupts->last_mask_cycles; - sim_io_printf (sd, " Last interrupts masked sequence: %s\n", + sim_io_printf (sd, " Last interrupts masked sequence: %s\n", cycle_to_string (interrupts->cpu, t)); if (interrupts->xirq_start_mask_cycle >= 0) @@ -332,14 +564,50 @@ interrupts_info (SIM_DESC sd, struct interrupts *interrupts) t = interrupts->xirq_min_mask_cycles == CYCLES_MAX ? interrupts->xirq_max_mask_cycles : interrupts->xirq_min_mask_cycles; - sim_io_printf (sd, " XIRQ Min interrupts masked sequence: %s\n", + sim_io_printf (sd, " XIRQ Min interrupts masked sequence: %s\n", cycle_to_string (interrupts->cpu, t)); t = interrupts->xirq_max_mask_cycles; - sim_io_printf (sd, " XIRQ Max interrupts masked sequence: %s\n", + sim_io_printf (sd, " XIRQ Max interrupts masked sequence: %s\n", cycle_to_string (interrupts->cpu, t)); t = interrupts->xirq_last_mask_cycles; sim_io_printf (sd, " XIRQ Last interrupts masked sequence: %s\n", cycle_to_string (interrupts->cpu, t)); + + if (interrupts->pending_mask) + { + sim_io_printf (sd, " Pending interrupts : "); + for (i = 0; i < M6811_INT_NUMBER; i++) + { + enum M6811_INT int_number = interrupts->interrupt_order[i]; + + if (interrupts->pending_mask & (1 << int_number)) + { + sim_io_printf (sd, "%s ", interrupt_names[int_number]); + } + } + sim_io_printf (sd, "\n"); + } + + for (i = 0; i < MAX_INT_HISTORY; i++) + { + int which; + struct interrupt_history *h; + signed64 dt; + + which = interrupts->history_index - i - 1; + if (which < 0) + which += MAX_INT_HISTORY; + h = &interrupts->interrupts_history[which]; + if (h->taken_cycle == 0) + break; + + dt = h->taken_cycle - h->raised_cycle; + sim_io_printf (sd, "%2d %-10.10s %30.30s ", i, + interrupt_names[h->type], + cycle_to_string (interrupts->cpu, h->taken_cycle)); + sim_io_printf (sd, "%s\n", + cycle_to_string (interrupts->cpu, dt)); + } } diff --git a/sim/m68hc11/interrupts.h b/sim/m68hc11/interrupts.h index 69afa54..d29e577 100644 --- a/sim/m68hc11/interrupts.h +++ b/sim/m68hc11/interrupts.h @@ -1,5 +1,5 @@ /* interrupts.h -- 68HC11 Interrupts Emulation - Copyright 1999, 2000, 2001 Free Software Foundation, Inc. + Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Written by Stephane Carrez (stcarrez@worldnet.fr) This file is part of GDB, GAS, and the GNU binutils. @@ -79,6 +79,39 @@ struct interrupt_def unsigned char enabled_mask; }; +#define MAX_INT_HISTORY 64 + +/* Structure used to keep track of interrupt history. + This is used to understand in which order interrupts were + raised and when. */ +struct interrupt_history +{ + enum M6811_INT type; + + /* CPU cycle when interrupt handler is called. */ + signed64 taken_cycle; + + /* CPU cycle when the interrupt is first raised by the device. */ + signed64 raised_cycle; +}; + +#define SIM_STOP_WHEN_RAISED 1 +#define SIM_STOP_WHEN_TAKEN 2 + +/* Information and control of pending interrupts. */ +struct interrupt +{ + /* CPU cycle when the interrupt is raised by the device. */ + signed64 cpu_cycle; + + /* Number of times the interrupt was raised. */ + unsigned long raised_count; + + /* Controls whether we must stop the simulator. */ + int stop_mode; +}; + + /* Management of 68HC11 interrupts: - We use a table of 'interrupt_def' to describe the interrupts that must be raised depending on IO register flags (enable and present flags). @@ -102,6 +135,7 @@ struct interrupts { /* Priority order of interrupts. This is controlled by setting the HPRIO IO register. */ enum M6811_INT interrupt_order[M6811_INT_NUMBER]; + struct interrupt interrupts[M6811_INT_NUMBER]; /* Simulator statistics to report useful debug information to users. */ @@ -119,9 +153,15 @@ struct interrupts { /* - Total number of interrupts raised. */ unsigned long nb_interrupts_raised; + + /* Interrupt history to help understand which interrupts + were raised recently and in which order. */ + int history_index; + struct interrupt_history interrupts_history[MAX_INT_HISTORY]; }; -extern int interrupts_initialize (struct _sim_cpu* cpu); +extern void interrupts_initialize (SIM_DESC sd, struct _sim_cpu* cpu); +extern void interrupts_reset (struct interrupts* interrupts); extern void interrupts_update_pending (struct interrupts* interrupts); extern int interrupts_get_current (struct interrupts* interrupts); extern int interrupts_process (struct interrupts* interrupts); diff --git a/sim/m68hc11/m68hc11_sim.c b/sim/m68hc11/m68hc11_sim.c index 1beef6d..40dda42 100644 --- a/sim/m68hc11/m68hc11_sim.c +++ b/sim/m68hc11/m68hc11_sim.c @@ -1,5 +1,5 @@ /* m6811_cpu.c -- 68HC11&68HC12 CPU Emulation - Copyright 1999, 2000, 2001 Free Software Foundation, Inc. + Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Written by Stephane Carrez (stcarrez@worldnet.fr) This file is part of GDB, GAS, and the GNU binutils. @@ -643,8 +643,6 @@ cpu_move16 (sim_cpu *cpu, uint8 code) int cpu_initialize (SIM_DESC sd, sim_cpu *cpu) { - int result; - sim_add_option_table (sd, 0, cpu_options); memset (&cpu->cpu_regs, 0, sizeof(cpu->cpu_regs)); @@ -662,10 +660,10 @@ cpu_initialize (SIM_DESC sd, sim_cpu *cpu) cpu->cpu_use_local_config = 0; cpu->cpu_config = M6811_NOSEC | M6811_NOCOP | M6811_ROMON | M6811_EEON; - result = interrupts_initialize (cpu); + interrupts_initialize (sd, cpu); cpu->cpu_is_initialized = 1; - return result; + return 0; } @@ -702,6 +700,9 @@ cpu_reset (sim_cpu *cpu) cpu->cpu_current_cycle = 0; cpu->cpu_is_initialized = 0; + /* Reset interrupts. */ + interrupts_reset (&cpu->cpu_interrupts); + /* Reinitialize the CPU operating mode. */ cpu->ios[M6811_HPRIO] = cpu->cpu_mode; return 0; |