diff options
| author | Tor Aamodt <[email protected]> | 2010-10-18 02:43:17 -0800 |
|---|---|---|
| committer | Tor Aamodt <[email protected]> | 2010-10-18 02:43:17 -0800 |
| commit | 87e4da5fc6086c3d0a661af1929255a8cbd728d7 (patch) | |
| tree | a4f40e66f5ca0d6efdf9d51672a1180c8a381170 /src/gpgpu-sim/gpu-cache.cc | |
| parent | b577cbcdf229a2c02d1bf8584c6e82be7a14cb33 (diff) | |
Re-designed cache model:
- read only cache model with integrated mshrs (no L1D, yet); new
cache interface should be easily extendable to support texture
cache with latency fifo and separate tag/data arrays, though
this is not yet added (currently tags and data arrays are not
decoupled for texture)
- new partition model using the above
removes all old MSHRs, L1D etc...
passing CUDA 3.1 regression
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 7875]
Diffstat (limited to 'src/gpgpu-sim/gpu-cache.cc')
| -rw-r--r-- | src/gpgpu-sim/gpu-cache.cc | 354 |
1 files changed, 113 insertions, 241 deletions
diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc index 98909ce..b26afb2 100644 --- a/src/gpgpu-sim/gpu-cache.cc +++ b/src/gpgpu-sim/gpu-cache.cc @@ -1,5 +1,5 @@ /* - * gpu-cache.c + * gpu-cache.cc * * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, * George L. Yuan and the @@ -65,291 +65,163 @@ */ #include "gpu-cache.h" -#include "gpu-misc.h" -#include "addrdec.h" #include "stat-tool.h" -#include "gpu-sim.h" #include <assert.h> -#include <string.h> -cache_t::~cache_t() +tag_array::~tag_array() { delete m_lines; } -cache_t::cache_t( const char *name, const cache_config &config, int core_id, int type_id ) +tag_array::tag_array( const cache_config &config, int core_id, int type_id ) : m_config(config) { - m_name = name; - m_nset = config.nset; - m_nset_log2 = LOGB2(config.nset); - m_assoc = config.assoc; - m_line_sz = config.line_sz; - m_line_sz_log2 = LOGB2(config.line_sz); - m_replacement_policy = config.replacement_policy; - m_write_policy = config.get_write_policy(); - unsigned nlines = config.get_num_lines(); - m_lines = new cache_block_t[nlines]; + assert( m_config.m_write_policy == READ_ONLY ); + m_lines = new cache_block_t[ config.get_num_lines()]; // initialize snapshot counters for visualizer m_prev_snapshot_access = 0; m_prev_snapshot_miss = 0; - m_prev_snapshot_merge_hit = 0; + m_prev_snapshot_pending_hit = 0; m_core_id = core_id; m_type_id = type_id; } -enum cache_request_status cache_t::access( new_addr_type addr, bool write, unsigned int cycle, address_type *wb_address ) +enum cache_request_status tag_array::probe( new_addr_type addr, unsigned &idx ) const { - m_access++; - unsigned set_index = (addr >> m_line_sz_log2) & ( (1<<m_nset_log2) - 1 ); - new_addr_type tag = addr >> (m_line_sz_log2 + m_nset_log2); + assert( m_config.m_write_policy == READ_ONLY ); + unsigned set_index = m_config.set_index(addr); + new_addr_type tag = m_config.tag(addr); - bool all_reserved = true; - cache_block_t *pending_line = NULL; - cache_block_t *clean_line = NULL; + unsigned invalid_line = (unsigned)-1; + unsigned valid_line = (unsigned)-1; + unsigned valid_timestamp = (unsigned)-1; - shader_cache_access_log(m_core_id, m_type_id, 0); - - for (unsigned way=0; way<m_assoc; way++) { - cache_block_t *line = &(m_lines[set_index*m_assoc+way] ); - if (line->tag == tag) { - if (line->status & RESERVED) { - pending_line = line; - break; - } else if (line->status & VALID) { - line->last_used = cycle; - if (write) - line->status |= DIRTY; - if (m_write_policy == write_through) - return HIT_W_WT; - return HIT; - } - } - if (!(line->status & RESERVED)) { - all_reserved = false; - if (!(line->status & VALID)) - clean_line = line; - } + bool all_reserved = true; + + // check for hit or pending hit + for (unsigned way=0; way<m_config.m_assoc; way++) { + unsigned index = set_index*m_config.m_assoc+way; + cache_block_t *line = &m_lines[index]; + if (line->m_tag == tag) { + if( line->m_status == RESERVED ) { + idx = index; + return HIT_RESERVED; + } else if( line->m_status == VALID ) { + idx = index; + return HIT; + } else { + assert( line->m_status == INVALID ); + } + } + if(line->m_status != RESERVED) { + all_reserved = false; + if(line->m_status == INVALID) { + invalid_line = index; + } else { + // valid line : keep track of most appropriate replacement candidate + if( m_config.m_replacement_policy == LRU ) { + if( line->m_last_access_time < valid_timestamp ) { + valid_timestamp = line->m_last_access_time; + valid_line = index; + } + } else if( m_config.m_replacement_policy == FIFO ) { + if( line->m_alloc_time < valid_timestamp ) { + valid_timestamp = line->m_alloc_time; + valid_line = index; + } + } + } + } } - m_miss++; - shader_cache_access_log(m_core_id, m_type_id, 1); - - if (pending_line || m_write_policy != write_back || write) { - if (pending_line) { - if( write ) // write hit-under-miss (irrelevant whether write-back or write-through) - // - timing assumes a large enough write buffer in shader core that we never - // encounter a structural hazard - // - write buffer merged with returning cache block in zero cycles - pending_line->status |= DIRTY; - return WB_HIT_ON_MISS; - } - return MISS_NO_WB; + if( all_reserved ) { + assert( m_config.m_alloc_policy == ON_MISS ); + return RESERVATION_FAIL; // miss and not enough space in cache to allocate on miss } - // at this point: this must be a write back cache (and not a hit-under-miss) - assert( m_write_policy == write_back ); + if( invalid_line != (unsigned)-1 ) { + idx = invalid_line; + } else if( valid_line != (unsigned)-1) { + idx = valid_line; + } else abort(); // if an unreserved block exists, it is either invalid or replaceable - 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; + return MISS; +} - if (clean_line) { - // found a clean line in the cache so, no need to do a writeback - clean_line->status |= RESERVED; - clean_line->tag = tag; - return MISS_NO_WB; - } +enum cache_request_status tag_array::access( new_addr_type addr, unsigned time, unsigned &idx ) +{ + m_access++; + shader_cache_access_log(m_core_id, m_type_id, 0); // log accesses to cache + enum cache_request_status status = probe(addr,idx); + switch(status) { + case HIT_RESERVED: + m_pending_hit++; + case HIT: + m_lines[idx].m_last_access_time=time; + break; + case MISS: + m_miss++; + shader_cache_access_log(m_core_id, m_type_id, 1); // log cache misses + if( m_config.m_alloc_policy == ON_MISS ) + m_lines[idx].allocate( m_config.tag(addr), m_config.block_addr(addr), time ); + break; + case RESERVATION_FAIL: + m_miss++; + shader_cache_access_log(m_core_id, m_type_id, 1); // log cache misses + break; + } + return status; +} - // no clean lines, need to kick a line out to reserve a spot - cache_block_t *wb_line = NULL; - - for (unsigned way=0; way<m_assoc; way++) { - cache_block_t *line = &(m_lines[set_index*m_assoc+way] ); - if (line->status & VALID && !(line->status & RESERVED)) { - if (!wb_line) { - wb_line = line; - continue; - } - switch (m_replacement_policy) { - case LRU: - if (line->last_used < wb_line->last_used) - wb_line = line; - break; - case FIFO: - if (line->fetch_time < wb_line->fetch_time) - wb_line = line; - break; - default: - abort(); - } - } - } - assert(wb_line); // should always find a line - assert((wb_line->status & (DIRTY|VALID)) == (DIRTY|VALID)); // should be dirty (or we would have found a clean line earlier) - - // reserve line - wb_line->status = RESERVED; - wb_line->tag = tag; - *wb_address = wb_line->addr; +void tag_array::fill( new_addr_type addr, unsigned time ) +{ + assert( m_config.m_alloc_policy == ON_FILL ); + unsigned idx; + enum cache_request_status status = probe(addr,idx); + assert(status==MISS); // MSHR should have prevented redundant memory request + m_lines[idx].allocate( m_config.tag(addr), m_config.block_addr(addr), time ); + m_lines[idx].fill(time); +} - return MISS_W_WB; +void tag_array::fill( unsigned index, unsigned time ) +{ + assert( m_config.m_alloc_policy == ON_MISS ); + m_lines[index].fill(time); } -// Obtain the windowed cache miss rate for visualizer -float cache_t::windowed_cache_miss_rate( int minus_merge_hit ) +void tag_array::flush() { - cache_t *cp = this; - unsigned int n_access = cp->m_access - cp->m_prev_snapshot_access; - unsigned int n_miss = cp->m_miss - cp->m_prev_snapshot_miss; - unsigned int n_merge_hit = cp->m_merge_hit - cp->m_prev_snapshot_merge_hit; + for (unsigned i=0; i < m_config.get_num_lines(); i++) + m_lines[i].m_status = INVALID; +} + +float tag_array::windowed_miss_rate( bool minus_pending_hit ) const +{ + unsigned n_access = m_access - m_prev_snapshot_access; + unsigned n_miss = m_miss - m_prev_snapshot_miss; + unsigned n_pending_hit = m_pending_hit - m_prev_snapshot_pending_hit; - if (minus_merge_hit) - n_miss -= n_merge_hit; + if (minus_pending_hit) + n_miss -= n_pending_hit; float missrate = 0.0f; if (n_access != 0) missrate = (float) n_miss / n_access; - return missrate; } -// start a new sampling window -void cache_t::new_window() +void tag_array::new_window() { m_prev_snapshot_access = m_access; m_prev_snapshot_miss = m_miss; - m_prev_snapshot_merge_hit = m_merge_hit; -} - -// 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. -new_addr_type cache_t::fill( new_addr_type addr, unsigned int sim_cycle ) -{ - unsigned int base = 0 ; - unsigned int maxway = m_assoc; - cache_block_t *pline, *cline; - new_addr_type packed_addr = addr; - unsigned set = (packed_addr >> m_line_sz_log2) & ( (1<<m_nset_log2) - 1 ); - unsigned long long tag = packed_addr >> (m_line_sz_log2 + m_nset_log2); - - if (m_write_policy == write_back) { - //this request must have a reserved spot - cline = NULL; - for (unsigned i=base; i<maxway; i++) { - pline = &(m_lines[set*m_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 - return -1; - } - } - - 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 - bool nofreeslot = true; - bool line_exists = false; - for (unsigned i=base; i<maxway; i++) { - pline = &(m_lines[set*m_assoc+i] ); - if (!(pline->status & VALID)) { - cline = pline; - nofreeslot = false; - break; - } else if (pline->tag == tag) { - cline = pline; - line_exists = true; - break; - } - } - - if (line_exists) { - return -1; // don't need to spill any line, nor it needs to be filled - } - - if (nofreeslot) { - cline = &(m_lines[set*m_assoc+base] ); - for (unsigned i=1+base; i<maxway; i++) { - pline = &(m_lines[set*m_assoc+i] ); - if (pline->status & VALID) { - switch (m_replacement_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 */ - unsigned long long int repl_addr; - 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; - - return repl_addr; -} - -unsigned int cache_t::flush() -{ - int dirty_lines_flushed = 0 ; - for (unsigned i = 0; i < m_nset * m_assoc ; i++) { - if ( (m_lines[i].status & (DIRTY|VALID)) == (DIRTY|VALID) ) { - dirty_lines_flushed++; - } - m_lines[i].status &= ~VALID; - m_lines[i].status &= ~DIRTY; - } - return dirty_lines_flushed; + m_prev_snapshot_pending_hit = m_pending_hit; } -void cache_t::print( FILE *stream, unsigned &total_access, unsigned &total_misses ) +void tag_array::print( FILE *stream, unsigned &total_access, unsigned &total_misses ) const { - fprintf( stream, "Cache %s:\t", m_name.c_str() ); - fprintf( stream, "Size = %d B (%d Set x %d-way x %d byte line)\n", - m_line_sz * m_nset * m_assoc, - m_nset, m_assoc, m_line_sz ); + m_config.print(stream); fprintf( stream, "\t\tAccess = %d, Miss = %d (%.3g), -MgHts = %d (%.3g)\n", m_access, m_miss, (float) m_miss / m_access, - m_miss - m_merge_hit, (float) (m_miss - m_merge_hit) / m_access); + m_miss - m_pending_hit, (float) (m_miss - m_pending_hit) / m_access); total_misses+=m_miss; total_access+=m_access; } |
