diff options
Diffstat (limited to 'src/gpgpu-sim/shader.cc')
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 4703 |
1 files changed, 2092 insertions, 2611 deletions
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index 5f465ed..1f2be5a 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -87,62 +87,8 @@ #define PRIORITIZE_MSHR_OVER_WB 1 #define MAX(a,b) (((a)>(b))?(a):(b)) -extern bool gpgpu_stall_on_use; -enum mem_stage_access_type { - C_MEM, - T_MEM, - S_MEM, - G_MEM_LD, - L_MEM_LD, - G_MEM_ST, - L_MEM_ST, - N_MEM_STAGE_ACCESS_TYPE -}; - -enum mem_stage_stall_type { - NO_RC_FAIL = 0, - BK_CONF, - MSHR_RC_FAIL, - ICNT_RC_FAIL, - COAL_STALL, - WB_ICNT_RC_FAIL, - WB_CACHE_RSRV_FAIL, - N_MEM_STAGE_STALL_TYPE -}; -unsigned int gpu_stall_shd_mem_breakdown[N_MEM_STAGE_ACCESS_TYPE][N_MEM_STAGE_STALL_TYPE] = { {0} }; -unsigned warp_size = 4; -int pipe_simd_width; -unsigned int *shader_cycle_distro; -unsigned int g_waiting_at_barrier = 0; -unsigned int gpgpu_shmem_size; -unsigned int gpgpu_shader_registers; -unsigned int gpgpu_shader_cta; -unsigned int gpgpu_n_load_insn = 0; -unsigned int gpgpu_n_store_insn = 0; -unsigned int gpgpu_n_shmem_insn = 0; -unsigned int gpgpu_n_tex_insn = 0; -unsigned int gpgpu_n_const_insn = 0; -unsigned int gpgpu_n_param_insn = 0; -unsigned int gpgpu_multi_unq_fetches = 0; -bool gpgpu_shmem_bkconflict; -unsigned int gpgpu_n_shmem_bkconflict = 0; -int gpgpu_n_shmem_bank = 16; -bool gpgpu_cache_bkconflict; -unsigned int gpgpu_n_cache_bkconflict = 0; -unsigned int gpgpu_n_cmem_portconflict = 0; -int gpgpu_n_cache_bank; -int gpgpu_warpdistro_shader; -int gpgpu_interwarp_mshr_merge; -int gpgpu_n_intrawarp_mshr_merge = 0; -int gpgpu_n_partial_writes = 0; -int gpgpu_shmem_port_per_bank; -int gpgpu_cache_port_per_bank; -int gpgpu_const_port_per_bank; -int gpgpu_shmem_pipe_speedup; -unsigned int gpu_max_cta_per_shader = 8; -unsigned int gpu_padded_cta_size = 32; -int gpgpu_local_mem_map; +unsigned mem_access_t::next_access_uid = 0; ///////////////////////////////////////////////////////////////////////////// /*-------------------------------------------------------------------------*/ @@ -150,312 +96,276 @@ int gpgpu_local_mem_map; static const char* MSHR_Status_str[] = { "INITIALIZED", + "INVALID", "IN_ICNT2MEM", - "IN_ICNTOL2QUEUE", + "IN_CBTOL2QUEUE", "IN_L2TODRAMQUEUE", "IN_DRAM_REQ_QUEUE", + "IN_DRAMRETURN_Q", "IN_DRAMTOL2QUEUE", - "IN_L2TOICNTQUEUE_HIT", - "IN_L2TOICNTQUEUE_MISS", + "IN_L2TOCBQUEUE_HIT", + "IN_L2TOCBQUEUE_MISS", "IN_ICNT2SHADER", "FETCHED", }; -// a helper function that deduce if a mshr contains an atomic operation -bool isatomic(mshr_entry_t *mshr) +void mshr_lookup::insert(mshr_entry* mshr) { - return (mshr->insts[0].callback.function != NULL); + using namespace std; + new_addr_type tag_addr = mshr->get_addr(); + m_lut.insert(make_pair(tag_addr, mshr)); } -#include <map> -#include <utility> -#include <algorithm> -// a class that speeds up mshr lookup with a C++ multimap -class mshr_lookup { -private: - typedef std::multimap<unsigned long long int, mshr_entry*> mshr_lut_t; - mshr_lut_t m_lut; // multiple mshr entries can have the same tag -private: - void insert(mshr_entry* mshr) - { - using namespace std; - unsigned long long int tag_addr = mshr->addr; - m_lut.insert(make_pair(tag_addr, mshr)); +mshr_entry* mshr_lookup::lookup( new_addr_type addr ) const +{ + std::pair<mshr_lut_t::const_iterator, mshr_lut_t::const_iterator> i_range = m_lut.equal_range(addr); + if (i_range.first == i_range.second) { + return NULL; + } else { + mshr_lut_t::const_iterator i_lut = i_range.first; + return i_lut->second->get_last_merged(); } +} - mshr_entry* lookup(unsigned long long int addr) const - { - using namespace std; - // mshr_lut_t::const_iterator i_lut = m_lut.find(tag_addr); - pair<mshr_lut_t::const_iterator, mshr_lut_t::const_iterator> i_range = m_lut.equal_range(addr); - if (i_range.first == i_range.second) { - return NULL; - } else { - mshr_lut_t::const_iterator i_lut = i_range.first; - mshr_entry* mshr_hit = i_lut->second; - //follow match to end of merge chain: - //this won't really work for different sized requests, ie can't merge a 64b request to a 32b - while (mshr_hit->merged_requests) { - mshr_hit = mshr_hit->merged_requests; - } - return mshr_hit; - } - } - - void remove(mshr_entry* mshr) - { - using namespace std; - std::pair<mshr_lut_t::iterator, mshr_lut_t::iterator> i_range = m_lut.equal_range(mshr->addr); +void mshr_lookup::remove(mshr_entry* mshr) +{ + using namespace std; + std::pair<mshr_lut_t::iterator, mshr_lut_t::iterator> i_range = m_lut.equal_range(mshr->get_addr()); - assert(i_range.first != i_range.second); + assert(i_range.first != i_range.second); - for (mshr_lut_t::iterator i_lut = i_range.first; i_lut != i_range.second; ++i_lut) { - if (i_lut->second == mshr) { - m_lut.erase(i_lut); - break; - } - } - } -public: - //checks if we should do mshr merging for this mshr - bool can_merge(mshr_entry_t * mshr) - { - if (mshr->iswrite) return false; // can't merge a write - if (isatomic(mshr)) return false; // can't merge a atomic operation - bool interwarp_mshr_merge = gpgpu_interwarp_mshr_merge & GLOBAL_MSHR_MERGE; - if (mshr->istexture) { - interwarp_mshr_merge = gpgpu_interwarp_mshr_merge & TEX_MSHR_MERGE; - } else if (mshr->isconst) { - interwarp_mshr_merge = gpgpu_interwarp_mshr_merge & CONST_MSHR_MERGE; + for (mshr_lut_t::iterator i_lut = i_range.first; i_lut != i_range.second; ++i_lut) { + if (i_lut->second == mshr) { + m_lut.erase(i_lut); + break; } - return interwarp_mshr_merge; } +} - void mshr_fast_lookup_insert(mshr_entry* mshr) - { - if (!can_merge(mshr)) return; - insert(mshr); - } +//checks if we should do mshr merging for this mshr +bool mshr_lookup::can_merge(mshr_entry * mshr) +{ + if (mshr->iswrite()) + return false; // can't merge a write + if (mshr->isatomic()) + return false; // can't merge a atomic operation + bool interwarp_mshr_merge = m_shader_config->gpgpu_interwarp_mshr_merge & GLOBAL_MSHR_MERGE; + if (mshr->isinst()) + interwarp_mshr_merge=true; + else if (mshr->istexture()) + interwarp_mshr_merge = m_shader_config->gpgpu_interwarp_mshr_merge & TEX_MSHR_MERGE; + else if (mshr->isconst()) + interwarp_mshr_merge = m_shader_config->gpgpu_interwarp_mshr_merge & CONST_MSHR_MERGE; + return interwarp_mshr_merge; +} + +void mshr_lookup::mshr_fast_lookup_insert(mshr_entry* mshr) +{ + if (!can_merge(mshr)) + return; + insert(mshr); +} - void mshr_fast_lookup_remove(mshr_entry* mshr) - { - if (!can_merge(mshr)) return; - remove(mshr); - } +void mshr_lookup::mshr_fast_lookup_remove(mshr_entry* mshr) +{ + if (!can_merge(mshr)) + return; + remove(mshr); +} - mshr_entry* shader_get_mergeable_mshr(mshr_entry_t* mshr) - { - if (!can_merge(mshr)) return NULL; - return lookup(mshr->addr); - } -}; +mshr_entry* mshr_lookup::shader_get_mergeable_mshr(mshr_entry* mshr) +{ + if (!can_merge(mshr)) return NULL; + return lookup(mshr->get_addr()); +} -class mem_access_t; -int is_tex ( int space ); -int is_const ( int space ); -int is_local ( int space ); -#include <iostream> -class mshr_shader_unit{ -public: - mshr_shader_unit(unsigned max_mshr): m_max_mshr(max_mshr), m_max_mshr_used(0){ - m_mshrs.resize(max_mshr); - for (std::vector<mshr_entry_t>::iterator i = m_mshrs.begin(); i != m_mshrs.end(); i++) m_free_list.push_back(i); - } - bool has_mshr(unsigned num){return (num <= m_free_list.size());} - unsigned mshr_used(){ return m_max_mshr - m_free_list.size();} - mshr_entry_t* add_mshr(mem_access_t &access, inst_t* warp); +mshr_shader_unit::mshr_shader_unit( const shader_core_config *config ): m_max_mshr_used(0), m_mshr_lookup(config) +{ + m_shader_config=config; + m_mshrs.resize(config->n_mshr_per_shader); + unsigned n=0; + for (std::vector<mshr_entry>::iterator i = m_mshrs.begin(); i != m_mshrs.end(); i++) { + mshr_entry &mshr = *i; + mshr.set_id(n++); + m_free_list.push_back(&mshr); + } +} - //return queue access; (includes texture pipeline return) - mshr_entry_t* return_head(){ - if (has_return()) - return &(*(choose_return_queue().front())); - else - return NULL; - } - //return queue pop; (includes texture pipeline return) - void pop_return_head() { - free_mshr(return_head()->this_mshr); - choose_return_queue().pop_front(); - } +mshr_entry* mshr_shader_unit::return_head() +{ + if (has_return()) + return &(*choose_return_queue().front()); + else + return NULL; +} - static void mshr_update_status(mshr_entry *mshr, enum mshr_status new_status ); - void mshr_return_from_mem(mshr_entry *mshr); - void check_mshr(mshr_entry *mshr){ - assert(find(m_free_list.begin(),m_free_list.end(),mshr->this_mshr)==m_free_list.end()); - assert(mshr->insts.size()); - } - unsigned get_max_mshr_used(){return m_max_mshr_used;} - void print(FILE* fp, shader_core_ctx_t* shader); -private: - typedef std::vector<mshr_entry_t> mshr_storage_type;//list might be less complicated, but slower? - mshr_storage_type m_mshrs; - std::deque< mshr_storage_type::iterator > m_free_list; - std::deque< mshr_storage_type::iterator > m_mshr_return_queue; - std::deque< mshr_storage_type::iterator > m_texture_mshr_pipeline; - unsigned m_max_mshr; - unsigned m_max_mshr_used; - mshr_lookup m_mshr_lookup; +void mshr_shader_unit::pop_return_head() +{ + free_mshr(return_head()); + choose_return_queue().pop_front(); +} - mshr_entry_t *alloc_free_mshr(bool istexture){ - assert(!m_free_list.empty()); - std::vector<mshr_entry_t>::iterator i = m_free_list.back(); - m_free_list.pop_back(); - i->this_mshr = i; - if (istexture) { - //put in texture pipeline - m_texture_mshr_pipeline.push_back(i); - } - if (mshr_used() > m_max_mshr_used) m_max_mshr_used = mshr_used(); - return &(*i); - } - void free_mshr(std::vector<mshr_entry_t>::iterator &i){ - //clean up up for next time, since not reallocating memory. - m_mshr_lookup.mshr_fast_lookup_remove(&(*i)); //need to remove before clearing insts, as they are accessed - i->insts.clear(); //add expects this to be clear - m_free_list.push_back(i); - } - bool has_return() { return (not m_mshr_return_queue.empty()) or ((not m_texture_mshr_pipeline.empty()) and m_texture_mshr_pipeline.front()->fetched());} - std::deque< std::vector<mshr_entry_t>::iterator > & choose_return_queue() { - //prioritize a ready texture over a global/const... - if ((not m_texture_mshr_pipeline.empty()) and m_texture_mshr_pipeline.front()->fetched()) return m_texture_mshr_pipeline; - assert(!m_mshr_return_queue.empty()); - return m_mshr_return_queue; - } -}; +mshr_entry *mshr_shader_unit::alloc_free_mshr(bool istexture) +{ + assert(!m_free_list.empty()); + mshr_entry *mshr = m_free_list.back(); + m_free_list.pop_back(); + if (istexture) + m_texture_mshr_pipeline.push_back(mshr); + if (mshr_used() > m_max_mshr_used) + m_max_mshr_used = mshr_used(); + return mshr; +} +void mshr_shader_unit::free_mshr( mshr_entry *mshr ) +{ + //clean up up for next time, since not reallocating memory. + m_mshr_lookup.mshr_fast_lookup_remove(mshr); + mshr->clear(); + m_free_list.push_back(mshr); +} +unsigned mshr_shader_unit::mshr_used() const +{ + return m_shader_config->n_mshr_per_shader - m_free_list.size(); +} -void mshr_shader_unit::mshr_update_status(mshr_entry *mshr, enum mshr_status new_status ) { - mshr->status = new_status; -#if DEBUGL1MISS - printf("cycle %d Addr %x %d \n",gpu_sim_cycle,CACHE_TAG_OF_64(mshr->addr),new_status); -#endif - mshr_entry * merged_req = mshr->merged_requests; - while (merged_req) { - merged_req->status = new_status; - merged_req = merged_req->merged_requests; - } +std::deque<mshr_entry*> &mshr_shader_unit::choose_return_queue() +{ + // prioritize a ready texture over a global/const... + if ((not m_texture_mshr_pipeline.empty()) and m_texture_mshr_pipeline.front()->fetched()) + return m_texture_mshr_pipeline; + assert(!m_mshr_return_queue.empty()); + return m_mshr_return_queue; } -inline void mshr_shader_unit::mshr_return_from_mem(mshr_entry *mshr){ - mshr_update_status(mshr, FETCHED); - if (not mshr->istexture) { +void mshr_shader_unit::mshr_return_from_mem(mshr_entry *mshr) +{ + mshr->set_status( FETCHED ); + if ( not mshr->istexture() ) { //place in return queue - m_mshr_return_queue.push_back(mshr->this_mshr); - //place all merged requests in return queue - mshr_entry * merged_req = mshr->merged_requests; - while (merged_req) { - m_mshr_return_queue.push_back(merged_req->this_mshr); - merged_req = merged_req->merged_requests; - } + mshr->add_to_queue( m_mshr_return_queue ); } } -void mshr_return_from_mem(shader_core_ctx_t * shader, mshr_entry_t* mshr){ - shader->mshr_unit->mshr_return_from_mem(mshr); +void shader_core_ctx::mshr_print(FILE* fp, unsigned mask) +{ + m_mshr_unit->print(fp, this, mask); } -unsigned get_max_mshr_used(shader_core_ctx_t * shader){ - return shader->mshr_unit->get_max_mshr_used(); +void mshr_shader_unit::print(FILE* fp, shader_core_ctx* shader, unsigned mask) +{ + unsigned n=0; + unsigned num_outstanding = 0; + for (mshr_storage_type::iterator it = m_mshrs.begin(); it != m_mshrs.end(); it++,n++) { + mshr_entry *mshr = &(*it); + if (find(m_free_list.begin(),m_free_list.end(), mshr) == m_free_list.end()) { + num_outstanding++; + mshr->print(fp,mask); + } + } + fprintf(fp,"\nTotal outstanding memory requests = %u\n", num_outstanding ); } +unsigned char shader_core_ctx::fq_push(unsigned long long int addr, + int bsize, + unsigned char write, + partial_write_mask_t partial_write_mask, + int wid, + mshr_entry* mshr, + int cache_hits_waiting, + enum mem_access_type mem_acc, + address_type pc) +{ + assert(write || (partial_write_mask == NO_PARTIAL_WRITE)); + int nbytes_L2 = m_gpu->get_L2_linesize(); + mem_fetch *mf = new mem_fetch(addr, + bsize, + nbytes_L2, + m_sid, + m_tpc, + wid, + cache_hits_waiting, + mshr, + write, + partial_write_mask, + mem_acc, + write?WT_REQ:RD_REQ, + pc); + if (mshr) mshr->set_mf(mf); + + // stats + if (write) made_write_mfs++; + else made_read_mfs++; + switch (mem_acc) { + case CONST_ACC_R: m_stats->gpgpu_n_mem_const++; break; + case TEXTURE_ACC_R: m_stats->gpgpu_n_mem_texture++; break; + case GLOBAL_ACC_R: m_stats->gpgpu_n_mem_read_global++; break; + case GLOBAL_ACC_W: m_stats->gpgpu_n_mem_write_global++; break; + case LOCAL_ACC_R: m_stats->gpgpu_n_mem_read_local++; break; + case LOCAL_ACC_W: m_stats->gpgpu_n_mem_write_local++; break; + case INST_ACC_R: m_stats->gpgpu_n_mem_read_inst++; break; + default: assert(0); + } -void mshr_print(FILE* fp, shader_core_ctx_t *shader) { - shader->mshr_unit->print(fp, shader); + return(m_gpu->issue_mf_from_fq(mf)); } -void mshr_shader_unit::print(FILE* fp, shader_core_ctx_t* shader){ - for (mshr_storage_type::iterator it = m_mshrs.begin(); it != m_mshrs.end(); it++) { - //valid if not in free list; - if (find(m_free_list.begin(),m_free_list.end(), it) == m_free_list.end()) { - mshr_entry *mshr = &(*it); - fprintf(fp, "MSHR(%d): %s Addr:0x%llx Fetched:%d Merged:%d Status:%s\n", - shader->sid, - (mshr->iswrite)? "=>" : "<=", - mshr->addr, mshr->fetched(), - (mshr->merged_requests != NULL or mshr->merged_on_other_reqest), MSHR_Status_str[mshr->status]); - for (unsigned i = 0; i < mshr->insts.size(); i++) { - fprintf(fp,"\tthread: UID:%d HW:%d ReqAddr:0x%llx\n", mshr->insts[i].uid, mshr->insts[i].hw_thread_id, mshr->insts[i].memreqaddr); - } - } - } +inst_t *shader_core_ctx::first_valid_thread( inst_t *warp ) +{ + for(unsigned t=0; t < m_config->warp_size; t++ ) + if( warp[t].hw_thread_id != -1 ) + return warp+t; + return NULL; } -void mshr_update_status(mshr_entry* mshr, enum mshr_status new_status) { - mshr_entry *merged_req; - mshr->status = new_status; -#if DEBUGL1MISS - printf("cycle %d Addr %x %d \n",gpu_sim_cycle,CACHE_TAG_OF_64(mshr->addr),new_status); -#endif - merged_req = mshr->merged_requests; - while (merged_req) { - merged_req->status = new_status; - merged_req = merged_req->merged_requests; - } +inst_t *shader_core_ctx::first_valid_thread( unsigned stage ) +{ + return first_valid_thread(m_pipeline_reg[stage]); } -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -/*-------------------------------------------------------------------------*/ +void shader_core_ctx::move_warp( inst_t *&dst, inst_t *&src ) +{ -inst_t create_nop_inst() // just because C++ does not have designated initializer list.... + assert( pipeline_regster_empty(dst) ); + inst_t* temp = dst; + dst = src; + src = temp; + for( unsigned t=0; t < m_config->warp_size; t++) + src[t] = inst_t(); +} + +void shader_core_ctx::clear_stage( inst_t *warp ) { - inst_t nop_inst; - nop_inst.pc = 0; - nop_inst.op=NO_OP; - memset(nop_inst.out, 0, sizeof(nop_inst.out)); - memset(nop_inst.in, 0, sizeof(nop_inst.in)); - nop_inst.is_vectorin=0; - nop_inst.is_vectorout=0; - nop_inst.memreqaddr=0; - nop_inst.reg_bank_access_pending=0; - nop_inst.reg_bank_conflict_stall_checked=0, - nop_inst.hw_thread_id=-1; - nop_inst.wlane=-1; - nop_inst.uid = (unsigned)-1; - nop_inst.priority = (unsigned)-1; - nop_inst.ptx_thd_info = NULL; - nop_inst.warp_active_mask = 0; - nop_inst.ts_cycle = 0; - nop_inst.id_cycle = 0; - nop_inst.ex_cycle = 0; - nop_inst.mm_cycle = 0; - nop_inst.cache_miss = false; - nop_inst.space = memory_space_t(); - nop_inst.cycles = 0; - return nop_inst; + for( unsigned t=0; t < m_config->warp_size; t++) + warp[t] = inst_t(); } -static inst_t nop_inst = create_nop_inst(); +bool shader_core_ctx::pipeline_regster_empty( inst_t *reg ) +{ + return first_valid_thread(reg) == NULL; +} -inst_t *first_valid_thread( inst_t *warp ) +void shader_core_ctx::L1cache_print( FILE *fp, unsigned &total_accesses, unsigned &total_misses) const { - for(unsigned t=0; t < ::warp_size; t++ ) - if( warp[t].hw_thread_id != -1 ) - return warp+t; - return NULL; + shd_cache_print(m_L1D,fp,total_accesses,total_misses); } -void move_warp( inst_t *dst, inst_t *src ) +void shader_core_ctx::L1texcache_print( FILE *fp, unsigned &total_accesses, unsigned &total_misses) const { - memcpy(dst,src,::warp_size * sizeof(inst_t)); - for( unsigned t=0; t < ::warp_size; t++) - src[t] = nop_inst; + shd_cache_print(m_L1T,fp,total_accesses,total_misses); } -bool pipeline_regster_empty( inst_t *reg ) +void shader_core_ctx::L1constcache_print( FILE *fp, unsigned &total_accesses, unsigned &total_misses) const { - return first_valid_thread(reg) == NULL; + shd_cache_print(m_L1C,fp,total_accesses,total_misses); } -std::list<unsigned> get_regs_written( inst_t *warp ) +std::list<unsigned> shader_core_ctx::get_regs_written( const inst_t &fvt ) const { std::list<unsigned> result; - inst_t *fvi = first_valid_thread(warp); - if( fvi == NULL ) - return result; for( unsigned op=0; op < 4; op++ ) { - int reg_num = fvi->arch_reg[op]; // this math needs to match that used in function_info::ptx_decode_inst + int reg_num = fvt.arch_reg[op]; // this math needs to match that used in function_info::ptx_decode_inst if( reg_num >= 0 ) // valid register result.push_back(reg_num); } @@ -471,694 +381,396 @@ int log2i(int n) { return lg; } -shader_core_ctx_t* shader_create( const char *name, int sid, - unsigned int n_threads, - unsigned int n_mshr, - fq_push_t fq_push, - fq_has_buffer_t fq_has_buffer, - unsigned int model ) -{ - shader_core_ctx_t *sc; - sc = (shader_core_ctx_t*)calloc(sizeof(shader_core_ctx_t),1); - sc = new (sc) shader_core_ctx(name,sid,n_threads,n_mshr,fq_push,fq_has_buffer,model, - gpu_n_warp_per_shader,gpgpu_shader_cta); - return sc; -} - -shader_core_ctx::shader_core_ctx( const char *name, int sid, - unsigned int n_threads, - unsigned int n_mshr, - fq_push_t fq_push, - fq_has_buffer_t fq_has_buffer, - unsigned model, - unsigned max_warps_per_cta, unsigned max_cta_per_core ) - : m_barriers( max_warps_per_cta, max_cta_per_core ) +shader_core_ctx::shader_core_ctx( class gpgpu_sim *gpu, + const char *name, + unsigned shader_id, + unsigned tpc_id, + const struct shader_core_config *config, + struct shader_core_stats *stats ) + : m_barriers( config->max_warps_per_shader, config->max_cta_per_core ) { - shader_core_ctx *sc = this; - assert( !((model == DWF) && gpgpu_operand_collector) ); - - int i; - unsigned int shd_n_set; - unsigned int shd_linesize; - unsigned int shd_n_assoc; - unsigned char shd_policy; - - unsigned int l1tex_cache_n_set; //L1 texture cache parameters - unsigned int l1tex_cache_linesize; - unsigned int l1tex_cache_n_assoc; - unsigned char l1tex_cache_policy; + m_gpu = gpu; + m_config = config; + m_stats = stats; + unsigned warp_size=config->warp_size; + assert( !((config->model == DWF) && m_config->gpgpu_operand_collector) ); - unsigned int l1const_cache_n_set; //L1 constant cache parameters - unsigned int l1const_cache_linesize; - unsigned int l1const_cache_n_assoc; - unsigned char l1const_cache_policy; - - if ( gpgpu_cuda_sim ) { - unsigned cta_size = ptx_sim_cta_size(); - if ( cta_size > n_threads ) { - printf("Execution error: Shader kernel CTA (block) size is too large for microarch config.\n"); - printf(" This can cause problems with applications that use __syncthreads.\n"); - printf(" CTA size (x*y*z) = %u, n_threads = %u\n", cta_size, n_threads ); - printf(" => either change -gpgpu_shader argument in gpgpusim.config file or\n"); - printf(" modify the CUDA source to decrease the kernel block size.\n"); - abort(); - } - } - - sc->name = name; - sc->sid = sid; - - sc->RR_k = 0; - - sc->model = model; - - sc->pipeline_reg = (inst_t**) calloc(N_PIPELINE_STAGES, sizeof(inst_t*)); + m_name = name; + m_sid = shader_id; + m_tpc = tpc_id; + m_dwf_RR_k = 0; + m_pipeline_reg = (inst_t**) calloc(N_PIPELINE_STAGES, sizeof(inst_t*)); for (int j = 0; j<N_PIPELINE_STAGES; j++) { - sc->pipeline_reg[j] = (inst_t*) calloc(warp_size, sizeof(inst_t)); - for (unsigned i=0; i<warp_size; i++) { - sc->pipeline_reg[j][i] = nop_inst; - } + m_pipeline_reg[j] = (inst_t*) calloc(warp_size, sizeof(inst_t)); + for (unsigned i=0; i<warp_size; i++) + m_pipeline_reg[j][i] = inst_t(); } - if (gpgpu_pre_mem_stages) { - sc->pre_mem_pipeline = (inst_t**) calloc(gpgpu_pre_mem_stages+1, sizeof(inst_t*)); - for (unsigned j = 0; j<=gpgpu_pre_mem_stages; j++) { - sc->pre_mem_pipeline[j] = (inst_t*) calloc(pipe_simd_width, sizeof(inst_t)); - for (int i=0; i<pipe_simd_width; i++) { - sc->pre_mem_pipeline[j][i] = nop_inst; + if (m_config->gpgpu_pre_mem_stages) { + pre_mem_pipeline = (inst_t**) calloc(m_config->gpgpu_pre_mem_stages+1, sizeof(inst_t*)); + for (unsigned j = 0; j<=m_config->gpgpu_pre_mem_stages; j++) { + pre_mem_pipeline[j] = (inst_t*) calloc(warp_size, sizeof(inst_t)); + for (unsigned i=0; i<warp_size; i++) { + pre_mem_pipeline[j][i] = inst_t(); } } } - sc->n_threads = n_threads; - sc->thread = (thread_ctx_t*) calloc(sizeof(thread_ctx_t), n_threads); - sc->not_completed = 0; + m_thread = (thread_ctx_t*) calloc(sizeof(thread_ctx_t), config->n_thread_per_shader); + m_not_completed = 0; - unsigned n_warp = (n_threads/warp_size) + ((n_threads%warp_size)?1:0); - sc->warp.resize(n_warp, shd_warp_t(warp_size)); - for (unsigned j = 0; j < n_warp; j++) { - sc->warp[j].wid = j; - } + m_warp.resize(m_config->max_warps_per_shader, shd_warp_t(this, warp_size)); - sc->n_active_cta = 0; - for (i = 0; i<MAX_CTA_PER_SHADER; i++ ) { - sc->cta_status[i]=0; + m_n_active_cta = 0; + for (unsigned i = 0; i<MAX_CTA_PER_SHADER; i++ ) + m_cta_status[i]=0; + m_next_warp = 0; + for (unsigned i = 0; i<config->n_thread_per_shader; i++) { + m_thread[i].m_functional_model_thread_state = NULL; + m_thread[i].m_avail4fetch = false; + m_thread[i].m_waiting_at_barrier = false; + m_thread[i].m_cta_id = -1; } - //Warp variable initializations - sc->next_warp = 0; - sc->branch_priority = 0; - sc->max_branch_priority = (int*) malloc(sizeof(int)*n_threads); + #define STRSIZE 1024 + char L1D_name[STRSIZE]; + char L1T_name[STRSIZE]; + char L1C_name[STRSIZE]; + char L1I_name[STRSIZE]; - for (unsigned i = 0; i<n_threads; i++) { - sc->max_branch_priority[i] = INT_MAX; - sc->thread[i].id = i; + snprintf(L1D_name, STRSIZE, "L1D_%03d", m_sid); + m_L1D = shd_cache_create(L1D_name,m_config->gpgpu_cache_dl1_opt,1,0,m_config->gpgpu_cache_wt_through?write_through:write_back); + shd_cache_bind_logger(m_L1D, m_sid, get_shader_normal_cache_id()); - sc->thread[i].warp_priority = sc->max_branch_priority[i]; - sc->thread[i].avail4fetch = 0; - sc->thread[i].m_waiting_at_barrier = 0; + snprintf(L1T_name, STRSIZE, "L1T_%03d", m_sid); + m_L1T = shd_cache_create(L1T_name,m_config->gpgpu_cache_texl1_opt,1,0, no_writes ); + shd_cache_bind_logger(m_L1T, m_sid, get_shader_texture_cache_id()); + ptx_set_tex_cache_linesize(m_L1T->line_sz); - sc->thread[i].ptx_thd_info = NULL; - sc->thread[i].cta_id = -1; - } - - sscanf(gpgpu_cache_dl1_opt,"%d:%d:%d:%c", - &shd_n_set, &shd_linesize, &shd_n_assoc, &shd_policy); - sscanf(gpgpu_cache_texl1_opt,"%d:%d:%d:%c", - &l1tex_cache_n_set, &l1tex_cache_linesize, &l1tex_cache_n_assoc, &l1tex_cache_policy); - sscanf(gpgpu_cache_constl1_opt,"%d:%d:%d:%c", - &l1const_cache_n_set, &l1const_cache_linesize, &l1const_cache_n_assoc, &l1const_cache_policy); -#define STRSIZE 32 - char L1c_name[STRSIZE]; - char L1texc_name[STRSIZE]; - char L1constc_name[STRSIZE]; - snprintf(L1c_name, STRSIZE, "L1c_%03d", sc->sid); - sc->L1cache = shd_cache_create(L1c_name,shd_n_set,shd_n_assoc,shd_linesize,shd_policy,1,0, - gpgpu_cache_wt_through?write_through:write_back); - shd_cache_bind_logger(sc->L1cache, sc->sid, get_shader_normal_cache_id()); - snprintf(L1texc_name, STRSIZE, "L1texc_%03d", sc->sid); - sc->L1texcache = shd_cache_create(L1texc_name,l1tex_cache_n_set,l1tex_cache_n_assoc,l1tex_cache_linesize,l1tex_cache_policy,1,0, no_writes ); - shd_cache_bind_logger(sc->L1texcache, sc->sid, get_shader_texture_cache_id()); - snprintf(L1constc_name, STRSIZE, "L1constc_%03d", sc->sid); - sc->L1constcache = shd_cache_create(L1constc_name,l1const_cache_n_set,l1const_cache_n_assoc,l1const_cache_linesize,l1const_cache_policy,1,0, no_writes ); - shd_cache_bind_logger(sc->L1constcache, sc->sid, get_shader_constant_cache_id()); - //at this point, should set the parameters used by addressing schemes of all textures - ptx_set_tex_cache_linesize(l1tex_cache_linesize); - - sc->mshr_unit = new mshr_shader_unit(gpu_n_mshr_per_shader); + snprintf(L1C_name, STRSIZE, "L1C_%03d", m_sid); + m_L1C = shd_cache_create(L1C_name,m_config->gpgpu_cache_constl1_opt,1,0, no_writes ); + shd_cache_bind_logger(m_L1C, m_sid, get_shader_constant_cache_id()); - sc->fq_push = fq_push; - sc->fq_has_buffer = fq_has_buffer; - - sc->pdom_warp = (pdom_warp_ctx_t*)calloc(n_threads / warp_size, sizeof(pdom_warp_ctx_t)); - for (unsigned i = 0; i < n_threads / warp_size; ++i) { - sc->pdom_warp[i].m_stack_top = 0; - sc->pdom_warp[i].m_pc = (address_type*)calloc(warp_size * 2, sizeof(address_type)); - sc->pdom_warp[i].m_calldepth = (unsigned int*)calloc(warp_size * 2, sizeof(unsigned int)); - sc->pdom_warp[i].m_active_mask = (unsigned int*)calloc(warp_size * 2, sizeof(unsigned int)); - sc->pdom_warp[i].m_recvg_pc = (address_type*)calloc(warp_size * 2, sizeof(address_type)); - sc->pdom_warp[i].m_branch_div_cycle = (unsigned long long *)calloc(warp_size * 2, sizeof(unsigned long long )); - - memset(sc->pdom_warp[i].m_pc, -1, warp_size * 2 * sizeof(address_type)); - memset(sc->pdom_warp[i].m_calldepth, 0, warp_size * 2 * sizeof(unsigned int)); - memset(sc->pdom_warp[i].m_active_mask, 0, warp_size * 2 * sizeof(unsigned int)); - memset(sc->pdom_warp[i].m_recvg_pc, -1, warp_size * 2 * sizeof(address_type)); - } + snprintf(L1I_name, STRSIZE, "L1I_%03d", m_sid); + m_L1I = shd_cache_create(L1I_name,m_config->gpgpu_cache_il1_opt,1,0, no_writes ); + shd_cache_bind_logger(m_L1D, m_sid, get_shader_instruction_cache_id()); - sc->waiting_at_barrier = 0; + m_mshr_unit = new mshr_shader_unit(m_config); + m_pdom_warp = new pdom_warp_ctx_t*[config->max_warps_per_shader]; + for (unsigned i = 0; i < config->max_warps_per_shader; ++i) + m_pdom_warp[i] = new pdom_warp_ctx_t(i,this); + if (m_config->using_commit_queue) + m_thd_commit_queue = new fifo_pipeline<std::vector<int> >("thd_commit_queue", 0, 0,gpu_sim_cycle); + m_shader_memory_new_instruction_processed = false; - sc->last_issued_thread = sc->n_threads - 1; - - sc->using_dwf = (sc->model == DWF); - - sc->using_rrstage = (sc->model == DWF); - - sc->using_commit_queue = (sc->model == DWF - || sc->model == POST_DOMINATOR || sc->model == NO_RECONVERGE); + // Initialize scoreboard + m_scoreboard = new Scoreboard(m_sid, m_config->max_warps_per_shader); - if (sc->using_commit_queue) { - sc->thd_commit_queue = dq_create("thd_commit_queue", 0, 0, 0); + if( m_config->gpgpu_operand_collector ) { + m_operand_collector.init( m_config->gpgpu_operand_collector_num_units, + m_config->gpgpu_operand_collector_num_units_sfu, + m_config->gpgpu_num_reg_banks, this, + &m_pipeline_reg[ID_EX], + &m_pipeline_reg[OC_EX_SFU] ); } - sc->shmem_size = gpgpu_shmem_size; - sc->n_registers = gpgpu_shader_registers; - sc->n_cta = gpgpu_shader_cta; + m_memory_queue.shared.reserve(warp_size); + m_memory_queue.constant.reserve(warp_size); + m_memory_queue.texture.reserve(warp_size); + m_memory_queue.local_global.reserve(warp_size); - sc->shader_memory_new_instruction_processed = false; + // writeback + m_pl_tid = (int*) malloc(sizeof(int)*warp_size); + m_mshr_lat_info = (insn_latency_info*) malloc(sizeof(insn_latency_info) * warp_size); + m_pl_lat_info = (insn_latency_info*) malloc(sizeof(insn_latency_info) * warp_size); - // Initialize scoreboard - sc->scrb = new Scoreboard(sc->sid, n_warp); + // fetch + m_last_warp_fetched = 0; + m_last_warp_issued = 0; + m_ready_warps = (int*)calloc(m_config->max_warps_per_shader,sizeof(int)); + m_tmp_ready_warps = (int*)calloc(m_config->max_warps_per_shader,sizeof(int)); + m_last_warp=0; + m_last_issued_thread=0; // MIMD - if( gpgpu_operand_collector ) { - m_opndcoll_new.init( gpgpu_operand_collector_num_units, - gpgpu_operand_collector_num_units_sfu, - gpgpu_num_reg_banks, this ); + m_warp_tracker = NULL; + m_thread_pc_tracker = NULL; + if (m_config->gpgpu_no_divg_load) { + m_warp_tracker = new warp_tracker_pool(this); + m_thread_pc_tracker = new thread_pc_tracker(warp_size, config->n_thread_per_shader); } + m_fetch_tid_out = (int*) malloc(sizeof(int) * warp_size); + m_dwf_rrstage_bank_access_counter = (int*) malloc(sizeof(int) * m_config->gpgpu_dwf_rr_stage_n_reg_banks); } - -unsigned shader_reinit(shader_core_ctx_t *sc, int start_thread, int end_thread ) +void shader_core_ctx::reinit(unsigned start_thread, unsigned end_thread, bool reset_not_completed ) { - int i; - unsigned result=0; - - if ( gpgpu_cuda_sim ) { - unsigned cta_size = ptx_sim_cta_size(); - if ( cta_size > sc->n_threads ) { - printf("Execution error: Shader kernel CTA (block) size is too large for microarch config.\n"); - printf(" This can cause problems with applications that use __syncthreads.\n"); - printf(" CTA size (x*y*z) = %u, n_threads = %u\n", cta_size, sc->n_threads ); - printf(" => either change -gpgpu_shader argument in gpgpusim.config file or\n"); - printf(" modify the CUDA source to decrease the kernel block size.\n"); - abort(); - } + if( reset_not_completed ) + m_not_completed = 0; + m_next_warp = 0; + m_last_issued_thread=0; + for (unsigned i = start_thread; i<end_thread; i++) { + m_thread[i].n_insn = 0; + m_thread[i].m_cta_id = -1; } - - sc->next_warp = 0; - sc->branch_priority = 0; - - for (i = start_thread; i<end_thread; i++) - ptx_sim_free_sm(&sc->thread[i].ptx_thd_info); - - for (i = start_thread; i<end_thread; i++) { - sc->max_branch_priority[i] = INT_MAX; - sc->thread[i].warp_priority = sc->max_branch_priority[i]; - sc->thread[i].n_insn = 0; - sc->thread[i].cta_id = -1; + for (unsigned i = start_thread / m_config->warp_size; i < end_thread / m_config->warp_size; ++i) { + m_warp[i].reset(); + m_pdom_warp[i]->reset(); } - - for (unsigned i = start_thread / warp_size; i < end_thread / warp_size; ++i) { - sc->warp[i].reset(warp_size); - sc->pdom_warp[i].m_stack_top = 0; - memset(sc->pdom_warp[i].m_pc, -1, warp_size * 2 * sizeof(address_type)); - memset(sc->pdom_warp[i].m_calldepth, 0, warp_size * 2 * sizeof(unsigned int)); - memset(sc->pdom_warp[i].m_active_mask, 0, warp_size * 2 * sizeof(unsigned int)); - memset(sc->pdom_warp[i].m_recvg_pc, -1, warp_size * 2 * sizeof(address_type)); - memset(sc->pdom_warp[i].m_branch_div_cycle, 0, warp_size * 2 * sizeof(unsigned long long )); - } - - sc->waiting_at_barrier = 0; - sc->last_issued_thread = end_thread - 1; - - if (sc->using_commit_queue) { - if (!gpgpu_spread_blocks_across_cores) //assertion no longer holds with multiple blocks per core - assert(dq_empty(sc->thd_commit_queue)); - } - sc->pending_shmem_bkacc = 0; - sc->pending_cache_bkacc = 0; - sc->pending_cmem_acc = 0; - - //do not reset this here, shader memory may be in the middle of processing another cta's instruction. - //sc->shader_memory_new_instruction_processed = false; - - return result; } -// initialize a CTA in the shader core, currently only useful for PDOM and DWF - -void shader_init_CTA(shader_core_ctx_t *shader, int start_thread, int end_thread) +void shader_core_ctx::init_warps( unsigned start_thread, unsigned end_thread ) { - int i; - int n_thread = end_thread - start_thread; - address_type start_pc = ptx_thread_get_next_pc(shader->thread[start_thread].ptx_thd_info); - if (shader->model == POST_DOMINATOR) { - int start_warp = start_thread / warp_size; - int end_warp = end_thread / warp_size + ((end_thread % warp_size)? 1 : 0); - for (i = start_warp; i < end_warp; ++i) { - shader->pdom_warp[i].m_stack_top = 0; - memset(shader->pdom_warp[i].m_pc, -1, warp_size * 2 * sizeof(address_type)); - memset(shader->pdom_warp[i].m_calldepth, 0, warp_size * 2 * sizeof(unsigned int)); - memset(shader->pdom_warp[i].m_active_mask, 0, warp_size * 2 * sizeof(unsigned int)); - memset(shader->pdom_warp[i].m_recvg_pc, -1, warp_size * 2 * sizeof(address_type)); - memset(shader->pdom_warp[i].m_branch_div_cycle, 0, warp_size * 2 * sizeof(unsigned long long )); - shader->pdom_warp[i].m_pc[0] = start_pc; - shader->pdom_warp[i].m_calldepth[0] = 1; - int t = 0; - for (t = 0; t < (int)warp_size; t++) { - if ( i * (int)warp_size + t < end_thread ) { - shader->pdom_warp[i].m_active_mask[0] |= (1 << t); + unsigned num_threads = end_thread - start_thread; + address_type start_pc = next_pc(start_thread); + if (m_config->model == POST_DOMINATOR) { + unsigned start_warp = start_thread / m_config->warp_size; + unsigned end_warp = end_thread / m_config->warp_size + ((end_thread % m_config->warp_size)? 1 : 0); + for (unsigned i = start_warp; i < end_warp; ++i) { + unsigned initial_active_mask = 0; + unsigned n_active=0; + for (unsigned t = 0; t < m_config->warp_size; t++) { + if ( i * m_config->warp_size + t < end_thread ) { + initial_active_mask |= (1 << t); + n_active++; + } } - } + m_pdom_warp[i]->launch(start_pc,initial_active_mask); + m_warp[i].init(start_pc,i,n_active); + m_not_completed += n_active; } - } else if (shader->model == DWF) { - dwf_init_CTA(shader->sid, start_thread, n_thread, start_pc); + } else if (m_config->model == DWF) { + dwf_init_CTA(m_sid, start_thread, num_threads, start_pc); + for (unsigned i = start_thread; i<end_thread; i++) + m_thread[i].m_in_scheduler = true; + } + for (unsigned tid=start_thread;tid<end_thread;tid++) { + m_thread[tid].m_avail4fetch = true; } - - for (i = start_thread; i<end_thread; i++) { - shader->thread[i].in_scheduler = 1; - } } - - - // register id for unused register slot in instruction #define DNA (0) unsigned g_next_shader_inst_uid=1; -// check to see if the fetch stage need to be stalled -int shader_fetch_stalled(shader_core_ctx_t *shader) +bool shader_core_ctx::fetch_stalled() { - int n_warp_parts = warp_size/pipe_simd_width; - - if (shader->warp_part2issue < n_warp_parts) { - return 1; - } - - for (unsigned i=0; i<warp_size; i++) { - if (shader->pipeline_reg[TS_IF][i].hw_thread_id != -1 ) { - return 1; // stalled + for (unsigned i=0; i<m_config->warp_size; i++) { + if (m_pipeline_reg[TS_IF][i].hw_thread_id != -1 ) { + return true; // stalled } } - for (int i=0; i<pipe_simd_width; i++) { - if (shader->pipeline_reg[IF_ID][i].hw_thread_id != -1 ) { - return 1; // stalled + for (unsigned i=0; i<m_config->warp_size; i++) { + if (m_pipeline_reg[IF_ID][i].hw_thread_id != -1 ) { + return true; // stalled } } - shader->warp_part2issue = 0; // reset pointer to first warp part - shader->new_warp_TS = 1; - - return 0; // not stalled + m_new_warp_TS = true; + return false; // not stalled } // initalize the pipeline stage register to nops -void shader_clear_stage_reg(shader_core_ctx_t *shader, int stage) +void shader_core_ctx::clear_stage_reg(int stage) { - for (unsigned i=0; i<warp_size; i++) { - shader->pipeline_reg[stage][i] = nop_inst; - } + clear_stage( m_pipeline_reg[stage] ); } // return the next pc of a thread -address_type shader_thread_nextpc(shader_core_ctx_t *shader, int tid) +address_type shader_core_ctx::next_pc( int tid ) const { - assert( gpgpu_cuda_sim ); - address_type pc = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); - return pc; + if( tid == -1 ) + return -1; + ptx_thread_info *the_thread = m_thread[tid].m_functional_model_thread_state; + if ( the_thread == NULL ) + return -1; + return the_thread->get_pc(); // PC should already be updatd to next PC at this point (was set in shader_decode() last time thread ran) } // issue thread to the warp // tid - thread id, warp_id - used by PDOM, wlane - position in warp -void shader_issue_thread(shader_core_ctx_t *shader, int tid, int wlane, unsigned active_mask ) -{ - if ( gpgpu_cuda_sim ) { - shader->pipeline_reg[TS_IF][wlane].hw_thread_id = tid; - shader->pipeline_reg[TS_IF][wlane].wlane = wlane; - shader->pipeline_reg[TS_IF][wlane].pc = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); - shader->pipeline_reg[TS_IF][wlane].ptx_thd_info = shader->thread[tid].ptx_thd_info; - shader->pipeline_reg[TS_IF][wlane].memreqaddr = 0; - shader->pipeline_reg[TS_IF][wlane].reg_bank_conflict_stall_checked = 0; - shader->pipeline_reg[TS_IF][wlane].reg_bank_access_pending = 0; - shader->pipeline_reg[TS_IF][wlane].uid = g_next_shader_inst_uid++; - shader->pipeline_reg[TS_IF][wlane].warp_active_mask = active_mask; - shader->pipeline_reg[TS_IF][wlane].ts_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; - } - assert( shader->thread[tid].avail4fetch > 0 ); - shader->thread[tid].avail4fetch--; - assert( shader->warp[wid_from_hw_tid(tid,warp_size)].n_avail4fetch > 0 ); - shader->warp[wid_from_hw_tid(tid,warp_size)].n_avail4fetch--; -} - -void update_max_branch_priority(shader_core_ctx_t *shader, unsigned warp_hw_id, unsigned grid_num ) +void shader_core_ctx::shader_issue_thread(int tid, int wlane, unsigned active_mask ) { - int temp_max = 0; - // This means that a group of threads has completed, - // hence need to update max_priority - for (unsigned i = 0; i<warp_size; i++) { - if ( !ptx_thread_done( shader->thread[hw_tid_from_wid(warp_hw_id,warp_size,i)].ptx_thd_info ) ) { - if (shader->thread[hw_tid_from_wid(warp_hw_id,warp_size,i)].warp_priority>=temp_max) { - temp_max = shader->thread[hw_tid_from_wid(warp_hw_id,warp_size,i)].warp_priority; - } - } - } - for (unsigned i = 0; i<warp_size; i++) { - shader->max_branch_priority[hw_tid_from_wid(warp_hw_id,warp_size,i)] = temp_max; - } -} - -void shader_fetch_simd_no_reconverge(shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) -{ - int i; - int tid; - int new_tid = 0; - address_type pc = 0; - int warp_ok = 0; - int n_warp = shader->n_threads/warp_size; - int complete = 0; - - assert(gpgpu_cuda_sim); - - // First, check to see if entire program is completed, - // if it is, then break out of loop - for (unsigned i=0; i<shader->n_threads; i++) { - if (!ptx_thread_done( shader->thread[i].ptx_thd_info )) { - complete = 0; - break; - } else { - complete = 1; - } - } - if (complete) { - // printf("Shader has completed program.\n"); - return; - } - - if (shader_fetch_stalled(shader)) { - return; - } - shader_clear_stage_reg(shader, TS_IF); - - // Finds a warp where all threads in it are available for fetching - // simultaneously(all threads are not yet in pipeline, or, the ones - // that are not available, are completed already - for (i=0; i<n_warp; i++) { - int n_completed = shader->warp[shader->next_warp].n_completed; - int n_avail4fetch = shader->warp[shader->next_warp].n_avail4fetch; - if (((n_completed) == (int)warp_size) || - ((n_completed + n_avail4fetch) < (int)warp_size) ) { - //All threads in this warp have completed, hence go to next warp - //Or, some of the threads are still in pipeline - warp_ok = 0; // hey look, it's a silent register update / store instruction! (this operation is redundant) - shader->next_warp = (shader->next_warp+1)%n_warp; - } else { - int n_waiting_at_barrier = shader->warp[shader->next_warp].n_waiting_at_barrier; - if ( n_waiting_at_barrier >= (int)warp_size ) { - warp_ok = 0; // hey look, it's a silent register update / store instruction! (this operation is redundant) - continue; - } - warp_ok = 1; - break; - } - } - // None of the instructions from inside the warp can be scheduled -> should - // probably just stall, ie nops into pipeline - if (!warp_ok) { - shader_clear_stage_reg(shader, TS_IF); // NOTE: is this needed? - shader->next_warp = (shader->next_warp+1)%n_warp; // NOTE: this is not round-robin. - return; - } - - tid = warp_size*shader->next_warp; - - for (i = 0; i<(int)warp_size; i++) { - if (shader->thread[tid+i].warp_priority == shader->max_branch_priority[tid+i]) { - pc = shader_thread_nextpc(shader, tid+i); - new_tid = tid+i; - break; - } - } - //Determine which instructions inside this 'warp' will be scheduled together at this run - //If they are cannot be scheduled together then 'save' their branch priority - for (i = 0; i<(int)warp_size; i++) { - if (!ptx_thread_done( shader->thread[tid+i].ptx_thd_info )) { - address_type next_pc; - next_pc = shader_thread_nextpc(shader, tid+i); - if (next_pc != pc || - shader->thread[tid+i].warp_priority != shader->max_branch_priority[tid+i] || - shader->thread[tid+i].m_waiting_at_barrier) { - if (!ptx_thread_done( shader->thread[tid+i].ptx_thd_info )) { - if ( !shader->thread[tid + i].m_waiting_at_barrier ) { - shader->thread[tid + i].warp_priority = shader->branch_priority; - } - } - } else { - shader_issue_thread(shader, tid+i, i,(unsigned)-1); - } - } - } - shader->branch_priority++; + m_thread[tid].m_functional_model_thread_state->ptx_fetch_inst( m_pipeline_reg[TS_IF][wlane] ); + m_pipeline_reg[TS_IF][wlane].hw_thread_id = tid; + m_pipeline_reg[TS_IF][wlane].wlane = wlane; + m_pipeline_reg[TS_IF][wlane].memreqaddr = 0; + m_pipeline_reg[TS_IF][wlane].uid = g_next_shader_inst_uid++; + m_pipeline_reg[TS_IF][wlane].warp_active_mask = active_mask; + m_pipeline_reg[TS_IF][wlane].issue_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; - shader->next_warp = (shader->next_warp+1)%n_warp; + assert( m_thread[tid].m_avail4fetch ); + m_thread[tid].m_avail4fetch = false; + assert( m_warp[wid_from_hw_tid(tid,m_config->warp_size)].get_avail4fetch() > 0 ); + m_warp[wid_from_hw_tid(tid,m_config->warp_size)].dec_avail4fetch(); } -int pdom_sched_find_next_warp (shader_core_ctx_t *shader,int pdom_sched_policy, int* ready_warps - , int ready_warp_count, int* last_warp, int w_comp_c, int w_pipe_c, int w_barr_c) +int shader_core_ctx::pdom_sched_find_next_warp (int ready_warp_count) { - int n_warp = shader->n_threads/warp_size; - int i=0; - int selected_warp = ready_warps[0]; - int found =0; - - switch (pdom_sched_policy) { - case 0: - selected_warp = ready_warps[0]; //first ok warp found - found=1; - break; - case 1 ://random - selected_warp = ready_warps[rand()%ready_warp_count]; - found=1; - break; - case 8 :// execute the first available warp which is after the warp execued last time - found=0; - selected_warp = (last_warp[shader->sid] + 1 ) % n_warp; + bool found = false; + int selected_warp = m_ready_warps[0]; + switch (m_config->pdom_sched_type) { + case 0: selected_warp = m_ready_warps[0]; found=true; break; // first ok warp found + case 1: selected_warp = m_ready_warps[rand()%ready_warp_count]; found=true; break; //random + case 8: + // "loose" round robin: + // execute the next available warp which is after the warp execued last time + selected_warp = (m_last_warp + 1) % m_config->max_warps_per_shader; while (!found) { - for (i=0;i<ready_warp_count;i++) { - if (selected_warp==ready_warps[i]) { - found=1; - } + for (int i=0;i<ready_warp_count;i++) { + if (selected_warp==m_ready_warps[i]) + found=true; } - if (!found) - selected_warp = (selected_warp + 1 ) % n_warp; + if( !found ) + selected_warp = (selected_warp + 1) % m_config->max_warps_per_shader; } break; - default: - assert(0); + default: assert(0); } if (found) { - if (ready_warp_count==1) { - n_pdom_sc_single_stat++; - } else { - n_pdom_sc_orig_stat++; - } + if (ready_warp_count==1) + m_stats->n_pdom_sc_single_stat++; + else + m_stats->n_pdom_sc_orig_stat++; return selected_warp; } else { return -1; } } -void shader_fetch_simd_postdominator(shader_core_ctx_t *shader, unsigned int shader_number, int grid_num) { - int i; +void shader_core_ctx::fetch_simd_postdominator() +{ int warp_ok = 0; - int n_warp = shader->n_threads/warp_size; - int complete = 0; + bool complete = false; int tmp_warp; int warp_id; address_type check_pc = -1; - assert(gpgpu_cuda_sim); - // First, check to see if entire program is completed, // if it is, then break out of loop - for (unsigned i=0; i<shader->n_threads; i++) { - if (!ptx_thread_done( shader->thread[i].ptx_thd_info )) { - complete = 0; + for (unsigned i=0; i<m_config->n_thread_per_shader; i++) { + if (!ptx_thread_done(i)) { + complete = false; break; } else { - complete = 1; + complete = true; } } - if (complete) { + if (complete) return; - } - if (shader_fetch_stalled(shader)) { + if (fetch_stalled()) return; - } - shader_clear_stage_reg(shader, TS_IF); - - int ready_warp_count = 0; - int w_comp_c = 0 ; - int w_pipe_c = 0 ; - int w_barr_c = 0 ; - static int * ready_warps = NULL; - static int * tmp_ready_warps = NULL; - if (!ready_warps) { - ready_warps = (int*)calloc(n_warp,sizeof(int)); - } - if (!tmp_ready_warps) { - tmp_ready_warps = (int*)calloc(n_warp,sizeof(int)); - } - for (i=0; i<n_warp; i++) { - ready_warps[i]=-1; - tmp_ready_warps[i]=-1; - } + clear_stage_reg(TS_IF); - static int* last_warp; //keeps track of last warp issued per shader - if (!last_warp) { - last_warp = (int*)calloc(gpu_n_shader,sizeof(int)); + unsigned ready_warp_count = 0; + for (unsigned i=0; i<m_config->max_warps_per_shader; i++) { + m_ready_warps[i]=-1; + m_tmp_ready_warps[i]=-1; } - // Finds a warp where all threads in it are available for fetching // simultaneously(all threads are not yet in pipeline, or, the ones // that are not available, are completed already - for (i=0; i<n_warp; i++) { - int n_completed = shader->warp[shader->next_warp].n_completed; - int n_avail4fetch = shader->warp[shader->next_warp].n_avail4fetch; - - if ((n_completed) == (int)warp_size) { - //All threads in this warp have completed - w_comp_c++; - } else if ((n_completed+n_avail4fetch) < (int)warp_size) { - //some of the threads are still in pipeline - w_pipe_c++; - } else if ( shader->warp_waiting_at_barrier(shader->next_warp) ) { - w_barr_c++; - } else if ( shader_warp_scoreboard_hazard(shader, shader->next_warp) ) { - // Do nothing - warp is filtered out - //printf("SCOREBOARD COLLISION - wid=%d\n", shader->next_warp); - } else { - // A valid warp is found at this point - tmp_ready_warps[ready_warp_count] = shader->next_warp; - ready_warp_count++; + for (unsigned i=0; i<m_config->max_warps_per_shader; i++) { + if( m_warp[m_next_warp].waiting() ) { + // waiting for kernel launch, barrier, membar, atomic + } else if( (m_warp[m_next_warp].get_n_completed()+m_warp[m_next_warp].get_avail4fetch()) < m_config->warp_size) { + // waiting for instruction still in pipeline barrel processing + } else if ( !warp_scoreboard_hazard(m_next_warp) ) { + // this warp is ready and can be issued if selected + m_tmp_ready_warps[ready_warp_count] = m_next_warp; + ready_warp_count++; } - shader->next_warp = (shader->next_warp + 1) % n_warp; - } - for (i=0;i<ready_warp_count;i++) { - ready_warps[i]=tmp_ready_warps[i]; + m_next_warp = (m_next_warp + 1) % m_config->max_warps_per_shader; } - - num_warps_issuable[ready_warp_count]++; - num_warps_issuable_pershader[shader->sid]+= ready_warp_count; - + for (unsigned i=0;i<ready_warp_count;i++) + m_ready_warps[i]=m_tmp_ready_warps[i]; + m_stats->num_warps_issuable[ready_warp_count]++; + m_stats->num_warps_issuable_pershader[m_sid]+= ready_warp_count; if (ready_warp_count) { - tmp_warp = pdom_sched_find_next_warp (shader, pdom_sched_type ,ready_warps - , ready_warp_count, last_warp, w_comp_c, w_pipe_c ,w_barr_c); + tmp_warp = pdom_sched_find_next_warp (ready_warp_count); if (tmp_warp != -1) { - shader->next_warp = tmp_warp; + m_next_warp = tmp_warp; warp_ok=1; } } - static int no_warp_issued; - // None of the instructions from inside the warp can be scheduled -> should - // probably just stall, ie nops into pipeline if (!warp_ok) { - shader_clear_stage_reg(shader, TS_IF); - shader->next_warp = (shader->next_warp+1) % n_warp; - no_warp_issued = 1 ; + // None of the instructions from inside the warp can be scheduled -> should + // probably just stall, ie nops into pipeline + clear_stage_reg(TS_IF); + m_next_warp = (m_next_warp+1) % m_config->max_warps_per_shader; return; } /************************************************************/ - //at this point we have a warp to execute which is pointed to by - //shader->next_warp - - warp_id = shader->next_warp; - last_warp[shader->sid] = warp_id; - int wtid = warp_size*warp_id; + // at this point we have a warp to execute which is pointed to by next_warp - pdom_warp_ctx_t *scheduled_warp = &(shader->pdom_warp[warp_id]); + warp_id = m_next_warp; + m_last_warp = warp_id; + int wtid = m_config->warp_size*warp_id; + pdom_warp_ctx_t *scheduled_warp = m_pdom_warp[warp_id]; // schedule threads according to active mask on the top of pdom stack - for (i = 0; i < (int)warp_size; i++) { + unsigned active_mask = scheduled_warp->get_active_mask(); + + for (unsigned i = 0; i < m_config->warp_size; i++) { unsigned int mask = (1 << i); - if ((scheduled_warp->m_active_mask[scheduled_warp->m_stack_top] & mask) == mask) { - assert (!ptx_thread_done( shader->thread[wtid+i].ptx_thd_info )); - shader_issue_thread(shader, wtid+i, i, scheduled_warp->m_active_mask[scheduled_warp->m_stack_top]); + if ((active_mask & mask) == mask) { + assert (!ptx_thread_done(wtid+i)); + shader_issue_thread(wtid+i,i,active_mask); } } - shader->next_warp = (shader->next_warp+1)%n_warp; + m_next_warp = (m_next_warp+1)%m_config->max_warps_per_shader; // check if all issued threads have the same pc - for (i = 0; i < (int) warp_size; i++) { - if ( shader->pipeline_reg[TS_IF][i].hw_thread_id != -1 ) { + for (unsigned i = 0; i < m_config->warp_size; i++) { + if ( m_pipeline_reg[TS_IF][i].hw_thread_id != -1 ) { if ( check_pc == (unsigned)-1 ) { - check_pc = shader->pipeline_reg[TS_IF][i].pc; + check_pc = m_pipeline_reg[TS_IF][i].pc; } else { - assert( check_pc == shader->pipeline_reg[TS_IF][i].pc ); + assert( check_pc == m_pipeline_reg[TS_IF][i].pc ); } } } } -bool shader_warp_scoreboard_hazard(shader_core_ctx_t *shader, int warp_id) { - static inst_t active_inst; - static op_type op = NO_OP; - static int i1, i2, i3, i4, o1, o2, o3, o4; //4 outputs needed for texture fetches in cuda-sim - static int vectorin, vectorout; - static int arch_reg[MAX_REG_OPERANDS] = { -1 }; - static int pred; - static int ar1, ar2; // address registers for memory operands +/** + * check if warp has data hazard + * + * @param warp_id + * + * @return bool : false if hazard exists + */ +bool shader_core_ctx::warp_scoreboard_hazard(int warp_id) +{ + inst_t active_inst; // Get an active thread in the warp - int wtid = warp_size*warp_id; - pdom_warp_ctx_t *scheduled_warp = &(shader->pdom_warp[warp_id]); + int wtid = m_config->warp_size*warp_id; + pdom_warp_ctx_t *scheduled_warp = m_pdom_warp[warp_id]; thread_ctx_t *active_thread = NULL; - for (int i = 0; i < (int)warp_size; i++) { + unsigned active_mask = scheduled_warp->get_active_mask(); + for (unsigned i = 0; i < m_config->warp_size; i++) { unsigned int mask = (1 << i); - if ((scheduled_warp->m_active_mask[scheduled_warp->m_stack_top] & mask) == mask) { - active_thread = &(shader->thread[wtid+i]); + if ((active_mask & mask) == mask) { + active_thread = &(m_thread[wtid+i]); } } - if(active_thread == NULL) return false; + if(active_thread == NULL) + return false; // Decode instruction - ptx_decode_inst( active_thread->ptx_thd_info, (unsigned*)&op, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg, &pred, &ar1, &ar2); - active_inst.op = op; - active_inst.in[0] = i1; - active_inst.in[1] = i2; - active_inst.in[2] = i3; - active_inst.in[3] = i4; - active_inst.out[0] = o1; - active_inst.out[1] = o2; - active_inst.out[2] = o3; - active_inst.out[3] = o4; - active_inst.is_vectorin = vectorin; - active_inst.is_vectorout = vectorout; - active_inst.pred = pred; - active_inst.ar1 = ar1; - active_inst.ar2 = ar2; - - return shader->scrb->checkCollision(warp_id, &active_inst); + active_thread->m_functional_model_thread_state->ptx_fetch_inst( active_inst ); + return m_scoreboard->checkCollision(warp_id, &active_inst); } -void shader_pdom_update_warp_mask(shader_core_ctx_t *shader, int warp_id) { - int wtid = warp_size*warp_id; +void pdom_warp_ctx_t::pdom_update_warp_mask() +{ + int wtid = m_warp_size*m_warp_id; - pdom_warp_ctx_t *scheduled_warp = &(shader->pdom_warp[warp_id]); + pdom_warp_ctx_t *scheduled_warp = this; int stack_top = scheduled_warp->m_stack_top; @@ -1176,21 +788,21 @@ void shader_pdom_update_warp_mask(shader_core_ctx_t *shader, int warp_id) { // extract a group of threads with the same next PC among the active threads in the warp address_type tmp_next_pc = null_pc; unsigned int tmp_active_mask = 0; - void *first_active_thread=NULL; - for (int i = warp_size - 1; i >= 0; i--) { + class ptx_thread_info *first_active_thread=NULL; + for (int i = m_warp_size - 1; i >= 0; i--) { unsigned int mask = (1 << i); if ((top_active_mask & mask) == mask) { // is this thread active? - if (ptx_thread_done( shader->thread[wtid+i].ptx_thd_info )) { - top_active_mask &= ~mask; // remove completed thread from active mask - } else if (tmp_next_pc == null_pc) { - first_active_thread = shader->thread[wtid+i].ptx_thd_info; - tmp_next_pc = shader_thread_nextpc(shader, wtid+i); - tmp_active_mask |= mask; - top_active_mask &= ~mask; - } else if (tmp_next_pc == shader_thread_nextpc(shader, wtid+i)) { - tmp_active_mask |= mask; - top_active_mask &= ~mask; - } + if (m_shader->ptx_thread_done(wtid+i)) { + top_active_mask &= ~mask; // remove completed thread from active mask + } else if (tmp_next_pc == null_pc) { + first_active_thread=m_shader->get_thread_state(wtid+i); + tmp_next_pc = first_active_thread->get_pc(); + tmp_active_mask |= mask; + top_active_mask &= ~mask; + } else if (tmp_next_pc == m_shader->get_thread_state(wtid+i)->get_pc()) { + tmp_active_mask |= mask; + top_active_mask &= ~mask; + } } } @@ -1229,60 +841,102 @@ void shader_pdom_update_warp_mask(shader_core_ctx_t *shader, int warp_id) { scheduled_warp->m_stack_top = stack_top - 1; assert(scheduled_warp->m_stack_top >= 0); - assert(scheduled_warp->m_stack_top < (int)warp_size * 2); + assert(scheduled_warp->m_stack_top < m_warp_size * 2); +} + +void gpgpu_sim::get_pdom_stack_top_info( unsigned sid, unsigned tid, unsigned *pc, unsigned *rpc ) +{ + m_sc[sid]->get_pdom_stack_top_info(tid,pc,rpc); +} + +void shader_core_ctx::get_pdom_stack_top_info( unsigned tid, unsigned *pc, unsigned *rpc ) +{ + unsigned warp_id = tid/m_config->warp_size; + m_pdom_warp[warp_id]->get_pdom_stack_top_info(pc,rpc); } +void pdom_warp_ctx_t::get_pdom_stack_top_info( unsigned *pc, unsigned *rpc ) +{ + *pc = m_pc[m_stack_top]; + *rpc = m_recvg_pc[m_stack_top]; +} -void get_pdom_stack_top_info( unsigned sid, unsigned tid, unsigned *pc, unsigned *rpc ) +unsigned pdom_warp_ctx_t::get_rp() const +{ + return m_recvg_pc[m_stack_top]; +} + +void pdom_warp_ctx_t::print (FILE *fout) const { - unsigned warp_id = tid/warp_size; - pdom_warp_ctx_t *warp_info = &(sc[sid]->pdom_warp[warp_id]); - unsigned idx = warp_info->m_stack_top; - *pc = warp_info->m_pc[idx]; - *rpc = warp_info->m_recvg_pc[idx]; + const pdom_warp_ctx_t *warp=this; + for ( unsigned k=0; k <= warp->m_stack_top; k++ ) { + if ( k==0 ) { + fprintf(fout, "w%02d %1u ", m_warp_id, k ); + } else { + fprintf(fout, " %1u ", k ); + } + for (unsigned m=1,j=0; j<m_warp_size; j++, m<<=1) + fprintf(fout, "%c", ((warp->m_active_mask[k] & m)?'1':'0') ); + fprintf(fout, " pc: %4u", warp->m_pc[k] ); + if ( warp->m_recvg_pc[k] == (unsigned)-1 ) { + fprintf(fout," rp: ---- cd: %2u ", warp->m_calldepth[k] ); + } else { + fprintf(fout," rp: %4u cd: %2u ", warp->m_recvg_pc[k], warp->m_calldepth[k] ); + } + if ( warp->m_branch_div_cycle[k] != 0 ) { + fprintf(fout," bd@%6u ", (unsigned) warp->m_branch_div_cycle[k] ); + } else { + fprintf(fout," " ); + } + ptx_print_insn( warp->m_pc[k], fout ); + fprintf(fout,"\n"); + } } -void shader_fetch_mimd( shader_core_ctx_t *shader, unsigned int shader_number ) + +void shader_core_ctx::new_cache_window() { - unsigned int last_issued_thread = 0; + shd_cache_new_window(m_L1D); + shd_cache_new_window(m_L1T); + shd_cache_new_window(m_L1C); +} - if (shader_fetch_stalled(shader)) { +void shader_core_ctx::fetch_mimd() +{ + if (fetch_stalled()) return; - } - shader_clear_stage_reg(shader, TS_IF); + clear_stage_reg(TS_IF); - // some form of barrel processing: - // - checking availability from the thread after the last issued thread - for (int i=0, j=0;i<(int)shader->n_threads && j< (int) warp_size;i++) { - int thd_id = (i + shader->last_issued_thread + 1) % shader->n_threads; - if (shader->thread[thd_id].avail4fetch && !shader->thread[thd_id].m_waiting_at_barrier ) { - shader_issue_thread(shader, thd_id, j,(unsigned)-1); - last_issued_thread = thd_id; + for (unsigned i=0, j=0;i<m_config->n_thread_per_shader && j< m_config->warp_size;i++) { + int thd_id = (i + m_last_issued_thread + 1) % m_config->n_thread_per_shader; + if (m_thread[thd_id].m_avail4fetch && !m_thread[thd_id].m_waiting_at_barrier ) { + shader_issue_thread(thd_id, j,(unsigned)-1); + m_last_issued_thread = thd_id; j++; } } - shader->last_issued_thread = last_issued_thread; } // seperate the incoming warp into multiple warps with seperate pcs -int split_warp_by_pc(int *tid_in, shader_core_ctx_t *shader, int **tid_split, address_type *pc) { +int shader_core_ctx::split_warp_by_pc(int *tid_in, int **tid_split, address_type *pc) +{ unsigned n_pc = 0; static int *pc_cnt = NULL; // count the number of threads with the same pc assert(tid_in); assert(tid_split); assert(pc); - memset(pc,0,sizeof(address_type)*warp_size); + memset(pc,0,sizeof(address_type)*m_config->warp_size); - if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*warp_size); - memset(pc_cnt,0,sizeof(int)*warp_size); + if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*m_config->warp_size); + memset(pc_cnt,0,sizeof(int)*m_config->warp_size); // go through each thread in the given warp - for (unsigned i=0; i< warp_size; i++) { + for (unsigned i=0; i< m_config->warp_size; i++) { if (tid_in[i] < 0) continue; int matched = 0; address_type thd_pc; - thd_pc = shader_thread_nextpc(shader, tid_in[i]); + thd_pc = next_pc(tid_in[i]); // check to see if the pc has occured before for (unsigned j=0; j<n_pc; j++) { @@ -1295,7 +949,7 @@ int split_warp_by_pc(int *tid_in, shader_core_ctx_t *shader, int **tid_split, ad } // if not, put the tid in a seperate warp if (!matched) { - assert(n_pc < warp_size); + assert(n_pc < m_config->warp_size); tid_split[n_pc][0] = tid_in[i]; pc[n_pc] = thd_pc; pc_cnt[n_pc] = 1; @@ -1306,12 +960,12 @@ int split_warp_by_pc(int *tid_in, shader_core_ctx_t *shader, int **tid_split, ad } // see if this warp just executed the barrier instruction -int warp_reached_barrier(int *tid_in, shader_core_ctx_t *shader) +int shader_core_ctx::warp_reached_barrier(int *tid_in) { int reached_barrier = 0; - for (unsigned i=0; i<warp_size; i++) { + for (unsigned i=0; i<m_config->warp_size; i++) { if (tid_in[i] < 0) continue; - if (shader->thread[tid_in[i]].m_reached_barrier) { + if (m_thread[tid_in[i]].m_reached_barrier) { reached_barrier = 1; break; } @@ -1320,26 +974,27 @@ int warp_reached_barrier(int *tid_in, shader_core_ctx_t *shader) } // seperate the incoming warp into multiple warps with seperate pcs and cta -int split_warp_by_cta(int *tid_in, shader_core_ctx_t *shader, int **tid_split, address_type *pc, int *cta) { +int shader_core_ctx::split_warp_by_cta(int *tid_in, int **tid_split, address_type *pc, int *cta) +{ unsigned n_pc = 0; static int *pc_cnt = NULL; // count the number of threads with the same pc assert(tid_in); assert(tid_split); assert(pc); - memset(pc,0,sizeof(address_type)*warp_size); + memset(pc,0,sizeof(address_type)*m_config->warp_size); - if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*warp_size); - memset(pc_cnt,0,sizeof(int)*warp_size); + if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*m_config->warp_size); + memset(pc_cnt,0,sizeof(int)*m_config->warp_size); // go through each thread in the given warp - for (unsigned i=0; i<warp_size; i++) { + for (unsigned i=0; i<m_config->warp_size; i++) { if (tid_in[i] < 0) continue; int matched = 0; address_type thd_pc; - thd_pc = shader_thread_nextpc(shader, tid_in[i]); + thd_pc = next_pc(tid_in[i]); - int thd_cta = ptx_thread_get_cta_uid( shader->thread[tid_in[i]].ptx_thd_info ); + int thd_cta = ptx_thread_get_cta_uid( m_thread[tid_in[i]].m_functional_model_thread_state ); // check to see if the pc has occured before for (unsigned j=0; j<n_pc; j++) { @@ -1352,7 +1007,7 @@ int split_warp_by_cta(int *tid_in, shader_core_ctx_t *shader, int **tid_split, a } // if not, put the tid in a seperate warp if (!matched) { - assert(n_pc < warp_size); + assert(n_pc < m_config->warp_size); tid_split[n_pc][0] = tid_in[i]; pc[n_pc] = thd_pc; cta[n_pc] = thd_cta; @@ -1363,638 +1018,508 @@ int split_warp_by_cta(int *tid_in, shader_core_ctx_t *shader, int **tid_split, a return n_pc; } -void shader_fetch_simd_dwf( shader_core_ctx_t *shader, unsigned int shader_number ) { - +void shader_core_ctx::fetch_simd_dwf() +{ static int *tid_in = NULL; static int *tid_out = NULL; if (!tid_in) { - tid_in = (int*) malloc(sizeof(int)*warp_size); - memset(tid_in, -1, sizeof(int)*warp_size); + tid_in = (int*) malloc(sizeof(int)*m_config->warp_size); + memset(tid_in, -1, sizeof(int)*m_config->warp_size); } if (!tid_out) { - tid_out = (int*) malloc(sizeof(int)*warp_size); - memset(tid_out, -1, sizeof(int)*warp_size); + tid_out = (int*) malloc(sizeof(int)*m_config->warp_size); + memset(tid_out, -1, sizeof(int)*m_config->warp_size); } static int **tid_split = NULL; if (!tid_split) { - tid_split = (int**)malloc(sizeof(int*)*warp_size); - tid_split[0] = (int*)malloc(sizeof(int)*warp_size*warp_size); - for (unsigned i=1; i<warp_size; i++) { - tid_split[i] = tid_split[0] + warp_size * i; + tid_split = (int**)malloc(sizeof(int*)*m_config->warp_size); + tid_split[0] = (int*)malloc(sizeof(int)*m_config->warp_size*m_config->warp_size); + for (unsigned i=1; i<m_config->warp_size; i++) { + tid_split[i] = tid_split[0] + m_config->warp_size * i; } } static address_type *thd_pc = NULL; - if (!thd_pc) thd_pc = (address_type*)malloc(sizeof(address_type)*warp_size); + if (!thd_pc) thd_pc = (address_type*)malloc(sizeof(address_type)*m_config->warp_size); static int *thd_cta = NULL; - if (!thd_cta) thd_cta = (int*)malloc(sizeof(int)*warp_size); + if (!thd_cta) thd_cta = (int*)malloc(sizeof(int)*m_config->warp_size); int warpupdate_bw = 1; - while (!dq_empty(shader->thd_commit_queue) && warpupdate_bw > 0) { + while (!m_thd_commit_queue->empty() && warpupdate_bw > 0) { // grab a committed warp, split it into multiple BRUs (tid_split) by PC - int *tid_commit = (int*)dq_pop(shader->thd_commit_queue); - memset(tid_split[0], -1, sizeof(int)*warp_size*warp_size); - memset(thd_pc, 0, sizeof(address_type)*warp_size); - memset(thd_cta, -1, sizeof(int)*warp_size); + std::vector<int> *tid_commit = m_thd_commit_queue->pop(gpu_sim_cycle); + memset(tid_split[0], -1, sizeof(int)*m_config->warp_size*m_config->warp_size); + memset(thd_pc, 0, sizeof(address_type)*m_config->warp_size); + memset(thd_cta, -1, sizeof(int)*m_config->warp_size); - int reached_barrier = warp_reached_barrier(tid_commit, shader); + int reached_barrier = warp_reached_barrier(tid_commit->data()); unsigned n_warp_update; if (reached_barrier) { - n_warp_update = split_warp_by_cta(tid_commit, shader, tid_split, thd_pc, thd_cta); + n_warp_update = split_warp_by_cta(tid_commit->data(), tid_split, thd_pc, thd_cta); } else { - n_warp_update = split_warp_by_pc(tid_commit, shader, tid_split, thd_pc); + n_warp_update = split_warp_by_pc(tid_commit->data(), tid_split, thd_pc); } - if (n_warp_update > 2) gpgpu_commit_pc_beyond_two++; + if (n_warp_update > 2) m_stats->gpgpu_commit_pc_beyond_two++; warpupdate_bw -= n_warp_update; // put the splitted warp updates into the DWF scheduler for (unsigned i=0;i<n_warp_update;i++) { - for (unsigned j=0;j<warp_size;j++) { + for (unsigned j=0;j<m_config->warp_size;j++) { if (tid_split[i][j] < 0) continue; - assert(shader->thread[tid_split[i][j]].avail4fetch); - assert(!shader->thread[tid_split[i][j]].in_scheduler); - shader->thread[tid_split[i][j]].in_scheduler = 1; + assert(m_thread[tid_split[i][j]].m_avail4fetch); + assert(!m_thread[tid_split[i][j]].m_in_scheduler); + m_thread[tid_split[i][j]].m_in_scheduler = true; } - dwf_clear_accessed(shader->sid); + dwf_clear_accessed(m_sid); if (reached_barrier) { - dwf_update_warp_at_barrier(shader->sid, tid_split[i], thd_pc[i], thd_cta[i]); + dwf_update_warp_at_barrier(m_sid, tid_split[i], thd_pc[i], thd_cta[i]); } else { - dwf_update_warp(shader->sid, tid_split[i], thd_pc[i]); + dwf_update_warp(m_sid, tid_split[i], thd_pc[i]); } } - free_commit_warp(tid_commit); + delete tid_commit; } // Track the #PC right after the warps are input to the scheduler - dwf_update_statistics(shader->sid); - dwf_clear_policy_access(shader->sid); + dwf_update_statistics(m_sid); + dwf_clear_policy_access(m_sid); - if (shader_fetch_stalled(shader)) { + if (fetch_stalled()) { return; } - shader_clear_stage_reg(shader, TS_IF); + clear_stage_reg(TS_IF); address_type scheduled_pc; - dwf_issue_warp(shader->sid, tid_out, &scheduled_pc); + dwf_issue_warp(m_sid, tid_out, &scheduled_pc); - for (unsigned i=0; i<warp_size; i++) { + for (unsigned i=0; i<m_config->warp_size; i++) { int issue_tid = tid_out[i]; if (issue_tid >= 0) { - shader_issue_thread(shader, issue_tid, i, (unsigned)-1); - shader->thread[issue_tid].in_scheduler = 0; - shader->thread[issue_tid].m_reached_barrier = 0; - shader->last_issued_thread = issue_tid; - assert(shader->pipeline_reg[TS_IF][i].pc == scheduled_pc); + shader_issue_thread(issue_tid, i, (unsigned)-1); + m_thread[issue_tid].m_in_scheduler = false; + m_thread[issue_tid].m_reached_barrier = false; + assert(m_pipeline_reg[TS_IF][i].pc == scheduled_pc); } } } -void print_shader_cycle_distro( FILE *fout ) +void gpgpu_sim::print_shader_cycle_distro( FILE *fout ) const { fprintf(fout, "Warp Occupancy Distribution:\n"); - fprintf(fout, "Stall:%d\t", shader_cycle_distro[0]); - fprintf(fout, "W0_Idle:%d\t", shader_cycle_distro[1]); - fprintf(fout, "W0_Mem:%d", shader_cycle_distro[2]); - for (unsigned i = 3; i < warp_size + 3; i++) { - fprintf(fout, "\tW%d:%d", i-2, shader_cycle_distro[i]); + fprintf(fout, "Stall:%d\t", m_shader_stats->shader_cycle_distro[0]); + fprintf(fout, "W0_Idle:%d\t", m_shader_stats->shader_cycle_distro[1]); + fprintf(fout, "W0_Mem:%d", m_shader_stats->shader_cycle_distro[2]); + for (unsigned i = 3; i < m_shader_config->warp_size + 3; i++) { + fprintf(fout, "\tW%d:%d", i-2, m_shader_stats->shader_cycle_distro[i]); } fprintf(fout, "\n"); } -void inflight_memory_insn_add( shader_core_ctx_t *shader, inst_t *mem_insn) + +#define PROGRAM_MEM_START 0xF0000000 /* should be distinct from other memory spaces... + check ptx_ir.h to verify this does not overlap + other memory spaces */ +void shader_core_ctx::fetch_new() { - if (enable_ptx_file_line_stats) { - ptx_file_line_stats_add_inflight_memory_insn(shader->sid, mem_insn->pc); - } + if( m_inst_fetch_buffer.m_valid ) { + // decode 1 or 2 instructions and place them into ibuffer + address_type pc = m_inst_fetch_buffer.m_pc; + const inst_t* pI1 = ptx_fetch_inst(pc); + assert(pI1); + m_warp[m_inst_fetch_buffer.m_warp_id].ibuffer_fill(0,pI1); + m_warp[m_inst_fetch_buffer.m_warp_id].inc_inst_in_pipeline(); + const inst_t* pI2 = ptx_fetch_inst(pc+pI1->isize); + if( pI2 ) { + m_warp[m_inst_fetch_buffer.m_warp_id].ibuffer_fill(1,pI2); + m_warp[m_inst_fetch_buffer.m_warp_id].inc_inst_in_pipeline(); + } + m_inst_fetch_buffer.m_valid = false; + } + + if( !m_inst_fetch_buffer.m_valid ) { + // find an active warp with space in instruction buffer that is not already waiting on a cache miss + // and get next 1-2 instructions from i-cache... + for( unsigned i=0; i < m_config->max_warps_per_shader; i++ ) { + unsigned warp_id = (m_last_warp_fetched+1+i) % m_config->max_warps_per_shader; + if( m_warp[warp_id].done() && !m_scoreboard->pendingWrites(warp_id) && !m_warp[warp_id].done_exit() + && m_warp[warp_id].stores_done() && !m_warp[warp_id].inst_in_pipeline() ) { + bool did_exit=false; + for( unsigned t=0; t<m_config->warp_size;t++) { + unsigned tid=warp_id*m_config->warp_size+t; + if( m_thread[tid].m_functional_model_thread_state ) { + register_cta_thread_exit(tid); + m_not_completed -= 1; + m_thread[tid].m_functional_model_thread_state=NULL; + did_exit=true; + } + } + if( did_exit ) + m_warp[warp_id].set_done_exit(); + } + if( !m_warp[warp_id].done() && !m_warp[warp_id].imiss_pending() && m_warp[warp_id].ibuffer_empty() ) { + address_type pc = m_warp[warp_id].get_pc(); + address_type ppc = pc + PROGRAM_MEM_START; + address_type wb=0; + unsigned nbytes=16; + unsigned offset_in_block = pc & (m_L1I->line_sz-1); + if( (offset_in_block+nbytes) > m_L1I->line_sz ) + nbytes = (m_L1I->line_sz-offset_in_block); + enum cache_request_status status = shd_cache_access_new( m_L1I, (unsigned long long)pc, nbytes, 0, gpu_sim_cycle, &wb ); + if( status != HIT ) { + unsigned req_size = READ_PACKET_SIZE; + if( m_gpu->fq_has_buffer(ppc, req_size, false, m_sid) ) { + m_last_warp_fetched=warp_id; + mshr_entry *mshr = new mshr_entry(); + mshr->init(ppc,false,instruction_space,warp_id); + fq_push( pc, req_size, false, + NO_PARTIAL_WRITE, + warp_id, + mshr, 0, + INST_ACC_R, pc ); + m_warp[warp_id].set_imiss_pending(mshr); + m_warp[warp_id].set_last_fetch(gpu_sim_cycle); + } + } else { + m_last_warp_fetched=warp_id; + m_inst_fetch_buffer = ifetch_buffer_t(pc,nbytes,warp_id); + m_warp[warp_id].set_last_fetch(gpu_sim_cycle); + } + break; + } + } + } } -void inflight_memory_insn_sub( shader_core_ctx_t *shader, inst_t *mem_insn) +int is_load ( const inst_t &inst ) { - if (enable_ptx_file_line_stats) { - ptx_file_line_stats_sub_inflight_memory_insn(shader->sid, mem_insn->pc); - } + return (inst.op == LOAD_OP || inst.memory_op == memory_load); } -void report_exposed_memory_latency( shader_core_ctx_t *shader ) +int is_store ( const inst_t &inst ) { - if (enable_ptx_file_line_stats) { - ptx_file_line_stats_commit_exposed_latency(shader->sid, 1); - } + return (inst.op == STORE_OP || inst.memory_op == memory_store); } -static int gpgpu_warp_occ_detailed = 0; -static int **warp_occ_detailed = NULL; - -void check_stage_pcs( shader_core_ctx_t *shader, unsigned stage ); -void check_pm_stage_pcs( shader_core_ctx_t *shader, unsigned stage ); +int is_const ( memory_space_t space ) +{ + return((space.get_type() == const_space) || (space == param_space_kernel)); +} -void shader_fetch( shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) +int is_local ( memory_space_t space ) { - assert(shader->model < NUM_SIMD_MODEL); - int n_warp_parts = warp_size/pipe_simd_width; + return (space == local_space) || (space == param_space_local); +} - // check if decode stage is stalled - int decode_stalled = 0; - for (int i = 0; i < pipe_simd_width; i++) { - if (shader->pipeline_reg[IF_ID][i].hw_thread_id != -1 ) - decode_stalled = 1; - } - if (shader->gpu_cycle % n_warp_parts == 0) { +void shader_core_ctx::ptx_exec_inst( inst_t &inst ) +{ + m_thread[inst.hw_thread_id].m_functional_model_thread_state->ptx_exec_inst(inst); + if( inst.callback.function != NULL ) + m_warp[inst.hw_thread_id/m_config->warp_size].inc_n_atomic(); + if (is_local(inst.space.get_type()) && (is_load(inst) || is_store(inst))) + inst.memreqaddr = translate_local_memaddr(inst.memreqaddr, inst.hw_thread_id, m_gpu->num_shader()); +} - switch (shader->model) { - case NO_RECONVERGE: - shader_fetch_simd_no_reconverge(shader, shader_number, grid_num ); - break; - case POST_DOMINATOR: - shader_fetch_simd_postdominator(shader, shader_number, grid_num); - break; - case MIMD: - shader_fetch_mimd(shader, shader_number); - break; - case DWF: - shader_fetch_simd_dwf(shader, shader_number); - break; - default: - fprintf(stderr, "Unknown scheduler: %d\n", shader->model); - assert(0); - break; - } +void shader_core_ctx::issue_warp( const inst_t *pI, unsigned active_mask, inst_t *&warp, unsigned warp_id ) +{ + m_warp[warp_id].ibuffer_free(); + assert(pI->valid()); + unsigned cta_id = (unsigned)-1; + for ( unsigned t=0; t < m_config->warp_size; t++ ) { + unsigned tid=m_config->warp_size*warp_id+t; + warp[t] = *pI; + warp[t].warp_active_mask = active_mask; + if( active_mask & (1<<t) ) { + cta_id = m_thread[tid].m_cta_id; + warp[t].hw_thread_id = tid; + warp[t].wlane = t; + warp[t].uid = g_next_shader_inst_uid++; + warp[t].issue_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + ptx_exec_inst( warp[t] ); + if ( ptx_thread_done(tid) ) { + m_warp[warp_id].inc_n_completed(); + m_warp[warp_id].ibuffer_flush(); + } + } + } + assert( cta_id != (unsigned)-1 ); + if( pI->op == BARRIER_OP ) + set_at_barrier(cta_id,warp_id); + else if( pI->op == MEMORY_BARRIER_OP ) + set_at_memory_barrier(warp_id); + m_pdom_warp[warp_id]->pdom_update_warp_mask(); + m_scoreboard->reserveRegisters(warp_id, pI); + m_warp[warp_id].set_next_pc(pI->pc + pI->isize); - static int *tid_out = NULL; - if (!tid_out) { - tid_out = (int*) malloc(sizeof(int) * warp_size); - } - memset(tid_out, -1, sizeof(int)*warp_size); + ///// + memset(m_fetch_tid_out, -1, sizeof(int)*m_config->warp_size); + int n_thd_in_warp = 0; + for (unsigned i=0; i<m_config->warp_size; i++) { + m_fetch_tid_out[i] = warp[i].hw_thread_id; + if (m_fetch_tid_out[i] >= 0) + n_thd_in_warp += 1; + } - if (!shader_cycle_distro) { - shader_cycle_distro = (unsigned int*) calloc(warp_size + 3, sizeof(unsigned int)); - } + m_new_warp_TS = false; - if (gpgpu_no_divg_load && shader->new_warp_TS && !decode_stalled) { - int n_thd_in_warp = 0; - address_type pc_out = 0xDEADBEEF; - for (unsigned i=0; i<warp_size; i++) { - tid_out[i] = shader->pipeline_reg[TS_IF][i].hw_thread_id; - if (tid_out[i] >= 0) { - n_thd_in_warp += 1; - pc_out = shader->pipeline_reg[TS_IF][i].pc; + // warp tracker keeps track of warps in the pipeline, let it know we are going to issue this warp + assert( n_thd_in_warp > 0 ); + m_warp_tracker->wpt_register_warp(m_fetch_tid_out, pI->pc, n_thd_in_warp,m_config->warp_size); +} + +void shader_core_ctx::decode_new() +{ + for ( unsigned i=0; i < m_config->max_warps_per_shader; i++ ) { + unsigned warp_id = (m_last_warp_issued+1+i) % m_config->max_warps_per_shader; + unsigned checked=0; + unsigned issued=0; + while( !m_warp[warp_id].waiting() && !m_warp[warp_id].ibuffer_empty() && (checked < 2) && (issued < 2) ) { + unsigned active_mask = m_pdom_warp[warp_id]->get_active_mask(); + const inst_t *pI = m_warp[warp_id].ibuffer_next(); + unsigned pc,rpc; + m_pdom_warp[warp_id]->get_pdom_stack_top_info(&pc,&rpc); + if( pI ) { + if( pc != pI->pc ) { + // control hazard + m_warp[warp_id].set_next_pc(pc); + m_warp[warp_id].ibuffer_flush(); + } else if ( !m_scoreboard->checkCollision(warp_id, pI) ) { + assert( m_warp[warp_id].inst_in_pipeline() ); + if ( (pI->op != SFU_OP) && pipeline_regster_empty(m_pipeline_reg[ID_OC]) ) { + issue_warp(pI, active_mask, m_pipeline_reg[ID_OC], warp_id); + issued++; + } else if ( (pI->op == SFU_OP || pI->op == ALU_SFU_OP) && pipeline_regster_empty(m_pipeline_reg[ID_OC_SFU]) ) { + issue_warp(pI, active_mask, m_pipeline_reg[ID_OC_SFU], warp_id); + issued++; + } + } } - } + m_warp[warp_id].ibuffer_step(); + checked++; + } + if ( issued ) { + m_last_warp_issued=warp_id; + break; + } + } +} + +void shader_core_ctx::fetch() +{ + // check if decode stage is stalled + bool decode_stalled = !pipeline_regster_empty( m_pipeline_reg[IF_ID] ); - //wpt_register_warp(tid_out, shader); - get_warp_tracker_pool().wpt_register_warp(tid_out, shader, pc_out); + // find a ready warp and put it in the TS_IF pipeline register + switch (m_config->model) { + case POST_DOMINATOR: fetch_simd_postdominator(); break; + case DWF: fetch_simd_dwf(); break; + case MIMD: fetch_mimd(); break; + default: fprintf(stderr, "Unknown scheduler: %d\n", m_config->model); assert(0); break; + } - if (gpu_runtime_stat_flag & GPU_RSTAT_DWF_MAP) { - track_thread_pc( shader->sid, tid_out, pc_out ); - } - if (gpgpu_cflog_interval != 0) { - insn_warp_occ_log( shader->sid, pc_out, n_thd_in_warp); - shader_warp_occ_log( shader->sid, n_thd_in_warp); - } - if ( gpgpu_warpdistro_shader < 0 || shader->sid == gpgpu_warpdistro_shader ) { - shader_cycle_distro[n_thd_in_warp + 2] += 1; - if (n_thd_in_warp == 0) { - if (shader->pending_mem_access == 0) shader_cycle_distro[1]++; - } - } - shader->new_warp_TS = 0; + memset(m_fetch_tid_out, -1, sizeof(int)*m_config->warp_size); - if (enable_ptx_file_line_stats && n_thd_in_warp > 0) { - //ptx_file_line_stats_add_warp_issued(pc_out); - //ptx_file_line_stats_add_warp_occ_total(pc_out, n_thd_in_warp); + if (m_config->gpgpu_no_divg_load && m_new_warp_TS && !decode_stalled) { + + // count number of active threads in this warp, determine PC value + // record active threads in tid_out + int n_thd_in_warp = 0; + address_type pc_out = 0xDEADBEEF; + for (unsigned i=0; i<m_config->warp_size; i++) { + m_fetch_tid_out[i] = m_pipeline_reg[TS_IF][i].hw_thread_id; + if (m_fetch_tid_out[i] >= 0) { + n_thd_in_warp += 1; + pc_out = m_pipeline_reg[TS_IF][i].pc; } + } - if ( gpgpu_warp_occ_detailed && - n_thd_in_warp && (shader->model == POST_DOMINATOR) ) { - int n_warp = gpu_n_thread_per_shader / warp_size; - if (!warp_occ_detailed) { - warp_occ_detailed = (int**) malloc(sizeof(int*) * gpu_n_shader * n_warp); - warp_occ_detailed[0] = (int*) calloc(sizeof(int), gpu_n_shader * n_warp * warp_size); - for (unsigned i = 0; i < n_warp * gpu_n_shader; i++) { - warp_occ_detailed[i] = warp_occ_detailed[0] + i * warp_size; - } - } + m_new_warp_TS = false; - int wid = -1; - for (unsigned i=0; i<warp_size; i++) { - if (tid_out[i] >= 0) wid = tid_out[i] / warp_size; - } - assert(wid != -1); - warp_occ_detailed[shader->sid * n_warp + wid][n_thd_in_warp - 1] += 1; + // warp tracker keeps track of warps in the pipeline, let it know we are going to issue this warp + if( n_thd_in_warp > 0 ) + m_warp_tracker->wpt_register_warp(m_fetch_tid_out, pc_out, n_thd_in_warp,m_config->warp_size); - if (shader->sid == 0 && wid == 16 && 0) { - printf("wtrace[%08x] ", pc_out); - for (unsigned i=0; i<warp_size; i++) { - printf("%03d ", tid_out[i]); - } - printf("\n"); - } - } - } else { - if ( gpgpu_warpdistro_shader < 0 || shader->sid == gpgpu_warpdistro_shader ) { - shader_cycle_distro[0] += 1; - } + // some statistics collection + if (gpu_runtime_stat_flag & GPU_RSTAT_DWF_MAP) + m_thread_pc_tracker->set_threads_pc( m_fetch_tid_out, pc_out ); + if (gpgpu_cflog_interval != 0) { + insn_warp_occ_log( m_sid, pc_out, n_thd_in_warp); + shader_warp_occ_log( m_sid, n_thd_in_warp); + } + if ( m_config->gpgpu_warpdistro_shader < 0 || m_sid == (unsigned)m_config->gpgpu_warpdistro_shader ) { + m_stats->shader_cycle_distro[n_thd_in_warp + 2] += 1; + if (n_thd_in_warp == 0) + if (m_pending_mem_access == 0) + m_stats->shader_cycle_distro[1]++; + } + } else { + if ( m_config->gpgpu_warpdistro_shader < 0 || m_sid == (unsigned)m_config->gpgpu_warpdistro_shader ) { + m_stats->shader_cycle_distro[0] += 1; } + } - if (!decode_stalled) { - for (unsigned i = 0; i < warp_size; i++) { - int tid_tsif = shader->pipeline_reg[TS_IF][i].hw_thread_id; - address_type pc_out = shader->pipeline_reg[TS_IF][i].pc; - cflog_update_thread_pc(shader->sid, tid_tsif, pc_out); - } + if (!decode_stalled) { + for (unsigned i = 0; i < m_config->warp_size; i++) { + int tid_tsif = m_pipeline_reg[TS_IF][i].hw_thread_id; + address_type pc_out = m_pipeline_reg[TS_IF][i].pc; + cflog_update_thread_pc(m_sid, tid_tsif, pc_out); } + } - if (enable_ptx_file_line_stats && !decode_stalled) { - int TS_stage_empty = 1; - for (unsigned i = 0; i < warp_size; i++) { - if (shader->pipeline_reg[TS_IF][i].hw_thread_id >= 0) { - TS_stage_empty = 0; - break; - } - } - if (TS_stage_empty) { - report_exposed_memory_latency(shader); + if (enable_ptx_file_line_stats && !decode_stalled) { + int TS_stage_empty = 1; + for (unsigned i = 0; i < m_config->warp_size; i++) { + if (m_pipeline_reg[TS_IF][i].hw_thread_id >= 0) { + TS_stage_empty = 0; + break; } } + if (TS_stage_empty) { + if (enable_ptx_file_line_stats) + ptx_file_line_stats_commit_exposed_latency(m_sid, 1); + } } // if not, send the warp part to decode stage - if (!decode_stalled && shader->warp_part2issue < n_warp_parts) { - check_stage_pcs(shader,TS_IF); - for (int i = 0; i < pipe_simd_width; i++) { - int wlane_idx = shader->warp_part2issue * pipe_simd_width + i; - shader->pipeline_reg[IF_ID][i] = shader->pipeline_reg[TS_IF][wlane_idx]; - shader->pipeline_reg[IF_ID][i].if_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; - shader->pipeline_reg[TS_IF][wlane_idx] = nop_inst; - } - shader->warp_part2issue += 1; + if (!decode_stalled) { + check_stage_pcs(TS_IF); + inst_t *fvi = first_valid_thread(m_pipeline_reg[TS_IF]); + if( fvi ) + m_warp[fvi->hw_thread_id/m_config->warp_size].set_last_fetch(gpu_sim_cycle); + move_warp(m_pipeline_reg[IF_ID],m_pipeline_reg[TS_IF]); } } -inline int is_load ( op_type op ) { - return op == LOAD_OP; -} - -inline int is_store ( op_type op ) { - return op == STORE_OP; -} - -inline int is_tex ( memory_space_t space ) { - return((space) == tex_space); -} - -inline int is_const ( memory_space_t space ) { - return((space.get_type() == const_space) || (space == param_space_kernel)); -} - -inline int is_local ( memory_space_t space ) { - return (space == local_space) || (space == param_space_local); -} - -inline int is_param ( memory_space_t space ) { - return (space == param_space_kernel); -} - -inline int is_shared ( memory_space_t space ) { - return((space) == shared_space); -} - -inline int shmem_bank ( address_type addr ) { - return((int)(addr/((address_type)WORD_SIZE)) % gpgpu_n_shmem_bank); -} - -inline int cache_bank ( address_type addr, shader_core_ctx_t *shader ) { - return(int)( addr >> (address_type)shader->L1cache->line_sz_log2 ) & ( gpgpu_n_cache_bank - 1 ); -} - -inline address_type coalesced_segment(address_type addr, unsigned segment_size_lg2bytes) +address_type coalesced_segment(address_type addr, unsigned segment_size_lg2bytes) { return (addr >> segment_size_lg2bytes); } - -inline address_type translate_local_memaddr(address_type localaddr, shader_core_ctx_t *shader, int tid) +address_type shader_core_ctx::translate_local_memaddr(address_type localaddr, int tid, unsigned num_shader ) { // During functional execution, each thread sees its own memory space for local memory, but these // need to be mapped to a shared address space for timing simulation. We do that mapping here. - localaddr -= 0x100; localaddr /=4; - if (gpgpu_local_mem_map) { + if (m_config->gpgpu_local_mem_map) { // Dnew = D*nTpC*nCpS*nS + nTpC*C + T%nTpC // C = S + nS*(T/nTpC) // D = data index; T = thread; C = CTA; S = shader core; p = per // keep threads in a warp contiguous // then distribute across memory space by CTAs from successive shader cores first, // then by successive CTA in same shader core - localaddr *= gpu_padded_cta_size * gpu_max_cta_per_shader * gpu_n_shader; - localaddr += gpu_padded_cta_size * (shader->sid + gpu_n_shader * (tid / gpu_padded_cta_size)); - localaddr += tid % gpu_padded_cta_size; + localaddr *= m_config->gpu_padded_cta_size * m_config->gpu_max_cta_per_shader * num_shader; + localaddr += m_config->gpu_padded_cta_size * (m_sid + num_shader * (tid / m_config->gpu_padded_cta_size)); + localaddr += tid % m_config->gpu_padded_cta_size; } else { // legacy mapping that maps the same address in the local memory space of all threads // to a single contiguous address region - localaddr *= gpu_n_shader * gpu_n_thread_per_shader; - localaddr += (gpu_n_thread_per_shader*shader->sid) + tid; + localaddr *= num_shader * m_config->n_thread_per_shader; + localaddr += (m_config->n_thread_per_shader *m_sid) + tid; } localaddr *= 4; - localaddr += 0x100; return localaddr; } ///////////////////////////////////////////////////////////////////////////////////////// -// Register Bank Conflict Structures - -bool gpgpu_reg_bank_conflict_model; - -#define MAX_REG_BANKS 32 -unsigned int gpgpu_num_reg_banks; // this needs to be less than MAX_REG_BANKS -bool gpgpu_reg_bank_use_warp_id; - -#define MAX_BANK_CONFLICT 8 /* tex can have four source and four destination regs */ - -class reg_bank_access { -public: - reg_bank_access():tot(0),rd(0),wr(0){ - for (unsigned i = 0; i < 4; i++) rd_regs[i] = -1; - } - unsigned tot; - unsigned rd; - unsigned wr; - int rd_regs[4]; -}; - -int register_bank(int regnum, int tid) -{ - int bank = regnum; - if (gpgpu_reg_bank_use_warp_id) - bank += tid >> 5/*log2(warp_size)*/; - return bank % gpgpu_num_reg_banks; -} - -reg_bank_access g_reg_bank_access[MAX_REG_BANKS]; - -// just to use as "shorthand" for clearing accesses each cycle -static const struct reg_bank_access empty_reg_bank_access; - -unsigned int gpu_reg_bank_conflict_stalls = 0; -void shader_opnd_collect_read(shader_core_ctx_t* shader) +void shader_core_ctx::decode() { - const int prevstage = ID_OC; - shader->m_opndcoll_new.step(shader->pipeline_reg[prevstage]); -} - -void shader_opnd_collect_write(shader_core_ctx_t* shader) -{ - shader->m_opndcoll_new.writeback(shader->pipeline_reg[WB_RT]); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void shader_decode( shader_core_ctx_t *shader, - unsigned int shader_number, - unsigned int grid_num ) { - - address_type addr; - dram_callback_t callback; op_type op = NO_OP; - int tid; - int i1, i2, i3, i4, o1, o2, o3, o4; //4 outputs needed for texture fetches in cuda-sim - int i; - int touched_priority=0; - int warp_tid=0; - unsigned data_size; - memory_space_t space; - unsigned cycles; - int vectorin, vectorout; - int arch_reg[MAX_REG_OPERANDS] = { -1 }; - int pred; - int ar1, ar2; // address registers for memory operands + unsigned warp_id = -1; + unsigned cta_id = -1; + address_type regs_regs_PC = 0xDEADBEEF; address_type warp_current_pc = 0x600DBEEF; address_type warp_next_pc = 0x600DBEEF; int warp_diverging = 0; - const int nextstage = (gpgpu_operand_collector) ? ID_OC : \ - (shader->using_rrstage ? ID_RR : ID_EX); - unsigned warp_id = -1; - unsigned cta_id = -1; + const int nextstage = (m_config->gpgpu_operand_collector) ? ID_OC : \ + (m_config->m_using_dwf_rrstage ? ID_RR : ID_EX); - // stalling for register bank conflict - if ( gpgpu_reg_bank_conflict_model ) { - for (i=0; i<pipe_simd_width;i++) { - if ( shader->pipeline_reg[IF_ID][i].reg_bank_conflict_stall_checked ) { - if ( shader->pipeline_reg[IF_ID][i].reg_bank_access_pending > 0 ) { - assert( shader->pipeline_reg[IF_ID][i].reg_bank_access_pending <= 8 ); - shader->pipeline_reg[IF_ID][i].reg_bank_access_pending--; - gpu_reg_bank_conflict_stalls++; - return; - } - } - } - } - - for (i=0; i<pipe_simd_width;i++) { - if (shader->pipeline_reg[nextstage][i].hw_thread_id != -1 ) { - return; /* stalled */ - } - } + if( !pipeline_regster_empty(m_pipeline_reg[nextstage]) ) + return; - check_stage_pcs(shader,IF_ID); + check_stage_pcs(IF_ID); // decode the instruction int first_valid_thread = -1; - for (i=0; i<pipe_simd_width;i++) { - - if (shader->pipeline_reg[IF_ID][i].hw_thread_id == -1 ) - continue; /* bubble */ - - /* get the next instruction to execute from fetch stage */ - tid = shader->pipeline_reg[IF_ID][i].hw_thread_id; + for (unsigned i=0; i<m_config->warp_size;i++) { + if (m_pipeline_reg[IF_ID][i].hw_thread_id == -1 ) + continue; /* bubble or masked off */ if (first_valid_thread == -1) { first_valid_thread = i; - warp_id = tid/warp_size; - assert( !shader->warp_waiting_at_barrier(warp_id) ); - cta_id = shader->thread[tid].cta_id; - } - - if ( gpgpu_cuda_sim ) { - ptx_decode_inst( shader->thread[tid].ptx_thd_info, (unsigned*)&op, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg, &pred, &ar1, &ar2); - shader->pipeline_reg[IF_ID][i].op = op; - shader->pipeline_reg[IF_ID][i].pc = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); - shader->pipeline_reg[IF_ID][i].ptx_thd_info = shader->thread[tid].ptx_thd_info; - - } else { - abort(); - } - // put the info into the shader instruction structure - // - useful in tracking instruction dependency (not needed for now) - shader->pipeline_reg[IF_ID][i].in[0] = i1; - shader->pipeline_reg[IF_ID][i].in[1] = i2; - shader->pipeline_reg[IF_ID][i].in[2] = i3; - shader->pipeline_reg[IF_ID][i].in[3] = i4; - shader->pipeline_reg[IF_ID][i].out[0] = o1; - shader->pipeline_reg[IF_ID][i].out[1] = o2; - shader->pipeline_reg[IF_ID][i].out[2] = o3; - shader->pipeline_reg[IF_ID][i].out[3] = o4; - - } - - // checking for register bank conflict and stall accordingly - if ( gpgpu_reg_bank_conflict_model && - first_valid_thread != -1 && - !shader->pipeline_reg[first_valid_thread][IF_ID].reg_bank_conflict_stall_checked ) - { - for (i = 4; i < 8; i++) { - if( arch_reg[i] != -1 ) { - assert( arch_reg[i] >=0 ); - assert( gpgpu_num_reg_banks <= MAX_REG_BANKS ); - int skip = 0; - int bank = arch_reg[i] % gpgpu_num_reg_banks; - int opndreg = shader->pipeline_reg[first_valid_thread][IF_ID].in[i-4]; - assert(opndreg >= 0); - int j; - for (j = 0; j < 4; j++) { - if (g_reg_bank_access[bank].rd_regs[j] == -1) - break; - else if (g_reg_bank_access[bank].rd_regs[j] == opndreg) { - // two operands reading from same register in same bank, can be merged into a single read - skip = 1; - break; - } - } - if (!skip) { - g_reg_bank_access[bank].tot++; - g_reg_bank_access[bank].rd++; - g_reg_bank_access[bank].rd_regs[j] = opndreg; - } - } - } - - unsigned max_access=0; - inst_t* conflict_inst = &shader->pipeline_reg[first_valid_thread][IF_ID]; - for(unsigned r = 0; r < gpgpu_num_reg_banks; r++ ) { - if( g_reg_bank_access[r].tot > max_access ) - max_access = g_reg_bank_access[r].tot; - g_reg_bank_access[r] = empty_reg_bank_access; - } - if( max_access >= 1 ) { - assert( max_access <= MAX_REG_OPERANDS ); - conflict_inst->reg_bank_access_pending = max_access - 1; - if( max_access > 1 ) { - conflict_inst->reg_bank_conflict_stall_checked = 1; - return; // stall pipeline - } - } - shader->pipeline_reg[first_valid_thread][IF_ID].reg_bank_conflict_stall_checked = 1; + op = m_pipeline_reg[IF_ID][i].op; + int tid = m_pipeline_reg[IF_ID][i].hw_thread_id; + warp_id = tid/m_config->warp_size; + assert( !warp_waiting_at_barrier(warp_id) ); + cta_id = m_thread[tid].m_cta_id; + } } // execute the instruction functionally short last_hw_thread_id = -1; bool first_thread_in_warp = true; - for (i=0; i<pipe_simd_width;i++) { - if (shader->pipeline_reg[IF_ID][i].hw_thread_id == -1 ) - continue; /* bubble */ + for (unsigned i=0; i<m_config->warp_size;i++) { + if (m_pipeline_reg[IF_ID][i].hw_thread_id == -1 ) + continue; /* bubble or masked off */ if(last_hw_thread_id > -1) first_thread_in_warp = false; - last_hw_thread_id = shader->pipeline_reg[IF_ID][i].hw_thread_id; + last_hw_thread_id = m_pipeline_reg[IF_ID][i].hw_thread_id; /* get the next instruction to execute from fetch stage */ - tid = shader->pipeline_reg[IF_ID][i].hw_thread_id; - if ( gpgpu_cuda_sim ) { - int arch_reg[MAX_REG_OPERANDS]; - - // Decode instruction - ptx_decode_inst( shader->thread[tid].ptx_thd_info, (unsigned*)&op, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg, &pred, &ar1, &ar2 ); - - // Functionally execute instruction - ptx_exec_inst( shader->thread[tid].ptx_thd_info, &addr, &space, &data_size, &cycles, &callback, shader->pipeline_reg[IF_ID][i].warp_active_mask ); - - shader->pipeline_reg[IF_ID][i].callback = callback; - shader->pipeline_reg[IF_ID][i].space = space; - if (is_local(space) && (is_load(op) || is_store(op))) { - addr = translate_local_memaddr(addr, shader, tid); - } - shader->pipeline_reg[IF_ID][i].is_vectorin = vectorin; - shader->pipeline_reg[IF_ID][i].is_vectorout = vectorout; - shader->pipeline_reg[IF_ID][i].pred = pred; - shader->pipeline_reg[IF_ID][i].ar1 = ar1; - shader->pipeline_reg[IF_ID][i].ar2 = ar2; - shader->pipeline_reg[IF_ID][i].data_size = data_size; - shader->pipeline_reg[IF_ID][i].cycles = cycles; - - // Mark destination registers as write-pending in scoreboard - // Only do this for the first thread in warp - if(first_thread_in_warp) { - shader->scrb->reserveRegisters(warp_id, &(shader->pipeline_reg[IF_ID][i])); - //shader->scrb->printContents(); - } + int tid = m_pipeline_reg[IF_ID][i].hw_thread_id; - warp_current_pc = shader->pipeline_reg[IF_ID][i].pc; - memcpy( shader->pipeline_reg[IF_ID][i].arch_reg, arch_reg, sizeof(arch_reg) ); - regs_regs_PC = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); - } + // Functionally execute instruction + m_thread[tid].m_functional_model_thread_state->ptx_exec_inst( m_pipeline_reg[IF_ID][i] ); + if( m_pipeline_reg[IF_ID][i].callback.function != NULL ) + m_warp[warp_id].inc_n_atomic(); + if (is_local(m_pipeline_reg[IF_ID][i].space) && (is_load(m_pipeline_reg[IF_ID][i]) || is_store(m_pipeline_reg[IF_ID][i]))) + m_pipeline_reg[IF_ID][i].memreqaddr = translate_local_memaddr(m_pipeline_reg[IF_ID][i].memreqaddr, tid, m_gpu->num_shader()); - shader->pipeline_reg[IF_ID][i].memreqaddr = addr; - if ( op == LOAD_OP ) { - shader->pipeline_reg[IF_ID][i].inst_type = LOAD_OP; - } else if ( op == STORE_OP ) { - shader->pipeline_reg[IF_ID][i].inst_type = STORE_OP; - } + // Mark destination registers as write-pending in scoreboard + // Only do this for the first thread in warp + if(first_thread_in_warp) + m_scoreboard->reserveRegisters(warp_id, &(m_pipeline_reg[IF_ID][i])); + warp_current_pc = m_pipeline_reg[IF_ID][i].pc; + regs_regs_PC = next_pc( tid ); - if ( gpgpu_cuda_sim && ptx_thread_at_barrier( shader->thread[tid].ptx_thd_info ) ) { - if (shader->model == DWF) { - shader->thread[tid].m_waiting_at_barrier=1; - shader->thread[tid].m_reached_barrier=1; // not reset at barrier release, but at the issue after that - shader->warp[wid_from_hw_tid(tid,warp_size)].n_waiting_at_barrier++; - shader->waiting_at_barrier++; - int cta_uid = ptx_thread_get_cta_uid( shader->thread[tid].ptx_thd_info ); - dwf_hit_barrier( shader->sid, cta_uid ); + if ( ptx_thread_at_barrier( m_thread[tid].m_functional_model_thread_state ) ) { + if (m_config->model == DWF) { + m_thread[tid].m_waiting_at_barrier=true; + m_thread[tid].m_reached_barrier=true; // not reset at barrier release, but at the issue after that + m_warp[wid_from_hw_tid(tid,m_config->warp_size)].inc_waiting_at_barrier(); + int cta_uid = ptx_thread_get_cta_uid( m_thread[tid].m_functional_model_thread_state ); + dwf_hit_barrier( m_sid, cta_uid ); - int release = ptx_thread_all_at_barrier( shader->thread[tid].ptx_thd_info ); //test if all threads arrived at the barrier + int release = ptx_thread_all_at_barrier( m_thread[tid].m_functional_model_thread_state ); //test if all threads arrived at the barrier if ( release ) { //All threads arrived at barrier...releasing - int cta_uid = ptx_thread_get_cta_uid( shader->thread[tid].ptx_thd_info ); - for ( unsigned t=0; t < gpu_n_thread_per_shader; ++t ) { - if ( !ptx_thread_at_barrier( shader->thread[t].ptx_thd_info ) ) + int cta_uid = ptx_thread_get_cta_uid( m_thread[tid].m_functional_model_thread_state ); + for ( unsigned t=0; t < m_config->n_thread_per_shader; ++t ) { + if ( !ptx_thread_at_barrier( m_thread[t].m_functional_model_thread_state ) ) continue; - int other_cta_uid = ptx_thread_get_cta_uid( shader->thread[t].ptx_thd_info ); + int other_cta_uid = ptx_thread_get_cta_uid( m_thread[t].m_functional_model_thread_state ); if ( other_cta_uid == cta_uid ) { //reseting @barrier tracking info - shader->warp[wid_from_hw_tid(t,warp_size)].n_waiting_at_barrier=0; - shader->thread[t].m_waiting_at_barrier=0; - ptx_thread_reset_barrier( shader->thread[t].ptx_thd_info ); - shader->waiting_at_barrier--; + m_warp[wid_from_hw_tid(t,m_config->warp_size)].clear_waiting_at_barrier(); + m_thread[t].m_waiting_at_barrier=false; + ptx_thread_reset_barrier( m_thread[t].m_functional_model_thread_state ); } } - if (shader->model == DWF) { - dwf_release_barrier( shader->sid, cta_uid ); + if (m_config->model == DWF) { + dwf_release_barrier( m_sid, cta_uid ); } - ptx_thread_release_barrier( shader->thread[tid].ptx_thd_info ); + ptx_thread_release_barrier( m_thread[tid].m_functional_model_thread_state ); } } } else { - assert( !shader->thread[tid].m_waiting_at_barrier ); + assert( !m_thread[tid].m_waiting_at_barrier ); } - // put the info into the shader instruction structure - // - useful in tracking instruction dependency (not needed for now) - shader->pipeline_reg[IF_ID][i].in[0] = i1; - shader->pipeline_reg[IF_ID][i].in[1] = i2; - shader->pipeline_reg[IF_ID][i].in[2] = i3; - shader->pipeline_reg[IF_ID][i].in[3] = i4; - shader->pipeline_reg[IF_ID][i].out[0] = o1; - shader->pipeline_reg[IF_ID][i].out[1] = o2; - shader->pipeline_reg[IF_ID][i].out[2] = o3; - shader->pipeline_reg[IF_ID][i].out[3] = o4; - - // go to the next instruction - // - done implicitly in ptx_exec_inst() - // branch divergence detection if (warp_next_pc != regs_regs_PC) { if (warp_next_pc == 0x600DBEEF) { @@ -2003,21 +1528,16 @@ void shader_decode( shader_core_ctx_t *shader, warp_diverging = 1; } } - - // direct the instruction to the appropriate next stage (config dependent) - shader->pipeline_reg[nextstage][i] = shader->pipeline_reg[IF_ID][i]; - shader->pipeline_reg[nextstage][i].id_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; - shader->pipeline_reg[IF_ID][i] = nop_inst; } - if( op == BARRIER_OP ) { - shader->set_at_barrier(cta_id,warp_id); - } + move_warp(m_pipeline_reg[nextstage],m_pipeline_reg[IF_ID]); - if ( shader->model == NO_RECONVERGE && touched_priority ) { - update_max_branch_priority(shader,warp_tid,grid_num); - } - shader->n_diverge += warp_diverging; + if( op == BARRIER_OP ) + set_at_barrier(cta_id,warp_id); + else if( op == MEMORY_BARRIER_OP ) + set_at_memory_barrier(warp_id); + + m_n_diverge += warp_diverging; if (warp_diverging == 1) { assert(warp_current_pc != 0x600DBEEF); // guard against empty warp causing warp divergence ptx_file_line_stats_add_warp_divergence(warp_current_pc, 1); @@ -2036,451 +1556,381 @@ int regfile_hash(signed thread_number, unsigned simd_size, unsigned n_banks) { } } -int gpgpu_n_reg_banks = 8; -void shader_preexecute( shader_core_ctx_t *shader, - unsigned int shader_number ) { - int i; - static int *thread_warp = NULL; - int n_access_per_cycle = pipe_simd_width / gpgpu_n_reg_banks; - - if (!thread_warp) { - thread_warp = (int*) malloc(sizeof(int) * pipe_simd_width); - } +void shader_core_ctx::preexecute() +{ + if( m_config->gpgpu_dwf_reg_bankconflict) { + // Model register bank conflicts as in + // Fung et al. MICRO'07 / ACM TACO'09 papers. + // + // This models conflicts due to moving threads to different SIMD lanes + // (which occur if not using "lane aware" dynamic warp formation). - for (i=0; i<pipe_simd_width; i++) { - if (shader->pipeline_reg[RR_EX][i].hw_thread_id != -1 ) { - //stalled, but can still service a register read - if (shader->RR_k) { - shader->RR_k--; + inst_t *fvi = first_valid_thread(m_pipeline_reg[RR_EX]); + if( fvi ) { + if (m_dwf_RR_k) { + //stalled due to register access conflict, but can still service a register read + m_dwf_RR_k--; + return; } - return; // stalled - } - } - - // if there is still register read to service, stall - if (shader->RR_k > 1) { - shader->RR_k--; - return; - } - - // if RR_k == 1, it was stalled previously and the register read is now done - if (!shader->RR_k && gpgpu_reg_bankconflict) { - int max_reg_bank_acc = 0; - for (i=0; i<pipe_simd_width; i++) { - thread_warp[i] = 0; - } - for (i=0; i<pipe_simd_width; i++) { - if (shader->pipeline_reg[ID_RR][i].hw_thread_id != -1 ) - thread_warp[regfile_hash(shader->pipeline_reg[ID_RR][i].hw_thread_id, - warp_size, gpgpu_n_reg_banks)]++; - } - for (i=0; i<pipe_simd_width; i++) { - if (thread_warp[i] > max_reg_bank_acc ) { - max_reg_bank_acc = thread_warp[i]; + + int n_access_per_cycle = m_config->warp_size / m_config->gpgpu_dwf_rr_stage_n_reg_banks; + int max_reg_bank_acc = 0; + for (unsigned i=0; i<m_config->gpgpu_dwf_rr_stage_n_reg_banks; i++) + m_dwf_rrstage_bank_access_counter[i] = 0; + for (unsigned i=0; i<m_config->warp_size; i++) { + if (m_pipeline_reg[ID_RR][i].hw_thread_id != -1 ) + m_dwf_rrstage_bank_access_counter[regfile_hash(m_pipeline_reg[ID_RR][i].hw_thread_id, + m_config->warp_size, + m_config->gpgpu_dwf_rr_stage_n_reg_banks)]++; + } + for (unsigned i=0; i<m_config->gpgpu_dwf_rr_stage_n_reg_banks; i++) { + if (m_dwf_rrstage_bank_access_counter[i] > max_reg_bank_acc ) + max_reg_bank_acc = m_dwf_rrstage_bank_access_counter[i]; + } + // calculate the number of cycles needed for each register bank to fulfill all accesses + m_dwf_RR_k = (max_reg_bank_acc / n_access_per_cycle) + ((max_reg_bank_acc % n_access_per_cycle)? 1 : 0); + + // if there is more than one access cycle needed at a bank, stall + if (m_dwf_RR_k > 1) { + n_regconflict_stall++; + m_dwf_RR_k--; + return; } } - // calculate the number of cycles needed for each register bank to fulfill all accesses - shader->RR_k = (max_reg_bank_acc / n_access_per_cycle) + ((max_reg_bank_acc % n_access_per_cycle)? 1 : 0); - } - - // if there are more than one access cycle needed at a bank, stall - if (shader->RR_k > 1) { - n_regconflict_stall++; - shader->RR_k--; - return; - } - - check_stage_pcs(shader,ID_RR); - - shader->RR_k = 0; //setting RR_k to 0 to indicate RF conflict check next cycle - for (i=0; i<pipe_simd_width;i++) { - if (shader->pipeline_reg[ID_RR][i].hw_thread_id == -1 ) - continue; //bubble - shader->pipeline_reg[ID_EX][i] = shader->pipeline_reg[ID_RR][i]; - shader->pipeline_reg[ID_RR][i] = nop_inst; + + check_stage_pcs(ID_RR); + m_dwf_RR_k = 0; } + if( pipeline_regster_empty(m_pipeline_reg[ID_EX]) ) + move_warp(m_pipeline_reg[ID_EX],m_pipeline_reg[ID_RR]); } -void shader_execute_pipe( shader_core_ctx_t *shader, unsigned int shader_number, unsigned pipeline, unsigned next_stage ) +void shader_core_ctx::execute_pipe( unsigned pipeline, unsigned next_stage ) { - int i; - for (i=0; i<pipe_simd_width; i++) { - if (gpgpu_pre_mem_stages) { - if (shader->pre_mem_pipeline[0][i].hw_thread_id != -1 ) { - return; // stalled - } - } else { - if (shader->pipeline_reg[next_stage][i].hw_thread_id != -1 ) - return; // stalled - } + if (m_config->gpgpu_pre_mem_stages) { + if( !pipeline_regster_empty(pre_mem_pipeline[0]) ) + return; // stalled + } else { + if( !pipeline_regster_empty(m_pipeline_reg[next_stage]) ) + return; // stalled } - - check_stage_pcs(shader,ID_EX); + + check_stage_pcs(ID_EX); // Check that all threads have the same delay cycles unsigned cycles = -1; - for (i=0; i<pipe_simd_width; i++) { - if (shader->pipeline_reg[pipeline][i].hw_thread_id == -1 ) + for (unsigned i=0; i<m_config->warp_size; i++) { + if (m_pipeline_reg[pipeline][i].hw_thread_id == -1 ) continue; // bubble if(cycles == (unsigned)-1) - cycles = shader->pipeline_reg[pipeline][i].cycles; + cycles = m_pipeline_reg[pipeline][i].cycles; else { - if( cycles != shader->pipeline_reg[pipeline][i].cycles ) { - printf("Shader %d: threads do not have the same delay cycles.\n", shader->sid); + if( cycles != m_pipeline_reg[pipeline][i].cycles ) { + printf("Shader %d: threads do not have the same delay cycles.\n", m_sid); assert(0); } } } - for (i=0; i<pipe_simd_width; i++) { - if (shader->pipeline_reg[pipeline][i].hw_thread_id == -1 ) - continue; // bubble - - // Stall based on delay cycles - shader->pipeline_reg[pipeline][i].cycles--; - if( shader->pipeline_reg[pipeline][i].cycles > 0 ) + bool stall_inst_not_done = false; + for (unsigned i=0; i<m_config->warp_size; i++) { + if (m_pipeline_reg[pipeline][i].hw_thread_id == -1 ) + continue; + m_pipeline_reg[pipeline][i].cycles--; + if( m_pipeline_reg[pipeline][i].cycles > 0 ) { + // Stall here to model instruction throughput for different types of instructions + stall_inst_not_done=true; continue; - - if (gpgpu_pre_mem_stages) { - shader->pre_mem_pipeline[0][i] = shader->pipeline_reg[pipeline][i]; - shader->pre_mem_pipeline[0][i].ex_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; - } else { - shader->pipeline_reg[next_stage][i] = shader->pipeline_reg[pipeline][i]; - shader->pipeline_reg[next_stage][i].ex_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; } - shader->pipeline_reg[pipeline][i] = nop_inst; - } - - if (!gpgpu_pre_mem_stages) { + } + if( stall_inst_not_done ) + return; + if (m_config->gpgpu_pre_mem_stages) { + move_warp(pre_mem_pipeline[0], m_pipeline_reg[pipeline]); + } else { + move_warp(m_pipeline_reg[next_stage],m_pipeline_reg[pipeline]); // inform memory stage that a new instruction has arrived - shader->shader_memory_new_instruction_processed = 0; + m_shader_memory_new_instruction_processed = false; } } -void shader_execute( shader_core_ctx_t *shader, unsigned int shader_number ) +void shader_core_ctx::execute() { - shader_execute_pipe(shader,shader_number, OC_EX_SFU, EX_MM); - shader_execute_pipe(shader,shader_number, ID_EX, EX_MM); + execute_pipe(OC_EX_SFU, EX_MM); + execute_pipe(ID_EX, EX_MM); } -void shader_pre_memory( shader_core_ctx_t *shader, - unsigned int shader_number ) { - int i,j; - - - for (j = gpgpu_pre_mem_stages; j > 0; j--) { - for (i=0; i<pipe_simd_width; i++) { - if (shader->pre_mem_pipeline[j][i].hw_thread_id != -1 ) { - return; - } - } - check_pm_stage_pcs(shader,j-1); - for (i=0; i<pipe_simd_width; i++) { - shader->pre_mem_pipeline[j][i] = shader->pre_mem_pipeline[j - 1][i]; - shader->pre_mem_pipeline[j - 1][i] = nop_inst; - } +void shader_core_ctx::pre_memory() +{ + // This stage can be used to approximately model a deeper pipeline. + // The main effect this models is the register read-after-write delay. + // We walk through pre-memory stages in reverse order + // (highest number = stage closest to writeback, 0 = stage closest to fetch + if( pipeline_regster_empty(m_pipeline_reg[EX_MM]) ) { + move_warp( m_pipeline_reg[EX_MM], pre_mem_pipeline[m_config->gpgpu_pre_mem_stages] ); + // inform memory stage that a new instruction has arrived + m_shader_memory_new_instruction_processed = false; } - check_pm_stage_pcs(shader,gpgpu_pre_mem_stages); - for (i=0;i<pipe_simd_width ;i++ ) - shader->pipeline_reg[EX_MM][i] = shader->pre_mem_pipeline[gpgpu_pre_mem_stages][i]; - - // inform memory stage that a new instruction has arrived - shader->shader_memory_new_instruction_processed = 0; - - if (gpgpu_pre_mem_stages) { - for (i=0; i<pipe_simd_width; i++) - shader->pre_mem_pipeline[0][i] = nop_inst; + for (unsigned j = m_config->gpgpu_pre_mem_stages; j > 0; j--) { + if( pipeline_regster_empty(pre_mem_pipeline[j]) ) + move_warp( pre_mem_pipeline[j], pre_mem_pipeline[j-1]); } } -int gpgpu_coalesce_arch; - -enum memory_path { - NO_MEM_PATH = 0, - SHARED_MEM_PATH, - GLOBAL_MEM_PATH, - TEXTURE_MEM_PATH, - CONSTANT_MEM_PATH, - NUM_MEM_PATHS //not a mem path -}; - -static unsigned next_access_uid = 0; - -class mem_access_t{ -public: - mem_access_t(): uid(next_access_uid++),addr(0),req_size(0),order(0),_quarter_count_all(0),warp_indices(),space(undefined_space),path(NO_MEM_PATH),isatomic(false),cache_hit(false),cache_checked(false),recheck_cache(false),iswrite(false),need_wb(false),wb_addr(0),reserved_mshr(NULL){}; - bool operator<(const mem_access_t &other) const {return (order > other.order);}//this is reverse - unsigned uid; - address_type addr; //address of the segment to load. - unsigned req_size; //bytes - unsigned order; // order of accesses, based on banks. - union{ - unsigned _quarter_count_all; - char quarter_count[4]; //access counts to each quarter of segment, for compaction; - }; - std::vector<unsigned> warp_indices; //warp indicies for this request. - memory_space_t space; - memory_path path; - bool isatomic; - bool cache_hit; - bool cache_checked; - bool recheck_cache; - bool iswrite; - bool need_wb; - address_type wb_addr; //address to wb too if necessary. - mshr_entry_t* reserved_mshr; -}; - -mshr_entry_t* mshr_shader_unit::add_mshr(mem_access_t &access, inst_t* warp) +mshr_entry* mshr_shader_unit::add_mshr(mem_access_t &access, inst_t* warp) { - static unsigned next_request_uid = 1; - mshr_entry_t* mshr = alloc_free_mshr(is_tex(access.space)); - //note no constructor was called, all entries must be reinitialized! - mshr->request_uid = next_request_uid++; - mshr->status = INITIALIZED; - mshr->addr = access.addr; - mshr->mf = NULL; - mshr->merged_on_other_reqest = false; - mshr->merged_requests =NULL; - mshr->iswrite = access.iswrite; - assert(access.warp_indices.size()); //code assumes at least one instruction attached to mshr. - for (unsigned i = 0; i < access.warp_indices.size(); i++) { - mshr->insts.push_back(warp[access.warp_indices[i]]); - } - mshr->islocal = is_local(access.space); - mshr->isconst = is_const(access.space); - mshr->istexture = is_tex(access.space); - if (gpgpu_interwarp_mshr_merge) { - mshr_entry_t* mergehit = m_mshr_lookup.shader_get_mergeable_mshr(mshr); - if (mergehit) { - //merge this request; - mergehit->merged_requests = mshr; - mshr->merged_on_other_reqest = true; - if (mergehit->fetched()) mshr_return_from_mem(mshr); - } - m_mshr_lookup.mshr_fast_lookup_insert(mshr); - } - return mshr; + //creates an mshr based on the access struct information + mshr_entry* mshr = alloc_free_mshr(access.space == tex_space); + mshr->init(access.addr,access.iswrite,access.space,warp->hw_thread_id/m_shader_config->warp_size); + assert(access.warp_indices.size()); //code assumes at least one instruction attached to mshr. + for (unsigned i = 0; i < access.warp_indices.size(); i++) + mshr->add_inst(warp[access.warp_indices[i]]); + if (m_shader_config->gpgpu_interwarp_mshr_merge) { + mshr_entry* mergehit = m_mshr_lookup.shader_get_mergeable_mshr(mshr); + if (mergehit) { + mergehit->merge(mshr); + if (mergehit->fetched()) + mshr_return_from_mem(mshr); + } + m_mshr_lookup.mshr_fast_lookup_insert(mshr); + } + return mshr; } - -inline address_type line_size_based_tag_func(address_type address, unsigned line_size) +address_type line_size_based_tag_func(address_type address, unsigned line_size) { + //gives the tag for an address based on a given line size return ((address) & (~((address_type)line_size - 1))); } -inline address_type null_tag_func(address_type address, unsigned line_size){ +address_type null_tag_func(address_type address, unsigned line_size) +{ return address; //no modification: each address is its own tag. Equivalent to line_size_based_tag_func(address,1), but line_size ignored. } // only 1 bank -inline int null_bank_func(address_type add, unsigned line_size) +int shader_core_ctx::null_bank_func(address_type add, unsigned line_size) { return 1; } -inline int shmem_bank_func(address_type add, unsigned line_size) +int shader_core_ctx::shmem_bank_func(address_type addr, unsigned line_size) { - return shmem_bank(add); + //returns the integer number of the physical bank addr would go in. + return ((int)(addr/((address_type)WORD_SIZE)) % m_config->gpgpu_n_shmem_bank); } -inline int dcache_bank_func(address_type add, unsigned line_size) +int shader_core_ctx::dcache_bank_func(address_type add, unsigned line_size) { - if (gpgpu_no_dl1) return 1; //no banks - else return (add / line_size) & (gpgpu_n_cache_bank - 1); + //returns the integer number of the physical bank addr would go in. + if (m_config->gpgpu_no_dl1) return 1; //no banks + else return (add / line_size) & (m_config->gpgpu_n_cache_bank - 1); } -#include <bitset> -void check_accessq( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq ){ - std::bitset<32> check = 0; - for (unsigned i = 0; i < accessq.size(); i++) { - if (shader) { - std::cout << shader->sid << ":" << i << " space " << accessq[i].space.get_type() << " " << gpu_sim_cycle << std::endl; - assert(accessq[i].space == shader->pipeline_reg[EX_MM][accessq[i].warp_indices[0]].space); - } - for (unsigned j = 0; j < accessq[i].warp_indices.size(); j++) { - if (check[accessq[i].warp_indices[j]]) { - std::cout << "OOOPS" << std::endl; //good line for breakpoint - }else{check[accessq[i].warp_indices[j]] = true;} - } - } -} +typedef int (shader_core_ctx::*bank_func_t)(address_type add, unsigned line_size); +typedef address_type (*tag_func_t)(address_type add, unsigned line_size); -// This speciallized function calculates the list of independant memory accesses, sorted by access order -// Acesses to same tag line are coalesced. -// will neither coalesce nor overlap bank accesses accross warp parts. -template < int (*bank_func)(address_type add, unsigned line_size), address_type (*tag_func)(address_type add, unsigned line_size) > -inline void get_memory_access_list(inst_t* insns, unsigned char* paths, memory_path path, unsigned warp_parts, unsigned line_size, bool limit_broadcast,std::vector<mem_access_t> &accessq) +void shader_core_ctx::get_memory_access_list( + shader_core_ctx::bank_func_t bank_func, + tag_func_t tag_func, + memory_pipe_t mem_pipe, + unsigned warp_parts, + unsigned line_size, + bool limit_broadcast, + std::vector<mem_access_t> &accessq ) { - // calculates the memory accesses for a generic cache with banks and tags. - // can be used for coalesescing + const inst_t* insns = m_pipeline_reg[EX_MM]; + // Calculates memory accesses generated by this warp + // Returns acesses which are "coalesced" + // Does not coalesce nor overlap bank accesses across warp "parts". - //tracks bank accesses for sorting into generations; - static std::map<unsigned,unsigned> bank_accs; - bank_accs.clear(); - //keep track of broadcasts with unique orders if limit_broadcast - //the normally calculated orders will never be greater than pipe_simd_width; - unsigned broadcast_order = pipe_simd_width; + // This is called once per warp when it enters the memory stage. + // It takes the warp and produces a queue of accesses that can be peformed. + // These are then performed over multiple cycles (stalling the pipeline) if the accessses cannot be + // performed all at once. + // It is a convenience for simulation; in hardware the warp would be processed each cycle + // until it was done. Each cycle would do the first accesses available to it and mark off the + // those threads served by those accesses. + + // Because it calculates all the accesses at once, what follows is largely not as the hw would do it. + // Accesses are assigned an order number based on when that access may be issued. + // Accesses with the same order number may occur at the same time: they are to different banks. + // Later when the queue is processed it will evaluate accesses of + // as many orders as ports on that cache/shmem. + // These accesses are placed into a queue and sorted so that accesses of the same order are next to each other. + + + // tracks bank accesses for sorting into generations; + // each entry is (bank #, number of accesses) + // the idea is that you can only access a bank a number of times each cycle equal to + // its number of ports in one cycle. + std::map<unsigned,unsigned> bank_accs; + + //keep track of broadcasts with unique orders if limit_broadcast + //the normally calculated orders will never be greater than warp_size + unsigned broadcast_order = m_config->warp_size; unsigned qbegin = accessq.size(); unsigned qpartbegin = qbegin; - unsigned mem_pipe_size = pipe_simd_width / warp_parts; - for (unsigned part = 0; part < (unsigned)pipe_simd_width; part += mem_pipe_size) { + unsigned mem_pipe_size = m_config->warp_size / warp_parts; + for (unsigned part = 0; part < m_config->warp_size; part += mem_pipe_size) { for (unsigned i = part; i < part + mem_pipe_size; i++) { - if (paths[i] != path) continue; //skip instructions from other memory paths - address_type segment = (*tag_func)(insns[i].memreqaddr, line_size); - unsigned quarter=0; - if ( line_size>=4 ) { - quarter = (insns[i].memreqaddr / (line_size/4)) & 3; - } - //check if we are already loading this segment. + if ( insns[i].hw_thread_id == -1 ) + continue; + + if( insns[i].space == undefined_space ) { + // Instruction must have been predicated off + continue; + } + + address_type lane_segment_address = tag_func(insns[i].memreqaddr, line_size); + unsigned quarter = 0; + if( line_size>=4 ) + quarter = (insns[i].memreqaddr / (line_size/4)) & 3; bool isatomic = (insns[i].callback.function != NULL); - unsigned match = 0; + bool match = false; if (not isatomic) { //atomics must have own request for (unsigned j = qpartbegin; j < accessq.size(); j++) { - if (segment == accessq[j].addr and not accessq[j].isatomic) { - //match + if (lane_segment_address == accessq[j].addr) { + assert( not accessq[j].isatomic ); accessq[j].quarter_count[quarter]++; accessq[j].warp_indices.push_back(i); - if (limit_broadcast) accessq[j].order = ++broadcast_order; //do proadcast in its own cycle. - match = 1; + if (limit_broadcast) + // two threads access this address, so its a broadcast. + accessq[j].order = ++broadcast_order; //do broadcast in its own cycle. + match = true; break; } } } - if (!match) { - //needs its own request - accessq.push_back(mem_access_t()); - accessq.back().addr = segment; - accessq.back().space = insns[i].space; - accessq.back().path = path; - accessq.back().isatomic = isatomic; - accessq.back().iswrite = is_store(insns[i].op); - accessq.back().req_size = line_size; - accessq.back().quarter_count[quarter]++; - accessq.back().warp_indices.push_back(i); - - //Determine Bank Conflicts. - unsigned bank = (*bank_func)(insns[i].memreqaddr, line_size); - //ensure no concurrent bank access accross warp parts. + if (!match) { // does not match an previous request by another thread, so need a new request + assert( insns[i].space != undefined_space ); + accessq.push_back( mem_access_t( lane_segment_address, + insns[i].space, + mem_pipe, + isatomic, + is_store(insns[i]), + line_size, quarter, i) ); + // Determine Bank Conflicts: + unsigned bank = (this->*bank_func)(insns[i].memreqaddr, line_size); + // ensure no concurrent bank access accross warp parts. // ie. order will be less than part for all previous loads in previous parts, so: - if (bank_accs[bank] < part) bank_accs[bank]=part; + if (bank_accs[bank] < part) + bank_accs[bank]=part; accessq.back().order = bank_accs[bank]; bank_accs[bank]++; } } qpartbegin = accessq.size(); //don't coalesce accross warp parts } - //sort requests into order accorting to order (orders will not necessarily be consequtive if multiple parts) - std::stable_sort(accessq.begin()+qbegin,accessq.end()); //this is a reverse sort, least order last, but doesn't really matter where consumed. -} + //sort requests into order according to order (orders will not necessarily be consequtive if multiple parts) + std::stable_sort(accessq.begin()+qbegin,accessq.end()); +} -void shader_memory_shared_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +void shader_core_ctx::memory_shared_process_warp() { - get_memory_access_list<&shmem_bank_func, &null_tag_func>(shader->pipeline_reg[EX_MM], paths, SHARED_MEM_PATH, - gpgpu_shmem_pipe_speedup, + // initial processing of shared memory warps + get_memory_access_list(&shader_core_ctx::shmem_bank_func, + null_tag_func, + SHARED_MEM_PATH, + m_config->gpgpu_shmem_pipe_speedup, 1, //shared memory doesn't care about line_size, needs to be at least 1; - true, //limit broadcasts to single cycle. - accessq); - //thats it :) + true, // limit broadcasts to single cycle. + m_memory_queue.shared); } -void shader_memory_const_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +void shader_core_ctx::memory_const_process_warp() { + // initial processing of const memory warps + std::vector<mem_access_t> &accessq = m_memory_queue.constant; unsigned qbegin = accessq.size(); - get_memory_access_list<&null_bank_func, &line_size_based_tag_func>(shader->pipeline_reg[EX_MM], paths, CONSTANT_MEM_PATH, - 1, //warp parts - shader->L1constcache->line_sz, - false, //no broadcast limit. - accessq); - //do cache checks here for each request, could be done later for more accurate timing of cache accesses, but probably uneccesary; + get_memory_access_list( + &shader_core_ctx::null_bank_func, + line_size_based_tag_func, + CONSTANT_MEM_PATH, + 1, //warp parts + m_L1C->line_sz, false, //no broadcast limit. + accessq); + //do cache checks here for each request (non-physical), could be done later for more accurate timing of cache accesses, but probably uneccesary; for (unsigned i = qbegin; i < accessq.size(); i++) { - if (is_param(accessq[i].space)) { + if ( accessq[i].space == param_space_kernel ) { accessq[i].cache_hit = true; } else { - cache_request_status status = shd_cache_access_wb(shader->L1constcache, + cache_request_status status = shd_cache_access_new(m_L1C, accessq[i].addr, WORD_SIZE, //this field is ingored. 0, //should always be a read - shader->gpu_cycle, + gpu_sim_cycle+gpu_tot_sim_cycle, NULL/*should never writeback*/); accessq[i].cache_hit = (status == HIT); - if (gpgpu_perfect_mem) accessq[i].cache_hit = true; - if (accessq[i].cache_hit) L1_const_miss++; + if (m_config->gpgpu_perfect_mem) accessq[i].cache_hit = true; + if (accessq[i].cache_hit) m_stats->L1_const_miss++; } accessq[i].cache_checked = true; } } -void shader_memory_texture_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +void shader_core_ctx::memory_texture_process_warp() { + // initial processing of shared texture warps + std::vector<mem_access_t> &accessq = m_memory_queue.texture; unsigned qbegin = accessq.size(); - get_memory_access_list<&null_bank_func, &line_size_based_tag_func>(shader->pipeline_reg[EX_MM], paths, TEXTURE_MEM_PATH, + get_memory_access_list(&shader_core_ctx::null_bank_func, + &line_size_based_tag_func, + TEXTURE_MEM_PATH, 1, //warp parts - shader->L1texcache->line_sz, + m_L1T->line_sz, false, //no broadcast limit. accessq); - //do cache checks here for each request, could be done later for more accurate timing of cache accesses, but probably uneccesary; + //do cache checks here for each request (non-hardware), could be done later for more accurate timing of cache accesses, but probably uneccesary; for (unsigned i = qbegin; i < accessq.size(); i++) { - cache_request_status status = shd_cache_access_wb(shader->L1texcache, + cache_request_status status = shd_cache_access_new(m_L1T, accessq[i].addr, WORD_SIZE, //this field is ignored. 0, //should always be a read - shader->gpu_cycle, + gpu_sim_cycle+gpu_tot_sim_cycle, NULL /*should never writeback*/); accessq[i].cache_hit = (status == HIT); - if (gpgpu_perfect_mem) accessq[i].cache_hit = true; - if (accessq[i].cache_hit) L1_texture_miss++; + if (m_config->gpgpu_perfect_mem) accessq[i].cache_hit = true; + if (accessq[i].cache_hit) m_stats->L1_texture_miss++; accessq[i].cache_checked = true; } } -void shader_memory_global_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +void shader_core_ctx::memory_global_process_warp() { + std::vector<mem_access_t> &accessq = m_memory_queue.local_global; unsigned qbegin = accessq.size(); unsigned warp_parts = 1; - unsigned line_size = shader->L1cache->line_sz; - if (gpgpu_coalesce_arch == 13) { + unsigned line_size = m_L1D->line_sz; + if (m_config->gpgpu_coalesce_arch == 13) { warp_parts = 2; - if(gpgpu_no_dl1) { - int valindex = -1; - for (int i = 0; i < pipe_simd_width; i++) { - if (paths[i] == GLOBAL_MEM_PATH) { - valindex = i; - break; - } - } - assert(valindex != -1); + if(m_config->gpgpu_no_dl1) { + unsigned data_size = first_valid_thread( m_pipeline_reg[EX_MM] )->data_size; // line size is dependant on instruction; - //assume first valid thread instruction is the same as the rest. - switch (shader->pipeline_reg[EX_MM][valindex].data_size) { - case 1: - line_size = 32; - break; - case 2: - line_size = 64; - break; - case 4: - case 8: - case 16: - line_size = 128; - break; - default: - assert(0); + switch (data_size) { + case 1: line_size = 32; break; + case 2: line_size = 64; break; + case 4: case 8: case 16: line_size = 128; break; + default: assert(0); } } } - get_memory_access_list<&dcache_bank_func, &line_size_based_tag_func>(shader->pipeline_reg[EX_MM], paths, GLOBAL_MEM_PATH, - warp_parts, //warp parts - line_size, - false, //no broadcast limit. - accessq); + get_memory_access_list( &shader_core_ctx::dcache_bank_func, + &line_size_based_tag_func, + GLOBAL_MEM_PATH, + warp_parts, + line_size, + false, + accessq); + // Now that we have the accesses, if we don't have a cache we can adjust request sizes to + // include only the data referenced by the threads for (unsigned i = qbegin; i < accessq.size(); i++) { - if (gpgpu_coalesce_arch == 13 and gpgpu_no_dl1) { + if (m_config->gpgpu_coalesce_arch == 13 and m_config->gpgpu_no_dl1) { //if there is no l1 cache it makes sense to do coalescing here. //reduce memory request sizes. char* quarter_counts = accessq[i].quarter_count; @@ -2506,242 +1956,195 @@ void shader_memory_global_process_inst(shader_core_ctx_t * shader, unsigned char -mem_stage_stall_type send_mem_request(shader_core_ctx_t *shader, mem_access_t &access){ - inst_t* warp = shader->pipeline_reg[EX_MM]; +mem_stage_stall_type shader_core_ctx::send_mem_request(mem_access_t &access) +{ + //Atempt to send an request/write to memory based on information in access. + + inst_t* warp = m_pipeline_reg[EX_MM]; inst_t* req_head = warp + access.warp_indices[0]; + // If the cache told us it needed to write back a dirty line, do this now + // It is possible to do this writeback in the same cycle as the access request, this may not be realistic. if (access.need_wb) { - //fill out and send a writeback - unsigned req_size = shader->L1cache->line_sz + WRITE_PACKET_SIZE; - if (!(shader->fq_has_buffer(access.wb_addr, req_size, true, shader->sid))) { - gpu_stall_sh2icnt++; + unsigned req_size = m_L1D->line_sz + WRITE_PACKET_SIZE; + if ( ! m_gpu->fq_has_buffer(access.wb_addr, req_size, true, m_sid) ) { + m_stats->gpu_stall_sh2icnt++; return WB_ICNT_RC_FAIL; } - - shader->fq_push( access.wb_addr, - req_size, - true, NO_PARTIAL_WRITE, shader->sid, -1, NULL, - 0, - is_local(access.space)?LOCAL_ACC_W:GLOBAL_ACC_W, //space of cache line is same as new request + fq_push( access.wb_addr, req_size, true, NO_PARTIAL_WRITE, -1, NULL, 0, + is_local(access.space)?LOCAL_ACC_W:GLOBAL_ACC_W, //space of cache line is same as new request -1); - L1_writeback++; + m_stats->L1_writeback++; access.need_wb = false; } - bool requires_mshr = (shader->model != MIMD) and (not access.iswrite); - - //this decoding here might belong elsewhere unsigned code; - mem_access_type access_type; + mem_access_type access_type; switch(access.space.get_type()) { case const_space: - case param_space_kernel: - code = CONSTC; - access_type = CONST_ACC_R; - break; - case tex_space: - code = TEXTC; - access_type = TEXTURE_ACC_R; - break; - case global_space: - code = DCACHE; - access_type = (access.iswrite)? GLOBAL_ACC_W: GLOBAL_ACC_R; - break; + case param_space_kernel: code = CONSTC; access_type = CONST_ACC_R; break; + case tex_space: code = TEXTC; access_type = TEXTURE_ACC_R; break; + case global_space: code = DCACHE; access_type = (access.iswrite)? GLOBAL_ACC_W: GLOBAL_ACC_R; break; case local_space: - case param_space_local: - code = DCACHE; - access_type = (access.iswrite)? LOCAL_ACC_W: LOCAL_ACC_R; - break; - default: - assert(0); // NOT A MEM SPACE; - break; + case param_space_local: code = DCACHE; access_type = (access.iswrite)? LOCAL_ACC_W: LOCAL_ACC_R; break; + default: assert(0); break; } - //reserve mshr + bool requires_mshr = (m_config->model != MIMD) and (not access.iswrite); if (requires_mshr and not access.reserved_mshr) { - - // can allocate mshr? - if (not shader->mshr_unit->has_mshr(1)) { - //no mshr available; + if (not m_mshr_unit->has_mshr(1)) return MSHR_RC_FAIL; - } - - access.reserved_mshr = shader->mshr_unit->add_mshr(access, warp); + access.reserved_mshr = m_mshr_unit->add_mshr(access, warp); access.recheck_cache = false; //we have an mshr now, so have checked cache in same cycle as checking mshrs, so have merged if necessary. } - //require inct if access is this far without reserved mshr, or has and mshr but not merged with another request - bool requires_icnt = (not access.reserved_mshr) or (not access.reserved_mshr->merged_on_other_reqest); - + bool requires_icnt = (not access.reserved_mshr) or (not access.reserved_mshr->ismerged() ); if (requires_icnt) { - //calculate request size for icnt check (and later send); unsigned request_size = access.req_size; if (access.iswrite) { - if (requires_mshr) { - //needs information for a load back into cache. - request_size += READ_PACKET_SIZE + WRITE_MASK_SIZE; - } else { - //plain write - request_size += WRITE_PACKET_SIZE + WRITE_MASK_SIZE; - } + if (requires_mshr) + request_size += READ_PACKET_SIZE + WRITE_MASK_SIZE; // needs information for a load back into cache. + else + request_size += WRITE_PACKET_SIZE + WRITE_MASK_SIZE; //plain write } - - - // can allocate icnt? - //unsigned char fq_has_buffer(unsigned long long int addr, int bsize, bool write, int sid); - if (!(shader->fq_has_buffer(access.addr, request_size, access.iswrite, shader->sid))) { - gpu_stall_sh2icnt++; - //std::cout<< "failed to push " << request_size << " bytes" << std::endl; + if ( !m_gpu->fq_has_buffer(access.addr, request_size, access.iswrite, m_sid) ) { + // can't allocate icnt + m_stats->gpu_stall_sh2icnt++; return ICNT_RC_FAIL; } - //send over interconnect - - unsigned cache_hits_waiting = 0; //fixme do we really want to be passing this in? - + unsigned cache_hits_waiting = 0; partial_write_mask_t write_mask = NO_PARTIAL_WRITE; + unsigned warp_id = req_head->hw_thread_id/m_config->warp_size; if (access.iswrite) { + if (!strcmp("GT200",m_config->pipeline_model) ) + m_warp[warp_id].inc_store_req(); for (unsigned i=0;i < access.warp_indices.size();i++) { unsigned w = access.warp_indices[i]; int data_offset = warp[w].memreqaddr & ((unsigned long long int)access.req_size - 1); for (unsigned b = data_offset; b < data_offset + warp[w].data_size; b++) write_mask.set(b); } - if (write_mask.count() != access.req_size) { - gpgpu_n_partial_writes++; - } + if (write_mask.count() != access.req_size) + m_stats->gpgpu_n_partial_writes++; } - - //typedef unsigned char (*fq_push_t)(unsigned long long int addr, int bsize, unsigned char readwrite, - // unsigned long long int partial_write_mask, - // int sid, int wid, mshr_entry* mshr, int cache_hits_waiting, - // enum mem_access_type mem_acc, address_type pc); - shader->fq_push( access.addr, - request_size, - access.iswrite, write_mask, shader->sid, req_head->hw_thread_id/warp_size, access.reserved_mshr, - cache_hits_waiting, access_type, req_head->pc); - + fq_push( access.addr, request_size, + access.iswrite, write_mask, warp_id , access.reserved_mshr, + cache_hits_waiting, access_type, req_head->pc); } - - //book keeping for mshr since this request is done (sent/accounted for) at this point; + // book keeping for mshr : this request is done (sent/accounted for) if (requires_mshr) { - for (unsigned i = 0; i < access.warp_indices.size(); i++) { unsigned o = access.warp_indices[i]; - shader->pending_mem_access++; - inflight_memory_insn_add(shader, &warp[o]); - -#if 0 //old stats - if (i > 0) { //maintain old stats (yes/no?) - shader->thread[warp[o].hw_thread_id].n_l1_mrghit_ac++; - shd_cache_mergehit(shader->L1texcache, warp[o].memreqaddr); //fixme; - } -#endif + m_pending_mem_access++; + if (enable_ptx_file_line_stats) + ptx_file_line_stats_add_inflight_memory_insn(m_sid, warp[o].pc); } // Scoreboard addition: do not make cache miss instructions wait for memory, // let the scoreboard handle stalling of instructions. // Mark thread as a cache miss - if (not access.iswrite) { // set the pipeline instructions in this request to noops, they all wait for memory; for (unsigned i = 0; i < access.warp_indices.size(); i++) { unsigned o = access.warp_indices[i]; - //shader->pipeline_reg[EX_MM][o] = nop_inst; - shader->pipeline_reg[EX_MM][o].cache_miss = true; + m_pipeline_reg[EX_MM][o].cache_miss = true; } } - } - return NO_RC_FAIL; } -bool shader_memory_shared_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, - mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ +bool shader_core_ctx::memory_shared_cycle( mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type) +{ + // Process a single cycle of activity from the shared memory queue. + + std::vector<mem_access_t> &accessq = m_memory_queue.shared; //consume port number orders from the top of the queue; - for (unsigned i = 0; i < (unsigned) gpgpu_shmem_port_per_bank; i++) { - if (accessq.empty()) break; + for (int i = 0; i < m_config->gpgpu_shmem_port_per_bank; i++) { + if (accessq.empty()) + break; unsigned current_order = accessq.back().order; //consume all requests of the same order (concurrent bank requests) - while ((not accessq.empty()) and accessq.back().order == current_order) accessq.pop_back(); + while ((not accessq.empty()) and accessq.back().order == current_order) + accessq.pop_back(); } if (not accessq.empty()) { rc_fail = BK_CONF; fail_type = S_MEM; - gpgpu_n_shmem_bkconflict++; + m_stats->gpgpu_n_shmem_bkconflict++; } return accessq.empty(); //done if empty. } -//generic memory access queue processing, accessq must be sorted by order -//--that is, requests of similar order are expected to be contiguous in the queueu. -//if you want to use this for shared memory, make sure they are marked as cashe hits (not default) -// cycle_exec may be called multiple times if memory fails. typically used for cache checks -template < mem_stage_stall_type (*cycle_exec)(shader_core_ctx_t*, mem_access_t&) > -inline mem_stage_stall_type shader_memory_generic_process_queue( shader_core_ctx_t *shader, - unsigned ports_per_bank, unsigned memory_send_max, - std::vector<mem_access_t> &accessq ){ - mem_stage_stall_type rc_fail = NO_RC_FAIL; - // number of requests to sent to memory this cycle - unsigned mem_req_count = 0; - //consume port number orders from the top of the queue; +mem_stage_stall_type shader_core_ctx::process_memory_access_queue( shader_core_ctx::cache_check_t cache_check, + unsigned ports_per_bank, + unsigned memory_send_max, + std::vector<mem_access_t> &accessq ) +{ + // Generic algorithm for processing a single cycle of accesses for the memory space types that go to L2 or DRAM. + + // precondition: accessq sorted by mem_access_t::order + mem_stage_stall_type hazard_cond = NO_RC_FAIL; + unsigned mem_req_count = 0; // number of requests to sent to memory this cycle for (unsigned i = 0; i < ports_per_bank; i++) { - if (accessq.empty()) break; + if (accessq.empty()) + break; unsigned current_order = accessq.back().order; - //consume all requests of the same order (concurrent bank requests) - //stop when things that go to memory exceed a per cycle limit. - while ((not accessq.empty()) and accessq.back().order == current_order and rc_fail == NO_RC_FAIL) { - rc_fail = (*cycle_exec)(shader, accessq.back()); - if (rc_fail != NO_RC_FAIL) break; //can't complete this request this cycle. + // consume all requests of the same "order" but stop if we hit a structural hazard + while ((not accessq.empty()) and accessq.back().order == current_order and hazard_cond == NO_RC_FAIL) { + hazard_cond = (this->*cache_check)(accessq.back()); + if (hazard_cond != NO_RC_FAIL) + break; // can't complete this request this cycle. if (not accessq.back().cache_hit){ if (mem_req_count < memory_send_max) { mem_req_count++; - rc_fail = send_mem_request(shader, accessq.back()); //try to get mshr, icnt, send; + hazard_cond = send_mem_request(accessq.back()); // attemp to get mshr, icnt, send; } - else { - rc_fail = COAL_STALL; //not really a coal stall, its a too many memory request stall; - } - if (rc_fail != NO_RC_FAIL) break; //can't complete this request this cycle. + else hazard_cond = COAL_STALL; // not really a coal stall, its a too many memory request stall; + if ( hazard_cond != NO_RC_FAIL) break; //can't complete this request this cycle. } accessq.pop_back(); } } - if (not accessq.empty() and rc_fail == NO_RC_FAIL) { + if (not accessq.empty() and hazard_cond == NO_RC_FAIL) { //no resource failed so must be a bank comflict. - rc_fail = BK_CONF; + hazard_cond = BK_CONF; } - return rc_fail; + return hazard_cond; } -mem_stage_stall_type ccache_check(shader_core_ctx_t *shader, mem_access_t& access){ /*done in process queue*/ return NO_RC_FAIL;} +bool shader_core_ctx::memory_constant_cycle( mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type) +{ + // Process a single cycle of activity from the the constant memory queue. -bool shader_memory_constant_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, - mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ + std::vector<mem_access_t> &accessq = m_memory_queue.constant; - mem_stage_stall_type fail = shader_memory_generic_process_queue<ccache_check>( shader, gpgpu_const_port_per_bank, - 1, //memory send max per cycle - accessq ); + mem_stage_stall_type fail = process_memory_access_queue(&shader_core_ctx::ccache_check, + m_config->gpgpu_const_port_per_bank, + 1, //memory send max per cycle + accessq ); if (fail != NO_RC_FAIL){ rc_fail = fail; //keep other fails if this didn't fail. fail_type = C_MEM; if (rc_fail == BK_CONF or rc_fail == COAL_STALL) { - gpgpu_n_cmem_portconflict++; //coal stalls aren't really a bank conflict, but this maintains previous behavior. + m_stats->gpgpu_n_cmem_portconflict++; //coal stalls aren't really a bank conflict, but this maintains previous behavior. } } return accessq.empty(); //done if empty. } -mem_stage_stall_type tcache_check(shader_core_ctx_t *shader, mem_access_t& access){ /*done in process queue*/ return NO_RC_FAIL;} - -bool shader_memory_texture_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, - mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ +bool shader_core_ctx::memory_texture_cycle( mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type) +{ + // Process a single cycle of activity from the the texture memory queue. - mem_stage_stall_type fail = shader_memory_generic_process_queue<tcache_check>(shader, 1, //how is tex memory banked? - 1, //memory send max per cycle - accessq ); + std::vector<mem_access_t> &accessq = m_memory_queue.texture; + mem_stage_stall_type fail = process_memory_access_queue(&shader_core_ctx::tcache_check, + 1, //how is tex memory banked? + 1, //memory send max per cycle + accessq ); if (fail != NO_RC_FAIL){ rc_fail = fail; //keep other fails if this didn't fail. fail_type = T_MEM; @@ -2750,15 +2153,20 @@ bool shader_memory_texture_cycle( shader_core_ctx_t *shader, std::vector<mem_acc } -mem_stage_stall_type dcache_check(shader_core_ctx_t *shader, mem_access_t& access){ - if (access.cache_checked and not access.recheck_cache) return NO_RC_FAIL; - if (!gpgpu_no_dl1 && !gpgpu_perfect_mem) { +mem_stage_stall_type shader_core_ctx::dcache_check(mem_access_t& access) +{ + // Global memory (data cache) checks the cache for each access at the time it is processed. + // This is more accurate to hardware, and necessary for proper action of the writeback cache. + + if (access.cache_checked and not access.recheck_cache) + return NO_RC_FAIL; + if (!m_config->gpgpu_no_dl1 && !m_config->gpgpu_perfect_mem) { //check cache - cache_request_status status = shd_cache_access_wb(shader->L1cache, + cache_request_status status = shd_cache_access_new(m_L1D, access.addr, WORD_SIZE, //this field is ignored. access.iswrite, - shader->gpu_cycle, + gpu_sim_cycle+gpu_tot_sim_cycle, &access.wb_addr); if (status == RESERVATION_FAIL) { access.cache_checked = false; @@ -2771,10 +2179,10 @@ mem_stage_stall_type dcache_check(shader_core_ctx_t *shader, mem_access_t& acces //write has hit a reserved cache line //it has writen its data into the cache line, so no need to go to memory access.cache_hit = true; - L1_write_hit_on_miss++; + m_stats->L1_write_hit_on_miss++; // here we would search the MSHRs for the originating read, // and mask off the writen bytes, so they are not overwritten in the cache when it comes back - // --- don't actually do this since we are pretending. + // --- don't actually do this since we don't functionally execute based upon values in cache // MSHR will still forward the unmasked value to its dependant reads. // if doing stall on use, must stall this thread after this write (otherwise, inproper values may be forwarded to future reads). } @@ -2788,404 +2196,217 @@ mem_stage_stall_type dcache_check(shader_core_ctx_t *shader, mem_access_t& acces access.cache_hit = false; } - if (gpgpu_perfect_mem) access.cache_hit = true; + if (m_config->gpgpu_perfect_mem) access.cache_hit = true; - //atomics always go to memory if (access.isatomic) { - if (!gpgpu_perfect_mem) { - access.cache_hit = false; - } else { - //unless perfect mem, in which case, the callback can only be done here - dram_callback_t &atom_exec = shader->pipeline_reg[EX_MM][access.warp_indices[0]].callback; + if (m_config->gpgpu_perfect_mem) { + // complete functional execution of atomic here + dram_callback_t &atom_exec = m_pipeline_reg[EX_MM][access.warp_indices[0]].callback; atom_exec.function(atom_exec.instruction, atom_exec.thread); + } else { + // atomics always go to memory + access.cache_hit = false; } } if (!access.cache_hit) { - if (access.iswrite) L1_write_miss++; - else L1_read_miss++; + if (access.iswrite) m_stats->L1_write_miss++; + else m_stats->L1_read_miss++; } return NO_RC_FAIL; } -bool shader_memory_global_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, - mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ - mem_stage_stall_type fail = shader_memory_generic_process_queue<&dcache_check>(shader, gpgpu_cache_port_per_bank, - 1, //memory send max per cycle - accessq ); - if (fail != NO_RC_FAIL) { - rc_fail = fail; //keep other fails if this didn't fail. - //need to determine load/store, local/global: - bool iswrite = accessq.back().iswrite; - if (is_local(accessq.back().space)) { - fail_type = (iswrite)?L_MEM_ST:L_MEM_LD; - } else { - fail_type = (iswrite)?G_MEM_ST:G_MEM_LD; - } +bool shader_core_ctx::memory_cycle( mem_stage_stall_type &stall_reason, mem_stage_access_type &access_type) +{ + // Process a single cycle of activity from the the global/local memory queue. - if (rc_fail == BK_CONF or rc_fail == COAL_STALL) { - gpgpu_n_cache_bkconflict++; - } - } - return accessq.empty(); //done if empty. -} + std::vector<mem_access_t> &accessq = m_memory_queue.local_global; + mem_stage_stall_type stall_cond = process_memory_access_queue(&shader_core_ctx::dcache_check, m_config->gpgpu_cache_port_per_bank, 1, accessq); -inline void mem_instruction_stats(inst_t* warp){ - //there must be a better way to count these - for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { - if (warp[i].hw_thread_id == -1) continue; //bubble - //this breaks some encapsulation: the is_[space] functions, if you change those, change this. - bool store = is_store(warp[i].op); - switch (warp[i].space.get_type()) { - case undefined_space: - case reg_space: - break; - case shared_space: - gpgpu_n_shmem_insn++; - break; - case const_space: - gpgpu_n_const_insn++; - break; - case param_space_kernel: - case param_space_local: - gpgpu_n_param_insn++; - break; - case tex_space: - gpgpu_n_tex_insn++; - break; - case global_space: - case local_space: - if (store){ - gpgpu_n_store_insn++; - } else { - gpgpu_n_load_insn++; - } - break; - default: - abort(); - } + if (stall_cond != NO_RC_FAIL) { + stall_reason = stall_cond; + bool iswrite = accessq.back().iswrite; + if (is_local(accessq.back().space)) + access_type = (iswrite)?L_MEM_ST:L_MEM_LD; + else + access_type = (iswrite)?G_MEM_ST:G_MEM_LD; + if (stall_cond == BK_CONF or stall_cond == COAL_STALL) + m_stats->gpgpu_n_cache_bkconflict++; } + return accessq.empty(); //done if empty. } -struct shader_queues_t{ - std::vector<mem_access_t> shared; - std::vector<mem_access_t> constant; - std::vector<mem_access_t> texture; - std::vector<mem_access_t> global; -}; - -void shader_memory_queue(shader_core_ctx_t *shader, shader_queues_t *accessqs) +void shader_core_ctx::memory_queue() { - //classify memory according to type; - static unsigned char *path = NULL; - if (!path) path = (unsigned char*)malloc(pipe_simd_width * sizeof(unsigned char)); - memset(path, 0, pipe_simd_width * sizeof(unsigned char)); - //static std::vector<char> path; - //path.clear(); path.resize(p, NO_MEM_PATH); - - static unsigned type_counts[NUM_MEM_PATHS]; - memset(type_counts, 0, sizeof(type_counts)); - //static std::vector<unsigned> type_counts; - //type_counts.clear(); type_counts.resize(NUM_MEM_PATHS); - - for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { - if (shader->pipeline_reg[EX_MM][i].hw_thread_id == -1) continue; //bubble - //this breaks some encapsulation: the is_[space] functions; if you change those, change this. - switch (shader->pipeline_reg[EX_MM][i].space.get_type()) { - case shared_space: - path[i] = SHARED_MEM_PATH; - type_counts[SHARED_MEM_PATH]++; - break; - case const_space: - case param_space_kernel: - path[i] = CONSTANT_MEM_PATH; - type_counts[CONSTANT_MEM_PATH]++; - break; - case tex_space: - path[i] = TEXTURE_MEM_PATH; - type_counts[TEXTURE_MEM_PATH]++; - break; - case global_space: - case local_space: - case param_space_local: - path[i] = GLOBAL_MEM_PATH; - type_counts[GLOBAL_MEM_PATH]++; - break; - case param_space_unclassified: - abort(); // todo: define access details - break; - default: - break; - } - } - - //instruction counting: - mem_instruction_stats(shader->pipeline_reg[EX_MM]); - - - if (type_counts[SHARED_MEM_PATH]) shader_memory_shared_process_inst(shader, path, accessqs->shared); - if (type_counts[CONSTANT_MEM_PATH]) shader_memory_const_process_inst(shader, path, accessqs->constant); - if (type_counts[TEXTURE_MEM_PATH]) shader_memory_texture_process_inst(shader, path, accessqs->texture); - if (type_counts[GLOBAL_MEM_PATH]) shader_memory_global_process_inst(shader, path, accessqs->global); + // Called once per warp when warp enters memory stage. + // Generates a list of memory accesses, but does not perform the memory access. + if( pipeline_regster_empty(m_pipeline_reg[EX_MM]) ) + return; + m_gpu->mem_instruction_stats(m_pipeline_reg[EX_MM]); + inst_t *inst = first_valid_thread(m_pipeline_reg[EX_MM]); + switch (inst->space.get_type()) { + case shared_space: memory_shared_process_warp(); break; + case tex_space: memory_texture_process_warp(); break; + case const_space: case param_space_kernel: memory_const_process_warp(); break; + case global_space: case local_space: case param_space_local: memory_global_process_warp(); break; + case param_space_unclassified: abort(); break; + default: break; // non-memory operations + } } - -void shader_memory( shader_core_ctx_t *shader, unsigned int shader_number ) +void shader_core_ctx::memory() { - enum mem_stage_stall_type rc_fail = NO_RC_FAIL; // resource allocation - - //these should be local to the shader structure but can't because it is included in non c++ files. - //so provide static storage for it here - static std::vector<shader_queues_t> shader_memory_queues; - if (shader_memory_queues.size() == 0) { - shader_memory_queues.resize(gpu_n_shader); - for (unsigned i = 0; i < gpu_n_shader; i++) { - shader_memory_queues[i].shared.reserve(pipe_simd_width); - shader_memory_queues[i].constant.reserve(pipe_simd_width); - shader_memory_queues[i].texture.reserve(pipe_simd_width); - shader_memory_queues[i].global.reserve(pipe_simd_width); - } - } - shader_queues_t *accessqs = &(shader_memory_queues[shader->sid]); - - if (shader->shader_memory_new_instruction_processed == 0) { - shader->shader_memory_new_instruction_processed = 1; //only do this once per pipeline occupant - shader_memory_queue(shader, accessqs); + if (!m_shader_memory_new_instruction_processed) { + m_shader_memory_new_instruction_processed = true; // do once per warp instruction + memory_queue(); } - bool done = true; + enum mem_stage_stall_type rc_fail = NO_RC_FAIL; mem_stage_access_type type; + done &= memory_shared_cycle(rc_fail, type); + done &= memory_constant_cycle(rc_fail, type); + done &= memory_texture_cycle(rc_fail, type); + done &= memory_cycle(rc_fail, type); - done &= shader_memory_shared_cycle(shader, accessqs->shared, rc_fail, type); - done &= shader_memory_constant_cycle(shader, accessqs->constant, rc_fail, type); - done &= shader_memory_texture_cycle(shader, accessqs->texture, rc_fail, type); - done &= shader_memory_global_cycle(shader, accessqs->global, rc_fail, type); - - //wb stalled? - int wb_stalled = 0; // check if next stage is stalled - for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { - if (shader->pipeline_reg[MM_WB][i].hw_thread_id != -1 ) { - wb_stalled = 1; - break; - } - } - - if (!done) { + if (!done) { // log stall types and return assert(rc_fail != NO_RC_FAIL); - //log stall types - gpu_stall_shd_mem++; - gpu_stall_shd_mem_breakdown[type][rc_fail]++; - } - - if (!done or wb_stalled) return; - - // this memory stage is done and not stalled by wb - // pipeline forward - - check_stage_pcs(shader,EX_MM); - // and pass instruction from EX_MM to MM_WB - for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { - if (shader->pipeline_reg[EX_MM][i].hw_thread_id == -1 ) - continue; // bubble - shader->pipeline_reg[MM_WB][i] = shader->pipeline_reg[EX_MM][i]; - shader->pipeline_reg[MM_WB][i].mm_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; - shader->pipeline_reg[EX_MM][i] = nop_inst; - } - - // reflect the change to EX|MM pipeline register to the pre_mem stage - if (gpgpu_pre_mem_stages) { - check_stage_pcs(shader,EX_MM); - for (unsigned i=0;i< (unsigned)pipe_simd_width ;i++ ) - shader->pre_mem_pipeline[gpgpu_pre_mem_stages][i] = shader->pipeline_reg[EX_MM][i]; + m_stats->gpu_stall_shd_mem++; + m_stats->gpu_stall_shd_mem_breakdown[type][rc_fail]++; + return; } + if( not pipeline_regster_empty( m_pipeline_reg[MM_WB] ) ) + return; // writeback stalled + check_stage_pcs(EX_MM); + move_warp(m_pipeline_reg[MM_WB],m_pipeline_reg[EX_MM]); } -int writeback_l1_miss =0 ; - - -void register_cta_thread_exit(shader_core_ctx_t *shader, int tid ) +void shader_core_ctx::register_cta_thread_exit(int tid ) { - if (gpgpu_cuda_sim && gpgpu_spread_blocks_across_cores) { - unsigned padded_cta_size = ptx_sim_cta_size(); - if (padded_cta_size%warp_size) { - padded_cta_size = ((padded_cta_size/warp_size)+1)*(warp_size); - } - int cta_num = tid/padded_cta_size; - assert( shader->cta_status[cta_num] > 0 ); - shader->cta_status[cta_num]--; - if (!shader->cta_status[cta_num]) { - shader->n_active_cta--; - shader->deallocate_barrier(cta_num); - shader_CTA_count_unlog(shader->sid, 1); - printf("GPGPU-Sim uArch: Shader %d finished CTA #%d (%lld,%lld)\n", shader->sid, cta_num, gpu_sim_cycle, gpu_tot_sim_cycle ); - } + shader_core_ctx *shader = this; + unsigned padded_cta_size = m_gpu->the_kernel().threads_per_cta(); + if (padded_cta_size%m_config->warp_size) + padded_cta_size = ((padded_cta_size/m_config->warp_size)+1)*(m_config->warp_size); + int cta_num = tid/padded_cta_size; + assert( shader->m_cta_status[cta_num] > 0 ); + shader->m_cta_status[cta_num]--; + if (!shader->m_cta_status[cta_num]) { + shader->m_n_active_cta--; + shader->deallocate_barrier(cta_num); + shader_CTA_count_unlog(shader->m_sid, 1); + printf("GPGPU-Sim uArch: Shader %d finished CTA #%d (%lld,%lld)\n", shader->m_sid, cta_num, gpu_sim_cycle, gpu_tot_sim_cycle ); } } -void obtain_insn_latency_info(insn_latency_info *latinfo, inst_t *insn) +void obtain_insn_latency_info(insn_latency_info *latinfo, const inst_t *insn) { latinfo->pc = insn->pc; - latinfo->latency = gpu_tot_sim_cycle + gpu_sim_cycle - insn->ts_cycle; - latinfo->ptx_thd_info = insn->ptx_thd_info; + latinfo->latency = gpu_tot_sim_cycle + gpu_sim_cycle - insn->issue_cycle; } int debug_tid = 0; -unsigned gpu_n_max_mshr_writeback=1; -void shader_writeback( shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) +void shader_core_ctx::writeback() { std::vector<inst_t> done_insts; - - static int *mshr_tid = NULL; - static int *pl_tid = NULL; - std::vector<insn_latency_info> unlock_lat_infos; - static insn_latency_info *mshr_lat_info = NULL; - static insn_latency_info *pl_lat_info = NULL; - - mshr_entry *mshr_head = NULL; - - int tid; - op_type op; - int o1, o2, o3, o4; - bool stalled_by_MSHR = false; - bool writeback_by_MSHR = false; bool w2rf = false; + memset(m_pl_tid,-1, sizeof(int)*m_config->warp_size); + check_stage_pcs(MM_WB); - if ( mshr_tid == NULL ) { - mshr_tid = (int*) malloc(sizeof(int)*pipe_simd_width); - pl_tid = (int*) malloc(sizeof(int)*pipe_simd_width); - mshr_lat_info = (insn_latency_info*) malloc(sizeof(insn_latency_info) * pipe_simd_width); - pl_lat_info = (insn_latency_info*) malloc(sizeof(insn_latency_info) * pipe_simd_width); - } - - memset(mshr_tid, -1, sizeof(int)*pipe_simd_width); - memset(pl_tid, -1, sizeof(int)*pipe_simd_width); - - - check_stage_pcs(shader,MM_WB); - - /* Generate Condition for instruction writeback to register file. */ - for (int i=0; i<pipe_simd_width; i++) { - w2rf |= (shader->pipeline_reg[MM_WB][i].hw_thread_id >= 0); - pl_tid[i] = shader->pipeline_reg[MM_WB][i].hw_thread_id; + // detect if a valid instruction is in MM_WB + for (unsigned i=0; i<m_config->warp_size; i++) { + w2rf |= (m_pipeline_reg[MM_WB][i].hw_thread_id >= 0); + m_pl_tid[i] = m_pipeline_reg[MM_WB][i].hw_thread_id; } //check mshrs for commit; unsigned mshr_threads_unlocked = 0; - for (unsigned i = 0; i < gpu_n_max_mshr_writeback; i++) { - mshr_head = shader->mshr_unit->return_head(); - if (mshr_head) { - //bail if we can't unlock anymore threads - if (mshr_threads_unlocked + mshr_head->insts.size() > (unsigned) pipe_simd_width) break; - assert(!gpgpu_strict_simd_wrbk);//implementation removed - assert (mshr_head->insts.size()); - for (unsigned j = 0; j < mshr_head->insts.size(); j++) { - inst_t &insn = mshr_head->insts[j]; + bool stalled_by_MSHR = false; + + mshr_entry *mshr_head = m_mshr_unit->return_head(); + if (mshr_head && (mshr_threads_unlocked + mshr_head->num_inst() <= m_config->warp_size) ) { + assert (mshr_head->num_inst()); + for (unsigned j = 0; j < mshr_head->num_inst(); j++) { + const inst_t &insn = mshr_head->get_inst(j); time_vector_update(insn.uid,MR_WRITEBACK,gpu_sim_cycle+gpu_tot_sim_cycle,RD_REQ); - obtain_insn_latency_info(&mshr_lat_info[mshr_threads_unlocked], &(mshr_head->insts[j])); - inflight_memory_insn_sub(shader, &mshr_head->insts[j]); + obtain_insn_latency_info(&m_mshr_lat_info[mshr_threads_unlocked], &insn); + if (enable_ptx_file_line_stats) + ptx_file_line_stats_sub_inflight_memory_insn(m_sid, insn.pc); assert (insn.hw_thread_id >= 0); - shader->pending_mem_access--; - // for ensuring that we don't unlock more than the code allows, needs to be fixed. + m_pending_mem_access--; mshr_threads_unlocked++; - } - done_insts.insert(done_insts.end(), mshr_head->insts.begin(), mshr_head->insts.end()); + if (m_config->gpgpu_operand_collector) { + if ( j== 0 ) + m_operand_collector.writeback(insn); + } else + stalled_by_MSHR = true; + } + mshr_head->get_insts(done_insts); - shader->mshr_unit->pop_return_head(); - writeback_by_MSHR = true; - unlock_lat_infos.resize(mshr_threads_unlocked); - std::copy(mshr_lat_info, mshr_lat_info + mshr_threads_unlocked, unlock_lat_infos.begin()); - - if (w2rf) { - stalled_by_MSHR = true; - } - assert(mshr_threads_unlocked); - } - } - if (stalled_by_MSHR) { - gpu_stall_by_MSHRwb++; - } - - if (!writeback_by_MSHR) { //!writeback_by_MSHR - memory_space_t warp_space = undefined_space; - - for (int i=0; i<pipe_simd_width; i++) { - op = shader->pipeline_reg[MM_WB][i].op; - tid = shader->pipeline_reg[MM_WB][i].hw_thread_id; - o1 = shader->pipeline_reg[MM_WB][i].out[0]; - o2 = shader->pipeline_reg[MM_WB][i].out[1]; - o3 = shader->pipeline_reg[MM_WB][i].out[2]; - o4 = shader->pipeline_reg[MM_WB][i].out[3]; - - obtain_insn_latency_info(&pl_lat_info[i], &shader->pipeline_reg[MM_WB][i]); - - // Collect threads that are done - // Do not include cache misses for a writeback - if(!shader->pipeline_reg[MM_WB][i].cache_miss) { - if(shader->pipeline_reg[MM_WB][i].hw_thread_id > -1) { - done_insts.push_back(shader->pipeline_reg[MM_WB][i]); - unlock_lat_infos.push_back(pl_lat_info[i]); - } - } + m_mshr_unit->pop_return_head(); + unlock_lat_infos.resize(mshr_threads_unlocked); + std::copy(m_mshr_lat_info, m_mshr_lat_info + mshr_threads_unlocked, unlock_lat_infos.begin()); + assert(mshr_threads_unlocked); + } - // All threads in the warp should have the same pc and space - if(pl_tid[i] > -1 ) { - warp_space = shader->pipeline_reg[MM_WB][i].space; - } + if ( m_config->gpgpu_operand_collector ) + stalled_by_MSHR = !m_operand_collector.writeback( m_pipeline_reg[MM_WB] ); - if(tid > -1) { -/* - if(!shader->pipeline_reg[MM_WB][i].cache_miss) - printf("CACHE HIT sid=%d tid=%d pc=%d \n", shader->sid, tid, shader->pipeline_reg[MM_WB][i].pc); - else - printf("CACHE MISS sid=%d tid=%d pc=%d \n", shader->sid, tid, shader->pipeline_reg[MM_WB][i].pc); -*/ - } - } + if (!stalled_by_MSHR) { + inst_t inst; + for (unsigned i=0; i<m_config->warp_size; i++) { + op_type op; + if (m_pipeline_reg[MM_WB][i].hw_thread_id > -1) + op = m_pipeline_reg[MM_WB][i].op; + obtain_insn_latency_info(&m_pl_lat_info[i], &m_pipeline_reg[MM_WB][i]); + if (!m_pipeline_reg[MM_WB][i].cache_miss) { // Do not include cache misses for a writeback + if (m_pipeline_reg[MM_WB][i].hw_thread_id > -1) { + done_insts.push_back(m_pipeline_reg[MM_WB][i]); + unlock_lat_infos.push_back(m_pl_lat_info[i]); + } + } + if (m_pl_tid[i] > -1 ) + inst = m_pipeline_reg[MM_WB][i]; + } - // Unlock the warp for re-fetching (put it in the fixed delay queue) - // Only need to unlock if warp is not empty - if(w2rf) - shader_queue_warp_unlocking(shader, pl_tid, warp_space, grid_num); - } + // Unlock the warp for re-fetching (put it in the fixed delay queue) + if (w2rf) // Only need to unlock if this is a valid instruction + queue_warp_unlocking(m_pl_tid, inst); + } else + m_stats->gpu_stall_by_MSHRwb++; - // Mark threads as done in warp tracker for (unsigned i=0; i<done_insts.size(); i++) { inst_t done_inst = done_insts[i]; - - shader_call_thread_done(shader, grid_num, done_inst); - - // Statistics - // At any rate, a real instruction is committed - // - don't count cache miss - gpu_sim_insn++; + call_thread_done(done_inst); + + gpu_sim_insn++; // a (scalar) instruction is done if ( !is_const(done_inst.space) ) - gpu_sim_insn_no_ld_const++; - gpu_sim_insn_last_update = gpu_sim_cycle; - shader->num_sim_insn++; - shader->thread[done_inst.hw_thread_id].n_insn++; - shader->thread[done_inst.hw_thread_id].n_insn_ac++; + m_stats->gpu_sim_insn_no_ld_const++; + m_gpu->gpu_sim_insn_last_update = gpu_sim_cycle; + m_num_sim_insn++; + m_thread[done_inst.hw_thread_id].n_insn++; + m_thread[done_inst.hw_thread_id].n_insn_ac++; if (enable_ptx_file_line_stats) { unsigned pc = unlock_lat_infos[i].pc; unsigned long latency = unlock_lat_infos[i].latency; - ptx_file_line_stats_add_latency(unlock_lat_infos[i].ptx_thd_info, pc, latency); + ptx_file_line_stats_add_latency(pc, latency); } - } - /* The pipeline can be stalled by MSHR */ - if (!stalled_by_MSHR) { - for (int i=0; i<pipe_simd_width; i++) { - shader->pipeline_reg[WB_RT][i] = shader->pipeline_reg[MM_WB][i]; - shader->pipeline_reg[MM_WB][i] = nop_inst; + if (!stalled_by_MSHR) { + if (!strcmp("GT200",m_config->pipeline_model) ) { + inst_t *fvt=first_valid_thread(m_pipeline_reg[MM_WB]); + if( fvt ) { + unsigned warp_id = fvt->hw_thread_id/m_config->warp_size; + m_warp[warp_id].dec_inst_in_pipeline(); } - } + } + move_warp(m_pipeline_reg[WB_RT], m_pipeline_reg[MM_WB]); + } - // Process the delay queue for current cycle - shader_process_delay_queue(shader); + process_delay_queue(); } /* @@ -3193,21 +2414,18 @@ void shader_writeback( shader_core_ctx_t *shader, unsigned int shader_number, in * * The amount of delay to add is determined by the instruction type. * - * @param *shader Pointer to shader core * @param *tid Array of tid in the warp to unlock - * @param pc Program counter for the current instruction in the warp * @param space Address space for the current instruction in the warp * */ -void shader_queue_warp_unlocking(shader_core_ctx_t *shader, int *tids, memory_space_t space, int grid_num) { - +void shader_core_ctx::queue_warp_unlocking(int *tids, const inst_t &inst ) +{ // Create a delay queue object and add it to the queue - shader_core_ctx_t::fixeddelay_queue_warp_t fixeddelay_queue_warp; - - fixeddelay_queue_warp.grid_num = grid_num; + fixeddelay_queue_warp_t fixeddelay_queue_warp; // Set ready_cycle based on instruction space - switch(space.get_type()) { + fixeddelay_queue_warp.inst = inst; + switch(inst.space.get_type()) { case shared_space: fixeddelay_queue_warp.ready_cycle = gpu_tot_sim_cycle + gpu_sim_cycle + 5; // Adds 5*4=20 cycles break; @@ -3217,10 +2435,9 @@ void shader_queue_warp_unlocking(shader_core_ctx_t *shader, int *tids, memory_sp } // Store threads in delay queue warp object - fixeddelay_queue_warp.tids.resize(warp_size); - std::copy(tids, tids+warp_size, fixeddelay_queue_warp.tids.begin()); - - shader->fixeddelay_queue.insert(fixeddelay_queue_warp); + fixeddelay_queue_warp.tids.resize(m_config->warp_size); + std::copy(tids, tids+m_config->warp_size, fixeddelay_queue_warp.tids.begin()); + m_fixeddelay_queue.insert(fixeddelay_queue_warp); } /* @@ -3229,22 +2446,23 @@ void shader_queue_warp_unlocking(shader_core_ctx_t *shader, int *tids, memory_sp * @param *shader Pointer to shader core * */ -void shader_process_delay_queue(shader_core_ctx_t *shader) { +void shader_core_ctx::process_delay_queue() { + shader_core_ctx *shader=this; // Unlock warps in fixeddelay_queue_warp - std::multiset<shader_core_ctx_t::fixeddelay_queue_warp_t, shader_core_ctx_t::fixeddelay_queue_warp_comp>::iterator it; - std::multiset<shader_core_ctx_t::fixeddelay_queue_warp_t, shader_core_ctx_t::fixeddelay_queue_warp_comp>::iterator it_last; - for ( it=shader->fixeddelay_queue.begin() ; - it != shader->fixeddelay_queue.end(); + std::multiset<fixeddelay_queue_warp_t, fixeddelay_queue_warp_comp>::iterator it; + std::multiset<fixeddelay_queue_warp_t, fixeddelay_queue_warp_comp>::iterator it_last; + for ( it=shader->m_fixeddelay_queue.begin() ; + it != shader->m_fixeddelay_queue.end(); ) { if(it->ready_cycle <= gpu_tot_sim_cycle + gpu_sim_cycle) { - if(!gpgpu_stall_on_use) { + if(!m_config->gpgpu_stall_on_use) { // This disables stall-on-use // If thread is still in warp_tracker, do not unlock yet bool skip_unlock = false; - for(unsigned i=0; i<warp_size; i++) { + for(unsigned i=0; i<m_config->warp_size; i++) { int tid = it->tids[i]; if(tid < 0) continue; - if(get_warp_tracker_pool().wpt_thread_in_wpt(shader,tid)) { + if(m_warp_tracker->wpt_thread_in_wpt(tid)) { skip_unlock = true; break; } @@ -3255,12 +2473,24 @@ void shader_process_delay_queue(shader_core_ctx_t *shader) { } } - // Unlock warp - shader_unlock_warp(shader,it->tids, it->grid_num); + if (!strcmp("GT200",m_config->pipeline_model) ) { + if( it->inst.space == shared_space ) { + for(unsigned i=0; i < m_config->warp_size; i++ ) { + if( it->tids[i]>= 0 ) { + unsigned warp_id = it->tids[i]/m_config->warp_size; + m_scoreboard->releaseRegisters(warp_id,&it->inst); + break; + } + } + } + } + + // Unlock warp + unlock_warp(it->tids); // Remove warp information from delay queue it_last = it++; - shader->fixeddelay_queue.erase(it_last); + shader->m_fixeddelay_queue.erase(it_last); } else { break; } @@ -3270,70 +2500,61 @@ void shader_process_delay_queue(shader_core_ctx_t *shader) { /* * Unlock a warp * - * @param *shd Pointer to shader core * @param tids Vector of tid in the warp to unlock * */ -void shader_unlock_warp(shader_core_ctx_t *shd, std::vector<int> tids, int grid_num) { - int thd_unlocked = 0; - int thd_exited = 0; - int tid; - int valid_tid = -1; - // Unlock - for (unsigned i=0; i<warp_size; i++) { - tid = tids[i]; - if (tid >= 0) { - valid_tid = tid; - // thread completed if it is going to fetching beyond code boundary - if ( gpgpu_cuda_sim && ptx_thread_done(shd->thread[tid].ptx_thd_info) ) { - shd->not_completed -= 1; - gpu_completed_thread += 1; - - int warp_id = wid_from_hw_tid(tid,warp_size); - if (!(shd->warp[warp_id].n_completed < (unsigned)warp_size)) { - printf("shader[%d]->warp[%d].n_completed = %d; warp_size = %d\n", - shd->sid,warp_id, shd->warp[warp_id].n_completed, warp_size); - } - assert( shd->warp[warp_id].n_completed < (unsigned)warp_size ); - shd->warp[warp_id].n_completed++; - if ( shd->model == NO_RECONVERGE ) { - update_max_branch_priority(shd,warp_id,grid_num); - } - - register_cta_thread_exit(shd, tid ); - thd_exited = 1; - - //printf("THREAD EXIT sid=%d tid=%d \n", shd->sid, tid); - - } else { - shd->thread[tid].avail4fetch++; - assert(shd->thread[tid].avail4fetch <= 1); - assert( shd->warp[tid/warp_size].n_avail4fetch < warp_size ); - shd->warp[tid/warp_size].n_avail4fetch++; - thd_unlocked = 1; - - //printf("THREAD UNLOCK sid=%d tid=%d \n", shd->sid, tid); - } - } - } - - // Update warp was unlocked, update the warp active mask - if(thd_unlocked || thd_exited) { - // Update the warp active mask - shader_pdom_update_warp_mask(shd, wid_from_hw_tid(valid_tid,warp_size)); - } +void shader_core_ctx::unlock_warp( std::vector<int> tids ) +{ + assert( tids.size() == m_config->warp_size ); // required by thd_commit_queue usage in fetch_simd_dwf() + int thd_unlocked = 0; + int thd_exited = 0; + int tid; + int valid_tid = -1; + if (!strcmp("GPGPUSIM_ORIG",m_config->pipeline_model) ) { + // Unlock + for (unsigned i=0; i<m_config->warp_size; i++) { + tid = tids[i]; + if (tid >= 0) { + valid_tid = tid; + // thread completed if it is going to fetching beyond code boundary + if ( ptx_thread_done(tid) ) { + m_not_completed -= 1; + m_stats->gpu_completed_thread += 1; + int warp_id = wid_from_hw_tid(tid,m_config->warp_size); + if (!(m_warp[warp_id].get_n_completed() < m_config->warp_size)) + printf("GPGPU-Sim uArch: shader[%d]->warp[%d].n_completed = %d; warp_size = %d\n", + m_sid,warp_id, m_warp[warp_id].get_n_completed(), m_config->warp_size); + assert( m_warp[warp_id].get_n_completed() < m_config->warp_size ); + m_warp[warp_id].inc_n_completed(); + register_cta_thread_exit( tid ); + thd_exited = 1; + } else { + if (!strcmp("GPGPUSIM_ORIG",m_config->pipeline_model) ) { + assert(!m_thread[tid].m_avail4fetch); + m_thread[tid].m_avail4fetch=true; + assert( m_warp[tid/m_config->warp_size].get_avail4fetch() < m_config->warp_size ); + m_warp[tid/m_config->warp_size].inc_avail4fetch(); + } + thd_unlocked = 1; + } + } + } + } + if (!strcmp("GPGPUSIM_ORIG",m_config->pipeline_model) ) { + if(thd_unlocked || thd_exited) { + // Update the warp active mask + m_pdom_warp[wid_from_hw_tid(valid_tid,m_config->warp_size)]->pdom_update_warp_mask(); + } + } - if (shd->model == POST_DOMINATOR || shd->model == NO_RECONVERGE) { + if (m_config->model == POST_DOMINATOR) { // Do nothing } else { // For this case, submit to commit_queue - if (shd->using_commit_queue && thd_unlocked) { - int *tid_unlocked = alloc_commit_warp(); - std::copy(tids.begin(), tids.end(), tid_unlocked); - dq_push(shd->thd_commit_queue,(void*)tid_unlocked); - } + if (m_config->using_commit_queue && thd_unlocked) + m_thd_commit_queue->push( new std::vector<int>(tids), gpu_sim_cycle ); } } @@ -3342,93 +2563,73 @@ void shader_unlock_warp(shader_core_ctx_t *shd, std::vector<int> tids, int grid_ * Signals to the warp_tracker that a thread in a warp (for a given pc/instruction) is done * * @param *shd Pointer to shader core - * @param grid_num Grid number * @param done_inst Completed instruction * */ -void shader_call_thread_done( shader_core_ctx_t *shader, int grid_num, inst_t &done_inst ) { - - if (gpgpu_no_divg_load) { - - //printf("THREAD RETURNED sid=%d tid=%d pc=%d \n", shader->sid, done_inst.hw_thread_id, done_inst.pc); - +void shader_core_ctx::call_thread_done( inst_t &done_inst ) +{ + if (m_config->gpgpu_no_divg_load) { // Signal to unlock the thread. If all threads are done, deregister warp - if( get_warp_tracker_pool().wpt_signal_avail(done_inst.hw_thread_id, shader, done_inst.pc) == 1 ) { + if( m_warp_tracker->wpt_signal_avail(done_inst.hw_thread_id, done_inst.pc) == 1 ) { // Entire warp has returned - //printf("WARP RETURNED sid=%d tid=%d pc=%d \n", shader->sid, done_inst.hw_thread_id, done_inst.pc); - // Deregister warp - get_warp_tracker_pool().wpt_deregister_warp(done_inst.hw_thread_id, shader, done_inst.pc); - - // Signal scoreboard to release register - shader->scrb->releaseRegisters( wid_from_hw_tid(done_inst.hw_thread_id, warp_size), &done_inst ); + m_warp_tracker->wpt_deregister_warp(done_inst.hw_thread_id, done_inst.pc); + if (! (!strcmp("GT200",m_config->pipeline_model) && (done_inst.space == shared_space)) ) + // Signal scoreboard to release register + m_scoreboard->releaseRegisters( wid_from_hw_tid(done_inst.hw_thread_id, m_config->warp_size), &done_inst ); } } - } -void shader_print_runtime_stat( FILE *fout ) { - unsigned i; - +void gpgpu_sim::shader_print_runtime_stat( FILE *fout ) +{ fprintf(fout, "SHD_INSN: "); - for (i=0;i<gpu_n_shader;i++) { - fprintf(fout, "%u ",sc[i]->num_sim_insn); - } + for (unsigned i=0;i<m_n_shader;i++) + fprintf(fout, "%u ",m_sc[i]->get_num_sim_insn()); fprintf(fout, "\n"); fprintf(fout, "SHD_THDS: "); - for (i=0;i<gpu_n_shader;i++) { - fprintf(fout, "%u ",sc[i]->not_completed); - } + for (unsigned i=0;i<m_n_shader;i++) + fprintf(fout, "%u ",m_sc[i]->get_not_completed()); fprintf(fout, "\n"); fprintf(fout, "SHD_DIVG: "); - for (i=0;i<gpu_n_shader;i++) { - fprintf(fout, "%u ",sc[i]->n_diverge); - } + for (unsigned i=0;i<m_n_shader;i++) + fprintf(fout, "%u ",m_sc[i]->get_n_diverge()); fprintf(fout, "\n"); fprintf(fout, "THD_INSN: "); - for (i=0; i<gpu_n_thread_per_shader; i++) { - fprintf(fout, "%d ", sc[0]->thread[i].n_insn); - } + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) + fprintf(fout, "%d ", m_sc[0]->get_thread_n_insn(i) ); fprintf(fout, "\n"); } -void shader_print_l1_miss_stat( FILE *fout ) { - unsigned i; - +void gpgpu_sim::shader_print_l1_miss_stat( FILE *fout ) +{ fprintf(fout, "THD_INSN_AC: "); - for (i=0; i<gpu_n_thread_per_shader; i++) { - fprintf(fout, "%d ", sc[0]->thread[i].n_insn_ac); - } + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) + fprintf(fout, "%d ", m_sc[0]->get_thread_n_insn_ac(i)); fprintf(fout, "\n"); - fprintf(fout, "T_L1_Mss: "); //l1 miss rate per thread - for (i=0; i<gpu_n_thread_per_shader; i++) { - fprintf(fout, "%d ", sc[0]->thread[i].n_l1_mis_ac); - } + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) + fprintf(fout, "%d ", m_sc[0]->get_thread_n_l1_mis_ac(i)); fprintf(fout, "\n"); - fprintf(fout, "T_L1_Mgs: "); //l1 merged miss rate per thread - for (i=0; i<gpu_n_thread_per_shader; i++) { - fprintf(fout, "%d ", sc[0]->thread[i].n_l1_mis_ac - sc[0]->thread[i].n_l1_mrghit_ac); - } + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) + fprintf(fout, "%d ", m_sc[0]->get_thread_n_l1_mis_ac(i) - m_sc[0]->get_thread_n_l1_mrghit_ac(i)); fprintf(fout, "\n"); - fprintf(fout, "T_L1_Acc: "); //l1 access per thread - for (i=0; i<gpu_n_thread_per_shader; i++) { - fprintf(fout, "%d ", sc[0]->thread[i].n_l1_access_ac); - } + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) + fprintf(fout, "%d ", m_sc[0]->get_thread_n_l1_access_ac(i)); fprintf(fout, "\n"); //per warp int temp =0; fprintf(fout, "W_L1_Mss: "); //l1 miss rate per warp - for (i=0; i<gpu_n_thread_per_shader; i++) { - temp += sc[0]->thread[i].n_l1_mis_ac; - if (i%warp_size == (unsigned)(warp_size-1)) { + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) { + temp += m_sc[0]->get_thread_n_l1_mis_ac(i); + if (i%m_shader_config->warp_size == (unsigned)(m_shader_config->warp_size-1)) { fprintf(fout, "%d ", temp); temp = 0; } @@ -3436,9 +2637,9 @@ void shader_print_l1_miss_stat( FILE *fout ) { fprintf(fout, "\n"); temp=0; fprintf(fout, "W_L1_Mgs: "); //l1 merged miss rate per warp - for (i=0; i<gpu_n_thread_per_shader; i++) { - temp += (sc[0]->thread[i].n_l1_mis_ac - sc[0]->thread[i].n_l1_mrghit_ac); - if (i%warp_size == (unsigned)(warp_size-1)) { + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) { + temp += (m_sc[0]->get_thread_n_l1_mis_ac(i) - m_sc[0]->get_thread_n_l1_mrghit_ac(i) ); + if (i%m_shader_config->warp_size == (unsigned)(m_shader_config->warp_size-1)) { fprintf(fout, "%d ", temp); temp = 0; } @@ -3446,49 +2647,51 @@ void shader_print_l1_miss_stat( FILE *fout ) { fprintf(fout, "\n"); temp =0; fprintf(fout, "W_L1_Acc: "); //l1 access per warp - for (i=0; i<gpu_n_thread_per_shader; i++) { - temp += sc[0]->thread[i].n_l1_access_ac; - if (i%warp_size == (unsigned)(warp_size-1)) { + for (unsigned i=0; i<m_shader_config->n_thread_per_shader; i++) { + temp += m_sc[0]->get_thread_n_l1_access_ac(i); + if (i%m_shader_config->warp_size == (unsigned)(m_shader_config->warp_size-1)) { fprintf(fout, "%d ", temp); temp = 0; } } fprintf(fout, "\n"); - } -void shader_print_warp( const shader_core_ctx_t *shader, inst_t *warp, FILE *fout, int stage_width, int print_mem, int mask ) +void shader_core_ctx::print_warp( inst_t *warp, FILE *fout, int print_mem, int mask ) const { - int i, j, warp_id = -1; - for (i=0; i<stage_width; i++) { + unsigned i, j, warp_id = (unsigned)-1; + for (i=0; i<m_config->warp_size; i++) { if (warp[i].hw_thread_id > -1) { - warp_id = warp[i].hw_thread_id / warp_size; + warp_id = warp[i].hw_thread_id / m_config->warp_size; break; } } - i = (i>=stage_width)? 0 : i; + i = (i>=m_config->warp_size)? 0 : i; - fprintf(fout,"0x%04x ", warp[i].pc ); + if( warp[i].pc != (address_type)-1 ) + fprintf(fout,"0x%04x ", warp[i].pc ); + else + fprintf(fout,"bubble " ); if( mask & 2 ) { fprintf(fout, "(" ); - for (j=0; j<stage_width; j++) + for (j=0; j<m_config->warp_size; j++) fprintf(fout, "%03d ", warp[j].hw_thread_id); fprintf(fout, "): "); } else { fprintf(fout, "w%02d[", warp_id); - for (j=0; j<stage_width; j++) + for (j=0; j<m_config->warp_size; j++) fprintf(fout, "%c", ((warp[j].hw_thread_id != -1)?'1':'0') ); fprintf(fout, "]: "); } - if( warp_id != -1 && shader->model == POST_DOMINATOR ) { - pdom_warp_ctx_t *warp=&(shader->pdom_warp[warp_id]); - if( warp->m_recvg_pc[warp->m_stack_top] == (unsigned)-1 ) { - fprintf(fout," rp:--- "); - } else { - fprintf(fout," rp:0x%03x ", warp->m_recvg_pc[warp->m_stack_top] ); - } + if( warp_id != (unsigned)-1 && m_config->model == POST_DOMINATOR ) { + unsigned rp = m_pdom_warp[warp_id]->get_rp(); + if( rp == (unsigned)-1 ) { + fprintf(fout," rp:--- "); + } else { + fprintf(fout," rp:0x%03x ", rp ); + } } ptx_print_insn( warp[i].pc, fout ); @@ -3501,62 +2704,64 @@ void shader_print_warp( const shader_core_ctx_t *shader, inst_t *warp, FILE *fou fprintf(fout, "\n"); } -void shader_print_stage(shader_core_ctx_t *shader, unsigned int stage, - FILE *fout, int stage_width, int print_mem, int mask ) +void shader_core_ctx::print_stage(unsigned int stage, FILE *fout, int print_mem, int mask ) { - inst_t *warp = shader->pipeline_reg[stage]; - shader_print_warp(shader,warp,fout,stage_width,print_mem,mask); + inst_t *warp = m_pipeline_reg[stage]; + print_warp(warp,fout,print_mem,mask); } -void shader_print_pre_mem_stages(shader_core_ctx_t *shader, FILE *fout, int print_mem, int mask ) +void shader_core_ctx::print_pre_mem_stages( FILE *fout, int print_mem, int mask ) { - int i, j; + unsigned i, j; int warp_id; - if (!gpgpu_pre_mem_stages) return; + if (!m_config->gpgpu_pre_mem_stages) return; - for (unsigned pms = 0; pms <= gpgpu_pre_mem_stages - 1; pms++) { - fprintf(fout, "PM[%01d] = ", pms); + for (unsigned pms = 0; pms <= m_config->gpgpu_pre_mem_stages; pms++) { + fprintf(fout, "PM[%01d] = ", pms); warp_id = -1; - for (i=0; i<pipe_simd_width; i++) { - if (shader->pre_mem_pipeline[pms][i].hw_thread_id > -1) { - warp_id = shader->pre_mem_pipeline[pms][i].hw_thread_id / warp_size; + for (i=0; i<m_config->warp_size; i++) { + if (pre_mem_pipeline[pms][i].hw_thread_id > -1) { + warp_id = pre_mem_pipeline[pms][i].hw_thread_id / m_config->warp_size; break; } } - i = (i>=pipe_simd_width)? 0 : i; + i = (i>=m_config->warp_size)? 0 : i; - fprintf(fout,"0x%04x ", shader->pre_mem_pipeline[pms][i].pc ); + if( pre_mem_pipeline[pms][i].pc != (address_type)-1 ) + fprintf(fout,"0x%04x ", pre_mem_pipeline[pms][i].pc ); + else + fprintf(fout,"bubble " ); if( mask & 2 ) { fprintf(fout, "(" ); - for (j=0; j<pipe_simd_width; j++) - fprintf(fout, "%03d ", shader->pre_mem_pipeline[pms][j].hw_thread_id); + for (j=0; j<m_config->warp_size; j++) + fprintf(fout, "%03d ", pre_mem_pipeline[pms][j].hw_thread_id); fprintf(fout, "): "); } else { fprintf(fout, "w%02d[", warp_id); - for (j=0; j<pipe_simd_width; j++) - fprintf(fout, "%c", ((shader->pre_mem_pipeline[pms][j].hw_thread_id != -1)?'1':'0') ); + for (j=0; j<m_config->warp_size; j++) + fprintf(fout, "%c", ((pre_mem_pipeline[pms][j].hw_thread_id != -1)?'1':'0') ); fprintf(fout, "]: "); } - if( warp_id != -1 && shader->model == POST_DOMINATOR ) { - pdom_warp_ctx_t *warp=&(shader->pdom_warp[warp_id]); - if( warp->m_recvg_pc[warp->m_stack_top] == (unsigned)-1 ) { - printf(" rp:--- "); - } else { - printf(" rp:0x%03x ", warp->m_recvg_pc[warp->m_stack_top] ); - } + if( warp_id != -1 && m_config->model == POST_DOMINATOR ) { + unsigned rp = m_pdom_warp[warp_id]->get_rp(); + if( rp == (unsigned)-1 ) { + fprintf(fout," rp:--- "); + } else { + fprintf(fout," rp:0x%03x ", rp ); + } } - ptx_print_insn( shader->pre_mem_pipeline[pms][i].pc, fout ); + ptx_print_insn( pre_mem_pipeline[pms][i].pc, fout ); if( mask & 0x10 ) { - if ( ( shader->pre_mem_pipeline[pms][i].op == LOAD_OP || - shader->pre_mem_pipeline[pms][i].op == STORE_OP ) && print_mem ) - fprintf(fout, " mem: 0x%016llx", shader->pre_mem_pipeline[pms][i].memreqaddr); + if ( ( pre_mem_pipeline[pms][i].op == LOAD_OP || + pre_mem_pipeline[pms][i].op == STORE_OP ) && print_mem ) + fprintf(fout, " mem: 0x%016llx", pre_mem_pipeline[pms][i].memreqaddr); } fprintf(fout, "\n"); } @@ -3564,158 +2769,134 @@ void shader_print_pre_mem_stages(shader_core_ctx_t *shader, FILE *fout, int prin const char * ptx_get_fname( unsigned PC ); -void shader_display_pipeline(shader_core_ctx_t *shader, FILE *fout, int print_mem, int mask ) +void shader_core_ctx::display_pdom_state(FILE *fout, int mask ) { - // call this function from within gdb to print out status of pipeline - // if you encounter a bug, or to visualize pipeline operation - // (this is a good way to "verify" your pipeline model makes sense!) + if ( (mask & 4) && m_config->model == POST_DOMINATOR ) { + fprintf(fout,"warp status:\n"); + unsigned n = m_config->n_thread_per_shader / m_config->warp_size; + for (unsigned i=0; i < n; i++) { + unsigned nactive = 0; + for (unsigned j=0; j<m_config->warp_size; j++ ) { + unsigned tid = i*m_config->warp_size + j; + int done = ptx_thread_done(tid); + nactive += (ptx_thread_done(tid)?0:1); + if ( done && (mask & 8) ) { + unsigned done_cycle = ptx_thread_donecycle( m_thread[tid].m_functional_model_thread_state ); + if ( done_cycle ) { + printf("\n w%02u:t%03u: done @ cycle %u", i, tid, done_cycle ); + } + } + } + if ( nactive == 0 ) { + continue; + } + m_pdom_warp[i]->print(fout); + } + fprintf(fout,"\n"); + } +} +void shader_core_ctx::display_pipeline(FILE *fout, int print_mem, int mask ) +{ fprintf(fout, "=================================================\n"); - fprintf(fout, "shader %u at cycle %Lu+%Lu (%u threads running)\n", shader->sid, - gpu_tot_sim_cycle, gpu_sim_cycle, shader->not_completed); + fprintf(fout, "shader %u at cycle %Lu+%Lu (%u threads running)\n", m_sid, + gpu_tot_sim_cycle, gpu_sim_cycle, m_not_completed); fprintf(fout, "=================================================\n"); - if ( (mask & 4) && shader->model == POST_DOMINATOR ) { - fprintf(fout,"warp status:\n"); - unsigned n = shader->n_threads / warp_size; - for (unsigned i=0; i < n; i++) { - unsigned nactive = 0; - for (unsigned j=0; j<warp_size; j++ ) { - unsigned tid = i*warp_size + j; - int done = ptx_thread_done( shader->thread[tid].ptx_thd_info ); - nactive += (ptx_thread_done( shader->thread[tid].ptx_thd_info )?0:1); - if ( done && (mask & 8) ) { - unsigned done_cycle = ptx_thread_donecycle( shader->thread[tid].ptx_thd_info ); - if ( done_cycle ) { - printf("\n w%02u:t%03u: done @ cycle %u", i, tid, done_cycle ); - } - } - } - if ( nactive == 0 ) { - continue; - } - pdom_warp_ctx_t *warp=&(shader->pdom_warp[i]); - for ( int k=0; k <= warp->m_stack_top; k++ ) { - if ( k==0 ) { - fprintf(fout, "w%02d (%2u thds active): %2u ", i, nactive, k ); - } else { - fprintf(fout, " %2u ", k ); - } - for (unsigned m=1,j=0; j<warp_size; j++, m<<=1) - fprintf(fout, "%c", ((warp->m_active_mask[k] & m)?'1':'0') ); - fprintf(fout, " pc: %4u", warp->m_pc[k] ); - if ( warp->m_recvg_pc[k] == (unsigned)-1 ) { - fprintf(fout," rp: ---- cd: %2u ", warp->m_calldepth[k] ); - } else { - fprintf(fout," rp: %4u cd: %2u ", warp->m_recvg_pc[k], warp->m_calldepth[k] ); - } - if ( warp->m_branch_div_cycle[k] != 0 ) { - fprintf(fout," bd@%6u ", (unsigned) warp->m_branch_div_cycle[k] ); - } else { - fprintf(fout," " ); - } - //fprintf(fout," func=\'%s\' ", ptx_get_fname( warp->m_pc[k] ) ); - ptx_print_insn( warp->m_pc[k], fout ); - fprintf(fout,"\n"); - } - } - fprintf(fout,"\n"); - } - - if ( mask & 0x20 ) { - fprintf(fout, "TS/IF = "); - shader_print_stage(shader, TS_IF, fout, warp_size, print_mem, mask); - } + if (!strcmp("GPGPUSIM_ORIG",m_config->pipeline_model) ) + display_pdom_state(fout,mask); - fprintf(fout, "IF/ID = "); - shader_print_stage(shader, IF_ID, fout, pipe_simd_width, print_mem, mask ); + if (!strcmp("GT200",m_config->pipeline_model) ) { + dump_istream_state(fout); + fprintf(fout,"\n"); - if (gpgpu_operand_collector) - shader->m_opndcoll_new.dump(fout); - - if (shader->using_rrstage) { - fprintf(fout, "ID/RR = "); - shader_print_stage(shader, ID_RR, fout, pipe_simd_width, print_mem, mask); + fprintf(fout, "IF/ID = "); + if( !m_inst_fetch_buffer.m_valid ) + fprintf(fout,"bubble\n"); + else { + fprintf(fout,"w%2u : pc = 0x%x, nbytes = %u\n", + m_inst_fetch_buffer.m_warp_id, + m_inst_fetch_buffer.m_pc, + m_inst_fetch_buffer.m_nbytes ); + } + fprintf(fout,"\nibuffer status:\n"); + for( unsigned i=0; i<m_config->max_warps_per_shader; i++) { + if( !m_warp[i].ibuffer_empty() ) + m_warp[i].print_ibuffer(fout); + } + fprintf(fout,"\n"); + display_pdom_state(fout,mask); } - fprintf(fout, "ID/EX = "); - shader_print_stage(shader, ID_EX, fout, pipe_simd_width, print_mem, mask); - - shader_print_pre_mem_stages(shader, fout, print_mem, mask); + m_scoreboard->printContents(); - if (!gpgpu_pre_mem_stages) - fprintf(fout, "EX/MEM= "); + if (!strcmp("GPGPUSIM_ORIG",m_config->pipeline_model) ) { + if ( mask & 0x20 ) { + fprintf(fout, "TS/IF = "); + print_stage(TS_IF, fout, print_mem, mask); + } + fprintf(fout, "IF/ID = "); + print_stage(IF_ID, fout, print_mem, mask ); + } + if (m_config->gpgpu_operand_collector) { + fprintf(fout,"ID/OC (SP) = "); + print_stage(ID_OC, fout, print_mem, mask); + fprintf(fout,"ID/OC (SFU) = "); + print_stage(ID_OC_SFU, fout, print_mem, mask); + m_operand_collector.dump(fout); + } + if (m_config->m_using_dwf_rrstage) { + fprintf(fout, "ID/RR = "); + print_stage(ID_RR, fout, print_mem, mask); + } + if (!strcmp("GT200",m_config->pipeline_model) ) + fprintf(fout, "ID/EX (SP) = "); else - fprintf(fout, "PM/MEM= "); - shader_print_stage(shader, EX_MM, fout, pipe_simd_width, print_mem, mask); - - fprintf(fout, "MEM/WB= "); - shader_print_stage(shader, MM_WB, fout, pipe_simd_width, print_mem, mask); - - fprintf(fout, "\n"); -} - -void shader_dump_thread_state(shader_core_ctx_t *shader, FILE *fout ) -{ - fprintf( fout, "\n"); - for ( unsigned w = 0; w < gpu_n_thread_per_shader/warp_size; w++ ) { - int tid = w*warp_size; - if ( shader->warp[w].n_completed < (unsigned)warp_size ) { - fprintf( fout, " %u:%3u fetch state = c:%u a4f:%u bw:%u (completed: ", shader->sid, tid, - shader->warp[w].n_completed, - shader->warp[w].n_avail4fetch, - shader->warp[w].n_waiting_at_barrier ); - - for ( unsigned i = tid; i < (w+1)*warp_size; i++ ) { - if ( gpgpu_cuda_sim && ptx_thread_done(shader->thread[i].ptx_thd_info) ) { - fprintf(fout,"1"); - } else { - fprintf(fout,"0"); - } - if ( (((i+1)%4) == 0) && (i+1) < (w+1)*warp_size ) { - fprintf(fout,","); - } - } - fprintf(fout,")\n"); - } + fprintf(fout, "ID/EX = "); + print_stage(ID_EX, fout, print_mem, mask); + if (!strcmp("GT200",m_config->pipeline_model) ) { + fprintf(fout, "ID/EX (SFU) = "); + print_stage(OC_EX_SFU, fout, print_mem, mask); } + print_pre_mem_stages(fout, print_mem, mask); + if (!m_config->gpgpu_pre_mem_stages) + fprintf(fout, "EX/MEM = "); + else + fprintf(fout, "PM/MEM = "); + print_stage(EX_MM, fout, print_mem, mask); + fprintf(fout, "MEM/WB = "); + print_stage(MM_WB, fout, print_mem, mask); + fprintf(fout, "\n"); + mshr_print(fout,0); } -void shader_dp(shader_core_ctx_t *shader, int print_mem) { - shader_display_pipeline(shader, stdout, print_mem, 7 ); -} - - -unsigned int max_cta_per_shader( shader_core_ctx_t *shader) +unsigned int shader_core_ctx::max_cta( class function_info *kernel ) { - unsigned int result; unsigned int padded_cta_size; - padded_cta_size = ptx_sim_cta_size(); - if (padded_cta_size%warp_size) { - padded_cta_size = ((padded_cta_size/warp_size)+1)*(warp_size); - //printf("padded_cta_size=%u\n", padded_cta_size); - } + padded_cta_size = m_gpu->the_kernel().threads_per_cta(); + if (padded_cta_size%m_config->warp_size) + padded_cta_size = ((padded_cta_size/m_config->warp_size)+1)*(m_config->warp_size); //Limit by n_threads/shader - unsigned int result_thread = shader->n_threads / padded_cta_size; + unsigned int result_thread = m_config->n_thread_per_shader / padded_cta_size; - const struct gpgpu_ptx_sim_kernel_info *kernel_info = ptx_sim_kernel_info(); + const struct gpgpu_ptx_sim_kernel_info *kernel_info = ptx_sim_kernel_info(kernel); //Limit by shmem/shader unsigned int result_shmem = (unsigned)-1; if (kernel_info->smem > 0) - result_shmem = shader->shmem_size / kernel_info->smem; + result_shmem = m_config->gpgpu_shmem_size / kernel_info->smem; //Limit by register count, rounded up to multiple of 4. unsigned int result_regs = (unsigned)-1; if (kernel_info->regs > 0) - result_regs = shader->n_registers / (padded_cta_size * ((kernel_info->regs+3)&~3)); + result_regs = m_config->gpgpu_shader_registers / (padded_cta_size * ((kernel_info->regs+3)&~3)); //Limit by CTA - unsigned int result_cta = shader->n_cta; + unsigned int result_cta = m_config->max_cta_per_core; - result = result_thread; + unsigned result = result_thread; result = gs_min2(result, result_shmem); result = gs_min2(result, result_regs); result = gs_min2(result, result_cta); @@ -3732,114 +2913,49 @@ unsigned int max_cta_per_shader( shader_core_ctx_t *shader) } if (result < 1) { - printf ("Error: max_cta_per_shader(\"%s\") returning %d. Kernel requires more resources than shader has?\n", shader->name, result); + printf ("Error: max_cta_per_shader(\"%s\") returning %d. Kernel requires more resources than shader has?\n", m_name, result); abort(); } return result; } -void shader_cycle( shader_core_ctx_t *shader, - unsigned int shader_number, - int grid_num ) +void shader_core_ctx::cycle_gt200() { - if (gpgpu_operand_collector) - shader_opnd_collect_write(shader); - shader_writeback(shader, shader_number, grid_num); - shader_memory(shader, shader_number); - if (gpgpu_pre_mem_stages) // for modeling deeper pipelines - shader_pre_memory(shader, shader_number); - shader_execute(shader, shader_number); - if (shader->using_rrstage) { - // Model register bank conflicts as in - // Fung et al. MICRO'07 / ACM TACO'09 papers. - shader_preexecute (shader, shader_number); - } - if (gpgpu_operand_collector) - shader_opnd_collect_read(shader); - shader_decode (shader, shader_number, grid_num); - shader_fetch (shader, shader_number, grid_num); + clear_stage(m_pipeline_reg[WB_RT]); + writeback(); + memory(); + execute(); + m_operand_collector.step(m_pipeline_reg[ID_OC],m_pipeline_reg[ID_OC_SFU]); + decode_new(); + fetch_new(); } -// performance counter that are not local to one shader -void shader_print_accstats( FILE* fout ) +void shader_core_ctx::cycle() { - fprintf(fout, "gpgpu_n_load_insn = %d\n", gpgpu_n_load_insn); - fprintf(fout, "gpgpu_n_store_insn = %d\n", gpgpu_n_store_insn); - fprintf(fout, "gpgpu_n_shmem_insn = %d\n", gpgpu_n_shmem_insn); - fprintf(fout, "gpgpu_n_tex_insn = %d\n", gpgpu_n_tex_insn); - fprintf(fout, "gpgpu_n_const_mem_insn = %d\n", gpgpu_n_const_insn); - fprintf(fout, "gpgpu_n_param_mem_insn = %d\n", gpgpu_n_param_insn); - - fprintf(fout, "gpgpu_n_shmem_bkconflict = %d\n", gpgpu_n_shmem_bkconflict); - fprintf(fout, "gpgpu_n_cache_bkconflict = %d\n", gpgpu_n_cache_bkconflict); - - fprintf(fout, "gpgpu_n_intrawarp_mshr_merge = %d\n", gpgpu_n_intrawarp_mshr_merge); - fprintf(fout, "gpgpu_n_cmem_portconflict = %d\n", gpgpu_n_cmem_portconflict); - - fprintf(fout, "gpgpu_n_writeback_l1_miss = %d\n", writeback_l1_miss); - - fprintf(fout, "gpgpu_n_partial_writes = %d\n", gpgpu_n_partial_writes); - - fprintf(fout, "gpgpu_stall_shd_mem[c_mem][bk_conf] = %d\n", gpu_stall_shd_mem_breakdown[C_MEM][BK_CONF]); - fprintf(fout, "gpgpu_stall_shd_mem[c_mem][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[C_MEM][MSHR_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[c_mem][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[C_MEM][ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[t_mem][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[T_MEM][MSHR_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[t_mem][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[T_MEM][ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[s_mem][bk_conf] = %d\n", gpu_stall_shd_mem_breakdown[S_MEM][BK_CONF]); - fprintf(fout, "gpgpu_stall_shd_mem[gl_mem][bk_conf] = %d\n", - gpu_stall_shd_mem_breakdown[G_MEM_LD][BK_CONF] + - gpu_stall_shd_mem_breakdown[G_MEM_ST][BK_CONF] + - gpu_stall_shd_mem_breakdown[L_MEM_LD][BK_CONF] + - gpu_stall_shd_mem_breakdown[L_MEM_ST][BK_CONF] - ); // coalescing stall at data cache - fprintf(fout, "gpgpu_stall_shd_mem[gl_mem][coal_stall] = %d\n", - gpu_stall_shd_mem_breakdown[G_MEM_LD][COAL_STALL] + - gpu_stall_shd_mem_breakdown[G_MEM_ST][COAL_STALL] + - gpu_stall_shd_mem_breakdown[L_MEM_LD][COAL_STALL] + - gpu_stall_shd_mem_breakdown[L_MEM_ST][COAL_STALL] - ); // coalescing stall + bank conflict at data cache - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][MSHR_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][WB_ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][WB_CACHE_RSRV_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_ST][MSHR_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_ST][ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_ST][WB_ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_ST][WB_CACHE_RSRV_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][MSHR_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][WB_ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][WB_CACHE_RSRV_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_st][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][MSHR_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_st][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][WB_ICNT_RC_FAIL]); - fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][WB_CACHE_RSRV_FAIL]); - - fprintf(fout, "gpu_reg_bank_conflict_stalls = %d\n", gpu_reg_bank_conflict_stalls); - - if (warp_occ_detailed) { - int n_warp = gpu_n_thread_per_shader / warp_size; - - for (unsigned s = 0; s<gpu_n_shader; s++) - for (int w = 0; w<n_warp; w++) { - fprintf(fout, "wod[%d][%d]=", s, w); - for (unsigned t = 0; t<warp_size; t++) { - fprintf(fout, "%d ", warp_occ_detailed[s * n_warp + w][t]); - } - fprintf(fout, "\n"); - } + clear_stage(m_pipeline_reg[WB_RT]); + writeback(); + memory(); + if (m_config->gpgpu_pre_mem_stages) // for modeling deeper pipelines + pre_memory(); + execute(); + if (m_config->m_using_dwf_rrstage) { + preexecute(); } + if (m_config->gpgpu_operand_collector) + m_operand_collector.step(m_pipeline_reg[ID_OC]); + decode(); + fetch(); } // Flushes all content of the cache to memory -void shader_cache_flush(shader_core_ctx_t* sc) +void shader_core_ctx::cache_flush() { unsigned int i; unsigned int set; unsigned long long int flush_addr; - shd_cache_t *cp = sc->L1cache; + shd_cache_t *cp = m_L1D; shd_cache_line_t *pline; for (i=0; i<cp->nset*cp->assoc; i++) { @@ -3848,7 +2964,7 @@ void shader_cache_flush(shader_core_ctx_t* sc) if ((pline->status & (DIRTY|VALID)) == (DIRTY|VALID)) { flush_addr = pline->addr; - sc->fq_push(flush_addr, sc->L1cache->line_sz, 1, NO_PARTIAL_WRITE, sc->sid, 0, NULL, 0, GLOBAL_ACC_W, -1); + fq_push(flush_addr, m_L1D->line_sz, 1, NO_PARTIAL_WRITE, 0, NULL, 0, GLOBAL_ACC_W, -1); pline->status &= ~VALID; pline->status &= ~DIRTY; @@ -3889,15 +3005,22 @@ std::list<opndcoll_rfu_t::op_t> opndcoll_rfu_t::arbiter_t::allocate_reads() _outmatch[j] = -1; for( unsigned i=0; i<m_num_banks; i++) { - for( unsigned j=0; j<m_num_collectors; j++) + for( unsigned j=0; j<m_num_collectors; j++) { + assert( i < (unsigned)_inputs ); + assert( j < (unsigned)_outputs ); _request[i][j] = 0; + } if( !m_queue[i].empty() ) { const op_t &op = m_queue[i].front(); int oc_id = op.get_oc_id(); + assert( i < (unsigned)_inputs ); + assert( oc_id < _outputs ); _request[i][oc_id] = 1; } - if( m_allocated_bank[i].is_write() ) + if( m_allocated_bank[i].is_write() ) { + assert( i < (unsigned)_inputs ); _inmatch[i] = 0; // write gets priority + } } ///// wavefront allocator from booksim... ---> @@ -3914,7 +3037,9 @@ std::list<opndcoll_rfu_t::op_t> opndcoll_rfu_t::arbiter_t::allocate_reads() ( _outmatch[output] == -1 ) && ( _request[input][output]/*.label != -1*/ ) ) { // Grant! + assert( input < _inputs ); _inmatch[input] = output; + assert( output < _outputs ); _outmatch[output] = input; } @@ -3939,24 +3064,6 @@ std::list<opndcoll_rfu_t::op_t> opndcoll_rfu_t::arbiter_t::allocate_reads() } } - -/* - for( unsigned c=0; c < m_num_collectors; c++ ) { - unsigned cu = (m_last_cu+c+1)%m_num_collectors; - for( unsigned b=0; b < m_num_banks; b++ ) { - unsigned bank = (m_allocator_rr_head[cu]+b+1)%m_num_banks; - if( (!m_queue[bank].empty()) && m_allocated_bank[bank].is_free() ) { - op_t &op = m_queue[bank].front(); - result.push_back(op); - m_allocated_bank[bank].alloc_read(op); - m_queue[bank].pop_front(); - m_allocator_rr_head[cu] = bank; - m_last_cu = cu; - break; // skip to next collector unit - } - } - } -*/ return result; } @@ -4042,7 +3149,7 @@ void barrier_set_t::warp_exit( unsigned warp_id ) } // assertions -bool barrier_set_t::warp_waiting_at_barrier( unsigned warp_id ) +bool barrier_set_t::warp_waiting_at_barrier( unsigned warp_id ) const { return m_warp_at_barrier.test(warp_id); } @@ -4075,11 +3182,37 @@ void shader_core_ctx::warp_exit( unsigned warp_id ) m_barriers.warp_exit( warp_id ); } -bool shader_core_ctx::warp_waiting_at_barrier( unsigned warp_id ) +bool shader_core_ctx::warp_waiting_at_barrier( unsigned warp_id ) const { return m_barriers.warp_waiting_at_barrier(warp_id); } +void shader_core_ctx::set_at_memory_barrier( unsigned warp_id ) +{ + m_warp[warp_id].set_membar(); +} + +bool shader_core_ctx::warp_waiting_at_mem_barrier( unsigned warp_id ) +{ + if( !m_warp[warp_id].get_membar() ) + return false; + if( !m_scoreboard->pendingWrites(warp_id) ) { + m_warp[warp_id].clear_membar(); + return false; + } + return true; +} + +bool shader_core_ctx::warp_waiting_for_atomics( unsigned warp_id ) const +{ + return m_warp[warp_id].get_n_atomic()>0; +} + +gpgpu_sim *shader_core_ctx::get_gpu() +{ + return m_gpu; +} + void shader_core_ctx::allocate_barrier( unsigned cta_id, warp_set_t warps ) { m_barriers.allocate_barrier(cta_id,warps); @@ -4090,36 +3223,384 @@ void shader_core_ctx::deallocate_barrier( unsigned cta_id ) m_barriers.deallocate_barrier(cta_id); } +void shader_core_ctx::decrement_atomic_count( unsigned wid ) +{ + assert( m_warp[wid].get_n_atomic() > 0 ); + m_warp[wid].dec_n_atomic(); +} + +bool shd_warp_t::done() +{ + return get_n_completed() == m_warp_size; +} + +bool shd_warp_t::waiting() +{ + if ( done() ) { + // waiting to be initialized with a kernel + return true; + } else if ( m_shader->warp_waiting_at_barrier(m_warp_id) ) { + // waiting for other warps in CTA to reach barrier + return true; + } else if ( m_shader->warp_waiting_at_mem_barrier(m_warp_id) ) { + // waiting for memory barrier + return true; + } else if ( m_shader->warp_waiting_for_atomics(m_warp_id) ) { + // waiting for atomic operation to complete at memory: + // this stall is not required for accurate timing model, but rather we + // stall here since if a call/return instruction occurs in the meantime + // the functional execution of the atomic when it hits DRAM can cause + // the wrong register to be read. + return true; + } + return false; +} + +void shd_warp_t::print( FILE *fout ) const +{ + if ( n_completed < m_warp_size ) { + fprintf( fout, "w%02u npc: 0x%04x, done:%2u a4f:%2u, i:%u s:%u a:%u b:%2u, (done: ", + m_warp_id, + m_next_pc, + n_completed, + n_avail4fetch, + m_inst_in_pipeline, + m_stores_outstanding, + m_n_atomic, + n_waiting_at_barrier ); + for (unsigned i = m_warp_id*m_warp_size; i < (m_warp_id+1)*m_warp_size; i++ ) { + if ( m_shader->ptx_thread_done(i) ) fprintf(fout,"1"); + else fprintf(fout,"0"); + if ( (((i+1)%4) == 0) && (i+1) < (m_warp_id+1)*m_warp_size ) + fprintf(fout,","); + } + fprintf(fout,") "); + fprintf(fout," last fetched @ %5llu", m_last_fetch); + if( m_imiss_pending ) + fprintf(fout," i-miss pending"); + fprintf(fout,"\n"); + } +} + +void shd_warp_t::print_ibuffer( FILE *fout ) const +{ + fprintf(fout," ibuffer[%2u] : ", m_warp_id ); + for( unsigned i=0; i < IBUFFER_SIZE; i++) { + const inst_t *inst = m_ibuffer[i]; + if( inst ) inst->print_insn(fout); + else fprintf(fout," <empty> "); + } + fprintf(fout,"\n"); +} + +pdom_warp_ctx_t::pdom_warp_ctx_t( unsigned wid, class shader_core_ctx *shdr ) +{ + m_warp_id=wid; + m_shader=shdr; + m_warp_size=m_shader->get_config()->warp_size; + m_stack_top = 0; + m_pc = (address_type*)calloc(m_warp_size * 2, sizeof(address_type)); + m_calldepth = (unsigned int*)calloc(m_warp_size * 2, sizeof(unsigned int)); + m_active_mask = (unsigned int*)calloc(m_warp_size * 2, sizeof(unsigned int)); + m_recvg_pc = (address_type*)calloc(m_warp_size * 2, sizeof(address_type)); + m_branch_div_cycle = (unsigned long long *)calloc(m_warp_size * 2, sizeof(unsigned long long )); + reset(); +} + +void pdom_warp_ctx_t::reset() +{ + m_stack_top = 0; + memset(m_pc, -1, m_warp_size * 2 * sizeof(address_type)); + memset(m_calldepth, 0, m_warp_size * 2 * sizeof(unsigned int)); + memset(m_active_mask, 0, m_warp_size * 2 * sizeof(unsigned int)); + memset(m_recvg_pc, -1, m_warp_size * 2 * sizeof(address_type)); + memset(m_branch_div_cycle, 0, m_warp_size * 2 * sizeof(unsigned long long )); +} + +void pdom_warp_ctx_t::launch( address_type start_pc, unsigned active_mask ) +{ + reset(); + m_pc[0] = start_pc; + m_calldepth[0] = 1; + m_active_mask[0] = active_mask; +} + +unsigned pdom_warp_ctx_t::get_active_mask() const +{ + return m_active_mask[m_stack_top]; +} + +void mshr_entry::init( new_addr_type address, bool wr, memory_space_t space, unsigned warp_id ) +{ + static unsigned next_request_uid = 1; + m_request_uid = next_request_uid++; + m_status = INITIALIZED; + m_addr = address; + m_mf = NULL; + m_merged_on_other_reqest = false; + m_merged_requests =NULL; + m_iswrite = wr; + m_isinst = space==instruction_space; + m_islocal = is_local(space); + m_isconst = is_const(space); + m_istexture = space==tex_space; + m_insts.clear(); + m_warp_id = warp_id; +} + +void mshr_entry::set_status( enum mshr_status status ) +{ + mshr_entry * req = this; + while (req) { + req->m_status = status; + req = req->m_merged_requests; + } +#if DEBUGL1MISS + printf("cycle %d Addr %x %d \n",gpu_sim_cycle,CACHE_TAG_OF_64(m_addr),status); +#endif +} + +void mshr_entry::print(FILE *fp, unsigned mask) const +{ + if ( mask & 0x100 ) { + fprintf(fp, "MSHR(%u): w%2u req uid=%5u, %s (0x%llx) merged:%d status:%s ", + m_id, + m_warp_id, + m_request_uid, + (m_iswrite)? "store" : "load ", + m_addr, + (m_merged_requests != NULL || m_merged_on_other_reqest), + MSHR_Status_str[m_status]); + if ( m_mf ) + ptx_print_insn( m_mf->pc, fp ); + fprintf(fp,"\n"); + if ( mask & 0x200 ) { + for (unsigned i = 0; i < m_insts.size(); i++) { + fprintf(fp,"\tthread: UID:%d HW:%d ReqAddr:0x%llx\n", + m_insts[i].uid, m_insts[i].hw_thread_id, m_insts[i].memreqaddr); + } + } + } +} + void opndcoll_rfu_t::init( unsigned num_collectors_alu, unsigned num_collectors_sfu, unsigned num_banks, - const shader_core_ctx *shader ) + shader_core_ctx *shader, + inst_t **alu_port, + inst_t **sfu_port ) { - unsigned num_alu_cu = gpgpu_operand_collector_num_units; - unsigned num_sfu_cu = gpgpu_operand_collector_num_units_sfu; - m_num_collectors = num_alu_cu+num_sfu_cu; + m_num_collectors = num_collectors_alu+num_collectors_sfu; m_shader=shader; m_arbiter.init(m_num_collectors,num_banks); - m_alu_port = shader->pipeline_reg[ID_EX]; - m_sfu_port = shader->pipeline_reg[OC_EX_SFU]; + m_alu_port = alu_port; + m_sfu_port = sfu_port; - m_dispatch_units[ m_alu_port ].init( num_alu_cu ); - m_dispatch_units[ m_sfu_port ].init( num_sfu_cu ); + m_dispatch_units[ m_alu_port ].init( num_collectors_alu ); + m_dispatch_units[ m_sfu_port ].init( num_collectors_sfu ); m_num_banks = num_banks; + + m_bank_warp_shift = 0; + m_warp_size = shader->get_config()->warp_size; + m_bank_warp_shift = (unsigned)(int) (log(m_warp_size+0.5) / log(2.0)); + assert( (m_bank_warp_shift == 5) || (m_warp_size != 32) ); + m_cu = new collector_unit_t[m_num_collectors]; unsigned c=0; - for(; c<num_alu_cu; c++) { - m_cu[c].init(c,m_alu_port); + for(; c<num_collectors_alu; c++) { + m_cu[c].init(c,m_alu_port,num_banks,m_bank_warp_shift,m_warp_size,this); m_free_cu[m_alu_port].push_back(&m_cu[c]); m_dispatch_units[m_alu_port].add_cu(&m_cu[c]); } for(; c<m_num_collectors; c++) { - m_cu[c].init(c,m_sfu_port); + m_cu[c].init(c,m_sfu_port,num_banks,m_bank_warp_shift,m_warp_size,this); m_free_cu[m_sfu_port].push_back(&m_cu[c]); m_dispatch_units[m_sfu_port].add_cu(&m_cu[c]); } } + +bool opndcoll_rfu_t::writeback( inst_t *warp ) +{ + // prefer not to stall writeback + inst_t *fvt=m_shader->first_valid_thread(warp); + if (!fvt) + return true; // nothing to do + return writeback(*fvt); +} + +int register_bank(int regnum, int tid, unsigned num_banks, unsigned bank_warp_shift) +{ + int bank = regnum; + if (bank_warp_shift) + bank += tid >> bank_warp_shift; + return bank % num_banks; +} + +bool opndcoll_rfu_t::writeback( const inst_t &fvt ) +{ + int tid = fvt.hw_thread_id; + assert( tid >= 0 ); // must be a valid instruction + std::list<unsigned> regs = m_shader->get_regs_written(fvt); + std::list<unsigned>::iterator r; + unsigned last_reg = -1; + unsigned n=0; + for( r=regs.begin(); r!=regs.end();r++,n++ ) { + unsigned reg = *r; + unsigned bank = register_bank(reg,tid,m_num_banks,m_bank_warp_shift); + if( m_arbiter.bank_idle(bank) ) { + m_arbiter.allocate_bank_for_write(bank,op_t(&fvt,reg,m_num_banks,m_bank_warp_shift)); + } else { + return false; + } + last_reg=reg; + } + return true; +} + +void opndcoll_rfu_t::dispatch_ready_cu() +{ + port_to_du_t::iterator p; + for( p=m_dispatch_units.begin(); p!=m_dispatch_units.end(); ++p ) { + inst_t **port = p->first; + if( !m_shader->pipeline_regster_empty(*port) ) + continue; + dispatch_unit_t &du = p->second; + collector_unit_t *cu = du.find_ready(); + if( cu ) { + cu->dispatch(); + m_free_cu[port].push_back(cu); + } + } +} + +void opndcoll_rfu_t::allocate_cu( inst_t *&id_oc_reg ) +{ + inst_t *fvi = m_shader->first_valid_thread(id_oc_reg); + if( fvi ) { + inst_t **port = NULL; + if( fvi->op == SFU_OP ) + port = m_sfu_port; + else + port = m_alu_port; + if( !m_free_cu[port].empty() ) { + collector_unit_t *cu = m_free_cu[port].back(); + m_free_cu[port].pop_back(); + cu->allocate(id_oc_reg); + m_arbiter.add_read_requests(cu); + } + } +} + +void opndcoll_rfu_t::allocate_reads() +{ + // process read requests that do not have conflicts + std::list<op_t> allocated = m_arbiter.allocate_reads(); + std::map<unsigned,op_t> read_ops; + for( std::list<op_t>::iterator r=allocated.begin(); r!=allocated.end(); r++ ) { + const op_t &rr = *r; + unsigned reg = rr.get_reg(); + unsigned tid = rr.get_tid(); + unsigned bank = register_bank(reg,tid,m_num_banks,m_bank_warp_shift); + m_arbiter.allocate_for_read(bank,rr); + read_ops[bank] = rr; + } + std::map<unsigned,op_t>::iterator r; + for(r=read_ops.begin();r!=read_ops.end();++r ) { + op_t &op = r->second; + unsigned cu = op.get_oc_id(); + unsigned operand = op.get_operand(); + assert( cu < m_num_collectors ); + m_cu[cu].collect_operand(operand); + } +} + + +void gpgpu_sim::decrement_atomic_count( unsigned sid, unsigned wid ) +{ + m_sc[sid]->decrement_atomic_count(wid); +} + + +bool opndcoll_rfu_t::collector_unit_t::ready() const +{ + return (!m_free) && m_not_ready.none() && m_rfu->shader_core()->pipeline_regster_empty(*m_port); +} + +void opndcoll_rfu_t::collector_unit_t::dump(FILE *fp, const shader_core_ctx *shader ) const +{ + if( m_free ) { + fprintf(fp," <free>\n"); + } else { + shader->print_warp(m_warp,fp,0,0); + for( unsigned i=0; i < MAX_REG_OPERANDS; i++ ) { + if( m_not_ready.test(i) ) { + std::string r = m_src_op[i].get_reg_string(); + fprintf(fp," '%s' not ready\n", r.c_str() ); + } + } + } +} + +void opndcoll_rfu_t::collector_unit_t::init( unsigned n, + inst_t **port, + unsigned num_banks, + unsigned log2_warp_size, + unsigned warp_size, + opndcoll_rfu_t *rfu ) +{ + m_rfu=rfu; + m_cuid=n; + m_port=port; + m_num_banks=num_banks; + assert(m_warp==NULL); + m_warp = (inst_t*)calloc(sizeof(inst_t),warp_size); + m_rfu->shader_core()->clear_stage(m_warp); + m_bank_warp_shift=log2_warp_size; +} + +void opndcoll_rfu_t::collector_unit_t::allocate( inst_t *&pipeline_reg ) +{ + assert(m_free); + assert(m_not_ready.none()); + m_free = false; + inst_t *fvi = m_rfu->shader_core()->first_valid_thread(pipeline_reg); + if( fvi ) { + m_tid = fvi->hw_thread_id; + m_warp_id = m_tid/m_rfu->shader_core()->get_config()->warp_size; + for( unsigned op=0; op < 4; op++ ) { + int reg_num = fvi->arch_reg[4+op]; // this math needs to match that used in function_info::ptx_decode_inst + if( reg_num >= 0 ) { // valid register + m_src_op[op] = op_t( this, op, reg_num, m_num_banks, m_bank_warp_shift ); + m_not_ready.set(op); + } else + m_src_op[op] = op_t(); + } + m_rfu->shader_core()->move_warp(m_warp,pipeline_reg); + } +} + +void opndcoll_rfu_t::collector_unit_t::dispatch() +{ + assert( m_not_ready.none() ); + m_rfu->shader_core()->move_warp(*m_port,m_warp); + m_free=true; + for( unsigned i=0; i<MAX_REG_OPERANDS;i++) + m_src_op[i].reset(); +} + +bool shader_core_ctx::ptx_thread_done( unsigned hw_thread_id ) const +{ + assert( hw_thread_id < m_config->n_thread_per_shader ); + ptx_thread_info *thd = m_thread[ hw_thread_id ].m_functional_model_thread_state; + return (thd==NULL) || thd->is_done(); +} + +class ptx_thread_info *shader_core_ctx::get_thread_state( unsigned hw_thread_id ) +{ + assert( hw_thread_id < m_config->n_thread_per_shader ); + return m_thread[ hw_thread_id ].m_functional_model_thread_state; +} |
