// Copyright (c) 2009-2021, Tor M. Aamodt, Wilson W.L. Fung, Andrew Turner, // Ali Bakhoda, Vijay Kandiah, Nikos Hardavellas, // Mahmoud Khairy, Junrui Pan, Timothy G. Rogers // The University of British Columbia, Northwestern University, Purdue // University All rights reserved. // // 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 names of The University of British Columbia, Northwestern // University nor the names of their contributors may be used to // endorse or promote products derived from this software without specific // prior written permission. // // 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 HOLDER 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. #ifndef SHADER_H #define SHADER_H #include #include #include #include #include #include #include #include #include #include #include #include // #include "../cuda-sim/ptx.tab.h" #include "../abstract_hardware_model.h" #include "delayqueue.h" #include "dram.h" #include "gpu-cache.h" #include "mem_fetch.h" #include "scoreboard.h" #include "stack.h" #include "stats.h" #include "traffic_breakdown.h" #define NO_OP_FLAG 0xFF /* READ_PACKET_SIZE: bytes: 6 address (flit can specify chanel so this gives up to ~2GB/channel, so good for now), 2 bytes [shaderid + mshrid](14 bits) + req_size(0-2 bits if req_size variable) - so up to 2^14 = 16384 mshr total */ #define READ_PACKET_SIZE 8 // WRITE_PACKET_SIZE: bytes: 6 address, 2 miscelaneous. #define WRITE_PACKET_SIZE 8 #define WRITE_MASK_SIZE 8 class gpgpu_context; enum exec_unit_type_t { NONE = 0, SP = 1, SFU = 2, MEM = 3, DP = 4, INT = 5, TENSOR = 6, SPECIALIZED = 7 }; class thread_ctx_t { public: unsigned m_cta_id; // hardware CTA this thread belongs // per thread stats (ac stands for accumulative). unsigned n_insn; unsigned n_insn_ac; unsigned n_l1_mis_ac; unsigned n_l1_mrghit_ac; unsigned n_l1_access_ac; bool m_active; }; class shd_warp_t { public: shd_warp_t(class shader_core_ctx *shader, unsigned warp_size) : m_shader(shader), m_warp_size(warp_size) { m_stores_outstanding = 0; m_inst_in_pipeline = 0; reset(); } void reset() { assert(m_stores_outstanding == 0); assert(m_inst_in_pipeline == 0); m_imiss_pending = false; m_warp_id = (unsigned)-1; m_dynamic_warp_id = (unsigned)-1; n_completed = m_warp_size; m_n_atomic = 0; m_membar = false; m_done_exit = true; m_last_fetch = 0; m_next = 0; m_streamID = (unsigned long long)-1; // Jin: cdp support m_cdp_latency = 0; m_cdp_dummy = false; // Ni: Initialize ldgdepbar_id m_ldgdepbar_id = 0; m_depbar_start_id = 0; m_depbar_group = 0; // Ni: Set waiting to false m_waiting_ldgsts = false; // Ni: Clear m_ldgdepbar_buf for (unsigned i = 0; i < m_ldgdepbar_buf.size(); i++) { m_ldgdepbar_buf[i].clear(); } m_ldgdepbar_buf.clear(); } void init(address_type start_pc, unsigned cta_id, unsigned wid, const std::bitset &active, unsigned dynamic_warp_id, unsigned long long streamID) { m_streamID = streamID; m_cta_id = cta_id; m_warp_id = wid; m_dynamic_warp_id = dynamic_warp_id; m_next_pc = start_pc; assert(n_completed >= active.count()); assert(n_completed <= m_warp_size); n_completed -= active.count(); // active threads are not yet completed m_active_threads = active; m_done_exit = false; // Jin: cdp support m_cdp_latency = 0; m_cdp_dummy = false; // Ni: Initialize ldgdepbar_id m_ldgdepbar_id = 0; m_depbar_start_id = 0; m_depbar_group = 0; // Ni: Set waiting to false m_waiting_ldgsts = false; // Ni: Clear m_ldgdepbar_buf for (unsigned i = 0; i < m_ldgdepbar_buf.size(); i++) { m_ldgdepbar_buf[i].clear(); } m_ldgdepbar_buf.clear(); } bool functional_done() const; bool waiting(); // not const due to membar bool hardware_done() const; // ITS (AWARE Reconvergence) warp eligibility helpers bool virtualized(); bool pending_reconvergence(); bool blocked(); bool valid(); bool done_exit() const { return m_done_exit; } void set_done_exit() { m_done_exit = true; } void print(FILE *fout) const; void print_ibuffer(FILE *fout) const; unsigned get_n_completed() const { return n_completed; } void set_completed(unsigned lane) { assert(m_active_threads.test(lane)); m_active_threads.reset(lane); n_completed++; } bool test_active(unsigned lane) { return m_active_threads.test(lane); } const std::bitset &get_active_threads() const { return m_active_threads; } void set_last_fetch(unsigned long long sim_cycle) { m_last_fetch = sim_cycle; } unsigned get_n_atomic() const { return m_n_atomic; } void inc_n_atomic() { m_n_atomic++; } void dec_n_atomic(unsigned n) { m_n_atomic -= n; } void set_membar() { m_membar = true; } void clear_membar() { m_membar = false; } bool get_membar() const { return m_membar; } virtual address_type get_pc() const { return m_next_pc; } virtual kernel_info_t *get_kernel_info() const; void set_next_pc(address_type pc) { m_next_pc = pc; } void store_info_of_last_inst_at_barrier(const warp_inst_t *pI) { m_inst_at_barrier = *pI; } warp_inst_t *restore_info_of_last_inst_at_barrier() { return &m_inst_at_barrier; } void ibuffer_fill(unsigned slot, const warp_inst_t *pI) { assert(slot < IBUFFER_SIZE); m_ibuffer[slot].m_inst = pI; m_ibuffer[slot].m_valid = true; m_ibuffer[slot].m_split_id = (unsigned)-1; m_ibuffer[slot].m_split_mask.reset(); if (slot < IBUFFER_HALF_SIZE) m_next = 0; // only reset for primary half } void ibuffer_fill(unsigned slot, const warp_inst_t *pI, unsigned split_id, const active_mask_t &split_mask) { assert(slot < IBUFFER_SIZE); m_ibuffer[slot].m_inst = pI; m_ibuffer[slot].m_valid = true; m_ibuffer[slot].m_split_id = split_id; m_ibuffer[slot].m_split_mask = split_mask; if (slot < IBUFFER_HALF_SIZE) m_next = 0; // only reset for primary half } bool ibuffer_empty() const { for (unsigned i = 0; i < IBUFFER_SIZE; i++) if (m_ibuffer[i].m_valid) return false; return true; } bool ibuffer_half_empty(unsigned half) const { unsigned start = half * IBUFFER_HALF_SIZE; for (unsigned i = start; i < start + IBUFFER_HALF_SIZE; i++) if (m_ibuffer[i].m_valid) return false; return true; } bool ibuffer_any_half_empty() const { return ibuffer_half_empty(0) || ibuffer_half_empty(1); } void ibuffer_flush() { for (unsigned i = 0; i < IBUFFER_SIZE; i++) { if (m_ibuffer[i].m_valid) { dec_inst_in_pipeline(); } m_ibuffer[i].m_inst = NULL; m_ibuffer[i].m_valid = false; m_ibuffer[i].m_split_id = (unsigned)-1; } for (unsigned h = 0; h < IBUFFER_NUM_HALVES; h++) m_ibuffer_halves[h].m_assigned = false; } void ibuffer_flush_half(unsigned half) { unsigned start = half * IBUFFER_HALF_SIZE; for (unsigned i = start; i < start + IBUFFER_HALF_SIZE; i++) { if (m_ibuffer[i].m_valid) dec_inst_in_pipeline(); m_ibuffer[i].m_inst = NULL; m_ibuffer[i].m_valid = false; m_ibuffer[i].m_split_id = (unsigned)-1; } m_ibuffer_halves[half].m_assigned = false; } const warp_inst_t *ibuffer_next_inst() { return m_ibuffer[m_next].m_inst; } bool ibuffer_next_valid() { return m_ibuffer[m_next].m_valid; } unsigned ibuffer_next_split_id() const { return m_ibuffer[m_next].m_split_id; } const active_mask_t &ibuffer_next_split_mask() const { return m_ibuffer[m_next].m_split_mask; } void ibuffer_free() { m_ibuffer[m_next].m_inst = NULL; m_ibuffer[m_next].m_valid = false; } void ibuffer_step() { m_next = (m_next + 1) % IBUFFER_HALF_SIZE; } unsigned get_ibuffer_next() const { return m_next; } // Direct slot access for co-issue pass bool ibuffer_slot_valid(unsigned slot) const { return m_ibuffer[slot].m_valid; } const warp_inst_t *ibuffer_slot_inst(unsigned slot) const { return m_ibuffer[slot].m_inst; } unsigned ibuffer_slot_split_id(unsigned slot) const { return m_ibuffer[slot].m_split_id; } const active_mask_t &ibuffer_slot_split_mask(unsigned slot) const { return m_ibuffer[slot].m_split_mask; } void ibuffer_free_slot(unsigned slot) { m_ibuffer[slot].m_inst = NULL; m_ibuffer[slot].m_valid = false; } // Half assignment tracking void ibuffer_assign_half(unsigned half, unsigned split_id, const active_mask_t &mask) { m_ibuffer_halves[half].m_assigned_split_id = split_id; m_ibuffer_halves[half].m_assigned_mask = mask; m_ibuffer_halves[half].m_assigned = true; } bool ibuffer_half_assigned(unsigned half) const { return m_ibuffer_halves[half].m_assigned; } unsigned ibuffer_half_split_id(unsigned half) const { return m_ibuffer_halves[half].m_assigned_split_id; } const active_mask_t &ibuffer_half_mask(unsigned half) const { return m_ibuffer_halves[half].m_assigned_mask; } bool imiss_pending() const { return m_imiss_pending; } void set_imiss_pending() { m_imiss_pending = true; } void clear_imiss_pending() { m_imiss_pending = false; } bool stores_done() const { return m_stores_outstanding == 0; } void inc_store_req() { m_stores_outstanding++; } void dec_store_req() { assert(m_stores_outstanding > 0); m_stores_outstanding--; } unsigned num_inst_in_buffer() const { unsigned count = 0; for (unsigned i = 0; i < IBUFFER_SIZE; i++) { if (m_ibuffer[i].m_valid) count++; } return count; } unsigned num_inst_in_pipeline() const { return m_inst_in_pipeline; } unsigned num_issued_inst_in_pipeline() const { return (num_inst_in_pipeline() - num_inst_in_buffer()); } bool inst_in_pipeline() const { return m_inst_in_pipeline > 0; } void inc_inst_in_pipeline() { m_inst_in_pipeline++; } void dec_inst_in_pipeline() { assert(m_inst_in_pipeline > 0); m_inst_in_pipeline--; } unsigned long long get_streamID() const { return m_streamID; } unsigned get_cta_id() const { return m_cta_id; } unsigned get_dynamic_warp_id() const { return m_dynamic_warp_id; } unsigned get_warp_id() const { return m_warp_id; } class shader_core_ctx *get_shader() { return m_shader; } private: static const unsigned IBUFFER_SIZE = 4; static const unsigned IBUFFER_HALF_SIZE = 2; static const unsigned IBUFFER_NUM_HALVES = 2; class shader_core_ctx *m_shader; unsigned long long m_streamID; unsigned m_cta_id; unsigned m_warp_id; unsigned m_warp_size; unsigned m_dynamic_warp_id; address_type m_next_pc; unsigned n_completed; // number of threads in warp completed std::bitset m_active_threads; bool m_imiss_pending; struct ibuffer_entry { ibuffer_entry() { m_valid = false; m_inst = NULL; m_split_id = (unsigned)-1; } const warp_inst_t *m_inst; bool m_valid; unsigned m_split_id; // splits table entry this belongs to active_mask_t m_split_mask; // the split's active mask at decode time }; struct ibuffer_half_t { ibuffer_half_t() : m_assigned(false), m_assigned_split_id((unsigned)-1) {} bool m_assigned; unsigned m_assigned_split_id; active_mask_t m_assigned_mask; }; warp_inst_t m_inst_at_barrier; ibuffer_entry m_ibuffer[IBUFFER_SIZE]; ibuffer_half_t m_ibuffer_halves[IBUFFER_NUM_HALVES]; unsigned m_next; unsigned m_n_atomic; // number of outstanding atomic operations bool m_membar; // if true, warp is waiting at memory barrier bool m_done_exit; // true once thread exit has been registered for threads in // this warp unsigned long long m_last_fetch; unsigned m_stores_outstanding; // number of store requests sent but not yet // acknowledged unsigned m_inst_in_pipeline; // Jin: cdp support public: unsigned int m_cdp_latency; bool m_cdp_dummy; // Ni: LDGDEPBAR barrier support public: unsigned int m_ldgdepbar_id; // LDGDEPBAR barrier ID std::vector> m_ldgdepbar_buf; // LDGDEPBAR barrier buffer unsigned int m_depbar_start_id; unsigned int m_depbar_group; bool m_waiting_ldgsts; // Ni: Whether the warp is waiting for the LDGSTS // instrs to finish }; inline unsigned hw_tid_from_wid(unsigned wid, unsigned warp_size, unsigned i) { return wid * warp_size + i; }; inline unsigned wid_from_hw_tid(unsigned tid, unsigned warp_size) { return tid / warp_size; }; const unsigned WARP_PER_CTA_MAX = 64; typedef std::bitset warp_set_t; unsigned register_bank(int regnum, int wid, unsigned num_banks, bool sub_core_model, unsigned banks_per_sched, unsigned sched_id); class shader_core_ctx; class shader_core_config; class shader_core_stats; enum scheduler_prioritization_type { SCHEDULER_PRIORITIZATION_LRR = 0, // Loose Round Robin SCHEDULER_PRIORITIZATION_SRR, // Strict Round Robin SCHEDULER_PRIORITIZATION_GTO, // Greedy Then Oldest SCHEDULER_PRIORITIZATION_GTLRR, // Greedy Then Loose Round Robin SCHEDULER_PRIORITIZATION_GTY, // Greedy Then Youngest SCHEDULER_PRIORITIZATION_OLDEST, // Oldest First SCHEDULER_PRIORITIZATION_YOUNGEST, // Youngest First }; // Each of these corresponds to a string value in the gpgpsim.config file // For example - to specify the LRR scheudler the config must contain lrr enum concrete_scheduler { CONCRETE_SCHEDULER_LRR = 0, CONCRETE_SCHEDULER_GTO, CONCRETE_SCHEDULER_TWO_LEVEL_ACTIVE, CONCRETE_SCHEDULER_RRR, CONCRETE_SCHEDULER_WARP_LIMITING, CONCRETE_SCHEDULER_OLDEST_FIRST, NUM_CONCRETE_SCHEDULERS }; class scheduler_unit { // this can be copied freely, so can be used in std // containers. public: scheduler_unit(shader_core_stats *stats, shader_core_ctx *shader, Scoreboard *scoreboard, simt_stack **simt, std::vector *warp, register_set *sp_out, register_set *dp_out, register_set *sfu_out, register_set *int_out, register_set *tensor_core_out, std::vector &spec_cores_out, register_set *mem_out, int id) : m_supervised_warps(), m_stats(stats), m_shader(shader), m_scoreboard(scoreboard), m_simt_stack(simt), /*m_pipeline_reg(pipe_regs),*/ m_warp(warp), m_sp_out(sp_out), m_dp_out(dp_out), m_sfu_out(sfu_out), m_int_out(int_out), m_tensor_core_out(tensor_core_out), m_mem_out(mem_out), m_spec_cores_out(spec_cores_out), m_id(id) {} virtual ~scheduler_unit() {} virtual void add_supervised_warp_id(int i) { m_supervised_warps.push_back(&warp(i)); } virtual void done_adding_supervised_warps() { m_last_supervised_issued = m_supervised_warps.end(); } // The core scheduler cycle method is meant to be common between // all the derived schedulers. The scheduler's behaviour can be // modified by changing the contents of the m_next_cycle_prioritized_warps // list. void cycle(); // These are some common ordering fucntions that the // higher order schedulers can take advantage of template void order_lrr( typename std::vector &result_list, const typename std::vector &input_list, const typename std::vector::const_iterator &last_issued_from_input, unsigned num_warps_to_add); template void order_rrr( typename std::vector &result_list, const typename std::vector &input_list, const typename std::vector::const_iterator &last_issued_from_input, unsigned num_warps_to_add); enum OrderingType { // The item that issued last is prioritized first then the sorted result // of the priority_function ORDERING_GREEDY_THEN_PRIORITY_FUNC = 0, // No greedy scheduling based on last to issue. Only the priority function // determines priority ORDERED_PRIORITY_FUNC_ONLY, NUM_ORDERING, }; template void order_by_priority( std::vector &result_list, const typename std::vector &input_list, const typename std::vector::const_iterator &last_issued_from_input, unsigned num_warps_to_add, OrderingType age_ordering, bool (*priority_func)(U lhs, U rhs)); static bool sort_warps_by_oldest_dynamic_id(shd_warp_t *lhs, shd_warp_t *rhs); // Derived classes can override this function to populate // m_supervised_warps with their scheduling policies virtual void order_warps() = 0; int get_schd_id() const { return m_id; } protected: virtual void do_on_warp_issued( unsigned warp_id, unsigned num_issued, const std::vector::const_iterator &prioritized_iter); inline int get_sid() const; // --- Change 4: co-issue scheduler helpers ---------------------------- // Classify an instruction's FU type (the 25-line op→exec_unit_type_t // switch that was duplicated across the primary pick, inter-warp pass, // and intra-warp pass). exec_unit_type_t classify_fu_type(const warp_inst_t *inst) const; // MEM co-issue safety filter. Returns true if the given inst must NOT // participate (as primary or co-issuer) in a MEM composite. v1 excludes // atomics, shared-space ops (deferred per-set bank-conflict rework), // texture/const caches (line-based coalesce model not per-set-aware), // MEMORY_BARRIER_OP, LDGSTS, and BRU spill/fill requests. bool mem_coissue_disallowed(const warp_inst_t *inst) const; // Check whether `cand_inst` can co-issue alongside `composite` given // `available_sets`. Writes the candidate's sets_needed to *sets_needed // on success. Uses the current compaction_mode to decide between the // count-only path (full compaction) and the overlap-check path // (identity / XOR-static). bool check_coissue_feasibility(const warp_inst_t *composite, const warp_inst_t *cand_inst, const active_mask_t &cand_mask, unsigned cand_warp_id, unsigned available_sets, unsigned *sets_needed); // Runs the inter-warp co-issue pass. sort_mode: // 0 = round-robin (iterate m_next_cycle_prioritized_warps as-is) // 1 = fewest-lanes first (sort by popcount(active_mask) ascending) // 2 = same-PC first, fall through to arbitrary within the cycle void try_inter_warp_coissue(warp_inst_t *co_issue_composite, exec_unit_type_t co_issue_fu_type, unsigned co_issue_primary_warp_id, unsigned &available_sets, unsigned &next_free_set, unsigned sort_mode); // Runs the intra-warp co-issue pass (primary warp's secondary slots). void try_intra_warp_coissue(warp_inst_t *co_issue_composite, exec_unit_type_t co_issue_fu_type, unsigned co_issue_primary_warp_id, unsigned &available_sets, unsigned &next_free_set); // Unified intra+inter candidate pool, density-sorted greedy packing. // Approximates optimal SIMD-lane utilization for the cycle. void try_utilization_max_coissue(warp_inst_t *co_issue_composite, exec_unit_type_t co_issue_fu_type, unsigned co_issue_primary_warp_id, unsigned &available_sets, unsigned &next_free_set); protected: shd_warp_t &warp(int i); // This is the prioritized warp list that is looped over each cycle to // determine which warp gets to issue. std::vector m_next_cycle_prioritized_warps; // The m_supervised_warps list is all the warps this scheduler is supposed to // arbitrate between. This is useful in systems where there is more than // one warp scheduler. In a single scheduler system, this is simply all // the warps assigned to this core. std::vector m_supervised_warps; // This is the iterator pointer to the last supervised warp you issued std::vector::const_iterator m_last_supervised_issued; shader_core_stats *m_stats; shader_core_ctx *m_shader; // these things should become accessors: but would need a bigger rearchitect // of how shader_core_ctx interacts with its parts. Scoreboard *m_scoreboard; simt_stack **m_simt_stack; // warp_inst_t** m_pipeline_reg; std::vector *m_warp; register_set *m_sp_out; register_set *m_dp_out; register_set *m_sfu_out; register_set *m_int_out; register_set *m_tensor_core_out; register_set *m_mem_out; std::vector &m_spec_cores_out; unsigned m_num_issued_last_cycle; unsigned m_current_turn_warp; int m_id; }; class lrr_scheduler : public scheduler_unit { public: lrr_scheduler(shader_core_stats *stats, shader_core_ctx *shader, Scoreboard *scoreboard, simt_stack **simt, std::vector *warp, register_set *sp_out, register_set *dp_out, register_set *sfu_out, register_set *int_out, register_set *tensor_core_out, std::vector &spec_cores_out, register_set *mem_out, int id) : scheduler_unit(stats, shader, scoreboard, simt, warp, sp_out, dp_out, sfu_out, int_out, tensor_core_out, spec_cores_out, mem_out, id) {} virtual ~lrr_scheduler() {} virtual void order_warps(); virtual void done_adding_supervised_warps() { m_last_supervised_issued = m_supervised_warps.end(); } }; class rrr_scheduler : public scheduler_unit { public: rrr_scheduler(shader_core_stats *stats, shader_core_ctx *shader, Scoreboard *scoreboard, simt_stack **simt, std::vector *warp, register_set *sp_out, register_set *dp_out, register_set *sfu_out, register_set *int_out, register_set *tensor_core_out, std::vector &spec_cores_out, register_set *mem_out, int id) : scheduler_unit(stats, shader, scoreboard, simt, warp, sp_out, dp_out, sfu_out, int_out, tensor_core_out, spec_cores_out, mem_out, id) {} virtual ~rrr_scheduler() {} virtual void order_warps(); virtual void done_adding_supervised_warps() { m_last_supervised_issued = m_supervised_warps.end(); } }; class gto_scheduler : public scheduler_unit { public: gto_scheduler(shader_core_stats *stats, shader_core_ctx *shader, Scoreboard *scoreboard, simt_stack **simt, std::vector *warp, register_set *sp_out, register_set *dp_out, register_set *sfu_out, register_set *int_out, register_set *tensor_core_out, std::vector &spec_cores_out, register_set *mem_out, int id) : scheduler_unit(stats, shader, scoreboard, simt, warp, sp_out, dp_out, sfu_out, int_out, tensor_core_out, spec_cores_out, mem_out, id) {} virtual ~gto_scheduler() {} virtual void order_warps(); virtual void done_adding_supervised_warps() { m_last_supervised_issued = m_supervised_warps.begin(); } }; class oldest_scheduler : public scheduler_unit { public: oldest_scheduler(shader_core_stats *stats, shader_core_ctx *shader, Scoreboard *scoreboard, simt_stack **simt, std::vector *warp, register_set *sp_out, register_set *dp_out, register_set *sfu_out, register_set *int_out, register_set *tensor_core_out, std::vector &spec_cores_out, register_set *mem_out, int id) : scheduler_unit(stats, shader, scoreboard, simt, warp, sp_out, dp_out, sfu_out, int_out, tensor_core_out, spec_cores_out, mem_out, id) {} virtual ~oldest_scheduler() {} virtual void order_warps(); virtual void done_adding_supervised_warps() { m_last_supervised_issued = m_supervised_warps.begin(); } }; class two_level_active_scheduler : public scheduler_unit { public: two_level_active_scheduler(shader_core_stats *stats, shader_core_ctx *shader, Scoreboard *scoreboard, simt_stack **simt, std::vector *warp, register_set *sp_out, register_set *dp_out, register_set *sfu_out, register_set *int_out, register_set *tensor_core_out, std::vector &spec_cores_out, register_set *mem_out, int id, char *config_str) : scheduler_unit(stats, shader, scoreboard, simt, warp, sp_out, dp_out, sfu_out, int_out, tensor_core_out, spec_cores_out, mem_out, id), m_pending_warps() { unsigned inner_level_readin; unsigned outer_level_readin; int ret = sscanf(config_str, "two_level_active:%d:%d:%d", &m_max_active_warps, &inner_level_readin, &outer_level_readin); assert(3 == ret); m_inner_level_prioritization = (scheduler_prioritization_type)inner_level_readin; m_outer_level_prioritization = (scheduler_prioritization_type)outer_level_readin; } virtual ~two_level_active_scheduler() {} virtual void order_warps(); void add_supervised_warp_id(int i) { if (m_next_cycle_prioritized_warps.size() < m_max_active_warps) { m_next_cycle_prioritized_warps.push_back(&warp(i)); } else { m_pending_warps.push_back(&warp(i)); } } virtual void done_adding_supervised_warps() { m_last_supervised_issued = m_supervised_warps.begin(); } protected: virtual void do_on_warp_issued( unsigned warp_id, unsigned num_issued, const std::vector::const_iterator &prioritized_iter); private: std::deque m_pending_warps; scheduler_prioritization_type m_inner_level_prioritization; scheduler_prioritization_type m_outer_level_prioritization; unsigned m_max_active_warps; }; // Static Warp Limiting Scheduler class swl_scheduler : public scheduler_unit { public: swl_scheduler(shader_core_stats *stats, shader_core_ctx *shader, Scoreboard *scoreboard, simt_stack **simt, std::vector *warp, register_set *sp_out, register_set *dp_out, register_set *sfu_out, register_set *int_out, register_set *tensor_core_out, std::vector &spec_cores_out, register_set *mem_out, int id, char *config_string); virtual ~swl_scheduler() {} virtual void order_warps(); virtual void done_adding_supervised_warps() { m_last_supervised_issued = m_supervised_warps.begin(); } protected: scheduler_prioritization_type m_prioritization; unsigned m_num_warps_to_limit; }; class opndcoll_rfu_t { // operand collector based register file unit public: // constructors opndcoll_rfu_t() { m_num_banks = 0; m_shader = NULL; m_initialized = false; } void add_cu_set(unsigned cu_set, unsigned num_cu, unsigned num_dispatch); typedef std::vector port_vector_t; typedef std::vector uint_vector_t; void add_port(port_vector_t &input, port_vector_t &ouput, uint_vector_t cu_sets); void init(unsigned num_banks, shader_core_ctx *shader); // modifiers bool writeback(warp_inst_t &warp); void step() { dispatch_ready_cu(); drain_pending_operands(); allocate_reads(); for (unsigned p = 0; p < m_in_ports.size(); p++) allocate_cu(p); process_banks(); } void dump(FILE *fp) const { fprintf(fp, "\n"); fprintf(fp, "Operand Collector State:\n"); for (unsigned n = 0; n < m_cu.size(); n++) { fprintf(fp, " CU-%2u: ", n); m_cu[n]->dump(fp, m_shader); } m_arbiter.dump(fp); } shader_core_ctx *shader_core() { return m_shader; } private: void process_banks() { m_arbiter.reset_alloction(); } // types (forward-declared so member function signatures below can // reference nested types defined later in this class body) class op_t; class collector_unit_t; void dispatch_ready_cu(); void allocate_cu(unsigned port); void allocate_reads(); // Deliver one allocated operand to its collector unit and bump the // regfile-read stat. Shared by allocate_reads (latency=0 fast path) // and drain_pending_operands (latency>0). void deliver_operand(op_t &op); // Drain any pending-operand queue entries whose cycle_ready has // elapsed; called from step() between dispatch_ready_cu and // allocate_reads so delivered operands dispatch on the *next* cycle. void drain_pending_operands(); class op_t { public: op_t() { m_valid = false; m_set_warp_id = (unsigned)-1; } op_t(collector_unit_t *cu, unsigned op, unsigned reg, unsigned num_banks, bool sub_core_model, unsigned banks_per_sched, unsigned sched_id) { m_valid = true; m_warp = NULL; m_cu = cu; m_operand = op; m_register = reg; m_shced_id = sched_id; m_set_warp_id = (unsigned)-1; m_bank = register_bank(reg, cu->get_warp_id(), num_banks, sub_core_model, banks_per_sched, sched_id); } // Per-set variant: uses explicit warp_id for bank computation op_t(collector_unit_t *cu, unsigned op, unsigned reg, unsigned num_banks, bool sub_core_model, unsigned banks_per_sched, unsigned sched_id, unsigned set_warp_id) { m_valid = true; m_warp = NULL; m_cu = cu; m_operand = op; m_register = reg; m_shced_id = sched_id; m_set_warp_id = set_warp_id; m_bank = register_bank(reg, set_warp_id, num_banks, sub_core_model, banks_per_sched, sched_id); } op_t(const warp_inst_t *warp, unsigned reg, unsigned num_banks, bool sub_core_model, unsigned banks_per_sched, unsigned sched_id) { m_valid = true; m_warp = warp; m_register = reg; m_cu = NULL; m_operand = -1; m_shced_id = sched_id; m_set_warp_id = (unsigned)-1; m_bank = register_bank(reg, warp->warp_id(), num_banks, sub_core_model, banks_per_sched, sched_id); } // Explicit warp_id variant for writeback of co-issued sets op_t(unsigned explicit_warp_id, unsigned reg, unsigned num_banks, bool sub_core_model, unsigned banks_per_sched, unsigned sched_id) { m_valid = true; m_warp = NULL; m_cu = NULL; m_operand = (unsigned)-1; m_register = reg; m_shced_id = sched_id; m_set_warp_id = explicit_warp_id; m_bank = register_bank(reg, explicit_warp_id, num_banks, sub_core_model, banks_per_sched, sched_id); } // accessors bool valid() const { return m_valid; } unsigned get_reg() const { assert(m_valid); return m_register; } unsigned get_wid() const { if (m_set_warp_id != (unsigned)-1) return m_set_warp_id; else if (m_warp) return m_warp->warp_id(); else if (m_cu) return m_cu->get_warp_id(); else abort(); } unsigned get_sid() const { return m_shced_id; } unsigned get_active_count() const { if (m_warp) return m_warp->active_count(); else if (m_cu) return m_cu->get_active_count(); else abort(); } const active_mask_t &get_active_mask() { if (m_warp) return m_warp->get_active_mask(); else if (m_cu) return m_cu->get_active_mask(); else abort(); } unsigned get_sp_op() const { if (m_warp) return m_warp->sp_op; else if (m_cu) return m_cu->get_sp_op(); else abort(); } unsigned get_oc_id() const { return m_cu->get_id(); } unsigned get_bank() const { return m_bank; } unsigned get_operand() const { return m_operand; } void dump(FILE *fp) const { if (m_cu) fprintf(fp, " ", m_register, m_cu->get_id(), m_cu->get_warp_id()); else if (!m_warp->empty()) fprintf(fp, " ", m_register, m_warp->warp_id()); } std::string get_reg_string() const { char buffer[64]; snprintf(buffer, 64, "R%u", m_register); return std::string(buffer); } // modifiers void reset() { m_valid = false; } private: bool m_valid; collector_unit_t *m_cu; const warp_inst_t *m_warp; unsigned m_operand; // operand offset in instruction. e.g., add r1,r2,r3; // r2 is oprd 0, r3 is 1 (r1 is dst) unsigned m_register; unsigned m_bank; unsigned m_shced_id; // scheduler id that has issued this inst unsigned m_set_warp_id; // per-set warp_id override for SIMD partitioning }; enum alloc_t { NO_ALLOC, READ_ALLOC, WRITE_ALLOC, }; class allocation_t { public: allocation_t() { m_allocation = NO_ALLOC; } bool is_read() const { return m_allocation == READ_ALLOC; } bool is_write() const { return m_allocation == WRITE_ALLOC; } bool is_free() const { return m_allocation == NO_ALLOC; } void dump(FILE *fp) const { if (m_allocation == NO_ALLOC) { fprintf(fp, ""); } else if (m_allocation == READ_ALLOC) { fprintf(fp, "rd: "); m_op.dump(fp); } else if (m_allocation == WRITE_ALLOC) { fprintf(fp, "wr: "); m_op.dump(fp); } fprintf(fp, "\n"); } void alloc_read(const op_t &op) { assert(is_free()); m_allocation = READ_ALLOC; m_op = op; } void alloc_write(const op_t &op) { assert(is_free()); m_allocation = WRITE_ALLOC; m_op = op; } void reset() { m_allocation = NO_ALLOC; } private: enum alloc_t m_allocation; op_t m_op; }; class arbiter_t { public: // constructors arbiter_t() { m_queue = NULL; m_allocated_bank = NULL; m_allocator_rr_head = NULL; _inmatch = NULL; _outmatch = NULL; _request = NULL; m_last_cu = 0; } void init(unsigned num_cu, unsigned num_banks) { assert(num_cu > 0); assert(num_banks > 0); m_num_collectors = num_cu; m_num_banks = num_banks; _inmatch = new int[m_num_banks]; _outmatch = new int[m_num_collectors]; _request = new int *[m_num_banks]; for (unsigned i = 0; i < m_num_banks; i++) _request[i] = new int[m_num_collectors]; m_queue = new std::list[num_banks]; m_allocated_bank = new allocation_t[num_banks]; m_allocator_rr_head = new unsigned[num_cu]; for (unsigned n = 0; n < num_cu; n++) m_allocator_rr_head[n] = n % num_banks; reset_alloction(); } // accessors void dump(FILE *fp) const { fprintf(fp, "\n"); fprintf(fp, " Arbiter State:\n"); fprintf(fp, " requests:\n"); for (unsigned b = 0; b < m_num_banks; b++) { fprintf(fp, " bank %u : ", b); std::list::const_iterator o = m_queue[b].begin(); for (; o != m_queue[b].end(); o++) { o->dump(fp); } fprintf(fp, "\n"); } fprintf(fp, " grants:\n"); for (unsigned b = 0; b < m_num_banks; b++) { fprintf(fp, " bank %u : ", b); m_allocated_bank[b].dump(fp); } fprintf(fp, "\n"); } // modifiers std::list allocate_reads(); void add_read_requests(collector_unit_t *cu) { const op_t *src = cu->get_operands(); for (unsigned i = 0; i < MAX_REG_OPERANDS * 2; i++) { const op_t &op = src[i]; if (op.valid()) { unsigned bank = op.get_bank(); m_queue[bank].push_back(op); } } } bool bank_idle(unsigned bank) const { return m_allocated_bank[bank].is_free(); } void allocate_bank_for_write(unsigned bank, const op_t &op) { assert(bank < m_num_banks); m_allocated_bank[bank].alloc_write(op); } void allocate_for_read(unsigned bank, const op_t &op) { assert(bank < m_num_banks); m_allocated_bank[bank].alloc_read(op); } void reset_alloction() { for (unsigned b = 0; b < m_num_banks; b++) m_allocated_bank[b].reset(); } private: unsigned m_num_banks; unsigned m_num_collectors; allocation_t *m_allocated_bank; // bank # -> register that wins std::list *m_queue; unsigned * m_allocator_rr_head; // cu # -> next bank to check for request (rr-arb) unsigned m_last_cu; // first cu to check while arb-ing banks (rr) int *_inmatch; int *_outmatch; int **_request; }; class input_port_t { public: input_port_t(port_vector_t &input, port_vector_t &output, uint_vector_t cu_sets) : m_in(input), m_out(output), m_cu_sets(cu_sets) { assert(input.size() == output.size()); assert(not m_cu_sets.empty()); } // private: port_vector_t m_in, m_out; uint_vector_t m_cu_sets; }; class collector_unit_t { public: // constructors collector_unit_t() { m_free = true; m_warp = NULL; m_output_register = NULL; m_src_op = new op_t[MAX_REG_OPERANDS * 2]; m_not_ready.reset(); m_warp_id = -1; m_num_banks = 0; m_has_simd_sets = false; } // accessors bool ready() const; const op_t *get_operands() const { return m_src_op; } void dump(FILE *fp, const shader_core_ctx *shader) const; unsigned get_warp_id() const { return m_warp_id; } unsigned get_active_count() const { return m_warp->active_count(); } const active_mask_t &get_active_mask() const { return m_warp->get_active_mask(); } unsigned get_sp_op() const { return m_warp->sp_op; } unsigned get_id() const { return m_cuid; } // returns CU hw id unsigned get_reg_id() const { return m_reg_id; } // modifiers void init(unsigned n, unsigned num_banks, const core_config *config, opndcoll_rfu_t *rfu, bool m_sub_core_model, unsigned reg_id, unsigned num_banks_per_sched); bool allocate(register_set *pipeline_reg, register_set *output_reg); void collect_operand(unsigned op) { m_not_ready.reset(op); } unsigned get_num_operands() const { return m_warp->get_num_operands(); } unsigned get_num_regs() const { return m_warp->get_num_regs(); } void dispatch(); bool is_free() { return m_free; } private: bool m_free; unsigned m_cuid; // collector unit hw id unsigned m_warp_id; warp_inst_t *m_warp; register_set *m_output_register; // pipeline register to issue to when ready op_t *m_src_op; std::bitset m_not_ready; unsigned m_num_banks; opndcoll_rfu_t *m_rfu; unsigned m_num_banks_per_sched; bool m_sub_core_model; unsigned m_reg_id; // if sub_core_model enabled, limit regs this cu can r/w // SIMD lane partitioning: tracks whether this CU holds a composite bool m_has_simd_sets; }; class dispatch_unit_t { public: dispatch_unit_t(std::vector *cus) { m_last_cu = 0; m_collector_units = cus; m_num_collectors = (*cus).size(); m_next_cu = 0; } void init(bool sub_core_model, unsigned num_warp_scheds) { m_sub_core_model = sub_core_model; m_num_warp_scheds = num_warp_scheds; } collector_unit_t *find_ready() { // With sub-core enabled round robin starts with the next cu assigned to a // different sub-core than the one that dispatched last unsigned cusPerSched = m_num_collectors / m_num_warp_scheds; unsigned rr_increment = m_sub_core_model ? cusPerSched - (m_last_cu % cusPerSched) : 1; for (unsigned n = 0; n < m_num_collectors; n++) { unsigned c = (m_last_cu + n + rr_increment) % m_num_collectors; if ((*m_collector_units)[c].ready()) { m_last_cu = c; return &((*m_collector_units)[c]); } } return NULL; } private: unsigned m_num_collectors; std::vector *m_collector_units; unsigned m_last_cu; // dispatch ready cu's rr unsigned m_next_cu; // for initialization bool m_sub_core_model; unsigned m_num_warp_scheds; }; // opndcoll_rfu_t data members bool m_initialized; unsigned m_num_collector_sets; // unsigned m_num_collectors; unsigned m_num_banks; unsigned m_warp_size; std::vector m_cu; arbiter_t m_arbiter; unsigned m_num_banks_per_sched; unsigned m_num_warp_scheds; bool sub_core_model; // unsigned m_num_ports; // std::vector m_input; // std::vector m_output; // std::vector m_num_collector_units; // warp_inst_t **m_alu_port; std::vector m_in_ports; typedef std::map /*collector sets*/> cu_sets_t; cu_sets_t m_cus; std::vector m_dispatch_units; // typedef std::map port_to_du_t; // port_to_du_t m_dispatch_units; // std::map > m_free_cu; shader_core_ctx *m_shader; // Delay queue for modeling operand-collector read pipelining when // `-gpgpu_opndcoll_read_latency > 0`. allocate_reads() pushes here; // drain_pending_operands() pops at the top of step(). struct pending_operand_t { op_t op; unsigned long long cycle_ready; }; std::deque m_pending_operands; }; class barrier_set_t { public: barrier_set_t(shader_core_ctx *shader, unsigned max_warps_per_core, unsigned max_cta_per_core, unsigned max_barriers_per_cta, unsigned warp_size); // during cta allocation void allocate_barrier(unsigned cta_id, warp_set_t warps); // during cta deallocation void deallocate_barrier(unsigned cta_id); typedef std::map cta_to_warp_t; typedef std::map bar_id_to_warp_t; /*set of warps reached a specific barrier id*/ // individual warp hits barrier void warp_reaches_barrier(unsigned cta_id, unsigned warp_id, warp_inst_t *inst); // warp reaches exit void warp_exit(unsigned warp_id); // assertions bool warp_waiting_at_barrier(unsigned warp_id) const; // ITS: count warps from a given CTA that are at barrier unsigned warps_count_at_barrier(int cta_id) { warp_set_t t = m_cta_to_warps[cta_id] & m_warp_at_barrier; return t.count(); } // debug void dump(); private: unsigned m_max_cta_per_core; unsigned m_max_warps_per_core; unsigned m_max_barriers_per_cta; unsigned m_warp_size; cta_to_warp_t m_cta_to_warps; bar_id_to_warp_t m_bar_id_to_warps; warp_set_t m_warp_active; warp_set_t m_warp_at_barrier; shader_core_ctx *m_shader; }; struct insn_latency_info { unsigned pc; unsigned long latency; }; struct ifetch_buffer_t { ifetch_buffer_t() : m_valid(false), m_ibuffer_half(0), m_split_id((unsigned)-1) { m_split_mask.reset(); } ifetch_buffer_t(address_type pc, unsigned nbytes, unsigned warp_id) : m_valid(true), m_pc(pc), m_nbytes(nbytes), m_warp_id(warp_id), m_ibuffer_half(0), m_split_id((unsigned)-1) { m_split_mask.reset(); } ifetch_buffer_t(address_type pc, unsigned nbytes, unsigned warp_id, unsigned ibuffer_half, unsigned split_id, const active_mask_t &split_mask) : m_valid(true), m_pc(pc), m_nbytes(nbytes), m_warp_id(warp_id), m_ibuffer_half(ibuffer_half), m_split_id(split_id), m_split_mask(split_mask) {} bool m_valid; address_type m_pc; unsigned m_nbytes; unsigned m_warp_id; unsigned m_ibuffer_half; // which I-Buffer half to fill (0 or 1) unsigned m_split_id; // splits table entry active_mask_t m_split_mask; // the split's active mask }; class shader_core_config; class simd_function_unit { public: simd_function_unit(const shader_core_config *config); ~simd_function_unit() { delete m_dispatch_reg; } // modifiers virtual void issue(register_set &source_reg); virtual void cycle() = 0; virtual void active_lanes_in_pipeline() = 0; // accessors virtual unsigned clock_multiplier() const { return 1; } virtual bool can_issue(const warp_inst_t &inst) const { return m_dispatch_reg->empty() && !occupied.test(inst.latency); } virtual bool is_issue_partitioned() = 0; virtual unsigned get_issue_reg_id() = 0; virtual bool stallable() const = 0; virtual void print(FILE *fp) const { fprintf(fp, "%s dispatch= ", m_name.c_str()); m_dispatch_reg->print(fp); } const char *get_name() { return m_name.c_str(); } protected: std::string m_name; const shader_core_config *m_config; warp_inst_t *m_dispatch_reg; static const unsigned MAX_ALU_LATENCY = 512; std::bitset occupied; }; class pipelined_simd_unit : public simd_function_unit { public: pipelined_simd_unit(register_set *result_port, const shader_core_config *config, unsigned max_latency, shader_core_ctx *core, unsigned issue_reg_id); // modifiers virtual void cycle(); virtual void issue(register_set &source_reg); virtual unsigned get_active_lanes_in_pipeline(); virtual void active_lanes_in_pipeline() = 0; /* virtual void issue( register_set& source_reg ) { //move_warp(m_dispatch_reg,source_reg); //source_reg.move_out_to(m_dispatch_reg); simd_function_unit::issue(source_reg); } */ // accessors virtual bool stallable() const { return false; } virtual bool can_issue(const warp_inst_t &inst) const { return simd_function_unit::can_issue(inst); } virtual bool is_issue_partitioned() = 0; unsigned get_issue_reg_id() { return m_issue_reg_id; } virtual void print(FILE *fp) const { simd_function_unit::print(fp); for (int s = m_pipeline_depth - 1; s >= 0; s--) { if (!m_pipeline_reg[s]->empty()) { fprintf(fp, " %s[%2d] ", m_name.c_str(), s); m_pipeline_reg[s]->print(fp); } } } protected: unsigned m_pipeline_depth; warp_inst_t **m_pipeline_reg; register_set *m_result_port; class shader_core_ctx *m_core; unsigned m_issue_reg_id; // if sub_core_model is enabled we can only issue // from a subset of operand collectors unsigned active_insts_in_pipeline; }; class sfu : public pipelined_simd_unit { public: sfu(register_set *result_port, const shader_core_config *config, shader_core_ctx *core, unsigned issue_reg_id); virtual bool can_issue(const warp_inst_t &inst) const { switch (inst.op) { case SFU_OP: break; case ALU_SFU_OP: break; case DP_OP: break; // for compute <= 29 (i..e Fermi and GT200) default: return false; } return pipelined_simd_unit::can_issue(inst); } virtual void active_lanes_in_pipeline(); virtual void issue(register_set &source_reg); bool is_issue_partitioned() { return true; } }; class dp_unit : public pipelined_simd_unit { public: dp_unit(register_set *result_port, const shader_core_config *config, shader_core_ctx *core, unsigned issue_reg_id); virtual bool can_issue(const warp_inst_t &inst) const { switch (inst.op) { case DP_OP: break; default: return false; } return pipelined_simd_unit::can_issue(inst); } virtual void active_lanes_in_pipeline(); virtual void issue(register_set &source_reg); bool is_issue_partitioned() { return true; } }; class tensor_core : public pipelined_simd_unit { public: tensor_core(register_set *result_port, const shader_core_config *config, shader_core_ctx *core, unsigned issue_reg_id); virtual bool can_issue(const warp_inst_t &inst) const { switch (inst.op) { case TENSOR_CORE_OP: break; default: return false; } return pipelined_simd_unit::can_issue(inst); } virtual void active_lanes_in_pipeline(); virtual void issue(register_set &source_reg); bool is_issue_partitioned() { return true; } }; class int_unit : public pipelined_simd_unit { public: int_unit(register_set *result_port, const shader_core_config *config, shader_core_ctx *core, unsigned issue_reg_id); virtual bool can_issue(const warp_inst_t &inst) const { switch (inst.op) { case SFU_OP: return false; case LOAD_OP: return false; case TENSOR_CORE_LOAD_OP: return false; case STORE_OP: return false; case TENSOR_CORE_STORE_OP: return false; case MEMORY_BARRIER_OP: return false; case SP_OP: return false; case DP_OP: return false; default: break; } return pipelined_simd_unit::can_issue(inst); } virtual void active_lanes_in_pipeline(); virtual void issue(register_set &source_reg); bool is_issue_partitioned() { return true; } }; class sp_unit : public pipelined_simd_unit { public: sp_unit(register_set *result_port, const shader_core_config *config, shader_core_ctx *core, unsigned issue_reg_id); virtual bool can_issue(const warp_inst_t &inst) const { switch (inst.op) { case SFU_OP: return false; case LOAD_OP: return false; case TENSOR_CORE_LOAD_OP: return false; case STORE_OP: return false; case TENSOR_CORE_STORE_OP: return false; case MEMORY_BARRIER_OP: return false; case DP_OP: return false; default: break; } return pipelined_simd_unit::can_issue(inst); } virtual void active_lanes_in_pipeline(); virtual void issue(register_set &source_reg); bool is_issue_partitioned() { return true; } }; class specialized_unit : public pipelined_simd_unit { public: specialized_unit(register_set *result_port, const shader_core_config *config, shader_core_ctx *core, int supported_op, char *unit_name, unsigned latency, unsigned issue_reg_id); virtual bool can_issue(const warp_inst_t &inst) const { if (inst.op != m_supported_op) { return false; } return pipelined_simd_unit::can_issue(inst); } virtual void active_lanes_in_pipeline(); virtual void issue(register_set &source_reg); bool is_issue_partitioned() { return true; } private: int m_supported_op; }; class simt_core_cluster; class shader_memory_interface; class shader_core_mem_fetch_allocator; class cache_t; class ldst_unit : public pipelined_simd_unit { public: ldst_unit(mem_fetch_interface *icnt, shader_core_mem_fetch_allocator *mf_allocator, shader_core_ctx *core, opndcoll_rfu_t *operand_collector, Scoreboard *scoreboard, const shader_core_config *config, const memory_config *mem_config, class shader_core_stats *stats, unsigned sid, unsigned tpc, gpgpu_sim *gpu); // Add a structure to record the LDGSTS instructions, // similar to m_pending_writes, but since LDGSTS does not have a output // register to write to, so a new structure needs to be added /* A multi-level map: unsigned (warp_id) -> unsigned (pc) -> unsigned (addr) * -> unsigned (count) */ std::map>> m_pending_ldgsts; // modifiers virtual void issue(register_set &inst); bool is_issue_partitioned() { return false; } virtual void cycle(); void fill(mem_fetch *mf); void flush(); void invalidate(); void writeback(); void release_virtual_entries(warp_inst_t &inst); bool memory_cycle(warp_inst_t &inst, mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type); // accessors virtual unsigned clock_multiplier() const; virtual bool can_issue(const warp_inst_t &inst) const { switch (inst.op) { case LOAD_OP: break; case TENSOR_CORE_LOAD_OP: break; case STORE_OP: break; case TENSOR_CORE_STORE_OP: break; case MEMORY_BARRIER_OP: break; default: return false; } return m_dispatch_reg->empty(); } virtual void active_lanes_in_pipeline(); virtual bool stallable() const { return true; } bool response_buffer_full() const; void print(FILE *fout) const; void print_cache_stats(FILE *fp, unsigned &dl1_accesses, unsigned &dl1_misses); void get_cache_stats(unsigned &read_accesses, unsigned &write_accesses, unsigned &read_misses, unsigned &write_misses, unsigned cache_type); void get_cache_stats(cache_stats &cs); void get_L1D_sub_stats(struct cache_sub_stats &css) const; void get_L1C_sub_stats(struct cache_sub_stats &css) const; void get_L1T_sub_stats(struct cache_sub_stats &css) const; protected: ldst_unit(mem_fetch_interface *icnt, shader_core_mem_fetch_allocator *mf_allocator, shader_core_ctx *core, opndcoll_rfu_t *operand_collector, Scoreboard *scoreboard, const shader_core_config *config, const memory_config *mem_config, shader_core_stats *stats, unsigned sid, unsigned tpc, l1_cache *new_l1d_cache); void init(mem_fetch_interface *icnt, shader_core_mem_fetch_allocator *mf_allocator, shader_core_ctx *core, opndcoll_rfu_t *operand_collector, Scoreboard *scoreboard, const shader_core_config *config, const memory_config *mem_config, shader_core_stats *stats, unsigned sid, unsigned tpc); protected: bool shared_cycle(warp_inst_t &inst, mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type); bool constant_cycle(warp_inst_t &inst, mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type); bool texture_cycle(warp_inst_t &inst, mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type); virtual mem_stage_stall_type process_cache_access( cache_t *cache, new_addr_type address, warp_inst_t &inst, std::list &events, mem_fetch *mf, enum cache_request_status status); mem_stage_stall_type process_memory_access_queue(cache_t *cache, warp_inst_t &inst); mem_stage_stall_type process_memory_access_queue_l1cache(l1_cache *cache, warp_inst_t &inst); // Mode 1 helper: decrement m_pending_writes_mask[wid][reg][mask] and, if // it hits zero, release the (reg, mask) entry in the mask-aware // scoreboard. No-op in mode 0. Inline here so the compiler can elide the // mode-0 path entirely. void dec_mask_pw_and_maybe_release(unsigned wid, unsigned reg, const active_mask_t &mask); // MEM co-issue: resolve which source warp + instruction an access/mem_fetch // belongs to (for routing pending_writes / scoreboard release back to the // originating warp under composites). Returns {src_wid, src_out_inst}. // For non-composite or primary-source accesses, returns {primary_wid, &inst}. // split_id differentiates intra-warp co-issued sets (same wid as primary) // from the primary set itself; use (unsigned)-1 when not applicable. struct mem_src_t { unsigned wid; const inst_t *out_inst; // Mode 1: source_inst's full issue mask, looked up from the // matching simd_set's source_mask. Empty for primary access (which // uses pipe_reg's own get_active_mask() at release time). active_mask_t source_mask; // Mode 2: source_inst's owning ibuffer half (0 or 1). For primary // access (non-co-issue), defaults to 0 (composite IS the primary). unsigned source_slot_id; }; mem_src_t resolve_source(const warp_inst_t &inst, unsigned access_src_wid, unsigned access_src_split_id) const; // MEMCO v3: inter-set coalescing pass on a composite's accessq. // Stage B: normalize each access's source_list to a single-entry list // derived from its single (source_wid, source_split_id) stamp. No merging // performed at this stage. Stage C will add the segment-equal merge step. // Behavior gated off: env var MEMCOV3_DISABLE_INTERSET_COALESCE=1 skips. void coalesce_accessq_across_sets(warp_inst_t &inst); gpgpu_sim *m_gpu; const memory_config *m_memory_config; class mem_fetch_interface *m_icnt; shader_core_mem_fetch_allocator *m_mf_allocator; class shader_core_ctx *m_core; unsigned m_sid; unsigned m_tpc; tex_cache *m_L1T; // texture cache read_only_cache *m_L1C; // constant cache l1_cache *m_L1D; // data cache std::map> m_pending_writes; std::list m_response_fifo; opndcoll_rfu_t *m_operand_collector; Scoreboard *m_scoreboard; mem_fetch *m_next_global; warp_inst_t m_next_wb; // MEM co-issue: source warp + split_id for the mem_fetch that fed m_next_wb. // Used by writeback() to decrement the correct source's pending_writes / // release the correct source's scoreboard registers (primary vs secondary). // Set at each "m_next_wb = ..." assignment; defaults to composite primary. unsigned m_next_wb_src_wid; unsigned m_next_wb_src_split_id; // MEMCO v3: list of all (wid, split_id) sources that contributed to the // access feeding m_next_wb. Populated from mf->get_access().get_source_list() // at arb time. Falls back to single-element [(m_next_wb_src_wid, // m_next_wb_src_split_id)] when the access has no explicit source_list. std::vector > m_next_wb_src_list; // MEMCO v3: diagnostic counters. n_merges = inter-set merge events; // n_merged_sources = total sources absorbed by merges (0 => no merging). unsigned long long m_n_interset_merges; unsigned long long m_n_interset_merged_sources; // MEM co-issue intra-warp pending writes: primary and intra-warp-coissued // may write the same register number (R5) for disjoint thread subsets. // The existing m_pending_writes[wid][reg] is single-counter per (wid,reg) // and would conflate the two — leading to premature / delayed scoreboard // release (deadlock). Use a separate map keyed by (wid, split_id, reg) // for intra-warp-coissued accesses so counts stay distinct. std::map>> m_pending_writes_secondary; // Mode 1 (mask-aware scoreboard) pending-writes counter, keyed by // (wid, reg, mask). Mode 0's m_pending_writes aggregates across all // in-flight insts to the same (wid, reg), which is fine for mode 0's // single-entry-per-(wid,reg) scoreboard but causes leaks under mode 1's // per-mask scoreboard: when the aggregate hits zero only the // last-retiring access's mask gets a scoreboard release; other masks // leak. This per-mask counter fires the mask-aware release at the // correct time for each inst. Updated only when mode==1. std::map>> m_pending_writes_mask; unsigned m_writeback_arb; // round-robin arbiter for writeback contention // between L1T, L1C, shared unsigned m_num_writeback_clients; enum mem_stage_stall_type m_mem_rc; shader_core_stats *m_stats; // for debugging unsigned long long m_last_inst_gpu_sim_cycle; unsigned long long m_last_inst_gpu_tot_sim_cycle; std::vector> l1_latency_queue; void L1_latency_queue_cycle(); }; enum pipeline_stage_name_t { ID_OC_SP = 0, ID_OC_DP, ID_OC_INT, ID_OC_SFU, ID_OC_MEM, OC_EX_SP, OC_EX_DP, OC_EX_INT, OC_EX_SFU, OC_EX_MEM, EX_WB, ID_OC_TENSOR_CORE, OC_EX_TENSOR_CORE, N_PIPELINE_STAGES }; const char *const pipeline_stage_name_decode[] = { "ID_OC_SP", "ID_OC_DP", "ID_OC_INT", "ID_OC_SFU", "ID_OC_MEM", "OC_EX_SP", "OC_EX_DP", "OC_EX_INT", "OC_EX_SFU", "OC_EX_MEM", "EX_WB", "ID_OC_TENSOR_CORE", "OC_EX_TENSOR_CORE", "N_PIPELINE_STAGES"}; struct specialized_unit_params { unsigned latency; unsigned num_units; unsigned id_oc_spec_reg_width; unsigned oc_ex_spec_reg_width; char name[20]; unsigned ID_OC_SPEC_ID; unsigned OC_EX_SPEC_ID; }; class shader_core_config : public core_config { public: shader_core_config(gpgpu_context *ctx) : core_config(ctx) { pipeline_widths_string = NULL; gpgpu_ctx = ctx; } void init() { int ntok = sscanf(gpgpu_shader_core_pipeline_opt, "%d:%d", &n_thread_per_shader, &warp_size); if (ntok != 2) { printf( "GPGPU-Sim uArch: error while parsing configuration string " "gpgpu_shader_core_pipeline_opt\n"); abort(); } char *toks = new char[100]; char *tokd = toks; strcpy(toks, pipeline_widths_string); toks = strtok(toks, ","); /* Removing the tensorcore pipeline while reading the config files if the tensor core is not available. If we won't remove it, old regression will be broken. So to support the legacy config files it's best to handle in this way. */ int num_config_to_read = N_PIPELINE_STAGES - 2 * (!gpgpu_tensor_core_avail); for (int i = 0; i < num_config_to_read; i++) { assert(toks); ntok = sscanf(toks, "%d", &pipe_widths[i]); assert(ntok == 1); toks = strtok(NULL, ","); } delete[] tokd; if (n_thread_per_shader > MAX_THREAD_PER_SM) { printf( "GPGPU-Sim uArch: Error ** increase MAX_THREAD_PER_SM in " "abstract_hardware_model.h from %u to %u\n", MAX_THREAD_PER_SM, n_thread_per_shader); abort(); } max_warps_per_shader = n_thread_per_shader / warp_size; assert(!(n_thread_per_shader % warp_size)); // Validate and compute SIMD partitioning derived values if (gpgpu_simd_partitioning) { assert(gpgpu_num_simd_sets > 0 && gpgpu_num_simd_sets <= warp_size); assert(warp_size % gpgpu_num_simd_sets == 0); simd_set_width = warp_size / gpgpu_num_simd_sets; printf("SIMD Partitioning: %u sets of %u lanes\n", gpgpu_num_simd_sets, simd_set_width); } else { gpgpu_num_simd_sets = 1; simd_set_width = warp_size; } // --- Compaction-mode back-compat shim (Change 1) --- // Old flag -gpgpu_enable_compaction maps to mode=2 (full). // New flag -gpgpu_compaction_mode=2 re-asserts the old bool so // the three existing call sites that still read the old flag // continue to work unchanged. Change 2 will migrate the call // sites to read gpgpu_compaction_mode directly. if (gpgpu_compaction_mode > 2) { fprintf(stderr, "GPGPU-Sim: invalid -gpgpu_compaction_mode %u; must be " "0, 1, or 2\n", gpgpu_compaction_mode); abort(); } if (gpgpu_co_issue_priority > 4) { fprintf(stderr, "GPGPU-Sim: invalid -gpgpu_co_issue_priority %u; must be " "in [0,4]\n", gpgpu_co_issue_priority); abort(); } if (gpgpu_scoreboard_mode > 2) { fprintf(stderr, "GPGPU-Sim: invalid -gpgpu_scoreboard_mode %u; must be " "0 (legacy), 1 (mask-aware), or 2 (slot-pinned)\n", gpgpu_scoreboard_mode); abort(); } if (gpgpu_enable_compaction && gpgpu_compaction_mode == 0) { gpgpu_compaction_mode = 2; } if (gpgpu_compaction_mode == 2) { gpgpu_enable_compaction = true; } // Mode 1 (xor-static) has no existing-call-site equivalent yet; // Change 2 wires it up. Until then, selecting mode=1 produces // identity SIMD-set layout (compute_simd_sets) because // gpgpu_enable_compaction is left as whatever the user set. set_pipeline_latency(); m_L1I_config.init(m_L1I_config.m_config_string, FuncCachePreferNone); m_L1T_config.init(m_L1T_config.m_config_string, FuncCachePreferNone); m_L1C_config.init(m_L1C_config.m_config_string, FuncCachePreferNone); m_L1D_config.init(m_L1D_config.m_config_string, FuncCachePreferNone); gpgpu_cache_texl1_linesize = m_L1T_config.get_line_sz(); gpgpu_cache_constl1_linesize = m_L1C_config.get_line_sz(); m_valid = true; m_specialized_unit_num = 0; // parse the specialized units for (unsigned i = 0; i < SPECIALIZED_UNIT_NUM; ++i) { unsigned enabled; specialized_unit_params sparam; sscanf(specialized_unit_string[i], "%u,%u,%u,%u,%u,%s", &enabled, &sparam.num_units, &sparam.latency, &sparam.id_oc_spec_reg_width, &sparam.oc_ex_spec_reg_width, sparam.name); if (enabled) { m_specialized_unit.push_back(sparam); strncpy(m_specialized_unit.back().name, sparam.name, sizeof(m_specialized_unit.back().name)); m_specialized_unit_num += sparam.num_units; } else break; // we only accept continuous specialized_units, i.e., 1,2,3,4 } // parse gpgpu_shmem_option for adpative cache config if (adaptive_cache_config) { std::stringstream ss(gpgpu_shmem_option); while (ss.good()) { std::string option; std::getline(ss, option, ','); shmem_opt_list.push_back((unsigned)std::stoi(option) * 1024); } std::sort(shmem_opt_list.begin(), shmem_opt_list.end()); } } void reg_options(class OptionParser *opp); unsigned max_cta(const kernel_info_t &k) const; unsigned num_shader() const { return n_simt_clusters * n_simt_cores_per_cluster; } unsigned sid_to_cluster(unsigned sid) const { return sid / n_simt_cores_per_cluster; } unsigned sid_to_cid(unsigned sid) const { return sid % n_simt_cores_per_cluster; } unsigned cid_to_sid(unsigned cid, unsigned cluster_id) const { return cluster_id * n_simt_cores_per_cluster + cid; } void set_pipeline_latency(); // backward pointer class gpgpu_context *gpgpu_ctx; // data char *gpgpu_shader_core_pipeline_opt; bool gpgpu_perfect_mem; bool gpgpu_clock_gated_reg_file; bool gpgpu_clock_gated_lanes; enum divergence_support_t model; // ITS (AWARE Reconvergence) config int rec_time_out; // -1 = no reconvergence timeout int num_rec_entries; // physical entries in reconvergence table int num_st_entries; // physical entries in splits table int rec_replacement; // reconvergence table replacement policy int st_replacement; // splits table replacement policy unsigned n_thread_per_shader; unsigned n_regfile_gating_group; unsigned max_warps_per_shader; unsigned max_cta_per_core; // Limit on number of concurrent CTAs in shader core unsigned max_barriers_per_cta; char *gpgpu_scheduler_string; unsigned gpgpu_shmem_per_block; unsigned gpgpu_registers_per_block; char *pipeline_widths_string; int pipe_widths[N_PIPELINE_STAGES]; mutable cache_config m_L1I_config; mutable cache_config m_L1T_config; mutable cache_config m_L1C_config; mutable l1d_cache_config m_L1D_config; bool gpgpu_dwf_reg_bankconflict; unsigned gpgpu_num_sched_per_core; int gpgpu_max_insn_issue_per_warp; bool gpgpu_dual_issue_diff_exec_units; // op collector bool enable_specialized_operand_collector; int gpgpu_operand_collector_num_units_sp; int gpgpu_operand_collector_num_units_dp; int gpgpu_operand_collector_num_units_sfu; int gpgpu_operand_collector_num_units_tensor_core; int gpgpu_operand_collector_num_units_mem; int gpgpu_operand_collector_num_units_gen; int gpgpu_operand_collector_num_units_int; unsigned int gpgpu_operand_collector_num_in_ports_sp; unsigned int gpgpu_operand_collector_num_in_ports_dp; unsigned int gpgpu_operand_collector_num_in_ports_sfu; unsigned int gpgpu_operand_collector_num_in_ports_tensor_core; unsigned int gpgpu_operand_collector_num_in_ports_mem; unsigned int gpgpu_operand_collector_num_in_ports_gen; unsigned int gpgpu_operand_collector_num_in_ports_int; unsigned int gpgpu_operand_collector_num_out_ports_sp; unsigned int gpgpu_operand_collector_num_out_ports_dp; unsigned int gpgpu_operand_collector_num_out_ports_sfu; unsigned int gpgpu_operand_collector_num_out_ports_tensor_core; unsigned int gpgpu_operand_collector_num_out_ports_mem; unsigned int gpgpu_operand_collector_num_out_ports_gen; unsigned int gpgpu_operand_collector_num_out_ports_int; unsigned int gpgpu_num_sp_units; unsigned int gpgpu_tensor_core_avail; unsigned int gpgpu_num_dp_units; unsigned int gpgpu_num_sfu_units; unsigned int gpgpu_num_tensor_core_units; unsigned int gpgpu_num_mem_units; unsigned int gpgpu_num_int_units; // Shader core resources unsigned gpgpu_shader_registers; int gpgpu_warpdistro_shader; int gpgpu_warp_issue_shader; unsigned gpgpu_num_reg_banks; bool gpgpu_reg_bank_use_warp_id; bool gpgpu_local_mem_map; bool gpgpu_ignore_resources_limitation; bool sub_core_model; unsigned max_sp_latency; unsigned max_int_latency; unsigned max_sfu_latency; unsigned max_dp_latency; unsigned max_tensor_core_latency; // SIMD lane partitioning bool gpgpu_simd_partitioning; unsigned gpgpu_num_simd_sets; unsigned simd_set_width; // derived: warp_size / num_simd_sets bool gpgpu_enable_compaction; unsigned gpgpu_compaction_mode; // 0=none, 1=xor-static, 2=full unsigned gpgpu_opndcoll_read_latency; // extra cycles to collector unsigned gpgpu_co_issue_priority; // 0=greedy..4=same-PC bool gpgpu_simd_partitioning_debug; // verbose SIMD_SETS printf gate bool gpgpu_mem_coissue; // enable MEM/LDST co-issue path unsigned gpgpu_scoreboard_mode; // 0=legacy, 1=mask-aware, 2=slot-pinned unsigned n_simt_cores_per_cluster; unsigned n_simt_clusters; unsigned n_simt_ejection_buffer_size; unsigned ldst_unit_response_queue_size; int simt_core_sim_order; unsigned smem_latency; unsigned mem2device(unsigned memid) const { return memid + n_simt_clusters; } // Jin: concurrent kernel on sm bool gpgpu_concurrent_kernel_sm; bool perfect_inst_const_cache; unsigned inst_fetch_throughput; unsigned reg_file_port_throughput; // specialized unit config strings char *specialized_unit_string[SPECIALIZED_UNIT_NUM]; mutable std::vector m_specialized_unit; unsigned m_specialized_unit_num; }; struct shader_core_stats_pod { void * shader_core_stats_pod_start[0]; // DO NOT MOVE FROM THE TOP - spaceless // pointer to the start of this structure unsigned long long *shader_cycles; unsigned *m_num_sim_insn; // number of scalar thread instructions committed // by this shader core unsigned *m_num_sim_winsn; // number of warp instructions committed by this // shader core unsigned *m_last_num_sim_insn; unsigned *m_last_num_sim_winsn; unsigned * m_num_decoded_insn; // number of instructions decoded by this shader core float *m_pipeline_duty_cycle; unsigned *m_num_FPdecoded_insn; unsigned *m_num_INTdecoded_insn; unsigned *m_num_storequeued_insn; unsigned *m_num_loadqueued_insn; unsigned *m_num_tex_inst; double *m_num_ialu_acesses; double *m_num_fp_acesses; double *m_num_imul_acesses; double *m_num_fpmul_acesses; double *m_num_idiv_acesses; double *m_num_fpdiv_acesses; double *m_num_sp_acesses; double *m_num_sfu_acesses; double *m_num_tensor_core_acesses; double *m_num_tex_acesses; double *m_num_const_acesses; double *m_num_dp_acesses; double *m_num_dpmul_acesses; double *m_num_dpdiv_acesses; double *m_num_sqrt_acesses; double *m_num_log_acesses; double *m_num_sin_acesses; double *m_num_exp_acesses; double *m_num_mem_acesses; unsigned *m_num_sp_committed; unsigned *m_num_tlb_hits; unsigned *m_num_tlb_accesses; unsigned *m_num_sfu_committed; unsigned *m_num_tensor_core_committed; unsigned *m_num_mem_committed; unsigned *m_read_regfile_acesses; unsigned *m_write_regfile_acesses; unsigned *m_non_rf_operands; double *m_num_imul24_acesses; double *m_num_imul32_acesses; unsigned *m_active_sp_lanes; unsigned *m_active_sfu_lanes; unsigned *m_active_tensor_core_lanes; unsigned *m_active_fu_lanes; unsigned *m_active_fu_mem_lanes; double *m_active_exu_threads; // For power model double *m_active_exu_warps; // For power model unsigned *m_n_diverge; // number of divergence occurring in this shader unsigned gpgpu_n_load_insn; unsigned gpgpu_n_store_insn; unsigned gpgpu_n_shmem_insn; unsigned gpgpu_n_sstarr_insn; unsigned gpgpu_n_tex_insn; unsigned gpgpu_n_const_insn; unsigned gpgpu_n_param_insn; unsigned gpgpu_n_shmem_bkconflict; unsigned gpgpu_n_l1cache_bkconflict; int gpgpu_n_intrawarp_mshr_merge; unsigned gpgpu_n_cmem_portconflict; unsigned gpu_stall_shd_mem_breakdown[N_MEM_STAGE_ACCESS_TYPE] [N_MEM_STAGE_STALL_TYPE]; unsigned gpu_reg_bank_conflict_stalls; unsigned *shader_cycle_distro; unsigned *last_shader_cycle_distro; unsigned *num_warps_issuable; unsigned gpgpu_n_stall_shd_mem; unsigned *single_issue_nums; unsigned *dual_issue_nums; unsigned ctas_completed; // memory access classification int gpgpu_n_mem_read_local; int gpgpu_n_mem_write_local; int gpgpu_n_mem_texture; int gpgpu_n_mem_const; int gpgpu_n_mem_read_global; int gpgpu_n_mem_write_global; int gpgpu_n_mem_read_inst; int gpgpu_n_mem_l2_writeback; int gpgpu_n_mem_l1_write_allocate; int gpgpu_n_mem_l2_write_allocate; unsigned made_write_mfs; unsigned made_read_mfs; unsigned *gpgpu_n_shmem_bank_access; long *n_simt_to_mem; // Interconnect power stats long *n_mem_to_simt; // Change 5: SIMD partitioning co-issue counters. All per-core arrays // of size num_shader(); histogram is flat size num_shader() * // (gpgpu_num_simd_sets + 1). unsigned long long *inter_warp_coissue_events; unsigned long long *intra_warp_coissue_events; unsigned long long *coissue_denied_by_fu_mismatch; unsigned long long *coissue_denied_by_scoreboard; unsigned long long *coissue_denied_by_no_sets; unsigned long long *coissue_total_active_lanes; unsigned long long *coissue_composite_cycles; unsigned long long *simd_sets_used_histogram; // --- Temporary diagnostic counters for inter-warp candidate filters --- // These quantify the "silent" rejections before the existing // coissue_denied_by_* counters. Used to diagnose why inter-warp // co-issue never fires on stall-bound workloads like `nw` and `lud`. unsigned long long *coissue_cand_seen; unsigned long long *coissue_skipped_null; unsigned long long *coissue_skipped_done_exit; unsigned long long *coissue_skipped_is_primary; unsigned long long *coissue_skipped_picked_earlier; unsigned long long *coissue_skipped_ibuffer_empty; unsigned long long *coissue_skipped_waiting; unsigned long long *coissue_skipped_simt_blocked; unsigned long long *coissue_skipped_no_inst; unsigned long long *coissue_skipped_pc_mismatch; unsigned long long *coissue_skipped_samepc_pass0; // MEM co-issue specific counters (the subset of coissue_* events where // the composite's FU type is MEM). Useful for diagnosing whether the // -gpgpu_mem_coissue flag is actually firing and how much of the total // co-issue activity is memory-backed on a given workload. unsigned long long *mem_coissue_inter_events; unsigned long long *mem_coissue_intra_events; unsigned long long *mem_coissue_denied_filter; // safety filter rejects }; class shader_core_stats : public shader_core_stats_pod { public: shader_core_stats(const shader_core_config *config) { m_config = config; shader_core_stats_pod *pod = reinterpret_cast( this->shader_core_stats_pod_start); memset(pod, 0, sizeof(shader_core_stats_pod)); shader_cycles = (unsigned long long *)calloc(config->num_shader(), sizeof(unsigned long long)); m_num_sim_insn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_sim_winsn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_last_num_sim_winsn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_last_num_sim_insn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_pipeline_duty_cycle = (float *)calloc(config->num_shader(), sizeof(float)); m_num_decoded_insn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_FPdecoded_insn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_storequeued_insn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_loadqueued_insn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_tex_inst = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_INTdecoded_insn = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_ialu_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_fp_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_imul_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_imul24_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_imul32_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_fpmul_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_idiv_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_fpdiv_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_dp_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_dpmul_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_dpdiv_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_sp_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_sfu_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_tensor_core_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_const_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_tex_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_sqrt_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_log_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_sin_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_exp_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_mem_acesses = (double *)calloc(config->num_shader(), sizeof(double)); m_num_sp_committed = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_tlb_hits = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_tlb_accesses = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_active_sp_lanes = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_active_sfu_lanes = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_active_tensor_core_lanes = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_active_fu_lanes = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_active_exu_threads = (double *)calloc(config->num_shader(), sizeof(double)); m_active_exu_warps = (double *)calloc(config->num_shader(), sizeof(double)); m_active_fu_mem_lanes = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_sfu_committed = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_tensor_core_committed = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_num_mem_committed = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_read_regfile_acesses = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_write_regfile_acesses = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_non_rf_operands = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_n_diverge = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); shader_cycle_distro = (unsigned *)calloc(config->warp_size + 3, sizeof(unsigned)); last_shader_cycle_distro = (unsigned *)calloc(m_config->warp_size + 3, sizeof(unsigned)); single_issue_nums = (unsigned *)calloc(config->gpgpu_num_sched_per_core, sizeof(unsigned)); dual_issue_nums = (unsigned *)calloc(config->gpgpu_num_sched_per_core, sizeof(unsigned)); ctas_completed = 0; n_simt_to_mem = (long *)calloc(config->num_shader(), sizeof(long)); n_mem_to_simt = (long *)calloc(config->num_shader(), sizeof(long)); m_outgoing_traffic_stats = new traffic_breakdown("coretomem"); m_incoming_traffic_stats = new traffic_breakdown("memtocore"); gpgpu_n_shmem_bank_access = (unsigned *)calloc(config->num_shader(), sizeof(unsigned)); m_shader_dynamic_warp_issue_distro.resize(config->num_shader()); m_shader_warp_slot_issue_distro.resize(config->num_shader()); // Change 5: SIMD partitioning co-issue counters inter_warp_coissue_events = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); intra_warp_coissue_events = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_denied_by_fu_mismatch = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_denied_by_scoreboard = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_denied_by_no_sets = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_total_active_lanes = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_composite_cycles = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); // Histogram bins: 0..gpgpu_num_simd_sets (inclusive). When // partitioning is off, gpgpu_num_simd_sets == 1 so we allocate 2 bins. unsigned nbins = config->gpgpu_num_simd_sets + 1; simd_sets_used_histogram = (unsigned long long *)calloc( (size_t)config->num_shader() * nbins, sizeof(unsigned long long)); // Diagnostic counters coissue_cand_seen = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_null = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_done_exit = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_is_primary = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_picked_earlier = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_ibuffer_empty = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_waiting = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_simt_blocked = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_no_inst = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_pc_mismatch = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); coissue_skipped_samepc_pass0 = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); // MEM co-issue-specific counters mem_coissue_inter_events = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); mem_coissue_intra_events = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); mem_coissue_denied_filter = (unsigned long long *)calloc( config->num_shader(), sizeof(unsigned long long)); } ~shader_core_stats() { delete m_outgoing_traffic_stats; delete m_incoming_traffic_stats; free(m_num_sim_insn); free(m_num_sim_winsn); free(m_num_FPdecoded_insn); free(m_num_INTdecoded_insn); free(m_num_storequeued_insn); free(m_num_loadqueued_insn); free(m_num_ialu_acesses); free(m_num_fp_acesses); free(m_num_imul_acesses); free(m_num_tex_inst); free(m_num_fpmul_acesses); free(m_num_idiv_acesses); free(m_num_fpdiv_acesses); free(m_num_sp_acesses); free(m_num_sfu_acesses); free(m_num_tensor_core_acesses); free(m_num_tex_acesses); free(m_num_const_acesses); free(m_num_dp_acesses); free(m_num_dpmul_acesses); free(m_num_dpdiv_acesses); free(m_num_sqrt_acesses); free(m_num_log_acesses); free(m_num_sin_acesses); free(m_num_exp_acesses); free(m_num_mem_acesses); free(m_num_sp_committed); free(m_num_tlb_hits); free(m_num_tlb_accesses); free(m_num_sfu_committed); free(m_num_tensor_core_committed); free(m_num_mem_committed); free(m_read_regfile_acesses); free(m_write_regfile_acesses); free(m_non_rf_operands); free(m_num_imul24_acesses); free(m_num_imul32_acesses); free(m_active_sp_lanes); free(m_active_sfu_lanes); free(m_active_tensor_core_lanes); free(m_active_fu_lanes); free(m_active_exu_threads); free(m_active_exu_warps); free(m_active_fu_mem_lanes); free(m_n_diverge); free(shader_cycle_distro); free(last_shader_cycle_distro); // Change 5: SIMD partitioning co-issue counters free(inter_warp_coissue_events); free(intra_warp_coissue_events); free(coissue_denied_by_fu_mismatch); free(coissue_denied_by_scoreboard); free(coissue_denied_by_no_sets); free(coissue_total_active_lanes); free(coissue_composite_cycles); free(simd_sets_used_histogram); free(coissue_cand_seen); free(coissue_skipped_null); free(coissue_skipped_done_exit); free(coissue_skipped_is_primary); free(coissue_skipped_picked_earlier); free(coissue_skipped_ibuffer_empty); free(coissue_skipped_waiting); free(coissue_skipped_simt_blocked); free(coissue_skipped_no_inst); free(coissue_skipped_pc_mismatch); free(coissue_skipped_samepc_pass0); free(mem_coissue_inter_events); free(mem_coissue_intra_events); free(mem_coissue_denied_filter); } void new_grid() {} void event_warp_issued(unsigned s_id, unsigned warp_id, unsigned num_issued, unsigned dynamic_warp_id); void visualizer_print(gzFile visualizer_file); void print(FILE *fout) const; const std::vector> &get_dynamic_warp_issue() const { return m_shader_dynamic_warp_issue_distro; } const std::vector> &get_warp_slot_issue() const { return m_shader_warp_slot_issue_distro; } private: const shader_core_config *m_config; traffic_breakdown *m_outgoing_traffic_stats; // core to memory partitions traffic_breakdown *m_incoming_traffic_stats; // memory partition to core // Counts the instructions issued for each dynamic warp. std::vector> m_shader_dynamic_warp_issue_distro; std::vector m_last_shader_dynamic_warp_issue_distro; std::vector> m_shader_warp_slot_issue_distro; std::vector m_last_shader_warp_slot_issue_distro; friend class power_stat_t; friend class shader_core_ctx; friend class ldst_unit; friend class simt_core_cluster; friend class sst_simt_core_cluster; friend class scheduler_unit; friend class TwoLevelScheduler; friend class LooseRoundRobbinScheduler; }; class memory_config; class shader_core_mem_fetch_allocator : public mem_fetch_allocator { public: shader_core_mem_fetch_allocator(unsigned core_id, unsigned cluster_id, const memory_config *config) { m_core_id = core_id; m_cluster_id = cluster_id; m_memory_config = config; } mem_fetch *alloc(new_addr_type addr, mem_access_type type, unsigned size, bool wr, unsigned long long cycle, unsigned long long streamID) const; mem_fetch *alloc(new_addr_type addr, mem_access_type type, const active_mask_t &active_mask, const mem_access_byte_mask_t &byte_mask, const mem_access_sector_mask_t §or_mask, unsigned size, bool wr, unsigned long long cycle, unsigned wid, unsigned sid, unsigned tpc, mem_fetch *original_mf, unsigned long long streamID) const; mem_fetch *alloc(const warp_inst_t &inst, const mem_access_t &access, unsigned long long cycle) const { warp_inst_t inst_copy = inst; // If the access was tagged with a source warp_id (MEM co-issue), // stamp the mem_fetch with that wid so writeback routes back to the // correct warp's scoreboard / pending_writes entry. Otherwise fall // back to the composite's primary warp_id (legacy behavior). unsigned wid = access.get_source_wid(); if (wid == (unsigned)-1) wid = inst.warp_id(); mem_fetch *mf = new mem_fetch( access, &inst_copy, inst.get_streamID(), access.is_write() ? WRITE_PACKET_SIZE : READ_PACKET_SIZE, wid, m_core_id, m_cluster_id, m_memory_config, cycle); return mf; } private: unsigned m_core_id; unsigned m_cluster_id; const memory_config *m_memory_config; }; class shader_core_ctx : public core_t { public: // creator: shader_core_ctx(class gpgpu_sim *gpu, class simt_core_cluster *cluster, unsigned shader_id, unsigned tpc_id, const shader_core_config *config, const memory_config *mem_config, shader_core_stats *stats); // Read-only diagnostic — dumps every warp's state (flags, ibuffer, // simt_tables, scoreboard) to help localize a deadlocked warp. Called // from gpu-sim.cc deadlock_check() under MEMCO_DBG_DEADLOCK env var. // Public so simt_core_cluster can invoke without relying on friendship. void dump_warps_at_deadlock(FILE *fout); // used by simt_core_cluster: // modifiers void cycle(); void reinit(unsigned start_thread, unsigned end_thread, bool reset_not_completed); void issue_block2core(class kernel_info_t &kernel); void cache_flush(); void cache_invalidate(); void accept_fetch_response(mem_fetch *mf); void accept_ldst_unit_response(class mem_fetch *mf); void broadcast_barrier_reduction(unsigned cta_id, unsigned bar_id, warp_set_t warps); void set_kernel(kernel_info_t *k) { assert(k); m_kernel = k; // k->inc_running(); printf("GPGPU-Sim uArch: Shader %d bind to kernel %u \'%s\'\n", m_sid, m_kernel->get_uid(), m_kernel->name().c_str()); } PowerscalingCoefficients *scaling_coeffs; // accessors bool fetch_unit_response_buffer_full() const; bool ldst_unit_response_buffer_full() const; unsigned get_not_completed() const { return m_not_completed; } unsigned get_n_active_cta() const { return m_n_active_cta; } unsigned isactive() const { if (m_n_active_cta > 0) return 1; else return 0; } kernel_info_t *get_kernel() { return m_kernel; } unsigned get_sid() const { return m_sid; } // used by functional simulation: // modifiers virtual void warp_exit(unsigned warp_id); // Ni: Unset ldgdepbar void unset_depbar(const warp_inst_t &inst); // accessors virtual bool warp_waiting_at_barrier(unsigned warp_id) const; void get_pdom_stack_top_info(unsigned tid, unsigned *pc, unsigned *rpc) const; float get_current_occupancy(unsigned long long &active, unsigned long long &total) const; // used by pipeline timing model components: // modifiers void mem_instruction_stats(const warp_inst_t &inst); void decrement_atomic_count(unsigned wid, unsigned n); void inc_store_req(unsigned warp_id) { m_warp[warp_id]->inc_store_req(); } void dec_inst_in_pipeline(unsigned warp_id) { m_warp[warp_id]->dec_inst_in_pipeline(); } // also used in writeback() void store_ack(class mem_fetch *mf); bool warp_waiting_at_mem_barrier(unsigned warp_id); void set_max_cta(const kernel_info_t &kernel); void warp_inst_complete(const warp_inst_t &inst); // accessors std::list get_regs_written(const inst_t &fvt) const; const shader_core_config *get_config() const { return m_config; } void print_cache_stats(FILE *fp, unsigned &dl1_accesses, unsigned &dl1_misses); void print_scoreboard_accounting(FILE *fp) const; void get_cache_stats(cache_stats &cs); void get_L1I_sub_stats(struct cache_sub_stats &css) const; void get_L1D_sub_stats(struct cache_sub_stats &css) const; void get_L1C_sub_stats(struct cache_sub_stats &css) const; void get_L1T_sub_stats(struct cache_sub_stats &css) const; void get_icnt_power_stats(long &n_simt_to_mem, long &n_mem_to_simt) const; // debug: void display_simt_state(FILE *fout, int mask) const; void display_pipeline(FILE *fout, int print_mem, int mask3bit) const; void incload_stat() { m_stats->m_num_loadqueued_insn[m_sid]++; } void incstore_stat() { m_stats->m_num_storequeued_insn[m_sid]++; } void incialu_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_ialu_acesses[m_sid] = m_stats->m_num_ialu_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_ialu_acesses[m_sid] = m_stats->m_num_ialu_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incimul_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_imul_acesses[m_sid] = m_stats->m_num_imul_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_imul_acesses[m_sid] = m_stats->m_num_imul_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incimul24_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_imul24_acesses[m_sid] = m_stats->m_num_imul24_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_imul24_acesses[m_sid] = m_stats->m_num_imul24_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incimul32_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_imul32_acesses[m_sid] = m_stats->m_num_imul32_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_imul32_acesses[m_sid] = m_stats->m_num_imul32_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incidiv_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_idiv_acesses[m_sid] = m_stats->m_num_idiv_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_idiv_acesses[m_sid] = m_stats->m_num_idiv_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incfpalu_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_fp_acesses[m_sid] = m_stats->m_num_fp_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_fp_acesses[m_sid] = m_stats->m_num_fp_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incfpmul_stat(unsigned active_count, double latency) { // printf("FP MUL stat increament\n"); if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_fpmul_acesses[m_sid] = m_stats->m_num_fpmul_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_fpmul_acesses[m_sid] = m_stats->m_num_fpmul_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incfpdiv_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_fpdiv_acesses[m_sid] = m_stats->m_num_fpdiv_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_fpdiv_acesses[m_sid] = m_stats->m_num_fpdiv_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incdpalu_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_dp_acesses[m_sid] = m_stats->m_num_dp_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_dp_acesses[m_sid] = m_stats->m_num_dp_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incdpmul_stat(unsigned active_count, double latency) { // printf("FP MUL stat increament\n"); if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_dpmul_acesses[m_sid] = m_stats->m_num_dpmul_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_dpmul_acesses[m_sid] = m_stats->m_num_dpmul_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incdpdiv_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_dpdiv_acesses[m_sid] = m_stats->m_num_dpdiv_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_dpdiv_acesses[m_sid] = m_stats->m_num_dpdiv_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incsqrt_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_sqrt_acesses[m_sid] = m_stats->m_num_sqrt_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_sqrt_acesses[m_sid] = m_stats->m_num_sqrt_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void inclog_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_log_acesses[m_sid] = m_stats->m_num_log_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_log_acesses[m_sid] = m_stats->m_num_log_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incexp_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_exp_acesses[m_sid] = m_stats->m_num_exp_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_exp_acesses[m_sid] = m_stats->m_num_exp_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void incsin_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_sin_acesses[m_sid] = m_stats->m_num_sin_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_sin_acesses[m_sid] = m_stats->m_num_sin_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void inctensor_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_tensor_core_acesses[m_sid] = m_stats->m_num_tensor_core_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_tensor_core_acesses[m_sid] = m_stats->m_num_tensor_core_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void inctex_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_tex_acesses[m_sid] = m_stats->m_num_tex_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_sfu(active_count, latency); } else { m_stats->m_num_tex_acesses[m_sid] = m_stats->m_num_tex_acesses[m_sid] + (double)active_count * latency; } m_stats->m_active_exu_threads[m_sid] += active_count; m_stats->m_active_exu_warps[m_sid]++; } void inc_const_accesses(unsigned active_count) { m_stats->m_num_const_acesses[m_sid] = m_stats->m_num_const_acesses[m_sid] + active_count; } void incsfu_stat(unsigned active_count, double latency) { m_stats->m_num_sfu_acesses[m_sid] = m_stats->m_num_sfu_acesses[m_sid] + (double)active_count * latency; } void incsp_stat(unsigned active_count, double latency) { m_stats->m_num_sp_acesses[m_sid] = m_stats->m_num_sp_acesses[m_sid] + (double)active_count * latency; } void incmem_stat(unsigned active_count, double latency) { if (m_config->gpgpu_clock_gated_lanes == false) { m_stats->m_num_mem_acesses[m_sid] = m_stats->m_num_mem_acesses[m_sid] + (double)active_count * latency + inactive_lanes_accesses_nonsfu(active_count, latency); } else { m_stats->m_num_mem_acesses[m_sid] = m_stats->m_num_mem_acesses[m_sid] + (double)active_count * latency; } } void incexecstat(warp_inst_t *&inst); void incregfile_reads(unsigned active_count) { m_stats->m_read_regfile_acesses[m_sid] = m_stats->m_read_regfile_acesses[m_sid] + active_count; } void incregfile_writes(unsigned active_count) { m_stats->m_write_regfile_acesses[m_sid] = m_stats->m_write_regfile_acesses[m_sid] + active_count; } void incnon_rf_operands(unsigned active_count) { m_stats->m_non_rf_operands[m_sid] = m_stats->m_non_rf_operands[m_sid] + active_count; } void incspactivelanes_stat(unsigned active_count) { m_stats->m_active_sp_lanes[m_sid] = m_stats->m_active_sp_lanes[m_sid] + active_count; } void incsfuactivelanes_stat(unsigned active_count) { m_stats->m_active_sfu_lanes[m_sid] = m_stats->m_active_sfu_lanes[m_sid] + active_count; } void incfuactivelanes_stat(unsigned active_count) { m_stats->m_active_fu_lanes[m_sid] = m_stats->m_active_fu_lanes[m_sid] + active_count; } void incfumemactivelanes_stat(unsigned active_count) { m_stats->m_active_fu_mem_lanes[m_sid] = m_stats->m_active_fu_mem_lanes[m_sid] + active_count; } void inc_simt_to_mem(unsigned n_flits) { m_stats->n_simt_to_mem[m_sid] += n_flits; } bool check_if_non_released_reduction_barrier(warp_inst_t &inst); // ITS (AWARE Reconvergence) methods bool is_virtualized(unsigned wid); bool pending_reconvergence(unsigned wid); bool warp_blocked(unsigned wid); bool warp_valid(unsigned wid); // SIMD lane partitioning: secondary split access bool has_secondary_split(unsigned wid); bool get_secondary_split_info(unsigned wid, unsigned *pc, unsigned *rpc, unsigned *split_id, simt_mask_t *mask); bool is_split_valid(unsigned wid, unsigned split_id); void get_split_info(unsigned wid, unsigned split_id, unsigned *pc, simt_mask_t *mask); bool move_split_to_front(unsigned wid, unsigned split_id); simt_tables *get_simt_tables(unsigned wid) { return m_simt_tables[wid]; } bool push_to_st_response_fifo(unsigned wid, unsigned entry); bool push_to_rt_response_fifo(unsigned wid, unsigned entry); void update_st_size(unsigned n); void update_rt_size(unsigned n); void check_time_out(); bool branch_unit_avail(unsigned wid); bool memory_cycle(warp_inst_t &inst, mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type); void updateSIMTDivergenceStructuresInitialization(); protected: unsigned inactive_lanes_accesses_sfu(unsigned active_count, double latency) { return (((32 - active_count) >> 1) * latency) + (((32 - active_count) >> 3) * latency) + (((32 - active_count) >> 3) * latency); } unsigned inactive_lanes_accesses_nonsfu(unsigned active_count, double latency) { return (((32 - active_count) >> 1) * latency); } int test_res_bus(int latency); address_type next_pc(int tid) const; void fetch(); void register_cta_thread_exit(unsigned cta_num, kernel_info_t *kernel); void decode(); void issue(); friend class scheduler_unit; // this is needed to use private issue warp. friend class TwoLevelScheduler; friend class LooseRoundRobbinScheduler; virtual warp_inst_t *issue_warp(register_set &warp, const warp_inst_t *pI, const active_mask_t &active_mask, unsigned warp_id, unsigned sch_id); void co_issue_warp(warp_inst_t *composite, const warp_inst_t *next_inst, const active_mask_t &active_mask, unsigned warp_id, unsigned sch_id, unsigned start_set = 0, unsigned split_id = (unsigned)-1); void create_front_pipeline(); void create_schedulers(); void create_exec_pipeline(); // pure virtual methods implemented based on the current execution mode // (execution-driven vs trace-driven) virtual void init_warps(unsigned cta_id, unsigned start_thread, unsigned end_thread, unsigned ctaid, int cta_size, kernel_info_t &kernel); virtual void checkExecutionStatusAndUpdate(warp_inst_t &inst, unsigned t, unsigned tid) = 0; virtual void func_exec_inst(warp_inst_t &inst) = 0; virtual unsigned sim_init_thread(kernel_info_t &kernel, ptx_thread_info **thread_info, int sid, unsigned tid, unsigned threads_left, unsigned num_threads, core_t *core, unsigned hw_cta_id, unsigned hw_warp_id, gpgpu_t *gpu) = 0; virtual void create_shd_warp() = 0; virtual const warp_inst_t *get_next_inst(unsigned warp_id, address_type pc) = 0; virtual void get_pdom_stack_top_info(unsigned warp_id, const warp_inst_t *pI, unsigned *pc, unsigned *rpc) = 0; virtual const active_mask_t &get_active_mask(unsigned warp_id, const warp_inst_t *pI) = 0; // Returns numbers of addresses in translated_addrs unsigned translate_local_memaddr(address_type localaddr, unsigned tid, unsigned num_shader, unsigned datasize, new_addr_type *translated_addrs); void read_operands(); void execute(); void writeback(); // used in display_pipeline(): void dump_warp_state(FILE *fout) const; void print_stage(unsigned int stage, FILE *fout) const; unsigned long long m_last_inst_gpu_sim_cycle; unsigned long long m_last_inst_gpu_tot_sim_cycle; // general information unsigned m_sid; // shader id unsigned m_tpc; // texture processor cluster id (aka, node id when using // interconnect concentration) const shader_core_config *m_config; const memory_config *m_memory_config; class simt_core_cluster *m_cluster; // statistics shader_core_stats *m_stats; // CTA scheduling / hardware thread allocation unsigned m_n_active_cta; // number of Cooperative Thread Arrays (blocks) // currently running on this shader. unsigned m_cta_status[MAX_CTA_PER_SHADER]; // CTAs status unsigned m_not_completed; // number of threads to be completed (==0 when all // thread on this core completed) std::bitset m_active_threads; // thread contexts thread_ctx_t *m_threadState; // interconnect interface mem_fetch_interface *m_icnt; shader_core_mem_fetch_allocator *m_mem_fetch_allocator; // fetch read_only_cache *m_L1I; // instruction cache int m_last_warp_fetched; // decode/dispatch std::vector m_warp; // per warp information array barrier_set_t m_barriers; ifetch_buffer_t m_inst_fetch_buffer; ifetch_buffer_t m_inst_fetch_buffer_secondary; // for secondary split std::vector m_pipeline_reg; Scoreboard *m_scoreboard; opndcoll_rfu_t m_operand_collector; int m_active_warps; std::vector m_specilized_dispatch_reg; // schedule std::vector schedulers; // issue unsigned int Issue_Prio; // execute unsigned m_num_function_units; std::vector m_dispatch_port; std::vector m_issue_port; std::vector m_fu; // stallable pipelines should be last in this array ldst_unit *m_ldst_unit; static const unsigned MAX_ALU_LATENCY = 512; unsigned num_result_bus; std::vector *> m_result_bus; // used for local address mapping with single kernel launch unsigned kernel_max_cta_per_shader; unsigned kernel_padded_threads_per_cta; // Used for handing out dynamic warp_ids to new warps. // the differnece between a warp_id and a dynamic_warp_id // is that the dynamic_warp_id is a running number unique to every warp // run on this shader, where the warp_id is the static warp slot. unsigned m_dynamic_warp_id; // Jin: concurrent kernels on a sm public: bool can_issue_1block(kernel_info_t &kernel); bool occupy_shader_resource_1block(kernel_info_t &kernel, bool occupy); void release_shader_resource_1block(unsigned hw_ctaid, kernel_info_t &kernel); int find_available_hwtid(unsigned int cta_size, bool occupy); private: unsigned int m_occupied_n_threads; unsigned int m_occupied_shmem; unsigned int m_occupied_regs; unsigned int m_occupied_ctas; std::bitset m_occupied_hwtid; std::map m_occupied_cta_to_hwtid; }; class exec_shader_core_ctx : public shader_core_ctx { public: exec_shader_core_ctx(class gpgpu_sim *gpu, class simt_core_cluster *cluster, unsigned shader_id, unsigned tpc_id, const shader_core_config *config, const memory_config *mem_config, shader_core_stats *stats) : shader_core_ctx(gpu, cluster, shader_id, tpc_id, config, mem_config, stats) { create_front_pipeline(); create_shd_warp(); create_schedulers(); create_exec_pipeline(); } virtual void checkExecutionStatusAndUpdate(warp_inst_t &inst, unsigned t, unsigned tid); virtual void func_exec_inst(warp_inst_t &inst); void execute_warp_inst_per_set(warp_inst_t &inst); virtual unsigned sim_init_thread(kernel_info_t &kernel, ptx_thread_info **thread_info, int sid, unsigned tid, unsigned threads_left, unsigned num_threads, core_t *core, unsigned hw_cta_id, unsigned hw_warp_id, gpgpu_t *gpu); virtual void create_shd_warp(); virtual const warp_inst_t *get_next_inst(unsigned warp_id, address_type pc); virtual void get_pdom_stack_top_info(unsigned warp_id, const warp_inst_t *pI, unsigned *pc, unsigned *rpc); virtual const active_mask_t &get_active_mask(unsigned warp_id, const warp_inst_t *pI); }; class simt_core_cluster { public: simt_core_cluster(class gpgpu_sim *gpu, unsigned cluster_id, const shader_core_config *config, const memory_config *mem_config, shader_core_stats *stats, memory_stats_t *mstats); void core_cycle(); void icnt_cycle(); void reinit(); unsigned issue_block2core(); void cache_flush(); void cache_invalidate(); bool icnt_injection_buffer_full(unsigned size, bool write); void icnt_inject_request_packet(class mem_fetch *mf); void update_icnt_stats(class mem_fetch *mf); // for perfect memory interface bool response_queue_full() { return (m_response_fifo.size() >= m_config->n_simt_ejection_buffer_size); } void push_response_fifo(class mem_fetch *mf) { m_response_fifo.push_back(mf); } void get_pdom_stack_top_info(unsigned sid, unsigned tid, unsigned *pc, unsigned *rpc) const; unsigned max_cta(const kernel_info_t &kernel); unsigned get_not_completed() const; void print_not_completed(FILE *fp) const; // Read-only diagnostic — dumps each non-empty core's per-warp state // (see shader_core_ctx::dump_warps_at_deadlock). void dump_warps_at_deadlock(FILE *fp); unsigned get_n_active_cta() const; unsigned get_n_active_sms() const; gpgpu_sim *get_gpu() { return m_gpu; } void display_pipeline(unsigned sid, FILE *fout, int print_mem, int mask); void print_cache_stats(FILE *fp, unsigned &dl1_accesses, unsigned &dl1_misses) const; void print_scoreboard_accounting(FILE *fp) const; void get_cache_stats(cache_stats &cs) const; void get_L1I_sub_stats(struct cache_sub_stats &css) const; void get_L1D_sub_stats(struct cache_sub_stats &css) const; void get_L1C_sub_stats(struct cache_sub_stats &css) const; void get_L1T_sub_stats(struct cache_sub_stats &css) const; void get_icnt_stats(long &n_simt_to_mem, long &n_mem_to_simt) const; float get_current_occupancy(unsigned long long &active, unsigned long long &total) const; virtual void create_shader_core_ctx() = 0; protected: unsigned m_cluster_id; gpgpu_sim *m_gpu; const shader_core_config *m_config; shader_core_stats *m_stats; memory_stats_t *m_memory_stats; shader_core_ctx **m_core; const memory_config *m_mem_config; unsigned m_cta_issue_next_core; std::list m_core_sim_order; std::list m_response_fifo; }; class exec_simt_core_cluster : public simt_core_cluster { public: exec_simt_core_cluster(class gpgpu_sim *gpu, unsigned cluster_id, const shader_core_config *config, const memory_config *mem_config, class shader_core_stats *stats, class memory_stats_t *mstats) : simt_core_cluster(gpu, cluster_id, config, mem_config, stats, mstats) { create_shader_core_ctx(); } virtual void create_shader_core_ctx(); }; /** * @brief SST cluster class * */ class sst_simt_core_cluster : public exec_simt_core_cluster { public: sst_simt_core_cluster(class gpgpu_sim *gpu, unsigned cluster_id, const shader_core_config *config, const memory_config *mem_config, class shader_core_stats *stats, class memory_stats_t *mstats) : exec_simt_core_cluster(gpu, cluster_id, config, mem_config, stats, mstats) {} /** * @brief Check if SST memory request injection * buffer is full by using extern * function is_SST_buffer_full() * defined in Balar * * @param size * @param write * @param type * @return true * @return false */ bool SST_injection_buffer_full(unsigned size, bool write, mem_access_type type); /** * @brief Send memory request packets to SST * memory * * @param mf */ void icnt_inject_request_packet_to_SST(class mem_fetch *mf); /** * @brief Advance ICNT between core and SST * */ void icnt_cycle_SST(); }; class shader_memory_interface : public mem_fetch_interface { public: shader_memory_interface(shader_core_ctx *core, simt_core_cluster *cluster) { m_core = core; m_cluster = cluster; } virtual bool full(unsigned size, bool write) const { return m_cluster->icnt_injection_buffer_full(size, write); } virtual void push(mem_fetch *mf) { m_core->inc_simt_to_mem(mf->get_num_flits(true)); m_cluster->icnt_inject_request_packet(mf); } private: shader_core_ctx *m_core; simt_core_cluster *m_cluster; }; class perfect_memory_interface : public mem_fetch_interface { public: perfect_memory_interface(shader_core_ctx *core, simt_core_cluster *cluster) { m_core = core; m_cluster = cluster; } virtual bool full(unsigned size, bool write) const { return m_cluster->response_queue_full(); } virtual void push(mem_fetch *mf) { if (mf && mf->isatomic()) mf->do_atomic(); // execute atomic inside the "memory subsystem" m_core->inc_simt_to_mem(mf->get_num_flits(true)); m_cluster->push_response_fifo(mf); } private: shader_core_ctx *m_core; simt_core_cluster *m_cluster; }; /** * @brief SST memory interface * */ class sst_memory_interface : public mem_fetch_interface { public: sst_memory_interface(shader_core_ctx *core, sst_simt_core_cluster *cluster) { m_core = core; m_cluster = cluster; } /** * @brief For constant, inst, tex cache access * * @param size * @param write * @return true * @return false */ virtual bool full(unsigned size, bool write) const { assert(false && "Use the full() method with access type instead!"); return true; } /** * @brief With SST, the core will direct all mem access except for * constant, tex, and inst reads to SST mem system * (i.e. not modeling constant mem right now), thus * requiring the mem_access_type information to be passed in * * @param size * @param write * @param type * @return true * @return false */ bool full(unsigned size, bool write, mem_access_type type) const { return m_cluster->SST_injection_buffer_full(size, write, type); } /** * @brief Push memory request to SST memory system and * update stats * * @param mf */ virtual void push(mem_fetch *mf) { m_core->inc_simt_to_mem(mf->get_num_flits(true)); m_cluster->icnt_inject_request_packet_to_SST(mf); } private: shader_core_ctx *m_core; sst_simt_core_cluster *m_cluster; }; inline int scheduler_unit::get_sid() const { return m_shader->get_sid(); } #endif /* SHADER_H */