|  | /* The common simulator framework for GDB, the GNU Debugger. | 
|  |  | 
|  | Copyright 2002-2023 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 |