401 lines
10 KiB
C
401 lines
10 KiB
C
|
/* This file is part of the program psim.
|
||
|
|
||
|
Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au>
|
||
|
|
||
|
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 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, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
#ifndef _SIM_EVENTS_C_
|
||
|
#define _SIM_EVENTS_C_
|
||
|
|
||
|
#include "sim-main.h"
|
||
|
#include "sim-assert.h"
|
||
|
|
||
|
#include <signal.h>
|
||
|
|
||
|
|
||
|
/* The event queue maintains a single absolute time using two
|
||
|
variables.
|
||
|
|
||
|
TIME_OF_EVENT: this holds the time at which the next event is ment
|
||
|
to occure. If no next event it will hold the time of the last
|
||
|
event.
|
||
|
|
||
|
TIME_FROM_EVENT: The current distance from TIME_OF_EVENT. If an
|
||
|
event is pending, this will be positive. If no future event is
|
||
|
pending this will be negative. This variable is decremented once
|
||
|
for each iteration of a clock cycle.
|
||
|
|
||
|
Initially, the clock is started at time one (0) with TIME_OF_EVENT
|
||
|
== 0 and TIME_FROM_EVENT == 0.
|
||
|
|
||
|
Clearly there is a bug in that this code assumes that the absolute
|
||
|
time counter will never become greater than 2^62.
|
||
|
|
||
|
To avoid the need to use 64bit arithmetic, the event queue always
|
||
|
contains at least one event scheduled every 16 000 ticks. This
|
||
|
limits the time from event counter to values less than
|
||
|
16 000. */
|
||
|
|
||
|
|
||
|
#if !defined (SIM_EVENTS_POLL_RATE)
|
||
|
#define SIM_EVENTS_POLL_RATE 0x4000
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#define _ETRACE sd
|
||
|
|
||
|
#undef ETRACE
|
||
|
#define ETRACE(ARGS) \
|
||
|
do \
|
||
|
{ \
|
||
|
if (WITH_TRACE) \
|
||
|
{ \
|
||
|
if (sd->events.trace) \
|
||
|
{ \
|
||
|
const char *file; \
|
||
|
SIM_FILTER_PATH(file, __FILE__); \
|
||
|
sim_io_printf (sd, "%s:%d: ", file, __LINE__); \
|
||
|
sim_io_printf ARGS; \
|
||
|
} \
|
||
|
} \
|
||
|
} \
|
||
|
while (0)
|
||
|
|
||
|
|
||
|
STATIC_INLINE_SIM_EVENTS\
|
||
|
(void)
|
||
|
sim_events_poll (void *data)
|
||
|
{
|
||
|
/* just re-schedule in 1000 million ticks time */
|
||
|
SIM_DESC sd = data;
|
||
|
sim_events_schedule(sd, SIM_EVENTS_POLL_RATE, sim_events_poll, sd);
|
||
|
sim_io_poll_quit (sd);
|
||
|
}
|
||
|
|
||
|
|
||
|
INLINE_SIM_EVENTS\
|
||
|
(void)
|
||
|
sim_events_init(SIM_DESC sd)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
sim_event *event;
|
||
|
|
||
|
/* drain the interrupt queue */
|
||
|
{
|
||
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
||
|
sigset_t old_mask;
|
||
|
sigset_t new_mask;
|
||
|
sigfillset(&new_mask);
|
||
|
/*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
|
||
|
#endif
|
||
|
event = events->held;
|
||
|
while (event != NULL) {
|
||
|
sim_event *dead = event;
|
||
|
event = event->next;
|
||
|
zfree(dead);
|
||
|
}
|
||
|
events->held = NULL;
|
||
|
events->held_end = &events->held;
|
||
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
||
|
/*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* drain the normal queue */
|
||
|
event = events->queue;
|
||
|
while (event != NULL) {
|
||
|
sim_event *dead = event;
|
||
|
event = event->next;
|
||
|
zfree(dead);
|
||
|
}
|
||
|
events->queue = NULL;
|
||
|
|
||
|
/* wind time back to zero */
|
||
|
events->processing = 0;
|
||
|
events->time_of_event = 0;
|
||
|
events->time_from_event = 0;
|
||
|
|
||
|
/* schedule our initial counter event */
|
||
|
sim_events_schedule(sd, 0, sim_events_poll, sd);
|
||
|
|
||
|
/* from now on, except when the large-int event is being processed
|
||
|
the event queue is non empty */
|
||
|
SIM_ASSERT(events->queue != NULL);
|
||
|
}
|
||
|
|
||
|
INLINE_SIM_EVENTS\
|
||
|
(signed64)
|
||
|
sim_events_time(SIM_DESC sd)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
return events->time_of_event - events->time_from_event;
|
||
|
}
|
||
|
|
||
|
STATIC_INLINE_SIM_EVENTS\
|
||
|
(void)
|
||
|
update_time_from_event(SIM_DESC sd)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
signed64 current_time = sim_events_time(sd);
|
||
|
if (events->queue != NULL) {
|
||
|
events->time_from_event = (events->queue->time_of_event - current_time);
|
||
|
events->time_of_event = events->queue->time_of_event;
|
||
|
}
|
||
|
else {
|
||
|
events->time_of_event = current_time - 1;
|
||
|
events->time_from_event = -1;
|
||
|
}
|
||
|
SIM_ASSERT(current_time == sim_events_time (sd));
|
||
|
SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
|
||
|
}
|
||
|
|
||
|
STATIC_INLINE_SIM_EVENTS\
|
||
|
(void)
|
||
|
insert_sim_event(SIM_DESC sd,
|
||
|
sim_event *new_event,
|
||
|
signed64 delta)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
sim_event *curr;
|
||
|
sim_event **prev;
|
||
|
signed64 time_of_event;
|
||
|
|
||
|
if (delta < 0)
|
||
|
engine_error (sd, "what is past is past!\n");
|
||
|
|
||
|
/* compute when the event should occure */
|
||
|
time_of_event = sim_events_time(sd) + delta;
|
||
|
|
||
|
/* find the queue insertion point - things are time ordered */
|
||
|
prev = &events->queue;
|
||
|
curr = events->queue;
|
||
|
while (curr != NULL && time_of_event >= curr->time_of_event) {
|
||
|
SIM_ASSERT(curr->next == NULL
|
||
|
|| curr->time_of_event <= curr->next->time_of_event);
|
||
|
prev = &curr->next;
|
||
|
curr = curr->next;
|
||
|
}
|
||
|
SIM_ASSERT(curr == NULL || time_of_event < curr->time_of_event);
|
||
|
|
||
|
/* insert it */
|
||
|
new_event->next = curr;
|
||
|
*prev = new_event;
|
||
|
new_event->time_of_event = time_of_event;
|
||
|
|
||
|
/* adjust the time until the first event */
|
||
|
update_time_from_event(sd);
|
||
|
}
|
||
|
|
||
|
INLINE_SIM_EVENTS\
|
||
|
(sim_event *)
|
||
|
sim_events_schedule(SIM_DESC sd,
|
||
|
signed64 delta_time,
|
||
|
sim_event_handler *handler,
|
||
|
void *data)
|
||
|
{
|
||
|
sim_event *new_event = ZALLOC(sim_event);
|
||
|
new_event->data = data;
|
||
|
new_event->handler = handler;
|
||
|
insert_sim_event(sd, new_event, delta_time);
|
||
|
ETRACE((_ETRACE,
|
||
|
"event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
|
||
|
(long)sim_events_time(sd),
|
||
|
(long)new_event,
|
||
|
(long)new_event->time_of_event,
|
||
|
(long)new_event->handler,
|
||
|
(long)new_event->data));
|
||
|
return new_event;
|
||
|
}
|
||
|
|
||
|
|
||
|
INLINE_SIM_EVENTS\
|
||
|
(sim_event *)
|
||
|
sim_events_schedule_after_signal(SIM_DESC sd,
|
||
|
signed64 delta_time,
|
||
|
sim_event_handler *handler,
|
||
|
void *data)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
sim_event *new_event = ZALLOC(sim_event);
|
||
|
|
||
|
new_event->data = data;
|
||
|
new_event->handler = handler;
|
||
|
new_event->time_of_event = delta_time; /* work it out later */
|
||
|
new_event->next = NULL;
|
||
|
|
||
|
{
|
||
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
||
|
sigset_t old_mask;
|
||
|
sigset_t new_mask;
|
||
|
sigfillset(&new_mask);
|
||
|
/*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
|
||
|
#endif
|
||
|
if (events->held == NULL) {
|
||
|
events->held = new_event;
|
||
|
}
|
||
|
else {
|
||
|
*events->held_end = new_event;
|
||
|
}
|
||
|
events->held_end = &new_event->next;
|
||
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
||
|
/*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
ETRACE((_ETRACE,
|
||
|
"event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
|
||
|
(long)sim_events_time(sd),
|
||
|
(long)new_event,
|
||
|
(long)new_event->time_of_event,
|
||
|
(long)new_event->handler,
|
||
|
(long)new_event->data));
|
||
|
|
||
|
return new_event;
|
||
|
}
|
||
|
|
||
|
|
||
|
INLINE_SIM_EVENTS\
|
||
|
(void)
|
||
|
sim_events_deschedule(SIM_DESC sd,
|
||
|
sim_event *event_to_remove)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
sim_event *to_remove = (sim_event*)event_to_remove;
|
||
|
SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
|
||
|
if (event_to_remove != NULL) {
|
||
|
sim_event *current;
|
||
|
sim_event **ptr_to_current;
|
||
|
for (ptr_to_current = &events->queue, current = *ptr_to_current;
|
||
|
current != NULL && current != to_remove;
|
||
|
ptr_to_current = ¤t->next, current = *ptr_to_current);
|
||
|
if (current == to_remove) {
|
||
|
*ptr_to_current = current->next;
|
||
|
ETRACE((_ETRACE,
|
||
|
"event descheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n",
|
||
|
(long)sim_events_time(sd),
|
||
|
(long)event_to_remove,
|
||
|
(long)current->time_of_event,
|
||
|
(long)current->handler,
|
||
|
(long)current->data));
|
||
|
zfree(current);
|
||
|
update_time_from_event(sd);
|
||
|
}
|
||
|
else {
|
||
|
ETRACE((_ETRACE,
|
||
|
"event descheduled at %ld - tag 0x%lx - not found\n",
|
||
|
(long)sim_events_time(sd),
|
||
|
(long)event_to_remove));
|
||
|
}
|
||
|
}
|
||
|
SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
INLINE_SIM_EVENTS\
|
||
|
(int)
|
||
|
sim_events_tick(SIM_DESC sd)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
|
||
|
/* we should only be here when the previous tick has been fully
|
||
|
processed */
|
||
|
SIM_ASSERT(!events->processing && events->queue != NULL);
|
||
|
|
||
|
/* Advance the time but *only* if there is nothing to process */
|
||
|
if (events->time_from_event == 0)
|
||
|
return 1;
|
||
|
else if (events->held != NULL)
|
||
|
return 1;
|
||
|
else {
|
||
|
events->time_from_event -= 1;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
INLINE_SIM_EVENTS\
|
||
|
(void)
|
||
|
sim_events_process(SIM_DESC sd)
|
||
|
{
|
||
|
sim_events *events = &sd->events;
|
||
|
signed64 event_time = sim_events_time(sd);
|
||
|
|
||
|
/* something to do */
|
||
|
SIM_ASSERT(events->time_from_event == 0 || events->held != NULL);
|
||
|
SIM_ASSERT(events->queue != NULL);
|
||
|
|
||
|
/* move any events that were queued by any signal handlers onto the
|
||
|
real event queue. */
|
||
|
if (events->held != NULL) {
|
||
|
sim_event *held_events;
|
||
|
sim_event *curr_event;
|
||
|
|
||
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
||
|
/*-LOCK-*/
|
||
|
sigset_t old_mask;
|
||
|
sigset_t new_mask;
|
||
|
sigfillset(&new_mask);
|
||
|
sigprocmask(SIG_SETMASK, &new_mask, &old_mask);
|
||
|
#endif
|
||
|
|
||
|
held_events = events->held;
|
||
|
events->held = NULL;
|
||
|
events->held_end = &events->held;
|
||
|
|
||
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
||
|
/*-UNLOCK-*/
|
||
|
sigprocmask(SIG_SETMASK, &old_mask, NULL);
|
||
|
#endif
|
||
|
|
||
|
do {
|
||
|
curr_event = held_events;
|
||
|
held_events = curr_event->next;
|
||
|
insert_sim_event(sd, curr_event, curr_event->time_of_event);
|
||
|
} while (held_events != NULL);
|
||
|
}
|
||
|
|
||
|
/* consume all events for this or earlier times. Be careful to
|
||
|
allow a new event to appear under our feet */
|
||
|
events->processing = 1;
|
||
|
while (events->queue->time_of_event <= event_time) {
|
||
|
sim_event *to_do = events->queue;
|
||
|
sim_event_handler *handler = to_do->handler;
|
||
|
void *data = to_do->data;
|
||
|
events->queue = to_do->next;
|
||
|
ETRACE((_ETRACE,
|
||
|
"event issued at %ld - tag 0x%lx - handler 0x%lx, data 0x%lx\n",
|
||
|
(long)event_time,
|
||
|
(long)to_do,
|
||
|
(long)handler,
|
||
|
(long)data));
|
||
|
zfree (to_do);
|
||
|
handler (data);
|
||
|
}
|
||
|
events->processing = 0;
|
||
|
|
||
|
/* re-caculate time for new events - advance the time */
|
||
|
update_time_from_event(sd);
|
||
|
SIM_ASSERT(events->time_from_event > 0 && events->queue != NULL);
|
||
|
events->time_from_event -= 1;
|
||
|
}
|
||
|
|
||
|
#endif
|