diff options
| author | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
|---|---|---|
| committer | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
| commit | 69f2911e04ffb1b19eef1fafb8c040af271f656e (patch) | |
| tree | 231d3b6bdc3a202f7c255bfcf7bf2c36e32cee9e /src/gpgpu-sim/gpu-cache.cc | |
creating branch for adding support for CUDA 3.x and Fermi
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 6829]
Diffstat (limited to 'src/gpgpu-sim/gpu-cache.cc')
| -rw-r--r-- | src/gpgpu-sim/gpu-cache.cc | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc new file mode 100644 index 0000000..679d1d2 --- /dev/null +++ b/src/gpgpu-sim/gpu-cache.cc @@ -0,0 +1,609 @@ +/* + * gpu-cache.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "gpu-cache.h" +#include "gpu-misc.h" +#include "addrdec.h" +#include <assert.h> +#include <string.h> + +// both shd_cache_access and shd_cache_probe functions use +// shd_cache_access_internal to access/probe cache +shd_cache_line_t* shd_cache_access_internal( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle, + unsigned int real_access); + +shd_cache_t * shd_cache_create( char *name, + unsigned int nset, + unsigned int assoc, + unsigned int line_sz, + unsigned char policy, + unsigned int hit_latency, + unsigned long long int bank_mask, + enum cache_write_policy wp) { + + shd_cache_t *cp; + unsigned int nlines; + + unsigned int i; + + if (!nset || !assoc) { + printf("Creating non-existing cache!\n"); + return 0; + } + + nlines = nset * assoc; + cp = (shd_cache_t*) calloc(1, sizeof(shd_cache_t)); + + cp->bank_mask = bank_mask; + cp->name = (char*) malloc(sizeof(char) * (strlen(name) + 1)); + strcpy(cp->name, name); + cp->nset = nset; + cp->nset_log2 = LOGB2(nset); + cp->assoc = assoc; + cp->line_sz = line_sz; + cp->line_sz_log2 = LOGB2(line_sz); + cp->policy = policy; + cp->hit_latency = hit_latency; + cp->lines = (shd_cache_line_t*) calloc(nlines, sizeof(shd_cache_line_t)); + cp->write_policy = wp; + + for (i=0; i<nlines; i++) { + cp->lines[i].line_sz = line_sz; + cp->lines[i].status = 0; + } + + // don't hook up with any logger + cp->core_id = -1; + cp->type_id = -1; + + // initialize snapshot counters for visualizer + cp->prev_snapshot_access = 0; + cp->prev_snapshot_miss = 0; + cp->prev_snapshot_merge_hit = 0; + +// printf("%s: %d(%x) x %d x %d(%x) %c, %d\n", +// cp->name, cp->nset, cp->nset_log2, cp->assoc, cp->line_sz, +// cp->line_sz_log2, cp->policy, nlines); + + return cp; +} + +void shd_cache_destroy( shd_cache_t* cp ) { + + free(cp->lines); + free(cp); +} + +extern void shader_cache_miss_log( int logger_id, int type ); +// hook up with shader core logger +void shd_cache_bind_logger(shd_cache_t* cp, int core_id, int type_id) { + cp->core_id = core_id; + cp->type_id = type_id; +} + +extern unsigned long long int addrdec_packbits(unsigned long long int mask, + unsigned long long int val, + unsigned char high, unsigned char low); +extern void shader_cache_access_log( int logger_id, int type, int miss); +extern void shader_cache_access_unlog( int logger_id, int type, int miss); + +shd_cache_line_t* shd_cache_access_internal( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle, + unsigned int real_access) { + //if real_access==0 then its only a cache probe stats and LRU tags should not be updated + unsigned int i; + unsigned int set; + unsigned long long int tag; + unsigned long long int packed_addr; + shd_cache_line_t *pline; + + if (cp->bank_mask) + packed_addr = addrdec_packbits(cp->bank_mask, addr, 64, 0); + else + packed_addr = addr; + + set = (packed_addr >> cp->line_sz_log2) & ( (1<<cp->nset_log2) - 1 ); + tag = packed_addr >> (cp->line_sz_log2 + cp->nset_log2); + + if (real_access) { + cp->access++; + shader_cache_access_log(cp->core_id, cp->type_id, 0); + } + + for (i=0; i<cp->assoc; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->status & VALID) { + if (pline->tag == tag) { + //printf("Cache Hit! Addr=%08x Set=%x Way=%x Tag=%x\n", packed_addr, set, i, tag); + if (real_access) { + pline->last_used = sim_cycle; + if (write) { + pline->status |= DIRTY; + } + } + return pline; + } + } + } + if (real_access) { + cp->miss++; + shader_cache_access_log(cp->core_id, cp->type_id, 1); + } + return 0; +} + +shd_cache_line_t* shd_cache_access( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle ) +{ + return shd_cache_access_internal(cp,addr,nbytes,write,sim_cycle,1/*this is a real access*/); +} + +extern int gpgpu_cache_wt_through; +shd_cache_t *test = NULL; +enum cache_request_status shd_cache_access_wb( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle, address_type *wb_address) +{ + unsigned int i; + unsigned int set; + unsigned long long int tag; + unsigned long long int packed_addr; + shd_cache_line_t *pline; + + unsigned already_reserved = 0; + unsigned all_reserved = 1; + shd_cache_line_t *free_line = NULL; + + if (cp->bank_mask) + packed_addr = addrdec_packbits(cp->bank_mask, addr, 64, 0); + else + packed_addr = addr; + + set = (packed_addr >> cp->line_sz_log2) & ( (1<<cp->nset_log2) - 1 ); + tag = packed_addr >> (cp->line_sz_log2 + cp->nset_log2); + + cp->access++; + shader_cache_access_log(cp->core_id, cp->type_id, 0); + + for (i=0; i<cp->assoc; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->tag == tag) { + if (pline->status & RESERVED) { + already_reserved = 1; + break; + } else if (pline->status & VALID) { + //printf("Cache Hit! Addr=%08x Set=%x Way=%x Tag=%x\n", packed_addr, set, i, tag); + pline->last_used = sim_cycle; + if (write) { + pline->status |= DIRTY; + } + //return pline; + if (cp->write_policy == write_through) return HIT_W_WT; + return HIT; + } + } + if (!(pline->status & RESERVED)) { + all_reserved = 0; + if (!(pline->status & VALID)) { + free_line = pline; + } + } + } + cp->miss++; + shader_cache_access_log(cp->core_id, cp->type_id, 1); + + if (already_reserved || cp->write_policy != write_back || write) { + //not in cache yet, but no wb as place is reserved. + //or wt caches never nead to worry about it (as do no_write caches) + //or is a write + if (already_reserved && write) { + //write the data into the cache line, make it dirty + //up to mshrs to save write mask to not overwrite this data when the read returns + pline->status |= DIRTY; + return WB_HIT_ON_MISS; + } else if (already_reserved) { + return WB_HIT_ON_MISS; + } + return MISS_NO_WB; + } + + //if not in cache, and a write back cache, and not already allocated, need to allocate a place for this request + + if (all_reserved) { + //cannot service this request, because we can't garantee that we have room for the line when it comes back + return RESERVATION_FAIL; + } + + //printf("RESRV %d\n",tag); + + if (free_line) { + //reserve fo this request + free_line->status |= RESERVED; + free_line->tag = tag; + //no writeback + return MISS_NO_WB; + } + + // need to kick a line out to reserve a spot + shd_cache_line_t *rline = NULL; + + for (i=0; i<cp->assoc; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->status & VALID && !(pline->status & RESERVED)) { + if (!rline) { + rline = pline; //select first available for ejection for later comparison + continue; + } + switch (cp->policy) { + case LRU: + if (pline->last_used < rline->last_used) + rline = pline; + break; + case FIFO: + if (pline->fetch_time < rline->fetch_time) + rline = pline; + break; + default: + rline = pline; //pick one, ie. the last valied one. + } + } + } + assert(rline); //ensure we actually found one. + + unsigned needs_wb = (rline->status & (DIRTY|VALID)) == (DIRTY|VALID); + /* Set the replaced cache line address */ + if (needs_wb) { + *wb_address = rline->addr; + } + + /* reserve this new line */ + rline->status |= RESERVED; + rline->status &= ~VALID; + rline->status &= ~DIRTY; + rline->tag = tag; + + /* printf("Fetching! Addr=%08x ReplAddr=%08x(%d) Set=%x Tag=%x\n", + packed_addr, repl_addr, nofreeslot, set, tag); + */ + if (needs_wb) { + return MISS_W_WB; + } else { + return MISS_NO_WB; + } +} + +shd_cache_line_t* shd_cache_probe( shd_cache_t *cp, + unsigned long long int addr) +{ + return shd_cache_access_internal(cp,addr, + 1,0,0, /*do not matter*/ + 0/*this is just a probe*/); +} + +void shd_cache_undo_stats( shd_cache_t *cp, int miss ) +{ + if (miss) { + cp->miss--; + shader_cache_access_unlog(cp->core_id, cp->type_id, 1); + } + cp->access--; + shader_cache_access_unlog(cp->core_id, cp->type_id, 0); +} + +// Obtain the windowed cache miss rate for visualizer +float shd_cache_windowed_cache_miss_rate( shd_cache_t *cp, int minus_merge_hit ) +{ + unsigned int n_access = cp->access - cp->prev_snapshot_access; + unsigned int n_miss = cp->miss - cp->prev_snapshot_miss; + unsigned int n_merge_hit = cp->merge_hit - cp->prev_snapshot_merge_hit; + + if (minus_merge_hit) { + n_miss -= n_merge_hit; + } + float missrate = 0.0f; + if (n_access != 0) { + missrate = (float) n_miss / n_access; + } + + return missrate; +} + +// start a new sampling window +void shd_cache_new_window( shd_cache_t *cp ) +{ + cp->prev_snapshot_access = cp->access; + cp->prev_snapshot_miss = cp->miss; + cp->prev_snapshot_merge_hit = cp->merge_hit; +} + +unsigned long long int L2_shd_cache_fill( shd_cache_t *cp, + unsigned long long int addr, + unsigned int sim_cycle ) { + unsigned long long int result = shd_cache_fill(cp, addr, sim_cycle); + return result; +} + +static unsigned int _n_line_existed = 0; // debug counter + +// Fetch requested data into cache line. +// Returning address on the replaced line if it is dirty, or -1 if it is clean +// Assume the line is filled all at once. +unsigned long long int shd_cache_fill( shd_cache_t *cp, + unsigned long long int addr, + unsigned int sim_cycle ) { + + unsigned int i; + unsigned int set; + unsigned long long int tag; + unsigned long long int packed_addr; + unsigned long long int repl_addr; + + unsigned char nofreeslot; + unsigned char line_exists; + unsigned int base = 0 ; + unsigned int maxway = cp->assoc ; + + shd_cache_line_t *pline, *cline; + + if (cp->bank_mask) + packed_addr = addrdec_packbits(cp->bank_mask, addr, 64, 0); + else + packed_addr = addr; + set = (packed_addr >> cp->line_sz_log2) & ( (1<<cp->nset_log2) - 1 ); + tag = packed_addr >> (cp->line_sz_log2 + cp->nset_log2); + + if (cp->write_policy == write_back) { + //this request must have a reserved spot + cline = NULL; + for (i=base; i<maxway; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if ((pline->tag == tag) && (pline->status & RESERVED)) { + cline = pline; + break; + } + if ((pline->tag == tag) && (pline->status & VALID)) { + //A second fill has returned to a line in the cache + //discard it as line in cache may have been modified, or is the same + _n_line_existed++; + + return -1; + } + } + + //if (cline) printf("FOUND %d\n",tag); + //else printf("UNFOUND!!! %d\n", tag); + + if (!cline) printf("----!!! about to abort - this probably happened because global memory msrh merging is not enabled with a writeback cache !!!----\n"); + + assert(cline); //error if it doesn't have a reserved space + + /* Fetch data into block */ + cline->status &= ~RESERVED; + cline->status |= VALID; + //cline->status &= ~DIRTY; Don't clear dirty bit, as might be dirty from write. + cline->tag = tag; + cline->addr = addr; + cline->last_used = sim_cycle; + cline->fetch_time = sim_cycle; + + // no wb, already handled. + return -1; + } + + //behavior unchanged for write through cache... probably not all necessary. + + // Look for any free slots and the possibility that the line is in the cache already + nofreeslot = 1; + line_exists = 0; + for (i=base; i<maxway; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (!(pline->status & VALID)) { + cline = pline; + nofreeslot = 0; + break; + } else if (pline->tag == tag) { + cline = pline; + line_exists = 1; + break; + } + } + + if (line_exists) { + _n_line_existed += 1; + return -1; // don't need to spill any line, nor it needs to be filled + } + + if (nofreeslot) { + cline = &(cp->lines[set*cp->assoc+base] ); + for (i=1+base; i<maxway; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->status & VALID) { + switch (cp->policy) { + case LRU: + if (pline->last_used < cline->last_used) + cline = pline; + break; + case FIFO: + if (pline->fetch_time < cline->fetch_time) + cline = pline; + break; + default: + break; + } + } + } + } + + /* Set the replaced cache line address */ + if ((cline->status & (DIRTY|VALID)) == (DIRTY|VALID)) { + repl_addr = cline->addr; + } else { + repl_addr = -1; + } + + /* Fetch data into block */ + cline->status |= VALID; + cline->status &= ~DIRTY; + cline->tag = tag; + cline->addr = addr; + cline->last_used = sim_cycle; + cline->fetch_time = sim_cycle; + +/* printf("Fetching! Addr=%08x ReplAddr=%08x(%d) Set=%x Tag=%x\n", + packed_addr, repl_addr, nofreeslot, set, tag); + */ + return repl_addr; +} + +void shd_cache_mergehit( shd_cache_t *cp, unsigned long long int addr ) +{ + cp->merge_hit += 1; +} + +void shd_cache_print( shd_cache_t *cp, FILE *stream) { + fprintf( stream, "Cache %s:\t", cp->name); + fprintf( stream, "Size = %d B (%d Set x %d-way x %d byte line)\n", + cp->line_sz * cp->nset * cp->assoc, + cp->nset, cp->assoc, cp->line_sz ); + fprintf( stream, "\t\tAccess = %d, Miss = %d (%.3g), -MgHts = %d (%.3g)\n", + cp->access, cp->miss, (float) cp->miss / cp->access, + cp->miss - cp->merge_hit, (float) (cp->miss - cp->merge_hit) / cp->access); +} + +#ifdef UNIT_TEST + +int main() { + shd_cache_t *cp[3]; + unsigned int addr, i; + unsigned int cachenum; + unsigned int sim_cycle; + + unsigned int test_addrs[8] = { 0x100, 0x200, 0x300, 0x400, + 0x104, 0x204, 0x500, 0x100}; + unsigned int repl_addr[8] = {0,0,0,0,0,0,0,0}; + unsigned int rdwr[8] = {0,1,0,0,0,0,0,0}; + + sim_cycle = 0; + cp[0] = shd_cache_create ("cp1", 16, 4, 16, LRU, 1); + cp[1] = shd_cache_create ("cp2", 16, 4, 16, FIFO, 1); + + for (cachenum = 0; cachenum<2; cachenum++) + for (i=0; i<8; i++) { + if ( !shd_cache_access(cp[cachenum], test_addrs[i], 4, rdwr[i], sim_cycle) ) { + repl_addr[i] = shd_cache_fill(cp[cachenum], test_addrs[i], sim_cycle); + shd_cache_access(cp[cachenum], test_addrs[i], 4, rdwr[i], sim_cycle); + } + sim_cycle++; + } + + printf("replaced address:"); + for (i=0; i<8; i++) { + printf("0x%x ", repl_addr[i]); + } + printf("\n"); + shd_cache_print(cp[0],stdout); + shd_cache_print(cp[1],stdout); + + shd_cache_fill(cp[0], 0x104b3ecb0, sim_cycle); + printf("Accessing 64-bit address tag: %d\n", + shd_cache_access(cp[0], 0x104b3ecb2, 4, 0, sim_cycle)); + printf("Accessing 64-bit address tag: %d\n", + shd_cache_access(cp[0], 0x103433330, 4, 0, sim_cycle)); + + + shd_set_coherency_policy(2); + cp[2] = shd_cache_create("cp2", 16, 4, 16, LRU, 1); + shd_cache_fill(cp[2], 0x12345000, 0); + shd_cache_access(cp[2], 0x12345000, 4, 1, 0); + shd_cache_access(cp[2], 0x12345004, 4, 0, 0); + shd_cache_access(cp[2], 0x12345008, 4, 0, 0); + shd_cache_access(cp[2], 0x1234500C, 4, 1, 0); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0xf, + shd_cache_linedirty(cp[2], 0x12345000, 0xf), 1 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x6, + shd_cache_linedirty(cp[2], 0x12345000, 0x6), 0 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x1, + shd_cache_linedirty(cp[2], 0x12345000, 0x1), 1 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x8, + shd_cache_linedirty(cp[2], 0x12345000, 0x8), 1 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x9, + shd_cache_linedirty(cp[2], 0x12345000, 0x9), 1 ); + +} + +#endif |
