"I am a person who works hard and plays hard."

Yuan Wei
Second Year Graduate Student Department of Computer Science
University of Virginia Charlottesville, VA 22903
Email: yw3f@cs.virginia.edu


Source Code Analysis

Main Page   Compound List   File List   Compound Members   File Members  

cache.c

Go to the documentation of this file.
00001 /*
00002  * cache.c - cache module routines
00003  *
00004  * This file is a part of the SimpleScalar tool suite written by
00005  * Todd M. Austin as a part of the Multiscalar Research Project.
00006  *  
00007  * The tool suite is currently maintained by Doug Burger and Todd M. Austin.
00008  * 
00009  * Copyright (C) 1994, 1995, 1996, 1997, 1998 by Todd M. Austin
00010  *
00011  * This source file is distributed "as is" in the hope that it will be
00012  * useful.  The tool set comes with no warranty, and no author or
00013  * distributor accepts any responsibility for the consequences of its
00014  * use. 
00015  * 
00016  * Everyone is granted permission to copy, modify and redistribute
00017  * this tool set under the following conditions:
00018  * 
00019  *    This source code is distributed for non-commercial use only. 
00020  *    Please contact the maintainer for restrictions applying to 
00021  *    commercial use.
00022  *
00023  *    Permission is granted to anyone to make or distribute copies
00024  *    of this source code, either as received or modified, in any
00025  *    medium, provided that all copyright notices, permission and
00026  *    nonwarranty notices are preserved, and that the distributor
00027  *    grants the recipient permission for further redistribution as
00028  *    permitted by this document.
00029  *
00030  *    Permission is granted to distribute this file in compiled
00031  *    or executable form under the same conditions that apply for
00032  *    source code, provided that either:
00033  *
00034  *    A. it is accompanied by the corresponding machine-readable
00035  *       source code,
00036  *    B. it is accompanied by a written offer, with no time limit,
00037  *       to give anyone a machine-readable copy of the corresponding
00038  *       source code in return for reimbursement of the cost of
00039  *       distribution.  This written offer must permit verbatim
00040  *       duplication by anyone, or
00041  *    C. it is distributed by someone who received only the
00042  *       executable form, and is accompanied by a copy of the
00043  *       written offer of source code that they received concurrently.
00044  *
00045  * In other words, you are welcome to use, share and improve this
00046  * source file.  You are forbidden to forbid anyone else to use, share
00047  * and improve what you give them.
00048  *
00049  * INTERNET: dburger@cs.wisc.edu
00050  * US Mail:  1210 W. Dayton Street, Madison, WI 53706
00051  *
00052  * $Id: cache.c,v 1.1.1.1 2000/05/26 15:21:52 taustin Exp $
00053  *
00054  * $Log: cache.c,v $
00055  * Revision 1.1.1.1  2000/05/26 15:21:52  taustin
00056  * SimpleScalar Tool Set
00057  *
00058  *
00059  * Revision 1.7  1999/12/31 18:29:56  taustin
00060  * debug mode support no longer core dumps cache module
00061  *
00062  * Revision 1.6  1999/03/08 06:30:30  taustin
00063  * possible problem with range checks at end of address space
00064  *
00065  * Revision 1.5  1998/08/27 08:02:01  taustin
00066  * implemented host interface description in host.h
00067  * added target interface support
00068  * implemented a more portable random() interface
00069  * fixed cache writeback stats for cache flushes
00070  *
00071  * Revision 1.4  1997/03/11  01:08:30  taustin
00072  * updated copyright
00073  * long/int tweaks made for ALPHA target support
00074  * double-word interfaces removed
00075  *
00076  * Revision 1.3  1997/01/06  15:56:20  taustin
00077  * comments updated
00078  * fixed writeback bug when balloc == FALSE
00079  * strdup() changed to mystrdup()
00080  * cache_reg_stats() now works with stats package
00081  * cp->writebacks stat added to cache
00082  *
00083  * Revision 1.1  1996/12/05  18:52:32  taustin
00084  * Initial revision
00085  *
00086  *
00087  */
00088 
00089 #include <stdio.h>
00090 #include <stdlib.h>
00091 #include <assert.h>
00092 
00093 #include "host.h"
00094 #include "misc.h"
00095 #include "machine.h"
00096 #include "cache.h"
00097 
00098 /* cache access macros */
00099 #define CACHE_TAG(cp, addr)     ((addr) >> (cp)->tag_shift)
00100 #define CACHE_SET(cp, addr)     (((addr) >> (cp)->set_shift) & (cp)->set_mask)
00101 #define CACHE_BLK(cp, addr)     ((addr) & (cp)->blk_mask)
00102 #define CACHE_TAGSET(cp, addr)  ((addr) & (cp)->tagset_mask)
00103 
00104 /* extract/reconstruct a block address */
00105 #define CACHE_BADDR(cp, addr)   ((addr) & ~(cp)->blk_mask)
00106 #define CACHE_MK_BADDR(cp, tag, set)                                    \
00107   (((tag) << (cp)->tag_shift)|((set) << (cp)->set_shift))
00108 
00109 /* index an array of cache blocks, non-trivial due to variable length blocks */
00110 #define CACHE_BINDEX(cp, blks, i)                                       \
00111   ((struct cache_blk_t *)(((char *)(blks)) +                            \
00112                           (i)*(sizeof(struct cache_blk_t) +             \
00113                                ((cp)->balloc                            \
00114                                 ? (cp)->bsize*sizeof(byte_t) : 0))))
00115 
00116 /* cache data block accessor, type parameterized */
00117 #define __CACHE_ACCESS(type, data, bofs)                                \
00118   (*((type *)(((char *)data) + (bofs))))
00119 
00120 /* cache data block accessors, by type */
00121 #define CACHE_DOUBLE(data, bofs)  __CACHE_ACCESS(double, data, bofs)
00122 #define CACHE_FLOAT(data, bofs)   __CACHE_ACCESS(float, data, bofs)
00123 #define CACHE_WORD(data, bofs)    __CACHE_ACCESS(unsigned int, data, bofs)
00124 #define CACHE_HALF(data, bofs)    __CACHE_ACCESS(unsigned short, data, bofs)
00125 #define CACHE_BYTE(data, bofs)    __CACHE_ACCESS(unsigned char, data, bofs)
00126 
00127 /* cache block hashing macros, this macro is used to index into a cache
00128    set hash table (to find the correct block on N in an N-way cache), the
00129    cache set index function is CACHE_SET, defined above */
00130 #define CACHE_HASH(cp, key)                                             \
00131   (((key >> 24) ^ (key >> 16) ^ (key >> 8) ^ key) & ((cp)->hsize-1))
00132 
00133 /* copy data out of a cache block to buffer indicated by argument pointer p */
00134 #define CACHE_BCOPY(cmd, blk, bofs, p, nbytes)  \
00135   if (cmd == Read)                                                      \
00136     {                                                                   \
00137       switch (nbytes) {                                                 \
00138       case 1:                                                           \
00139         *((byte_t *)p) = CACHE_BYTE(&blk->data[0], bofs); break;        \
00140       case 2:                                                           \
00141         *((half_t *)p) = CACHE_HALF(&blk->data[0], bofs); break;        \
00142       case 4:                                                           \
00143         *((word_t *)p) = CACHE_WORD(&blk->data[0], bofs); break;        \
00144       default:                                                          \
00145         { /* >= 8, power of two, fits in block */                       \
00146           int words = nbytes >> 2;                                      \
00147           while (words-- > 0)                                           \
00148             {                                                           \
00149               *((word_t *)p) = CACHE_WORD(&blk->data[0], bofs); \
00150               p += 4; bofs += 4;                                        \
00151             }\
00152         }\
00153       }\
00154     }\
00155   else /* cmd == Write */                                               \
00156     {                                                                   \
00157       switch (nbytes) {                                                 \
00158       case 1:                                                           \
00159         CACHE_BYTE(&blk->data[0], bofs) = *((byte_t *)p); break;        \
00160       case 2:                                                           \
00161         CACHE_HALF(&blk->data[0], bofs) = *((half_t *)p); break;        \
00162       case 4:                                                           \
00163         CACHE_WORD(&blk->data[0], bofs) = *((word_t *)p); break;        \
00164       default:                                                          \
00165         { /* >= 8, power of two, fits in block */                       \
00166           int words = nbytes >> 2;                                      \
00167           while (words-- > 0)                                           \
00168             {                                                           \
00169               CACHE_WORD(&blk->data[0], bofs) = *((word_t *)p);         \
00170               p += 4; bofs += 4;                                        \
00171             }\
00172         }\
00173     }\
00174   }
00175 
00176 /* bound sqword_t/dfloat_t to positive int */
00177 #define BOUND_POS(N)            ((int)(MIN(MAX(0, (N)), 2147483647)))
00178 
00179 /* unlink BLK from the hash table bucket chain in SET */
00180 static void
00181 unlink_htab_ent(struct cache_t *cp,             /* cache to update */
00182                 struct cache_set_t *set,        /* set containing bkt chain */
00183                 struct cache_blk_t *blk)        /* block to unlink */
00184 {
00185   struct cache_blk_t *prev, *ent;
00186   int index = CACHE_HASH(cp, blk->tag);
00187 
00188   /* locate the block in the hash table bucket chain */
00189   for (prev=NULL,ent=set->hash[index];
00190        ent;
00191        prev=ent,ent=ent->hash_next)
00192     {
00193       if (ent == blk)
00194         break;
00195     }
00196   assert(ent);
00197 
00198   /* unlink the block from the hash table bucket chain */
00199   if (!prev)
00200     {
00201       /* head of hash bucket list */
00202       set->hash[index] = ent->hash_next;
00203     }
00204   else
00205     {
00206       /* middle or end of hash bucket list */
00207       prev->hash_next = ent->hash_next;
00208     }
00209   ent->hash_next = NULL;
00210 }
00211 
00212 /* insert BLK onto the head of the hash table bucket chain in SET */
00213 static void
00214 link_htab_ent(struct cache_t *cp,               /* cache to update */
00215               struct cache_set_t *set,          /* set containing bkt chain */
00216               struct cache_blk_t *blk)          /* block to insert */
00217 {
00218   int index = CACHE_HASH(cp, blk->tag);
00219 
00220   /* insert block onto the head of the bucket chain */
00221   blk->hash_next = set->hash[index];
00222   set->hash[index] = blk;
00223 }
00224 
00225 /* where to insert a block onto the ordered way chain */
00226 enum list_loc_t { Head, Tail };
00227 
00228 /* insert BLK into the order way chain in SET at location WHERE */
00229 static void
00230 update_way_list(struct cache_set_t *set,        /* set contained way chain */
00231                 struct cache_blk_t *blk,        /* block to insert */
00232                 enum list_loc_t where)          /* insert location */
00233 {
00234   /* unlink entry from the way list */
00235   if (!blk->way_prev && !blk->way_next)
00236     {
00237       /* only one entry in list (direct-mapped), no action */
00238       assert(set->way_head == blk && set->way_tail == blk);
00239       /* Head/Tail order already */
00240       return;
00241     }
00242   /* else, more than one element in the list */
00243   else if (!blk->way_prev)
00244     {
00245       assert(set->way_head == blk && set->way_tail != blk);
00246       if (where == Head)
00247         {
00248           /* already there */
00249           return;
00250         }
00251       /* else, move to tail */
00252       set->way_head = blk->way_next;
00253       blk->way_next->way_prev = NULL;
00254     }
00255   else if (!blk->way_next)
00256     {
00257       /* end of list (and not front of list) */
00258       assert(set->way_head != blk && set->way_tail == blk);
00259       if (where == Tail)
00260         {
00261           /* already there */
00262           return;
00263         }
00264       set->way_tail = blk->way_prev;
00265       blk->way_prev->way_next = NULL;
00266     }
00267   else
00268     {
00269       /* middle of list (and not front or end of list) */
00270       assert(set->way_head != blk && set->way_tail != blk);
00271       blk->way_prev->way_next = blk->way_next;
00272       blk->way_next->way_prev = blk->way_prev;
00273     }
00274 
00275   /* link BLK back into the list */
00276   if (where == Head)
00277     {
00278       /* link to the head of the way list */
00279       blk->way_next = set->way_head;
00280       blk->way_prev = NULL;
00281       set->way_head->way_prev = blk;
00282       set->way_head = blk;
00283     }
00284   else if (where == Tail)
00285     {
00286       /* link to the tail of the way list */
00287       blk->way_prev = set->way_tail;
00288       blk->way_next = NULL;
00289       set->way_tail->way_next = blk;
00290       set->way_tail = blk;
00291     }
00292   else
00293     panic("bogus WHERE designator");
00294 }
00295 
00296 /* create and initialize a general cache structure */
00297 struct cache_t *                        /* pointer to cache created */
00298 cache_create(char *name,                /* name of the cache */
00299              int nsets,                 /* total number of sets in cache */
00300              int bsize,                 /* block (line) size of cache */
00301              int balloc,                /* allocate data space for blocks? */
00302              int usize,                 /* size of user data to alloc w/blks */
00303              int assoc,                 /* associativity of cache */
00304              enum cache_policy policy,  /* replacement policy w/in sets */
00305              /* block access function, see description w/in struct cache def */
00306              unsigned int (*blk_access_fn)(enum mem_cmd cmd,
00307                                            md_addr_t baddr, int bsize,
00308                                            struct cache_blk_t *blk,
00309                                            tick_t now),
00310              unsigned int hit_latency)  /* latency in cycles for a hit */
00311 {
00312   struct cache_t *cp;
00313   struct cache_blk_t *blk;
00314   int i, j, bindex;
00315 
00316   /* check all cache parameters */
00317   if (nsets <= 0)
00318     fatal("cache size (in sets) `%d' must be non-zero", nsets);
00319   if ((nsets & (nsets-1)) != 0)
00320     fatal("cache size (in sets) `%d' is not a power of two", nsets);
00321   /* blocks must be at least one datum large, i.e., 8 bytes for SS */
00322   if (bsize < 8)
00323     fatal("cache block size (in bytes) `%d' must be 8 or greater", bsize);
00324   if ((bsize & (bsize-1)) != 0)
00325     fatal("cache block size (in bytes) `%d' must be a power of two", bsize);
00326   if (usize < 0)
00327     fatal("user data size (in bytes) `%d' must be a positive value", usize);
00328   if (assoc <= 0)
00329     fatal("cache associativity `%d' must be non-zero and positive", assoc);
00330   if ((assoc & (assoc-1)) != 0)
00331     fatal("cache associativity `%d' must be a power of two", assoc);
00332   if (!blk_access_fn)
00333     fatal("must specify miss/replacement functions");
00334 
00335   /* allocate the cache structure */
00336   cp = (struct cache_t *)
00337     calloc(1, sizeof(struct cache_t) + (nsets-1)*sizeof(struct cache_set_t));
00338   if (!cp)
00339     fatal("out of virtual memory");
00340 
00341   /* initialize user parameters */
00342   cp->name = mystrdup(name);
00343   cp->nsets = nsets;
00344   cp->bsize = bsize;
00345   cp->balloc = balloc;
00346   cp->usize = usize;
00347   cp->assoc = assoc;
00348   cp->policy = policy;
00349   cp->hit_latency = hit_latency;
00350 
00351   /* miss/replacement functions */
00352   cp->blk_access_fn = blk_access_fn;
00353 
00354   /* compute derived parameters */
00355   cp->hsize = CACHE_HIGHLY_ASSOC(cp) ? (assoc >> 2) : 0;
00356   cp->blk_mask = bsize-1;
00357   cp->set_shift = log_base2(bsize);
00358   cp->set_mask = nsets-1;
00359   cp->tag_shift = cp->set_shift + log_base2(nsets);
00360   cp->tag_mask = (1 << (32 - cp->tag_shift))-1;
00361   cp->tagset_mask = ~cp->blk_mask;
00362   cp->bus_free = 0;
00363 
00364   /* print derived parameters during debug */
00365   debug("%s: cp->hsize     = %d", cp->name, cp->hsize);
00366   debug("%s: cp->blk_mask  = 0x%08x", cp->name, cp->blk_mask);
00367   debug("%s: cp->set_shift = %d", cp->name, cp->set_shift);
00368   debug("%s: cp->set_mask  = 0x%08x", cp->name, cp->set_mask);
00369   debug("%s: cp->tag_shift = %d", cp->name, cp->tag_shift);
00370   debug("%s: cp->tag_mask  = 0x%08x", cp->name, cp->tag_mask);
00371 
00372   /* initialize cache stats */
00373   cp->hits = 0;
00374   cp->misses = 0;
00375   cp->replacements = 0;
00376   cp->writebacks = 0;
00377   cp->invalidations = 0;
00378 
00379   /* blow away the last block accessed */
00380   cp->last_tagset = 0;
00381   cp->last_blk = NULL;
00382 
00383   /* allocate data blocks */
00384   cp->data = (byte_t *)calloc(nsets * assoc,
00385                               sizeof(struct cache_blk_t) +
00386                               (cp->balloc ? (bsize*sizeof(byte_t)) : 0));
00387   if (!cp->data)
00388     fatal("out of virtual memory");
00389 
00390   /* slice up the data blocks */
00391   for (bindex=0,i=0; i<nsets; i++)
00392     {
00393       cp->sets[i].way_head = NULL;
00394       cp->sets[i].way_tail = NULL;
00395       /* get a hash table, if needed */
00396       if (cp->hsize)
00397         {
00398           cp->sets[i].hash =
00399             (struct cache_blk_t **)calloc(cp->hsize,
00400                                           sizeof(struct cache_blk_t *));
00401           if (!cp->sets[i].hash)
00402             fatal("out of virtual memory");
00403         }
00404       /* NOTE: all the blocks in a set *must* be allocated contiguously,
00405          otherwise, block accesses through SET->BLKS will fail (used
00406          during random replacement selection) */
00407       cp->sets[i].blks = CACHE_BINDEX(cp, cp->data, bindex);
00408       
00409       /* link the data blocks into ordered way chain and hash table bucket
00410          chains, if hash table exists */
00411       for (j=0; j<assoc; j++)
00412         {
00413           /* locate next cache block */
00414           blk = CACHE_BINDEX(cp, cp->data, bindex);
00415           bindex++;
00416 
00417           /* invalidate new cache block */
00418           blk->status = 0;
00419           blk->tag = 0;
00420           blk->ready = 0;
00421           blk->user_data = (usize != 0
00422                             ? (byte_t *)calloc(usize, sizeof(byte_t)) : NULL);
00423 
00424           /* insert cache block into set hash table */
00425           if (cp->hsize)
00426             link_htab_ent(cp, &cp->sets[i], blk);
00427 
00428           /* insert into head of way list, order is arbitrary at this point */
00429           blk->way_next = cp->sets[i].way_head;
00430           blk->way_prev = NULL;
00431           if (cp->sets[i].way_head)
00432             cp->sets[i].way_head->way_prev = blk;
00433           cp->sets[i].way_head = blk;
00434           if (!cp->sets[i].way_tail)
00435             cp->sets[i].way_tail = blk;
00436         }
00437     }
00438   return cp;
00439 }
00440 
00441 /* parse policy */
00442 enum cache_policy                       /* replacement policy enum */
00443 cache_char2policy(char c)               /* replacement policy as a char */
00444 {
00445   switch (c) {
00446   case 'l': return LRU;
00447   case 'r': return Random;
00448   case 'f': return FIFO;
00449   default: fatal("bogus replacement policy, `%c'", c);
00450   }
00451 }
00452 
00453 /* print cache configuration */
00454 void
00455 cache_config(struct cache_t *cp,        /* cache instance */
00456              FILE *stream)              /* output stream */
00457 {
00458   fprintf(stream,
00459           "cache: %s: %d sets, %d byte blocks, %d bytes user data/block\n",
00460           cp->name, cp->nsets, cp->bsize, cp->usize);
00461   fprintf(stream,
00462           "cache: %s: %d-way, `%s' replacement policy, write-back\n",
00463           cp->name, cp->assoc,
00464           cp->policy == LRU ? "LRU"
00465           : cp->policy == Random ? "Random"
00466           : cp->policy == FIFO ? "FIFO"
00467           : (abort(), ""));
00468 }
00469 
00470 /* register cache stats */
00471 void
00472 cache_reg_stats(struct cache_t *cp,     /* cache instance */
00473                 struct stat_sdb_t *sdb) /* stats database */
00474 {
00475   char buf[512], buf1[512], *name;
00476 
00477   /* get a name for this cache */
00478   if (!cp->name || !cp->name[0])
00479     name = "<unknown>";
00480   else
00481     name = cp->name;
00482 
00483   sprintf(buf, "%s.accesses", name);
00484   sprintf(buf1, "%s.hits + %s.misses", name, name);
00485   stat_reg_formula(sdb, buf, "total number of accesses", buf1, "%12.0f");
00486   sprintf(buf, "%s.hits", name);
00487   stat_reg_counter(sdb, buf, "total number of hits", &cp->hits, 0, NULL);
00488   sprintf(buf, "%s.misses", name);
00489   stat_reg_counter(sdb, buf, "total number of misses", &cp->misses, 0, NULL);
00490   sprintf(buf, "%s.replacements", name);
00491   stat_reg_counter(sdb, buf, "total number of replacements",
00492                  &cp->replacements, 0, NULL);
00493   sprintf(buf, "%s.writebacks", name);
00494   stat_reg_counter(sdb, buf, "total number of writebacks",
00495                  &cp->writebacks, 0, NULL);
00496   sprintf(buf, "%s.invalidations", name);
00497   stat_reg_counter(sdb, buf, "total number of invalidations",
00498                  &cp->invalidations, 0, NULL);
00499   sprintf(buf, "%s.miss_rate", name);
00500   sprintf(buf1, "%s.misses / %s.accesses", name, name);
00501   stat_reg_formula(sdb, buf, "miss rate (i.e., misses/ref)", buf1, NULL);
00502   sprintf(buf, "%s.repl_rate", name);
00503   sprintf(buf1, "%s.replacements / %s.accesses", name, name);
00504   stat_reg_formula(sdb, buf, "replacement rate (i.e., repls/ref)", buf1, NULL);
00505   sprintf(buf, "%s.wb_rate", name);
00506   sprintf(buf1, "%s.writebacks / %s.accesses", name, name);
00507   stat_reg_formula(sdb, buf, "writeback rate (i.e., wrbks/ref)", buf1, NULL);
00508   sprintf(buf, "%s.inv_rate", name);
00509   sprintf(buf1, "%s.invalidations / %s.accesses", name, name);
00510   stat_reg_formula(sdb, buf, "invalidation rate (i.e., invs/ref)", buf1, NULL);
00511 }
00512 
00513 /* print cache stats */
00514 void
00515 cache_stats(struct cache_t *cp,         /* cache instance */
00516             FILE *stream)               /* output stream */
00517 {
00518   double sum = (double)(cp->hits + cp->misses);
00519 
00520   fprintf(stream,
00521           "cache: %s: %.0f hits %.0f misses %.0f repls %.0f invalidations\n",
00522           cp->name, (double)cp->hits, (double)cp->misses,
00523           (double)cp->replacements, (double)cp->invalidations);
00524   fprintf(stream,
00525           "cache: %s: miss rate=%f  repl rate=%f  invalidation rate=%f\n",
00526           cp->name,
00527           (double)cp->misses/sum, (double)(double)cp->replacements/sum,
00528           (double)cp->invalidations/sum);
00529 }
00530 
00531 /* access a cache, perform a CMD operation on cache CP at address ADDR,
00532    places NBYTES of data at *P, returns latency of operation if initiated
00533    at NOW, places pointer to block user data in *UDATA, *P is untouched if
00534    cache blocks are not allocated (!CP->BALLOC), UDATA should be NULL if no
00535    user data is attached to blocks */
00536 unsigned int                            /* latency of access in cycles */
00537 cache_access(struct cache_t *cp,        /* cache to access */
00538              enum mem_cmd cmd,          /* access type, Read or Write */
00539              md_addr_t addr,            /* address of access */
00540              void *vp,                  /* ptr to buffer for input/output */
00541              int nbytes,                /* number of bytes to access */
00542              tick_t now,                /* time of access */
00543              byte_t **udata,            /* for return of user data ptr */
00544              md_addr_t *repl_addr)      /* for address of replaced block */
00545 {
00546   byte_t *p = vp;
00547   md_addr_t tag = CACHE_TAG(cp, addr);
00548   md_addr_t set = CACHE_SET(cp, addr);
00549   md_addr_t bofs = CACHE_BLK(cp, addr);
00550   struct cache_blk_t *blk, *repl;
00551   int lat = 0;
00552 
00553   /* default replacement address */
00554   if (repl_addr)
00555     *repl_addr = 0;
00556 
00557   /* check alignments */
00558   if ((nbytes & (nbytes-1)) != 0 || (addr & (nbytes-1)) != 0)
00559     fatal("cache: access error: bad size or alignment, addr 0x%08x", addr);
00560 
00561   /* access must fit in cache block */
00562   /* FIXME:
00563      ((addr + (nbytes - 1)) > ((addr & ~cp->blk_mask) + (cp->bsize - 1))) */
00564   if ((addr + nbytes) > ((addr & ~cp->blk_mask) + cp->bsize))
00565     fatal("cache: access error: access spans block, addr 0x%08x", addr);
00566 
00567   /* permissions are checked on cache misses */
00568 
00569   /* check for a fast hit: access to same block */
00570   if (CACHE_TAGSET(cp, addr) == cp->last_tagset)
00571     {
00572       /* hit in the same block */
00573       blk = cp->last_blk;
00574       goto cache_fast_hit;
00575     }
00576     
00577   if (cp->hsize)
00578     {
00579       /* higly-associativity cache, access through the per-set hash tables */
00580       int hindex = CACHE_HASH(cp, tag);
00581 
00582       for (blk=cp->sets[set].hash[hindex];
00583            blk;
00584            blk=blk->hash_next)
00585         {
00586           if (blk->tag == tag && (blk->status & CACHE_BLK_VALID))
00587             goto cache_hit;
00588         }
00589     }
00590   else
00591     {
00592       /* low-associativity cache, linear search the way list */
00593       for (blk=cp->sets[set].way_head;
00594            blk;
00595            blk=blk->way_next)
00596         {
00597           if (blk->tag == tag && (blk->status & CACHE_BLK_VALID))
00598             goto cache_hit;
00599         }
00600     }
00601 
00602   /* cache block not found */
00603 
00604   /* **MISS** */
00605   cp->misses++;
00606 
00607   /* select the appropriate block to replace, and re-link this entry to
00608      the appropriate place in the way list */
00609   switch (cp->policy) {
00610   case LRU:
00611   case FIFO:
00612     repl = cp->sets[set].way_tail;
00613     update_way_list(&cp->sets[set], repl, Head);
00614     break;
00615   case Random:
00616     {
00617       int bindex = myrand() & (cp->assoc - 1);
00618       repl = CACHE_BINDEX(cp, cp->sets[set].blks, bindex);
00619     }
00620     break;
00621   default:
00622     panic("bogus replacement policy");
00623   }
00624 
00625   /* remove this block from the hash bucket chain, if hash exists */
00626   if (cp->hsize)
00627     unlink_htab_ent(cp, &cp->sets[set], repl);
00628 
00629   /* blow away the last block to hit */
00630   cp->last_tagset = 0;
00631   cp->last_blk = NULL;
00632 
00633   /* write back replaced block data */
00634   if (repl->status & CACHE_BLK_VALID)
00635     {
00636       cp->replacements++;
00637 
00638       if (repl_addr)
00639         *repl_addr = CACHE_MK_BADDR(cp, repl->tag, set);
00640  
00641       /* don't replace the block until outstanding misses are satisfied */
00642       lat += BOUND_POS(repl->ready - now);
00643  
00644       /* stall until the bus to next level of memory is available */
00645       lat += BOUND_POS(cp->bus_free - (now + lat));
00646  
00647       /* track bus resource usage */
00648       cp->bus_free = MAX(cp->bus_free, (now + lat)) + 1;
00649 
00650       if (repl->status & CACHE_BLK_DIRTY)
00651         {
00652           /* write back the cache block */
00653           cp->writebacks++;
00654           lat += cp->blk_access_fn(Write,
00655                                    CACHE_MK_BADDR(cp, repl->tag, set),
00656                                    cp->bsize, repl, now+lat);
00657         }
00658     }
00659 
00660   /* update block tags */
00661   repl->tag = tag;
00662   repl->status = CACHE_BLK_VALID;       /* dirty bit set on update */
00663 
00664   /* read data block */
00665   lat += cp->blk_access_fn(Read, CACHE_BADDR(cp, addr), cp->bsize,
00666                            repl, now+lat);
00667 
00668   /* copy data out of cache block */
00669   if (cp->balloc)
00670     {
00671       CACHE_BCOPY(cmd, repl, bofs, p, nbytes);
00672     }
00673 
00674   /* update dirty status */
00675   if (cmd == Write)
00676     repl->status |= CACHE_BLK_DIRTY;
00677 
00678   /* get user block data, if requested and it exists */
00679   if (udata)
00680     *udata = repl->user_data;
00681 
00682   /* update block status */
00683   repl->ready = now+lat;
00684 
00685   /* link this entry back into the hash table */
00686   if (cp->hsize)
00687     link_htab_ent(cp, &cp->sets[set], repl);
00688 
00689   /* return latency of the operation */
00690   return lat;
00691 
00692 
00693  cache_hit: /* slow hit handler */
00694   
00695   /* **HIT** */
00696   cp->hits++;
00697 
00698   /* copy data out of cache block, if block exists */
00699   if (cp->balloc)
00700     {
00701       CACHE_BCOPY(cmd, blk, bofs, p, nbytes);
00702     }
00703 
00704   /* update dirty status */
00705   if (cmd == Write)
00706     blk->status |= CACHE_BLK_DIRTY;
00707 
00708   /* if LRU replacement and this is not the first element of list, reorder */
00709   if (blk->way_prev && cp->policy == LRU)
00710     {
00711       /* move this block to head of the way (MRU) list */
00712       update_way_list(&cp->sets[set], blk, Head);
00713     }
00714 
00715   /* tag is unchanged, so hash links (if they exist) are still valid */
00716 
00717   /* record the last block to hit */
00718   cp->last_tagset = CACHE_TAGSET(cp, addr);
00719   cp->last_blk = blk;
00720 
00721   /* get user block data, if requested and it exists */
00722   if (udata)
00723     *udata = blk->user_data;
00724 
00725   /* return first cycle data is available to access */
00726   return (int) MAX(cp->hit_latency, (blk->ready - now));
00727 
00728  cache_fast_hit: /* fast hit handler */
00729   
00730   /* **FAST HIT** */
00731   cp->hits++;
00732 
00733   /* copy data out of cache block, if block exists */
00734   if (cp->balloc)
00735     {
00736       CACHE_BCOPY(cmd, blk, bofs, p, nbytes);
00737     }
00738 
00739   /* update dirty status */
00740   if (cmd == Write)
00741     blk->status |= CACHE_BLK_DIRTY;
00742 
00743   /* this block hit last, no change in the way list */
00744 
00745   /* tag is unchanged, so hash links (if they exist) are still valid */
00746 
00747   /* get user block data, if requested and it exists */
00748   if (udata)
00749     *udata = blk->user_data;
00750 
00751   /* record the last block to hit */
00752   cp->last_tagset = CACHE_TAGSET(cp, addr);
00753   cp->last_blk = blk;
00754 
00755   /* return first cycle data is available to access */
00756   return (int) MAX(cp->hit_latency, (blk->ready - now));
00757 }
00758 
00759 /* return non-zero if block containing address ADDR is contained in cache
00760    CP, this interface is used primarily for debugging and asserting cache
00761    invariants */
00762 int                                     /* non-zero if access would hit */
00763 cache_probe(struct cache_t *cp,         /* cache instance to probe */
00764             md_addr_t addr)             /* address of block to probe */
00765 {
00766   md_addr_t tag = CACHE_TAG(cp, addr);
00767   md_addr_t set = CACHE_SET(cp, addr);
00768   struct cache_blk_t *blk;
00769 
00770   /* permissions are checked on cache misses */
00771 
00772   if (cp->hsize)
00773   {
00774     /* higly-associativity cache, access through the per-set hash tables */
00775     int hindex = CACHE_HASH(cp, tag);
00776     
00777     for (blk=cp->sets[set].hash[hindex];
00778          blk;
00779          blk=blk->hash_next)
00780     {   
00781       if (blk->tag == tag && (blk->status & CACHE_BLK_VALID))
00782           return TRUE;
00783     }
00784   }
00785   else
00786   {
00787     /* low-associativity cache, linear search the way list */
00788     for (blk=cp->sets[set].way_head;
00789          blk;
00790          blk=blk->way_next)
00791     {
00792       if (blk->tag == tag && (blk->status & CACHE_BLK_VALID))
00793           return TRUE;
00794     }
00795   }
00796   
00797   /* cache block not found */
00798   return FALSE;
00799 }
00800 
00801 /* flush the entire cache, returns latency of the operation */
00802 unsigned int                            /* latency of the flush operation */
00803 cache_flush(struct cache_t *cp,         /* cache instance to flush */
00804             tick_t now)                 /* time of cache flush */
00805 {
00806   int i, lat = cp->hit_latency; /* min latency to probe cache */
00807   struct cache_blk_t *blk;
00808 
00809   /* blow away the last block to hit */
00810   cp->last_tagset = 0;
00811   cp->last_blk = NULL;
00812 
00813   /* no way list updates required because all blocks are being invalidated */
00814   for (i=0; i<cp->nsets; i++)
00815     {
00816       for (blk=cp->sets[i].way_head; blk; blk=blk->way_next)
00817         {
00818           if (blk->status & CACHE_BLK_VALID)
00819             {
00820               cp->invalidations++;
00821               blk->status &= ~CACHE_BLK_VALID;
00822 
00823               if (blk->status & CACHE_BLK_DIRTY)
00824                 {
00825                   /* write back the invalidated block */
00826                   cp->writebacks++;
00827                   lat += cp->blk_access_fn(Write,
00828                                            CACHE_MK_BADDR(cp, blk->tag, i),
00829                                            cp->bsize, blk, now+lat);
00830                 }
00831             }
00832         }
00833     }
00834 
00835   /* return latency of the flush operation */
00836   return lat;
00837 }
00838 
00839 /* flush the block containing ADDR from the cache CP, returns the latency of
00840    the block flush operation */
00841 unsigned int                            /* latency of flush operation */
00842 cache_flush_addr(struct cache_t *cp,    /* cache instance to flush */
00843                  md_addr_t addr,        /* address of block to flush */
00844                  tick_t now)            /* time of cache flush */
00845 {
00846   md_addr_t tag = CACHE_TAG(cp, addr);
00847   md_addr_t set = CACHE_SET(cp, addr);
00848   struct cache_blk_t *blk;
00849   int lat = cp->hit_latency; /* min latency to probe cache */
00850 
00851   if (cp->hsize)
00852     {
00853       /* higly-associativity cache, access through the per-set hash tables */
00854       int hindex = CACHE_HASH(cp, tag);
00855 
00856       for (blk=cp->sets[set].hash[hindex];
00857            blk;
00858            blk=blk->hash_next)
00859         {
00860           if (blk->tag == tag && (blk->status & CACHE_BLK_VALID))
00861             break;
00862         }
00863     }
00864   else
00865     {
00866       /* low-associativity cache, linear search the way list */
00867       for (blk=cp->sets[set].way_head;
00868            blk;
00869            blk=blk->way_next)
00870         {
00871           if (blk->tag == tag && (blk->status & CACHE_BLK_VALID))
00872             break;
00873         }
00874     }
00875 
00876   if (blk)
00877     {
00878       cp->invalidations++;
00879       blk->status &= ~CACHE_BLK_VALID;
00880 
00881       /* blow away the last block to hit */
00882       cp->last_tagset = 0;
00883       cp->last_blk = NULL;
00884 
00885       if (blk->status & CACHE_BLK_DIRTY)
00886         {
00887           /* write back the invalidated block */
00888           cp->writebacks++;
00889           lat += cp->blk_access_fn(Write,
00890                                    CACHE_MK_BADDR(cp, blk->tag, set),
00891                                    cp->bsize, blk, now+lat);
00892         }
00893       /* move this block to tail of the way (LRU) list */
00894       update_way_list(&cp->sets[set], blk, Tail);
00895     }
00896 
00897   /* return latency of the operation */
00898   return lat;
00899 }


UVa CS Department of Computer Science
School of Engineering, University of Virginia
151 Engineer's Way, P.O. Box 400740
Charlottesville, Virginia 22904-4740

(434) 982-2200  Fax: (434) 982-2214