eventcallback.c 5.86 KB
/*
 * Copyright (C) 1996-1998 by the Board of Trustees
 *    of Leland Stanford Junior University.
 * 
 * This file is part of the SimOS distribution. 
 * See LICENSE file for terms of the license. 
 *
 */

/****************************************************************
 * eventcallback.c
 * 
 * Generic callback mechanism. 
 * 
 * $Author: blythe $
 * $Date: 2002/05/29 01:09:09 $
 *****************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "simutil.h"
#include "simtypes.h"
#include "sim_error.h"
#include "eventcallback.h"
#include "cpu_interface.h"
#include "machine_params.h"

/* Keep the asserts happy */

#define DEBUGLINE "%10u %-20s: rout %#x elm %#x arg %#x\n"


#ifdef notdef
#define EVENT_LOCK(_x)   { if( !CPUVec.singleEventQueue ) Simcp0Lock(  &(_x->lock)); }
#define EVENT_UNLOCK(_x) { if( !CPUVec.singleEventQueue ) Simcp0Unlock(&(_x->lock)); } 
#else
#define EVENT_LOCK(_x)
#define EVENT_UNLOCK(_x)
#endif

EventQueue *eventQueues = 0;

/*****************************************************************
 * EventDoCallback
 *****************************************************************/
void EventDoCallback(int cpuNum, void (*rout)(int,EventCallbackHdr *,void*),
		     EventCallbackHdr *elm, void *arg, SimTime timeInFuture) 
{ 
   SimTime when; 
   EventCallbackHdr *next;  
   EventQueue *queue;

   ASSERT( elm && CPUVec.CycleCount );
   ASSERT( !elm->active );
   ASSERT( (int64)timeInFuture >= 0);

   when = CPUVec.CycleCount(cpuNum) + timeInFuture;
   elm->rout = rout;
   elm->arg = arg;
   elm->active = TRUE;
   elm->cpuNum = cpuNum;
   if (CPUVec.singleEventQueue) { 
      queue = eventQueues;
   } else { 
      queue = eventQueues + cpuNum;
   }
   /*
    * This can happen in Embra MPinUP if the cycle counts
    * are off my more that the timeInFuture
    */
   if (when <= queue->lastCall && timeInFuture > 0) {
      when = queue->lastCall +1;
   }
   if (when < queue->lastCall && timeInFuture == 0) {
      when = queue->lastCall;
   }
   
   elm->when = when;
   ASSERT(when < SIM_MAX_TIME);
   EVENT_LOCK(queue);
   next = &queue->sentinel;
   while (1) {
      ASSERT( next->next );
      if (when < next->next->when) {
         elm->next = next->next;
         next->next = elm;
         break;
      } else { 
         next = next->next;
      }
   }
   queue->calltime = queue->sentinel.next->when;
   EVENT_UNLOCK(queue);
}

/*****************************************************************
 * EventProcess
 * cpuNum is ignored if all events are in the same queue
 *****************************************************************/
void
EventProcess(int cpuNum, SimTime now) 
{
    EventQueue *queue;
    EventCallbackHdr *h;
    if (CPUVec.singleEventQueue ) {
       queue = eventQueues;
       ASSERT( cpuNum < 0 );
    } else {
       queue = eventQueues + cpuNum;
       ASSERT( cpuNum >= 0 && cpuNum < TOTAL_CPUS );
    }
    h = &queue->sentinel;
    EVENT_LOCK(queue);
    ASSERT(h);
    if (queue->lastCall >=  now) { 
       CPUPrint("EventCallback: processing events at %lld. Prev time: %lld\n",
                (uint64)now, (uint64)queue->lastCall);
    }
    while (h->next->when <= now) {
       EventCallbackHdr *elm = h->next;
       /*
        * Make sure that the time is also up for the
        * particular CPU. If not, bypass the callback
        */
       if (elm->when  <= CPUVec.CycleCount(elm->cpuNum)) {
          h->next = h->next->next; 
          elm->active = FALSE;
          EVENT_UNLOCK(queue);
          elm->rout(elm->cpuNum, elm, elm->arg);
          EVENT_LOCK(queue);
          /* 
           * Restart from the top of the queue. Event might have been 
           * inserted during the processing
           */
          h = &queue->sentinel;
          /*
           * moved this here, so that it is updated only if
           * an event for the correct CPU is found 
           */ 
          queue->lastCall = now;
       } else {
          h=h->next;
       }
    }
    queue->calltime = queue->sentinel.next->when;
    EVENT_UNLOCK(queue);
}

/*****************************************************************
 * EventCallbackWaiting
 *****************************************************************/
bool
EventCallbackWaiting(EventCallbackHdr * event)
{
   return event->active;
}

/*****************************************************************
 * EventCallbackUpdate
 * 
 *****************************************************************/
void
EventCallbackUpdate(EventCallbackHdr * event, SimTime newtime)
{
   int cpuNum = event->cpuNum;
   ASSERT(event->active);
   ASSERT(newtime < event->when);
   
   EventCallbackRemove(event);
   EventDoCallback(cpuNum,event->rout,event, event->arg, newtime - 
                   CPUVec.CycleCount(cpuNum));
   
}


/*****************************************************************
 * EventCallbackRemove
 *
 *****************************************************************/
void
EventCallbackRemove(EventCallbackHdr * event)
{
   EventQueue *queue;
   int cpuNum = event->cpuNum;
   EventCallbackHdr *next;
   
   if (CPUVec.singleEventQueue ) {
      queue = eventQueues;
   } else { 
      queue = eventQueues+cpuNum;
   }
   EVENT_LOCK(queue);
   next = &queue->sentinel;
   while (next->next) {
      if( next->next ==  &queue->sentinel ) break;
      if (next->next == event) {
         next->next = event->next;
         event->active = FALSE;
         queue->calltime = queue->sentinel.next->when;
         EVENT_UNLOCK(queue);
         return;
      }
      next = next->next;
   }
   CPUWarning("EventCallbackRemove called on event not on list\n");
}


void EventCallbackInit(int numCPUs)
{
   int i;
   if (!eventQueues) {
      eventQueues = ZMALLOC(sizeof(EventQueue)*numCPUs,"EventCallback");
      ASSERT( eventQueues );
   }
   for(i=0;i<numCPUs;i++) {
      eventQueues[i].sentinel.next =  & eventQueues[i].sentinel;
      eventQueues[i].sentinel.when = SIM_MAX_TIME;
   }

}