* 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.
This commit is contained in:
Stephane Carrez 2002-03-07 18:59:38 +00:00
parent aa066ac86a
commit 261289656f
4 changed files with 354 additions and 24 deletions

View File

@ -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

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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;