summaryrefslogtreecommitdiff
path: root/src/gpgpu-sim
diff options
context:
space:
mode:
authorTor Aamodt <[email protected]>2010-10-19 23:10:51 -0800
committerTor Aamodt <[email protected]>2010-10-19 23:10:51 -0800
commitee5ea34857e4ecc6c63d4971e549076c6a9888ba (patch)
tree6931d8981a4179b479cfdc43cd3ec3972e754d9d /src/gpgpu-sim
parent6c65cb0119ca7c84993cab6b8828687e1b331bd0 (diff)
adding texture cache model with fragment fifo for latency hiding
passing CUDA 3.1 regression [git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 7886]
Diffstat (limited to 'src/gpgpu-sim')
-rw-r--r--src/gpgpu-sim/gpu-cache.cc27
-rw-r--r--src/gpgpu-sim/gpu-cache.h699
-rw-r--r--src/gpgpu-sim/gpu-sim.cc2
-rw-r--r--src/gpgpu-sim/l2cache.cc2
-rw-r--r--src/gpgpu-sim/l2cache.h2
-rw-r--r--src/gpgpu-sim/mem_fetch.cc20
-rw-r--r--src/gpgpu-sim/mem_fetch.h3
-rw-r--r--src/gpgpu-sim/shader.cc8
-rw-r--r--src/gpgpu-sim/shader.h6
9 files changed, 532 insertions, 237 deletions
diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc
index b26afb2..c746da7 100644
--- a/src/gpgpu-sim/gpu-cache.cc
+++ b/src/gpgpu-sim/gpu-cache.cc
@@ -148,27 +148,26 @@ enum cache_request_status tag_array::probe( new_addr_type addr, unsigned &idx )
return MISS;
}
-enum cache_request_status tag_array::access( new_addr_type addr, unsigned time, unsigned &idx )
-{
+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) {
+ switch (status) {
case HIT_RESERVED:
- m_pending_hit++;
+ m_pending_hit++;
case HIT:
- m_lines[idx].m_last_access_time=time;
- break;
+ 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;
+ 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;
+ m_miss++;
+ shader_cache_access_log(m_core_id, m_type_id, 1); // log cache misses
+ break;
}
return status;
}
diff --git a/src/gpgpu-sim/gpu-cache.h b/src/gpgpu-sim/gpu-cache.h
index df08e91..c75b24e 100644
--- a/src/gpgpu-sim/gpu-cache.h
+++ b/src/gpgpu-sim/gpu-cache.h
@@ -74,8 +74,6 @@
#include "../abstract_hardware_model.h"
#include "../tr1_hash_map.h"
-class mem_fetch; // mem_fetch opaque to cache and mshrs
-
enum cache_block_state {
INVALID,
RESERVED,
@@ -90,37 +88,37 @@ enum cache_request_status {
};
struct cache_block_t {
- cache_block_t()
- {
- m_tag=0;
- m_block_addr=0;
- m_alloc_time=0;
- m_fill_time=0;
- m_last_access_time=0;
- m_status=INVALID;
- }
- void allocate( new_addr_type tag, new_addr_type block_addr, unsigned time )
- {
- m_tag=tag;
- m_block_addr=block_addr;
- m_alloc_time=time;
- m_last_access_time=time;
- m_fill_time=0;
- m_status=RESERVED;
- }
- void fill( unsigned time )
- {
- assert( m_status == RESERVED );
- m_status=VALID;
- m_fill_time=time;
- }
+ cache_block_t()
+ {
+ m_tag=0;
+ m_block_addr=0;
+ m_alloc_time=0;
+ m_fill_time=0;
+ m_last_access_time=0;
+ m_status=INVALID;
+ }
+ void allocate( new_addr_type tag, new_addr_type block_addr, unsigned time )
+ {
+ m_tag=tag;
+ m_block_addr=block_addr;
+ m_alloc_time=time;
+ m_last_access_time=time;
+ m_fill_time=0;
+ m_status=RESERVED;
+ }
+ void fill( unsigned time )
+ {
+ assert( m_status == RESERVED );
+ m_status=VALID;
+ m_fill_time=time;
+ }
- new_addr_type m_tag;
- new_addr_type m_block_addr;
- unsigned m_alloc_time;
- unsigned m_last_access_time;
- unsigned m_fill_time;
- cache_block_state m_status;
+ new_addr_type m_tag;
+ new_addr_type m_block_addr;
+ unsigned m_alloc_time;
+ unsigned m_last_access_time;
+ unsigned m_fill_time;
+ cache_block_state m_status;
};
enum replacement_policy_t {
@@ -148,72 +146,72 @@ class cache_config {
public:
cache_config()
{
- m_valid = false;
- m_config_string = NULL; // set by option parser
+ m_valid = false;
+ m_config_string = NULL; // set by option parser
}
void init()
{
- assert( m_config_string );
- char rp, wp, ap, mshr_type;
- int ntok = sscanf(m_config_string,"%u:%u:%u:%c:%c:%c,%c:%u:%u,%u",
- &m_nset, &m_line_sz, &m_assoc, &rp, &wp, &ap,
- &mshr_type,&m_mshr_entries,&m_mshr_max_merge,&m_miss_queue_size);
- if( ntok < 10 )
- exit_parse_error();
- switch(rp) {
- case 'L': m_replacement_policy = LRU; break;
- case 'F': m_replacement_policy = FIFO; break;
- default: exit_parse_error();
- }
- switch(wp) {
- case 'R': m_write_policy = READ_ONLY; break;
- case 'B': m_write_policy = WRITE_BACK; break;
- case 'T': m_write_policy = WRITE_THROUGH; break;
- default: exit_parse_error();
- }
- switch(ap) {
- case 'm': m_alloc_policy = ON_MISS; break;
- case 'f': m_alloc_policy = ON_FILL; break;
- default: exit_parse_error();
- }
- switch(mshr_type) {
- case 'F': m_mshr_type = TEX_FIFO; break;
- case 'A': m_mshr_type = ASSOC; break;
- default: exit_parse_error();
- }
- m_line_sz_log2 = LOGB2(m_line_sz);
- m_nset_log2 = LOGB2(m_nset);
- m_valid = true;
+ assert( m_config_string );
+ char rp, wp, ap, mshr_type;
+ int ntok = sscanf(m_config_string,"%u:%u:%u:%c:%c:%c,%c:%u:%u,%u:%u",
+ &m_nset, &m_line_sz, &m_assoc, &rp, &wp, &ap,
+ &mshr_type,&m_mshr_entries,&m_mshr_max_merge,&m_miss_queue_size,&m_result_fifo_entries);
+ if ( ntok < 10 )
+ exit_parse_error();
+ switch (rp) {
+ case 'L': m_replacement_policy = LRU; break;
+ case 'F': m_replacement_policy = FIFO; break;
+ default: exit_parse_error();
+ }
+ switch (wp) {
+ case 'R': m_write_policy = READ_ONLY; break;
+ case 'B': m_write_policy = WRITE_BACK; break;
+ case 'T': m_write_policy = WRITE_THROUGH; break;
+ default: exit_parse_error();
+ }
+ switch (ap) {
+ case 'm': m_alloc_policy = ON_MISS; break;
+ case 'f': m_alloc_policy = ON_FILL; break;
+ default: exit_parse_error();
+ }
+ switch (mshr_type) {
+ case 'F': m_mshr_type = TEX_FIFO; assert(ntok==11); break;
+ case 'A': m_mshr_type = ASSOC; break;
+ default: exit_parse_error();
+ }
+ m_line_sz_log2 = LOGB2(m_line_sz);
+ m_nset_log2 = LOGB2(m_nset);
+ m_valid = true;
}
unsigned get_line_sz() const
{
- assert( m_valid );
- return m_line_sz;
+ assert( m_valid );
+ return m_line_sz;
}
unsigned get_num_lines() const
{
- assert( m_valid );
- return m_nset * m_assoc;
+ assert( m_valid );
+ return m_nset * m_assoc;
}
void print( FILE *fp ) const
{
- fprintf( fp, "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 );
+ fprintf( fp, "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 );
}
unsigned set_index( new_addr_type addr ) const
{
- return (addr >> m_line_sz_log2) & (m_nset-1);
+ return(addr >> m_line_sz_log2) & (m_nset-1);
}
new_addr_type tag( new_addr_type addr ) const
{
- return addr >> (m_line_sz_log2+m_nset_log2);
+ return addr >> (m_line_sz_log2+m_nset_log2);
}
new_addr_type block_addr( new_addr_type addr ) const
{
- return addr & ~(m_line_sz-1);
+ return addr & ~(m_line_sz-1);
}
char *m_config_string;
@@ -221,8 +219,8 @@ public:
private:
void exit_parse_error()
{
- printf("GPGPU-Sim uArch: cache configuration parsing error (%s)\n", m_config_string );
- abort();
+ printf("GPGPU-Sim uArch: cache configuration parsing error (%s)\n", m_config_string );
+ abort();
}
bool m_valid;
@@ -237,18 +235,29 @@ private:
enum allocation_policy_t m_alloc_policy; // 'm' = allocate on miss, 'f' = allocate on fill
enum mshr_config_t m_mshr_type;
- unsigned m_mshr_entries;
- unsigned m_mshr_max_merge;
- unsigned m_miss_queue_size;
+ union {
+ unsigned m_mshr_entries;
+ unsigned m_fragment_fifo_entries;
+ };
+ union {
+ unsigned m_mshr_max_merge;
+ unsigned m_request_fifo_entries;
+ };
+ union {
+ unsigned m_miss_queue_size;
+ unsigned m_rob_entries;
+ };
+ unsigned m_result_fifo_entries;
friend class tag_array;
- friend class cache_t;
+ friend class read_only_cache;
+ friend class tex_cache;
};
class tag_array {
public:
tag_array( const cache_config &config, int core_id, int type_id );
- ~tag_array();
+ ~tag_array();
enum cache_request_status probe( new_addr_type addr, unsigned &idx ) const;
enum cache_request_status access( new_addr_type addr, unsigned time, unsigned &idx );
@@ -258,36 +267,36 @@ public:
void flush(); // flash invalidate all entries
void new_window();
-
+
void print( FILE *stream, unsigned &total_access, unsigned &total_misses ) const;
float windowed_miss_rate( bool minus_pending_hit ) const;
protected:
- const cache_config &m_config;
+ const cache_config &m_config;
+
+ cache_block_t *m_lines; /* nbanks x nset x assoc lines in total */
- cache_block_t *m_lines; /* nbanks x nset x assoc lines in total */
+ unsigned m_access;
+ unsigned m_miss;
+ unsigned m_pending_hit; // number of cache miss that hit a line that is allocated but not filled
- unsigned m_access;
- unsigned m_miss;
- unsigned m_pending_hit; // number of cache miss that hit a line that is allocated but not filled
+ // performance counters for calculating the amount of misses within a time window
+ unsigned m_prev_snapshot_access;
+ unsigned m_prev_snapshot_miss;
+ unsigned m_prev_snapshot_pending_hit;
- // performance counters for calculating the amount of misses within a time window
- unsigned m_prev_snapshot_access;
- unsigned m_prev_snapshot_miss;
- unsigned m_prev_snapshot_pending_hit;
-
- int m_core_id; // which shader core is using this
- int m_type_id; // what kind of cache is this (normal, texture, constant)
+ int m_core_id; // which shader core is using this
+ int m_type_id; // what kind of cache is this (normal, texture, constant)
};
class mshr_table {
public:
mshr_table( unsigned num_entries, unsigned max_merged )
- : m_num_entries(num_entries),
- m_max_merged(max_merged),
+ : m_num_entries(num_entries),
+ m_max_merged(max_merged),
#ifndef USE_MAP
- m_data(2*num_entries)
+ m_data(2*num_entries)
#endif
{
}
@@ -295,80 +304,80 @@ public:
// is there a pending request to the lower memory level already?
bool probe( new_addr_type block_addr ) const
{
- table::const_iterator a = m_data.find(block_addr);
- return a != m_data.end();
+ table::const_iterator a = m_data.find(block_addr);
+ return a != m_data.end();
}
// is there space for tracking a new memory access?
bool full( new_addr_type block_addr ) const
{
- table::const_iterator i=m_data.find(block_addr);
- if( i != m_data.end() )
- return i->second.size() >= m_max_merged;
- else
- return m_data.size() >= m_num_entries;
+ table::const_iterator i=m_data.find(block_addr);
+ if ( i != m_data.end() )
+ return i->second.size() >= m_max_merged;
+ else
+ return m_data.size() >= m_num_entries;
}
// add or merge this access
void add( new_addr_type block_addr, mem_fetch *mf )
{
- m_data[block_addr].push_back(mf);
- assert( m_data.size() <= m_num_entries );
- assert( m_data[block_addr].size() <= m_max_merged );
+ m_data[block_addr].push_back(mf);
+ assert( m_data.size() <= m_num_entries );
+ assert( m_data[block_addr].size() <= m_max_merged );
}
// true if cannot accept new fill responses
bool busy() const
{
- return false;
+ return false;
}
// accept a new cache fill response: mark entry ready for processing
void mark_ready( new_addr_type block_addr )
{
- assert( !busy() );
- table::iterator a = m_data.find(block_addr);
- assert( a != m_data.end() ); // don't remove same request twice
- m_current_response.push_back( block_addr );
- assert( m_current_response.size() <= m_data.size() );
+ assert( !busy() );
+ table::iterator a = m_data.find(block_addr);
+ assert( a != m_data.end() ); // don't remove same request twice
+ m_current_response.push_back( block_addr );
+ assert( m_current_response.size() <= m_data.size() );
}
// true if ready accesses exist
bool access_ready() const
{
- return !m_current_response.empty();
+ return !m_current_response.empty();
}
// next ready access
mem_fetch *next_access()
{
- assert( access_ready() );
- new_addr_type block_addr = m_current_response.front();
- assert( !m_data[block_addr].empty() );
- mem_fetch *result = m_data[block_addr].front();
- m_data[block_addr].pop_front();
- if( m_data[block_addr].empty() ) {
- // release entry
- m_data.erase(block_addr);
- m_current_response.pop_front();
- }
- return result;
+ assert( access_ready() );
+ new_addr_type block_addr = m_current_response.front();
+ assert( !m_data[block_addr].empty() );
+ mem_fetch *result = m_data[block_addr].front();
+ m_data[block_addr].pop_front();
+ if ( m_data[block_addr].empty() ) {
+ // release entry
+ m_data.erase(block_addr);
+ m_current_response.pop_front();
+ }
+ return result;
}
void display( FILE *fp ) const
{
- fprintf(fp,"MSHR contents\n");
- for( table::const_iterator e=m_data.begin(); e!=m_data.end(); ++e ) {
- unsigned block_addr = e->first;
- fprintf(fp,"MSHR: tag=0x%06x, %zu entries : ", block_addr, e->second.size());
- if( !e->second.empty() ) {
- mem_fetch *mf = e->second.front();
- fprintf(fp,"%p :",mf);
- mf->print(fp);
- } else {
- fprintf(fp," no memory requests???\n");
- }
- }
+ fprintf(fp,"MSHR contents\n");
+ for ( table::const_iterator e=m_data.begin(); e!=m_data.end(); ++e ) {
+ unsigned block_addr = e->first;
+ fprintf(fp,"MSHR: tag=0x%06x, %zu entries : ", block_addr, e->second.size());
+ if ( !e->second.empty() ) {
+ mem_fetch *mf = e->second.front();
+ fprintf(fp,"%p :",mf);
+ mf->print(fp);
+ } else {
+ fprintf(fp," no memory requests???\n");
+ }
+ }
}
private:
@@ -388,99 +397,106 @@ private:
class cache_t {
public:
- cache_t( const char *name, const cache_config &config, int core_id, int type_id, mem_fetch_interface *memport )
- : m_config(config), m_tag_array(config,core_id,type_id), m_mshrs(config.m_mshr_entries,config.m_mshr_max_merge)
+ virtual ~cache_t() {}
+ virtual enum cache_request_status access( new_addr_type addr, mem_fetch *mf, unsigned time ) = 0;
+};
+
+class read_only_cache : public cache_t {
+public:
+ read_only_cache( const char *name, const cache_config &config, int core_id, int type_id, mem_fetch_interface *memport )
+ : m_config(config), m_tag_array(config,core_id,type_id), m_mshrs(config.m_mshr_entries,config.m_mshr_max_merge)
{
- m_name = name;
- assert(config.m_mshr_type == ASSOC);
- assert(config.m_write_policy == READ_ONLY);
- m_memport=memport;
+ m_name = name;
+ assert(config.m_mshr_type == ASSOC);
+ assert(config.m_write_policy == READ_ONLY);
+ m_memport=memport;
}
// access cache: returns RESERVATION_FAIL if request could not be accepted (for any reason)
- enum cache_request_status access( new_addr_type addr, mem_fetch *mf, unsigned time )
- {
- new_addr_type block_addr = m_config.block_addr(addr);
- unsigned cache_index = (unsigned)-1;
- enum cache_request_status status = m_tag_array.probe(block_addr,cache_index);
- if( status == HIT ) {
- m_tag_array.access(block_addr,time,cache_index); // update LRU state
- return HIT;
- }
- if( status != RESERVATION_FAIL ) {
- bool mshr_hit = m_mshrs.probe(block_addr);
- bool mshr_avail = !m_mshrs.full(block_addr);
- if( mshr_hit && mshr_avail ) {
- m_tag_array.access(addr,time,cache_index);
- m_mshrs.add(block_addr,mf);
- return MISS;
- } else if( !mshr_hit && mshr_avail && (m_miss_queue.size() < m_config.m_miss_queue_size) ) {
- m_tag_array.access(addr,time,cache_index);
- m_mshrs.add(block_addr,mf);
- m_extra_mf_fields[mf] = extra_mf_fields(block_addr,cache_index);
- m_miss_queue.push_back(mf);
- return MISS;
- }
- }
- return RESERVATION_FAIL;
+ virtual enum cache_request_status access( new_addr_type addr, mem_fetch *mf, unsigned time ) {
+ new_addr_type block_addr = m_config.block_addr(addr);
+ unsigned cache_index = (unsigned)-1;
+ enum cache_request_status status = m_tag_array.probe(block_addr,cache_index);
+ if ( status == HIT ) {
+ m_tag_array.access(block_addr,time,cache_index); // update LRU state
+ return HIT;
+ }
+ if ( status != RESERVATION_FAIL ) {
+ bool mshr_hit = m_mshrs.probe(block_addr);
+ bool mshr_avail = !m_mshrs.full(block_addr);
+ if ( mshr_hit && mshr_avail ) {
+ m_tag_array.access(addr,time,cache_index);
+ m_mshrs.add(block_addr,mf);
+ return MISS;
+ } else if ( !mshr_hit && mshr_avail && (m_miss_queue.size() < m_config.m_miss_queue_size) ) {
+ m_tag_array.access(addr,time,cache_index);
+ m_mshrs.add(block_addr,mf);
+ m_extra_mf_fields[mf] = extra_mf_fields(block_addr,cache_index, mf->get_data_size());
+ mf->set_data_size( m_config.get_line_sz() );
+ m_miss_queue.push_back(mf);
+ return MISS;
+ }
+ }
+ return RESERVATION_FAIL;
}
void cycle()
{
- // send next request to lower level of memory
- if( !m_miss_queue.empty() ) {
- mem_fetch *mf = m_miss_queue.front();
- if( !m_memport->full(mf->get_data_size(),mf->get_is_write()) ) {
- m_miss_queue.pop_front();
- m_memport->push(mf);
- }
- }
+ // send next request to lower level of memory
+ if ( !m_miss_queue.empty() ) {
+ mem_fetch *mf = m_miss_queue.front();
+ if ( !m_memport->full(mf->get_data_size(),mf->get_is_write()) ) {
+ m_miss_queue.pop_front();
+ m_memport->push(mf);
+ }
+ }
}
// interface for response from lower memory level (model bandwidth restictions in caller)
void fill( mem_fetch *mf, unsigned time )
{
extra_mf_fields_lookup::iterator e = m_extra_mf_fields.find(mf);
- assert( e != m_extra_mf_fields.end() );
- assert( e->second.m_valid );
- if ( m_config.m_alloc_policy == ON_MISS )
- m_tag_array.fill(e->second.m_cache_index,time);
- else if ( m_config.m_alloc_policy == ON_FILL )
- m_tag_array.fill(e->second.m_block_addr,time);
- else abort();
- m_mshrs.mark_ready(e->second.m_block_addr);
- m_extra_mf_fields.erase(mf);
+ assert( e != m_extra_mf_fields.end() );
+ assert( e->second.m_valid );
+ mf->set_data_size( e->second.m_data_size );
+ if ( m_config.m_alloc_policy == ON_MISS )
+ m_tag_array.fill(e->second.m_cache_index,time);
+ else if ( m_config.m_alloc_policy == ON_FILL )
+ m_tag_array.fill(e->second.m_block_addr,time);
+ else abort();
+ m_mshrs.mark_ready(e->second.m_block_addr);
+ m_extra_mf_fields.erase(mf);
}
// are any (accepted) accesses that had to wait for memory now ready? (does not include accesses that "HIT")
bool access_ready() const
{
- return m_mshrs.access_ready();
+ return m_mshrs.access_ready();
}
// pop next ready access (does not include accesses that "HIT")
mem_fetch *next_access()
{
- return m_mshrs.next_access();
+ return m_mshrs.next_access();
}
// flash invalidate all entries in cache
void flush()
{
- m_tag_array.flush();
+ m_tag_array.flush();
}
void print(FILE *fp, unsigned &accesses, unsigned &misses) const
{
- fprintf( fp, "Cache %s:\t", m_name.c_str() );
- m_tag_array.print(fp,accesses,misses);
+ fprintf( fp, "Cache %s:\t", m_name.c_str() );
+ m_tag_array.print(fp,accesses,misses);
}
void display_state( FILE *fp ) const
{
- fprintf(fp,"Cache %s:\n", m_name.c_str() );
- m_mshrs.display(fp);
- fprintf(fp,"\n");
+ fprintf(fp,"Cache %s:\n", m_name.c_str() );
+ m_mshrs.display(fp);
+ fprintf(fp,"\n");
}
private:
@@ -492,20 +508,295 @@ private:
mem_fetch_interface *m_memport;
struct extra_mf_fields {
- extra_mf_fields() { m_valid = false; }
- extra_mf_fields( new_addr_type a, unsigned i )
- {
- m_block_addr = a;
- m_cache_index = i;
- m_valid = true;
- }
- bool m_valid;
- new_addr_type m_block_addr;
- unsigned m_cache_index;
+ extra_mf_fields() { m_valid = false;}
+ extra_mf_fields( new_addr_type a, unsigned i, unsigned d )
+ {
+ m_valid = true;
+ m_block_addr = a;
+ m_cache_index = i;
+ m_data_size = d;
+ }
+ bool m_valid;
+ new_addr_type m_block_addr;
+ unsigned m_cache_index;
+ unsigned m_data_size;
};
+
typedef std::map<mem_fetch*,extra_mf_fields> extra_mf_fields_lookup;
+
extra_mf_fields_lookup m_extra_mf_fields;
};
+
+// See the following paper to understand this cache model:
+//
+// Igehy, et al., Prefetching in a Texture Cache Architecture,
+// Proceedings of the 1998 Eurographics/SIGGRAPH Workshop on Graphics Hardware
+// http://www-graphics.stanford.edu/papers/texture_prefetch/
+class tex_cache : public cache_t {
+public:
+ tex_cache( const char *name, const cache_config &config, int core_id, int type_id, mem_fetch_interface *memport )
+ : m_config(config),
+ m_tags(config,core_id,type_id),
+ m_fragment_fifo(config.m_fragment_fifo_entries),
+ m_request_fifo(config.m_request_fifo_entries),
+ m_rob(config.m_rob_entries),
+ m_result_fifo(config.m_result_fifo_entries)
+ {
+ m_name = name;
+ assert(config.m_mshr_type == TEX_FIFO);
+ assert(config.m_write_policy == READ_ONLY);
+ assert(config.m_alloc_policy == ON_MISS);
+ m_memport=memport;
+ m_cache = new data_block[ config.get_num_lines() ];
+ }
+
+ // return values: RESERVATION_FAIL if request could not be accepted
+ // otherwise returns HIT_RESERVED or MISS; NOTE: *never* returns HIT
+ // since unlike a normal CPU cache, a "HIT" in texture cache does not
+ // mean the data is ready (still need to get through fragment fifo)
+ enum cache_request_status access( new_addr_type addr, mem_fetch *mf, unsigned time )
+ {
+ if( m_fragment_fifo.full() || m_request_fifo.full() || m_rob.full() )
+ return RESERVATION_FAIL;
+
+ // at this point, we will accept the request : access tags and immediately allocate line
+ new_addr_type block_addr = m_config.block_addr(addr);
+ unsigned cache_index = (unsigned)-1;
+ enum cache_request_status status = m_tags.access(block_addr,time,cache_index);
+ assert( status != RESERVATION_FAIL );
+ assert( status != HIT_RESERVED ); // as far as tags are concerned: HIT or MISS
+ m_fragment_fifo.push( fragment_entry(mf,cache_index,status==MISS,mf->get_data_size()) );
+ if( status == MISS ) {
+ // we need to send a memory request...
+ unsigned rob_index = m_rob.push( rob_entry(cache_index, mf, block_addr) );
+ m_extra_mf_fields[mf] = extra_mf_fields(rob_index);
+ mf->set_data_size(m_config.get_line_sz());
+ m_tags.fill(cache_index,time); // mark block as valid
+ m_request_fifo.push(mf);
+ return MISS;
+ } else {
+ // the value *will* *be* in the cache already
+ return HIT_RESERVED;
+ }
+ }
+
+ void cycle()
+ {
+ // send next request to lower level of memory
+ if ( !m_request_fifo.empty() ) {
+ mem_fetch *mf = m_request_fifo.peek();
+ if ( !m_memport->full(mf->get_ctrl_size(),false) ) {
+ m_request_fifo.pop();
+ m_memport->push(mf);
+ }
+ }
+ // read ready lines from cache
+ if( !m_fragment_fifo.empty() && !m_result_fifo.full() ) {
+ const fragment_entry &e = m_fragment_fifo.peek();
+ if( e.m_miss ) {
+ // check head of reorder buffer to see if data is back from memory
+ unsigned rob_index = m_rob.next_pop_index();
+ const rob_entry &r = m_rob.peek(rob_index);
+ assert( r.m_request == e.m_request );
+ assert( r.m_block_addr == m_config.block_addr(e.m_request->get_addr()) );
+ if( r.m_ready ) {
+ assert( r.m_index == e.m_cache_index );
+ m_cache[r.m_index].m_valid = true;
+ m_cache[r.m_index].m_block_addr = r.m_block_addr;
+ m_result_fifo.push(e.m_request);
+ m_rob.pop();
+ m_fragment_fifo.pop();
+ }
+ } else {
+ // hit:
+ assert( m_cache[e.m_cache_index].m_valid );
+ assert( m_cache[e.m_cache_index].m_block_addr = m_config.block_addr(e.m_request->get_addr()) );
+ m_result_fifo.push( e.m_request );
+ m_fragment_fifo.pop();
+ }
+ }
+ }
+
+ // place returning cache block into reorder buffer
+ void fill( mem_fetch *mf, unsigned time )
+ {
+ extra_mf_fields_lookup::iterator e = m_extra_mf_fields.find(mf);
+ assert( e != m_extra_mf_fields.end() );
+ assert( e->second.m_valid );
+ assert( !m_rob.empty() );
+
+ unsigned rob_index = e->second.m_rob_index;
+ rob_entry &r = m_rob.peek(rob_index);
+ assert( !r.m_ready );
+ r.m_ready = true;
+ r.m_time = time;
+ assert( r.m_block_addr == m_config.block_addr(mf->get_addr()) );
+ }
+
+ // are any (accepted) accesses that had to wait for memory now ready? (does not include accesses that "HIT")
+ bool access_ready() const
+ {
+ return !m_result_fifo.empty();
+ }
+
+ // pop next ready access (includes both accesses that "HIT" and those that "MISS")
+ mem_fetch *next_access()
+ {
+ return m_result_fifo.pop();
+ }
+
+ void display_state( FILE *fp ) const
+ {
+ fprintf(fp,"%s (texture cache) state:\n", m_name.c_str() );
+ fprintf(fp,"fragment fifo entries = %u / %u\n", m_fragment_fifo.size(), m_fragment_fifo.capacity() );
+ fprintf(fp,"reorder buffer entries = %u / %u\n", m_rob.size(), m_rob.capacity() );
+ fprintf(fp,"request fifo entries = %u / %u\n", m_request_fifo.size(), m_request_fifo.capacity() );
+ if( !m_rob.empty() )
+ fprintf(fp,"reorder buffer contents:\n");
+ for( int n=m_rob.size()-1; n>=0; n-- ) {
+ unsigned index = (m_rob.next_pop_index() + n)%m_rob.capacity();
+ const rob_entry &r = m_rob.peek(index);
+ fprintf(fp, "tex rob[%2d] : %s ", index, (r.m_ready?"ready ":"pending") );
+ if( r.m_ready )
+ fprintf(fp,"@%6u", r.m_time );
+ else
+ fprintf(fp," ");
+ fprintf(fp,"[idx=%4u]",r.m_index);
+ r.m_request->print(fp,false);
+ }
+ if( !m_fragment_fifo.empty() ) {
+ fprintf(fp,"fragment fifo (oldest) :");
+ fragment_entry &f = m_fragment_fifo.peek();
+ fprintf(fp,"%s: ", f.m_miss?"miss":"hit ");
+ f.m_request->print(fp,false);
+ }
+ }
+
+private:
+ std::string m_name;
+ const cache_config &m_config;
+
+ struct fragment_entry {
+ fragment_entry() {}
+ fragment_entry( mem_fetch *mf, unsigned idx, bool m, unsigned d )
+ {
+ m_request=mf;
+ m_cache_index=idx;
+ m_miss=m;
+ m_data_size=d;
+ }
+ mem_fetch *m_request; // request information
+ unsigned m_cache_index; // where to look for data
+ bool m_miss; // true if sent memory request
+ unsigned m_data_size;
+ };
+
+ struct rob_entry {
+ rob_entry() { m_ready = false; m_time=0; m_request=NULL; }
+ rob_entry( unsigned i, mem_fetch *mf, new_addr_type a )
+ {
+ m_ready=false;
+ m_index=i;
+ m_time=0;
+ m_request=mf;
+ m_block_addr=a;
+ }
+ bool m_ready;
+ unsigned m_time; // which cycle did this entry become ready?
+ unsigned m_index; // where in cache should block be placed?
+ mem_fetch *m_request;
+ new_addr_type m_block_addr;
+ };
+
+ struct data_block {
+ data_block() { m_valid = false; }
+ bool m_valid;
+ new_addr_type m_block_addr;
+ };
+
+ // TODO: replace fifo_pipeline with this?
+ template<class T> class fifo {
+ public:
+ fifo( unsigned size )
+ {
+ m_size=size;
+ m_num=0;
+ m_head=0;
+ m_tail=0;
+ m_data = new T[size];
+ }
+ bool full() const { return m_num == m_size; }
+ bool empty() const { return m_num == 0; }
+ unsigned size() const { return m_num; }
+ unsigned capacity() const { return m_size; }
+ unsigned push( const T &e )
+ {
+ assert(!full());
+ m_data[m_head] = e;
+ unsigned result = m_head;
+ inc_head();
+ return result;
+ }
+ T pop()
+ {
+ assert(!empty());
+ T result = m_data[m_tail];
+ inc_tail();
+ return result;
+ }
+ const T &peek( unsigned index ) const
+ {
+ assert( index < m_size );
+ return m_data[index];
+ }
+ T &peek( unsigned index )
+ {
+ assert( index < m_size );
+ return m_data[index];
+ }
+ T &peek() const
+ {
+ return m_data[m_tail];
+ }
+ unsigned next_pop_index() const
+ {
+ return m_tail;
+ }
+ private:
+ void inc_head() { m_head = (m_head+1)%m_size; m_num++; }
+ void inc_tail() { assert(m_num>0); m_tail = (m_tail+1)%m_size; m_num--; }
+
+ unsigned m_head; // next entry goes here
+ unsigned m_tail; // oldest entry found here
+ unsigned m_num; // how many in fifo?
+ unsigned m_size; // maximum number of entries in fifo
+ T *m_data;
+ };
+
+ tag_array m_tags;
+ fifo<fragment_entry> m_fragment_fifo;
+ fifo<mem_fetch*> m_request_fifo;
+ fifo<rob_entry> m_rob;
+ data_block *m_cache;
+ fifo<mem_fetch*> m_result_fifo; // next completed texture fetch
+
+ mem_fetch_interface *m_memport;
+
+ struct extra_mf_fields {
+ extra_mf_fields() { m_valid = false;}
+ extra_mf_fields( unsigned i )
+ {
+ m_valid = true;
+ m_rob_index = i;
+ }
+ bool m_valid;
+ unsigned m_rob_index;
+ };
+
+ typedef std::map<mem_fetch*,extra_mf_fields> extra_mf_fields_lookup;
+
+ extra_mf_fields_lookup m_extra_mf_fields;
+};
#endif
diff --git a/src/gpgpu-sim/gpu-sim.cc b/src/gpgpu-sim/gpu-sim.cc
index 0228704..e3e54dc 100644
--- a/src/gpgpu-sim/gpu-sim.cc
+++ b/src/gpgpu-sim/gpu-sim.cc
@@ -964,6 +964,8 @@ void gpgpu_sim::cycle()
} else {
gpu_stall_icnt2sh++;
}
+ } else {
+ m_memory_partition_unit[i]->pop();
}
}
}
diff --git a/src/gpgpu-sim/l2cache.cc b/src/gpgpu-sim/l2cache.cc
index 634fa55..9c443f0 100644
--- a/src/gpgpu-sim/l2cache.cc
+++ b/src/gpgpu-sim/l2cache.cc
@@ -108,7 +108,7 @@ memory_partition_unit::memory_partition_unit( unsigned partition_id,
char L2c_name[32];
snprintf(L2c_name, 32, "L2_bank_%03d", m_id);
m_L2interface = new L2interface(this);
- m_L2cache = new cache_t(L2c_name,m_config->m_L2_config,-1,-1,m_L2interface);
+ m_L2cache = new read_only_cache(L2c_name,m_config->m_L2_config,-1,-1,m_L2interface);
unsigned int icnt_L2;
unsigned int L2_dram;
diff --git a/src/gpgpu-sim/l2cache.h b/src/gpgpu-sim/l2cache.h
index 6900394..7d1a8b2 100644
--- a/src/gpgpu-sim/l2cache.h
+++ b/src/gpgpu-sim/l2cache.h
@@ -103,7 +103,7 @@ private:
unsigned m_id;
const struct memory_config *m_config;
class dram_t *m_dram;
- class cache_t *m_L2cache;
+ class read_only_cache *m_L2cache;
class L2interface *m_L2interface;
// model delay of ROP units with a fixed latency
diff --git a/src/gpgpu-sim/mem_fetch.cc b/src/gpgpu-sim/mem_fetch.cc
index 0d9e4b2..74a1ac1 100644
--- a/src/gpgpu-sim/mem_fetch.cc
+++ b/src/gpgpu-sim/mem_fetch.cc
@@ -128,21 +128,23 @@ static const char* Status_str[] = {
"IN_CLUSTER_TO_SHADER_QUEUE",
"IN_SHADER_LDST_RESPONSE_FIFO",
"IN_SHADER_FETCHED",
-"MEM_FETCH_DELETED",
-"??",
-"???"
+"MEM_FETCH_DELETED"
};
-void mem_fetch::print( FILE *fp ) const
+void mem_fetch::print( FILE *fp, bool print_inst ) const
{
- fprintf(fp," mf: uid=%6u, addr=0x%08llx, sid=%u, wid=%u, %s, partition=%u, ",
+ if( this == NULL ) {
+ fprintf(fp," <NULL mem_fetch pointer>\n");
+ return;
+ }
+ fprintf(fp," mf: uid=%6u, addr=0x%08llx, sid=%2u, wid=%2u, %s, partition=%u, ",
m_request_uid, m_addr, m_sid, m_wid, (m_write?"write":"read "), m_raw_addr.chip);
- if( (unsigned)m_status < NUM_MEM_REQ_STAT )
+ if( (unsigned)m_status < NUM_MEM_REQ_STAT )
fprintf(fp," status = %s (%llu), ", Status_str[m_status], m_status_change );
- else
+ else
fprintf(fp," status = %u??? (%llu), ", m_status, m_status_change );
- if( !m_inst.empty() ) m_inst.print(fp);
- else fprintf(fp,"\n");
+ if( !m_inst.empty() && print_inst ) m_inst.print(fp);
+ else fprintf(fp,"\n");
}
void mem_fetch::set_status( enum mem_fetch_status status, unsigned long long cycle )
diff --git a/src/gpgpu-sim/mem_fetch.h b/src/gpgpu-sim/mem_fetch.h
index 3aae63f..8fe09e0 100644
--- a/src/gpgpu-sim/mem_fetch.h
+++ b/src/gpgpu-sim/mem_fetch.h
@@ -136,10 +136,11 @@ public:
void set_type( enum mf_type t ) { m_type=t; }
void do_atomic();
- void print( FILE *fp ) const;
+ void print( FILE *fp, bool print_inst = true ) const;
const addrdec_t &get_tlx_addr() const { return m_raw_addr; }
unsigned get_data_size() const { return m_data_size; }
+ void set_data_size( unsigned size ) { m_data_size=size; }
unsigned get_ctrl_size() const { return m_ctrl_size; }
unsigned size() const { return m_data_size+m_ctrl_size; }
new_addr_type get_addr() const { return m_addr; }
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index 1f0ebc4..0604bb8 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -144,7 +144,7 @@ shader_core_ctx::shader_core_ctx( class gpgpu_sim *gpu,
#define STRSIZE 1024
char L1I_name[STRSIZE];
snprintf(L1I_name, STRSIZE, "L1I_%03d", m_sid);
- m_L1I = new cache_t(L1I_name,m_config->m_L1I_config,m_sid,get_shader_instruction_cache_id(),m_icnt);
+ m_L1I = new read_only_cache(L1I_name,m_config->m_L1I_config,m_sid,get_shader_instruction_cache_id(),m_icnt);
m_warp.resize(m_config->max_warps_per_shader, shd_warp_t(this, warp_size));
m_pdom_warp = new pdom_warp_ctx_t*[config->max_warps_per_shader];
@@ -711,7 +711,7 @@ mem_stage_stall_type ldst_unit::process_memory_access_queue( cache_t *cache, war
delete mf;
break;
} else {
- assert( status == MISS );
+ assert( status == MISS || status == HIT_RESERVED );
inst.accessq_pop_back();
if( inst.is_load() ) {
for( unsigned r=0; r < 4; r++)
@@ -873,8 +873,8 @@ ldst_unit::ldst_unit( shader_memory_interface *icnt,
char L1C_name[STRSIZE];
snprintf(L1T_name, STRSIZE, "L1T_%03d", m_sid);
snprintf(L1C_name, STRSIZE, "L1C_%03d", m_sid);
- m_L1T = new cache_t(L1T_name,m_config->m_L1T_config,m_sid,get_shader_texture_cache_id(),icnt);
- m_L1C = new cache_t(L1C_name,m_config->m_L1C_config,m_sid,get_shader_constant_cache_id(),icnt);
+ m_L1T = new tex_cache(L1T_name,m_config->m_L1T_config,m_sid,get_shader_texture_cache_id(),icnt);
+ m_L1C = new read_only_cache(L1C_name,m_config->m_L1C_config,m_sid,get_shader_constant_cache_id(),icnt);
m_mem_rc = NO_RC_FAIL;
m_num_writeback_clients=4; // = shared memory, global/local, L1T, L1C
m_writeback_arb = 0;
diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h
index 0d74e95..b9e0bda 100644
--- a/src/gpgpu-sim/shader.h
+++ b/src/gpgpu-sim/shader.h
@@ -879,8 +879,8 @@ private:
unsigned m_sid;
unsigned m_tpc;
- cache_t *m_L1T; // texture cache
- cache_t *m_L1C; // constant cache
+ tex_cache *m_L1T; // texture cache
+ read_only_cache *m_L1C; // constant cache
std::map<unsigned/*warp_id*/, std::map<unsigned/*regnum*/,unsigned/*count*/> > m_pending_writes;
std::list<mem_fetch*> m_response_fifo;
opndcoll_rfu_t *m_operand_collector;
@@ -1066,7 +1066,7 @@ private:
shader_memory_interface *m_icnt;
// fetch
- cache_t *m_L1I; // instruction cache
+ read_only_cache *m_L1I; // instruction cache
int m_last_warp_fetched;
// decode/dispatch