238 lines
6.4 KiB
C
238 lines
6.4 KiB
C
|
/* Main simulator loop for CGEN-based simulators.
|
||
|
Copyright (C) 1998 Free Software Foundation, Inc.
|
||
|
Contributed by Cygnus Solutions.
|
||
|
|
||
|
This file is part of GDB, the GNU debugger.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 2, or (at your option)
|
||
|
any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License along
|
||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||
|
|
||
|
/* ??? These are old notes, kept around for now.
|
||
|
Collecting profile data and tracing slow us down so we don't do them in
|
||
|
"fast mode".
|
||
|
There are 6 possibilities on 2 axes:
|
||
|
- no-scaching, insn-scaching, basic-block-scaching
|
||
|
- run with full features or run fast
|
||
|
Supporting all six possibilities in one executable is a bit much but
|
||
|
supporting full/fast seems reasonable.
|
||
|
If the scache is configured in it is always used.
|
||
|
If pbb-scaching is configured in it is always used.
|
||
|
??? Sometimes supporting more than one set of semantic functions will make
|
||
|
the simulator too large - this should be configurable. Blah blah blah.
|
||
|
??? Supporting full/fast can be more modular, blah blah blah.
|
||
|
When the framework is more modular, this can be.
|
||
|
*/
|
||
|
|
||
|
#include "sim-main.h"
|
||
|
#include "sim-assert.h"
|
||
|
|
||
|
#ifndef SIM_ENGINE_PREFIX_HOOK
|
||
|
#define SIM_ENGINE_PREFIX_HOOK(sd)
|
||
|
#endif
|
||
|
#ifndef SIM_ENGINE_POSTFIX_HOOK
|
||
|
#define SIM_ENGINE_POSTFIX_HOOK(sd)
|
||
|
#endif
|
||
|
|
||
|
static sim_event_handler has_stepped;
|
||
|
static void prime_cpu (SIM_CPU *, int);
|
||
|
static void engine_run_1 (SIM_DESC, int, int);
|
||
|
static void engine_run_n (SIM_DESC, int, int, int, int);
|
||
|
|
||
|
/* sim_resume for cgen */
|
||
|
|
||
|
void
|
||
|
sim_resume (SIM_DESC sd, int step, int siggnal)
|
||
|
{
|
||
|
sim_engine *engine = STATE_ENGINE (sd);
|
||
|
jmp_buf buf;
|
||
|
int jmpval;
|
||
|
|
||
|
ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
||
|
|
||
|
/* we only want to be single stepping the simulator once */
|
||
|
if (engine->stepper != NULL)
|
||
|
{
|
||
|
sim_events_deschedule (sd, engine->stepper);
|
||
|
engine->stepper = NULL;
|
||
|
}
|
||
|
if (step)
|
||
|
engine->stepper = sim_events_schedule (sd, 1, has_stepped, sd);
|
||
|
|
||
|
sim_module_resume (sd);
|
||
|
|
||
|
#if WITH_SCACHE
|
||
|
if (USING_SCACHE_P (sd))
|
||
|
scache_flush (sd);
|
||
|
#endif
|
||
|
|
||
|
/* run/resume the simulator */
|
||
|
|
||
|
sim_engine_set_run_state (sd, sim_running, 0);
|
||
|
|
||
|
engine->jmpbuf = &buf;
|
||
|
jmpval = setjmp (buf);
|
||
|
if (jmpval == sim_engine_start_jmpval
|
||
|
|| jmpval == sim_engine_restart_jmpval)
|
||
|
{
|
||
|
int last_cpu_nr = sim_engine_last_cpu_nr (sd);
|
||
|
int next_cpu_nr = sim_engine_next_cpu_nr (sd);
|
||
|
int nr_cpus = sim_engine_nr_cpus (sd);
|
||
|
/* ??? Setting max_insns to 0 allows pbb/jit code to run wild and is
|
||
|
useful if all one wants to do is run a benchmark. Need some better
|
||
|
way to identify this case. */
|
||
|
int max_insns = (step
|
||
|
? 1
|
||
|
: (nr_cpus == 1
|
||
|
/*&& wip:no-events*/
|
||
|
/* Don't do this if running under gdb, need to
|
||
|
poll ui for events. */
|
||
|
&& STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE)
|
||
|
? 0
|
||
|
: 8); /*FIXME: magic number*/
|
||
|
int fast_p = STATE_RUN_FAST_P (sd);
|
||
|
|
||
|
sim_events_preprocess (sd, last_cpu_nr >= nr_cpus, next_cpu_nr >= nr_cpus);
|
||
|
if (next_cpu_nr >= nr_cpus)
|
||
|
next_cpu_nr = 0;
|
||
|
if (nr_cpus == 1)
|
||
|
engine_run_1 (sd, max_insns, fast_p);
|
||
|
else
|
||
|
engine_run_n (sd, next_cpu_nr, nr_cpus, max_insns, fast_p);
|
||
|
}
|
||
|
#if 1 /*wip*/
|
||
|
else
|
||
|
{
|
||
|
/* Account for the last insn executed. */
|
||
|
SIM_CPU *cpu = STATE_CPU (sd, sim_engine_last_cpu_nr (sd));
|
||
|
++ CPU_INSN_COUNT (cpu);
|
||
|
TRACE_INSN_FINI (cpu, NULL, 1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
engine->jmpbuf = NULL;
|
||
|
|
||
|
{
|
||
|
int i;
|
||
|
int nr_cpus = sim_engine_nr_cpus (sd);
|
||
|
|
||
|
#if 0 /*wip,ignore*/
|
||
|
/* If the loop exits, either we single-stepped or @cpu@_engine_stop
|
||
|
was called. */
|
||
|
if (step)
|
||
|
sim_engine_set_run_state (sd, sim_stopped, SIM_SIGTRAP);
|
||
|
else
|
||
|
sim_engine_set_run_state (sd, pending_reason, pending_sigrc);
|
||
|
#endif
|
||
|
|
||
|
for (i = 0; i < nr_cpus; ++i)
|
||
|
{
|
||
|
SIM_CPU *cpu = STATE_CPU (sd, i);
|
||
|
|
||
|
PROFILE_TOTAL_INSN_COUNT (CPU_PROFILE_DATA (cpu)) += CPU_INSN_COUNT (cpu);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sim_module_suspend (sd);
|
||
|
}
|
||
|
|
||
|
/* Halt the simulator after just one instruction. */
|
||
|
|
||
|
static void
|
||
|
has_stepped (SIM_DESC sd, void *data)
|
||
|
{
|
||
|
ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
||
|
sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP);
|
||
|
}
|
||
|
|
||
|
/* Prepare a cpu for running.
|
||
|
MAX_INSNS is the number of insns to execute per time slice.
|
||
|
If 0 it means the cpu can run as long as it wants (e.g. until the
|
||
|
program completes).
|
||
|
??? Perhaps this should be an argument to the engine_fn. */
|
||
|
|
||
|
static void
|
||
|
prime_cpu (SIM_CPU *cpu, int max_insns)
|
||
|
{
|
||
|
CPU_MAX_SLICE_INSNS (cpu) = max_insns;
|
||
|
CPU_INSN_COUNT (cpu) = 0;
|
||
|
|
||
|
/* Initialize the insn descriptor table.
|
||
|
This has to be done after all initialization so we just defer it to
|
||
|
here. */
|
||
|
|
||
|
if (MACH_PREPARE_RUN (CPU_MACH (cpu)))
|
||
|
(* MACH_PREPARE_RUN (CPU_MACH (cpu))) (cpu);
|
||
|
}
|
||
|
|
||
|
/* Main loop, for 1 cpu. */
|
||
|
|
||
|
static void
|
||
|
engine_run_1 (SIM_DESC sd, int max_insns, int fast_p)
|
||
|
{
|
||
|
sim_cpu *cpu = STATE_CPU (sd, 0);
|
||
|
ENGINE_FN *fn = fast_p ? CPU_FAST_ENGINE_FN (cpu) : CPU_FULL_ENGINE_FN (cpu);
|
||
|
|
||
|
prime_cpu (cpu, max_insns);
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
SIM_ENGINE_PREFIX_HOOK (sd);
|
||
|
|
||
|
(*fn) (cpu);
|
||
|
|
||
|
SIM_ENGINE_POSTFIX_HOOK (sd);
|
||
|
|
||
|
/* process any events */
|
||
|
if (sim_events_tick (sd))
|
||
|
sim_events_process (sd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Main loop, for multiple cpus. */
|
||
|
|
||
|
static void
|
||
|
engine_run_n (SIM_DESC sd, int next_cpu_nr, int nr_cpus, int max_insns, int fast_p)
|
||
|
{
|
||
|
int i;
|
||
|
ENGINE_FN *engine_fns[MAX_NR_PROCESSORS];
|
||
|
|
||
|
for (i = 0; i < nr_cpus; ++i)
|
||
|
{
|
||
|
SIM_CPU *cpu = STATE_CPU (sd, i);
|
||
|
|
||
|
engine_fns[i] = fast_p ? CPU_FAST_ENGINE_FN (cpu) : CPU_FULL_ENGINE_FN (cpu);
|
||
|
prime_cpu (cpu, max_insns);
|
||
|
}
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
SIM_ENGINE_PREFIX_HOOK (sd);
|
||
|
|
||
|
/* FIXME: proper cycling of all of them, blah blah blah. */
|
||
|
while (next_cpu_nr != nr_cpus)
|
||
|
{
|
||
|
SIM_CPU *cpu = STATE_CPU (sd, next_cpu_nr);
|
||
|
|
||
|
(* engine_fns[next_cpu_nr]) (cpu);
|
||
|
++next_cpu_nr;
|
||
|
}
|
||
|
|
||
|
SIM_ENGINE_POSTFIX_HOOK (sd);
|
||
|
|
||
|
/* process any events */
|
||
|
if (sim_events_tick (sd))
|
||
|
sim_events_process (sd);
|
||
|
}
|
||
|
}
|