From ee5ea34857e4ecc6c63d4971e549076c6a9888ba Mon Sep 17 00:00:00 2001 From: Tor Aamodt Date: Tue, 19 Oct 2010 23:10:51 -0800 Subject: 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] --- configs/QuadroFX5800/gpgpusim.config | 13 +- src/gpgpu-sim/gpu-cache.cc | 27 +- src/gpgpu-sim/gpu-cache.h | 701 ++++++++++++++++++++++---------- src/gpgpu-sim/gpu-sim.cc | 2 + src/gpgpu-sim/l2cache.cc | 2 +- src/gpgpu-sim/l2cache.h | 2 +- src/gpgpu-sim/mem_fetch.cc | 20 +- src/gpgpu-sim/mem_fetch.h | 3 +- src/gpgpu-sim/shader.cc | 8 +- src/gpgpu-sim/shader.h | 6 +- src/intersim/interconnect_interface.cpp | 2 +- 11 files changed, 538 insertions(+), 248 deletions(-) diff --git a/configs/QuadroFX5800/gpgpusim.config b/configs/QuadroFX5800/gpgpusim.config index f8d27f5..6f1c936 100644 --- a/configs/QuadroFX5800/gpgpusim.config +++ b/configs/QuadroFX5800/gpgpusim.config @@ -17,18 +17,13 @@ -gpgpu_simd_model 1 # memory stage behaviour --gpgpu_no_dl1 1 --gpgpu_n_cache_bank 1 --gpgpu_cache:dl1 128:64:4:L:T:m --gpgpu_tex_cache:l1 8:32:20:L:R:m --gpgpu_const_cache:l1 64:64:2:L:R:f --gpgpu_cache:dl2 64:32:8:L:R:m +-gpgpu_cache:il1 4:256:4:L:R:f,A:2:32,4 +-gpgpu_tex_cache:l1 8:32:20:L:R:m,F:128:4,16:2 +-gpgpu_const_cache:l1 64:64:2:L:R:f,A:2:32,4 +-gpgpu_cache:dl2 64:32:8:L:R:m,A:16:4,4 -gpgpu_shmem_pipe_speedup 2 -gpgpu_shmem_port_per_bank 2 --gpgpu_cache_port_per_bank 2 --gpgpu_const_port_per_bank 2 --gpgpu_interwarp_mshr_merge 6 # interconnection -network_mode 1 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; - } - - 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; + 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; }; 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; - - int m_core_id; // which shader core is using this - int m_type_id; // what kind of cache is this (normal, texture, constant) + // 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) }; 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 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 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 m_fragment_fifo; + fifo m_request_fifo; + fifo m_rob; + data_block *m_cache; + fifo 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 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," \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 > m_pending_writes; std::list 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 diff --git a/src/intersim/interconnect_interface.cpp b/src/intersim/interconnect_interface.cpp index b838dcf..40cf00d 100644 --- a/src/intersim/interconnect_interface.cpp +++ b/src/intersim/interconnect_interface.cpp @@ -467,7 +467,7 @@ void init_interconnect (char* config_file, if (icnt_config.GetInt("input_buf_size")) { input_buffer_capacity = icnt_config.GetInt("input_buf_size"); } else { - input_buffer_capacity = 8; + input_buffer_capacity = 9; } create_buf(traffic[0]->_dests,input_buffer_capacity,icnt_config.GetInt( "num_vcs" )); MATLAB_OUTPUT = icnt_config.GetInt("MATLAB_OUTPUT"); -- cgit v1.3