📄 cache.c
字号:
/* * cache.c - cache module routines * * This file is a part of the SimpleScalar tool suite written by * Todd M. Austin as a part of the Multiscalar Research Project. * * The tool suite is currently maintained by Doug Burger and Todd M. Austin. * * Copyright (C) 1994, 1995, 1996, 1997 by Todd M. Austin * * This source file is distributed "as is" in the hope that it will be * useful. The tool set comes with no warranty, and no author or * distributor accepts any responsibility for the consequences of its * use. * * Everyone is granted permission to copy, modify and redistribute * this tool set under the following conditions: * * This source code is distributed for non-commercial use only. * Please contact the maintainer for restrictions applying to * commercial use. * * Permission is granted to anyone to make or distribute copies * of this source code, either as received or modified, in any * medium, provided that all copyright notices, permission and * nonwarranty notices are preserved, and that the distributor * grants the recipient permission for further redistribution as * permitted by this document. * * Permission is granted to distribute this file in compiled * or executable form under the same conditions that apply for * source code, provided that either: * * A. it is accompanied by the corresponding machine-readable * source code, * B. it is accompanied by a written offer, with no time limit, * to give anyone a machine-readable copy of the corresponding * source code in return for reimbursement of the cost of * distribution. This written offer must permit verbatim * duplication by anyone, or * C. it is distributed by someone who received only the * executable form, and is accompanied by a copy of the * written offer of source code that they received concurrently. * * In other words, you are welcome to use, share and improve this * source file. You are forbidden to forbid anyone else to use, share * and improve what you give them. * * INTERNET: dburger@cs.wisc.edu * US Mail: 1210 W. Dayton Street, Madison, WI 53706 * * $Id: cache.c,v 1.4 1997/03/11 01:08:30 taustin Exp taustin $ * * $Log: cache.c,v $ * Revision 1.4 1997/03/11 01:08:30 taustin * updated copyright * long/int tweaks made for ALPHA target support * double-word interfaces removed * * Revision 1.3 1997/01/06 15:56:20 taustin * comments updated * fixed writeback bug when balloc == FALSE * strdup() changed to mystrdup() * cache_reg_stats() now works with stats package * cp->writebacks stat added to cache * * Revision 1.1 1996/12/05 18:52:32 taustin * Initial revision * * */#include <stdio.h>#include <stdlib.h>#include <assert.h>#include "misc.h"#include "ss.h"#include "cache.h"/* cache access macros */#define CACHE_TAG(cp, addr) ((addr) >> (cp)->tag_shift)#define CACHE_SET(cp, addr) (((addr) >> (cp)->set_shift) & (cp)->set_mask)#define CACHE_BLK(cp, addr) ((addr) & (cp)->blk_mask)#define CACHE_TAGSET(cp, addr) ((addr) & (cp)->tagset_mask)/* extract/reconstruct a block address */#define CACHE_BADDR(cp, addr) ((addr) & ~(cp)->blk_mask)#define CACHE_MK_BADDR(cp, tag, set) \ (((tag) << (cp)->tag_shift)|((set) << (cp)->set_shift))/* index an array of cache blocks, non-trivial due to variable length blocks */#define CACHE_BINDEX(cp, blks, i) \ ((struct cache_blk *)(((char *)(blks)) + \ (i)*(sizeof(struct cache_blk) + \ ((cp)->balloc ? (cp)->bsize*sizeof(char) : 0))))/* cache data block accessor, type parameterized */#define __CACHE_ACCESS(type, data, bofs) \ (*((type *)(((char *)data) + (bofs))))/* cache data block accessors, by type */#define CACHE_DOUBLE(data, bofs) __CACHE_ACCESS(double, data, bofs)#define CACHE_FLOAT(data, bofs) __CACHE_ACCESS(float, data, bofs)#define CACHE_WORD(data, bofs) __CACHE_ACCESS(unsigned int, data, bofs)#define CACHE_HALF(data, bofs) __CACHE_ACCESS(unsigned short, data, bofs)#define CACHE_BYTE(data, bofs) __CACHE_ACCESS(unsigned char, data, bofs)/* cache block hashing macros, this macro is used to index into a cache set hash table (to find the correct block on N in an N-way cache), the cache set index function is CACHE_SET, defined above */#define CACHE_HASH(cp, key) \ (((key >> 24) ^ (key >> 16) ^ (key >> 8) ^ key) & ((cp)->hsize-1))/* copy data out of a cache block to buffer indicated by argument pointer p */#define CACHE_BCOPY(cmd, blk, bofs, p, nbytes) \ if (cmd == Read) \ { \ switch (nbytes) { \ case 1: \ *((unsigned char *)p) = CACHE_BYTE(&blk->data[0], bofs); break; \ case 2: \ *((unsigned short *)p) = CACHE_HALF(&blk->data[0], bofs); break;\ case 4: \ *((unsigned int *)p) = CACHE_WORD(&blk->data[0], bofs); break; \ default: \ { /* >= 8, power of two, fits in block */ \ int words = nbytes >> 2; \ while (words-- > 0) \ { \ *((unsigned int *)p) = CACHE_WORD(&blk->data[0], bofs); \ p += 4; bofs += 4; \ }\ }\ }\ }\ else /* cmd == Write */ \ { \ switch (nbytes) { \ case 1: \ CACHE_BYTE(&blk->data[0], bofs) = *((unsigned char *)p); break; \ case 2: \ CACHE_HALF(&blk->data[0], bofs) = *((unsigned short *)p); break;\ case 4: \ CACHE_WORD(&blk->data[0], bofs) = *((unsigned int *)p); break; \ default: \ { /* >= 8, power of two, fits in block */ \ int words = nbytes >> 2; \ while (words-- > 0) \ { \ CACHE_WORD(&blk->data[0], bofs) = *((unsigned int *)p); \ p += 4; bofs += 4; \ }\ }\ }\ }/* unlink BLK from the hash table bucket chain in SET */static voidunlink_htab_ent(struct cache *cp, /* cache to update */ struct cache_set *set, /* set containing bkt chain */ struct cache_blk *blk) /* block to unlink */{ struct cache_blk *prev, *ent; int index = CACHE_HASH(cp, blk->tag); /* locate the block in the hash table bucket chain */ for (prev=NULL,ent=set->hash[index]; ent; prev=ent,ent=ent->hash_next) { if (ent == blk) break; } assert(ent); /* unlink the block from the hash table bucket chain */ if (!prev) { /* head of hash bucket list */ set->hash[index] = ent->hash_next; } else { /* middle or end of hash bucket list */ prev->hash_next = ent->hash_next; } ent->hash_next = NULL;}/* insert BLK onto the head of the hash table bucket chain in SET */static voidlink_htab_ent(struct cache *cp, /* cache to update */ struct cache_set *set, /* set containing bkt chain */ struct cache_blk *blk) /* block to insert */{ int index = CACHE_HASH(cp, blk->tag); /* insert block onto the head of the bucket chain */ blk->hash_next = set->hash[index]; set->hash[index] = blk;}/* where to insert a block onto the ordered way chain */enum list_loc_t { Head, Tail };/* insert BLK into the order way chain in SET at location WHERE */static voidupdate_way_list(struct cache_set *set, /* set contained way chain */ struct cache_blk *blk, /* block to insert */ enum list_loc_t where) /* insert location */{ /* unlink entry from the way list */ if (!blk->way_prev && !blk->way_next) { /* only one entry in list (direct-mapped), no action */ assert(set->way_head == blk && set->way_tail == blk); /* Head/Tail order already */ return; } /* else, more than one element in the list */ else if (!blk->way_prev) { assert(set->way_head == blk && set->way_tail != blk); if (where == Head) { /* already there */ return; } /* else, move to tail */ set->way_head = blk->way_next; blk->way_next->way_prev = NULL; } else if (!blk->way_next) { /* end of list (and not front of list) */ assert(set->way_head != blk && set->way_tail == blk); if (where == Tail) { /* already there */ return; } set->way_tail = blk->way_prev; blk->way_prev->way_next = NULL; } else { /* middle of list (and not front or end of list) */ assert(set->way_head != blk && set->way_tail != blk); blk->way_prev->way_next = blk->way_next; blk->way_next->way_prev = blk->way_prev; } /* link BLK back into the list */ if (where == Head) { /* link to the head of the way list */ blk->way_next = set->way_head; blk->way_prev = NULL; set->way_head->way_prev = blk; set->way_head = blk; } else if (where == Tail) { /* link to the tail of the way list */ blk->way_prev = set->way_tail; blk->way_next = NULL; set->way_tail->way_next = blk; set->way_tail = blk; } else panic("bogus WHERE designator");}/* create and initialize a general cache structure */struct cache * /* pointer to cache created */cache_create(char *name, /* name of the cache */ int nsets, /* total number of sets in cache */ int bsize, /* block (line) size of cache */ int balloc, /* allocate data space for blocks? */ int usize, /* size of user data to alloc w/blks */ int assoc, /* associativity of cache */ enum cache_policy policy, /* replacement policy w/in sets */ /* block access function, see description w/in struct cache def */ unsigned int (*blk_access_fn)(enum mem_cmd cmd, SS_ADDR_TYPE baddr, int bsize, struct cache_blk *blk, SS_TIME_TYPE now), unsigned int hit_latency) /* latency in cycles for a hit */{ struct cache *cp; struct cache_blk *blk; int i, j, bindex; /* check all cache parameters */ if (nsets <= 0) fatal("cache size (in sets) `%d' must be non-zero", nsets); if ((nsets & (nsets-1)) != 0) fatal("cache size (in sets) `%d' is not a power of two", nsets); /* blocks must be at least one datum large, i.e., 8 bytes for SS */ if (bsize < 8) fatal("cache block size (in bytes) `%d' must be 8 or greater", bsize); if ((bsize & (bsize-1)) != 0) fatal("cache block size (in bytes) `%d' must be a power of two", bsize); if (usize < 0) fatal("user data size (in bytes) `%d' must be a positive value", usize); if (assoc <= 0) fatal("cache associativity `%d' must be non-zero and positive", assoc); if ((assoc & (assoc-1)) != 0) fatal("cache associativity `%d' must be a power of two", assoc); if (!blk_access_fn) fatal("must specify miss/replacement functions"); /* allocate the cache structure */ cp = (struct cache *) calloc(1, sizeof(struct cache) + (nsets-1)*sizeof(struct cache_set)); if (!cp) fatal("out of virtual memory"); /* initialize user parameters */ cp->name = mystrdup(name); cp->nsets = nsets; cp->bsize = bsize; cp->balloc = balloc; cp->usize = usize; cp->assoc = assoc; cp->policy = policy; cp->hit_latency = hit_latency; /* miss/replacement functions */ cp->blk_access_fn = blk_access_fn; /* compute derived parameters */ cp->hsize = CACHE_HIGHLY_ASSOC(cp) ? (assoc >> 2) : 0; cp->blk_mask = bsize-1; cp->set_shift = log_base2(bsize); cp->set_mask = nsets-1; cp->tag_shift = cp->set_shift + log_base2(nsets); cp->tag_mask = (1 << (32 - cp->tag_shift))-1; cp->tagset_mask = ~cp->blk_mask; cp->bus_free = 0; /* print derived parameters during debug */ debug("%s: cp->hsize = %d", cp->hsize); debug("%s: cp->blk_mask = 0x%08x", cp->blk_mask); debug("%s: cp->set_shift = %d", cp->set_shift); debug("%s: cp->set_mask = 0x%08x", cp->set_mask); debug("%s: cp->tag_shift = %d", cp->tag_shift); debug("%s: cp->tag_mask = 0x%08x", cp->tag_mask); /* initialize cache stats */ cp->hits = 0; cp->misses = 0; cp->replacements = 0; cp->writebacks = 0; cp->invalidations = 0; /* blow away the last block accessed */ cp->last_tagset = 0; cp->last_blk = NULL; /* allocate data blocks */ cp->data = (char *)calloc(nsets * assoc, sizeof(struct cache_blk) + (cp->balloc ? (bsize*sizeof(char)) : 0)); if (!cp->data) fatal("out of virtual memory"); /* slice up the data blocks */ for (bindex=0,i=0; i<nsets; i++) { cp->sets[i].way_head = NULL; cp->sets[i].way_tail = NULL; /* get a hash table, if needed */ if (cp->hsize) { cp->sets[i].hash = (struct cache_blk **)calloc(cp->hsize, sizeof(struct cache_blk *)); if (!cp->sets[i].hash) fatal("out of virtual memory"); } /* NOTE: all the blocks in a set *must* be allocated contiguously, otherwise, block accesses through SET->BLKS will fail (used during random replacement selection) */ cp->sets[i].blks = CACHE_BINDEX(cp, cp->data, bindex); /* link the data blocks into ordered way chain and hash table bucket chains, if hash table exists */ for (j=0; j<assoc; j++) { /* locate next cache block */ blk = CACHE_BINDEX(cp, cp->data, bindex); bindex++; /* invalidate new cache block */ blk->status = 0; blk->tag = 0; blk->ready = 0; blk->user_data = usize ? calloc(usize, sizeof(char)) : NULL; /* insert cache block into set hash table */ if (cp->hsize) link_htab_ent(cp, &cp->sets[i], blk); /* insert into head of way list, order is arbitrary at this point */ blk->way_next = cp->sets[i].way_head; blk->way_prev = NULL; if (cp->sets[i].way_head) cp->sets[i].way_head->way_prev = blk; cp->sets[i].way_head = blk; if (!cp->sets[i].way_tail) cp->sets[i].way_tail = blk; } } return cp;}/* parse policy */enum cache_policy /* replacement policy enum */cache_char2policy(char c) /* replacement policy as a char */{ switch (c) { case 'l': return LRU; case 'r': return Random; case 'f': return FIFO; default: fatal("bogus replacement policy, `%c'", c); }}/* print cache configuration */voidcache_config(struct cache *cp, /* cache instance */ FILE *stream) /* output stream */{ fprintf(stream, "cache: %s: %d sets, %d byte blocks, %d bytes user data/block\n", cp->name, cp->nsets, cp->bsize, cp->usize); fprintf(stream, "cache: %s: %d-way, `%s' replacement policy, write-back\n", cp->name, cp->assoc, cp->policy == LRU ? "LRU"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -