diff options
| -rw-r--r-- | CHANGES | 4 | ||||
| -rw-r--r-- | src/gpgpu-sim/Makefile | 7 | ||||
| -rw-r--r-- | src/gpgpu-sim/addrdec.cc | 39 | ||||
| -rw-r--r-- | src/gpgpu-sim/addrdec.h | 7 | ||||
| -rw-r--r-- | src/gpgpu-sim/dram.cc | 7 | ||||
| -rw-r--r-- | src/gpgpu-sim/dram.h | 3 | ||||
| -rw-r--r-- | src/gpgpu-sim/gpu-sim.cc | 47 | ||||
| -rw-r--r-- | src/gpgpu-sim/gpu-sim.h | 26 | ||||
| -rw-r--r-- | src/gpgpu-sim/l2cache.cc | 340 | ||||
| -rw-r--r-- | src/gpgpu-sim/l2cache.h | 131 | ||||
| -rw-r--r-- | src/gpgpu-sim/l2cache_trace.h | 55 | ||||
| -rw-r--r-- | src/gpgpu-sim/mem_fetch.h | 1 | ||||
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 2 | ||||
| -rw-r--r-- | src/trace.cc | 1 | ||||
| -rw-r--r-- | src/trace.h | 1 | ||||
| -rw-r--r-- | src/trace_streams.tup | 1 |
16 files changed, 520 insertions, 152 deletions
@@ -32,6 +32,10 @@ Version 3.2.1+edits (development branch) versus 3.2.1 - Adding an option to force global memory access to skip L1 data cache, while local memory accesses can still be cached in L1 data cache. This feature can be used to emulate the behavior of '-Xptxas -dlcm=cg'. +- Redesigned the memory partition microarchitecture model to allow multiple L2 + cache banks (sub partitions) in each memory partition. Each memory partition + contains a single DRAM scheduler, and one or more L2 cache banks. Each L2 + cache bank has an independent port to the interconnection network. - Bug Fixes: - Fixed the flit count sent to GPUWattch for atomic operations. - Fix for Bug 51 - Updated the function declaration of diff --git a/src/gpgpu-sim/Makefile b/src/gpgpu-sim/Makefile index e355b59..bead38a 100644 --- a/src/gpgpu-sim/Makefile +++ b/src/gpgpu-sim/Makefile @@ -33,10 +33,8 @@ TRACE?=0 ifeq ($(DEBUG),1) CXXFLAGS = -Wall -DDEBUG - CXXFLAGS_L2CACHE = -Wall -DDEBUG else CXXFLAGS = -Wall - CXXFLAGS_L2CACHE = -Wall endif ifeq ($(TRACE),1) @@ -47,13 +45,11 @@ include ../../version_detection.mk ifeq ($(GNUC_CPP0X), 1) CXXFLAGS += -std=c++0x - CXXFLAGS_L2CACHE += -std=c++0x endif ifneq ($(DEBUG),1) OPTFLAGS += -O3 else - CXXFLAGS_L2CACHE += CXXFLAGS += endif @@ -90,9 +86,6 @@ depend: touch $(OUTPUT_DIR)/Makefile.makedepend makedepend -f$(OUTPUT_DIR)/Makefile.makedepend -p$(OUTPUT_DIR)/ $(CSRCS) 2> /dev/null -$(OUTPUT_DIR)/l2cache.$(OEXT): l2cache.cc - $(CPP) $(OPTFLAGS) $(CXXFLAGS_L2CACHE) -o $*.$(OEXT) -c l2cache.cc - $(OUTPUT_DIR)/%.$(OEXT): %.cc $(CPP) $(OPTFLAGS) $(CXXFLAGS) $(POWER_FLAGS) -o $(OUTPUT_DIR)/$*.$(OEXT) -c $*.cc diff --git a/src/gpgpu-sim/addrdec.cc b/src/gpgpu-sim/addrdec.cc index 9b2f339..c1fa6e5 100644 --- a/src/gpgpu-sim/addrdec.cc +++ b/src/gpgpu-sim/addrdec.cc @@ -71,7 +71,7 @@ new_addr_type linear_to_raw_address_translation::partition_address( new_addr_typ } else { // see addrdec_tlx for explanation unsigned long long int partition_addr; - partition_addr = ( (addr>>ADDR_CHIP_S) / Nchips) << ADDR_CHIP_S; + partition_addr = ( (addr>>ADDR_CHIP_S) / m_n_channel) << ADDR_CHIP_S; partition_addr |= addr & ((1 << ADDR_CHIP_S) - 1); return partition_addr; } @@ -90,8 +90,8 @@ void linear_to_raw_address_translation::addrdec_tlx(new_addr_type addr, addrdec_ // Split the given address at ADDR_CHIP_S into (MSBs,LSBs) // - extract chip address using modulus of MSBs // - recreate the rest of the address by stitching the quotient of MSBs and the LSBs - addr_for_chip = (addr>>ADDR_CHIP_S) % Nchips; - rest_of_addr = ( (addr>>ADDR_CHIP_S) / Nchips) << ADDR_CHIP_S; + addr_for_chip = (addr>>ADDR_CHIP_S) % m_n_channel; + rest_of_addr = ( (addr>>ADDR_CHIP_S) / m_n_channel) << ADDR_CHIP_S; rest_of_addr |= addr & ((1 << ADDR_CHIP_S) - 1); tlx->chip = addr_for_chip; @@ -100,6 +100,11 @@ void linear_to_raw_address_translation::addrdec_tlx(new_addr_type addr, addrdec_ tlx->col = addrdec_packbits(addrdec_mask[COL], rest_of_addr, addrdec_mkhigh[COL], addrdec_mklow[COL]); tlx->burst= addrdec_packbits(addrdec_mask[BURST], rest_of_addr, addrdec_mkhigh[BURST], addrdec_mklow[BURST]); } + + // combine the chip address and the lower bits of DRAM bank address to form the subpartition ID + unsigned sub_partition_addr_mask = m_n_sub_partition_in_channel - 1; + tlx->sub_partition = tlx->chip * m_n_sub_partition_in_channel + + (tlx->bk & sub_partition_addr_mask); } void linear_to_raw_address_translation::addrdec_parseoption(const char *option) @@ -152,14 +157,15 @@ void linear_to_raw_address_translation::addrdec_parseoption(const char *option) } } -void linear_to_raw_address_translation::init(unsigned int nchips) +void linear_to_raw_address_translation::init(unsigned int n_channel, unsigned int n_sub_partition_in_channel) { unsigned i; unsigned long long int mask; - unsigned int nchipbits = ::LOGB2_32(nchips); - Nchips = nchips; + unsigned int nchipbits = ::LOGB2_32(n_channel); + m_n_channel = n_channel; + m_n_sub_partition_in_channel = n_sub_partition_in_channel; - gap = (nchips - ::powli(2,nchipbits)); + gap = (n_channel - ::powli(2,nchipbits)); if (gap) { nchipbits++; } @@ -280,9 +286,11 @@ void linear_to_raw_address_translation::init(unsigned int nchips) } } // otherwise, no need to change the masks } else { - // make sure nchips is power of two when explicit dram id mask is used - assert((nchips & (nchips - 1)) == 0); + // make sure n_channel is power of two when explicit dram id mask is used + assert((n_channel & (n_channel - 1)) == 0); } + // make sure m_n_sub_partition_in_channel is power of two + assert((m_n_sub_partition_in_channel & (m_n_sub_partition_in_channel - 1)) == 0); addrdec_getmasklimit(addrdec_mask[CHIP], &addrdec_mkhigh[CHIP], &addrdec_mklow[CHIP] ); addrdec_getmasklimit(addrdec_mask[BK], &addrdec_mkhigh[BK], &addrdec_mklow[BK] ); @@ -348,7 +356,7 @@ void linear_to_raw_address_translation::sweep_test() const printf("[AddrDec] ** Error: address decoding mapping aliases two addresses to same partition with same intra-partition address: %llx %llx\n", h->second, raw_addr); abort(); } else { - assert((int)tlx.chip < Nchips); + assert((int)tlx.chip < m_n_channel); // ensure that partition_address() returns the concatenated address if ((ADDR_CHIP_S != -1 and raw_addr >= (1ULL << ADDR_CHIP_S)) or (ADDR_CHIP_S == -1 and raw_addr >= (1ULL << addrdec_mklow[CHIP]))) { @@ -363,11 +371,12 @@ void linear_to_raw_address_translation::sweep_test() const void addrdec_t::print( FILE *fp ) const { - if (chip) fprintf(fp,"\tchip:%x ", chip); - if (row) fprintf(fp,"\trow:%x ", row); - if (col) fprintf(fp,"\tcol:%x ", col); - if (bk) fprintf(fp,"\tbk:%x ", bk); - if (burst) fprintf(fp,"\tburst:%x ", burst); + fprintf(fp,"\tchip:%x ", chip); + fprintf(fp,"\trow:%x ", row); + fprintf(fp,"\tcol:%x ", col); + fprintf(fp,"\tbk:%x ", bk); + fprintf(fp,"\tburst:%x ", burst); + fprintf(fp,"\tsub_partition:%x ", sub_partition); } diff --git a/src/gpgpu-sim/addrdec.h b/src/gpgpu-sim/addrdec.h index 94b113a..f16c9b1 100644 --- a/src/gpgpu-sim/addrdec.h +++ b/src/gpgpu-sim/addrdec.h @@ -43,13 +43,15 @@ struct addrdec_t { unsigned row; unsigned col; unsigned burst; + + unsigned sub_partition; }; class linear_to_raw_address_translation { public: linear_to_raw_address_translation(); void addrdec_setoption(option_parser_t opp); - void init(unsigned int nchips); + void init(unsigned int n_channel, unsigned int n_sub_partition_in_channel); // accessors void addrdec_tlx(new_addr_type addr, addrdec_t *tlx) const; @@ -78,7 +80,8 @@ private: new_addr_type addrdec_mask[N_ADDRDEC]; unsigned int gap; - int Nchips; + int m_n_channel; + int m_n_sub_partition_in_channel; }; #endif diff --git a/src/gpgpu-sim/dram.cc b/src/gpgpu-sim/dram.cc index f28db2f..92c9727 100644 --- a/src/gpgpu-sim/dram.cc +++ b/src/gpgpu-sim/dram.cc @@ -421,11 +421,16 @@ void dram_t::cycle() } //if mrq is being serviced by dram, gets popped after CL latency fulfilled -class mem_fetch* dram_t::pop() +class mem_fetch* dram_t::return_queue_pop() { return returnq->pop(); } +class mem_fetch* dram_t::return_queue_top() +{ + return returnq->top(); +} + void dram_t::print( FILE* simFile) const { unsigned i; diff --git a/src/gpgpu-sim/dram.h b/src/gpgpu-sim/dram.h index 8c0a2b8..a8bff14 100644 --- a/src/gpgpu-sim/dram.h +++ b/src/gpgpu-sim/dram.h @@ -104,7 +104,8 @@ public: unsigned int queue_limit() const; void visualizer_print( gzFile visualizer_file ); - class mem_fetch* pop(); + class mem_fetch* return_queue_pop(); + class mem_fetch* return_queue_top(); void push( class mem_fetch *data ); void cycle(); void dram_log (int task); diff --git a/src/gpgpu-sim/gpu-sim.cc b/src/gpgpu-sim/gpu-sim.cc index 5fa487a..475833f 100644 --- a/src/gpgpu-sim/gpu-sim.cc +++ b/src/gpgpu-sim/gpu-sim.cc @@ -159,6 +159,9 @@ void memory_config::reg_options(class OptionParser * opp) option_parser_register(opp, "-gpgpu_n_mem", OPT_UINT32, &m_n_mem, "number of memory modules (e.g. memory controllers) in gpu", "8"); + option_parser_register(opp, "-gpgpu_n_sub_partition_per_mchannel", OPT_UINT32, &m_n_sub_partition_per_memory_channel, + "number of memory subpartition in each memory module", + "1"); option_parser_register(opp, "-gpgpu_n_mem_per_ctrlr", OPT_UINT32, &gpu_n_mem_per_ctrlr, "number of memory chips per memory controller", "1"); @@ -429,6 +432,9 @@ void gpgpu_sim_config::reg_options(option_parser_t opp) option_parser_register(opp, "-trace_sampling_core", OPT_INT32, &Trace::sampling_core, "The core which is printed using CORE_DPRINTF. Default 0", "0"); + option_parser_register(opp, "-trace_sampling_memory_partition", OPT_INT32, + &Trace::sampling_memory_partition, "The memory partition which is printed using MEMPART_DPRINTF. Default -1 (i.e. all)", + "-1"); ptx_file_line_stats_options(opp); } @@ -564,10 +570,16 @@ gpgpu_sim::gpgpu_sim( const gpgpu_sim_config &config ) m_cluster[i] = new simt_core_cluster(this,i,m_shader_config,m_memory_config,m_shader_stats,m_memory_stats); m_memory_partition_unit = new memory_partition_unit*[m_memory_config->m_n_mem]; - for (unsigned i=0;i<m_memory_config->m_n_mem;i++) + m_memory_sub_partition = new memory_sub_partition*[m_memory_config->m_n_mem_sub_partition]; + for (unsigned i=0;i<m_memory_config->m_n_mem;i++) { m_memory_partition_unit[i] = new memory_partition_unit(i, m_memory_config, m_memory_stats); + for (unsigned p = 0; p < m_memory_config->m_n_sub_partition_per_memory_channel; p++) { + unsigned submpid = i * m_memory_config->m_n_sub_partition_per_memory_channel + p; + m_memory_sub_partition[submpid] = m_memory_partition_unit[i]->get_sub_partition(p); + } + } - icnt_init(m_shader_config->n_simt_clusters,m_memory_config->m_n_mem); + icnt_init(m_shader_config->n_simt_clusters,m_memory_config->m_n_mem_sub_partition); time_vector_create(NUM_MEM_REQ_STAT); fprintf(stdout, "GPGPU-Sim uArch: performance model initialization complete.\n"); @@ -926,9 +938,9 @@ void gpgpu_sim::gpu_print_stat() total_l2_css.clear(); printf("\n========= L2 cache stats =========\n"); - for (unsigned i=0;i<m_memory_config->m_n_mem;i++){ - m_memory_partition_unit[i]->accumulate_L2cache_stats(l2_stats); - m_memory_partition_unit[i]->get_L2cache_sub_stats(l2_css); + for (unsigned i=0;i<m_memory_config->m_n_mem_sub_partition;i++){ + m_memory_sub_partition[i]->accumulate_L2cache_stats(l2_stats); + m_memory_sub_partition[i]->get_L2cache_sub_stats(l2_css); fprintf( stdout, "L2_cache_bank[%d]: Access = %u, Miss = %u, Miss_rate = %.3lf, Pending_hits = %u, Reservation_fails = %u\n", i, l2_css.accesses, l2_css.misses, (double)l2_css.misses / (double)l2_css.accesses, l2_css.pending_hits, l2_css.res_fails); @@ -1150,8 +1162,8 @@ void gpgpu_sim::cycle() } if (clock_mask & ICNT) { // pop from memory controller to interconnect - for (unsigned i=0;i<m_memory_config->m_n_mem;i++) { - mem_fetch* mf = m_memory_partition_unit[i]->top(); + for (unsigned i=0;i<m_memory_config->m_n_mem_sub_partition;i++) { + mem_fetch* mf = m_memory_sub_partition[i]->top(); if (mf) { unsigned response_size = mf->get_is_write()?mf->get_ctrl_size():mf->size(); if ( ::icnt_has_buffer( m_shader_config->mem2device(i), response_size ) ) { @@ -1159,12 +1171,12 @@ void gpgpu_sim::cycle() mf->set_return_timestamp(gpu_sim_cycle+gpu_tot_sim_cycle); mf->set_status(IN_ICNT_TO_SHADER,gpu_sim_cycle+gpu_tot_sim_cycle); ::icnt_push( m_shader_config->mem2device(i), mf->get_tpc(), mf, response_size ); - m_memory_partition_unit[i]->pop(); + m_memory_sub_partition[i]->pop(); } else { gpu_stall_icnt2sh++; } } else { - m_memory_partition_unit[i]->pop(); + m_memory_sub_partition[i]->pop(); } } } @@ -1182,17 +1194,17 @@ void gpgpu_sim::cycle() // L2 operations follow L2 clock domain if (clock_mask & L2) { m_power_stats->pwr_mem_stat->l2_cache_stats[CURRENT_STAT_IDX].clear(); - for (unsigned i=0;i<m_memory_config->m_n_mem;i++) { + for (unsigned i=0;i<m_memory_config->m_n_mem_sub_partition;i++) { //move memory request from interconnect into memory partition (if not backed up) //Note:This needs to be called in DRAM clock domain if there is no L2 cache in the system - if ( m_memory_partition_unit[i]->full() ) { + if ( m_memory_sub_partition[i]->full() ) { gpu_stall_dramfull++; } else { mem_fetch* mf = (mem_fetch*) icnt_pop( m_shader_config->mem2device(i) ); - m_memory_partition_unit[i]->push( mf, gpu_sim_cycle + gpu_tot_sim_cycle ); + m_memory_sub_partition[i]->push( mf, gpu_sim_cycle + gpu_tot_sim_cycle ); } - m_memory_partition_unit[i]->cache_cycle(gpu_sim_cycle+gpu_tot_sim_cycle); - m_memory_partition_unit[i]->accumulate_L2cache_stats(m_power_stats->pwr_mem_stat->l2_cache_stats[CURRENT_STAT_IDX]); + m_memory_sub_partition[i]->cache_cycle(gpu_sim_cycle+gpu_tot_sim_cycle); + m_memory_sub_partition[i]->accumulate_L2cache_stats(m_power_stats->pwr_mem_stat->l2_cache_stats[CURRENT_STAT_IDX]); } } @@ -1263,7 +1275,7 @@ void gpgpu_sim::cycle() if (m_memory_config->m_L2_config.get_num_lines()) { int dlc = 0; for (unsigned i=0;i<m_memory_config->m_n_mem;i++) { - dlc = m_memory_partition_unit[i]->flushL2(); + dlc = m_memory_sub_partition[i]->flushL2(); assert (dlc == 0); // need to model actual writes to DRAM here printf("Dirty lines flushed from L2 %d is %d\n", i, dlc ); } @@ -1386,8 +1398,3 @@ simt_core_cluster * gpgpu_sim::getSIMTCluster() return *m_cluster; } -void memory_partition_unit::visualizer_print( gzFile visualizer_file ) -{ - m_dram->visualizer_print(visualizer_file); -} - diff --git a/src/gpgpu-sim/gpu-sim.h b/src/gpgpu-sim/gpu-sim.h index 03f9bf2..4e6b7a5 100644 --- a/src/gpgpu-sim/gpu-sim.h +++ b/src/gpgpu-sim/gpu-sim.h @@ -190,20 +190,28 @@ struct memory_config { option_parser_destroy(dram_opp); } - int nbkt = nbk/nbkgrp; - unsigned i; - for (i=0; nbkt>0; i++) { - nbkt = nbkt>>1; - } - bk_tag_length = i; + int nbkt = nbk/nbkgrp; + unsigned i; + for (i=0; nbkt>0; i++) { + nbkt = nbkt>>1; + } + bk_tag_length = i; assert(nbkgrp>0 && "Number of bank groups cannot be zero"); tRCDWR = tRCD-(WL+1); tRTW = (CL+(BL/data_command_freq_ratio)+2-WL); tWTR = (WL+(BL/data_command_freq_ratio)+tCDLR); tWTP = (WL+(BL/data_command_freq_ratio)+tWR); dram_atom_size = BL * busW * gpu_n_mem_per_ctrlr; // burst length x bus width x # chips per partition - m_address_mapping.init(m_n_mem); + + assert( m_n_sub_partition_per_memory_channel > 0 ); + assert( (nbk % m_n_sub_partition_per_memory_channel == 0) + && "Number of DRAM banks must be a perfect multiple of memory sub partition"); + m_n_mem_sub_partition = m_n_mem * m_n_sub_partition_per_memory_channel; + fprintf(stdout, "Total number of memory sub partition = %u\n", m_n_mem_sub_partition); + + m_address_mapping.init(m_n_mem, m_n_sub_partition_per_memory_channel); m_L2_config.init(&m_address_mapping); + m_valid = true; icnt_flit_size = 32; // Default 32 } @@ -221,6 +229,8 @@ struct memory_config { enum dram_ctrl_t scheduler_type; bool gpgpu_memlatency_stat; unsigned m_n_mem; + unsigned m_n_sub_partition_per_memory_channel; + unsigned m_n_mem_sub_partition; unsigned gpu_n_mem_per_ctrlr; unsigned rop_latency; @@ -416,7 +426,6 @@ private: int next_clock_domain(void); void issue_block2core(); void print_dram_stats(FILE *fout) const; - void L2c_print_cache_stat() const; void shader_print_runtime_stat( FILE *fout ); void shader_print_l1_miss_stat( FILE *fout ) const; void shader_print_cache_stats( FILE *fout ) const; @@ -430,6 +439,7 @@ private: class simt_core_cluster **m_cluster; class memory_partition_unit **m_memory_partition_unit; + class memory_sub_partition **m_memory_sub_partition; std::vector<kernel_info_t*> m_running_kernels; unsigned m_last_issued_kernel; diff --git a/src/gpgpu-sim/l2cache.cc b/src/gpgpu-sim/l2cache.cc index a661b4b..d667fd7 100644 --- a/src/gpgpu-sim/l2cache.cc +++ b/src/gpgpu-sim/l2cache.cc @@ -43,6 +43,7 @@ #include "gpu-sim.h" #include "shader.h" #include "mem_latency_stat.h" +#include "l2cache_trace.h" mem_fetch * partition_mf_allocator::alloc(new_addr_type addr, mem_access_type type, unsigned size, bool wr ) const @@ -62,11 +63,234 @@ mem_fetch * partition_mf_allocator::alloc(new_addr_type addr, mem_access_type ty memory_partition_unit::memory_partition_unit( unsigned partition_id, const struct memory_config *config, class memory_stats_t *stats ) +: m_id(partition_id), m_config(config), m_stats(stats), m_arbitration_metadata(config) { - m_id = partition_id; + m_dram = new dram_t(m_id,m_config,m_stats,this); + + m_sub_partition = new memory_sub_partition*[m_config->m_n_sub_partition_per_memory_channel]; + for (unsigned p = 0; p < m_config->m_n_sub_partition_per_memory_channel; p++) { + unsigned sub_partition_id = m_id * m_config->m_n_sub_partition_per_memory_channel + p; + m_sub_partition[p] = new memory_sub_partition(sub_partition_id, m_config, stats); + } +} + +memory_partition_unit::~memory_partition_unit() +{ + delete m_dram; + for (unsigned p = 0; p < m_config->m_n_sub_partition_per_memory_channel; p++) { + delete m_sub_partition[p]; + } + delete[] m_sub_partition; +} + +memory_partition_unit::arbitration_metadata::arbitration_metadata(const struct memory_config *config) +: m_last_borrower(config->m_n_sub_partition_per_memory_channel - 1), + m_private_credit(config->m_n_sub_partition_per_memory_channel, 0), + m_shared_credit(0) +{ + // each sub partition get at least 1 credit for forward progress + // the rest is shared among with other partitions + m_private_credit_limit = 1; + m_shared_credit_limit = config->gpgpu_frfcfs_dram_sched_queue_size + + config->gpgpu_dram_return_queue_size + - (config->m_n_sub_partition_per_memory_channel - 1); + if (config->gpgpu_frfcfs_dram_sched_queue_size == 0 + or config->gpgpu_dram_return_queue_size == 0) + { + m_shared_credit_limit = 0; // no limit if either of the queue has no limit in size + } + assert(m_shared_credit_limit >= 0); +} + +bool memory_partition_unit::arbitration_metadata::has_credits(int inner_sub_partition_id) const +{ + int spid = inner_sub_partition_id; + if (m_private_credit[spid] < m_private_credit_limit) { + return true; + } else if (m_shared_credit_limit == 0 || m_shared_credit < m_shared_credit_limit) { + return true; + } else { + return false; + } +} + +void memory_partition_unit::arbitration_metadata::borrow_credit(int inner_sub_partition_id) +{ + int spid = inner_sub_partition_id; + if (m_private_credit[spid] < m_private_credit_limit) { + m_private_credit[spid] += 1; + } else if (m_shared_credit_limit == 0 || m_shared_credit < m_shared_credit_limit) { + m_shared_credit += 1; + } else { + assert(0 && "DRAM arbitration error: Borrowing from depleted credit!"); + } + m_last_borrower = spid; +} + +void memory_partition_unit::arbitration_metadata::return_credit(int inner_sub_partition_id) +{ + int spid = inner_sub_partition_id; + if (m_private_credit[spid] > 0) { + m_private_credit[spid] -= 1; + } else { + m_shared_credit -= 1; + } + assert((m_shared_credit >= 0) && "DRAM arbitration error: Returning more than available credits!"); +} + +void memory_partition_unit::arbitration_metadata::print( FILE *fp ) const +{ + fprintf(fp, "private_credit = "); + for (unsigned p = 0; p < m_private_credit.size(); p++) { + fprintf(fp, "%d ", m_private_credit[p]); + } + fprintf(fp, "(limit = %d)\n", m_private_credit_limit); + fprintf(fp, "shared_credit = %d (limit = %d)\n", m_shared_credit, m_shared_credit_limit); +} + +bool memory_partition_unit::busy() const +{ + bool busy = false; + for (unsigned p = 0; p < m_config->m_n_sub_partition_per_memory_channel; p++) { + if (m_sub_partition[p]->busy()) { + busy = true; + } + } + return busy; +} + +void memory_partition_unit::cache_cycle(unsigned cycle) +{ + for (unsigned p = 0; p < m_config->m_n_sub_partition_per_memory_channel; p++) { + m_sub_partition[p]->cache_cycle(cycle); + } +} + +void memory_partition_unit::visualizer_print( gzFile visualizer_file ) const +{ + m_dram->visualizer_print(visualizer_file); + for (unsigned p = 0; p < m_config->m_n_sub_partition_per_memory_channel; p++) { + m_sub_partition[p]->visualizer_print(visualizer_file); + } +} + +// determine whether a given subpartition can issue to DRAM +bool memory_partition_unit::can_issue_to_dram(int inner_sub_partition_id) +{ + int spid = inner_sub_partition_id; + bool sub_partition_contention = m_sub_partition[spid]->dram_L2_queue_full(); + bool has_dram_resource = m_arbitration_metadata.has_credits(spid); + + MEMPART_DPRINTF("sub partition %d sub_partition_contention=%c has_dram_resource=%c\n", + spid, (sub_partition_contention)? 'T':'F', (has_dram_resource)? 'T':'F'); + + return (has_dram_resource && !sub_partition_contention); +} + +int memory_partition_unit::global_sub_partition_id_to_local_id(int global_sub_partition_id) const +{ + return (global_sub_partition_id - m_id * m_config->m_n_sub_partition_per_memory_channel); +} + +void memory_partition_unit::dram_cycle() +{ + // pop completed memory request from dram and push it to dram-to-L2 queue + // of the original sub partition + mem_fetch* mf_return = m_dram->return_queue_top(); + if (mf_return) { + unsigned dest_global_spid = mf_return->get_sub_partition_id(); + int dest_spid = global_sub_partition_id_to_local_id(dest_global_spid); + assert(m_sub_partition[dest_spid]->get_id() == dest_global_spid); + if (!m_sub_partition[dest_spid]->dram_L2_queue_full()) { + if( mf_return->get_access_type() == L1_WRBK_ACC ) { + m_sub_partition[dest_spid]->set_done(mf_return); + delete mf_return; + } else { + m_sub_partition[dest_spid]->dram_L2_queue_push(mf_return); + mf_return->set_status(IN_PARTITION_DRAM_TO_L2_QUEUE,gpu_sim_cycle+gpu_tot_sim_cycle); + m_arbitration_metadata.return_credit(dest_spid); + MEMPART_DPRINTF("mem_fetch request %p return from dram to sub partition %d\n", mf_return, dest_spid); + } + m_dram->return_queue_pop(); + } + } else { + m_dram->return_queue_pop(); + } + + m_dram->cycle(); + m_dram->dram_log(SAMPLELOG); + + if( !m_dram->full() ) { + // L2->DRAM queue to DRAM latency queue + // Arbitrate among multiple L2 subpartitions + int last_issued_partition = m_arbitration_metadata.last_borrower(); + for (unsigned p = 0; p < m_config->m_n_sub_partition_per_memory_channel; p++) { + int spid = (p + last_issued_partition + 1) % m_config->m_n_sub_partition_per_memory_channel; + if (!m_sub_partition[spid]->L2_dram_queue_empty() && can_issue_to_dram(spid)) { + mem_fetch *mf = m_sub_partition[spid]->L2_dram_queue_top(); + m_sub_partition[spid]->L2_dram_queue_pop(); + MEMPART_DPRINTF("Issue mem_fetch request %p from sub partition %d to dram\n", mf, spid); + dram_delay_t d; + d.req = mf; + d.ready_cycle = gpu_sim_cycle+gpu_tot_sim_cycle + m_config->dram_latency; + m_dram_latency_queue.push(d); + mf->set_status(IN_PARTITION_DRAM_LATENCY_QUEUE,gpu_sim_cycle+gpu_tot_sim_cycle); + m_arbitration_metadata.borrow_credit(spid); + break; // the DRAM should only accept one request per cycle + } + } + } + + // DRAM latency queue + if( !m_dram_latency_queue.empty() && ( (gpu_sim_cycle+gpu_tot_sim_cycle) >= m_dram_latency_queue.front().ready_cycle ) && !m_dram->full() ) { + mem_fetch* mf = m_dram_latency_queue.front().req; + m_dram_latency_queue.pop(); + m_dram->push(mf); + } +} + +void memory_partition_unit::set_done( mem_fetch *mf ) +{ + unsigned global_spid = mf->get_sub_partition_id(); + int spid = global_sub_partition_id_to_local_id(global_spid); + assert(m_sub_partition[spid]->get_id() == global_spid); + if (mf->get_access_type() == L1_WRBK_ACC || mf->get_access_type() == L2_WRBK_ACC) { + m_arbitration_metadata.return_credit(spid); + MEMPART_DPRINTF("mem_fetch request %p return from dram to sub partition %d\n", mf, spid); + } + m_sub_partition[spid]->set_done(mf); +} + +void memory_partition_unit::set_dram_power_stats(unsigned &n_cmd, + unsigned &n_activity, + unsigned &n_nop, + unsigned &n_act, + unsigned &n_pre, + unsigned &n_rd, + unsigned &n_wr, + unsigned &n_req) const +{ + m_dram->set_dram_power_stats(n_cmd, n_activity, n_nop, n_act, n_pre, n_rd, n_wr, n_req); +} + +void memory_partition_unit::print( FILE *fp ) const +{ + fprintf(fp, "Memory Partition %u: \n", m_id); + for (unsigned p = 0; p < m_config->m_n_sub_partition_per_memory_channel; p++) { + m_sub_partition[p]->print(fp); + } + m_dram->print(fp); +} + +memory_sub_partition::memory_sub_partition( unsigned sub_partition_id, + const struct memory_config *config, + class memory_stats_t *stats ) +{ + m_id = sub_partition_id; m_config=config; m_stats=stats; - m_dram = new dram_t(m_id,m_config,m_stats,this); + + assert(m_id < m_config->m_n_mem_sub_partition); char L2c_name[32]; snprintf(L2c_name, 32, "L2_bank_%03d", m_id); @@ -88,7 +312,7 @@ memory_partition_unit::memory_partition_unit( unsigned partition_id, wb_addr=-1; } -memory_partition_unit::~memory_partition_unit() +memory_sub_partition::~memory_sub_partition() { delete m_icnt_L2_queue; delete m_L2_dram_queue; @@ -98,7 +322,7 @@ memory_partition_unit::~memory_partition_unit() delete m_L2interface; } -void memory_partition_unit::cache_cycle( unsigned cycle ) +void memory_sub_partition::cache_cycle( unsigned cycle ) { // L2 fill responses if( !m_config->m_L2_config.disabled()) { @@ -189,22 +413,47 @@ void memory_partition_unit::cache_cycle( unsigned cycle ) } } -bool memory_partition_unit::full() const +bool memory_sub_partition::full() const { return m_icnt_L2_queue->full(); } -void memory_partition_unit::print_cache_stat(unsigned &accesses, unsigned &misses) const +bool memory_sub_partition::L2_dram_queue_empty() const +{ + return m_L2_dram_queue->empty(); +} + +class mem_fetch* memory_sub_partition::L2_dram_queue_top() const +{ + return m_L2_dram_queue->top(); +} + +void memory_sub_partition::L2_dram_queue_pop() +{ + m_L2_dram_queue->pop(); +} + +bool memory_sub_partition::dram_L2_queue_full() const +{ + return m_dram_L2_queue->full(); +} + +void memory_sub_partition::dram_L2_queue_push( class mem_fetch* mf ) +{ + m_dram_L2_queue->push(mf); +} + +void memory_sub_partition::print_cache_stat(unsigned &accesses, unsigned &misses) const { FILE *fp = stdout; if( !m_config->m_L2_config.disabled() ) m_L2cache->print(fp,accesses,misses); } -void memory_partition_unit::print( FILE *fp ) const +void memory_sub_partition::print( FILE *fp ) const { if ( !m_request_tracker.empty() ) { - fprintf(fp,"Memory Parition %u: pending memory requests:\n", m_id); + fprintf(fp,"Memory Sub Parition %u: pending memory requests:\n", m_id); for ( std::set<mem_fetch*>::const_iterator r=m_request_tracker.begin(); r != m_request_tracker.end(); ++r ) { mem_fetch *mf = *r; if ( mf ) @@ -215,7 +464,6 @@ void memory_partition_unit::print( FILE *fp ) const } if( !m_config->m_L2_config.disabled() ) m_L2cache->display_state(fp); - m_dram->print(fp); } void memory_stats_t::visualizer_print( gzFile visualizer_file ) @@ -264,26 +512,19 @@ void gpgpu_sim::print_dram_stats(FILE *fout) const fprintf(fout,"gpgpu_n_dram_precharges = %d\n",tot_pre ); fprintf(fout,"gpgpu_n_dram_requests = %d\n",tot_req ); } -void gpgpu_sim::L2c_print_cache_stat() const -{ - unsigned i, j, k; - for (i=0,j=0,k=0;i<m_memory_config->m_n_mem;i++) - m_memory_partition_unit[i]->print_cache_stat(k,j); - printf("L2 Cache Total Miss Rate = %0.3f\n", (float)j/k); -} -unsigned memory_partition_unit::flushL2() +unsigned memory_sub_partition::flushL2() { m_L2cache->flush(); return 0; // L2 is read only in this version } -bool memory_partition_unit::busy() const +bool memory_sub_partition::busy() const { return !m_request_tracker.empty(); } -void memory_partition_unit::push( mem_fetch* req, unsigned long long cycle ) +void memory_sub_partition::push( mem_fetch* req, unsigned long long cycle ) { if (req) { m_request_tracker.insert(req); @@ -301,7 +542,7 @@ void memory_partition_unit::push( mem_fetch* req, unsigned long long cycle ) } } -mem_fetch* memory_partition_unit::pop() +mem_fetch* memory_sub_partition::pop() { mem_fetch* mf = m_L2_icnt_queue->pop(); m_request_tracker.erase(mf); @@ -314,7 +555,7 @@ mem_fetch* memory_partition_unit::pop() return mf; } -mem_fetch* memory_partition_unit::top() +mem_fetch* memory_sub_partition::top() { mem_fetch *mf = m_L2_icnt_queue->top(); if( mf && (mf->get_access_type() == L2_WRBK_ACC || mf->get_access_type() == L1_WRBK_ACC) ) { @@ -326,62 +567,21 @@ mem_fetch* memory_partition_unit::top() return mf; } -void memory_partition_unit::set_done( mem_fetch *mf ) +void memory_sub_partition::set_done( mem_fetch *mf ) { m_request_tracker.erase(mf); } -void memory_partition_unit::dram_cycle() -{ - // pop completed memory request from dram and push it to dram-to-L2 queue - if ( !m_dram_L2_queue->full() ) { - mem_fetch* mf = m_dram->pop(); - if (mf) { - if( mf->get_access_type() == L1_WRBK_ACC ) { - m_request_tracker.erase(mf); - delete mf; - } else { - m_dram_L2_queue->push(mf); - mf->set_status(IN_PARTITION_DRAM_TO_L2_QUEUE,gpu_sim_cycle+gpu_tot_sim_cycle); - } - } - } - m_dram->cycle(); - m_dram->dram_log(SAMPLELOG); - - if( !m_dram->full() && !m_L2_dram_queue->empty() ) { - // L2->DRAM queue to DRAM latency queue - mem_fetch *mf = m_L2_dram_queue->pop(); - dram_delay_t d; - d.req = mf; - d.ready_cycle = gpu_sim_cycle+gpu_tot_sim_cycle + m_config->dram_latency; - m_dram_latency_queue.push(d); - mf->set_status(IN_PARTITION_DRAM_LATENCY_QUEUE,gpu_sim_cycle+gpu_tot_sim_cycle); - } - - // DRAM latency queue - if( !m_dram_latency_queue.empty() && ( (gpu_sim_cycle+gpu_tot_sim_cycle) >= m_dram_latency_queue.front().ready_cycle ) && !m_dram->full() ) { - mem_fetch* mf = m_dram_latency_queue.front().req; - m_dram_latency_queue.pop(); - m_dram->push(mf); - } +void memory_sub_partition::accumulate_L2cache_stats(class cache_stats &l2_stats) const { + l2_stats += m_L2cache->get_stats(); } -void memory_partition_unit::set_dram_power_stats(unsigned &n_cmd, - unsigned &n_activity, - unsigned &n_nop, - unsigned &n_act, - unsigned &n_pre, - unsigned &n_rd, - unsigned &n_wr, - unsigned &n_req) const{ - m_dram->set_dram_power_stats(n_cmd, n_activity, n_nop, n_act, n_pre, n_rd, n_wr, n_req); +void memory_sub_partition::get_L2cache_sub_stats(struct cache_sub_stats &css) const{ + m_L2cache->get_sub_stats(css); } -void memory_partition_unit::accumulate_L2cache_stats(class cache_stats &l2_stats) const { - l2_stats += m_L2cache->get_stats(); +void memory_sub_partition::visualizer_print( gzFile visualizer_file ) +{ + // TODO: Add visualizer stats for L2 cache } -void memory_partition_unit::get_L2cache_sub_stats(struct cache_sub_stats &css) const{ - m_L2cache->get_sub_stats(css); -} diff --git a/src/gpgpu-sim/l2cache.h b/src/gpgpu-sim/l2cache.h index 6ef00a7..07e37ca 100644 --- a/src/gpgpu-sim/l2cache.h +++ b/src/gpgpu-sim/l2cache.h @@ -52,9 +52,12 @@ private: const memory_config *m_memory_config; }; -class memory_partition_unit +// Memory partition unit contains all the units assolcated with a single DRAM channel. +// - It arbitrates the DRAM channel among multiple sub partitions. +// - It does not connect directly with the interconnection network. +class memory_partition_unit { -public: +public: memory_partition_unit( unsigned partition_id, const struct memory_config *config, class memory_stats_t *stats ); ~memory_partition_unit(); @@ -63,6 +66,93 @@ public: void cache_cycle( unsigned cycle ); void dram_cycle(); + void set_done( mem_fetch *mf ); + + void visualizer_print( gzFile visualizer_file ) const; + void print_stat( FILE *fp ) { m_dram->print_stat(fp); } + void visualize() const { m_dram->visualize(); } + void print( FILE *fp ) const; + + class memory_sub_partition * get_sub_partition(int sub_partition_id) + { + return m_sub_partition[sub_partition_id]; + } + + // Power model + void set_dram_power_stats(unsigned &n_cmd, + unsigned &n_activity, + unsigned &n_nop, + unsigned &n_act, + unsigned &n_pre, + unsigned &n_rd, + unsigned &n_wr, + unsigned &n_req) const; + + int global_sub_partition_id_to_local_id(int global_sub_partition_id) const; + + unsigned get_mpid() const { return m_id; } + +private: + + unsigned m_id; + const struct memory_config *m_config; + class memory_stats_t *m_stats; + class memory_sub_partition **m_sub_partition; + class dram_t *m_dram; + + class arbitration_metadata + { + public: + arbitration_metadata(const struct memory_config *config); + + // check if a subpartition still has credit + bool has_credits(int inner_sub_partition_id) const; + // borrow a credit for a subpartition + void borrow_credit(int inner_sub_partition_id); + // return a credit from a subpartition + void return_credit(int inner_sub_partition_id); + + // return the last subpartition that borrowed credit + int last_borrower() const { return m_last_borrower; } + + void print( FILE *fp ) const; + private: + // id of the last subpartition that borrowed credit + int m_last_borrower; + + int m_shared_credit_limit; + int m_private_credit_limit; + + // credits borrowed by the subpartitions + std::vector<int> m_private_credit; + int m_shared_credit; + }; + arbitration_metadata m_arbitration_metadata; + + // determine wheither a given subpartition can issue to DRAM + bool can_issue_to_dram(int inner_sub_partition_id); + + // model DRAM access scheduler latency (fixed latency between L2 and DRAM) + struct dram_delay_t + { + unsigned long long ready_cycle; + class mem_fetch* req; + }; + std::queue<dram_delay_t> m_dram_latency_queue; +}; + +class memory_sub_partition +{ +public: + memory_sub_partition( unsigned sub_partition_id, const struct memory_config *config, class memory_stats_t *stats ); + ~memory_sub_partition(); + + unsigned get_id() const { return m_id; } + + bool busy() const; + + void cache_cycle( unsigned cycle ); + bool full() const; void push( class mem_fetch* mf, unsigned long long clock_cycle ); class mem_fetch* pop(); @@ -71,31 +161,26 @@ public: unsigned flushL2(); + // interface to L2_dram_queue + bool L2_dram_queue_empty() const; + class mem_fetch* L2_dram_queue_top() const; + void L2_dram_queue_pop(); + + // interface to dram_L2_queue + bool dram_L2_queue_full() const; + void dram_L2_queue_push( class mem_fetch* mf ); + void visualizer_print( gzFile visualizer_file ); void print_cache_stat(unsigned &accesses, unsigned &misses) const; - void print_stat( FILE *fp ) { m_dram->print_stat(fp); } - void visualize() const { m_dram->visualize(); } void print( FILE *fp ) const; - - // Power model - void set_dram_power_stats(unsigned &n_cmd, - unsigned &n_activity, - unsigned &n_nop, - unsigned &n_act, - unsigned &n_pre, - unsigned &n_rd, - unsigned &n_wr, - unsigned &n_req) const; - void accumulate_L2cache_stats(class cache_stats &l2_stats) const; void get_L2cache_sub_stats(struct cache_sub_stats &css) const; private: // data - unsigned m_id; + unsigned m_id; //< the global sub partition ID const struct memory_config *m_config; - class dram_t *m_dram; class l2_cache *m_L2cache; class L2interface *m_L2interface; partition_mf_allocator *m_mf_allocator; @@ -108,14 +193,6 @@ private: }; std::queue<rop_delay_t> m_rop; - // model DRAM access scheduler latency (fixed latency between L2 and DRAM) - struct dram_delay_t - { - unsigned long long ready_cycle; - class mem_fetch* req; - }; - std::queue<dram_delay_t> m_dram_latency_queue; - // these are various FIFOs between units within a memory partition fifo_pipeline<mem_fetch> *m_icnt_L2_queue; fifo_pipeline<mem_fetch> *m_L2_dram_queue; @@ -134,7 +211,7 @@ private: class L2interface : public mem_fetch_interface { public: - L2interface( memory_partition_unit *unit ) { m_unit=unit; } + L2interface( memory_sub_partition *unit ) { m_unit=unit; } virtual ~L2interface() {} virtual bool full( unsigned size, bool write) const { @@ -147,7 +224,7 @@ public: m_unit->m_L2_dram_queue->push(mf); } private: - memory_partition_unit *m_unit; + memory_sub_partition *m_unit; }; #endif diff --git a/src/gpgpu-sim/l2cache_trace.h b/src/gpgpu-sim/l2cache_trace.h new file mode 100644 index 0000000..3dac87d --- /dev/null +++ b/src/gpgpu-sim/l2cache_trace.h @@ -0,0 +1,55 @@ +// Copyright (c) 2009-2011, Tor M. Aamodt, Tim Rogers, Wilson W. L. Fung +// The University of British Columbia +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 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. +// Neither the name of The University of British Columbia nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// 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. + +#pragma once + +#include "../trace.h" + +#if TRACING_ON + +#define MEMPART_PRINT_STR SIM_PRINT_STR " %d - " +#define MEMPART_DTRACE(x) ( DTRACE(x) && (Trace::sampling_memory_partition == -1 || Trace::sampling_memory_partition == (int)get_mpid()) ) + +// Intended to be called from inside components of a memory partition +// Depends on a get_mpid() function +#define MEMPART_DPRINTF(...) do {\ + if (MEMPART_DTRACE(MEMORY_PARTITION_UNIT)) {\ + printf( MEMPART_PRINT_STR,\ + gpu_sim_cycle + gpu_tot_sim_cycle,\ + Trace::trace_streams_str[Trace::MEMORY_PARTITION_UNIT],\ + get_mpid() );\ + printf(__VA_ARGS__);\ + }\ +} while (0) + +#else + +#define MEMPART_DTRACE(x) (false) +#define MEMPART_DPRINTF(x, ...) do {} while (0) + +#endif + diff --git a/src/gpgpu-sim/mem_fetch.h b/src/gpgpu-sim/mem_fetch.h index c4ba0dc..f788c2b 100644 --- a/src/gpgpu-sim/mem_fetch.h +++ b/src/gpgpu-sim/mem_fetch.h @@ -83,6 +83,7 @@ public: void set_addr(new_addr_type addr) { m_access.set_addr(addr); } new_addr_type get_addr() const { return m_access.get_addr(); } new_addr_type get_partition_addr() const { return m_partition_addr; } + unsigned get_sub_partition_id() const { return m_raw_addr.sub_partition; } bool get_is_write() const { return m_access.is_write(); } unsigned get_request_uid() const { return m_request_uid; } unsigned get_sid() const { return m_sid; } diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index 87e59ed..73a8a62 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -3184,7 +3184,7 @@ void simt_core_cluster::icnt_inject_request_packet(class mem_fetch *mf) packet_size = mf->get_ctrl_size(); } m_stats->m_outgoing_traffic_stats->record_traffic(mf, packet_size); - unsigned destination = mf->get_tlx_addr().chip; + unsigned destination = mf->get_sub_partition_id(); mf->set_status(IN_ICNT_TO_MEM,gpu_sim_cycle+gpu_tot_sim_cycle); if (!mf->get_is_write() && !mf->isatomic()) ::icnt_push(m_cluster_id, m_config->mem2device(destination), (void*)mf, mf->get_ctrl_size() ); diff --git a/src/trace.cc b/src/trace.cc index 05b9d6b..5171e46 100644 --- a/src/trace.cc +++ b/src/trace.cc @@ -41,6 +41,7 @@ namespace Trace { bool enabled = false; int sampling_core = 0; + int sampling_memory_partition = -1; bool trace_streams_enabled[NUM_TRACE_STREAMS] = {false}; const char* config_str; diff --git a/src/trace.h b/src/trace.h index c7b389e..a79b4a0 100644 --- a/src/trace.h +++ b/src/trace.h @@ -46,6 +46,7 @@ namespace Trace { extern bool enabled; extern int sampling_core; + extern int sampling_memory_partition; extern const char* trace_streams_str[]; extern bool trace_streams_enabled[NUM_TRACE_STREAMS]; extern const char* config_str; diff --git a/src/trace_streams.tup b/src/trace_streams.tup index fa6fd1e..c41690e 100644 --- a/src/trace_streams.tup +++ b/src/trace_streams.tup @@ -28,5 +28,6 @@ TS_TUP_BEGIN( trace_streams_type ) TS_TUP( WARP_SCHEDULER ), TS_TUP( SCOREBOARD ), + TS_TUP( MEMORY_PARTITION_UNIT ), TS_TUP( NUM_TRACE_STREAMS ) TS_TUP_END( trace_streams_type ) |
