diff options
| author | Tim Rogers <[email protected]> | 2013-07-06 12:22:34 -0800 |
|---|---|---|
| committer | Andrew Boktor <[email protected]> | 2014-08-14 13:50:58 -0700 |
| commit | 7da201cb86702aca80407b94858c380fd90aba38 (patch) | |
| tree | 5ad4d81a260d9110679c03b9eb34bf57e14572f5 | |
| parent | ded72b7697f0244aa789e25330b58674797929b0 (diff) | |
Integrating changes from my personal branch.
Main contribution is a static warp limiting scheduler.
There is also some minor cleanup to the heirarchy of the cache code and removal some excessively long lines
Review ID: 36001 lgtm: 1
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 16580]
| -rw-r--r-- | CHANGES | 3 | ||||
| -rw-r--r-- | src/gpgpu-sim/gpu-cache.cc | 269 | ||||
| -rw-r--r-- | src/gpgpu-sim/gpu-cache.h | 188 | ||||
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 98 | ||||
| -rw-r--r-- | src/gpgpu-sim/shader.h | 23 |
5 files changed, 435 insertions, 146 deletions
@@ -26,6 +26,9 @@ Version 3.2.1+edits (development branch) versus 3.2.1 - Changes to the makefiles s.t. all intermediate files get output to the build directory, and nothing is written to the same directory as the source code - Implemented the WARPSZ query in CUDA. +- Implemented a Static Warp Limiting Scheduler similar described in Rogers et. al. + (MICRO 2012). +- Some whitespace cleanup. - Bug Fixes: - Fixed the flit count sent to GPUWattch for atomic operations. - Fix for Bug 51 - Updated the function declaration of diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc index f6f8ac8..4c5c5c9 100644 --- a/src/gpgpu-sim/gpu-cache.cc +++ b/src/gpgpu-sim/gpu-cache.cc @@ -204,6 +204,10 @@ enum cache_request_status tag_array::access( new_addr_type addr, unsigned time, m_res_fail++; shader_cache_access_log(m_core_id, m_type_id, 1); // log cache misses break; + default: + fprintf( stderr, "tag_array::access - Error: Unknown" + "cache_request_status %d\n", status ); + abort(); } return status; } @@ -660,22 +664,30 @@ enum cache_request_status data_cache::wr_hit_global_we_local_wb(new_addr_type ad /****** Write-miss functions (Set by config file) ******/ -/// Write-allocate miss: Send write request to lower level memory and send a read request for the same block -enum cache_request_status data_cache::wr_miss_wa(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status) { - +/// Write-allocate miss: Send write request to lower level memory +// and send a read request for the same block +enum cache_request_status +data_cache::wr_miss_wa( new_addr_type addr, + unsigned cache_index, mem_fetch *mf, + unsigned time, std::list<cache_event> &events, + enum cache_request_status status ) +{ new_addr_type block_addr = m_config.block_addr(addr); // 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)))) + 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; send_write_request(mf, WRITE_REQUEST_SENT, time, events); // Tries to send write allocate request, returns true on success and false on failure //if(!send_write_allocate(mf, addr, block_addr, cache_index, time, events)) - // return RESERVATION_FAIL; + // return RESERVATION_FAIL; const mem_access_t *ma = new mem_access_t( L2_WR_ALLOC_R, mf->get_addr(), @@ -697,11 +709,15 @@ enum cache_request_status data_cache::wr_miss_wa(new_addr_type addr, unsigned ca cache_block_t evicted; // Send read request resulting from write miss - send_read_request(addr, block_addr, cache_index, n_mf, time, do_miss, wb, evicted, events, false, true); + send_read_request(addr, block_addr, cache_index, n_mf, time, do_miss, wb, + evicted, events, false, true); if( do_miss ){ - if( wb && (m_config.m_write_policy != WRITE_THROUGH) ) { // If evicted block is modified and not a write-through (already modified lower level) - mem_fetch *wb = m_memfetch_creator->alloc(evicted.m_block_addr,L2_WRBK_ACC,m_config.get_line_sz(),true); + // If evicted block is modified and not a write-through + // (already modified lower level) + if( wb && (m_config.m_write_policy != WRITE_THROUGH) ) { + 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); } @@ -712,55 +728,93 @@ enum cache_request_status data_cache::wr_miss_wa(new_addr_type addr, unsigned ca } /// No write-allocate miss: Simply send write request to lower level memory -enum cache_request_status data_cache::wr_miss_no_wa(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status ){ - if(miss_queue_full(0)) - return RESERVATION_FAIL; // cannot handle request this cycle +enum cache_request_status +data_cache::wr_miss_no_wa( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ) +{ + if(miss_queue_full(0)) + return RESERVATION_FAIL; // cannot handle request this cycle - // on miss, generate write through (no write buffering -- too many threads for that) - send_write_request(mf, WRITE_REQUEST_SENT, time, events); + // on miss, generate write through (no write buffering -- too many threads for that) + send_write_request(mf, WRITE_REQUEST_SENT, time, events); - return MISS; + return MISS; } /****** Read hit functions (Set by config file) ******/ -/// Baseline read hit: Update LRU status of block. Special case for atomic instructions -> Mark block as modified -enum cache_request_status data_cache::rd_hit_base(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status ){ - new_addr_type block_addr = m_config.block_addr(addr); - m_tag_array->access(block_addr,time,cache_index); - if(mf->isatomic()){ // Atomics treated as global read/write requests - Perform read, mark line as MODIFIED - assert(mf->get_access_type() == GLOBAL_ACC_R); - cache_block_t &block = m_tag_array->get_block(cache_index); +/// Baseline read hit: Update LRU status of block. +// Special case for atomic instructions -> Mark block as modified +enum cache_request_status +data_cache::rd_hit_base( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ) +{ + new_addr_type block_addr = m_config.block_addr(addr); + m_tag_array->access(block_addr,time,cache_index); + // Atomics treated as global read/write requests - Perform read, mark line as + // MODIFIED + if(mf->isatomic()){ + assert(mf->get_access_type() == GLOBAL_ACC_R); + cache_block_t &block = m_tag_array->get_block(cache_index); block.m_status = MODIFIED; // mark line as dirty - } - return HIT; + } + return HIT; } /****** Read miss functions (Set by config file) ******/ -/// Baseline read miss: Send read request to lower level memory, perform write-back as necessary -enum cache_request_status data_cache::rd_miss_base(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status ){ +/// Baseline read miss: Send read request to lower level memory, +// perform write-back as necessary +enum cache_request_status +data_cache::rd_miss_base( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ){ if(miss_queue_full(1)) - return RESERVATION_FAIL; // cannot handle request this cycle (might need to generate two requests) + // cannot handle request this cycle + // (might need to generate two requests) + return RESERVATION_FAIL; new_addr_type block_addr = m_config.block_addr(addr); bool do_miss = false; bool wb = false; cache_block_t evicted; - send_read_request(addr, block_addr, cache_index, mf, time, do_miss, wb, evicted, events, false, false); + send_read_request( addr, + block_addr, + cache_index, + mf, time, do_miss, wb, evicted, events, false, false); if( do_miss ){ - if(wb && (m_config.m_write_policy != WRITE_THROUGH) ){ // If evicted block is modified and not a write-through (already modified lower level) - mem_fetch *wb = m_memfetch_creator->alloc(evicted.m_block_addr, L1_WRBK_ACC,m_config.get_line_sz(),true); - send_write_request(wb, WRITE_BACK_REQUEST_SENT, time, events); - } + // If evicted block is modified and not a write-through + // (already modified lower level) + if(wb && (m_config.m_write_policy != WRITE_THROUGH) ){ + mem_fetch *wb = m_memfetch_creator->alloc(evicted.m_block_addr, + L1_WRBK_ACC,m_config.get_line_sz(),true); + send_write_request(wb, WRITE_BACK_REQUEST_SENT, time, events); + } return MISS; } return RESERVATION_FAIL; } -/// Access cache for read_only_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<cache_event> &events ) { +/// Access cache for read_only_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<cache_event> &events ) +{ assert( mf->get_data_size() <= m_config.get_line_sz()); assert(m_config.m_write_policy == READ_ONLY); assert(!mf->get_is_write()); @@ -776,11 +830,11 @@ enum cache_request_status read_only_cache::access( new_addr_type addr, mem_fetch bool do_miss=false; send_read_request(addr, block_addr, cache_index, mf, time, do_miss, events, true, false); if(do_miss) - cache_status = MISS; + cache_status = MISS; else - cache_status = RESERVATION_FAIL; + cache_status = RESERVATION_FAIL; }else{ - cache_status = RESERVATION_FAIL; + cache_status = RESERVATION_FAIL; } } @@ -788,65 +842,95 @@ enum cache_request_status read_only_cache::access( new_addr_type addr, mem_fetch return cache_status; } -/// This is meant to model the first level data cache in Fermi. -/// It is write-evict (global) or write-back (local) at the granularity of individual blocks (Set by GPGPU-Sim configuration file) -/// (the policy used in fermi according to the CUDA manual) -enum cache_request_status l1_cache::access( new_addr_type addr, mem_fetch *mf, unsigned time, std::list<cache_event> &events ){ - - assert( mf->get_data_size() <= m_config.get_line_sz()); - bool wr = 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); - enum cache_request_status cache_status = RESERVATION_FAIL; - - // Each function pointer ( m_[rd/wr]_[hit/miss] ) is set in the data_cache constructor to reflect the corresponding cache configuration options. - // Function pointers were used to avoid many long conditional branches resulting from many cache configuration options. - if(wr){ // Write - if(status == HIT){ - cache_status = (this->*m_wr_hit)(addr, cache_index, mf, time, events, status); - }else if ( status != RESERVATION_FAIL ) { - cache_status = (this->*m_wr_miss)(addr, cache_index, mf, time, events, status); +//! A general function that takes the result of a tag_array probe +// and performs the correspding functions based on the cache configuration +// The access fucntion calls this function +enum cache_request_status +data_cache::process_tag_probe( bool wr, + enum cache_request_status probe_status, + new_addr_type addr, + unsigned cache_index, + mem_fetch* mf, + unsigned time, + std::list<cache_event>& events ) +{ + // Each function pointer ( m_[rd/wr]_[hit/miss] ) is set in the + // data_cache constructor to reflect the corresponding cache configuration + // options. Function pointers were used to avoid many long conditional + // branches resulting from many cache configuration options. + cache_request_status access_status = probe_status; + if(wr){ // Write + if(probe_status == HIT){ + access_status = (this->*m_wr_hit)( addr, + cache_index, + mf, time, events, probe_status ); + }else if ( probe_status != RESERVATION_FAIL ) { + access_status = (this->*m_wr_miss)( addr, + cache_index, + mf, time, events, probe_status ); } }else{ // Read - if(status == HIT){ - cache_status = (this->*m_rd_hit)(addr, cache_index, mf, time, events, status); - }else if ( status != RESERVATION_FAIL ) { - cache_status = (this->*m_rd_miss)(addr, cache_index, mf, time, events, status); + if(probe_status == HIT){ + access_status = (this->*m_rd_hit)( addr, + cache_index, + mf, time, events, probe_status ); + }else if ( probe_status != RESERVATION_FAIL ) { + access_status = (this->*m_rd_miss)( addr, + cache_index, + mf, time, events, probe_status ); } } - m_stats.inc_stats(mf->get_access_type(), m_stats.select_stats_status(status, cache_status)); - return cache_status; + return access_status; } -/// Models second level shared cache with global write-back and write-allocate policies -/// Currently the same as l1_cache, but separated to allow for different implementations -enum cache_request_status l2_cache::access( new_addr_type addr, mem_fetch *mf, unsigned time, std::list<cache_event> &events ){ +// Both the L1 and L2 currently use the same access function. +// Differentiation between the two caches is done through configuration +// of caching policies. +// Both the L1 and L2 override this function to provide a means of +// performing actions specific to each cache when such actions are implemnted. +enum cache_request_status +data_cache::access( new_addr_type addr, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events ) +{ assert( mf->get_data_size() <= m_config.get_line_sz()); bool wr = 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); - enum cache_request_status cache_status = RESERVATION_FAIL; + enum cache_request_status probe_status + = m_tag_array->probe( block_addr, cache_index ); + enum cache_request_status access_status + = process_tag_probe( wr, probe_status, addr, cache_index, mf, time, events ); + m_stats.inc_stats(mf->get_access_type(), + m_stats.select_stats_status(probe_status, access_status)); + return access_status; +} - // Each function pointer ( m_[rd/wr]_[hit/miss] ) is set in the data_cache constructor to reflect the corresponding cache configuration options. - // Function pointers were used to avoid many long conditional branches resulting from many cache configuration options. - if(wr){ // Write - if(status == HIT){ - cache_status = (this->*m_wr_hit)(addr, cache_index, mf, time, events, status); - }else if ( status != RESERVATION_FAIL ) { - cache_status = (this->*m_wr_miss)(addr, cache_index, mf, time, events, status); - } - }else{ // Read - if(status == HIT){ - cache_status = (this->*m_rd_hit)(addr, cache_index, mf, time, events, status); - }else if ( status != RESERVATION_FAIL ) { - cache_status = (this->*m_rd_miss)(addr, cache_index, mf, time, events, status); - } - } - m_stats.inc_stats(mf->get_access_type(), m_stats.select_stats_status(status, cache_status)); - return cache_status; +/// This is meant to model the first level data cache in Fermi. +/// It is write-evict (global) or write-back (local) at the +/// granularity of individual blocks (Set by GPGPU-Sim configuration file) +/// (the policy used in fermi according to the CUDA manual) +enum cache_request_status +l1_cache::access( new_addr_type addr, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events ) +{ + return data_cache::access( addr, mf, time, events ); +} + +// The l2 cache access function calls the base data_cache access +// implementation. When the L2 needs to diverge from L1, L2 specific +// changes should be made here. +enum cache_request_status +l2_cache::access( new_addr_type addr, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events ) +{ + return data_cache::access( addr, mf, time, events ); } /// Access function for tex_cache @@ -854,7 +938,9 @@ enum cache_request_status l2_cache::access( new_addr_type addr, mem_fetch *mf, u /// 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<cache_event> &events ) { +enum cache_request_status tex_cache::access( new_addr_type addr, mem_fetch *mf, + unsigned time, std::list<cache_event> &events ) +{ if ( m_fragment_fifo.full() || m_request_fifo.full() || m_rob.full() ) return RESERVATION_FAIL; @@ -915,7 +1001,8 @@ void tex_cache::cycle(){ } 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()) ); + 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(); } @@ -942,15 +1029,19 @@ void tex_cache::fill( mem_fetch *mf, unsigned time ) 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() ); + 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") ); + fprintf(fp, "tex rob[%3d] : %s ", + index, (r.m_ready?"ready ":"pending") ); if ( r.m_ready ) fprintf(fp,"@%6u", r.m_time ); else diff --git a/src/gpgpu-sim/gpu-cache.h b/src/gpgpu-sim/gpu-cache.h index 011b1f5..594125e 100644 --- a/src/gpgpu-sim/gpu-cache.h +++ b/src/gpgpu-sim/gpu-cache.h @@ -352,7 +352,6 @@ protected: 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 ) @@ -634,7 +633,7 @@ public: virtual ~data_cache() {} - void init( mem_fetch_allocator *mfcreator ) + virtual void init( mem_fetch_allocator *mfcreator ) { m_memfetch_creator=mfcreator; @@ -646,26 +645,39 @@ public: // Set write hit function switch(m_config.m_write_policy){ - case READ_ONLY: assert(0 && "Error: Writable Data_cache set as READ_ONLY\n"); break; // READ_ONLY is now a separate cache class, config is deprecated + // READ_ONLY is now a separate cache class, config is deprecated + case READ_ONLY: + assert(0 && "Error: Writable Data_cache set as READ_ONLY\n"); + break; case WRITE_BACK: m_wr_hit = &data_cache::wr_hit_wb; break; case WRITE_THROUGH: m_wr_hit = &data_cache::wr_hit_wt; break; case WRITE_EVICT: m_wr_hit = &data_cache::wr_hit_we; break; - case LOCAL_WB_GLOBAL_WT: m_wr_hit = &data_cache::wr_hit_global_we_local_wb; break; - default: assert(0 && "Error: Must set valid cache write policy\n"); break; // Need to set a write hit function + case LOCAL_WB_GLOBAL_WT: + m_wr_hit = &data_cache::wr_hit_global_we_local_wb; + break; + default: + assert(0 && "Error: Must set valid cache write policy\n"); + break; // Need to set a write hit function } // Set write miss function switch(m_config.m_write_alloc_policy){ case WRITE_ALLOCATE: m_wr_miss = &data_cache::wr_miss_wa; break; case NO_WRITE_ALLOCATE: m_wr_miss = &data_cache::wr_miss_no_wa; break; - default: assert(0 && "Error: Must set valid cache write miss policy\n"); break; // Need to set a write miss function + default: + assert(0 && "Error: Must set valid cache write miss policy\n"); + break; // Need to set a write miss function } } + virtual enum cache_request_status access( new_addr_type addr, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events ); protected: data_cache( const char *name, cache_config &config, - int core_id, + int core_id, int type_id, mem_fetch_interface *memport, mem_fetch_allocator *mfcreator, @@ -675,85 +687,185 @@ protected: { init( mfcreator ); } + //! A general function that takes the result of a tag_array probe + // and performs the correspding functions based on the cache configuration + // The access fucntion calls this function + enum cache_request_status + process_tag_probe( bool wr, + enum cache_request_status status, + new_addr_type addr, + unsigned cache_index, + mem_fetch* mf, + unsigned time, + std::list<cache_event>& events ); protected: mem_fetch_allocator *m_memfetch_creator; // Functions for data cache access /// Sends write request to lower level memory (write or writeback) - void send_write_request(mem_fetch *mf, cache_event request, unsigned time, std::list<cache_event> &events); - + void send_write_request( mem_fetch *mf, + cache_event request, + unsigned time, + std::list<cache_event> &events); - // Member Function pointers - Set by configuration options to the functions below each grouping + // Member Function pointers - Set by configuration options + // to the functions below each grouping /******* Write-hit configs *******/ - enum cache_request_status (data_cache::*m_wr_hit)(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); + enum cache_request_status + (data_cache::*m_wr_hit)( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); /// Marks block as MODIFIED and updates block LRU - enum cache_request_status wr_hit_wb(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); // write-back - enum cache_request_status wr_hit_wt(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); // write-through + enum cache_request_status + wr_hit_wb( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); // write-back + enum cache_request_status + wr_hit_wt( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); // write-through + /// Marks block as INVALID and sends write request to lower level memory - enum cache_request_status wr_hit_we(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); // write-evict - enum cache_request_status wr_hit_global_we_local_wb(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); // global write-evict, local write-back + enum cache_request_status + wr_hit_we( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); // write-evict + enum cache_request_status + wr_hit_global_we_local_wb( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); + // global write-evict, local write-back /******* Write-miss configs *******/ - enum cache_request_status (data_cache::*m_wr_miss)(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); - /// Sends read request, and possible write-back request, to lower level memory for a write miss with write-allocate - enum cache_request_status wr_miss_wa(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); // write-allocate - enum cache_request_status wr_miss_no_wa(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); // no write-allocate + enum cache_request_status + (data_cache::*m_wr_miss)( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); + /// Sends read request, and possible write-back request, + // to lower level memory for a write miss with write-allocate + enum cache_request_status + wr_miss_wa( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); // write-allocate + enum cache_request_status + wr_miss_no_wa( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); // no write-allocate // Currently no separate functions for reads /******* Read-hit configs *******/ - enum cache_request_status (data_cache::*m_rd_hit)(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); - enum cache_request_status rd_hit_base(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); + enum cache_request_status + (data_cache::*m_rd_hit)( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); + enum cache_request_status + rd_hit_base( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); /******* Read-miss configs *******/ - enum cache_request_status (data_cache::*m_rd_miss)(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); - enum cache_request_status rd_miss_base(new_addr_type addr, unsigned cache_index, mem_fetch *mf, unsigned time, std::list<cache_event> &events, enum cache_request_status status); + enum cache_request_status + (data_cache::*m_rd_miss)( new_addr_type addr, + unsigned cache_index, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); + enum cache_request_status + rd_miss_base( new_addr_type addr, + unsigned cache_index, + mem_fetch*mf, + unsigned time, + std::list<cache_event> &events, + enum cache_request_status status ); }; /// This is meant to model the first level data cache in Fermi. -/// It is write-evict (global) or write-back (local) at the granularity of individual blocks +/// It is write-evict (global) or write-back (local) at +/// the granularity of individual blocks /// (the policy used in fermi according to the CUDA manual) class l1_cache : public data_cache { public: - l1_cache(const char *name, cache_config &config, - int core_id, int type_id, mem_fetch_interface *memport, + l1_cache(const char *name, cache_config &config, + int core_id, int type_id, mem_fetch_interface *memport, mem_fetch_allocator *mfcreator, enum mem_fetch_status status ) - : data_cache(name,config,core_id,type_id,memport,mfcreator,status){} + : data_cache(name,config,core_id,type_id,memport,mfcreator,status){} virtual ~l1_cache(){} - virtual enum cache_request_status access( new_addr_type addr, mem_fetch *mf, unsigned time, std::list<cache_event> &events ); + virtual enum cache_request_status + access( new_addr_type addr, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events ); protected: - l1_cache( const char *name, + l1_cache( const char *name, cache_config &config, - int core_id, + int core_id, int type_id, mem_fetch_interface *memport, mem_fetch_allocator *mfcreator, enum mem_fetch_status status, tag_array* new_tag_array ) - : data_cache(name,config,core_id,type_id,memport,mfcreator,status, new_tag_array){} + : data_cache( name, + config, + core_id,type_id,memport,mfcreator,status, new_tag_array ){} }; -/// Models second level shared cache with global write-back and write-allocate policies +/// Models second level shared cache with global write-back +/// and write-allocate policies class l2_cache : public data_cache { public: - l2_cache(const char *name, cache_config &config, - int core_id, int type_id, mem_fetch_interface *memport, + l2_cache(const char *name, cache_config &config, + int core_id, int type_id, mem_fetch_interface *memport, mem_fetch_allocator *mfcreator, enum mem_fetch_status status ) - : data_cache(name,config,core_id,type_id,memport,mfcreator,status){} + : data_cache(name,config,core_id,type_id,memport,mfcreator,status){} virtual ~l2_cache() {} - virtual enum cache_request_status access( new_addr_type addr, mem_fetch *mf, unsigned time, std::list<cache_event> &events ); - + virtual enum cache_request_status + access( new_addr_type addr, + mem_fetch *mf, + unsigned time, + std::list<cache_event> &events ); }; -/********************************************************************************************************************************************************/ +/*****************************************************************************/ // See the following paper to understand this cache model: // diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index 767fb36..5bda78c 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -50,6 +50,7 @@ #define PRIORITIZE_MSHR_OVER_WB 1 #define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) ///////////////////////////////////////////////////////////////////////////// @@ -131,6 +132,8 @@ shader_core_ctx::shader_core_ctx( class gpgpu_sim *gpu, CONCRETE_SCHEDULER_TWO_LEVEL_ACTIVE : sched_config.find("gto") != std::string::npos ? CONCRETE_SCHEDULER_GTO : + sched_config.find("warp_limiting") != std::string::npos ? + CONCRETE_SCHEDULER_WARP_LIMITING: NUM_CONCRETE_SCHEDULERS; assert ( scheduler != NUM_CONCRETE_SCHEDULERS ); @@ -180,6 +183,21 @@ shader_core_ctx::shader_core_ctx( class gpgpu_sim *gpu, ) ); break; + case CONCRETE_SCHEDULER_WARP_LIMITING: + schedulers.push_back( + new swl_scheduler( m_stats, + this, + m_scoreboard, + m_simt_stack, + &m_warp, + &m_pipeline_reg[ID_OC_SP], + &m_pipeline_reg[ID_OC_SFU], + &m_pipeline_reg[ID_OC_MEM], + i, + config->gpgpu_scheduler_string + ) + ); + break; default: abort(); }; @@ -903,7 +921,13 @@ void scheduler_unit::do_on_warp_issued( unsigned warp_id, bool scheduler_unit::sort_warps_by_oldest_dynamic_id(shd_warp_t* lhs, shd_warp_t* rhs) { if (rhs && lhs) { - return lhs->get_dynamic_warp_id() < rhs->get_dynamic_warp_id(); + if ( lhs->done_exit() || lhs->waiting() ) { + return false; + } else if ( rhs->done_exit() || rhs->waiting() ) { + return true; + } else { + return lhs->get_dynamic_warp_id() < rhs->get_dynamic_warp_id(); + } } else { return lhs < rhs; } @@ -950,32 +974,32 @@ two_level_active_scheduler::do_on_warp_issued( unsigned warp_id, void two_level_active_scheduler::order_warps() { - //Move waiting warps to m_pending_warps + //Move waiting warps to m_pending_warps unsigned num_demoted = 0; - for ( std::vector< shd_warp_t* >::iterator iter = m_next_cycle_prioritized_warps.begin(); - iter != m_next_cycle_prioritized_warps.end(); ) { - bool waiting = (*iter)->waiting(); - for (int i=0; i<4; i++){ - const warp_inst_t* inst = (*iter)->ibuffer_next_inst(); - //Is the instruction waiting on a long operation? - if ( inst && inst->in[i] > 0 && this->m_scoreboard->islongop((*iter)->get_warp_id(), inst->in[i])){ - waiting = true; - } - } + for ( std::vector< shd_warp_t* >::iterator iter = m_next_cycle_prioritized_warps.begin(); + iter != m_next_cycle_prioritized_warps.end(); ) { + bool waiting = (*iter)->waiting(); + for (int i=0; i<4; i++){ + const warp_inst_t* inst = (*iter)->ibuffer_next_inst(); + //Is the instruction waiting on a long operation? + if ( inst && inst->in[i] > 0 && this->m_scoreboard->islongop((*iter)->get_warp_id(), inst->in[i])){ + waiting = true; + } + } - if( waiting ) { - m_pending_warps.push_back(*iter); - iter = m_next_cycle_prioritized_warps.erase(iter); + if( waiting ) { + m_pending_warps.push_back(*iter); + iter = m_next_cycle_prioritized_warps.erase(iter); SCHED_DPRINTF( "DEMOTED warp_id=%d, dynamic_warp_id=%d\n", (*iter)->get_warp_id(), (*iter)->get_dynamic_warp_id() ); ++num_demoted; - } else { + } else { ++iter; } - } + } - //If there is space in m_next_cycle_prioritized_warps, promote the next m_pending_warps + //If there is space in m_next_cycle_prioritized_warps, promote the next m_pending_warps unsigned num_promoted = 0; if ( SCHEDULER_PRIORITIZATION_SRR == m_outer_level_prioritization ) { while ( m_next_cycle_prioritized_warps.size() < m_max_active_warps ) { @@ -985,7 +1009,7 @@ void two_level_active_scheduler::order_warps() (m_next_cycle_prioritized_warps.back())->get_warp_id(), (m_next_cycle_prioritized_warps.back())->get_dynamic_warp_id() ); ++num_promoted; - } + } } else { fprintf( stderr, "Unimplemented m_outer_level_prioritization: %d\n", @@ -995,6 +1019,42 @@ void two_level_active_scheduler::order_warps() assert( num_promoted == num_demoted ); } +swl_scheduler::swl_scheduler ( shader_core_stats* stats, shader_core_ctx* shader, + Scoreboard* scoreboard, simt_stack** simt, + std::vector<shd_warp_t>* warp, + register_set* sp_out, + register_set* sfu_out, + register_set* mem_out, + int id, + char* config_string ) + : scheduler_unit ( stats, shader, scoreboard, simt, warp, sp_out, sfu_out, mem_out, id ) +{ + int ret = sscanf( config_string, + "warp_limiting:%d:%d", + (int*)&m_prioritization, + &m_num_warps_to_limit + ); + assert( 2 == ret ); + // Currently only GTO is implemented + assert( m_prioritization == SCHEDULER_PRIORITIZATION_GTO ); + assert( m_num_warps_to_limit <= shader->get_config()->max_warps_per_shader ); +} + +void swl_scheduler::order_warps() +{ + if ( SCHEDULER_PRIORITIZATION_GTO == m_prioritization ) { + order_by_priority( m_next_cycle_prioritized_warps, + m_supervised_warps, + m_last_supervised_issued, + MIN( m_num_warps_to_limit, m_supervised_warps.size() ), + ORDERING_GREEDY_THEN_PRIORITY_FUNC, + scheduler_unit::sort_warps_by_oldest_dynamic_id ); + } else { + fprintf(stderr, "swl_scheduler m_prioritization = %d\n", m_prioritization); + abort(); + } +} + void shader_core_ctx::read_operands() { } diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h index 76133e2..7c99ac5 100644 --- a/src/gpgpu-sim/shader.h +++ b/src/gpgpu-sim/shader.h @@ -279,6 +279,7 @@ enum concrete_scheduler CONCRETE_SCHEDULER_LRR = 0, CONCRETE_SCHEDULER_GTO, CONCRETE_SCHEDULER_TWO_LEVEL_ACTIVE, + CONCRETE_SCHEDULER_WARP_LIMITING, NUM_CONCRETE_SCHEDULERS }; @@ -452,6 +453,28 @@ private: unsigned m_max_active_warps; }; +// Static Warp Limiting Scheduler +class swl_scheduler : public scheduler_unit { +public: + swl_scheduler ( shader_core_stats* stats, shader_core_ctx* shader, + Scoreboard* scoreboard, simt_stack** simt, + std::vector<shd_warp_t>* warp, + register_set* sp_out, + register_set* sfu_out, + register_set* mem_out, + int id, + char* config_string ); + virtual ~swl_scheduler () {} + virtual void order_warps (); + virtual void done_adding_supervised_warps() { + m_last_supervised_issued = m_supervised_warps.begin(); + } + +protected: + scheduler_prioritization_type m_prioritization; + unsigned m_num_warps_to_limit; +}; + class opndcoll_rfu_t { // operand collector based register file unit |
