simos_interface.c 6.53 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. 
 *
 */

/*****************************************************************
 * simos_interface.c - SimOS interface to the HP disk simulator. 
 * Always runs on thread 0. 
 * 
 * $Author: blythe $
 * $Date: 2002/05/29 01:09:11 $
 *****************************************************************/

#include "sim.h"
#include "simtypes.h"
#include "simos_interface.h"

#include "diskdevice.h"
#include "diskdevices.h"
#include "modularize.h"
#include "queue.h"
#include "eventcallback.h"
#include "hd.h"
#include "cpu_interface.h"
#include "heap.h"
#include "simutil.h"
#include "sim_error.h"
#include "machine_params.h"



Heap heap;			/* the event heap */
double diskTICSperMSEC;
typedef struct DiskModelCallback{ 
    EventCallbackHdr hdr; /* Event header of the callback */
    int diskNum;
    int sector;
    int nsectors; 
    int iswrite;
    dmaRoutine_t dmaRoutine;
    void *dmaArg;
    doneRoutine_t doneRoutine;
    int doneArg;
} DiskModelCallback;

DiskModelCallback *diskModelCallback;
EventCallbackHdr *diskModelEhdr;

#define BQSIZE    15		/* size of bus queue */ 

/*
 * DiskModelInit - Initialize the HP disk model. We allow the user to 
 *                 specify the MAX number of disks being models and 
 *                 how the time of the disk model should be scaled. 
 *                 A scaling factor of 1.0 gives realistic timings. 
 */

void
DiskModelInit(int maxNumDisks, double scaling)
{
    int diskNum;
    int *busOwner;
    NCQUEUE *busWaitq;

    if (scaling == 0) {
       CPUError("HD: Can't run HP disk at scaling of 0... use fixed model.\n");
    }

    /*  diskTICSperMSEC = (scaling*1000.0*1000.0)/(double)simConfigNsecPerInstr; */
    SIMASSERT(  CPU_CLOCK );
    diskTICSperMSEC = (scaling * 1000.0 * CPU_CLOCK) ;
    

    InitTime();		            /* initialize the global clock */


    busOwner = (int *) MALLOC(sizeof(int),"busOwner");
    *busOwner = BUS_FREE;
    busWaitq = MakeQueue_noncyc(BQSIZE, "bus queue");

    for (diskNum = 0; diskNum < maxNumDisks; diskNum++) {
       DiskDeviceInit(diskNum, diskNum, busOwner, busWaitq, NULL);
    }       

    /* Currently we model each disk as having its own SCSI bus.  */
    diskModelEhdr = (EventCallbackHdr *) 
       ZMALLOC(sizeof(EventCallbackHdr),"DiskModelEHdr");
    
    diskModelCallback = (DiskModelCallback *) 
       ZMALLOC(sizeof(DiskModelCallback)*SIM_MAX_DISKS, "DiskModelCallback");

    ASSERT( diskModelCallback );
}


 
/*
 * DiskModelRequest - Start the modeling of a DISK request. We allow the user 
 *                    to provide routines to handle the data as it comes over 
 *                    the SCSI bus (dmaRoutine) and the compleition response
 *                    (doneroutine).
 * Because we do not want to have to deal with shared memory in MP embra, 
 * we run all of the disk model requests on CPU 0. The DMA module is shared 
 * memory mapped. The final callback interrupts the CPU that requested the 
 * transfer in the first place 
 */

static void 
DiskModelStartCallback(int cpuNum,EventCallbackHdr *hdr,void *arg)
{
    TICS begintime = CPUVec.CycleCount(0);
    DiskModelCallback *cback = (DiskModelCallback *)hdr;
    /*
     * Advance the simulator's time to the current time and launch the request. 
     */
    WaitTime(begintime);  
    DiskDeviceTransferStart(cback->diskNum, 
                            cback->sector, 
                            cback->nsectors, 
                            cback->iswrite, 
                            cback->dmaRoutine,    
                            cback->dmaArg, 
                            cback->doneRoutine, 
                            cback->doneArg);
    
}

void
DiskModelRequest(int machine, int diskNum, int sector, int nsectors, 
		 int iswrite,  dmaRoutine_t dmaRoutine,
                 doneRoutine_t doneRoutine, int doneArg)
{
    DiskModelCallback *cback = diskModelCallback+diskNum;
    ASSERT( diskModelCallback );

    /* get all of the parameter in the callback. Have it handled by CPU 0
       of the machine */
    cback->diskNum = diskNum;
    cback->sector = sector;
    cback->nsectors = nsectors;
    cback->iswrite = iswrite;
    cback->dmaRoutine = dmaRoutine;
    cback->doneRoutine = doneRoutine;
    cback->doneArg = doneArg;
    /* pass over message to CPU 0 of the machine */
    EventDoCallback(FIRST_CPU(machine), DiskModelStartCallback,
                    (EventCallbackHdr *)cback,0,0);
}


/*
 * The following code and data interfaces the event callback mechanism of 
 * the disk model with the simos Eventcallback mod. This is done by 
 * recording a EventCallback that corresponds to the ROOT of the callback
 * Heap maintained by the disk simulator. 
 */
static void RecordCallback(int cpuNum,EventCallbackHdr *ptr, void *arg);

static TICS lastregtime;      /* Time of registered callback */

void
UpdateEventCallback(TICS tics)
{ 
      TICS regtime = tics+1;
      if (!EventCallbackActive(diskModelEhdr)) { 
	  /* If we don't have a callback registers - set one. */
	  SimTime time = CPUVec.CycleCount(0);
          if (time >= regtime) {
             /* This can happen in Embra because the callbacks can be delayed. */
             regtime = time+1;
          }
          EventDoCallback(FIRST_CPU(M_FROM_CPU(diskModelEhdr->cpuNum)),
                          RecordCallback,diskModelEhdr, (void *) 0,
                          regtime - time);
	  lastregtime = regtime;
      } else {
	  SimTime  time = CPUVec.CycleCount(0);
          if (time >= regtime) {
             /* This can happen in Embra because the callbacks can be delayed. */
             regtime = time+1;
          }
          if (regtime < lastregtime) {
             /* If we have one register's too far in the future - Update it. */
             EventCallbackUpdate(diskModelEhdr,regtime);
             lastregtime = regtime;
          } else {
             /* Update wasn't to the root of the heap */
          }
      }
}

static void
RecordCallback(int cpuNum,EventCallbackHdr *ptr, void *arg)
{
    HeapKey Hpkey;
    HeapData Hpdat;
    TICS current_time = CPUVec.CycleCount(0);

    /*
     * Update the disk's simulated time to the current time  and 
     * re-register the top of the heap if needed. 
     */

    WaitTime(current_time);
    if (TopHeap(heap, &Hpdat, &Hpkey) != FALSE) {
       UpdateEventCallback(Hpkey);
    }
}

void
AdvanceTime(void)
{
    TICS begintime = CPUVec.CycleCount(0);
    WaitTime(begintime);  /* Update simulators time */
}