#include "interface.h"  // Do NOT edit interface .h
#include "sample_prefetcher.h"

//#include "base.h"
//#include "mshr.h"
//#include "pref_mshr.h"
//#include "accessmap.h"
//#include "adaptive.h"

// Storages.

// MSHR for demand requests(0 bits)
MissStatusHandlingRegister *mshr;

// MSHR for prefetch requests(901 bits)
PrefetchMissStatusHandlingRegister *mshrpf;

// Memory Access Map Table(29147 bits)
MemoryAccessMapTable *pref;

// Adaptive Stream Prefetcher(1696 bits)
AdaptiveStreamPrefetcher *asp;

// Prefetch requests for generating prefetches.
// * These registers are overwritten
// * only when the new demand requests are reached to the prefetcher.
int NumFwdPref;              // 5 bit counter
int NumBwdPref;              // 5 bit counter
bool FwdPrefMap[MAP_Size/2]; // 128 bit register
bool BwdPrefMap[MAP_Size/2]; // 128 bit register
CacheAddr_t AddressRegister; // 26 bit register
// Pipeline registers total budget : 292 bit

// Total Budget Size: 32036 bit

//
//  Function to initialize the prefetchers.  DO NOT change the prototype of this
//  function.  You can change the body of the function by calling your necessary
//  initialization functions.
//

void InitPrefetchers() // DO NOT CHANGE THE PROTOTYPE
{
  // INSERT YOUR CHANGES IN HERE
  mshrpf = new PrefetchMissStatusHandlingRegister();
  mshr = new MissStatusHandlingRegister();
  pref = new MemoryAccessMapTable();
  asp  = new AdaptiveStreamPrefetcher();
  NumFwdPref = NumBwdPref = 0;
}


//
//  Function that is called every cycle to issue prefetches should the
//  prefetcher want to.  The arguments to the function are the current cycle,
//  the demand requests to L1 and L2 cache.  Again, DO NOT change the prototype of this
//  function.  You can change the body of the function by calling your necessary
//  routines to invoke your prefetcher.
//

// DO NOT CHANGE THE PROTOTYPE
void IssuePrefetches( COUNTER cycle, PrefetchData_t *L1Data, PrefetchData_t * L2Data )
{    

  // INSERT YOUR CHANGES IN HERE

  /////////////////////////////////////////////////////
  // Housekeeping
  /////////////////////////////////////////////////////
  mshrpf->Housekeeping(cycle);
  mshr->Housekeeping(cycle);
  pref->Housekeeping(cycle);
  asp->Housekeeping(cycle);

  /////////////////////////////////////////////////////
  // L1 Prefetching
  /////////////////////////////////////////////////////

  // L1 Prefetch
  // This prefetcher is based on "Adaptive Stream Prefetcher"
  for(int i=0; i<4; i++) {
    if(L1Data[i].LastRequestCycle == cycle) {
      asp->IssuePrefetch(cycle, L1Data[i].DataAddr, L1Data[i].hit);
    }
  }

  /////////////////////////////////////////////////////
  /////////////////////////////////////////////////////
  // L2 Prefetching (AMPM Prefetching)
  // 
  // Following part is pipelined Structure
  // Stage sequence is written in reverse order.
  // (This coding style might be similar to SimpleScalar)
  /////////////////////////////////////////////////////
  /////////////////////////////////////////////////////

  /////////////////////////////////////////////////////
  // Stage 3. Issue Prefetch from MSHR
  /////////////////////////////////////////////////////
  mshrpf->PrefetchHousekeeping(mshr, cycle);

  /////////////////////////////////////////////////////
  // Stage 2. Issue Prefetch Request to MSHR
  /////////////////////////////////////////////////////

  // Maximum issue rate to prefetch MSHR...
  static const int MAX_ISSUE = 1;

  // This loop is implemented as a priority encoder
  for(int i=1, count=0;
      ( NumFwdPref && (count < MAX_ISSUE) &&
	(!mshrpf->Full()) && (i<(MAP_Size/2)) );
      i++) {
    if(FwdPrefMap[i]) {
      CacheAddr_t PrefAddress = AddressRegister + (i * BLK_SIZE);
      FwdPrefMap[i] = false;
      
      // Update Memory Access Map
      pref->UpdateEntry(cycle, PrefAddress);

      //if((GetPrefetchBit(1, PrefAddress) < 0) &&
      // !mshr->Search(PrefAddress)) {
      if(!mshr->Search(PrefAddress)) {
	mshrpf->Issue  (cycle, PrefAddress);
      }
      NumFwdPref--;
      count++;
    }
  }
  
  // This loop is implemented as a priority encoder
  for(int i=1, count=0;
      ( NumBwdPref && (count < MAX_ISSUE) &&
	(!mshrpf->Full()) && (i<(MAP_Size/2)) );
      i++) {
    if(BwdPrefMap[i] && (AddressRegister > (i * BLK_SIZE))) {
      CacheAddr_t PrefAddress = AddressRegister - (i * BLK_SIZE);
      BwdPrefMap[i] = false;
      
      // Update Memory Access Map
      pref->UpdateEntry(cycle, PrefAddress);

      //if((GetPrefetchBit(1, PrefAddress) < 0) &&
      // !mshr->Search(PrefAddress)) {
      if(!mshr->Search(PrefAddress)) {
	mshrpf->Issue  (cycle, PrefAddress);
      }
      NumBwdPref--;
      count++;
    }
  }

  /////////////////////////////////////////////////////
  // Stage 1. Access to Memory Access Map Table
  /////////////////////////////////////////////////////

  // Memory Access Map Table Access
  if(L2Data->LastRequestCycle == cycle) {
    bool MSHR_hit = mshr->Search(L2Data->DataAddr) || mshrpf->Search(L2Data->DataAddr);

    // Update MSHR
    // This stored data is used for filtering prefetch requests...
    if(!MSHR_hit && !L2Data->hit) {
      mshr->Issue(cycle, L2Data->DataAddr);
    }

    // Read prefetch candidate from Memory Access Map Table
    pref->IssuePrefetch(cycle, L2Data->DataAddr, MSHR_hit,
			FwdPrefMap, BwdPrefMap,
			&NumFwdPref, &NumBwdPref);
    AddressRegister = L2Data->DataAddr;
    AddressRegister &= ~(BLK_SIZE-1);
  }

}
