From ae2fbc54c20e9a22d96410cf4c6bf3fbaf6a5f6e Mon Sep 17 00:00:00 2001 From: Tayler Hetherington Date: Mon, 17 Sep 2012 13:12:31 -0800 Subject: Moved the majority of function definitions (greater than one line) from gpu-cache.h to gpu-cache.cc for mshr_table, baseline/read_only/data/texture_caches [git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 14090] --- src/gpgpu-sim/gpu-cache.cc | 427 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) (limited to 'src/gpgpu-sim/gpu-cache.cc') diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc index ace2686..2d1a7b7 100644 --- a/src/gpgpu-sim/gpu-cache.cc +++ b/src/gpgpu-sim/gpu-cache.cc @@ -218,3 +218,430 @@ bool was_read_sent( const std::list &events ) } return false; } +/****************************************************************** MSHR ******************************************************************/ + +// is there a pending request to the lower memory level already? +bool mshr_table::probe( new_addr_type block_addr ) const{ + table::const_iterator a = m_data.find(block_addr); + return a != m_data.end(); +} + +// is there space for tracking a new memory access? +bool mshr_table::full( new_addr_type block_addr ) const{ + table::const_iterator i=m_data.find(block_addr); + if ( i != m_data.end() ) + return i->second.m_list.size() >= m_max_merged; + else + return m_data.size() >= m_num_entries; +} + +// add or merge this access +void mshr_table::add( new_addr_type block_addr, mem_fetch *mf ){ + m_data[block_addr].m_list.push_back(mf); + assert( m_data.size() <= m_num_entries ); + assert( m_data[block_addr].m_list.size() <= m_max_merged ); + // indicate that this MSHR entry contains an atomic operation + if ( mf->isatomic() ) { + m_data[block_addr].m_has_atomic = true; + } +} + +// accept a new cache fill response: mark entry ready for processing +void mshr_table::mark_ready( new_addr_type block_addr, bool &has_atomic ){ + 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 ); + has_atomic = a->second.m_has_atomic; + assert( m_current_response.size() <= m_data.size() ); +} + +// next ready access +mem_fetch *mshr_table::next_access(){ + assert( access_ready() ); + new_addr_type block_addr = m_current_response.front(); + assert( !m_data[block_addr].m_list.empty() ); + mem_fetch *result = m_data[block_addr].m_list.front(); + m_data[block_addr].m_list.pop_front(); + if ( m_data[block_addr].m_list.empty() ) { + // release entry + m_data.erase(block_addr); + m_current_response.pop_front(); + } + return result; +} + +void mshr_table::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, atomic=%d %zu entries : ", block_addr, e->second.m_has_atomic, e->second.m_list.size()); + if ( !e->second.m_list.empty() ) { + mem_fetch *mf = e->second.m_list.front(); + fprintf(fp,"%p :",mf); + mf->print(fp); + } else { + fprintf(fp," no memory requests???\n"); + } + } +} +/***************************************************************** Caches *****************************************************************/ + +void baseline_cache::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); + } + } +} + +// interface for response from lower memory level (model bandwidth restictions in caller) +void baseline_cache::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 ); + 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(); + bool has_atomic = false; + m_mshrs.mark_ready(e->second.m_block_addr, has_atomic); + if (has_atomic) { + assert(m_config.m_alloc_policy == ON_MISS); + cache_block_t &block = m_tag_array.get_block(e->second.m_cache_index); + block.m_status = MODIFIED; // mark line as dirty for atomic operation + } + m_extra_mf_fields.erase(mf); +} + +bool baseline_cache::waiting_for_fill( mem_fetch *mf ){ + extra_mf_fields_lookup::iterator e = m_extra_mf_fields.find(mf); + return e != m_extra_mf_fields.end(); +} + +void baseline_cache::print(FILE *fp, unsigned &accesses, unsigned &misses) const{ + fprintf( fp, "Cache %s:\t", m_name.c_str() ); + m_tag_array.print(fp,accesses,misses); +} + +void baseline_cache::display_state( FILE *fp ) const{ + fprintf(fp,"Cache %s:\n", m_name.c_str() ); + m_mshrs.display(fp); + fprintf(fp,"\n"); +} + +void baseline_cache::read_request(new_addr_type addr, new_addr_type block_addr, unsigned cache_index, mem_fetch *mf, + unsigned time, bool &do_miss, std::list &events, bool read_only){ + // Read miss handler without writeback + bool wb=false; + cache_block_t e; + read_request(addr, block_addr, cache_index, mf, time, do_miss, wb, e, events, read_only); +} + +void baseline_cache::read_request(new_addr_type addr, new_addr_type block_addr, unsigned cache_index, mem_fetch *mf, + unsigned time, bool &do_miss, bool &wb, cache_block_t &evicted, std::list &events, bool read_only){ + // Read miss handler. Check MSHR hit or MSHR available + + if(m_config.set_index(addr) != m_config.set_index(block_addr)) + abort(); + if(m_config.tag(addr) != m_config.tag(block_addr)) + abort(); + + bool mshr_hit = m_mshrs.probe(block_addr); + bool mshr_avail = !m_mshrs.full(block_addr); + if ( mshr_hit && mshr_avail ) { + if(read_only) + m_tag_array.access(block_addr,time,cache_index); + else + m_tag_array.access(block_addr,time,cache_index,wb,evicted); + + m_mshrs.add(block_addr,mf); + do_miss = true; + } else if ( !mshr_hit && mshr_avail && (m_miss_queue.size() < m_config.m_miss_queue_size) ) { + if(read_only) + m_tag_array.access(block_addr,time,cache_index); + else + m_tag_array.access(block_addr,time,cache_index,wb,evicted); + + 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); + mf->set_status(m_miss_queue_status,time); + events.push_back(READ_REQUEST_SENT); + do_miss = true; + } +} + + +// access cache: returns RESERVATION_FAIL if request could not be accepted (for any reason) +enum cache_request_status read_only_cache::access( new_addr_type addr, mem_fetch *mf, unsigned time, std::list &events ) { + assert( mf->get_data_size() <= m_config.get_line_sz()); + + assert(m_config.m_write_policy == READ_ONLY); + assert(!mf->get_is_write()); + 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 ) { + if(!miss_queue_full(0)){ + bool do_miss=false; + read_request(addr, block_addr, cache_index, mf, time, do_miss, events, true); + if(do_miss) + return MISS; + } + } + return RESERVATION_FAIL; +} + + +// This is meant to model the first level data cache in Fermi. +// It is write-evict (global) or write-back (local) at the granularity for L1 and full write-back for L2 +// of individual blocks (the policy used in fermi according to the CUDA manual) +enum cache_request_status data_cache::access( new_addr_type addr, mem_fetch *mf, unsigned time, std::list &events ){ + assert( mf->get_data_size() <= m_config.get_line_sz()); + + bool wr = mf->get_is_write(); + bool isatomic = mf->isatomic(); + enum mem_access_type type = mf->get_access_type(); + + 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 ) { + + // If write through policy or private cache with global write hit + if(wr && (m_config.m_write_policy == WRITE_THROUGH || + ( (m_config.m_cache_scope == PRIVATE) && (type == GLOBAL_ACC_W) ))){ + // Write through + if(miss_queue_full(0)) + return RESERVATION_FAIL; // cannot handle request this cycle + + // generate a write through + cache_block_t &block = m_tag_array.get_block(cache_index); + write_request(mf, WRITE_REQUEST_SENT, time, events); + + // invalidate block + block.m_status = INVALID; + + }else{ // Write back cache or global read hit + m_tag_array.access(block_addr,time,cache_index); // update LRU state + if ( wr ) { + assert( type == LOCAL_ACC_W || type == L1_WRBK_ACC || m_config.m_cache_scope == SHARED); + // treated as write back... + cache_block_t &block = m_tag_array.get_block(cache_index); + block.m_status = MODIFIED; + } else if ( isatomic ) { + assert( type == GLOBAL_ACC_R ); + // treated as write back... + cache_block_t &block = m_tag_array.get_block(cache_index); + block.m_status = MODIFIED; // mark line as dirty + } + } + return HIT; + } else if ( status != RESERVATION_FAIL ) { + if ( wr ) { + if(m_config.m_write_aclloc_policy == NO_WRITE_ALLOCATE){ + // No write allocate, maximum 1 requests + if(miss_queue_full(0)) + return RESERVATION_FAIL; // cannot handle request this cycle + }else{ + // Write allocate, maximum 3 requests (write miss, read request, write back request) + // Conservatively ensure the worst-case request can be handled this cycle + bool mshr_hit = m_mshrs.probe(block_addr); + bool mshr_avail = !m_mshrs.full(block_addr); + if(miss_queue_full(2) || + ( !(mshr_hit && mshr_avail) && !(!mshr_hit && mshr_avail && (m_miss_queue.size() < m_config.m_miss_queue_size)) ) ) + return RESERVATION_FAIL; + } + + // on miss, generate write through (no write buffering -- too many threads for that) + write_request(mf, WRITE_REQUEST_SENT, time, events); + + // If no write allocate, simply return miss + if(m_config.m_write_aclloc_policy == NO_WRITE_ALLOCATE) + return MISS; + + // Write allocate - Generate new read miss + const mem_access_t *ma = new mem_access_t( L2_WR_ALLOC_R, + mf->get_addr(), + mf->get_data_size(), + false, // Now performing a read + mf->get_access_warp_mask(), + mf->get_access_byte_mask() ); + + mem_fetch *n_mf = new mem_fetch( *ma, + NULL, + mf->get_ctrl_size(), + mf->get_wid(), + mf->get_sid(), + mf->get_tpc(), + mf->get_mem_config()); + + bool do_miss = false; + bool wb = false; + cache_block_t evicted; + + // Send read request resulting from write miss + read_request(addr, block_addr, cache_index, n_mf, time, do_miss, wb, evicted, events, false); + + if( wb ) { // If evicted block is modified + mem_fetch *wb = m_memfetch_creator->alloc(evicted.m_block_addr,L2_WRBK_ACC,m_config.get_line_sz(),true); + m_miss_queue.push_back(wb); + wb->set_status(m_miss_queue_status,time); + } + if( do_miss ) + return MISS; + return RESERVATION_FAIL; + + } else { + if(miss_queue_full(1)) + return RESERVATION_FAIL; // cannot handle request this cycle (might need to generate two requests) + + bool do_miss = false; + bool wb = false; + cache_block_t evicted; + read_request(addr, block_addr, cache_index, mf, time, do_miss, wb, evicted, events, false); + + if(wb){ + mem_fetch *wb = m_memfetch_creator->alloc(evicted.m_block_addr, L1_WRBK_ACC,m_config.get_line_sz(),true); + write_request(wb, WRITE_BACK_REQUEST_SENT, time, events); + } + if( do_miss ) + return MISS; + } + } + return RESERVATION_FAIL; +} + +void data_cache::write_request(mem_fetch *mf, cache_event request, unsigned time, std::list &events){ + // Send write request to lower level memory (write or writeback) + events.push_back(request); + m_miss_queue.push_back(mf); + mf->set_status(m_miss_queue_status,time); +} + + +// 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 tex_cache::access( new_addr_type addr, mem_fetch *mf, unsigned time, std::list &events ) { + if ( m_fragment_fifo.full() || m_request_fifo.full() || m_rob.full() ) + return RESERVATION_FAIL; + + assert( mf->get_data_size() <= m_config.get_line_sz()); + + // 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); + mf->set_status(m_request_queue_status,time); + events.push_back(READ_REQUEST_SENT); + return MISS; + } else { + // the value *will* *be* in the cache already + return HIT_RESERVED; + } +} + +void tex_cache::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 tex_cache::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() ); + mf->set_status(m_rob_status,time); + + 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()) ); +} + +void tex_cache::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[%3d] : %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); + } +} +/******************************************************************************************************************************************/ + -- cgit v1.3