dash_prefetch.c 7.59 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. 
 *
 */

/*****************************************************************
 * dash_prefetch.c
 *
 * Since the f77 compiler cannot issue prefetch opcodes, we 
 * simulate the effect through the use of a shadow array. 
 * The parameters that determine the mapping between shadow
 * and effective arrays are set by Tcl commands. This style of
 * prefetch was commonly used on dash.
 * 
 * The Tcl interface is as follows
 * dashPrefetch enable
 * dashPrefetch disable
 * dashPrefetch (shared|exlcusive) baseAddr shadowAddr
 *****************************************************************/

#include <string.h>
#include "tcl_init.h"
#include "cpu_interface.h"
#include "hw_events.h"
#include "sim_error.h"
#include "memref.h"
#include "cp0.h"
#include "cpu.h"
#include "cpu_stats.h"

#define DASH_MAXENTRIES 512
#define DASH_ENCODING 10000
#define PF_NOTMAPPED (PF_NUMSTATS)

static struct DashPrefetchState {
   VA shadowStart;
   VA shadowEnd;
   int arrayEntries;
   int except;
   VA arrayShadowStart[DASH_MAXENTRIES];
   VA arrayStart[DASH_MAXENTRIES];
   
   int64 issued[DASH_MAXENTRIES];
   int64 stat[DASH_MAXENTRIES][PF_NUMSTATS+1];
   
   SimTime nextOutput;
} dashState;

int dashPrefetchEnabled = 0;

/*****************************************************************
 * prefetch command 
 *****************************************************************/
int 
DashPrefetch(int cpuNum, VA shadowAddr, void *vPtr, int size, int *retVal) 
{
   uint offset;
   int ret, id, typeSize;
   VA vAddr;
   PA pAddr=0;
   int exclusive;
   uint tlbFlavor;
   void *bdoorAddr = 0;
   if (simosCPUType != MIPSY) {
      return 0;
   }
   if (shadowAddr < dashState.shadowStart || 
       shadowAddr >= dashState.shadowEnd) {
      return 0;  /* no prefetch */
   }
   
   /*
    * now, size should always be 4
    */
   ASSERT (size==4);
   typeSize = (*(int*)vPtr) / DASH_ENCODING;
   id = (*(int*)vPtr) % DASH_ENCODING;
   ASSERT (typeSize==4 || typeSize==8);
   exclusive = (id%2);
   id = id / 2;
   
   /* 
    * debug stats
    */
   if (CPUVec.CycleCount(cpuNum) >= dashState.nextOutput) {
      int i,j; 
      CPUPrint("\n\n PREFETCH STATS at %lld cycles \n", CPUVec.CycleCount(cpuNum));
      for (i=0;i<DASH_MAXENTRIES;i++) {
         if (dashState.issued[i]) { 
            double x[PF_NUMSTATS+1];
            for (j=0; j<PF_NUMSTATS+1; j++) {
               x[j] = 100.0 * dashState.stat[i][j] 
                  / dashState.issued[i];
            }
            CPUPrint("PREFETCH  id=%3d  addr=0x%08x Issued=%10lld  res=%6.2f%% merg=%6.2f%% fetch=%6.2f%% failure=%6.2f%%  upg=%6.2f%% notmapped=%6.2f%%\n",
                     i,
                      dashState.arrayStart[i],
                      dashState.issued[i],
                      x[ PF_RESIDENT],x[PF_MERGE],x[PF_STALL],
                      x[PF_FAILURE],x[PF_UPGRADE],x[PF_NOTMAPPED]);
         }
      }
      dashState.nextOutput +=  32* 1024 * 1024 ;
   }
   
   ASSERT (id >= 0 && id < DASH_MAXENTRIES);
   if (!dashState.arrayStart[id]) {
      CPUError("Shadow entry %d not defined \n",id);
   } 
   offset = shadowAddr -  dashState.arrayShadowStart[id];
   if (typeSize==8) {
      offset *=2;
   }

   vAddr = dashState.arrayStart[id] + offset;
   
   PREFETCH_ISSUE_EVENT(cpuNum,PE[cpuNum].PC,vAddr);
   dashState.issued[id]++;

   if (dashState.except) { 
      tlbFlavor = TLB_DFETCH;
   } else {
      tlbFlavor = TLB_DFETCH | TLB_PREFETCH; 
   } 

   tlbFlavor = TLB_DFETCH | TLB_PREFETCH;
   ret = TranslateVirtual(&PE[cpuNum], vAddr, &pAddr, &tlbFlavor,&bdoorAddr);

   if (ret == SUCCESS) {
      if (exclusive) { 
         if (MemRefPrefetch(cpuNum, vAddr, pAddr, 1)) {
            ret = PF_FAILURE;
         } else {
            ret = PF_SUCCESS;
         }
      } else { 
         if (MemRefPrefetch(cpuNum, vAddr, pAddr, 0)) {
            ret = PF_FAILURE;
         } else {
            ret = PF_SUCCESS;
         }
      } 

      if (interest(pAddr)) {
         DebugDetail('g', "dash-pf",cpuNum,
                     "pAddr=0x%x vAddr=0x%x, exclusive=%d ret=%d \n",
                     pAddr,vAddr,exclusive,ret);
      }
      
      dashState.stat[id][ret]++;
      if (ret == PF_FAILURE) { 
         /* Think of a way to do this commonly to all cpus. */
         STATS_INC(cpuNum, prefStats.prefFails, 1);
         PE[cpuNum].cpuStatus = cpu_stalled;
         PE[cpuNum].stalledInst = 0; 
         STATS_SET(cpuNum, stallStart, CPUVec.CycleCount(cpuNum));
      } else {
         ret = SUCCESS;
      }
   } else { 
      if (!dashState.except) { 
         ret = SUCCESS;
      }
      dashState.stat[id][PF_NOTMAPPED]++;
      PREFETCH_FAILED_TRANS(cpuNum,PE[cpuNum].PC,vAddr);      
   }
   *retVal = ret;
   return 1; /* was a prefetch, ignore store instruction */
}

/*****************************************************************
 * Tcl interface to dash_prefetch 
 *****************************************************************/
int 
DashTclCmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
   int x[3];

   if (simosCPUType != MIPSY) {
      Tcl_AppendResult(interp, 
                       "Can only use dashPrefetch in mipsy\n", 
                       NULL);
      return TCL_ERROR;
 
   }

   if (argc < 2) { 
      Tcl_AppendResult(interp, 
                       "Use dashPrefetch [enable|disable|shadow|define|exception]  \n", 
                       NULL);
      return TCL_ERROR;
   }

   if (!strcmp(argv[1], "enable")) {
      if (dashState.shadowStart < dashState.shadowEnd) {
         dashPrefetchEnabled = 1;
         return TCL_OK;
      } else { 
         Tcl_AppendResult(interp, 
                          "dashPrefetch not initialized \n", NULL);
         return TCL_ERROR;       
      }
   }
   if (!strcmp(argv[1],"shadow")) {
      
      if (argc<4) {
         Tcl_AppendResult(interp, 
                          "Use dashPrefetch shadow start size \n", NULL);
         return TCL_ERROR;  
      }
      if (Tcl_GetInt(interp, argv[2], &x[0]) != TCL_OK ||
          Tcl_GetInt(interp, argv[3], &x[1]) != TCL_OK ) {
         Tcl_AppendResult(interp, 
                          "Use dashPrefetch shadow start end \n", NULL);
         return TCL_ERROR;   
      }
      dashState.shadowStart = x[0];
      dashState.shadowEnd = x[0] + x[1];
      return TCL_OK;
   }
   
   if (!strcmp(argv[1],"disable")) {
      dashPrefetchEnabled = 0;
      return TCL_OK;
   }
   if (!strcmp(argv[1],"exception")) {
      dashState.except =  1;
      CPUWarning("DASH PREFETCH: excepting on TLB misses \n");
      return TCL_OK;
   }
   if (!strcmp(argv[1],"define")) {      
      if (argc < 5) { 
         Tcl_AppendResult(interp, 
                          "Use dashPrefetch [define] id baseAddr shadowAddr \n", NULL);
         return TCL_ERROR;
      }
      if (Tcl_GetInt(interp, argv[2], &x[0]) != TCL_OK || 
          Tcl_GetInt(interp, argv[3], &x[1]) != TCL_OK ||
          Tcl_GetInt(interp, argv[4], &x[2]) != TCL_OK  ) {
         Tcl_AppendResult(interp, 
                          "Use dashPrefetch define id baseAddr shadowAddr \n", NULL);
         return TCL_ERROR;
      }
      if (x[0] < 0 || x[0] >DASH_MAXENTRIES) {
         Tcl_AppendResult(interp, 
                          "dashPrefetch id must in 0-255 range \n", NULL);
         return TCL_ERROR; 
      }
      dashState.arrayStart[x[0]] = x[1];
      dashState.arrayShadowStart[x[0]] = x[2];
      return TCL_OK;
   }
   Tcl_AppendResult(interp, 
                    "Use dashPrefetch [enable|disable|define] \n", NULL);
   return TCL_ERROR;
}