/* Cache support for the FRV simulator
   Copyright (C) 1999-2018 Free Software Foundation, Inc.
   Contributed by Red Hat.

This file is part of the GNU Simulators.

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 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef CACHE_H
#define CACHE_H

/* A representation of a set-associative cache with LRU replacement,
   cache line locking, non-blocking support and multiple read ports.  */

/* An enumeration of cache pipeline request kinds.  */
typedef enum
{
  req_load,
  req_store,
  req_invalidate,
  req_flush,
  req_preload,
  req_unlock,
  req_WAR
} FRV_CACHE_REQUEST_KIND;

/* The cache pipeline requests.  */
typedef struct {
  int preload;
  int lock;
} FRV_CACHE_WAR_REQUEST;

typedef struct {
  char *data;
  int length;
} FRV_CACHE_STORE_REQUEST;

typedef struct {
  int flush;
  int all;
} FRV_CACHE_INVALIDATE_REQUEST;

typedef struct {
  int lock;
  int length;
} FRV_CACHE_PRELOAD_REQUEST;

/* A cache pipeline request.  */
typedef struct frv_cache_request
{
  struct frv_cache_request *next;
  struct frv_cache_request *prev;
  FRV_CACHE_REQUEST_KIND kind;
  unsigned reqno;
  unsigned priority;
  SI address;
  union {
    FRV_CACHE_STORE_REQUEST store;
    FRV_CACHE_INVALIDATE_REQUEST invalidate;
    FRV_CACHE_PRELOAD_REQUEST preload;
    FRV_CACHE_WAR_REQUEST WAR;
  } u;
} FRV_CACHE_REQUEST;

/* The buffer for returning data to the caller.  */
typedef struct {
  unsigned reqno;
  SI address;
  char *data;
  int valid;
} FRV_CACHE_RETURN_BUFFER;

/* The status of flush requests.  */
typedef struct {
  unsigned reqno;
  SI address;
  int valid;
} FRV_CACHE_FLUSH_STATUS;

/* Communicate status of requests to the caller.  */
typedef struct {
  FRV_CACHE_FLUSH_STATUS  flush;
  FRV_CACHE_RETURN_BUFFER return_buffer;
} FRV_CACHE_STATUS;

/* A cache pipeline stage.  */
typedef struct {
  FRV_CACHE_REQUEST *request;
} FRV_CACHE_STAGE;

enum {
  FIRST_STAGE,
  A_STAGE = FIRST_STAGE, /* Addressing stage */
  I_STAGE,               /* Interference stage */
  LAST_STAGE = I_STAGE,
  FRV_CACHE_STAGES
};

/* Representation of the WAR register.  */
typedef struct {
  unsigned reqno;
  unsigned priority;
  SI address;
  int preload;
  int lock;
  int latency;
  int valid;
} FRV_CACHE_WAR;

/* A cache pipeline.  */
#define NUM_WARS 2
typedef struct {
  FRV_CACHE_REQUEST      *requests;
  FRV_CACHE_STAGE         stages[FRV_CACHE_STAGES];
  FRV_CACHE_WAR           WAR[NUM_WARS];
  FRV_CACHE_STATUS        status;
} FRV_CACHE_PIPELINE;

enum {LS, LD, FRV_CACHE_PIPELINES};

/* Representation of the xARS registers.  */
typedef struct {
  int pipe;
  unsigned reqno;
  unsigned priority;
  SI address;
  int preload;
  int lock;
  int valid;
} FRV_CACHE_ARS;

/* A cache tag.  */
typedef struct {
  USI   tag;    /* Address tag.  */
  int   lru;    /* Lower values indicates less recently used.  */
  char *line;   /* Points to storage for line in data_storage.  */
  char  dirty;  /* line has been written to since last stored?  */
  char  locked; /* line is locked?  */
  char  valid;  /* tag is valid?  */
} FRV_CACHE_TAG;

/* Cache statistics.  */
typedef struct {
  unsigned long accesses;   /* number of cache accesses.  */
  unsigned long hits;       /* number of cache hits.  */
} FRV_CACHE_STATISTICS;

/* The cache itself.
   Notes:
   - line_size must be a power of 2
   - sets must be a power of 2
   - ways must be a power of 2
*/
typedef struct {
  SIM_CPU *cpu;
  unsigned configured_ways;   /* Number of ways configured in each set.  */
  unsigned configured_sets;   /* Number of sets configured in the cache.  */
  unsigned ways;              /* Number of ways in each set.  */
  unsigned sets;              /* Number of sets in the cache.  */
  unsigned line_size;         /* Size of each cache line.  */
  unsigned memory_latency;    /* Latency of main memory in cycles.  */
  FRV_CACHE_TAG *tag_storage; /* Storage for tags.  */
  char *data_storage;         /* Storage for data (cache lines).  */
  FRV_CACHE_PIPELINE pipeline[2];  /* Cache pipelines.  */
  FRV_CACHE_ARS BARS;         /* BARS register.  */
  FRV_CACHE_ARS NARS;         /* BARS register.  */
  FRV_CACHE_STATISTICS statistics; /* Operation statistics.  */
} FRV_CACHE;

/* The tags are stored by ways within sets in order to make computations
   easier.  */
#define CACHE_TAG(cache, set, way) ( \
  & ((cache)->tag_storage[(set) * (cache)->ways + (way)]) \
)

/* Compute the address tag corresponding to the given address.  */
#define CACHE_ADDRESS_TAG(cache, address) ( \
  (address) & ~(((cache)->line_size * (cache)->sets) - 1) \
)

/* Determine the index at which the set containing this tag starts.  */
#define CACHE_TAG_SET_START(cache, tag) ( \
  ((tag) - (cache)->tag_storage) & ~((cache)->ways - 1) \
)

/* Determine the number of the set which this cache tag is in.  */
#define CACHE_TAG_SET_NUMBER(cache, tag) ( \
  CACHE_TAG_SET_START ((cache), (tag)) / (cache)->ways \
)

#define CACHE_RETURN_DATA(cache, slot, address, mode, N) (               \
  T2H_##N (*(mode *)(& (cache)->pipeline[slot].status.return_buffer.data \
		     [((address) & ((cache)->line_size - 1))]))          \
)
#define CACHE_RETURN_DATA_ADDRESS(cache, slot, address, N) (              \
  ((void *)& (cache)->pipeline[slot].status.return_buffer.data[(address)  \
			                       & ((cache)->line_size - 1)]) \
)

#define DATA_CROSSES_CACHE_LINE(cache, address, size) ( \
  ((address) & ((cache)->line_size - 1)) + (size) > (cache)->line_size \
)

#define CACHE_INITIALIZED(cache) ((cache)->data_storage != NULL)

/* These functions are used to initialize and terminate a cache.  */
void
frv_cache_init (SIM_CPU *, FRV_CACHE *);
void
frv_cache_term (FRV_CACHE *);
void
frv_cache_reconfigure (SIM_CPU *, FRV_CACHE *);
int
frv_cache_enabled (FRV_CACHE *);

/* These functions are used to operate the cache in non-cycle-accurate mode.
   Each request is handled individually and immediately using the current
   cache internal state.  */
int
frv_cache_read (FRV_CACHE *, int, SI);
int
frv_cache_write (FRV_CACHE *, SI, char *, unsigned);
int
frv_cache_preload (FRV_CACHE *, SI, USI, int);
int
frv_cache_invalidate (FRV_CACHE *, SI, int);
int
frv_cache_invalidate_all (FRV_CACHE *, int);

/* These functions are used to operate the cache in cycle-accurate mode.
   The internal operation of the cache is simulated down to the cycle level.  */
#define NO_REQNO 0xffffffff
void
frv_cache_request_load (FRV_CACHE *, unsigned, SI, int);
void
frv_cache_request_store (FRV_CACHE *, SI, int, char *, unsigned);
void
frv_cache_request_invalidate (FRV_CACHE *, unsigned, SI, int, int, int);
void
frv_cache_request_preload (FRV_CACHE *, SI, int, int, int);
void
frv_cache_request_unlock (FRV_CACHE *, SI, int);

void
frv_cache_run (FRV_CACHE *, int);

int
frv_cache_data_in_buffer (FRV_CACHE*, int, SI, unsigned);
int
frv_cache_data_flushed (FRV_CACHE*, int, SI, unsigned);

int
frv_cache_read_passive_SI (FRV_CACHE *, SI, SI *);

#endif /* CACHE_H */
