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

/***************************************************************************
          CacheWBNB.h  - Non-blocking Writeback Allocate on Write Cache
                             -------------------
    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_CACHEWBNB_H__
#define __MICROLIB_CACHEWBNB_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 a cache. It is a template class.
 * Implements a cache with one input bus (to upper levels) and one output bus (to lower levels).
 * It can have more than one input port (sharing the same bus) and is the cache the module that
 * controls the accepted request.
 * The cache is non blocking, uses writeback as write policy and allocate on write as allocation policy */
/* T, nSources: data type manipulated by the instructions */
/* replacementPolicy: replacement policy to apply (LRU, FIFO, random) */
/* 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 */
/* nLineSize: size of a cache line in bytes */
/* nCacheLines: number of cache lines */
/* nAssociativity: associativity of the cache,
 *        if nAssociativitys == nCacheLine complete associative,
 *        if nAssociativity == 1 direct cache
 *        must be bigger than 0 */
/* nStages: number of pipeline stages */
/* nDelay: number of cpu cycles taken by each stage */
/* nPorts: the total number of ports that can be used to service requests, should be bigger than 0.
 *         just one request is accepted by cycle */
/* nMSHR: maximum number of cache lines that can be waiting missing data */
/* nMSHRRead: maximum number of requests that can be waiting per nMSHR */
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>
class MicrolibCacheWBNB:public sc_module
{
public:
  /* cache: it is the structure that will keep the data of the cache */
  CacheContainer<nCacheLines, nLineSize, nAssociativity, replacementPolicy, 1> cache;
private:
  /* cacheQueue: implements the cache pipeline */
  Queue<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, 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;
  /* 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 that are ready
   *               to be sent to the lower memory level */
  Queue<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nMSHR * 2> requestQueue;

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

  /* NOTE: for speed reasons errors are not checked in external control functions */

  /* If an accept is received from the CPU enable the output to the CPU.
   * Just one accept can be handled.
   * Lower ports are prioritary. */
  void CheckCPUAccept()
  {
    /* port: indicates if a port has been already accepted, so other ports are not accepted */
    int port;
    /* index: used as index of the ports */
    int index;

    /* parse all the ports */
    for (port = -1, index = 0; index < nPorts; index++)
    {
      /* if no port has already been accepted, then actual port can be accepted if signal accept is set,
       * otherwise, if a port has already been accepted, do not accept on the other ports */
      if (port == -1)
      {
        if (inCPUtoCacheAccept[index])
        {
          outCachetoCPUEnable[index] = true;
          port = index;
        }
        else
        {
          outCachetoCPUEnable[index] = false;
        }
      }
      else
        outCachetoCPUEnable[index] = false;
    }
  }

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

  /* This function checks if there are new requests.
   * If there are new requests, just the one appearing in the lower port is accepted,
   * the others are not accepted. */
  void CheckCPURequests()
  {
    /* cacheit: iterator to parse the contents of the cache pipeline */
    QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit;
    /* port: indicates if a request has been already accepted, and in which port */
    int port;
    /* index: used to parse the ports of the cache */
    int index;

    /* First check if there is a port with a request.
     * Just one request can be accepted */
    for (port = -1, index = 0; index < nPorts; index++)
    {
      /* If no port has been accepted, check if the port contains a request */
      if (port == -1)
      {
        if (inCPUtoCacheValid[index])
        {
          /* The port contains a request, then set the port to the number of port that makes the request */
          port = index;
        }
        else
        {
          outCachetoCPUAccept[index] = false;
        }
      }
      else
        /* Upper ports are not accepted if there is a request in a lower port */
        outCachetoCPUAccept[index] = false;
    }

    /* Is there a port with a request? */
    if (port != -1)
    {
      /* There is request, then check if it can be accepted by the cache. */

      /* If the cache pipeline is empty, the request can be accepted without problems. */
      if (cacheQueue.Empty())
      {
        outCachetoCPUAccept[port] = true;
      }
      else
      {
        /* The cache pipeline is not empty,
         * check if there will be place in the cache pipeline at the beginning of the next cycle. */
        int stage = 0;
        bool cont = true;

        for (cacheit = cacheQueue.SeekAtTail();
             cont && cacheit;
             cacheit--, stage++)
        {
          if (cacheit->stage > stage)
          {
            outCachetoCPUAccept[port] = true;
            cont = false;
          }
          else
          {
            if (stage != nStages - 1)
            {
              if (cacheit->delay[cacheit->stage] > 1)
              {
                outCachetoCPUAccept[port] = false;
                cont = false;
              }
            }
            else
            {
              if (cacheit->delay[stage] > 0)
              {
                outCachetoCPUAccept[port] = false;
                cont = false;
              }
              else
              {
                if (cacheit->write)
                {
                  switch (cacheit->state)
                  {
                    case WRITE:
                      outCachetoCPUAccept[port] = cache.hit(cacheit->address);
                      break;
// 									case WRITE_MISS_NO_READ:
// 										outCachetoCPUAccept[port] = inMemtoCacheAccept;
// 										break;
                    default:
                      outCachetoCPUAccept[port] = false;
                      break;
                  }
                }
                else
                {
                  outCachetoCPUAccept[port] = false;
                }
              }
            }
          }
        }
      }
    }
  }

  /* If the memory system replies to a miss then accept the incomming data */
  void CheckMemValid()
  {
    /* nothing has to be checked, the system never blocks the memory inputs */
    outCachetoMemAccept = inMemtoCacheValid;
  }

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

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

  /* Checks that the upper level accepted the data sent by the cache.
   * Returns true if the state of the cache changed.
   */
  bool CheckCPUAcceptedData()
  {
    /* port: index of the port that acceptd the data. */
    int port;
    /* index: used to parse the ports of the cache. */
    int index;
    /* changed: true if the state of the cache changed. */
    bool changed = false;

//boveerle
    /* check if an accept from the cpu is being waited */
    if(returnBuffer.ready)
    {
//eoveerle
    /* Parse the port looking for a port that has accepted the data. */
    for (index = 0, port = -1; index < nPorts; index++)
    {
      if (port == -1)
      {
        if (inCPUtoCacheAccept[index])
        {
          port = index;
        }
      }
      else
      {
        /* If more than one port has sent an accept signal, then error.
         * The cache does not send more than one request at a time.
         */
        if (inCPUtoCacheAccept[index])
        {
          cerr << "Error(" << name() << "): more than one module/port accepted data from the cache." << endl;
          cerr << *this;
          exit(-1);
        }
      }
    }

    /* Did any port accepted data? */
    if (port != -1)
    {
      /* If there was nothing in the return buffer,
       * there should have not been an accept signal from the upper level */
      if (!returnBuffer.ready)
      {
        cerr << "Error(" << name() << "): data accepted by the cpu when nothing has been sent" << endl;;
        cerr << *this;
        exit(-1);
      }

      /* Update the state of the return buffer. */
      /* Check how much has still to be sent. */
      returnBuffer.size = returnBuffer.size - nCachetoCPUDataPathSize;
      if (returnBuffer.size > 0)
      {
        /* The returnBuffer is not empty, so it continues servicing the data. */
        returnBuffer.index = returnBuffer.index + nCachetoCPUDataPathSize;
        if (returnBuffer.index == nCPULineSize) returnBuffer.index = 0;
        returnBuffer.address = returnBuffer.base + returnBuffer.index;
      }
      else
      {
        /* The returnBuffer is empty. */
        returnBuffer.ready = false;
      }
    }
//boveerle
      } //if(returnBuffer.ready)
//eoveerle

    /* Return true if the state of the cache changed */
    return changed;
  }

  /* Checks that the lower level accepted the data sent by the cache.
   * Returns true if the state of the cache changed.
   */
  bool CheckMemAcceptedDatafromCache()
  {
    /* cacheit: iterator to access the request Queue */
    QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nMSHR * 2> cacheit;
    /* changed: true if the state of the cache changed. */
    bool changed = false;

    /* Did the lower level accepted the data? */
    if (inMemtoCacheAccept)
    {
      /* If the lower level accepted data from the cache but teh cache did not send any data, then error */
      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 sent request was a read or a write. */
      if (requestBuffer.write)
      {
        /* The request is a write. */
        /* If last request has finished (all data has been sent)
         * remove the request from the cache pipeline */
        requestBuffer.size -= nCachetoMemDataPathSize;
        if (requestBuffer.size > 0)
        {
          /* The request has not been finished, the request has to be keeped,
           * the address has to be increased,
           * and the cache request queue is not modified. */
          requestBuffer.index += nCachetoMemDataPathSize;
          requestBuffer.address = requestBuffer.base + requestBuffer.index;
        }
        else
        {
          /* The request has been finished, the cache request queue has to be updated. */
          /* Check in which state was the entry that made the request. */
          cacheit = requestQueue.SeekAtHead();
          switch (cacheit->state)
          {
            case WRITE_MISS_EVICTION:
              /* The request was an eviction caused by a write miss,
               * the request of the miss address can now be done. */
              cacheit->state = WRITE_MISS_EVICTION_WRITE_DONE;
              break;
// 					case WRITE_MISS_NO_READ:
// 						requestQueue.RemoveHead();
// 						changed = true;
// 						break;
            case READ_MISS_EVICTION:
              /* The request was an eviction caused by a read miss,
               * the request of the miss address can now be done. */
              cacheit->state = READ_MISS_EVICTION_WRITE_DONE;
              break;
            case PREFETCH_MISS_EVICTION:
              /* The request was an eviction caused by a prefetch miss,
               * the request of the miss address can now be done. */
              cacheit->state = PREFETCH_MISS_EVICTION_WRITE_DONE;
              break;
            case EVICT_READY:
              /* The request was an eviction caused by an eviction hit,
               * the entry can be removed from the request queue. */
              requestQueue.RemoveHead();
              /* The state of the cache request queue has changed. */
              changed = true;
              break;
          }
          /* Set the request buffer to not ready, this way new requests
           * can be issued. */
          requestBuffer.ready = false;
        }
      }
      else
      {
        /* The request is a read. */
        /* If last request has finished (all data has been sent)
         * remove the request from the cache pipeline */
        cacheit = requestQueue.SeekAtHead();
        switch (cacheit->state)
        {
          case WRITE_MISS:
          case WRITE_MISS_EVICTION_WRITE_DONE:
            /* The request was a read caused by a write miss,
             * the entry can be removed from the request queue. */
          case READ_MISS:
          case READ_MISS_EVICTION_WRITE_DONE:
            /* The request was a read caused by a read miss,
             * the entry can be removed from the request queue. */
          case PREFETCH_MISS:
          case PREFETCH_MISS_EVICTION_WRITE_DONE:
            /* The request was a read caused by a prefetch miss,
             * the entry can be removed from the request queue. */
            requestQueue.RemoveHead();
            /* The state of the cache request queue has been changed */
            changed = true;
            break;
        }
        /* Set the request buffer to not ready, this way new requests
         * can be issued. */
        requestBuffer.ready = false;
      }
    }
    else
    {
      /* If data has not been accepted, the cache has been blocked. */
      if (requestBuffer.ready)
      {
        waiting_memory_bus++;
      }
    }

    /* Return true if the state of the cache changed */
    return changed;
  }

  /* This function checks if upper and lower levels of the cache have accepted
   * data sent by the cache.
   * Returns true if the cache state changed. */
  bool CheckAcceptedData()
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Check if the upper level accepted data sent by the cache. */
    changed = CheckCPUAcceptedData();

    /* Check if the lower level accepted data sent by the cache. */
    changed = CheckMemAcceptedDatafromCache() || changed;

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Updates the state of the writeBuffer with data comming from lower levels.
   * Returns true if the cache state changed. */
  bool UpdateWriteBuffer()
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Check that data has been received from lower leves. */
    if (inMemtoCacheEnable)
    {
      /* Check that it is not an incorrect line. */
      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 bytes were received before, then error, we have received two times the same data. */
          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);
        /* Update state of the writeBuffer. */
        writeBuffer.size += nMemtoCacheDataPathSize;
        writeBuffer.address = inMemtoCacheAddress & (~((address_t)(nLineSize - 1)));
        writeBuffer.uid = inMemtoCacheTag;
      }
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Updates the state of the MSHR with received data.
   * Returns true if the cache state changed. */
  bool UpdateMSHRWithWriteBuffer()
  {
    /* buffer: temporary buffer to copy the data of the MSHR. */
    uint8_t buffer[nLineSize];
    /* locked_set: set index of the line that is ready. */
    int locked_set;
    /* locked_line: line index of the line that is ready. */
    int locked_line;
    /* dirty: indicates if the updated line is dirty */
    bool dirty;
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Update the MSHR with the received data. */
    MSHR.Update(writeBuffer.address, writeBuffer.buffer,
                &locked_set, &locked_line, &dirty, buffer);

    /* Update the cache with the updated MSHR entry. */
    cache.setCacheLine(locked_set, locked_line,
                       writeBuffer.address,
                       (char *)buffer,
                       true, dirty);
    /* Unlock the cache entry, the data is now ready. */
    cache.Unlock(locked_set, locked_line);

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Updates the state of the cache with data received from lower levels.
   * Returns true if the cache state changed. */
  bool ReadMemData()
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Updates the writeBuffer if data is received from the lower levels. */
    changed = UpdateWriteBuffer();

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

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Updates the state of the cache pipeline.
   * Returns true if the state of the pipeline changed. */
  bool RunCachePipeline()
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;
    /* cacheit: iterator to check the entries of the cache pipeline. */
    QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit;
    /* cacheitPrev: iterator to check the entries of the cache pipeline. */
    QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheitPrev;

    /* For each entry advance one stage if possible. */
    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;
    }

    /* Reduce the delay of each entry. */
    for (cacheit = cacheQueue.SeekAtHead(); cacheit; cacheit++)
    {
      if (cacheit->delay[cacheit->stage] > 0)
      {
        --cacheit->delay[cacheit->stage];
        changed = true;
      }
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Reads requests from the upper levels and add them to the cache pipeline.
   * Returns true if the cache state changed. */
  bool ReadCPUData()
  {
    /* entry: used as container of new requests. */
    CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize> *entry;
    /* index: used to scan cache ports */
    int index;
    /* port: indicates if there is a port where a request is being received */
    int port;
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Scan the ports to check if there is a new request. */
    for (index = 0, port = -1; index < nPorts; index++)
    {
      if (port == -1)
      {
        if (inCPUtoCacheEnable[index])
        {
          port = index;
        }
      }
      else
      {
        /* No more than one request can be received, error if so. */
        if (inCPUtoCacheEnable[index])
        {
          cerr << "Error(" << name() << "): more than one module sent data to the cache (and been accepted)." << endl;
          cerr << *this;
          exit(-1);
        }
      }
    }

    /* If there is a new request from the upper level, it is added to the cache pipeline. */
    if (port != -1)
    {
      accesses++;
      /* Check that the cache queue is not full,
       * it should never happen. */
      if (cacheQueue.Full())
      {
        cerr << "Error(" << name() << "): upper level request should not have been accepted" << endl;
        cerr << *this;
        exit(-1);
      }

      /* Create new entry in the cache pipeline. */
      entry = cacheQueue.New();
      /* Init the entry. */
      entry->stage = 0;
      for (int i = 0; i < nStages; i++)
        entry->delay[i] = nDelay;
      if (entry->delay[0] > 0) entry->delay[0]--;

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

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

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

      /* The state of the cache has changed. */
      changed = true;
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Sets the state of the cache pipeline head, in this case it is a read request.
   * cacheit: The head of the cache pipeline.
   * Returns true if the state of the cache has changed. */
  bool SetCacheHeadReadState(QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit)
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Check if the access is a hit or a miss. */
    cacheit->hit = cache.hit(cacheit->address);
    if (cacheit->hit)
    {
      /* The access is a hit,
       * modify the history (lru or fiifo) of the cache. */
      cache.historyAccess(cache.getSet(cacheit->address), cache.getLine(cacheit->address));
      hits++;
      hits_read++;
      /* Set the entry as a read hit that is ready. */
      cacheit->state = READ_HIT_READY;
      /* Copy the data that has to be returned. */
      if (nCPULineSize > 0)
      {
        cache.getCacheLineIndexed(cacheit->data,
                                  cache.getSet(cacheit->address),
                                  cache.getLine(cacheit->address),
                                  (cacheit->address & (~(address_t)(nCPULineSize - 1))) & ((address_t)(nLineSize - 1)),
                                  cacheit->size);
      }
      else
      {
        cache.getCacheLineIndexed(cacheit->data,
                                  cache.getSet(cacheit->address),
                                  cache.getLine(cacheit->address),
                                  (cacheit->address & ((address_t)(nLineSize - 1))),
                                  cacheit->size);
      }
    }
    else
    {
      /* The access is a miss. */
      /* Try to add the entry to the MSHR if it has not already been done. */
      if (cacheit->state == READ)
      {
        cacheit->set = cache.getReplaceSet(cacheit->address);
        cacheit->line = cache.getReplaceLine(cacheit->address);
        switch (MSHR.AddReadEntry(cacheit->address, cacheit->base, cacheit->index,
                                  cacheit->size, cacheit->port, cacheit->uid,
                                  cacheit->instr, cacheit->set, cacheit->line
//boveerle
, cacheit->req_sender
//eoveerle
))
        {
          case NOT_ACCEPTED_MSHR_ENTRY:
            /* The entry has not been accepted in the MSHR. */
            blocked_mshrs++;
            /* Return true if the state of the cache changed. */
            return changed;
            break;
          case EXISTENT_MSHR_ENTRY:
            /* There is already a miss requesting the address and the entry has been accepted.
             * The entry can be removed. */
            cacheQueue.RemoveHead();
            changed = true;
            existent_mshrs++;
            mshr_hits++;
            mshr_hits_read++;
            /* Return true if the state of the cache changed. */
            return changed;
            break;
          case NEW_MSHR_ENTRY:
            /* The entry has been accepted, but the line has to be requested. */
            cacheit->state = READ_MSHR;
            new_mshrs++;
            break;
        }
      }
      /* Set the state of the cache and the entry. */
      SetCacheHeadReadStateMSHR(cacheit);
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* This function tries to set the state of the read miss that has been added to the MSHR.
   * cacheit: the entry to set. */
  void SetCacheHeadReadStateMSHR(QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit)
  {

    /* If the line that the entry needs is blocked, do nothing. */
    if (cache.IsLocked(cacheit->set, cacheit->line))
    {
      return;
    }
    /* Lock the cache line that will be needed by the entry. */
    cache.Lock(cacheit->set, cacheit->line);
    misses++;
    misses_read++;
    /* Modify the history of the cache. */
    cache.historyAccess(cacheit->set, cacheit->line);
    /* Check if there is data to evict. */
    if (cache.getValid(cacheit->set, cacheit->line))
    {
      cacheit->evictedaddress = cache.getAddress(cacheit->set,
                                cacheit->line);
      cacheit->evicted = cache.getWrite(cacheit->set,
                                        cacheit->line);
      if (cacheit->evicted)
      {
        /* A line needs to be evicted. */
        writebacks++;
        writebacks_read++;
        cache.getCacheLine(cacheit->linedata,
                           cacheit->set,
                           cacheit->line);
        cacheit->state = READ_MISS_EVICTION;
      }
      else
      {
        /* There is no line to evict. */
        cacheit->state = READ_MISS;
      }
      cache.setValid(cacheit->set, cacheit->line, false);
    }
    else
    {
      /* There is no line to evict. */
      cacheit->evicted = false;
      cacheit->state = READ_MISS;
    }
  }

  /* Sets the state of the cache pipeline head, in this case it is a write request.
   * cacheit: The head of the cache pipeline.
   * Returns true if the state of the cache has changed. */
  bool SetCacheHeadWriteState(QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit)
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Check if the access is a hit or a miss. */
    cacheit->hit = cache.hit(cacheit->address);
    if (cacheit->hit)
    {
      /* The access is a hit,
       * modify the history (lru or fiifo) of the cache. */
      cache.historyAccess(cache.getSet(cacheit->address), cache.getLine(cacheit->address));
      hits++;
      hits_write++;
      /* Modify the data contained in the cache. */
      cache.setCacheLineIndexed(cache.getSet(cacheit->address),
                                cache.getLine(cacheit->address),
                                cacheit->data,
                                cacheit->address & ((address_t)(nLineSize - 1)),
                                cacheit->size,
                                true);
      /* The entry can be removed from the pipeline.  */
      cacheQueue.RemoveHead();
      changed = true;
    }
    else
    {
      /* The access is a miss. */
      /* Try to add the entry to the MSHR if it has not already been done. */
      if (cacheit->state == WRITE)
      {
        cacheit->set = cache.getReplaceSet(cacheit->address);
        cacheit->line = cache.getReplaceLine(cacheit->address);
        switch (MSHR.AddWriteEntry(cacheit->address, cacheit->base, cacheit->index,
                                   cacheit->size, (uint8_t *)cacheit->data,
                                   cacheit->instr, cacheit->set, cacheit->line
//boveerle
, cacheit->req_sender
//eoveerle
))
        {
          case NOT_ACCEPTED_MSHR_ENTRY:
            /* The entry has not been accepted in the MSHR. */
            blocked_mshrs++;
            /* Return true if the state of the cache changed. */
            return changed;
            break;
          case EXISTENT_MSHR_ENTRY:
            /* There is already a miss requesting the address and the entry has been accepted.
             * The entry can be removed. */
            cacheQueue.RemoveHead();
            changed = true;
            existent_mshrs++;
            mshr_hits++;
            mshr_hits_write++;
            /* Return true if the state of the cache changed. */
            return changed;
            break;
          case NEW_MSHR_ENTRY:
            /* The entry has been accepted, but the line has to be requested. */
            cacheit->state = WRITE_MSHR;
            new_mshrs++;
            break;
        }
      }
      /* Set the state of the cache and the entry. */
      SetCacheHeadWriteStateMSHR(cacheit);
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* This function tries to set the state of the write miss that has been added to the MSHR.
   * cacheit: the entry to set. */
  void SetCacheHeadWriteStateMSHR(QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit)
  {

    /* If the line that the entry needs is blocked, do nothing. */
    if (cache.IsLocked(cacheit->set, cacheit->line))
    {
      return;
    }
    /* Lock the cache line that will be needed by the entry. */
    cache.Lock(cacheit->set, cacheit->line);
    misses++;
    misses_write++;
    cache.historyAccess(cacheit->set, cacheit->line);
    /* Check if there is data to evict. */
    if (cache.getValid(cacheit->set, cacheit->line))
    {
      cacheit->evictedaddress = cache.getAddress(cacheit->set,
                                cacheit->line);
      cacheit->evicted = cache.getWrite(cacheit->set,
                                        cacheit->line);
      if (cacheit->evicted)
      {
        /* A line needs to be evicted. */
        writebacks++;
        writebacks_write++;

        cache.getCacheLine(cacheit->linedata,
                           cacheit->set,
                           cacheit->line);
        cacheit->state = WRITE_MISS_EVICTION;
      }
      else
      {
        /* There is no line to evict. */
        cacheit->state = WRITE_MISS;
      }
      cache.setValid(cacheit->set,
                     cacheit->line, false);
    }
    else
    {
      /* There is no line to evict. */
      cacheit->evicted = false;
      cacheit->state = WRITE_MISS;
    }
  }

  /* Sets the state of the cache pipeline head, in this case it is a prefetch request.
   * cacheit: The head of the cache pipeline.
   * Returns true if the state of the cache has changed. */
  bool SetCacheHeadPrefetchState(QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit)
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Check if the access is a hit or a miss. */
    cacheit->hit = cache.hit(cacheit->address);
    if (cacheit->hit)
    {
      /* The access is a hit,
       * modify the history (lru or fiifo) of the cache. */
      cache.historyAccess(cache.getSet(cacheit->address),
                          cache.getLine(cacheit->address));
      hits++;
      hits_prefetch++;
      /* The prefetch request can be removed. */
      cacheQueue.RemoveHead();
      changed = true;
    }
    else
    {
      /* The access is a miss. */
      /* Try to add the entry to the MSHR if it has not already been done. */
      if (cacheit->state == PREFETCH)
      {
        cacheit->set = cache.getReplaceSet(cacheit->address);
        cacheit->line = cache.getReplaceLine(cacheit->address);
        switch (MSHR.AddPrefetchEntry(cacheit->address, cacheit->base, cacheit->index,
                                      cacheit->size, cacheit->port, cacheit->uid,
                                      cacheit->instr, cacheit->set, cacheit->line
//boveerle
, cacheit->req_sender
//eoveerle
))
        {
          case NOT_ACCEPTED_MSHR_ENTRY:
            /* The entry has not been accepted in the MSHR. */
            blocked_mshrs++;
            /* Return true if the state of the cache changed. */
            return changed;
            break;
          case EXISTENT_MSHR_ENTRY:
            /* There is already a miss requesting the address and the entry has been accepted.
             * The entry can be removed. */
            cacheQueue.RemoveHead();
            changed = true;
            existent_mshrs++;
            mshr_hits++;
            mshr_hits_prefetch++;
            /* Return true if the state of the cache changed. */
            return changed;
            break;
          case NEW_MSHR_ENTRY:
            /* The entry has been accepted, but the line has to be requested. */
            cacheit->state = PREFETCH_MSHR;
            new_mshrs++;
            break;
        }
      }
      /* Set the state of the cache and the entry. */
      SetCacheHeadPrefetchStateMSHR(cacheit);
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* This function tries to set the state of the prefetch miss that has been added to the MSHR.
   * cacheit: the entry to set. */
  void SetCacheHeadPrefetchStateMSHR(QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit)
  {

    /* If the line that the entry needs is blocked, do nothing. */
    if (cache.IsLocked(cacheit->set, cacheit->line))
      return;
    /* Lock the cache line that will be needed by the entry. */
    cache.Lock(cacheit->set, cacheit->line);
    misses++;
    misses_prefetch++;
    cache.historyAccess(cacheit->set, cacheit->line);
    /* Check if there is data to evict. */
    if (cache.getValid(cacheit->set, cacheit->line))
    {
      cacheit->evictedaddress = cache.getAddress(cacheit->set,
                                cacheit->line);
      cacheit->evicted = cache.getWrite(cacheit->set,
                                        cacheit->line);
      if (cacheit->evicted)
      {
        /* A line needs to be evicted. */
        writebacks++;
        writebacks_prefetch++;
        cache.getCacheLine(cacheit->linedata,
                           cacheit->set,
                           cacheit->line);
        cacheit->state = PREFETCH_MISS_EVICTION;
      }
      else
      {
        /* There is no line to evict. */
        cacheit->state = PREFETCH_MISS;
      }
      cache.setValid(cacheit->set, cacheit->line, false);
    }
    else
    {
      /* There is no line to evict. */
      cacheit->evicted = false;
      cacheit->state = PREFETCH_MISS;
    }
  }

  /* Sets the state of the cache pipeline head, in this case it is a evict request.
   * cacheit: The head of the cache pipeline.
   * Returns true if the state of the cache has changed. */
  bool SetCacheHeadEvictState(QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit)
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;

    /* Check if the access is a hit or a miss. */
    cacheit->hit = cache.hit(cacheit->address);
    if (cacheit->hit)
    {
      hits++;
      hits_evict++;
      /* no history access, we want to reuse the entry of the evicted line */
      cacheit->set = cache.getSet(cacheit->address);
      cacheit->line = cache.getLine(cacheit->address);
      /* Check if the entry needs to be removed. */
      if (cache.getValid(cacheit->set, cacheit->line))
      {
        cacheit->evictedaddress = cache.getAddress(cacheit->set,
                                  cacheit->line);
        cacheit->evicted = cache.getWrite(cacheit->set,
                                          cacheit->line);
        cache.setValid(cacheit->set, cacheit->line, false);
        cache.setWrite(cacheit->set, cacheit->line, false);
        if (cacheit->evicted)
        {
          /* The cache entry needs to be removed. */
          writebacks++;
          writebacks_evict++;
          cache.getCacheLine(cacheit->linedata,
                             cacheit->set,
                             cacheit->line);
          cacheit->state = EVICT_READY;
        }
        else
        {
          /* The cache entry does not need to be removed,
           * the evict request can be removed. */
          cacheQueue.RemoveHead();
          changed = true;
        }
      }
      else
      {
        /* Why did we get a hit if the line is not valid??? Error.*/
        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.
       * The eviction request can be removed from the cache pipeline. */
      misses++;
      misses_evict++;
      cacheQueue.RemoveHead();
      changed = true;
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Sets the state of the cache pipeline head.
   * Returns true if the state of the cache has changed. */
  bool SetCacheHeadState()
  {
    /* changed: indicates if the state of the cache changed. */
    bool changed = false;
    /* cacheit: iterator to access at the head of the cache pipeline. */
    QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit;

    /* Get head of the pipeline. */
    cacheit = cacheQueue.SeekAtHead();
    if (cacheit)
    {
      /* Check if the head of the pipeline is ready to execute. */
      if (cacheit->stage == nStages - 1 && cacheit->delay[nStages - 1] == 0)
      {
        /* The head of the pipeline is ready. */
        switch (cacheit->state)
        {
          case READ:
          case READ_MSHR:
            /* The head of the pipeline is a read, set state. */
            changed = SetCacheHeadReadState(cacheit);
            /* If the read request is a miss that is ready, then insert it to the request queue,
             * and remove it from the cache pipeline. */
            if (cacheit && cacheit->state != READ && cacheit->state != READ_MSHR && cacheit->state != READ_HIT_READY)
            {
              requestQueue << *cacheit;
              cacheQueue.RemoveHead();
            }
            break;
          case WRITE:
          case WRITE_MSHR:
            /* The head of the pipeline is a write, set state. */
            changed = SetCacheHeadWriteState(cacheit);
            /* If the write request is a miss that is ready, then insert it to the request queue,
             * and remove it from the cache pipeline. */
            if (cacheit && cacheit->state != WRITE && cacheit->state != WRITE_MSHR)
            {
              requestQueue << *cacheit;
              cacheQueue.RemoveHead();
            }
            break;
          case PREFETCH:
          case PREFETCH_MSHR:
            /* The head of the pipeline is prefetch, set state. */
            changed = SetCacheHeadPrefetchState(cacheit);
            /* If the prefetch request is a miss that is ready, then insert it to the request queue,
             * and remove it from the cache pipeline. */
            if (cacheit && cacheit->state != PREFETCH && cacheit->state != PREFETCH_MSHR)
            {
              requestQueue << *cacheit;
              cacheQueue.RemoveHead();
            }
            break;
          case EVICT:
            /* The head of the pipeline is an eviction, set state. */
            changed = SetCacheHeadEvictState(cacheit);
            /* If the eviction is ready, then insert it to the request queue,
             * and remove it from the cache pipeline. */
            if (cacheit && cacheit->state != EVICT)
            {
              requestQueue << *cacheit;
              cacheQueue.RemoveHead();
            }
            break;
        }
      }
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Checks if there is an entry in the cache pipeline to send to the upper level (CPU).
   * Returns true if the state has changed.
   */
  bool SendCacheDatatoCPU()
  {
    /* changed: true if the state of the cache changed. */
    bool changed = false;
    /* cacheit: iterator to access the head of the cache pipeline */
    QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nStages> cacheit;

    /* Get the head of the cache */
    cacheit = cacheQueue.SeekAtHead();
    /* Check that the return Buffer is empty */
    if (!returnBuffer.ready)
    {
      /* If the head of the cache is a read hit ready, then set the return buffer with the entry */
      if (cacheit)
      {
        switch (cacheit->state)
        {
          case READ_HIT_READY:
            returnBuffer.port = cacheit->port;
            returnBuffer.instr = cacheit->instr;
            returnBuffer.base = cacheit->base;
            returnBuffer.index = cacheit->index;
            returnBuffer.address = cacheit->address;
            returnBuffer.size = cacheit->size;
            memcpy(returnBuffer.data, cacheit->data, returnBuffer.size);
            returnBuffer.id = ID_CACHE;
            returnBuffer.uid = cacheit->uid;
            returnBuffer.ready = true;
//boveerle
	returnBuffer.req_sender = cacheit->req_sender;
	//cerr << "@SendCacheDatatoCPU req_sender=" << returnBuffer.req_sender->name() << endl;
//eoveerle
            /* The entry can be removed from the pipeline. */
            cacheQueue.RemoveHead();

            /* The state has changed. */
            changed = true;
            break;
        }
      }
    }

    /* Return true if the state of the cache changed. */
    return changed;
  }

  /* Check if there is a MSHR entry ready to be sent to the upper level (CPU).
   * Returns true if the state of the cache has changed.
   */
  bool SendMSHRDatatoCPU()
  {
    /* Variables to get from the MSHR in case that data has to be sent to the upper level. */
    int port;
    T instruction;
    address_t address;
    int size;
    uint8_t data[nCPULineSize > 0 ? nCPULineSize : nLineSize];
    int tag;
    address_t base;
    int index;
    /* changed: true if the state of the cache has changed. */
    bool changed = false;
//boveerle
	module* req_sender;
//eoveerle

    /* Check that the return buffer is empty. */
    if (!returnBuffer.ready)
    {
      /* Get data from the MSHR if possible and set the return buffer. */
      if (MSHR.GetRead(&address, &base, &index, &size, &port, &instruction, &tag, data
//boveerle
, &req_sender
//eoveerle
))
      {
        returnBuffer.port = port;
        returnBuffer.instr = instruction;
        returnBuffer.base = base;
        returnBuffer.index = index;
        returnBuffer.address = address;
        returnBuffer.size = size;
        memcpy(returnBuffer.data, data, returnBuffer.size);
        returnBuffer.id = ID_MSHR;
        returnBuffer.uid = tag;
        returnBuffer.ready = true;
//boveerle
	returnBuffer.req_sender = req_sender;
	//cerr << "@SendMSHRDatatoCPU req_sender=" << returnBuffer.req_sender << endl;//->name() << endl;
//eoveerle
      }
    }

    /* Returns true if the state of the cache has changed. */
    return changed;
  }

  /* Checks if there is data ready to be sent to the lower level (memory).
   * Returns true if the state of the cache has changed.
   */
  bool SendCacheDatatoMem()
  {
    /* changed: true if the state of the cache has changed. */
    bool changed = false;
    /* cacheit: iterator to get a request to send from the request queue */
    QueuePointer<CachePipeStage<T, /*nSources,*/ nLineSize, nStages, nCPUtoCacheDataPathSize>, nMSHR * 2> cacheit;

    /* Get the head of the request queue. */
    cacheit = requestQueue.SeekAtHead();
    if (cacheit)
    {
      switch (cacheit->state)
      {
        case WRITE_MISS:
          /* The entry is a write miss, set the request buffer state to a read (!write). */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = false;
          requestBuffer.size = nLineSize; /* a full line is requested */
          requestBuffer.address = cacheit->address;
          requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
          requestBuffer.index = 0;
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case WRITE_MISS_EVICTION:
          /* The entry is an eviction from a write miss, set the request buffer state to a write. */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = true;
          requestBuffer.size = nLineSize;
          requestBuffer.address = cacheit->evictedaddress & (~(address_t)(nLineSize - 1));
          requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
          requestBuffer.index = 0;
          memcpy(requestBuffer.data, cacheit->linedata, requestBuffer.size);
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case WRITE_MISS_EVICTION_WRITE_DONE:
          /* The entry is a write miss, set the request buffer state to a read (!write). */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = false;
          requestBuffer.size = nLineSize; /* a full line is requested */
          requestBuffer.address = cacheit->address;
          requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
          requestBuffer.index = 0;
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
// 			case WRITE_MISS_NO_READ:
// 				/* The entry is
// 				requestBuffer.instr = cacheit->instr;
// 				requestBuffer.write = true;
// 				requestBuffer.size = cacheit->size;
// 				requestBuffer.address = cacheit->address;
// 				requestBuffer.base = requestBuffer.address;
// 				requestBuffer.index = 0;
// 				memcpy(requestBuffer.data, cacheit->data, requestBuffer.size);
// 				requestBuffer.uid = cacheit->uid;
// 				requestBuffer.ready = true;
// 				break;
        case READ_MISS:
          /* The entry is a read miss, set the request buffer to a read (!write). */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = false;
          requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
          requestBuffer.address = cacheit->address;
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case READ_MISS_EVICTION:
          /* The entry is an eviction from a read miss, set the request buffer to a write. */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = true;
          requestBuffer.size = nLineSize;
          requestBuffer.address = cacheit->evictedaddress & (~(address_t)(nLineSize - 1));
          requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
          requestBuffer.index = 0;
          memcpy(requestBuffer.data, cacheit->linedata, requestBuffer.size);
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case READ_MISS_EVICTION_WRITE_DONE:
          /* The entry is a read miss, set the request buffer to a read (!write). */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = false;
          requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
          requestBuffer.address = cacheit->address;
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case PREFETCH_MISS:
          /* The entry is a prefetch miss, set the request buffer to a read (!write). */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = false;
          requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
          requestBuffer.address = cacheit->address;
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case PREFETCH_MISS_EVICTION:
          /* The entry is an eviction from a prefetch miss, set the request buffer to a write. */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = true;
          requestBuffer.size = nLineSize;
          requestBuffer.address = cacheit->evictedaddress & (~(address_t)(nLineSize - 1));
          requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
          requestBuffer.index = 0;
          memcpy(requestBuffer.data, cacheit->linedata, requestBuffer.size);
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case PREFETCH_MISS_EVICTION_WRITE_DONE:
          /* The entry is a prefetch miss, set the request buffer to a read (!write). */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = false;
          requestBuffer.size = nLineSize; /* a full line is requested, not just the requested data */
          requestBuffer.address = cacheit->address;
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
        case EVICT_READY:
          /* The request is an eviction hit, set the request buffer to a write. */
          requestBuffer.instr = cacheit->instr;
          requestBuffer.write = true;
          requestBuffer.size = nLineSize;
          requestBuffer.address = cacheit->evictedaddress & (~(address_t)(nLineSize - 1));
          requestBuffer.base = requestBuffer.address & (~(address_t)(nLineSize - 1));
          requestBuffer.index = 0;
          memcpy(requestBuffer.data, cacheit->linedata, requestBuffer.size);
          requestBuffer.uid = cacheit->uid;
          requestBuffer.ready = true;
          break;
      }
//boveerle
	requestBuffer.req_sender = cacheit->req_sender;
//eoveerle
    }

    /* Return true if the cache state has changed. */
    return changed;
  }

  /* This functions sends data to the upper and lower levels.
   * Returns true if the cache state has changed.
   */
  bool SendData()
  {
    /* index: used to go through the ports. */
    int index;
    /* changed: true if the state of the cache has changed. */
    bool changed = false;

    /* If there is nothing in the returnBuffer the cache can try to sent something to
     * upper levels (CPU) */
    if (!returnBuffer.ready)
    {
      /* First the MSHR, misses (that now are ready) are now prioritary */
      changed = SendMSHRDatatoCPU() || changed;
      /* If there is still nothing to send, then try to send something from the cache pipeline. */
      if (!returnBuffer.ready)
        changed = SendCacheDatatoCPU() || changed;
    }

    /* If there is nothing in the request buffer, the cache can try to sent something
     * to lower levels (memory system) */
    if (!requestBuffer.ready)
    {
      changed = SendCacheDatatoMem() || changed;
    }

    /* If there is data ready in the return buffer, then set the output ports to upper levels (CPU) */
    if (returnBuffer.ready)
    {
      /* Just one port is set to valid */
      outCachetoCPUInstruction[returnBuffer.port] = returnBuffer.instr;
      outCachetoCPUSize[returnBuffer.port] = returnBuffer.size > nCachetoCPUDataPathSize ? nCachetoCPUDataPathSize : returnBuffer.size;
      outCachetoCPUAddress[returnBuffer.port] = returnBuffer.base + returnBuffer.index;
      outCachetoCPUData[returnBuffer.port].Write(&returnBuffer.data[returnBuffer.index],
          (returnBuffer.size > nCachetoCPUDataPathSize) ? nCachetoCPUDataPathSize : returnBuffer.size);
      outCachetoCPUTag[returnBuffer.port] = returnBuffer.uid;
      outCachetoCPUValid[returnBuffer.port] = true;
      /* Other ports are not set to valid */
      for (index = 0; index < nPorts; index++)
      {
        if (index != returnBuffer.port)
          outCachetoCPUValid[index] = false;
      }
//boveerle
	//cerr << "@ReturnBuffer.ready, req_sender=" << returnBuffer.req_sender << endl;
	    outCachetoCPUReqSender[returnBuffer.port] = returnBuffer.req_sender;
//eoveerle
    }
    else
    {
      /* Nothing to send, set all the port to not valid (nothing to send) */
      for (index = 0; index < nPorts; index++)
      {
        outCachetoCPUValid[index] = false;
      }
    }

    /* If the request buffer is ready, then set the output ports to lower levels (memory system). */
    if (requestBuffer.ready)
    {
      outCachetoMemInstruction = requestBuffer.instr;
      outCachetoMemAddress = requestBuffer.address & ~(address_t)(nCachetoMemDataPathSize - 1);
      /* Set the correct command: write/read. */
      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
    {
      /* Nothing to send, set the lower level port to not valid (nothing to send) */
      outCachetoMemValid = false;
    }

    /* Returns true if the state of the cache has changed. */
    return changed;
  }

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

public:
  /* ready: added for simplescalar plugging.
   *        indicates if the cache is ready to receive new requests. */
  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 mshr_hits;
  uint64_t mshr_hits_read;
  uint64_t mshr_hits_write;
  uint64_t mshr_hits_prefetch;
  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 new_mshrs;
  uint64_t existent_mshrs;
  uint64_t blocked_mshrs;
  uint64_t rq_size;
  uint64_t waiting_memory_bus;

  /* Clock */
  sc_in_clk inClock;

  /* input signals from the upper levels (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 upper levels (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

//  ml_out_data<bool> outBusy; !!!

  /* input signals from the lower level (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 lower level (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;

  /* Constructor of the CacheWBNB module. Initializes ports and statistics.
   * name: the name wanted for the module.
   */
  MicrolibCacheWBNB(const char *name):sc_module(name)
  {
    /* index: used as index of the different ports. */
    int index;

    /* Set internal connections. */
    outStateChanged(stateChanged);
    inStateChanged(stateChanged);

    /* Initialize the module in SystemC. */
    SC_HAS_PROCESS(MicrolibCacheWBNB);
/*
    // Make the ExternalControl process sensitive to the input instructions and acknowledge.
    SC_METHOD(ExternalControl);
    for (index = 0; index < nPorts; index++)
      sensitive << inCPUtoCacheValid[index] << inCPUtoCacheAccept[index];
    sensitive << inMemtoCacheValid << inMemtoCacheAccept;
    sensitive << inStateChanged;

    // Make the InternalControl sensitive to the clock front edge
    SC_METHOD(InternalControl);
    sensitive_pos << inClock;
*/

SC_METHOD(CheckCPUAccept);
for (int i=0;i<nPorts;i++) sensitive << inCPUtoCacheAccept[i];
SC_METHOD(CheckMemAccept);
sensitive << inMemtoCacheAccept;
SC_METHOD(CheckCPURequests);
for (int 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;

    /* Initialize statistics. */
    accesses = 0;
    accesses_read = 0;
    accesses_write = 0;
    accesses_prefetch = 0;
    accesses_evict = 0;
    hits = 0;
    hits_read = 0;
    hits_write = 0;
    hits_prefetch = 0;
    hits_evict = 0;
    mshr_hits = 0;
    mshr_hits_read = 0;
    mshr_hits_write = 0;
    mshr_hits_prefetch = 0;
    misses = 0;
    misses_read = 0;
    misses_write = 0;
    misses_prefetch = 0;
    misses_evict = 0;
    writebacks = 0;
    writebacks_read = 0;
    writebacks_write = 0;
    writebacks_prefetch = 0;
    writebacks_evict = 0;
    new_mshrs = 0;
    existent_mshrs = 0;
    blocked_mshrs = 0;
    rq_size = 0;
    waiting_memory_bus = 0;
    /* Initialize ready for SimpleScalar plugging. */
    ready = 1;
    debug = false;
  }

  /* Combinational functions of the module. */
  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();
  }

  /* Sequential functions of the module. */
  void InternalControl()
  {
    /* changed: true if the state of the cache has changed. */
    bool changed;

    /* 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;

    /* This signal indicates if the cache has still work to do. */
// 		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;
    }

    /* Calculate the size of the request queue cycle by cycle. (cumulative) */
    rq_size += requestQueue.Size();

    /* Added for simplescalar plugging */
    if (!cacheQueue.Full())
    {
      ready = 1;
    }
    else
    {
      ready = 0;
    }
  }


  void start_of_cycle()
  {
    bool changed = SendData();

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

    /* Calculate the size of the request queue cycle by cycle. (cumulative) */
    rq_size += requestQueue.Size();

    /* Added for simplescalar plugging */
    if (!cacheQueue.Full())
    {
      ready = 1;
    }
    else
    {
      ready = 0;
    }
    if(debug) dump();
  }


  void end_of_cycle()
  {
    /* changed: true if the state of the cache has changed. */
    bool changed;

    /* 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;

  }



  /* Interface needed by the cache module to read the contents of the cache
   * when a system call occurs. */
  void Read(void *buffer, address_t address, int size)
  {
    cache.Read(buffer, address, size);
  }

  /* Interface needed by the cache module to write the contents of the cache
   * when a system calls occurs. */
  void Write(address_t address, const void *buffer, int size)
  {
    cache.Write(address, buffer, size);
  }

  /* Interface needed by the cache module to set the contents of the cache
   * when a system call occurs. */
  void Set(address_t address, UInt8 data, int size)
  {
    cache.Set(address, data, size);
  }

  /* Interface needed by the cache module to check the contents of the cache. */
  bool Check()
  {
    return cacheQueue.Check();
  }

  /* Function to dump the stats of the module.
   * os: the output stream where the stats are sent. */
  void DumpStats(ostream& os)
  {
    os << "_accesses " << accesses << " # " << " accesses" << endl;
    os << "_accesses_read " << accesses_read << " # " << " accesses_read" << endl;
    os << "_accesses_write " << accesses_write << " # " << " accesses_write" << endl;
    os << "_accesses_prefetch " << accesses_prefetch << " # " << " accesses_prefetch" << endl;
    os << "_accesses_evict " << accesses_evict << " # " << " accesses_evict" << endl;

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

    os << "_misses " << misses << " # " << " misses" << endl;
    os << "_misses_read " << misses_read << " # " << " misses_read" << endl;
    os << "_misses_write " << misses_write << " # " << " misses_write" << endl;
    os << "_misses_prefetch " << misses_prefetch << " # " << " misses_prefetch" << endl;
    os << "_misses_evict " << misses_evict << " # " << " misses_evict" << endl;
    os << "_miss_rate " << (double) misses / (double) accesses << " # " << " miss rate" << endl;

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

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

    os << "_new_mshrs " << new_mshrs << " # " << " new mshrs" << endl;
    os << "_existent_mshrs " << existent_mshrs << " # " << " existent_mshrs" << endl;
    os << "_blocked_mshrs " << blocked_mshrs << " # " << " blocked_mshrs" << endl;

    os << "_rq_size " << rq_size << " # " << " cumulative request queue size" << endl;
    os << "_rq_size_cycle " << (rq_size/timestamp()) << " # " << " average size of the request queue cycle by cycle" << endl;
    os << "_waiting_memory_bus " << waiting_memory_bus << " # " << " number of cycles the " << " was waiting for the memory bus" << endl;

  }

  /* Operator to write the state of the module. */
  friend ostream& operator << (ostream& os, MicrolibCacheWBNB<T, nSources, replacementPolicy, nCPUtoCacheDataPathSize, nCachetoCPUDataPathSize, nCPULineSize, nMemtoCacheDataPathSize, nCachetoMemDataPathSize, nLineSize, nCacheLines, nAssociativity, nStages, nDelay, nPorts, nMSHR, nMSHRRead>& cache)
  {
    os << "=================" << cache.name() << " ==================" << endl;
    os << "Cache Pipeline:" << endl;
    os << cache.cacheQueue << endl;
    os << "Miss Address File:" << endl;
    os << cache.MSHR << endl;
    os << "Delayed Write Buffer:" << endl;
    os << cache.writeBuffer << endl;
    os << "Return Buffer:" << endl;
    os << cache.returnBuffer << endl;
    os << "Request Buffer:" << endl;
    os << cache.requestBuffer << endl;
    os << "Request Queue:" << endl;
    os << cache.requestQueue << endl;
    os << "==========================================================" << endl;

    return os;
  }

  /* Function to write the state of the module. */
  void PrintState()
  {
    cerr << "=================" << name() << " ==================" << endl;
    cerr << "Cache Pipeline:" << endl;
    cerr << cacheQueue << endl;
    cerr << "Miss Address File:" << endl;
    cerr << MSHR << endl;
    cerr << "Delayed Write Buffer:" << endl;
    cerr << writeBuffer << endl;
    cerr << "Return Buffer:" << endl;
    cerr << returnBuffer << endl;
    cerr << "Request Buffer:" << endl;
    cerr << requestBuffer << endl;
    cerr << "==========================================================" << endl;
  }

  /**
   * \brief Dump some debugging information
   */
  void dump()
  { if(timestamp()==0)
    { ofstream os("cache_MICwbnb.log",ios_base::trunc);
      os.close();
    }
    ofstream os("cache_MICwbnb.log",ios_base::app);
    os << cache;
    os.close();
  }

  bool debug;


};

#endif //__CACHEWBNB_H__
