// *******************************************************************************
// Multi-level Adaptive Sequential Tagged Prefetching
// based on Performance Gradient Tracking
// Ramos, Briz, Ibanez, Vinals
// *******************************************************************************
#include "interface.h"  // Do NOT edit interface .h
#include "sample_prefetcher.h"

//
//  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
   
      MSHR_ini();
   }


//
//  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
     
      MSHR_cycle(cycle);
      AD_cycle(cycle, L1Data);
      SEQTL1_cycle(cycle, L1Data);
      SEQT_cycle(cycle, L2Data);
     
   }

// *******************************************************************************
//			SEQT (L2 Prefetcher)
// *******************************************************************************

    void SEQT_cycle(COUNTER cycle, PrefetchData_t *L2Data){
     
      // miss or "1st use" in L2? (considering demand and prefetch references)
      if (cycle == L2Data->LastRequestCycle && ((L2Data->hit == 0  && !MSHR_lookup(MSHRD2, cycle, L2Data->DataAddr)) || GetPrefetchBit(1, L2Data->DataAddr)==1)  ){
      
         if (L2Data->hit == 0)
            MSHR_insert(MSHRD2, cycle, L2Data->DataAddr);
         else 
            UnSetPrefetchBit(1, L2Data->DataAddr);
      
         if (AD_degree){
            // program SDA
            SDA_last_addr=L2Data->DataAddr;
            SDA_degree=AD_degree; 
         }
      }
   
      // the SDA generates 1 prefetch per cycle
      if (SDA_degree){
         ADDRINT predicted_address= SDA_last_addr+0x40; 
      
      	 // issue prefetch (if not filtered)
         if (GetPrefetchBit(1, predicted_address)==-1){
            if (!MSHR_lookup(PMAF2, cycle,predicted_address & 0xffff) && !MSHR_lookup(MSHRD2, cycle,predicted_address)){
               IssueL2Prefetch(cycle,predicted_address);
               MSHR_insert(PMAF2, cycle,predicted_address & 0xffff);
            }
         }
      	 // program SDA
         SDA_last_addr=predicted_address;
         SDA_degree--;
      }
   }

// *******************************************************************************
//			SEQTL1 (L1 Prefetcher)
// *******************************************************************************
    void SEQTL1_cycle(COUNTER cycle, PrefetchData_t *L1Data){
      int i;
      
      for (i=0; i<4; i++){
         // miss or "1st use" in L1?
         if (cycle == L1Data[i].LastRequestCycle && (L1Data[i].hit == 0 || GetPrefetchBit(0, L1Data[i].DataAddr)==1) ){
         
            if (L1Data[i].hit == 1){
               UnSetPrefetchBit(0, L1Data[i].DataAddr);
            }                                                       
         
            // program SDA1
            SDA1_last_addr=L1Data[i].DataAddr;
            SDA1_degree=(L1Data[i].hit == 0 ? 1 : 4 ); 
         } //end if
      } //end for
   
      // the SDA1 generates 1 prefetch per cycle
      if (SDA1_degree){
         ADDRINT predicted_address= SDA1_last_addr+0x40; 
         
      	 // issue prefetch (if not filtered)
         if (GetPrefetchBit(0, predicted_address)==-1){
            if (!MSHR_lookup(PMAF1, cycle,predicted_address & 0xffff)){       
               if (IssueL1Prefetch(cycle,predicted_address)==0){;
                  MSHR_insert(PMAF1, cycle,predicted_address & 0xffff);		 
               }
            }	 
         }
      	 // program next prefetch for the next cycle
         SDA1_last_addr=predicted_address;
         SDA1_degree--;
      }
   }


// *******************************************************************************
//			MSHR
// *******************************************************************************

    void MSHR_ini (void){
      PMAF1=(MSHR *) calloc(1, sizeof(MSHR));
      PMAF1->size=32;
      MSHRD2=(MSHR *) calloc(1, sizeof(MSHR));
      MSHRD2->size=16;
      PMAF2=(MSHR *) calloc(1, sizeof(MSHR));
      PMAF2->size=32;
   }
    int MSHR_lookup (MSHR * MSHR, COUNTER cycle, ADDRINT addr){
      int i;
      ADDRINT MASK = 0x3f; 
   
      if (!MSHR->size || !MSHR->num)
         return 0;
   
      for (i=0; i < MSHR->size; i++){
         if (MSHR->entry[i].valid && (MSHR->entry[i].addr == (addr & ~MASK)) ){
            return 1;
         }
      }
      return 0;
   }
    void MSHR_insert (MSHR * MSHR, COUNTER cycle, ADDRINT addr){
      ADDRINT MASK = 0x3f; 
   
      if (!MSHR->size || !addr)
         return;
   
      MSHR->entry[MSHR->tail].valid=1;
      MSHR->entry[MSHR->tail].addr= (addr & ~MASK);
      MSHR->tail=(MSHR->tail+1)%MSHR->size;
   
      if (MSHR->num<MSHR->size)
         MSHR->num++;
      else
         MSHR->head=MSHR->tail;
   }

    void MSHR_cycle (COUNTER cycle){
   
      if (PMAF1->num && (GetPrefetchBit(0, PMAF1->entry[PMAF1->head].addr)!= -1) ){
         PMAF1->num--;
         PMAF1->entry[PMAF1->head].valid=0;
         PMAF1->head=(PMAF1->head+1)%PMAF1->size;
      }
   
      if (MSHRD2->num && (GetPrefetchBit(1, MSHRD2->entry[MSHRD2->head].addr)!= -1) ){
         MSHRD2->num--;
         MSHRD2->entry[MSHRD2->head].valid=0;
         MSHRD2->head=(MSHRD2->head+1)%MSHRD2->size;
      }
   
      if (PMAF2->num && (GetPrefetchBit(0, PMAF2->entry[PMAF2->head].addr)!= -1) ){
         PMAF2->num--;
         PMAF2->entry[PMAF2->head].valid=0;
         PMAF2->head=(PMAF2->head+1)%PMAF2->size;
      }
   }

// *******************************************************************************
//			ADAPTIVE DEGREE (L2)
// *******************************************************************************

    void AD_cycle(COUNTER cycle, PrefetchData_t *L1Data){
      int i;
   
      AD_cycles++;
      for(i = 0; i < 4; i++) {
         if(cycle == L1Data[i].LastRequestCycle) 
            AD_L1_accesses++;
      }
      
      if (AD_cycles>AD_interval){
         AD_cycles=0;
         if (AD_L1_accesses<AD_last_L1_accesses){
            AD_state=!AD_state;
         }
         if (!AD_state) {
            if (AD_deg_index<AD_MAX_INDEX-1) AD_deg_index++; 
         } 
         else {
            if (AD_deg_index>0) AD_deg_index--; 
         }
         AD_degree=AD_degs[AD_deg_index];
         AD_last_L1_accesses=AD_L1_accesses;
         AD_L1_accesses=0;
      }
   }

