From bb4cc3179dd36e882fb78732c9dfb99b1528b8d9 Mon Sep 17 00:00:00 2001 From: Wilson Fung Date: Wed, 17 Jul 2013 16:44:50 -0800 Subject: Redesigned the memory partition unit to support multiple L2 cache banks per partition. Each L2 cache banks has its own connection to the interconnection network to allow L2 bandwidth to scale without increase the number of memory parttiion units. [git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 16613] --- src/gpgpu-sim/l2cache.cc | 340 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 270 insertions(+), 70 deletions(-) (limited to 'src/gpgpu-sim/l2cache.cc') 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::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;im_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); -} -- cgit v1.3