293 lines
7.1 KiB
C
293 lines
7.1 KiB
C
/* The common simulator framework for GDB, the GNU Debugger.
|
|
|
|
Copyright 2002-2022 Free Software Foundation, Inc.
|
|
|
|
Contributed by Andrew Cagney and Red Hat.
|
|
|
|
This file is part of GDB.
|
|
|
|
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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
#ifndef SIM_EVENTS_H
|
|
#define SIM_EVENTS_H
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "ansidecl.h"
|
|
|
|
/* Notes:
|
|
|
|
When scheduling an event, the a delta of zero/one refers to the
|
|
timeline as follows:
|
|
|
|
epoch 0|1 1|2 2|3 3|
|
|
**queue**|--insn--|*queue*|--insn--|*queue*|--insn--|*queue*|
|
|
| ^ ^ | ^ ^
|
|
`- +0 ------------ +1 --.. `----- +0 ------------- +1 --..
|
|
|
|
When the queue is initialized, the time is set to zero with a
|
|
number of initialization events scheduled. Consequently, as also
|
|
illustrated above, the event queue should be processed before the
|
|
first instruction. That instruction being executed during tick 1.
|
|
|
|
The simulator main loop may take a form similar to:
|
|
|
|
if (halt-/restart-setjmp)
|
|
{
|
|
|
|
.... // Determine who should go next
|
|
last-cpu-nr = get-last-cpu-nr (sd);
|
|
next-cpu-nr = get-next-cpu-nr (sd);
|
|
events-were-last? = (last-cpu-nr >= nr-cpus);
|
|
events-were-next? = (next-cpu-nr >= nr-cpus);
|
|
|
|
.... // process any outstanding events
|
|
sim_events_preprocess (sd, events-were-last?, events-were-next?);
|
|
if (events-were-next)
|
|
next-cpu-nr = 0;
|
|
|
|
.... // prime main loop
|
|
|
|
while (1)
|
|
{
|
|
.... // model one insn of next-cpu-nr .. nr-cpus
|
|
if (sim_events_tick (sd))
|
|
sim_events_process (sd);
|
|
next-cpu-nr = 0
|
|
}
|
|
}
|
|
|
|
NB. In the above pseudo code it is assumed that any cpu-nr >=
|
|
nr-cpus is a marker for the event queue. */
|
|
|
|
|
|
typedef void sim_event_handler(SIM_DESC sd, void *data);
|
|
|
|
typedef struct _sim_event sim_event;
|
|
|
|
typedef struct _sim_events sim_events;
|
|
struct _sim_events {
|
|
int nr_ticks_to_process;
|
|
sim_event *queue;
|
|
sim_event *watchpoints;
|
|
sim_event *watchedpoints;
|
|
sim_event *free_list;
|
|
/* flag additional work needed */
|
|
volatile int work_pending;
|
|
/* the asynchronous event queue */
|
|
#ifndef MAX_NR_SIGNAL_SIM_EVENTS
|
|
#define MAX_NR_SIGNAL_SIM_EVENTS 2
|
|
#endif
|
|
sim_event *held;
|
|
volatile int nr_held;
|
|
/* timekeeping */
|
|
unsigned long elapsed_wallclock;
|
|
SIM_ELAPSED_TIME resume_wallclock;
|
|
int64_t time_of_event;
|
|
int64_t time_from_event;
|
|
};
|
|
|
|
|
|
|
|
/* Install the "events" module. */
|
|
|
|
extern SIM_RC sim_events_install (SIM_DESC sd);
|
|
|
|
|
|
/* Schedule an event DELTA_TIME ticks into the future */
|
|
|
|
extern sim_event *sim_events_schedule
|
|
(SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data);
|
|
|
|
extern sim_event *sim_events_schedule_tracef
|
|
(SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data,
|
|
const char *fmt,
|
|
...) ATTRIBUTE_NULL_PRINTF (5, 6);
|
|
|
|
extern sim_event *sim_events_schedule_vtracef
|
|
(SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data,
|
|
const char *fmt,
|
|
va_list ap) ATTRIBUTE_NULL_PRINTF (5, 0);
|
|
|
|
|
|
extern void sim_events_schedule_after_signal
|
|
(SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data);
|
|
|
|
/* NB: signal level events can't have trace strings as malloc isn't
|
|
available */
|
|
|
|
|
|
|
|
/* Schedule an event milli-seconds from NOW. The exact interpretation
|
|
of wallclock is host dependant. */
|
|
|
|
extern sim_event *sim_events_watch_clock
|
|
(SIM_DESC sd,
|
|
unsigned delta_ms_time,
|
|
sim_event_handler *handler,
|
|
void *data);
|
|
|
|
|
|
/* Schedule an event when a PC matches a range. */
|
|
|
|
extern sim_event *sim_events_watch_pc
|
|
(SIM_DESC sd,
|
|
int is_within,
|
|
uint64_t lb,
|
|
uint64_t ub,
|
|
sim_event_handler *handler,
|
|
void *data);
|
|
|
|
|
|
/* Schedule an event when the test (IS_WITHIN == (VAL >= LB && VAL <=
|
|
UB)) of the NR_BYTES value at HOST_ADDR with BYTE_ORDER endian is
|
|
true.
|
|
|
|
HOST_ADDR: pointer into the host address space.
|
|
BYTE_ORDER: BFD_ENDIAN_UNKNOWN - host endian; BFD_ENDIAN_BIG;
|
|
BFD_ENDIAN_LITTLE. */
|
|
|
|
extern sim_event *sim_events_watch_sim
|
|
(SIM_DESC sd,
|
|
void *host_addr,
|
|
int nr_bytes,
|
|
enum bfd_endian byte_order,
|
|
int is_within,
|
|
uint64_t lb,
|
|
uint64_t ub,
|
|
sim_event_handler *handler,
|
|
void *data);
|
|
|
|
|
|
/* Schedule an event when the test (IS_WITHIN == (VAL >= LB && VAL <=
|
|
UB)) of the NR_BYTES value at CORE_ADDR in BYTE_ORDER endian is
|
|
true.
|
|
|
|
CORE_ADDR/MAP: pointer into the target address space.
|
|
BYTE_ORDER: BFD_ENDIAN_UNKNOWN - host endian; BFD_ENDIAN_BIG;
|
|
BFD_ENDIAN_LITTLE. */
|
|
|
|
extern sim_event *sim_events_watch_core
|
|
(SIM_DESC sd,
|
|
address_word core_addr,
|
|
unsigned map,
|
|
int nr_bytes,
|
|
enum bfd_endian byte_order,
|
|
int is_within,
|
|
uint64_t lb,
|
|
uint64_t ub,
|
|
sim_event_handler *handler,
|
|
void *data);
|
|
|
|
/* Deschedule the specified event */
|
|
|
|
extern void sim_events_deschedule
|
|
(SIM_DESC sd,
|
|
sim_event *event_to_remove);
|
|
|
|
|
|
/* Prepare for main simulator loop. Ensure that the next thing to do
|
|
is not event processing.
|
|
|
|
If the simulator halted part way through event processing then both
|
|
EVENTS_WERE_LAST and EVENTS_WERE_NEXT shall be true.
|
|
|
|
If the simulator halted after processing the last cpu, then only
|
|
EVENTS_WERE_NEXT shall be true. */
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(void) sim_events_preprocess
|
|
(SIM_DESC sd,
|
|
int events_were_last,
|
|
int events_were_next);
|
|
|
|
|
|
/* Progress time.
|
|
|
|
Separated into two parts so that the main loop can save its context
|
|
before the event queue is processed. When sim_events_tick*()
|
|
returns true, any simulation context should be saved and
|
|
sim_events_process() called.
|
|
|
|
SIM_EVENTS_TICK advances the clock by 1 cycle.
|
|
|
|
SIM_EVENTS_TICKN advances the clock by N cycles (1..MAXINT). */
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(int) sim_events_tick
|
|
(SIM_DESC sd);
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(int) sim_events_tickn
|
|
(SIM_DESC sd,
|
|
int n);
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(void) sim_events_process
|
|
(SIM_DESC sd);
|
|
|
|
|
|
/* Advance the clock by an additional SLIP cycles at the next call to
|
|
sim_events_tick*(). For multiple calls, the effect is
|
|
accumulative. */
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(void) sim_events_slip
|
|
(SIM_DESC sd,
|
|
int slip);
|
|
|
|
|
|
/* Progress time such that an event shall occur upon the next call to
|
|
sim_events tick */
|
|
|
|
#if 0
|
|
INLINE_SIM_EVENTS\
|
|
(void) sim_events_timewarp
|
|
(SIM_DESC sd);
|
|
#endif
|
|
|
|
|
|
/* local concept of elapsed target time */
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(int64_t) sim_events_time
|
|
(SIM_DESC sd);
|
|
|
|
|
|
/* local concept of elapsed host time (milliseconds) */
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(unsigned long) sim_events_elapsed_time
|
|
(SIM_DESC sd);
|
|
|
|
/* Returns the time that remains before the event is raised. */
|
|
INLINE_SIM_EVENTS\
|
|
(int64_t) sim_events_remain_time
|
|
(SIM_DESC sd, sim_event *event);
|
|
|
|
|
|
#endif
|