/* -*- c++ -*- */

/***************************************************************************
                         CacheWBNBVCMultiPorted.h -
        Non-blocking Writeback Allocate on Write Cache Multiported
                             -------------------
    begin                : mon may 03 2004
    copyright            : (C) 2004 by Universite Paris Sud
    author               : Daniel Gracia Perez
    email                : gracia@lri.fr
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef __MICROLIB_CACHEWBNBVCMULTIPORTED_H__
#define __MICROLIB_CACHEWBNBVCMULTIPORTED_H__

#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "mem_common.h"
#include "common.h"
#include "base/utility.h"
#include "system/CacheContainer.h"
#include "system/memory/cache/CacheCommon.h"
#include "system/memory/cache/MSHRContainer.h"

/* A SystemC module implementing the cache L1. It is a template class. */
/* T, nSources, nDestinations: data type manipulated by the instructions */
/* replacementPolicy: replacement policy to apply */
/* nPorts: the total number of ports that can be used to service requests, should be bigger than 1*/
/* nCPUtoCacheDataPathSize: maximum size in bytes of the data that the cache can receive from the CPU */
/* nCachetoCPUDataPathSize: maximum size in bytes of the data that the cache can send towards the CPU */
/* nCPULineSize: the minimum size of the data that has to be sent towards the CPU,
 *               if 0 then the value is ignored (e.g. connected to the CPU)
 *               else the size of the cache line in level -1.
 *               it is used to set the base address of the requested data */
/* nMemtoCacheDataPathSize: maximum size in bytes of the data that the cache can be received from the memory system */
/* nCachetoMemDataPathSize: maximum size in bytes of the data that the cache can send towards the memory system,
 *                          used in the case of write back policy,
 *                          it should be the same than nCPUtoCacheDataPathSize if write through */
/* nLineSize: size of a cache line in bytes */
/* nCacheLines: number of cache lines */
/* nAssociativity: number of sets in the cache,
 *        if nAssociativity==nCacheLine complete associative,
 *        if nAssociativity==1 direct cache */
/* nStages: number of pipe stages, number of cycles */
template <class T,
int nSources,
ReplacementPolicyType replacementPolicy,
int nCPUtoCacheDataPathSize,
int nCachetoCPUDataPathSize,
int nCPULineSize,
int nMemtoCacheDataPathSize,
int nCachetoMemDataPathSize,
int nLineSize,
int nCacheLines,
int nAssociativity,
int nStages,
int nDelay,
int nPorts,
int nMSHR,
int nMSHRRead,
int nVCCacheLines,
int nVCAssociativity,
ReplacementPolicyType nVCReplacementPolicy>
class MicrolibCacheWBNBVCMultiPorted:public sc_module
{
public:
  /* cache: it is the structure that will keep the data of the cache */
  CacheContainer<nCacheLines, nLineSize, nAssociativity, replacementPolicy, 1> cache;
  /* vc: it is the structure that will keep the data of the victim cache */
  CacheContainer<nVCCacheLines, nLineSize, nVCAssociativity, nVCReplacementPolicy, 1> vc;
private:
  /* cacheQueue: implements the cache pipeline */
  Queue<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheQueue;
  /* MSHRQueue: implements the miss address file */
  MSHRContainer<T, nSources, nLineSize, (nCPULineSize > 0) ? nCPULineSize : nLineSize, nMSHR, nMSHRRead> MSHR;
  /* writeBuffer: it is used as delayed write buffer of the cache */
  DelayedWriteBuffer<nLineSize> writeBuffer;
  /* returnBuffer: it is used to stock temporaly the data sent towards the CPU */
  ReturnData<T, /*nSources,*/ nLineSize, nCachetoCPUDataPathSize> returnBuffer[nPorts];
  /* requestBuffer: it is used to stock temporaly the data sent towards the memory system */
  RequestData<T, /*nSources,*/ nMemtoCacheDataPathSize, nLineSize> requestBuffer;

  /* requestQueue: implements a request queue from the misses produced in the cacheQueue */
  Queue<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nMSHR * 2> requestQueue;

  /*******************************************************************************/
  /*******************************************************************************/
  /******************** External Control Functions (start) ***********************/
  /*******************************************************************************/
  /*******************************************************************************/

  /* if an accept is received from the CPU enable the output to the CPU */
  void CheckCPUAccept()
  {
    int index;


    for (index = 0; index < nPorts; index++)
    {
      if (inCPUtoCacheAccept[index])
      {
        outCachetoCPUEnable[index] = true;
      }
      else
      {
        outCachetoCPUEnable[index] = false;
      }
    }
  }

  /* if an accept is received from the memory system enable the output to the memory system */
  void CheckMemAccept()
  {
    outCachetoMemEnable = inMemtoCacheAccept;
  }

  void CheckCPURequests()
  {
    QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit;
    int port, index;
    bool accept;

    for (port = -1, index = 0; index < nPorts; index++)
    {
      if (inCPUtoCacheValid[index])
      {
        port = 1;
      }
    }
    accept = false;
    if (port != -1)
    {
      /* if the cache is not full, the request can be accepted */
      if (cacheQueue.Empty())
      {
        accept = true;
      }
      else
      {
        /* check if there will be place in the cache pipeline */
        int stage = 0;
        bool cont = true;
        for (cacheit = cacheQueue.SeekAtTail();
             cont && cacheit;
             cacheit--, stage++)
        {
          if (cacheit->stage > stage)
          {
            accept = true;
            cont = false;
          }
          else
          {
            if (stage != nStages-1)
            {
              if (cacheit->delay[cacheit->stage] > 1)
              {
                accept = false;
                cont = false;
              }
            }
            else
            {
              if (cacheit->delay[stage] > 0)
              {
                accept = false;
                cont = false;
              }
              else
              {
                int counter_write_miss_no_read = 0;
                for (index = 0; cont && index < nPorts; index++)
                {
                  switch (cacheit->state[index])
                  {
                    case WRITE:
                      accept = cache.hit(cacheit->address[index]);
                      if (!accept)
                      {
                        cont = false;
                      }
                      break;
                    case WRITE_MISS_NO_READ:
                      if (counter_write_miss_no_read)
                      {
                        accept = false;
                      }
                      else
                      {
                        accept = inMemtoCacheAccept;
                        counter_write_miss_no_read++;
                      }
                      if (!accept)
                      {
                        cont = false;
                      }
                      break;
                    case NOTHING:
                      accept = true;
                      break;
                    default:
                      accept = false;
                      cont = false;
                      break;
                  }
                }
              }
            }
          }
        }
      }
    }
    for (index = 0; index < nPorts; index++)
    {
      if (inCPUtoCacheValid[index])
      {
        outCachetoCPUAccept[index] = accept;
      }
      else
      {
        outCachetoCPUAccept[index] = false;
      }
    }
  }

  /* if the memory system replies to a miss */
  void CheckMemValid()
  {
    if (inMemtoCacheValid)
    {
      /* nothing has to be checked, the system never blocks the memory inputs */
      outCachetoMemAccept = true;
    }
    else
    {
      outCachetoMemAccept = false;
    }
  }

  /*******************************************************************************/
  /*******************************************************************************/
  /********************** External Control Functions (end) ***********************/
  /*******************************************************************************/
  /*******************************************************************************/

  /*******************************************************************************/
  /*******************************************************************************/
  /******************** Internal Control Functions (start) ***********************/
  /*******************************************************************************/
  /*******************************************************************************/

  bool CheckCPUAcceptedData()
  {
    bool changed = false;
    QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit;
    int index;

    for (index = 0; index < nPorts; index++)
    {
//boveerle
    /* check if an accept from the cpu is being waited */
    if(returnBuffer[index].ready)
    {
//eoveerle
      if (inCPUtoCacheAccept[index])
      {
        if (!returnBuffer[index].ready)
        {
          cerr << "Error(" << name() << "): data accepted by the CPU when nothing has been sent" << endl;
          cerr << *this;
          exit(-1);
        }
        /* check how much has still to be sent */
        returnBuffer[index].size = returnBuffer[index].size - nCachetoCPUDataPathSize;
        if (returnBuffer[index].size > 0)
        {
          /* the returnBuffer is not empty, so it continues servicing the data */
          returnBuffer[index].index = returnBuffer[index].index + nCachetoCPUDataPathSize;
          if (returnBuffer[index].index == nCPULineSize) returnBuffer[index].index = 0;
          returnBuffer[index].address = returnBuffer[index].base + returnBuffer[index].index;
        }
        else
        {
          /* returnBuffer is empty */
          /* the cache enttry that made the request can change its state,
           * if it is a request send by the cache queue */
          if (returnBuffer[index].id == ID_CACHE)
          {
            returnBuffer[index].ready = false;
          }
          else
          {
            returnBuffer[index].ready = false;
          }
        }
      }
//boveerle
      } //if(returnBuffer[index].ready)
//eoveerle
    }

    return changed;
  }

  bool CheckMemAcceptedDatafromCache()
  {
    QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nMSHR * 2> cacheit;
    bool changed = false;

    if (inMemtoCacheAccept)
    {
      if (!requestBuffer.ready)
      {
        cerr << "Error(" << name() << "): data accepted by the memory system when nothing has been sent from the cache" << endl;
        cerr << *this;
        exit(-1);
      }
      /* check if the request was a read or a write */
      if (requestBuffer.write)
      {
        /* this request is a write */
        /* if last request was finished from the cache pipeline (a write request)
         * remove the request from the cache pipeline */
        requestBuffer.size -= nCachetoMemDataPathSize;
        if (requestBuffer.size > 0)
        {
          /* the request has not bben finished, the request has to be keeped,
           * the address has to be increased,
           * and the cache pipeline is not notified */
          requestBuffer.index += nCachetoMemDataPathSize;
          requestBuffer.address = requestBuffer.base + requestBuffer.index;
        }
        else
        {
          /* the request has been finished, the cache pipeline has to be updated */
          cacheit = requestQueue.SeekAtHead();
          switch (cacheit->state[requestBuffer.uid])
          {
              /* write cases */
            case WRITE_MISS_EVICTION_WAITING_ACCEPT:
              cacheit->state[requestBuffer.uid] = WRITE_MISS_EVICTION_WRITE_DONE;
              break;
            case WRITE_MISS_NO_READ_WAITING_ACCEPT:
              cacheit->state[requestBuffer.uid] = NOTHING;
              break;
              /* read cases */
            case READ_MISS_EVICTION_WAITING_ACCEPT:
              cacheit->state[requestBuffer.uid] = READ_MISS_EVICTION_WRITE_DONE;
              break;
              /* prefetch cases */
            case PREFETCH_MISS_EVICTION_WAITING_ACCEPT:
              cacheit->state[requestBuffer.uid] = PREFETCH_MISS_EVICTION_WRITE_DONE;
              break;
              /* evict cases */
            case EVICT_READY_WAITING_ACCEPT:
              cacheit->state[requestBuffer.uid] = NOTHING;
              break;
          }
          requestBuffer.ready=false;
          /* check if the cache entry array can be removed */
          bool remove;
          int index;
          for (remove = true, index = 0; remove && index < nPorts; index++)
          {
            remove = (cacheit->state[index] == NOTHING);
          }
          if (remove)
          {
            requestQueue.RemoveHead();
            changed = true;
          }
        }
      }
      else
      {
        /* the request is a read */
        /* the access is a cache miss
         * the cache entry (the head of the cache queue)
         * is not removed from the cache pipeline (this is a blocking cache) */
        /* the request buffer is marked as not ready, to be used with the next memory request */
// 				cacheit=cacheQueue.SeekAtHead();
        cacheit = requestQueue.SeekAtHead();
        switch (cacheit->state[requestBuffer.uid])
        {
            /* write cases */
          case WRITE_MISS_WAITING_ACCEPT:
            cacheit->state[requestBuffer.uid] = NOTHING;
            break;
          case WRITE_MISS_EVICTION_WRITE_DONE_WAITING_ACCEPT:
            cacheit->state[requestBuffer.uid] = NOTHING;
            break;
            /* read cases */
          case READ_MISS_WAITING_ACCEPT:
            cacheit->state[requestBuffer.uid] = NOTHING;
            break;
          case READ_MISS_EVICTION_WRITE_DONE_WAITING_ACCEPT:
            cacheit->state[requestBuffer.uid] = NOTHING;
            break;
            /* prefetch cases */
          case PREFETCH_MISS_WAITING_ACCEPT:
            cacheit->state[requestBuffer.uid] = NOTHING;
            break;
          case PREFETCH_MISS_EVICTION_WRITE_DONE_WAITING_ACCEPT:
            cacheit->state[requestBuffer.uid] = NOTHING;
            break;
        }
        requestBuffer.ready=false;
        /* check if the cache entry array can be removed */
        bool remove;
        int index;
        for (remove = true, index = 0; remove && index < nPorts; index++)
        {
          remove = (cacheit->state[index] == NOTHING);
        }
        if (remove)
        {
          requestQueue.RemoveHead();
          changed = true;
        }
      }
    }
    else
    {
      if (requestBuffer.ready) waiting_memory_bus++;
    }

    return changed;
  }

  bool CheckAcceptedData()
  {
    bool changed = false;

    changed = CheckCPUAcceptedData();

    changed = CheckMemAcceptedDatafromCache() || changed;

    return changed;
  }

  bool UpdateWriteBuffer()
  {
    bool changed = false;

    /* check that data was received from the memory system */
    if (inMemtoCacheEnable)
    {
      /* check that it is not an incorrect line */
      writeBuffer.uid = inMemtoCacheTag;
      if (writeBuffer.address != (inMemtoCacheAddress & ~((address_t)nLineSize - 1)) &&
          writeBuffer.size != 0)
      {
        if (writeBuffer.size == nLineSize)
        {
          cerr << "Error(" << name() << "): writeBuffer was full and a memory access was received" << endl;
          cerr << *this;
          exit(-1);
        }
        else
        {
          cerr << "Error(" << name() << "): memory system has not finished actual write buffer" << endl;
          cerr << *this;
          exit(-1);
        }
      }
      else
      {
        /* update bytes that are received */
        for (int i = 0; i < nMemtoCacheDataPathSize; i++)
        {
          if (writeBuffer.ready[(int)(i + (inMemtoCacheAddress &
                                           ((address_t)(nLineSize - 1))))])
          {
            cerr << "Error(" << name() << "): writeBuffer has received two times the same address" << endl;
            cerr << *this;
            exit(-1);
          }
          writeBuffer.ready[i + (inMemtoCacheAddress
                                 & ((address_t)(nLineSize - 1)))] = true;
        }
        memcpy(&(writeBuffer.buffer[inMemtoCacheAddress&((address_t)(nLineSize - 1))]),
               inMemtoCacheData.Read(),
               nMemtoCacheDataPathSize);
        writeBuffer.size += nMemtoCacheDataPathSize;
        writeBuffer.address = inMemtoCacheAddress & (~((address_t)(nLineSize - 1)));
      }
    }

    return changed;
  }

  bool UpdateMSHRWithWriteBuffer()
  {
    bool changed = false;
    uint8_t buffer[nLineSize];
    int locked_set, locked_line;
    bool dirty;

    MSHR.Update(writeBuffer.address, writeBuffer.buffer,
                &locked_set, &locked_line, &dirty, buffer);

    cache.setCacheLine(locked_set, locked_line,
                       writeBuffer.address,
                       (char *)buffer,
                       true, dirty);
    cache.Unlock(locked_set, locked_line);

    return changed;
  }

  bool ReadMemData()
  {
    bool changed = false;

    /* update the writeBuffer if data is received from the memory system */
    changed = UpdateWriteBuffer();

    /* if writeBuffer has been completed write the cacheline to the cache */
    if (writeBuffer.size == nLineSize)
    {
      changed = UpdateMSHRWithWriteBuffer() || changed;
      writeBuffer.size = 0;
      for (int i = 0; i < nLineSize; i++)
        writeBuffer.ready[i] = false;
    }

    return changed;
  }

  bool RunCachePipeline()
  {
    bool changed = false;
    QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, cacheitPrev;

    for (cacheit = cacheQueue.SeekAtHead(); cacheit; cacheit++)
    {
      if (cacheit->delay[cacheit->stage] == 0)
      {
        if (cacheitPrev)
        {
          if (cacheitPrev->stage > cacheit->stage + 1)
          {
            cacheit->stage++;
            changed = true;
          }
        }
        else
        {
          if (cacheit->stage < nStages - 1)
          {
            cacheit->stage++;
            changed = true;
          }
        }
      }
      cacheitPrev = cacheit;
    }

    for (cacheit = cacheQueue.SeekAtHead(); cacheit; cacheit++)
    {
      if (cacheit->delay[cacheit->stage] > 0)
      {
        --cacheit->delay[cacheit->stage];
        changed = true;
      }
    }

    return changed;
  }

  bool ReadCPUData()
  {
    bool changed = false;
    CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts> *entry;
    int index;

    /* it should be checked if new requests can be received (for debugging) */
    entry = NULL;
    for (index = 0; index < nPorts; index++)
    {
      if (inCPUtoCacheEnable[index])
      {
        accesses++;
        if (entry == NULL)
        {
          if (cacheQueue.Full())
          {
            cerr << "Error(" << name() << "): CPU request should not have been accepted" << endl;
            cerr << cacheQueue;
            cerr << *this;
            exit(-1);
          }
          entry = cacheQueue.New();
          entry->stage = 0;
          for (int i = 0; i < nStages; i++)
            entry->delay[i] = nDelay;
          if (entry->delay[0] > 0) entry->delay[0]--;
          for (int i = 0; i < nPorts; i++)
            entry->state[i] = NOTHING;

          changed = true;
        }

        /* copy signals to the cache pipeline entry */
        entry->write[index] = (inCPUtoCacheCommand[index] == WRITE_CACHE_COMMAND);
        switch (inCPUtoCacheCommand[index])
        {
          case WRITE_CACHE_COMMAND:
            accesses_write++;
            entry->state[index] = WRITE;
            break;
          case READ_CACHE_COMMAND:
            accesses_read++;
            entry->state[index] = READ;
            break;
          case PREFETCH_CACHE_COMMAND:
            accesses_prefetch++;
            entry->state[index] = PREFETCH;
            break;
          case EVICT_CACHE_COMMAND:
            accesses_evict++;
            entry->state[index] = EVICT;
            break;
        }

        entry->size[index] = inCPUtoCacheSize[index];
        if (entry->write[index])
          memcpy(entry->data[index], inCPUtoCacheData[index].Read(), entry->size[index]);
        entry->address[index] = inCPUtoCacheAddress[index];

        if (nCPULineSize > 0)
        {
          entry->base[index] = entry->address[index] & (~(address_t)(nCPULineSize - 1));
          entry->index[index] = entry->address[index] & ((address_t)(nCPULineSize - 1)) &
                                ~((address_t)(nCachetoCPUDataPathSize - 1));
        }
        else
        {
          entry->base[index] = entry->address[index];
          entry->index[index] = 0;
        }
        entry->instr[index] = inCPUtoCacheInstruction[index];
        entry->port[index] = index;
        entry->block[index] = false;
        entry->tag[index] = inCPUtoCacheTag[index];
        entry->sent[index] = false;
//boveerle
	entry->req_sender[index] = inCPUtoCacheReqSender[index];
	//cerr << "@ReadCPUData req_sender[index]=" << entry->req_sender[index]->name() << "(" << entry->req_sender[index] << ")" << endl;
//eoveerle
      }
    }

    return changed;
  }

  void ExchangeLines(address_t addr)
  {
    int vcset;
    int vcline;
    int cset;
    int cline;
    char cdata[nLineSize];
    char vcdata[nLineSize];
    address_t caddr;
    address_t vcaddr;
    bool cvalid,cwrite;
    bool vcvalid,vcwrite;

    vcset=vc.getSet(addr);
    vcline=vc.getLine(addr);
    cset=cache.getReplaceSet(addr);
    cline=cache.getReplaceLine(addr);
    cache.getCacheLine(cdata,cset,cline);
    vc.getCacheLine(vcdata,vcset,vcline);
    caddr=cache.getAddress(cset,cline);
    vcaddr=vc.getAddress(vcset,vcline);
    cvalid=cache.getValid(cset,cline);
    cwrite=cache.getWrite(cset,cline);
    vcvalid=vc.getValid(vcset,vcline);
    vcwrite=vc.getWrite(vcset,vcline);
    cache.setCacheLine(cset,cline,vcaddr,vcdata,vcvalid,vcwrite);
    vc.setCacheLine(vcset,vcline,caddr,cdata,cvalid,cwrite);
    cache.historyAccess(cset,cline);
    vc.historyAccess(vcset,vcline);
  }

  bool SetCacheHeadReadState(QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, int index)
  {
    bool changed = false;

    cacheit->hit[index] = cache.hit(cacheit->address[index]);
    if (cacheit->hit[index])
    {
      cache.historyAccess(cache.getSet(cacheit->address[index]),
                          cache.getLine(cacheit->address[index]));
      hits++;
      hits_read++;
      cacheit->state[index] = READ_HIT_READY;
      if (nCPULineSize>0)
      {
        cache.getCacheLineIndexed(cacheit->data[index],
                                  cache.getSet(cacheit->address[index]),
                                  cache.getLine(cacheit->address[index]),
                                  (cacheit->address[index] & (~(address_t)(nCPULineSize - 1))) & ((address_t)(nLineSize - 1)),
                                  cacheit->size[index]);
      }
      else
      {
        cache.getCacheLineIndexed(cacheit->data[index],
                                  cache.getSet(cacheit->address[index]),
                                  cache.getLine(cacheit->address[index]),
                                  (cacheit->address[index] & ((address_t)(nLineSize - 1))),
                                  cacheit->size[index]);
      }
    }
    else
    {
      cacheit->hit[index] = vc.hit(cacheit->address[index]);
      if (cacheit->hit[index])
      {
        cacheit->set[index] = cache.getReplaceSet(cacheit->address[index]);
        cacheit->line[index] = cache.getReplaceLine(cacheit->address[index]);
        if (cache.IsLocked(cacheit->set[index], cacheit->line[index]))
        {
          return changed;
        }
        hits++;
        hits_vc++;
        hits_read++;
        hits_vc_read++;
        cacheit->state[index] = READ_HIT_READY;
        if (nCPULineSize > 0)
        {
          vc.getCacheLineIndexed(cacheit->data[index],
                                 vc.getSet(cacheit->address[index]),
                                 vc.getLine(cacheit->address[index]),
                                 (cacheit->address[index] & (~(address_t)(nCPULineSize - 1))) & ((address_t)(nLineSize - 1)),
                                 cacheit->size[index]);
        }
        else
        {
          vc.getCacheLineIndexed(cacheit->data[index],
                                 vc.getSet(cacheit->address[index]),
                                 vc.getLine(cacheit->address[index]),
                                 (cacheit->address[index] & ((address_t)(nLineSize - 1))),
                                 cacheit->size[index]);
        }
        ExchangeLines(cacheit->address[index]);
      }
      else
      {
        if (cacheit->state[index] == READ)
        {
          cacheit->set[index] = cache.getReplaceSet(cacheit->address[index]);
          cacheit->line[index] = cache.getReplaceLine(cacheit->address[index]);
          for (int i = 0; cache.IsLocked(cacheit->set[index], cacheit->line[index]) && i < nCacheLines; i++)
          {
            cache.historyAccess(cacheit->set[index],
                                cacheit->line[index]);
            cacheit->set[index] = cache.getReplaceSet(cacheit->address[index]);
            cacheit->line[index] = cache.getReplaceLine(cacheit->address[index]);
          }
          switch (MSHR.AddReadEntry(cacheit->address[index], cacheit->base[index], cacheit->index[index],
                                    cacheit->size[index], cacheit->port[index], cacheit->tag[index],
                                    cacheit->instr[index], cacheit->set[index], cacheit->line[index]
//boveerle
, cacheit->req_sender[index]
//eoveerle
))
          {
            case NOT_ACCEPTED_MSHR_ENTRY:
              blocked_mshrs++;
              return changed;
              break;
            case EXISTENT_MSHR_ENTRY:
              cacheit->state[index] = NOTHING;
              existent_mshrs++;
              hits_mshr++;
              hits_mshr_read++;
              return changed;
              break;
            case NEW_MSHR_ENTRY:
              new_mshrs++;
              cacheit->state[index] = READ_MSHR;
              break;
          }
        }
        SetCacheHeadReadStateMSHR(cacheit, index);
      }
    }

    return changed;
  }

  void SetCacheHeadReadStateMSHR(QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, int index)
  {
    if (cache.IsLocked(cacheit->set[index], cacheit->line[index]))
    {
      return;
    }
    cache.Lock(cacheit->set[index], cacheit->line[index]);
    misses++;
    misses_read++;
    cache.historyAccess(cacheit->set[index], cacheit->line[index]);
    if (cache.getValid(cacheit->set[index], cacheit->line[index]))
    {
      int vcline, vcset;
      char data[nLineSize];
      vcline = vc.getReplaceLine(cache.getAddress(cacheit->set[index], cacheit->line[index]));
      vcset = vc.getReplaceSet(cache.getAddress(cacheit->set[index], cacheit->line[index]));
// 			cacheit->evictedaddress[index] = cache.getAddress(cacheit->set[index],
// 																												cacheit->line[index]);
// 			cacheit->evicted[index] = cache.getWrite(cacheit->set[index],
// 																							 cacheit->line[index]);
      cacheit->evictedaddress[index] = vc.getAddress(vcset, vcline);
      cacheit->evicted[index] = vc.getValid(vcset, vcline) && vc.getWrite(vcset, vcline);
      if (cacheit->evicted[index])
      {
        writebacks++;
        writebacks_read++;
// 				cache.getCacheLine(cacheit->linedata[index],
// 													 cacheit->set[index],
// 													 cacheit->line[index]);
        vc.getCacheLine(cacheit->linedata[index], vcset, vcline);
        cacheit->state[index] = READ_MISS_EVICTION;
      }
      else
      {
        cacheit->state[index] = READ_MISS;
      }
      cache.getCacheLine(data, cacheit->set[index], cacheit->line[index]);
      vc.setCacheLine(vcset, vcline,
                      cache.getAddress(cacheit->set[index], cacheit->line[index]),
                      data,
                      true,
                      cache.getWrite(cacheit->set[index], cacheit->line[index]));
      vc.historyAccess(vcset, vcline);
      cache.setValid(cacheit->set[index], cacheit->line[index], false);
    }
    else
    {
      cacheit->evicted[index] = false;
      cacheit->state[index] = READ_MISS;
    }
  }

  bool SetCacheHeadWriteState(QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, int index)
  {
    bool changed = false;

    cacheit->hit[index] = cache.hit(cacheit->address[index]);
    if (cacheit->hit[index])
    {
      cache.historyAccess(cache.getSet(cacheit->address[index]),
                          cache.getLine(cacheit->address[index]));
      hits++;
      hits_write++;
      cache.setCacheLineIndexed(cache.getSet(cacheit->address[index]),
                                cache.getLine(cacheit->address[index]),
                                cacheit->data[index],
                                cacheit->address[index] & ((address_t)(nLineSize-1)),
                                cacheit->size[index],
                                true);
      /* remove the cache head */
      cacheit->state[index] = NOTHING;
    }
    else
    {
      cacheit->hit[index] = vc.hit(cacheit->address[index]);
      if (cacheit->hit[index])
      {
        cacheit->set[index] = cache.getReplaceSet(cacheit->address[index]);
        cacheit->line[index] = cache.getReplaceLine(cacheit->address[index]);
        if (cache.IsLocked(cacheit->set[index], cacheit->line[index]))
        {
          return changed;
        }
        hits++;
        hits_vc++;
        hits_write++;
        hits_vc_write++;
        cacheit->state[index] = NOTHING;
        vc.setCacheLineIndexed(vc.getSet(cacheit->address[index]),
                               vc.getLine(cacheit->address[index]),
                               cacheit->data[index],
                               cacheit->address[index] & ((address_t)(nLineSize-1)),
                               cacheit->size[index],
                               true);
        ExchangeLines(cacheit->address[index]);
      }
      else
      {
        if (cacheit->state[index] == WRITE)
        {
          cacheit->set[index] = cache.getReplaceSet(cacheit->address[index]);
          cacheit->line[index] = cache.getReplaceLine(cacheit->address[index]);
          switch (MSHR.AddWriteEntry(cacheit->address[index], cacheit->base[index], cacheit->index[index],
                                     cacheit->size[index], (uint8_t *)cacheit->data[index],
                                     cacheit->instr[index], cacheit->set[index], cacheit->line[index]
//boveerle
, cacheit->req_sender[index]
//eoveerle
))
          {
            case NOT_ACCEPTED_MSHR_ENTRY:
              blocked_mshrs++;
              return changed;
              break;
            case EXISTENT_MSHR_ENTRY:
              cacheit->state[index] = NOTHING;
              existent_mshrs++;
              hits_mshr++;
              hits_mshr_write++;
              return changed;
              break;
            case NEW_MSHR_ENTRY:
              new_mshrs++;
              cacheit->state[index] = WRITE_MSHR;
              break;
          }
        }
        SetCacheHeadWriteStateMSHR(cacheit, index);
      }
    }

    return changed;
  }

  void SetCacheHeadWriteStateMSHR(QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, int index)
  {
    if (cache.IsLocked(cacheit->set[index], cacheit->line[index]))
    {
      return;
    }
    cache.Lock(cacheit->set[index], cacheit->line[index]);
    misses++;
    misses_write++;
    cache.historyAccess(cacheit->set[index], cacheit->line[index]);
    if (cache.getValid(cacheit->set[index], cacheit->line[index]))
    {
      int vcset, vcline;
      char data[nLineSize];
      vcset = vc.getReplaceSet(cache.getAddress(cacheit->set[index], cacheit->line[index]));
      vcline = vc.getReplaceLine(cache.getAddress(cacheit->set[index], cacheit->line[index]));
// 			cacheit->evictedaddress[index] = cache.getAddress(cacheit->set[index],
// 																												cacheit->line[index]);
// 			cacheit->evicted[index] = cache.getWrite(cacheit->set[index],
// 																							 cacheit->line[index]);
      cacheit->evictedaddress[index] = vc.getAddress(vcset, vcline);
      cacheit->evicted[index] = vc.getValid(vcset, vcline) && vc.getWrite(vcset, vcline);
      if (cacheit->evicted[index])
      {
        writebacks++;
        writebacks_write++;
// 				cache.getCacheLine(cacheit->linedata[index],
// 													 cacheit->set[index],
// 													 cacheit->line[index]);
        vc.getCacheLine(cacheit->linedata[index],
                        vcset,
                        vcline);
        cacheit->state[index] = WRITE_MISS_EVICTION;
      }
      else
      {
        cacheit->state[index] = WRITE_MISS;
      }
      cache.getCacheLine(data, cacheit->set[index], cacheit->line[index]);
      vc.setCacheLine(vcset, vcline,
                      cache.getAddress(cacheit->set[index], cacheit->line[index]),
                      data,
                      true,
                      cache.getWrite(cacheit->set[index], cacheit->line[index]));
      vc.historyAccess(vcset, vcline);
      cache.setValid(cacheit->set[index],
                     cacheit->line[index], false);
    }
    else
    {
      cacheit->evicted[index] = false;
      cacheit->state[index] = WRITE_MISS;
    }
  }

  bool SetCacheHeadPrefetchState(QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, int index)
  {
    bool changed = false;
    cacheit->hit[index] = cache.hit(cacheit->address[index]);
    if (cacheit->hit[index])
    {
      cache.historyAccess(cache.getSet(cacheit->address[index]),
                          cache.getLine(cacheit->address[index]));
      hits++;
      hits_prefetch++;
      cacheit->state[index] = NOTHING;
    }
    else
    {
      if (cacheit->state[index] == PREFETCH)
      {
        cacheit->set[index] = cache.getReplaceSet(cacheit->address[index]);
        cacheit->line[index] = cache.getReplaceLine(cacheit->address[index]);
        switch (MSHR.AddPrefetchEntry(cacheit->address[index], cacheit->base[index], cacheit->index[index],
                                      cacheit->size[index], cacheit->port[index], cacheit->tag[index],
                                      cacheit->instr[index], cacheit->set[index], cacheit->line[index]
//boveerle
, cacheit->req_sender[index]
//eoveerle
))
        {
          case NOT_ACCEPTED_MSHR_ENTRY:
            blocked_mshrs++;
            return changed;
            break;
          case EXISTENT_MSHR_ENTRY:
            cacheit->state[index] = NOTHING;
            existent_mshrs++;
            hits_mshr++;
            hits_mshr_prefetch++;
            return changed;
            break;
          case NEW_MSHR_ENTRY:
            cacheit->state[index] = PREFETCH_MSHR;
            new_mshrs++;
            break;
        }
      }
      SetCacheHeadPrefetchStateMSHR(cacheit, index);
    }

    return changed;
  }

  bool SetCacheHeadPrefetchStateMSHR(QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, int index)
  {
    bool changed = false;

    if (cache.IsLocked(cacheit->set[index], cacheit->line[index]))
      return changed;
    cache.Lock(cacheit->set[index], cacheit->line[index]);
    misses++;
    misses_prefetch++;
    cache.historyAccess(cacheit->set[index], cacheit->line[index]);
    if (cache.getValid(cacheit->set[index], cacheit->line[index]))
    {
      cacheit->evictedaddress[index] = cache.getAddress(cacheit->set[index],
                                       cacheit->line[index]);
      cacheit->evicted[index] = cache.getWrite(cacheit->set[index],
                                cacheit->line[index]);
      if (cacheit->evicted[index])
      {
        writebacks++;
        writebacks_prefetch++;
        cache.getCacheLine(cacheit->linedata[index],
                           cacheit->set[index],
                           cacheit->line[index]);
        cacheit->state[index] = PREFETCH_MISS_EVICTION;
      }
      else
      {
        cacheit->state[index] = PREFETCH_MISS;
      }
      cache.setValid(cacheit->set[index], cacheit->line[index], false);
    }
    else
    {
      cacheit->evicted[index] = false;
      cacheit->state[index] = PREFETCH_MISS;
    }

    return changed;
  }

  bool SetCacheHeadEvictState(QueuePointer<CachePipeStageMultiPorted<T,/*nSources,*/nLineSize,nStages,nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit, int index)
  {
    bool changed = false;

    cacheit->hit[index] = cache.hit(cacheit->address[index]);
    if (cacheit->hit[index])
    {
      hits++;
      hits_evict++;
      /* no history access, we want to reuse the entry of the evicted line */
      cacheit->set[index] = cache.getSet(cacheit->address[index]);
      cacheit->line[index] = cache.getLine(cacheit->address[index]);
      if (cache.getValid(cacheit->set[index], cacheit->line[index]))
      {
        cacheit->evictedaddress[index] = cache.getAddress(cacheit->set[index],
                                         cacheit->line[index]);
        cacheit->evicted[index] = cache.getWrite(cacheit->set[index],
                                  cacheit->line[index]);
        cache.setValid(cacheit->set[index], cacheit->line[index], false);
        cache.setWrite(cacheit->set[index], cacheit->line[index], false);
        if (cacheit->evicted[index])
        {
          writebacks++;
          writebacks_evict++;
          cache.getCacheLine(cacheit->linedata[index],
                             cacheit->set[index], cacheit->line[index]);
          cacheit->state[index] = EVICT_READY;
        }
        else
        {
          cacheit->state[index] = NOTHING;
        }
      }
      else
      {
        /* why did we get a hit if the line is not valid??? */
        cerr << name() << ": trying to evict a line that is not valid" << endl;
        cerr << *this;
        exit(-1);
      }
    }
    else
    {
      /* do nothing, eviction of a line that does not exist in cache */
      misses++;
      misses_evict++;
      cacheit->state[index] = NOTHING;
    }

    return changed;
  }

  bool SetCacheHeadState()
  {
    bool changed = false;
    QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit;
    int index;

    cacheit = cacheQueue.SeekAtHead();
    if (cacheit)
    {
      if (cacheit->stage == nStages - 1 && cacheit->delay[nStages - 1] == 0)
      {
        for (index = 0; index < nPorts; index++)
        {
          switch (cacheit->state[index])
          {
            case READ:
            case READ_MSHR:
              changed = SetCacheHeadReadState(cacheit, index) || changed;
              break;
            case WRITE:
            case WRITE_MSHR:
              changed = SetCacheHeadWriteState(cacheit, index) || changed;
// 						if(cacheit->state[index] == WRITE)
// 							return changed;
              break;
            case PREFETCH:
            case PREFETCH_MSHR:
              changed = SetCacheHeadPrefetchState(cacheit, index) || changed;
              break;
            case EVICT:
              changed = SetCacheHeadEvictState(cacheit, index) || changed;
              break;
          }
        }
        /* check if the cache entry array can be removed */
        bool remove;
        for (remove = true, index = 0; remove && index < nPorts; index++)
        {
          remove = (cacheit->state[index] == NOTHING);
        }
        if (remove)
        {
          cacheQueue.RemoveHead();
          changed = true;
        }
        else
        {
          /* the entry was not removed, check what can be moved to the requestQueue */
          CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts> entry;
          entry = *cacheit;
          for (index = 0; index < nPorts; index++)
          {
            switch (entry.state[index])
            {
              case READ_HIT_READY:
                entry.state[index] = NOTHING;
                break;
              case READ:
              case READ_MSHR:
              case WRITE:
              case WRITE_MSHR:
              case PREFETCH:
              case PREFETCH_MSHR:
              case EVICT:
                for (; index < nPorts; index++)
                {
                  entry.state[index] = NOTHING;
                }
                break;
              default:
                cacheit->state[index] = NOTHING;
                break;
            }
          }
          bool add;
          for (add = false, index = 0; index < nPorts; index++)
          {
            add = add || entry.state[index] != NOTHING;
          }
          if (add)
          {
            requestQueue << entry;
          }
          /* finally try to remove again the entry from the cache pipeline */
          for (remove = true, index = 0; remove && index < nPorts; index++)
          {
            remove = (cacheit->state[index] == NOTHING);
          }
          if (remove)
          {
            cacheQueue.RemoveHead();
            changed = true;
          }
        }
      }
    }

    return changed;
  }

  bool SendCacheDatatoCPU()
  {
    bool changed = false;;
    QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit;
    int port_index, cache_index;

    cacheit = cacheQueue.SeekAtHead();
    if (cacheit)
    {
      for (port_index = 0, cache_index = 0; port_index < nPorts; port_index++)
      {
        if (!returnBuffer[port_index].ready)
        {
          for (; (cache_index < nPorts) && (cacheit->state[cache_index] != READ_HIT_READY ||
                                            (cacheit->state[cache_index] == READ_HIT_READY &&
                                             cacheit->sent[cache_index])); cache_index++);
          if (cache_index < nPorts)
          {
            returnBuffer[port_index].port = cacheit->port[cache_index];
            returnBuffer[port_index].instr = cacheit->instr[cache_index];
            returnBuffer[port_index].base = cacheit->base[cache_index];
            returnBuffer[port_index].index = cacheit->index[cache_index];
            returnBuffer[port_index].address = cacheit->address[cache_index];
            returnBuffer[port_index].size = cacheit->size[cache_index];
            memcpy(returnBuffer[port_index].data, cacheit->data[cache_index], returnBuffer[port_index].size);
            returnBuffer[port_index].id = ID_CACHE;
            returnBuffer[port_index].uid = cacheit->tag[cache_index];
            returnBuffer[port_index].cacheQueueIndex = cache_index;
// 						cacheit->sent[cache_index] = true;
// 						cacheit->outPort[cache_index] = port_index;
            returnBuffer[port_index].ready = true;
//boveerle
	returnBuffer[port_index].req_sender = cacheit->req_sender[cache_index];
	//cerr << "@SendCacheDatatoCPU req_sender=" << returnBuffer[port_index].req_sender->name() << endl;
//eoveerle
            cacheit->state[cache_index] = NOTHING;
          }
        }
      }
      bool remove;
      int index;
      for (remove = true, index = 0; remove && index < nPorts; index++)
      {
        remove = (cacheit->state[index] == NOTHING);
      }
      if (remove)
      {
        cacheQueue.RemoveHead();
        changed = true;
      }
    }

    return changed;
  }

  bool SendMSHRDatatoCPU()
  {
    bool changed = false;
    int port;
    T instruction;
    address_t address;
    int size;
    uint8_t data[nCPULineSize > 0 ? nCPULineSize : nLineSize];
    int tag;
    address_t base;
    int index;
//boveerle
	module* req_sender;
//eoveerle

    int port_index;

    for (port_index = 0; port_index < nPorts; port_index++)
    {
      if (!returnBuffer[port_index].ready)
      {
        if (MSHR.GetRead(&address, &base, &index, &size, &port, &instruction, &tag, data
//boveerle
, &req_sender
//eoveerle
))
        {
          returnBuffer[port_index].port = port;
          returnBuffer[port_index].instr = instruction;
          returnBuffer[port_index].base = base;
          returnBuffer[port_index].index = index;
          returnBuffer[port_index].address = address;
          returnBuffer[port_index].size = size;
          memcpy(returnBuffer[port_index].data, data, returnBuffer[port_index].size);
          returnBuffer[port_index].id = ID_MSHR;
          returnBuffer[port_index].uid = tag;
          returnBuffer[port_index].ready = true;
//boveerle
	returnBuffer[port_index].req_sender = req_sender;
	//cerr << "@SendMSHRDatatoCPU req_sender=" << returnBuffer[port_index].req_sender << endl;//->name() << endl;
//eoveerle
        }
        else
        {
          return changed;
        }
      }
    }

    return changed;
  }

  bool SendCacheDatatoMem()
  {
    bool changed = false;
//     QueuePointer<CachePipeStageMultiPorted<T, nSources, nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nStages> cacheit;
    QueuePointer<CachePipeStageMultiPorted<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize, nPorts>, nMSHR * 2> cacheit;
    int index;

    cacheit = requestQueue.SeekAtHead();
    if (cacheit)
    {
      for (index = 0; (!requestBuffer.ready) && (index < nPorts); index++)
      {
        switch (cacheit->state[index])
        {
          case WRITE_MISS:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = false;
            requestBuffer.size = nLineSize; /* a full line is requested */
            requestBuffer.address = cacheit->address[index];
            requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
            requestBuffer.index = 0;
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = WRITE_MISS_WAITING_ACCEPT;
            break;
          case WRITE_MISS_EVICTION:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = true;
            requestBuffer.size = nLineSize;
            requestBuffer.address = cacheit->evictedaddress[index] & (~(address_t)(nLineSize - 1));
            requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
            requestBuffer.index = 0;
            memcpy(requestBuffer.data, cacheit->linedata[index], requestBuffer.size);
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = WRITE_MISS_EVICTION_WAITING_ACCEPT;
            break;
          case WRITE_MISS_EVICTION_WRITE_DONE:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = false;
            requestBuffer.size = nLineSize; /* a full line is requested */
            requestBuffer.address = cacheit->address[index];
            requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
            requestBuffer.index = 0;
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = WRITE_MISS_EVICTION_WRITE_DONE_WAITING_ACCEPT;
            break;
          case WRITE_MISS_NO_READ:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = true;
            requestBuffer.size = cacheit->size[index];
            requestBuffer.address = cacheit->address[index];
            requestBuffer.base = requestBuffer.address;
            requestBuffer.index = 0;
            memcpy(requestBuffer.data, cacheit->data[index], requestBuffer.size);
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = WRITE_MISS_NO_READ_WAITING_ACCEPT;
            break;
          case READ_MISS:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = false;
            requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
            requestBuffer.address = cacheit->address[index];
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = READ_MISS_WAITING_ACCEPT;
            break;
          case READ_MISS_EVICTION:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = true;
            requestBuffer.size = nLineSize;
            requestBuffer.address = cacheit->evictedaddress[index] & (~(address_t)(nLineSize - 1));
            requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
            requestBuffer.index = 0;
            memcpy(requestBuffer.data, cacheit->linedata[index], requestBuffer.size);
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = READ_MISS_EVICTION_WAITING_ACCEPT;
            break;
          case READ_MISS_EVICTION_WRITE_DONE:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = false;
            requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
            requestBuffer.address = cacheit->address[index];
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = READ_MISS_EVICTION_WRITE_DONE_WAITING_ACCEPT;
            break;
          case PREFETCH_MISS:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = false;
            requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
            requestBuffer.address = cacheit->address[index];
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = PREFETCH_MISS_WAITING_ACCEPT;
            break;
          case PREFETCH_MISS_EVICTION:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = true;
            requestBuffer.size = nLineSize;
            requestBuffer.address = cacheit->evictedaddress[index] & (~(address_t)(nLineSize - 1));
            requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
            requestBuffer.index = 0;
            memcpy(requestBuffer.data, cacheit->linedata[index], requestBuffer.size);
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = PREFETCH_MISS_EVICTION_WAITING_ACCEPT;
            break;
          case PREFETCH_MISS_EVICTION_WRITE_DONE:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = false;
            requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
            requestBuffer.address = cacheit->address[index];
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = PREFETCH_MISS_EVICTION_WRITE_DONE_WAITING_ACCEPT;
            break;
          case EVICT_READY:
            requestBuffer.instr = cacheit->instr[index];
            requestBuffer.write = true;
            requestBuffer.size = nLineSize;
            requestBuffer.address = cacheit->evictedaddress[index] & (~(address_t)(nLineSize - 1));
            requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
            requestBuffer.index = 0;
            memcpy(requestBuffer.data, cacheit->linedata[index], requestBuffer.size);
            requestBuffer.uid = index;
            requestBuffer.ready = true;
            cacheit->state[index] = EVICT_READY_WAITING_ACCEPT;
            break;
        }
//boveerle
	requestBuffer.req_sender = cacheit->req_sender[index];
//eoveerle
      }
    }
    cacheit = requestQueue.SeekAtHead();
    if (cacheit)
    {
      bool remove;
      for (remove = true, index = 0; remove && index < nPorts; index++)
      {
        remove = (cacheit->state[index] == NOTHING);
      }
      if (remove)
      {
        requestQueue.RemoveHead();
        changed = true;
      }
    }

    return changed;
  }

  bool SendData()
  {
    bool changed = false;
    int index;

    /* if there is nothing in the returnBuffer the cache can try to sent something to
     * the CPU */
    changed = SendMSHRDatatoCPU();

    changed = SendCacheDatatoCPU() || changed;

    if (!requestBuffer.ready)
    {
      changed = SendCacheDatatoMem() || changed;
    }

    for (index = 0; index < nPorts; index++)
    {
      if (returnBuffer[index].ready)
      {
        outCachetoCPUValid[index] = true;
        outCachetoCPUInstruction[index] = returnBuffer[index].instr;
        outCachetoCPUSize[index] = returnBuffer[index].size > nCachetoCPUDataPathSize ? nCachetoCPUDataPathSize : returnBuffer[index].size;
        outCachetoCPUAddress[index] = returnBuffer[index].base + returnBuffer[index].index;
        outCachetoCPUData[index].Write(&returnBuffer[index].data[returnBuffer[index].index],
                                       (returnBuffer[index].size > nCachetoCPUDataPathSize) ? nCachetoCPUDataPathSize : returnBuffer[index].size);
        outCachetoCPUTag[index] = returnBuffer[index].uid;
//boveerle
	//cerr << "@ReturnBuffer.ready, req_sender=" << returnBuffer.req_sender << endl;
	    outCachetoCPUReqSender[index] = returnBuffer[index].req_sender;
//eoveerle
      }
      else
      {
        outCachetoCPUValid[index] = false;
      }
    }


    if (requestBuffer.ready)
    {
      outCachetoMemInstruction = requestBuffer.instr;
      outCachetoMemAddress = requestBuffer.address;
      if (requestBuffer.write)
      {
        outCachetoMemCommand = WRITE_CACHE_COMMAND;
      }
      else
      {
        outCachetoMemCommand = READ_CACHE_COMMAND;
      }
      int size = requestBuffer.size;
      if (requestBuffer.write && size > nCachetoMemDataPathSize)
      {
        size = nCachetoMemDataPathSize;
      }
      outCachetoMemSize = size;
      if (requestBuffer.write)
      {
        outCachetoMemData.Write(requestBuffer.data + requestBuffer.index, size);
      }
      outCachetoMemTag = requestBuffer.uid;
      outCachetoMemValid = true;
    }
    else
    {
      outCachetoMemValid = false;
    }

    return changed;
  }

  /*******************************************************************************/
  /*******************************************************************************/
  /********************** Internal Control Functions (end) ***********************/
  /*******************************************************************************/
  /*******************************************************************************/

public:
  int ready;
  /* statistics */
  uint64_t accesses;
  uint64_t accesses_read;
  uint64_t accesses_write;
  uint64_t accesses_prefetch;
  uint64_t accesses_evict;
  uint64_t hits;
  uint64_t hits_read;
  uint64_t hits_write;
  uint64_t hits_prefetch;
  uint64_t hits_evict;
  uint64_t hits_vc;
  uint64_t hits_vc_read;
  uint64_t hits_vc_write;
  uint64_t hits_mshr;
  uint64_t hits_mshr_read;
  uint64_t hits_mshr_write;
  uint64_t hits_mshr_prefetch;
  uint64_t hits_mshr_evict;
  uint64_t misses;
  uint64_t misses_read;
  uint64_t misses_write;
  uint64_t misses_prefetch;
  uint64_t misses_evict;
  uint64_t writebacks;
  uint64_t writebacks_read;
  uint64_t writebacks_write;
  uint64_t writebacks_prefetch;
  uint64_t writebacks_evict;
  uint64_t blocked_mshrs;
  uint64_t existent_mshrs;
  uint64_t new_mshrs;
  uint64_t rq_size;
  uint64_t waiting_memory_bus;

  /* Clock */
  sc_in_clk inClock;

  /* input signals from the CPU */
  ml_in_valid inCPUtoCacheValid[nPorts];
  ml_in_enable inCPUtoCacheEnable[nPorts];
  ml_in_data<T> inCPUtoCacheInstruction[nPorts];
  ml_in_cache_cmd inCPUtoCacheCommand[nPorts];
  ml_in_vector<nCPUtoCacheDataPathSize> inCPUtoCacheData[nPorts];
  ml_in_size inCPUtoCacheSize[nPorts];
  ml_in_addr inCPUtoCacheAddress[nPorts];
  ml_in_data<int> inCPUtoCacheTag[nPorts];
  ml_in_accept inCPUtoCacheAccept[nPorts];
//boveerle
    ml_in_reqsender inCPUtoCacheReqSender[nPorts];
//eoveerle

  /* output signals to the CPU */
  ml_out_valid outCachetoCPUValid[nPorts];
  ml_out_data<T> outCachetoCPUInstruction[nPorts];
  ml_out_vector<nCachetoCPUDataPathSize> outCachetoCPUData[nPorts];
  ml_out_size outCachetoCPUSize[nPorts];
  ml_out_addr outCachetoCPUAddress[nPorts];
  ml_out_data<int> outCachetoCPUTag[nPorts];
  ml_out_accept outCachetoCPUAccept[nPorts];
  ml_out_enable outCachetoCPUEnable[nPorts];
//boveerle
    ml_out_reqsender outCachetoCPUReqSender[nPorts];
//eoveerle

  /* input signals from the memory system */
  ml_in_valid inMemtoCacheValid;
  ml_in_enable inMemtoCacheEnable;
  ml_in_data<T> inMemtoCacheInstruction;
  ml_in_vector<nMemtoCacheDataPathSize> inMemtoCacheData;
  ml_in_addr inMemtoCacheAddress;
  ml_in_size inMemtoCacheSize;
  ml_in_data<int> inMemtoCacheTag;
  ml_in_accept inMemtoCacheAccept;

  /* output signals to the memory system */
  ml_out_valid outCachetoMemValid;
  ml_out_data <T> outCachetoMemInstruction;
  ml_out_cache_cmd outCachetoMemCommand;
  ml_out_vector<nCachetoMemDataPathSize> outCachetoMemData;
  ml_out_size outCachetoMemSize;
  ml_out_addr outCachetoMemAddress;
  ml_out_data<int> outCachetoMemTag;
  ml_out_accept outCachetoMemAccept;
  ml_out_enable outCachetoMemEnable;

  /* signals to know if the external control needs to be waken up because the internal control
   * modified the state of the cache */
  ml_out_data<bool> outStateChanged;
  ml_in_data<bool> inStateChanged;
  /* signal to connect outStateChanged and inStateChanged */
  ml_signal_data<bool> stateChanged;

  MicrolibCacheWBNBVCMultiPorted(const char *name):sc_module(name)
  {
    int i;

    ready = 1;

    outStateChanged(stateChanged);
    inStateChanged(stateChanged);

    SC_HAS_PROCESS(MicrolibCacheWBNBVCMultiPorted);
/*
    // Make the ExternalControl process sensitive to the input instructions and acknowledge
    // Make the ExternalControl process sensitive to the input instructions and acknowledge
    SC_METHOD(ExternalControl);
    for (i=0;i<nPorts;i++)
      sensitive << inCPUtoCacheValid[i] << inCPUtoCacheAccept[i];
    sensitive << inMemtoCacheValid << inMemtoCacheAccept;
    sensitive << inStateChanged;
    // Make the InternalControl sensitive to the clock front edge
    SC_METHOD(InternalControl);
    sensitive_pos << inClock;
*/

SC_METHOD(CheckCPUAccept);
for (i=0;i<nPorts;i++) sensitive << inCPUtoCacheAccept[i];
SC_METHOD(CheckMemAccept);
sensitive << inMemtoCacheAccept;
SC_METHOD(CheckCPURequests);
for (i=0;i<nPorts;i++) sensitive << inCPUtoCacheValid[i];
SC_METHOD(CheckMemValid);
sensitive << inMemtoCacheValid;

SC_METHOD(start_of_cycle);
sensitive_pos << inClock;
SC_METHOD(end_of_cycle);
sensitive_neg << inClock;



    accesses = 0;
    accesses_read = 0;
    accesses_write = 0;
    accesses_prefetch = 0;
    accesses_evict = 0;
    hits = 0;
    hits_read = hits_write = hits_prefetch = hits_evict = 0;
    hits_vc = hits_vc_read = hits_vc_write = 0;
    hits_mshr = 0;
    hits_mshr_read = hits_mshr_write = hits_mshr_prefetch = hits_mshr_evict = 0;
    misses = 0;
    misses_read = misses_write = misses_prefetch = misses_evict = 0;
    writebacks = 0;
    writebacks_read = writebacks_write = writebacks_prefetch = writebacks_evict = 0;
    blocked_mshrs = 0;
    existent_mshrs = 0;
    new_mshrs = 0;
    rq_size = 0;
    waiting_memory_bus = 0;

  }

  void ExternalControl()
  {
    /* if an accept is received from the CPU enable the output to the CPU */
    CheckCPUAccept();

    /* if an accept is received from the memory system enable the output to the memory system */
    CheckMemAccept();

    /* if the CPU request a load/store */
    CheckCPURequests();

    /* if the memory system replies to a miss */
    CheckMemValid();
  }

  void InternalControl()
  {
    bool changed = false;

    /* checks that the data sent in the previous cycle has been accepted
     * and update the state of the cache pipeline and the MSHR queue if
     * necessary */
    changed = CheckAcceptedData();


    /* checks that the module has received something from the memory system
     * and update the MSHR queue state and the cache state if necessary */
    changed = ReadMemData() || changed;


    /* run the cache pipeline */
    changed = RunCachePipeline() || changed;


    /* checks that the cpu has sent something to the module an add it to the
     * cache pipeline */
    changed = ReadCPUData() || changed;


    /* set the cache head state */
    changed = SetCacheHeadState() || changed;


    /* checks if there is something to be sent from the cache pipeline and the
     * MSHR queue and send them to the cpu and memory system */
    changed = SendData() || changed;


    // 		outBusy = !cacheQueue.Empty() || !MSHR.Empty();

    /* set the outStateChanged if the state of the cache changed,
     * this will call the external control if necessary */
    if (changed)
    {
      outStateChanged = !inStateChanged;
    }

    if (cacheQueue.Empty())
    {
      ready = 1;
    }
    else
    {
      ready = 0;
    }

    rq_size += requestQueue.Size();
  }



  void start_of_cycle()
  {
    SendData();

    if (cacheQueue.Empty())
    {
      ready = 1;
    }
    else
    {
      ready = 0;
    }

    rq_size += requestQueue.Size();
  }








  void end_of_cycle()
  {
    bool changed = false;

    /* checks that the data sent in the previous cycle has been accepted
     * and update the state of the cache pipeline and the MSHR queue if
     * necessary */
    changed = CheckAcceptedData();


    /* checks that the module has received something from the memory system
     * and update the MSHR queue state and the cache state if necessary */
    changed = ReadMemData() || changed;


    /* run the cache pipeline */
    changed = RunCachePipeline() || changed;


    /* checks that the cpu has sent something to the module an add it to the
     * cache pipeline */
    changed = ReadCPUData() || changed;


    /* set the cache head state */
    changed = SetCacheHeadState() || changed;


  }







  void Read(void *buffer, address_t address, int size)
  {
    cache.Read(buffer, address, size);
  }

  void Write(address_t address, const void *buffer, int size)
  {
    cache.Write(address, buffer, size);
  }

  void Set(address_t address, UInt8 data, int size)
  {
    cache.Set(address, data, size);
  }

  bool Check()
  {
    return cacheQueue.Check();
  }

  void DumpStats(ostream& os)
  {
    /*
    	os << name() << "_accesses " << accesses << " # " << name() << " accesses" << endl;
    	os << name() << "_accesses_read " << accesses_read << " # " << name() << " accesses_read" << endl;
    	os << name() << "_accesses_write " << accesses_write << " # " << name() << " accesses_write" << endl;
    	os << name() << "_accesses_prefetch " << accesses_prefetch << " # " << name() << " accesses_prefetch" << endl;
    	os << name() << "_accesses_evict " << accesses_evict << " # " << name() << " accesses_evict" << endl;

    	os << name() << "_hits " << hits << " # " << name() << " hits" << endl;
    	os << name() << "_hits_read " << hits_read << " # " << name() << " hits_read" << endl;
    	os << name() << "_hits_write " << hits_write << " # " << name() << " hits_write" << endl;
    	os << name() << "_hits_prefetch " << hits_prefetch << " # " << name() << " hits_prefetch" << endl;
    	os << name() << "_hits_evict " << hits_evict << " # " << name() << " hits_evict" << endl;

    	os << name() << "_mshr_hits " << mshr_hits << " # " << name() << " mshr_hits" << endl;
    	os << name() << "_mshr_hits_read " << mshr_hits_read << " # " << name() << " mshr_hits_read" << endl;
    	os << name() << "_mshr_hits_write " << mshr_hits_write << " # " << name() << " mshr_hits_write" << endl;
    	os << name() << "_mshr_hits_prefetch " << mshr_hits_prefetch << " # " << name() << " mshr_hits_prefetch" << endl;

    	os << name() << "_misses " << misses << " # " << name() << " misses" << endl;
    	os << name() << "_misses_read " << misses_read << " # " << name() << " misses_read" << endl;
    	os << name() << "_misses_write " << misses_write << " # " << name() << " misses_write" << endl;
    	os << name() << "_misses_prefetch " << misses_prefetch << " # " << name() << " misses_prefetch" << endl;
    	os << name() << "_misses_evict " << misses_evict << " # " << name() << " misses_evict" << endl;

    	os << name() << "_miss_rate " << (double) misses / (double) accesses << " # " << name() << " miss rate" << endl;

    	os << name() << "_writebacks " << writebacks << " # " << name() << " writebacks" << endl;
    	os << name() << "_writebacks_read " << writebacks_read << " # " << name() << " writebacks_read" << endl;
    	os << name() << "_writebacks_write " << writebacks_write << " # " << name() << " writebacks_write" << endl;
    	os << name() << "_writebacks_prefetch " << writebacks_prefetch << " # " << name() << " writebacks_prefetch" << endl;
    	os << name() << "_writebacks_evict " << writebacks_evict << " # " << name() << " writebacks_evict" << endl;

    	os << name() << "_new_mshrs " << new_mshrs << " # " << name() << " new mshrs" << endl;
    	os << name() << "_existent_mshrs " << existent_mshrs << " # " << name() << " existent_mshrs" << endl;
    	os << name() << "_blocked_mshrs " << blocked_mshrs << " # " << name() << " blocked_mshrs" << endl;
    */
  }

  friend ostream& operator << (ostream& os, MicrolibCacheWBNBVCMultiPorted<T, nSources, replacementPolicy, nCPUtoCacheDataPathSize, nCachetoCPUDataPathSize, nCPULineSize, nMemtoCacheDataPathSize, nCachetoMemDataPathSize, nLineSize, nCacheLines, nAssociativity, nStages, nDelay, nPorts, nMSHR, nMSHRRead, nVCCacheLines, nVCAssociativity, nVCReplacementPolicy>& cache)
  {
    int index;
    os << "=================" << cache.name() << " ==================" << endl;
    os << "Cache Pipeline:" << endl;
    os << cache.cacheQueue;
    os << "Miss Address File:" << endl;
    os << cache.MSHR << endl;
    os << "Delayed Write Buffer:" << endl;
    os << cache.writeBuffer << endl;
    for (index = 0; index < nPorts; index++)
    {
      os << "Return Buffer[" << index << "]:" << endl;
      os << cache.returnBuffer[index] << endl;
    }
    os << "Request Buffer:" << endl;
    os << cache.requestBuffer << endl;
    os << "Request Queue:" << endl;
    os << cache.requestQueue << endl;
    os << "==========================================================" << endl;

    return os;
  }

  void PrintState()
  {
    int index;
    cerr << "=================" << name() << " ==================" << endl;
    cerr << "Cache Pipeline:" << endl;
    cerr << cacheQueue;
    cerr << "Miss Address File:" << endl;
    cerr << MSHR << endl;
    cerr << "Delayed Write Buffer:" << endl;
    cerr << writeBuffer << endl;
    for (index = 0; index < nPorts; index++)
    {
      cerr << "Return Buffer[" << index << "]:" << endl;
      cerr << returnBuffer[index] << endl;
    }
    cerr << "Request Buffer:" << endl;
    cerr << requestBuffer << endl;
    cerr << "Request Queue:" << endl;
    cerr << requestQueue << endl;
    cerr << "==========================================================" << endl;
  }

};

#endif //__CACHEWBNBVCMULTIPORTED_H__
