diff options
| author | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
|---|---|---|
| committer | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
| commit | 69f2911e04ffb1b19eef1fafb8c040af271f656e (patch) | |
| tree | 231d3b6bdc3a202f7c255bfcf7bf2c36e32cee9e /src/gpgpu-sim/shader.cc | |
creating branch for adding support for CUDA 3.x and Fermi
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 6829]
Diffstat (limited to 'src/gpgpu-sim/shader.cc')
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 3811 |
1 files changed, 3811 insertions, 0 deletions
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc new file mode 100644 index 0000000..8f3f700 --- /dev/null +++ b/src/gpgpu-sim/shader.cc @@ -0,0 +1,3811 @@ +/* + * shader.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Ivan Sham, Henry Wong, Dan O'Connor, Henry Tran and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <float.h> +#include "shader.h" +#include "gpu-sim.h" +#include "addrdec.h" +#include "dram.h" +#include "dwf.h" +#include "warp_tracker.h" +#include "cflogger.h" +#include "gpu-misc.h" +#include "../cuda-sim/ptx_sim.h" +#include "../cuda-sim/ptx-stats.h" +#include "../cuda-sim/dram_callback.h" +#include "mem_fetch.h" +#include <string.h> + +#define PRIORITIZE_MSHR_OVER_WB 1 +#define MAX(a,b) (((a)>(b))?(a):(b)) + +extern unsigned int mergemiss; +extern unsigned int L1_write_miss; +extern unsigned int L1_read_miss; +extern unsigned int L1_write_hit_on_miss; +extern unsigned int L1_writeback; +extern unsigned int L1_texture_miss; +extern unsigned int L1_const_miss; + +extern unsigned int finished_trace; +extern int gpgpu_perfect_mem; +extern int gpgpu_no_dl1; +extern char *gpgpu_cache_texl1_opt; +extern char *gpgpu_cache_constl1_opt; +extern char *gpgpu_cache_dl1_opt; +extern unsigned int gpu_n_thread_per_shader; +extern unsigned int gpu_n_mshr_per_shader; +extern unsigned int gpu_n_shader; +extern unsigned int gpu_n_mem; +extern int gpgpu_reg_bankconflict; +extern int gpgpu_dram_sched_queue_size; +extern int gpgpu_memlatency_stat; +extern dram_t **dram; +extern int *num_warps_issuable; +extern int *num_warps_issuable_pershader; + +extern unsigned long long gpu_sim_insn; +extern unsigned long long gpu_sim_insn_no_ld_const; +extern unsigned long long gpu_sim_insn_last_update; +extern unsigned long long gpu_completed_thread; +extern unsigned long long gpu_sim_cycle; +extern shader_core_ctx_t **sc; +extern unsigned int gpgpu_pre_mem_stages; +extern int gpgpu_no_divg_load; +extern unsigned int gpgpu_thread_swizzling; +extern unsigned int gpgpu_strict_simd_wrbk; +extern unsigned int warp_conflict_at_writeback; +extern unsigned int gpgpu_commit_pc_beyond_two; +extern int gpgpu_spread_blocks_across_cores; +extern int gpgpu_cflog_interval; + +extern unsigned int gpu_stall_by_MSHRwb; +extern unsigned int gpu_stall_shd_mem; +extern unsigned int gpu_stall_sh2icnt; + +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; +extern unsigned int **totalbankaccesses; //bankaccesses[shader id][dram chip id][bank id] +extern unsigned int *MCB_accesses; //upon cache miss, tracks which memory controllers accessed by a warp +extern unsigned int *num_MCBs_accessed; //tracks how many memory controllers are accessed whenever any thread in a warp misses in cache +extern unsigned int *max_return_queue_length; + +unsigned int *shader_cycle_distro; + +unsigned int g_waiting_at_barrier = 0; + +unsigned int gpgpu_shmem_size = 16384; +unsigned int gpgpu_shader_registers = 8192; +unsigned int gpgpu_shader_cta = 8; + +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; + +extern int gpgpu_cache_wt_through; + +int gpgpu_shmem_bkconflict = 0; +unsigned int gpgpu_n_shmem_bkconflict = 0; +int gpgpu_n_shmem_bank = 16; + +int gpgpu_cache_bkconflict = 0; +unsigned int gpgpu_n_cache_bkconflict = 0; +unsigned int gpgpu_n_cmem_portconflict = 0; +int gpgpu_n_cache_bank = 16; + +extern int gpu_runtime_stat_flag; +int gpgpu_warpdistro_shader = -1; + +int gpgpu_interwarp_mshr_merge = 0; +int gpgpu_n_intrawarp_mshr_merge = 0; + +extern int gpgpu_partial_write_mask; +int gpgpu_n_partial_writes = 0; + +extern int gpgpu_n_mem_write_local; +extern int gpgpu_n_mem_write_global; + +#ifndef MhZ + #define MhZ *1000000 +#endif +extern double core_freq; +extern double icnt_freq; +extern double dram_freq; +extern double l2_freq; + +int gpgpu_shmem_port_per_bank = 4; +int gpgpu_cache_port_per_bank = 4; +int gpgpu_const_port_per_bank = 4; +int gpgpu_shmem_pipe_speedup = 2; + +unsigned int gpu_max_cta_per_shader = 8; +unsigned int gpu_padded_cta_size = 32; +int gpgpu_local_mem_map = 1; + + +extern int pdom_sched_type; +extern int n_pdom_sc_orig_stat; +extern int n_pdom_sc_single_stat; +extern int gpgpu_cuda_sim; +extern unsigned long long gpu_tot_sim_cycle; + +extern unsigned g_max_regs_per_thread; +void ptx_decode_inst( void *thd, unsigned *op, int *i1, int *i2, int *i3, int *i4, int *o1, int *o2, int *o3, int *o4, int *vectorin, int *vectorout, int *arch_reg ); +unsigned ptx_get_inst_op( void *thd); +void ptx_exec_inst( void *thd, address_type *addr, unsigned *space, unsigned *data_size, dram_callback_t* callback, unsigned warp_active_mask); +int ptx_branch_taken( void *thd ); +void ptx_sim_free_sm( void** thread_info ); +unsigned ptx_sim_init_thread( void** thread_info, int sid, unsigned tid,unsigned threads_left,unsigned num_threads, core_t *core, unsigned hw_cta_id, unsigned hw_warp_id); +unsigned ptx_sim_cta_size(); +const struct gpgpu_ptx_sim_kernel_info* ptx_sim_kernel_info(); +void set_option_gpgpu_spread_blocks_across_cores(int option); +int ptx_thread_done( void *thr ); +unsigned ptx_thread_donecycle( void *thr ); +int ptx_thread_get_next_pc( void *thd ); +void* ptx_thread_get_next_finfo( void *thd ); +int ptx_thread_at_barrier( void *thd ); +int ptx_thread_all_at_barrier( void *thd ); +unsigned long long ptx_thread_get_cta_uid( void *thd ); +void ptx_thread_reset_barrier( void *thd ); +void ptx_thread_release_barrier( void *thd ); +void ptx_print_insn( address_type pc, FILE *fp ); +int ptx_set_tex_cache_linesize( unsigned linesize); +void time_vector_update(unsigned int uid,int slot ,long int cycle,int type); + + + +///////////////////////////////////////////////////////////////////////////// +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + +static const char* MSHR_Status_str[] = { + "INITIALIZED", + "IN_ICNT2MEM", + "IN_ICNTOL2QUEUE", + "IN_L2TODRAMQUEUE", + "IN_DRAM_REQ_QUEUE", + "IN_DRAMTOL2QUEUE", + "IN_L2TOICNTQUEUE_HIT", + "IN_L2TOICNTQUEUE_MISS", + "IN_ICNT2SHADER", + "FETCHED", +}; + +// a helper function that deduce if a mshr contains an atomic operation +bool isatomic(mshr_entry_t *mshr) +{ + return (mshr->insts[0].callback.function != NULL); +} + +#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* 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); + + 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; + } + return interwarp_mshr_merge; + } + + void 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); + } + + mshr_entry* shader_get_mergeable_mshr(mshr_entry_t* mshr) + { + if (!can_merge(mshr)) return NULL; + return lookup(mshr->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); + + //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(); + } + + 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; + + 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; + } +}; + + + +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; + } +} + +inline void mshr_shader_unit::mshr_return_from_mem(mshr_entry *mshr){ + mshr_update_status(mshr, 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; + } + } +} + +void mshr_return_from_mem(shader_core_ctx_t * shader, mshr_entry_t* mshr){ + shader->mshr_unit->mshr_return_from_mem(mshr); +} + +unsigned get_max_mshr_used(shader_core_ctx_t * shader){ + return shader->mshr_unit->get_max_mshr_used(); +} + + +void mshr_print(FILE* fp, shader_core_ctx_t *shader) { + shader->mshr_unit->print(fp, shader); +} + +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); + } + } + } +} + +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 create_nop_inst() // just because C++ does not have designated initializer list.... +{ + 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; + return nop_inst; +} + +static inst_t nop_inst = create_nop_inst(); + +int log2i(int n) { + int lg; + lg = -1; + while (n) { + n>>=1;lg++; + } + return lg; +} + +extern unsigned int gpu_n_warp_per_shader; + +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; + 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; + + 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 = new shader_core_ctx_t( gpu_n_warp_per_shader, gpgpu_shader_cta ); + + 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*)); + 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; + } + } + + 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; + } + } + } + sc->n_threads = n_threads; + sc->thread = (thread_ctx_t*) calloc(sizeof(thread_ctx_t), n_threads); + sc->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; + } + + sc->n_active_cta = 0; + for (i = 0; i<MAX_CTA_PER_SHADER; i++ ) { + sc->cta_status[i]=0; + } + //Warp variable initializations + sc->next_warp = 0; + sc->branch_priority = 0; + sc->max_branch_priority = (int*) malloc(sizeof(int)*n_threads); + + + for (unsigned i = 0; i<n_threads; i++) { + sc->max_branch_priority[i] = INT_MAX; + sc->thread[i].id = i; + + sc->thread[i].warp_priority = sc->max_branch_priority[i]; + sc->thread[i].avail4fetch = 0; + sc->thread[i].m_waiting_at_barrier = 0; + + 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); + + 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)); + } + + sc->waiting_at_barrier = 0; + + 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); + + if (sc->using_commit_queue) { + sc->thd_commit_queue = dq_create("thd_commit_queue", 0, 0, 0); + } + + sc->shmem_size = gpgpu_shmem_size; + sc->n_registers = gpgpu_shader_registers; + sc->n_cta = gpgpu_shader_cta; + + sc->shader_memory_new_instruction_processed = false; + + return sc; +} + + +unsigned shader_reinit(shader_core_ctx_t *sc, int start_thread, int end_thread ) +{ + 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(); + } + } + + 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 / 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) +{ + 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); + } + } + } + } else if (shader->model == DWF) { + dwf_init_CTA(shader->sid, start_thread, n_thread, start_pc); + } + + 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) +{ + 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 (int i=0; i<pipe_simd_width; i++) { + if (shader->pipeline_reg[IF_ID][i].hw_thread_id != -1 ) { + return 1; // stalled + } + } + + shader->warp_part2issue = 0; // reset pointer to first warp part + shader->new_warp_TS = 1; + + return 0; // not stalled +} + +// initalize the pipeline stage register to nops +void shader_clear_stage_reg(shader_core_ctx_t *shader, int stage) +{ + for (unsigned i=0; i<warp_size; i++) { + shader->pipeline_reg[stage][i] = nop_inst; + } +} + +// return the next pc of a thread +address_type shader_thread_nextpc(shader_core_ctx_t *shader, int tid) +{ + assert( gpgpu_cuda_sim ); + address_type pc = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); + return pc; +} + +// 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 ) +{ + 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++; + + shader->next_warp = (shader->next_warp+1)%n_warp; +} + +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 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; + while (!found) { + for (i=0;i<ready_warp_count;i++) { + if (selected_warp==ready_warps[i]) { + found=1; + } + } + if (!found) + selected_warp = (selected_warp + 1 ) % n_warp; + } + break; + default: + assert(0); + } + if (found) { + if (ready_warp_count==1) { + n_pdom_sc_single_stat++; + } else { + 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; + int warp_ok = 0; + int n_warp = shader->n_threads/warp_size; + int complete = 0; + 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; + break; + } else { + complete = 1; + } + } + if (complete) { + return; + } + + if (shader_fetch_stalled(shader)) { + 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; + } + + static int* last_warp; //keeps track of last warp issued per shader + if (!last_warp) { + last_warp = (int*)calloc(gpu_n_shader,sizeof(int)); + } + + + // 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 { + // A valid warp is found at this point + tmp_ready_warps[ready_warp_count] = shader->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]; + } + + num_warps_issuable[ready_warp_count]++; + num_warps_issuable_pershader[shader->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); + if (tmp_warp != -1) { + shader->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 ; + 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*shader->next_warp; + + pdom_warp_ctx_t *scheduled_warp = &(shader->pdom_warp[warp_id]); + + int stack_top = scheduled_warp->m_stack_top; + + address_type top_pc = scheduled_warp->m_pc[stack_top]; + unsigned int top_active_mask = scheduled_warp->m_active_mask[stack_top]; + address_type top_recvg_pc = scheduled_warp->m_recvg_pc[stack_top]; + + assert(top_active_mask != 0); + + const address_type null_pc = 0; + int warp_diverged = 0; + address_type new_recvg_pc = null_pc; + while (top_active_mask != 0) { + + // 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 (i = 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; + } + } + } + + // discard the new entry if its PC matches with reconvergence PC + // that automatically reconverges the entry + if (tmp_next_pc == top_recvg_pc) continue; + + // this new entry is not converging + // if this entry does not include thread from the warp, divergence occurs + if (top_active_mask != 0 && warp_diverged == 0) { + warp_diverged = 1; + // modify the existing top entry into a reconvergence entry in the pdom stack + new_recvg_pc = get_converge_point(top_pc,first_active_thread); + if (new_recvg_pc != top_recvg_pc) { + scheduled_warp->m_pc[stack_top] = new_recvg_pc; + scheduled_warp->m_branch_div_cycle[stack_top] = gpu_sim_cycle; + stack_top += 1; + scheduled_warp->m_branch_div_cycle[stack_top] = 0; + } + } + + // discard the new entry if its PC matches with reconvergence PC + if (warp_diverged && tmp_next_pc == new_recvg_pc) continue; + + // update the current top of pdom stack + scheduled_warp->m_pc[stack_top] = tmp_next_pc; + scheduled_warp->m_active_mask[stack_top] = tmp_active_mask; + if (warp_diverged) { + scheduled_warp->m_calldepth[stack_top] = 0; + scheduled_warp->m_recvg_pc[stack_top] = new_recvg_pc; + } else { + scheduled_warp->m_recvg_pc[stack_top] = top_recvg_pc; + } + stack_top += 1; // set top to next entry in the pdom stack + } + 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); + + // schedule threads according to active mask on the top of pdom stack + for (i = 0; i < (int)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]); + } + } + shader->next_warp = (shader->next_warp+1)%n_warp; + + // 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 ) { + if ( check_pc == (unsigned)-1 ) { + check_pc = shader->pipeline_reg[TS_IF][i].pc; + } else { + assert( check_pc == shader->pipeline_reg[TS_IF][i].pc ); + } + } + } +} + + +void get_pdom_stack_top_info( unsigned sid, unsigned tid, unsigned *pc, unsigned *rpc ) +{ + 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]; +} + +void shader_fetch_mimd( shader_core_ctx_t *shader, unsigned int shader_number ) +{ + unsigned int last_issued_thread = 0; + + if (shader_fetch_stalled(shader)) { + return; + } + shader_clear_stage_reg(shader, 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; + 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) { + 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); + + if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*warp_size); + memset(pc_cnt,0,sizeof(int)*warp_size); + + // go through each thread in the given warp + for (unsigned i=0; i< 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]); + + // check to see if the pc has occured before + for (unsigned j=0; j<n_pc; j++) { + if (thd_pc == pc[j]) { + tid_split[j][pc_cnt[j]] = tid_in[i]; + pc_cnt[j]++; + matched = 1; + break; + } + } + // if not, put the tid in a seperate warp + if (!matched) { + assert(n_pc < warp_size); + tid_split[n_pc][0] = tid_in[i]; + pc[n_pc] = thd_pc; + pc_cnt[n_pc] = 1; + n_pc++; + } + } + return n_pc; +} + +// see if this warp just executed the barrier instruction +int warp_reached_barrier(int *tid_in, shader_core_ctx_t *shader) +{ + int reached_barrier = 0; + for (unsigned i=0; i<warp_size; i++) { + if (tid_in[i] < 0) continue; + if (shader->thread[tid_in[i]].m_reached_barrier) { + reached_barrier = 1; + break; + } + } + return reached_barrier; +} + +// 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) { + 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); + + if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*warp_size); + memset(pc_cnt,0,sizeof(int)*warp_size); + + // go through each thread in the given warp + for (unsigned i=0; i<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]); + + int thd_cta = ptx_thread_get_cta_uid( shader->thread[tid_in[i]].ptx_thd_info ); + + // check to see if the pc has occured before + for (unsigned j=0; j<n_pc; j++) { + if (thd_pc == pc[j] && thd_cta == cta[j]) { + tid_split[j][pc_cnt[j]] = tid_in[i]; + pc_cnt[j]++; + matched = 1; + break; + } + } + // if not, put the tid in a seperate warp + if (!matched) { + assert(n_pc < warp_size); + tid_split[n_pc][0] = tid_in[i]; + pc[n_pc] = thd_pc; + cta[n_pc] = thd_cta; + pc_cnt[n_pc] = 1; + n_pc++; + } + } + return n_pc; +} + +void shader_fetch_simd_dwf( shader_core_ctx_t *shader, unsigned int shader_number ) { + + 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); + } + if (!tid_out) { + tid_out = (int*) malloc(sizeof(int)*warp_size); + memset(tid_out, -1, sizeof(int)*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; + } + } + + static address_type *thd_pc = NULL; + if (!thd_pc) thd_pc = (address_type*)malloc(sizeof(address_type)*warp_size); + static int *thd_cta = NULL; + if (!thd_cta) thd_cta = (int*)malloc(sizeof(int)*warp_size); + + int warpupdate_bw = 1; + while (!dq_empty(shader->thd_commit_queue) && 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); + + int reached_barrier = warp_reached_barrier(tid_commit, shader); + + unsigned n_warp_update; + if (reached_barrier) { + n_warp_update = split_warp_by_cta(tid_commit, shader, tid_split, thd_pc, thd_cta); + } else { + n_warp_update = split_warp_by_pc(tid_commit, shader, tid_split, thd_pc); + } + + if (n_warp_update > 2) 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++) { + 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; + } + dwf_clear_accessed(shader->sid); + if (reached_barrier) { + dwf_update_warp_at_barrier(shader->sid, tid_split[i], thd_pc[i], thd_cta[i]); + } else { + dwf_update_warp(shader->sid, tid_split[i], thd_pc[i]); + } + } + + free_commit_warp(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); + + if (shader_fetch_stalled(shader)) { + return; + } + shader_clear_stage_reg(shader, TS_IF); + + address_type scheduled_pc; + dwf_issue_warp(shader->sid, tid_out, &scheduled_pc); + + for (unsigned i=0; i<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); + } + } +} + +void print_shader_cycle_distro( FILE *fout ) +{ + 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, "\n"); +} + +void inflight_memory_insn_add( shader_core_ctx_t *shader, inst_t *mem_insn) +{ + if (enable_ptx_file_line_stats) { + ptx_file_line_stats_add_inflight_memory_insn(shader->sid, mem_insn->pc); + } +} + +void inflight_memory_insn_sub( shader_core_ctx_t *shader, inst_t *mem_insn) +{ + if (enable_ptx_file_line_stats) { + ptx_file_line_stats_sub_inflight_memory_insn(shader->sid, mem_insn->pc); + } +} + +void report_exposed_memory_latency( shader_core_ctx_t *shader ) +{ + if (enable_ptx_file_line_stats) { + ptx_file_line_stats_commit_exposed_latency(shader->sid, 1); + } +} + +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 ); + +void shader_fetch( shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) +{ + assert(shader->model < NUM_SIMD_MODEL); + int n_warp_parts = warp_size/pipe_simd_width; + + // 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) { + + if (shader->model == POST_DOMINATOR || shader->model == NO_RECONVERGE) { + int warpupdate_bw = 1; // number of warps to be unlocked per scheduler cycle + while (!dq_empty(shader->thd_commit_queue) && warpupdate_bw > 0) { + // grab a committed warp and unlock it here + int *tid_commit = (int*)dq_pop(shader->thd_commit_queue); + for (unsigned i = 0; i < warp_size; i++) { + if (tid_commit[i] >= 0) { + shader->thread[tid_commit[i]].avail4fetch++; + assert(shader->thread[tid_commit[i]].avail4fetch <= 1); + assert( shader->warp[wid_from_hw_tid(tid_commit[i],warp_size)].n_avail4fetch < (unsigned)warp_size ); + shader->warp[wid_from_hw_tid(tid_commit[i],warp_size)].n_avail4fetch++; + } + } + warpupdate_bw -= 1; + free_commit_warp(tid_commit); + } + } + + 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; + } + + static int *tid_out = NULL; + if (!tid_out) { + tid_out = (int*) malloc(sizeof(int) * warp_size); + } + memset(tid_out, -1, sizeof(int)*warp_size); + + if (!shader_cycle_distro) { + shader_cycle_distro = (unsigned int*) calloc(warp_size + 3, sizeof(unsigned int)); + } + + 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; + } + } + wpt_register_warp(tid_out, shader); + 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; + + 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 ( 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; + } + } + + 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; + + 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; + } + } + + 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 (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 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; + } +} + +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 ( int space ) { + return((space) == TEX_DIRECTIVE); +} + +inline int is_const ( int space ) { + return((space) == CONST_DIRECTIVE || (space) == PARAM_DIRECTIVE); +} + +inline int is_local ( int space ) { + return((space) == LOCAL_DIRECTIVE); +} + +inline int is_param ( int space ) { + return((space) == PARAM_DIRECTIVE); +} + +inline int is_shared ( int space ) { + return((space) == SHARED_DIRECTIVE); +} + +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) +{ + return (addr >> segment_size_lg2bytes); +} + + +inline address_type translate_local_memaddr(address_type localaddr, shader_core_ctx_t *shader, int tid) +{ + // 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) { + // 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; + } 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 *= 4; + localaddr += 0x100; + + return localaddr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Register Bank Conflict Structures + +int gpgpu_reg_bank_conflict_model = 0; + +#define MAX_REG_BANKS 32 +unsigned int gpgpu_num_reg_banks=8; // this needs to be less than MAX_REG_BANKS + +#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]; +}; + +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_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; + register int is_write; + 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 space, data_size; + int vectorin, vectorout; + int arch_reg[MAX_REG_OPERANDS] = { -1 }; + address_type regs_regs_PC = 0xDEADBEEF; + address_type warp_current_pc = 0x600DBEEF; + address_type warp_next_pc = 0x600DBEEF; + int warp_diverging = 0; + unsigned warp_id = -1; + unsigned cta_id = -1; + + // 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++) { + int next_stage = (shader->using_rrstage)? ID_RR:ID_EX; + if (shader->pipeline_reg[next_stage][i].hw_thread_id != -1 ) { + return; /* stalled */ + } + } + + check_stage_pcs(shader,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; + 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); + 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; + } + + // execute the instruction functionally + 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; + if ( gpgpu_cuda_sim ) { + int arch_reg[MAX_REG_OPERANDS]; + ptx_decode_inst( shader->thread[tid].ptx_thd_info, (unsigned*)&op, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg ); + ptx_exec_inst( shader->thread[tid].ptx_thd_info, &addr, &space, &data_size, &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].data_size = data_size; + 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 ); + } + + 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; + } + + 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 ); + + int release = ptx_thread_all_at_barrier( shader->thread[tid].ptx_thd_info ); //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 ) ) + continue; + int other_cta_uid = ptx_thread_get_cta_uid( shader->thread[t].ptx_thd_info ); + 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--; + } + } + if (shader->model == DWF) { + dwf_release_barrier( shader->sid, cta_uid ); + } + ptx_thread_release_barrier( shader->thread[tid].ptx_thd_info ); + } + } + } else { + assert( !shader->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; + + if ( op == STORE_OP ) { + is_write = TRUE; + } + + if ( op == BRANCH_OP ) { + int taken=0; + assert( gpgpu_cuda_sim ); + taken = ptx_branch_taken(shader->thread[tid].ptx_thd_info); + } + + // 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) { + warp_next_pc = regs_regs_PC; + } else { + warp_diverging = 1; + } + } + + // direct the instruction to the appropriate next stage (config dependent) + int next_stage = (shader->using_rrstage)? ID_RR:ID_EX; + shader->pipeline_reg[next_stage][i] = shader->pipeline_reg[IF_ID][i]; + shader->pipeline_reg[next_stage][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); + } + + if ( shader->model == NO_RECONVERGE && touched_priority ) { + update_max_branch_priority(shader,warp_tid,grid_num); + } + shader->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); + } +} + +unsigned int n_regconflict_stall = 0; + + +int regfile_hash(signed thread_number, unsigned simd_size, unsigned n_banks) { + if (gpgpu_thread_swizzling) { + signed warp_ID = thread_number / simd_size; + return((thread_number + warp_ID) % n_banks); + } else { + return(thread_number % 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); + } + + 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--; + } + 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]; + } + } + // 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; + } + +} + + +void shader_execute( shader_core_ctx_t *shader, + unsigned int shader_number ) { + + 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 ) { + //printf("stalled in shader_execute\n"); + return; // stalled + } + } else { + if (shader->pipeline_reg[EX_MM][i].hw_thread_id != -1 ) + return; // stalled + } + } + + check_stage_pcs(shader,ID_EX); + + for (i=0; i<pipe_simd_width; i++) { + if (shader->pipeline_reg[ID_EX][i].hw_thread_id == -1 ) + continue; // bubble + if (gpgpu_pre_mem_stages) { + shader->pre_mem_pipeline[0][i] = shader->pipeline_reg[ID_EX][i]; + shader->pre_mem_pipeline[0][i].ex_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + } else { + shader->pipeline_reg[EX_MM][i] = shader->pipeline_reg[ID_EX][i]; + shader->pipeline_reg[EX_MM][i].ex_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + } + shader->pipeline_reg[ID_EX][i] = nop_inst; + } + + if (!gpgpu_pre_mem_stages) { + // inform memory stage that a new instruction has arrived + shader->shader_memory_new_instruction_processed = 0; + } +} + +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; + } + } + 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; + } +} + +int gpgpu_coalesce_arch = 13; + +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(0),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. + unsigned 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) +{ + 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; +} + + +inline address_type line_size_based_tag_func(address_type address, unsigned line_size) +{ + return ((address) & (~((address_type)line_size - 1))); +} + +inline 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) +{ + return 1; +} + +inline int shmem_bank_func(address_type add, unsigned line_size) +{ + return shmem_bank(add); +} + +inline int 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); +} + +#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 << " " << gpu_sim_cycle << std::endl; + assert(accessq[i].space == (unsigned) 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;} + } + } +} + +// 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) +{ + // calculates the memory accesses for a generic cache with banks and tags. + // can be used for coalesescing + + //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; + + 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) { + 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. + bool isatomic = (insns[i].callback.function != NULL); + unsigned match = 0; + 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 + 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; + 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. + // ie. order will be less than part for all previous loads in previous parts, so: + 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. +} + + +void shader_memory_shared_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +{ + get_memory_access_list<&shmem_bank_func, &null_tag_func>(shader->pipeline_reg[EX_MM], paths, SHARED_MEM_PATH, + 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 :) +} + +void shader_memory_const_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +{ + 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; + for (unsigned i = qbegin; i < accessq.size(); i++) { + if (is_param(accessq[i].space)) { + accessq[i].cache_hit = true; + } else { + cache_request_status status = shd_cache_access_wb(shader->L1constcache, + accessq[i].addr, + WORD_SIZE, //this field is ingored. + 0, //should always be a read + shader->gpu_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++; + } + 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) +{ + 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, + 1, //warp parts + shader->L1texcache->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; + for (unsigned i = qbegin; i < accessq.size(); i++) { + cache_request_status status = shd_cache_access_wb(shader->L1texcache, + accessq[i].addr, + WORD_SIZE, //this field is ignored. + 0, //should always be a read + shader->gpu_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++; + 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) +{ + unsigned qbegin = accessq.size(); + unsigned warp_parts = 1; + unsigned line_size = shader->L1cache->line_sz; + if (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); + // 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: + 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); + + for (unsigned i = qbegin; i < accessq.size(); i++) { + if (gpgpu_coalesce_arch == 13 and 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; + bool low = quarter_counts[0] or quarter_counts[1]; + bool high = quarter_counts[2] or quarter_counts[3]; + if (accessq[i].req_size == 128) { + if (low xor high) { //can reduce size + accessq[i].req_size = 64; + if (high) accessq[i].addr += 64; + low = quarter_counts[0] or quarter_counts[2]; //set low and high for next pass + high = quarter_counts[1] or quarter_counts[3]; + } + } + if (accessq[i].req_size == 64) { + if (low xor high) { //can reduce size + accessq[i].req_size = 32; + if (high) accessq[i].addr += 32; + } + } + } + } +} + + + +mem_stage_stall_type send_mem_request(shader_core_ctx_t *shader, mem_access_t &access){ + inst_t* warp = shader->pipeline_reg[EX_MM]; + inst_t* req_head = warp + access.warp_indices[0]; + + 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++; + 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 + -1); + 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; + switch(access.space) { + case CONST_DIRECTIVE: + case PARAM_DIRECTIVE: + code = CONSTC; + access_type = CONST_ACC_R; + break; + case TEX_DIRECTIVE: + code = TEXTC; + access_type = TEXTURE_ACC_R; + break; + case GLOBAL_DIRECTIVE: + code = DCACHE; + access_type = (access.iswrite)? GLOBAL_ACC_W: GLOBAL_ACC_R; + break; + case LOCAL_DIRECTIVE: + code = DCACHE; + access_type = (access.iswrite)? LOCAL_ACC_W: LOCAL_ACC_R; + break; + default: + assert(0); // NOT A MEM SPACE; + break; + } + + //reserve mshr + if (requires_mshr and not access.reserved_mshr) { + + // can allocate mshr? + if (not shader->mshr_unit->has_mshr(1)) { + //no mshr available; + return MSHR_RC_FAIL; + } + + access.reserved_mshr = shader->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); + + 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; + } + } + + + // 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; + return ICNT_RC_FAIL; + } + + //send over interconnect + + unsigned cache_hits_waiting = 0; //fixme do we really want to be passing this in? + + partial_write_mask_t write_mask = NO_PARTIAL_WRITE; + if (access.iswrite) { + 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++; + } + } + + //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); + + } + + + //book keeping for mshr since this request is done (sent/accounted for) at this point; + 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 + } + + 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; + + } + } + } + + 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){ + //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; + 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(); + } + if (not accessq.empty()) { + rc_fail = BK_CONF; + fail_type = S_MEM; + 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; + for (unsigned i = 0; i < ports_per_bank; i++) { + 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. + 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; + } + 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. + } + accessq.pop_back(); + } + } + if (not accessq.empty() and rc_fail == NO_RC_FAIL) { + //no resource failed so must be a bank comflict. + rc_fail = BK_CONF; + } + return rc_fail; +} + +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_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){ + + 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 ); + 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. + } + } + 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){ + + 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 ); + if (fail != NO_RC_FAIL){ + rc_fail = fail; //keep other fails if this didn't fail. + fail_type = T_MEM; + } + return accessq.empty(); //done if empty. +} + + +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) { + //check cache + cache_request_status status = shd_cache_access_wb(shader->L1cache, + access.addr, + WORD_SIZE, //this field is ignored. + access.iswrite, + shader->gpu_cycle, + &access.wb_addr); + if (status == RESERVATION_FAIL) { + access.cache_checked = false; + return WB_CACHE_RSRV_FAIL; + } + access.cache_hit = (status == HIT); //if HIT_W_WT then still send to memory so "MISS" + if (status == MISS_W_WB) access.need_wb = true; + if (status == WB_HIT_ON_MISS and access.iswrite) + { + //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++; + // 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. + // 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). + } + if (status == WB_HIT_ON_MISS and not access.iswrite) { + //read has hit on a reserved cache line, + //we need to make sure cache check happens on same cycle as a mshr merge happens, otherwise we might miss it coming back + access.recheck_cache = true; + } + access.cache_checked = true; + } else { + access.cache_hit = false; + } + + if (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; + atom_exec.function(atom_exec.instruction, atom_exec.thread); + } + } + + if (!access.cache_hit) { + if (access.iswrite) L1_write_miss++; + else 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; + } + + if (rc_fail == BK_CONF or rc_fail == COAL_STALL) { + gpgpu_n_cache_bkconflict++; + } + } + return accessq.empty(); //done if empty. +} + +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) { + case SHARED_DIRECTIVE: + gpgpu_n_shmem_insn++; + break; + case CONST_DIRECTIVE: + gpgpu_n_const_insn++; + break; + case PARAM_DIRECTIVE: + gpgpu_n_param_insn++; + break; + case TEX_DIRECTIVE: + gpgpu_n_tex_insn++; + break; + case GLOBAL_DIRECTIVE: + case LOCAL_DIRECTIVE: + if (store){ + gpgpu_n_store_insn++; + } else { + gpgpu_n_load_insn++; + } + break; + default: + //assert(0); //unknown mem space. + break; //not a mem instruction + } + } +} + +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) +{ + //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) { + case SHARED_DIRECTIVE: + path[i] = SHARED_MEM_PATH; + type_counts[SHARED_MEM_PATH]++; + break; + case CONST_DIRECTIVE: + case PARAM_DIRECTIVE: + path[i] = CONSTANT_MEM_PATH; + type_counts[CONSTANT_MEM_PATH]++; + break; + case TEX_DIRECTIVE: + path[i] = TEXTURE_MEM_PATH; + type_counts[TEXTURE_MEM_PATH]++; + break; + case GLOBAL_DIRECTIVE: + case LOCAL_DIRECTIVE: + path[i] = GLOBAL_MEM_PATH; + type_counts[GLOBAL_MEM_PATH]++; + break; + default: + //path[i] = NO_MEM_PATH; + break; //not a mem instruction + } + } + + //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); + +} + + +void shader_memory( shader_core_ctx_t *shader, unsigned int shader_number ) +{ + 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); + } + + bool done = true; + mem_stage_access_type 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) { + 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 cache hit + 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]; + } +} + +int writeback_l1_miss =0 ; + + +void register_cta_thread_exit(shader_core_ctx_t *shader, 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("Shader %d finished CTA #%d (%lld,%lld)\n", shader->sid, cta_num, gpu_sim_cycle, gpu_tot_sim_cycle ); + } + } +} + +#if 0 +//this function is unecessary, cache is properly dirtied by fill with cache line function in gpu-sim.cc +void dirty_cache_lines(shader_core_ctx_t *shader, mshr_entry_t* mshr){ + shd_cache_line_t *hit_cacheline; + if (mshr->istexture) { + hit_cacheline = shd_cache_access(shader->L1texcache, + mshr->addr, WORD_SIZE, + mshr->iswrite, //should always be 0 + shader->gpu_cycle); + shd_cache_undo_stats( shader->L1texcache, !hit_cacheline ); + } else if (mshr->isconst) { + hit_cacheline = shd_cache_access(shader->L1constcache, + mshr->addr, WORD_SIZE, + mshr->iswrite, //should always be 0 + shader->gpu_cycle); + shd_cache_undo_stats( shader->L1constcache, !hit_cacheline ); + } else if (!gpgpu_no_dl1) { + hit_cacheline = shd_cache_access(shader->L1cache, + mshr->addr, WORD_SIZE, + mshr->iswrite, + shader->gpu_cycle); + shd_cache_undo_stats( shader->L1constcache, !hit_cacheline ); + if (!hit_cacheline) { + writeback_l1_miss++; + } + } +} +#endif + +typedef struct { + unsigned pc; + unsigned long latency; + void *ptx_thd_info; +} insn_latency_info; + +void obtain_insn_latency_info(insn_latency_info *latinfo, 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; +} + +unsigned gpu_n_max_mshr_writeback=1; +void shader_writeback( shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) +{ + static int *unlock_tid = NULL; + static int *freed_warp = NULL; + static int *mshr_tid = NULL; + static int *pl_tid = NULL; + static int *done_tid = NULL; + insn_latency_info *unlock_lat_info = NULL; + 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; + + if ( unlock_tid == NULL ) { + unlock_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + mshr_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + pl_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + done_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + freed_warp = (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(unlock_tid, -1, sizeof(int)*pipe_simd_width); + memset(mshr_tid, -1, sizeof(int)*pipe_simd_width); + memset(pl_tid, -1, sizeof(int)*pipe_simd_width); + memset(done_tid, -1, sizeof(int)*pipe_simd_width); + memset(freed_warp, 0, sizeof(int)*pipe_simd_width); + unlock_lat_info = NULL; + + check_stage_pcs(shader,MM_WB); + + /* Generate Condition for instruction writeback to register file. + A load miss *instruction* does not reach writeback until the data is fetched */ + for (int i=0; i<pipe_simd_width; i++) { + tid = shader->pipeline_reg[MM_WB][i].hw_thread_id; + w2rf |= (tid >= 0); + pl_tid[i] = tid; + } + + //check mshrs for commit; + //in future do req bank checking here; + 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, needs to be implemented better. + if (mshr_threads_unlocked + mshr_head->insts.size() > (unsigned) pipe_simd_width) break;//todo, do this right + assert(!gpgpu_strict_simd_wrbk);//implementation commented out below +/* + //for stalling in the middle of an mshr writeback due to mshr having threads from multiple warps, does this happen anymore? + static unsigned next_mshr_index = 0; + int mshr_warpid = -1; + bool mshr_not_blocked_by_samewarp = true; + //use below: for (j = next_mshr_index; ... +*/ + assert (mshr_head->insts.size()); + for (unsigned j = 0; j < mshr_head->insts.size(); j++) { + inst_t &insn = mshr_head->insts[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]); + assert (insn.hw_thread_id >= 0); + unlock_tid[mshr_threads_unlocked] = insn.hw_thread_id; + shader->pending_mem_access--; + // for ensuring that we don't unlock more than the code allows, needs to be fixed. + mshr_threads_unlocked++; +/* + next_mshr_index++; + if ((shader->model == POST_DOMINATOR || shader->model == NO_RECONVERGE) && gpgpu_strict_simd_wrbk) { + // restricting the threads from mshr to be in the same warp + if (mshr_warpid == -1) { + mshr_warpid = mshr_head->insts[j].hw_thread_id / warp_size; + } else if ((unsigned)mshr_warpid != (mshr_head->insts[j].hw_thread_id / warp_size)) { + warp_conflict_at_writeback++; + mshr_not_blocked_by_samewarp = false; + break; + } + } +*/ + } + //done with it since garanteed to wb +/* + if (mshr_not_blocked_by_samewarp) { +*/ + //this mshr is done this cycle, can pop it + shader->mshr_unit->pop_return_head(); +/* + //reset for next mshr + next_mshr_index = 0; + } +*/ + writeback_by_MSHR = true; + unlock_lat_info = mshr_lat_info; + 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 + 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]; + + unlock_tid[i] = pl_tid[i]; + obtain_insn_latency_info(&pl_lat_info[i], &shader->pipeline_reg[MM_WB][i]); + } + unlock_lat_info = pl_lat_info; + } + int thd_unlocked = 0; + for (int i=0; i<pipe_simd_width; i++) { + // NOTE: no need to check for next-stage stall at the last stage + if (unlock_tid[i] >= 0 ) { // not unlocking an invalid thread (ie. due to a bubble) + // thread completed if it is going to fetching beyond code boundry + if ( gpgpu_cuda_sim && ptx_thread_done(shader->thread[unlock_tid[i]].ptx_thd_info) ) { + + finished_trace += 1; + shader->not_completed -= 1; + gpu_completed_thread += 1; + + int warp_id = wid_from_hw_tid(unlock_tid[i],warp_size); + if (!(shader->warp[warp_id].n_completed < (unsigned)warp_size)) { + printf("shader[%d]->warp[%d].n_completed = %d; warp_size = %d\n", + shader->sid,warp_id, shader->warp[warp_id].n_completed, warp_size); + } + assert( shader->warp[warp_id].n_completed < (unsigned)warp_size ); + shader->warp[warp_id].n_completed++; + if ( shader->model == NO_RECONVERGE ) { + update_max_branch_priority(shader,warp_id,grid_num); + } + + if (gpgpu_no_divg_load) { + int amask = wpt_signal_complete(unlock_tid[i], shader); + freed_warp[i] = (amask != 0)? 1 : 0; + } else { + register_cta_thread_exit(shader, unlock_tid[i] ); + } + } else { //thread is not finished yet + // program is not finished yet, allow more fetch + if (gpgpu_no_divg_load) { + freed_warp[i] = wpt_signal_avail(unlock_tid[i], shader); + } else { + shader->thread[unlock_tid[i]].avail4fetch++; + assert(shader->thread[unlock_tid[i]].avail4fetch <= 1); + assert( shader->warp[wid_from_hw_tid(unlock_tid[i],warp_size)].n_avail4fetch < (unsigned)warp_size ); + shader->warp[wid_from_hw_tid(unlock_tid[i],warp_size)].n_avail4fetch++; + thd_unlocked = 1; + } + } + + // At any rate, a real instruction is committed + // - don't count cache miss + if ( shader->pipeline_reg[MM_WB][i].inst_type != NO_OP_FLAG ) { + gpu_sim_insn++; + if ( !is_const(shader->pipeline_reg[MM_WB][i].space) ) + gpu_sim_insn_no_ld_const++; + gpu_sim_insn_last_update = gpu_sim_cycle; + shader->num_sim_insn++; + shader->thread[unlock_tid[i]].n_insn++; + shader->thread[unlock_tid[i]].n_insn_ac++; + } + + if (enable_ptx_file_line_stats) { + unsigned pc = unlock_lat_info[i].pc; + unsigned long latency = unlock_lat_info[i].latency; + ptx_file_line_stats_add_latency(unlock_lat_info[i].ptx_thd_info, pc, latency); + } + } + } + if (shader->using_commit_queue && thd_unlocked) { + int *tid_unlocked = alloc_commit_warp(); + memcpy(tid_unlocked, unlock_tid, sizeof(int)*pipe_simd_width); //NOTE: this maybe warp_size + dq_push(shader->thd_commit_queue,(void*)tid_unlocked); + } + + + /* 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; + } + } +} + + +void shader_print_runtime_stat( FILE *fout ) { + unsigned i; + + fprintf(fout, "SHD_INSN: "); + for (i=0;i<gpu_n_shader;i++) { + fprintf(fout, "%u ",sc[i]->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); + } + fprintf(fout, "\n"); + fprintf(fout, "SHD_DIVG: "); + for (i=0;i<gpu_n_shader;i++) { + fprintf(fout, "%u ",sc[i]->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); + } + fprintf(fout, "\n"); +} + + +void shader_print_l1_miss_stat( FILE *fout ) { + unsigned i; + + 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); + } + 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); + } + 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); + } + 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); + } + 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)) { + fprintf(fout, "%d ", temp); + temp = 0; + } + } + 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)) { + fprintf(fout, "%d ", temp); + temp = 0; + } + } + 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)) { + fprintf(fout, "%d ", temp); + temp = 0; + } + } + 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 ) +{ + int i, j, warp_id = -1; + + for (i=0; i<stage_width; i++) { + if (shader->pipeline_reg[stage][i].hw_thread_id > -1) { + warp_id = shader->pipeline_reg[stage][i].hw_thread_id / warp_size; + break; + } + } + i = (i>=stage_width)? 0 : i; + + fprintf(fout,"0x%04x ", shader->pipeline_reg[stage][i].pc ); + + if( mask & 2 ) { + fprintf(fout, "(" ); + for (j=0; j<stage_width; j++) + fprintf(fout, "%03d ", shader->pipeline_reg[stage][j].hw_thread_id); + fprintf(fout, "): "); + } else { + fprintf(fout, "w%02d[", warp_id); + for (j=0; j<stage_width; j++) + fprintf(fout, "%c", ((shader->pipeline_reg[stage][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] ); + } + } + + ptx_print_insn( shader->pipeline_reg[stage][i].pc, fout ); + + if( mask & 0x10 ) { + if ( (shader->pipeline_reg[stage][i].op == STORE_OP || + shader->pipeline_reg[stage][i].op == LOAD_OP) && print_mem ) + fprintf(fout, " mem: 0x%016llx", shader->pipeline_reg[stage][i].memreqaddr); + } + fprintf(fout, "\n"); +} + +void shader_print_pre_mem_stages(shader_core_ctx_t *shader, FILE *fout, int print_mem, int mask ) +{ + int i, j; + int warp_id; + + if (!gpgpu_pre_mem_stages) return; + + for (unsigned pms = 0; pms <= gpgpu_pre_mem_stages - 1; 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; + break; + } + } + i = (i>=pipe_simd_width)? 0 : i; + + fprintf(fout,"0x%04x ", shader->pre_mem_pipeline[pms][i].pc ); + + 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); + 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') ); + 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] ); + } + } + + ptx_print_insn( shader->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); + } + fprintf(fout, "\n"); + } +} + +const char * ptx_get_fname( unsigned PC ); + +void shader_display_pipeline(shader_core_ctx_t *shader, FILE *fout, int print_mem, 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!) + + 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, "=================================================\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); + } + + fprintf(fout, "IF/ID = "); + shader_print_stage(shader, IF_ID, fout, pipe_simd_width, print_mem, mask ); + + if (shader->using_rrstage) { + fprintf(fout, "ID/RR = "); + shader_print_stage(shader, ID_RR, fout, pipe_simd_width, print_mem, 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); + + if (!gpgpu_pre_mem_stages) + fprintf(fout, "EX/MEM= "); + 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"); + } + } +} + +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 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); + } + + //Limit by n_threads/shader + unsigned int result_thread = shader->n_threads / padded_cta_size; + + const struct gpgpu_ptx_sim_kernel_info *kernel_info = ptx_sim_kernel_info(); + + //Limit by shmem/shader + unsigned int result_shmem = (unsigned)-1; + if (kernel_info->smem > 0) + result_shmem = shader->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)); + + //Limit by CTA + unsigned int result_cta = shader->n_cta; + + result = result_thread; + result = gs_min2(result, result_shmem); + result = gs_min2(result, result_regs); + result = gs_min2(result, result_cta); + + static const struct gpgpu_ptx_sim_kernel_info* last_kinfo = NULL; + if (last_kinfo != kernel_info) { //Only print out stats if kernel_info struct changes + last_kinfo = kernel_info; + printf ("CTA/core = %u, limited by:", result); + if (result == result_thread) printf (" threads"); + if (result == result_shmem) printf (" shmem"); + if (result == result_regs) printf (" regs"); + if (result == result_cta) printf (" cta_limit"); + printf ("\n"); + } + + if (result < 1) { + printf ("Error: max_cta_per_shader(\"%s\") returning %d. Kernel requires more resources than shader has?\n", shader->name, result); + abort(); + } + return result; +} + +void shader_cycle( shader_core_ctx_t *shader, + unsigned int shader_number, + int grid_num ) +{ + + // last pipeline stage + shader_writeback(shader, shader_number, grid_num); + + // three parallel stages (only one does something on a given cycle) + //shader_const_memory (shader, shader_number); + shader_memory (shader, shader_number); + //shader_texture_memory (shader, shader_number); + + // empty stage + if (gpgpu_pre_mem_stages) + shader_pre_memory(shader, shader_number); + + shader_execute (shader, shader_number); + if (shader->using_rrstage) { + // model register bank conflicts + // (see Fung et al. MICRO'07 paper or ACM TACO paper) + shader_preexecute (shader, shader_number); + } + + shader_decode (shader, shader_number, grid_num); + + shader_fetch (shader, shader_number, grid_num); +} + +// performance counter that are not local to one shader +void shader_print_accstats( FILE* fout ) +{ + 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[g_mem][coal_stall] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][COAL_STALL]); + 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_LD][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_LD][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"); + } + } +} + +// Flushes all content of the cache to memory + +void shader_cache_flush(shader_core_ctx_t* sc) +{ + unsigned int i; + unsigned int set; + unsigned long long int flush_addr; + + shd_cache_t *cp = sc->L1cache; + shd_cache_line_t *pline; + + for (i=0; i<cp->nset*cp->assoc; i++) { + pline = &(cp->lines[i]); + set = i / cp->assoc; + 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); + + pline->status &= ~VALID; + pline->status &= ~DIRTY; + } else if (pline->status & VALID) { + pline->status &= ~VALID; + } + } +} + +barrier_set_t::barrier_set_t( unsigned max_warps_per_core, unsigned max_cta_per_core ) +{ + m_max_warps_per_core = max_warps_per_core; + m_max_cta_per_core = max_cta_per_core; + if( max_warps_per_core > WARP_PER_CTA_MAX ) { + printf("ERROR ** increase WARP_PER_CTA_MAX in shader.h from %u to >= %u or warps per cta in gpgpusim.config\n", + WARP_PER_CTA_MAX, max_warps_per_core ); + exit(1); + } + m_warp_active.reset(); + m_warp_at_barrier.reset(); +} + +// during cta allocation +void barrier_set_t::allocate_barrier( unsigned cta_id, warp_set_t warps ) +{ + assert( cta_id < m_max_cta_per_core ); + cta_to_warp_t::iterator w=m_cta_to_warps.find(cta_id); + assert( w == m_cta_to_warps.end() ); // cta should not already be active or allocated barrier resources + m_cta_to_warps[cta_id] = warps; + assert( m_cta_to_warps.size() <= m_max_cta_per_core ); // catch cta's that were not properly deallocated + + m_warp_active |= warps; + m_warp_at_barrier &= ~warps; +} + +// during cta deallocation +void barrier_set_t::deallocate_barrier( unsigned cta_id ) +{ + cta_to_warp_t::iterator w=m_cta_to_warps.find(cta_id); + if( w == m_cta_to_warps.end() ) + return; + warp_set_t warps = w->second; + warp_set_t at_barrier = warps & m_warp_at_barrier; + assert( at_barrier.any() == false ); // no warps stuck at barrier + warp_set_t active = warps & m_warp_active; + assert( active.any() == false ); // no warps in CTA still running + m_warp_active &= ~warps; + m_warp_at_barrier &= ~warps; + m_cta_to_warps.erase(w); +} + +// individual warp hits barrier +void barrier_set_t::warp_reaches_barrier( unsigned cta_id, unsigned warp_id ) +{ + cta_to_warp_t::iterator w=m_cta_to_warps.find(cta_id); + + if( w == m_cta_to_warps.end() ) { // cta is active + printf("ERROR ** cta_id %u not found in barrier set on cycle %llu+%llu...\n", cta_id, gpu_tot_sim_cycle, gpu_sim_cycle ); + dump(); + abort(); + } + assert( w->second.test(warp_id) == true ); // warp is in cta + + m_warp_at_barrier.set(warp_id); + + warp_set_t warps_in_cta = w->second; + warp_set_t at_barrier = warps_in_cta & m_warp_at_barrier; + warp_set_t active = warps_in_cta & m_warp_active; + + if( at_barrier == active ) { + // all warps have reached barrier, so release waiting warps... + m_warp_at_barrier &= ~at_barrier; + } +} + +// fetching a warp +bool barrier_set_t::available_for_fetch( unsigned warp_id ) const +{ + return m_warp_active.test(warp_id) && m_warp_at_barrier.test(warp_id); +} + +// warp reaches exit +void barrier_set_t::warp_exit( unsigned warp_id ) +{ + // caller needs to verify all threads in warp are done, e.g., by checking PDOM stack to + // see it has only one entry during exit_impl() + m_warp_active.reset(warp_id); +} + +// assertions +bool barrier_set_t::warp_waiting_at_barrier( unsigned warp_id ) +{ + return m_warp_at_barrier.test(warp_id); +} + +void barrier_set_t::dump() const +{ + printf( "barrier set information\n"); + printf( " m_max_cta_per_core = %u\n", m_max_cta_per_core ); + printf( " m_max_warps_per_core = %u\n", m_max_warps_per_core ); + printf( " cta_to_warps:\n"); + + cta_to_warp_t::const_iterator i; + for( i=m_cta_to_warps.begin(); i!=m_cta_to_warps.end(); i++ ) { + unsigned cta_id = i->first; + warp_set_t warps = i->second; + printf(" cta_id %u : %s\n", cta_id, warps.to_string().c_str() ); + } + printf(" warp_active: %s\n", m_warp_active.to_string().c_str() ); + printf(" warp_at_barrier: %s\n", m_warp_at_barrier.to_string().c_str() ); + fflush(stdout); +} + +shader_core_ctx::shader_core_ctx( unsigned max_warps_per_cta, unsigned max_cta_per_core ) + : m_barriers( max_warps_per_cta, max_cta_per_core ) +{ +} + +void shader_core_ctx::set_at_barrier( unsigned cta_id, unsigned warp_id ) +{ + m_barriers.warp_reaches_barrier(cta_id,warp_id); +} + +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 ) +{ + return m_barriers.warp_waiting_at_barrier(warp_id); +} + +void shader_core_ctx::allocate_barrier( unsigned cta_id, warp_set_t warps ) +{ + m_barriers.allocate_barrier(cta_id,warps); +} + +void shader_core_ctx::deallocate_barrier( unsigned cta_id ) +{ + m_barriers.deallocate_barrier(cta_id); +} |
