diff options
| author | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
|---|---|---|
| committer | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
| commit | 69f2911e04ffb1b19eef1fafb8c040af271f656e (patch) | |
| tree | 231d3b6bdc3a202f7c255bfcf7bf2c36e32cee9e /src/gpgpu-sim/l2cache.cc | |
creating branch for adding support for CUDA 3.x and Fermi
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 6829]
Diffstat (limited to 'src/gpgpu-sim/l2cache.cc')
| -rw-r--r-- | src/gpgpu-sim/l2cache.cc | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/src/gpgpu-sim/l2cache.cc b/src/gpgpu-sim/l2cache.cc new file mode 100644 index 0000000..aef837b --- /dev/null +++ b/src/gpgpu-sim/l2cache.cc @@ -0,0 +1,999 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <list> +#include <set> +#include "../tr1_hash_map.h" // for unordered_map failback + +#include "../option_parser.h" +#include "mem_fetch.h" +#include "dram.h" +#include "gpu-cache.h" +#include "histogram.h" +#include "l2cache.h" +#include "../intersim/statwraper.h" + +class L2c_mshr; +class L2c_miss_tracker; +class L2c_access_locality; + +mem_fetch_t* g_debug_mf = NULL; + +// L2 cache block (include the cache model + flow controls) +struct L2cacheblk +{ + shd_cache_t *L2cache; + + delay_queue *cbtoL2queue; //latency 10 + delay_queue *cbtoL2writequeue; + delay_queue *dramtoL2queue; //latency 10 + delay_queue *dramtoL2writequeue; + delay_queue *L2todramqueue; //latency 0 + delay_queue *L2todram_wbqueue; + delay_queue *L2tocbqueue; //latency 0 + + mem_fetch_t *L2request; //request currently being serviced by the L2 Cache + + L2c_mshr *m_mshr; // mshr model + L2c_miss_tracker *m_missTracker; // tracker observing for redundant misses + L2c_access_locality *m_accessLocality; // tracking true locality of L2 Cache access + + L2cacheblk(size_t linesize); + ~L2cacheblk(); +}; + +// external dependencies +extern unsigned long long int addrdec_mask[5]; +extern dram_t **dram; +extern int gpgpu_dram_sched_queue_size; +extern unsigned int gpu_n_shader; +extern unsigned int gpu_n_mem; +extern unsigned long long gpu_sim_cycle; +extern unsigned made_write_mfs; +extern unsigned freed_L1write_mfs; +extern unsigned freed_L2write_mfs; +extern unsigned int gpgpu_n_sent_writes; +extern unsigned int gpgpu_n_processed_writes; +extern unsigned g_next_request_uid; + +void memlatstat_icnt2sh_push(mem_fetch_t *mf); +void memlatstat_dram_access(mem_fetch_t *mf, unsigned dram_id, unsigned bank); +void memlatstat_start(mem_fetch_t *mf); +unsigned memlatstat_done(mem_fetch_t *mf); + +// option +char *gpgpu_L2_queue_config; +int gpgpu_l2_readoverwrite = 0; +int l2_ideal = 0; + +void L2c_options(option_parser_t opp) +{ + option_parser_register(opp, "-gpgpu_L2_queue", OPT_CSTR, &gpgpu_L2_queue_config, + "L2 data cache queue length and latency config", + "0:0:0:0:0:0:10:10"); + + option_parser_register(opp, "-gpgpu_l2_readoverwrite", OPT_BOOL, &gpgpu_l2_readoverwrite, + "Prioritize read requests over write requests for L2", + "0"); + + option_parser_register(opp, "-l2_ideal", OPT_BOOL, &l2_ideal, + "Use a ideal L2 cache that always hit", + "0"); +} + +// stats +unsigned L2_write_miss = 0; +unsigned L2_write_hit = 0; +unsigned L2_read_hit = 0; +unsigned L2_read_miss = 0; +unsigned int *L2_cbtoL2length; +unsigned int *L2_cbtoL2writelength; +unsigned int *L2_L2tocblength; +unsigned int *L2_dramtoL2length; +unsigned int *L2_dramtoL2writelength; +unsigned int *L2_L2todramlength; + +//////////////////////////////////////////////// +// L2 MSHR model + +class L2c_mshr +{ +private: + typedef std::list<const mem_fetch_t*> mem_fetch_list; + typedef tr1_hash_map<address_type, mem_fetch_list> L2missGroup; + L2missGroup m_L2missgroup; // structure tracking redundant dram access + + struct active_chain { + address_type cacheTag; + mem_fetch_list *list; + active_chain() : cacheTag(0xDEADBEEF), list(NULL) { } + }; + active_chain m_active_mshr_chain; + size_t m_linesize; // L2 cache line size + + const size_t m_n_entries; // total number of entries available + size_t m_entries_used; // number of entries in use + + int m_n_miss; + int m_n_miss_serviced_by_dram; + int m_n_mshr_hits; + size_t m_max_entries_used; + + address_type cache_tag(const mem_fetch_t *mf) const + { + // return mf->addr; + return (mf->addr & ~(m_linesize - 1)); + } + +public: + L2c_mshr(size_t linesize, size_t n_entries = 64) + : m_linesize(linesize), m_n_entries(n_entries), m_entries_used(0), + m_n_miss(0), m_n_miss_serviced_by_dram(0), m_n_mshr_hits(0), m_max_entries_used(0) { } + + // add a cache miss to MSHR, return true if this access is hit another existing entry and merges with it + bool new_miss(const mem_fetch_t *mf); + + // notify MSHR that a new cache line has been fetched, activate the associated MSHR chain + void miss_serviced(const mem_fetch_t *mf); + + // probe if there are pending hits left in this MSHR chain + bool mshr_chain_empty(); + + // peek the first entry in the active MSHR chain + mem_fetch_t *mshr_chain_top(); + + // pop the first entry in the active MSHR chain + void mshr_chain_pop(); + + void print(FILE *fout = stdout); + void print_stat(FILE *fout = stdout); +}; + +bool L2c_mshr::new_miss(const mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + mem_fetch_list &missGroup = m_L2missgroup[cacheTag]; + + bool mshr_hit = not missGroup.empty(); + + missGroup.push_front(mf); + + m_n_miss += 1; + if (mshr_hit) + m_n_mshr_hits += 1; + m_entries_used += 1; + m_max_entries_used = std::max(m_max_entries_used, m_entries_used); + + return mshr_hit; +} + +void L2c_mshr::miss_serviced(const mem_fetch_t *mf) +{ + assert(m_active_mshr_chain.list == NULL); + address_type cacheTag = cache_tag(mf); + L2missGroup::iterator missGroup = m_L2missgroup.find(cacheTag); + if (missGroup == m_L2missgroup.end() || mf->type == L2_WTBK_DATA) { + assert(mf->type == L2_WTBK_DATA); // only this returning mem req can be missed by the MSHR + return; + } + assert(missGroup->first == cacheTag); + + m_active_mshr_chain.cacheTag = cacheTag; + m_active_mshr_chain.list = &(missGroup->second); + + m_n_miss_serviced_by_dram += 1; +} + +bool L2c_mshr::mshr_chain_empty() +{ + return (m_active_mshr_chain.list == NULL); +} + +mem_fetch_t *L2c_mshr::mshr_chain_top() +{ + const mem_fetch_t *mf = m_active_mshr_chain.list->back(); + assert(cache_tag(mf) == m_active_mshr_chain.cacheTag); + + return const_cast<mem_fetch_t*>(mf); +} + +void L2c_mshr::mshr_chain_pop() +{ + m_entries_used -= 1; + m_active_mshr_chain.list->pop_back(); + if (m_active_mshr_chain.list->empty()) { + address_type cacheTag = m_active_mshr_chain.cacheTag; + m_L2missgroup.erase(cacheTag); + m_active_mshr_chain.list = NULL; + } +} + +void L2c_mshr::print(FILE *fout) +{ + fprintf(fout, "L2c MSHR: n_entries_used = %zu\n", m_entries_used); + L2missGroup::iterator missGroup; + for (missGroup = m_L2missgroup.begin(); missGroup != m_L2missgroup.end(); ++missGroup) { + fprintf(fout, "%#08x: ", missGroup->first); + mem_fetch_list &mf_list = missGroup->second; + for (mem_fetch_list::iterator imf = mf_list.begin(); imf != mf_list.end(); ++imf) { + fprintf(fout, "%p:%d ", *imf, (*imf)->request_uid); + } + fprintf(fout, "\n"); + } +} + +void L2c_mshr::print_stat(FILE *fout) +{ + fprintf(fout, "L2c MSHR: max_entry = %zu, n_miss = %d, n_mshr_hits = %d, n_serviced_by_dram %d\n", + m_max_entries_used, m_n_miss, m_n_mshr_hits, m_n_miss_serviced_by_dram); +} + +//////////////////////////////////////////////// +// track redundant dram access generated by L2 cache +class L2c_miss_tracker +{ +private: + typedef std::set<mem_fetch_t*> mem_fetch_set; + typedef tr1_hash_map<address_type, mem_fetch_set> L2missGroup; + L2missGroup m_L2missgroup; // structure tracking redundant dram access + size_t m_linesize; // L2 cache line size + + typedef tr1_hash_map<address_type, int> L2redundantCnt; + L2redundantCnt m_L2redundantCnt; + + int m_totalL2redundantAcc; + + address_type cache_tag(const mem_fetch_t *mf) const + { + // return mf->addr; + return (mf->addr & ~(m_linesize - 1)); + } + +public: + L2c_miss_tracker(size_t linesize) : m_linesize(linesize), m_totalL2redundantAcc(0) { } + void new_miss(mem_fetch_t *mf); + void miss_serviced(mem_fetch_t *mf); + + void print(FILE *fout, bool brief = true); + void print_stat(FILE *fout, bool brief = true); + +}; + +void L2c_miss_tracker::new_miss(mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + mem_fetch_set &missGroup = m_L2missgroup[cacheTag]; + + if (missGroup.size() != 0) { + m_L2redundantCnt[cacheTag] += 1; + m_totalL2redundantAcc += 1; + } + + missGroup.insert(mf); +} + +void L2c_miss_tracker::miss_serviced(mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + L2missGroup::iterator iMissGroup = m_L2missgroup.find(cacheTag); + if (iMissGroup == m_L2missgroup.end()) return; // this is possible for write miss + mem_fetch_set &missGroup = iMissGroup->second; + + missGroup.erase(mf); + + // remove the miss group if it goes empty + if (missGroup.empty()) { + m_L2missgroup.erase(iMissGroup); + } +} + +void L2c_miss_tracker::print(FILE *fout, bool brief) +{ + L2missGroup::iterator iMissGroup; + for (iMissGroup = m_L2missgroup.begin(); iMissGroup != m_L2missgroup.end(); ++iMissGroup) { + fprintf(fout, "%#08x: ", iMissGroup->first); + for (mem_fetch_set::iterator iMemSet = iMissGroup->second.begin(); iMemSet != iMissGroup->second.end(); ++iMemSet) { + fprintf(fout, "%p ", *iMemSet); + } + fprintf(fout, "\n"); + } +} + +void L2c_miss_tracker::print_stat(FILE *fout, bool brief) +{ + fprintf(fout, "RedundantMiss = %d\n", m_totalL2redundantAcc); + if (brief == true) return; + fprintf(fout, " Detail:"); + for (L2redundantCnt::iterator iL2rc = m_L2redundantCnt.begin(); iL2rc != m_L2redundantCnt.end(); ++iL2rc) { + fprintf(fout, "%#08x:%d ", iL2rc->first, iL2rc->second); + } + fprintf(fout, "\n"); +} + +//////////////////////////////////////////////// +// track all locality of L2 cache access +class L2c_access_locality +{ +private: + size_t m_linesize; // L2 cache line size + + typedef tr1_hash_map<address_type, int> L2accCnt; + L2accCnt m_L2accCnt; + + int m_totalL2accAcc; + + address_type cache_tag(const mem_fetch_t *mf) const + { + // return mf->addr; + return (mf->addr & ~(m_linesize - 1)); + } + +public: + L2c_access_locality(size_t linesize) : m_linesize(linesize), m_totalL2accAcc(0) { } + void access(mem_fetch_t *mf); + + void print_stat(FILE *fout, bool brief = true); + +}; + +void L2c_access_locality::access(mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + m_L2accCnt[cacheTag] += 1; + m_totalL2accAcc += 1; +} + +void L2c_access_locality::print_stat(FILE *fout, bool brief) +{ + float access_locality = (float) m_totalL2accAcc / m_L2accCnt.size(); + fprintf(fout, "Access Locality = %d / %zu (%f) \n", m_totalL2accAcc, m_L2accCnt.size(), access_locality); + if (brief == true) return; + fprintf(fout, " Detail:"); + pow2_histogram locality_histo(" Hits"); + for (L2accCnt::iterator iL2rc = m_L2accCnt.begin(); iL2rc != m_L2accCnt.end(); ++iL2rc) { + locality_histo.add2bin(iL2rc->second); + // fprintf(fout, "%#08x:%d\n", iL2rc->first, iL2rc->second); + } + locality_histo.fprint(fout); + fprintf(fout, "\n"); +} + +L2cacheblk::L2cacheblk(size_t linesize) +: m_mshr(new L2c_mshr(linesize)), + m_missTracker(new L2c_miss_tracker(linesize)), + m_accessLocality(new L2c_access_locality(linesize)) +{ } + +L2cacheblk::~L2cacheblk() +{ + delete m_mshr; + delete m_missTracker; + delete m_accessLocality; +} + + +//////////////////////////////////////////////// +// L2 access functions + +// L2 Cache Creation +void L2c_create ( dram_t* dram_p, const char* cache_opt ) +{ + unsigned int shd_n_set; + unsigned int shd_linesize; + unsigned int shd_n_assoc; + unsigned char shd_policy; + + unsigned int L2c_cb_L2_length; + unsigned int L2c_cb_L2w_length; + unsigned int L2c_L2_dm_length; + unsigned int L2c_dm_L2_length; + unsigned int L2c_dm_L2w_length; + unsigned int L2c_L2_cb_length; + unsigned int L2c_L2_cb_minlength; + unsigned int L2c_L2_dm_minlength; + + sscanf(cache_opt,"%d:%d:%d:%c", + &shd_n_set, &shd_linesize, &shd_n_assoc, &shd_policy); + + L2cacheblk *p_L2c = new L2cacheblk(shd_linesize); + + char L2c_name[32]; + snprintf(L2c_name, 32, "L2c_%03d", dram_p->id); + p_L2c->L2cache = shd_cache_create(L2c_name, + shd_n_set, shd_n_assoc, shd_linesize, + shd_policy, 16, ~addrdec_mask[CHIP], + write_through); //write_through maintains old behavior for now + + sscanf(gpgpu_L2_queue_config,"%d:%d:%d:%d:%d:%d:%d:%d", + &L2c_cb_L2_length, &L2c_cb_L2w_length, &L2c_L2_dm_length, + &L2c_dm_L2_length, &L2c_dm_L2w_length, &L2c_L2_cb_length, + &L2c_L2_cb_minlength, &L2c_L2_dm_minlength ); + //(<name>,<latency>,<min_length>,<max_length>) + p_L2c->cbtoL2queue = dq_create("cbtoL2queue", 0,0,L2c_cb_L2_length); + p_L2c->cbtoL2writequeue = dq_create("cbtoL2writequeue", 0,0,L2c_cb_L2w_length); + p_L2c->L2todramqueue = dq_create("L2todramqueue", 0,L2c_L2_dm_minlength,L2c_L2_dm_length); + p_L2c->dramtoL2queue = dq_create("dramtoL2queue", 0,0,L2c_dm_L2_length); + p_L2c->dramtoL2writequeue = dq_create("dramtoL2writequeue",0,0,L2c_dm_L2w_length); + p_L2c->L2tocbqueue = dq_create("L2tocbqueue", 0,L2c_L2_cb_minlength,L2c_L2_cb_length); + + p_L2c->L2todram_wbqueue = dq_create("L2todram_wbqueue", 0,L2c_L2_dm_minlength, + L2c_L2_dm_minlength + gpgpu_dram_sched_queue_size + L2c_dm_L2_length); + + p_L2c->L2request = NULL; + + assert(dram_p->m_L2cache == NULL); + dram_p->m_L2cache = reinterpret_cast<void*>(p_L2c); +} + +unsigned L2c_get_linesize( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + return p_L2c->L2cache->line_sz; +} + +int L2c_full( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + return(dq_full(p_L2c->cbtoL2queue) || dq_full(p_L2c->cbtoL2writequeue)); +} + +void L2c_push( dram_t *dram_p, mem_fetch_t *mf ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + if (gpgpu_l2_readoverwrite && mf->write) + dq_push(p_L2c->cbtoL2writequeue, mf); + else + dq_push(p_L2c->cbtoL2queue, mf); + p_L2c->m_accessLocality->access(mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_CBTOL2QUEUE); +} + +mem_fetch_t* L2c_pop( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t *mf; + mf = (mem_fetch_t*)dq_pop(p_L2c->L2tocbqueue); + + return mf; +} + +mem_fetch_t* L2c_top( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + return (mem_fetch_t*)dq_top(p_L2c->L2tocbqueue); +} + +void L2c_qlen ( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + printf("\n"); + printf("cb->L2{%d}\tcb->L2w{%d}\tL2->cb{%d}\n", + p_L2c->cbtoL2queue->length, + p_L2c->cbtoL2writequeue->length, + p_L2c->L2tocbqueue->length); + printf("dm->L2{%d}\tdm->L2w{%d}\tL2->dm{%d}\tL2->wb_dm{%d}\n", + p_L2c->dramtoL2queue->length, + p_L2c->dramtoL2writequeue->length, + p_L2c->L2todramqueue->length, + p_L2c->L2todram_wbqueue->length); +} + +// service memory request in icnt-to-L2 queue, writing to L2 as necessary +// (if L2 writeback miss, writeback to memory) +void L2c_service_mem_req ( dram_t* dram_p, int dm_id ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t* mf; + + if (!p_L2c->L2request) { + //if not servicing L2 cache request.. + p_L2c->L2request = (mem_fetch_t*) dq_pop(p_L2c->cbtoL2queue); //..then get one + if (!p_L2c->L2request) { + p_L2c->L2request = (mem_fetch_t*) dq_pop(p_L2c->cbtoL2writequeue); + } + } + + mf = p_L2c->L2request; + + if (!mf) return; + + switch (mf->type) { + case RD_REQ: + case WT_REQ: { + shd_cache_line_t *hit_cacheline = shd_cache_access(p_L2c->L2cache, + mf->addr, + 4, mf->write, + gpu_sim_cycle); + + if (hit_cacheline || l2_ideal) { //L2 Cache Hit; reads are sent as a single command and need to be stored + if (!mf->write) { //L2 Cache Read + if ( dq_full(p_L2c->L2tocbqueue) ) { + p_L2c->L2cache->access--; + } else { + mf->type = REPLY_DATA; + dq_push(p_L2c->L2tocbqueue, mf); + // at this point, should first check if earlier L2 miss is ready to be serviced + // if so, service earlier L2 miss first + p_L2c->L2request = NULL; //finished servicing + L2_read_hit++; + memlatstat_icnt2sh_push(mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_L2TOCBQUEUE_HIT); + } + } else { //L2 Cache Write aka servicing L1 Writeback + p_L2c->L2request = NULL; + L2_write_hit++; + freed_L1write_mfs++; + free(mf); //writeback from L1 successful + gpgpu_n_processed_writes++; + } + } else { + // L2 Cache Miss; issue commands accordingly + if ( dq_full(p_L2c->L2todramqueue) ) { + p_L2c->L2cache->miss--; + p_L2c->L2cache->access--; + } else { + // if a miss hit the mshr, that means there is another inflight request for the same data + // this miss just need to access the cache later when this request is serviced + bool mshr_hit = p_L2c->m_mshr->new_miss(mf); + if (not mshr_hit) { + if (!mf->write) { + dq_push(p_L2c->L2todramqueue, mf); + } else { + // if request is writeback from L1 and misses, + // then redirect mf writes to dram (no write allocate) + mf->nbytes_L2 = mf->nbytes_L1 - READ_PACKET_SIZE; + dq_push(p_L2c->L2todramqueue, mf); + } + } + if (mf->mshr) mshr_update_status(mf->mshr, IN_L2TODRAMQUEUE); + p_L2c->L2request = NULL; + } + } + } + break; + default: assert(0); + } +} + +// service memory request in L2todramqueue, pushing to dram +void L2c_push_miss_to_dram ( dram_t* dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t* mf; + + if ( gpgpu_dram_sched_queue_size && dram_full(dram_p) ) return; + + mf = (mem_fetch_t*) dq_pop(p_L2c->L2todram_wbqueue); //prioritize writeback + if (!mf) mf = (mem_fetch_t*) dq_pop(p_L2c->L2todramqueue); + if (mf) { + if (mf->write) { + L2_write_miss++; + } else { + L2_read_miss++; + } + p_L2c->m_missTracker->new_miss(mf); + memlatstat_dram_access(mf, dram_p->id, mf->tlx.bk); + dram_push(dram_p, + mf->tlx.bk, mf->tlx.row, mf->tlx.col, + mf->nbytes_L2, mf->write, + mf->wid, mf->sid, mf->cache_hits_waiting, mf->addr, mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_DRAM_REQ_QUEUE); + } +} + +//Service writes that are finished in Dram +//only updates the stats and frees the mf +void dramtoL2_service_write(mem_fetch_t * mf) { + freed_L2write_mfs++; + free(mf); + gpgpu_n_processed_writes++; +} + +// pop completed memory request from dram and push it to dram-to-L2 queue +void L2c_get_dram_output ( dram_t* dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t* mf; + mem_fetch_t* mf_top; + if ( dq_full(p_L2c->dramtoL2queue) || dq_full(p_L2c->dramtoL2writequeue) ) return; + mf_top = (mem_fetch_t*) dram_top(dram_p); //test + mf = (mem_fetch_t*) dram_pop(dram_p); + assert (mf_top==mf ); + if (mf) { + if (gpgpu_l2_readoverwrite && mf->write) + dq_push(p_L2c->dramtoL2writequeue, mf); + else + dq_push(p_L2c->dramtoL2queue, mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_DRAMTOL2QUEUE); + } +} + +// service memory request in dramtoL2queue, writing to L2 as necessary +// (may cause cache eviction and subsequent writeback) +void L2c_process_dram_output ( dram_t* dram_p, int dm_id ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + static mem_fetch_t **L2dramout = NULL; + static unsigned long long int *wb_addr = NULL; + if (!L2dramout) L2dramout = (mem_fetch_t**)calloc(gpu_n_mem, sizeof(mem_fetch_t*)); + if (!wb_addr) { + wb_addr = (unsigned long long int*)calloc(gpu_n_mem, sizeof(unsigned long long int)); + for (unsigned i = 0; i < gpu_n_mem; i++) wb_addr[i] = -1; + } + + if (L2dramout[dm_id] == NULL) { + // pop from mshr chain if it is not empty, otherwise, pop a new cacheline from dram output queue + if (p_L2c->m_mshr->mshr_chain_empty() == false) { + L2dramout[dm_id] = p_L2c->m_mshr->mshr_chain_top(); + p_L2c->m_mshr->mshr_chain_pop(); + } else { + L2dramout[dm_id] = (mem_fetch_t*) dq_pop(p_L2c->dramtoL2queue); + if (!L2dramout[dm_id]) L2dramout[dm_id] = (mem_fetch_t*) dq_pop(p_L2c->dramtoL2writequeue); + + if (L2dramout[dm_id] != NULL) { + p_L2c->m_mshr->miss_serviced(L2dramout[dm_id]); + + if (p_L2c->m_mshr->mshr_chain_empty() == false) { // possible if this is a L2 writeback + L2dramout[dm_id] = p_L2c->m_mshr->mshr_chain_top(); + p_L2c->m_mshr->mshr_chain_pop(); + } + } + } + } + + mem_fetch_t* mf = L2dramout[dm_id]; + if (mf) { + if (!mf->write) { //service L2 read miss + + // it is a pre-fill dramout mf + if (wb_addr[dm_id] == (unsigned long long int)-1) { + if ( dq_full(p_L2c->L2tocbqueue) ) goto RETURN; + + if (mf->mshr) mshr_update_status(mf->mshr, IN_L2TOCBQUEUE_MISS); + + //only transfer across icnt once the whole line has been received by L2 cache + mf->type = REPLY_DATA; + dq_push(p_L2c->L2tocbqueue, mf); + + assert(mf->sid <= (int)gpu_n_shader); + shd_cache_line_t *fetch_line_exist = shd_cache_probe(p_L2c->L2cache, mf->addr); + if (fetch_line_exist == NULL) { + wb_addr[dm_id] = L2_shd_cache_fill(p_L2c->L2cache, mf->addr, gpu_sim_cycle ); + } + } + // only perform a write on cache eviction (write-back policy) + // it is the 1st or nth time trial to writeback + if (wb_addr[dm_id] != (unsigned long long int)-1) { + // performing L2 writeback (no false sharing for memory-side cache) + int wb_succeed = L2c_write_back(wb_addr[dm_id], p_L2c->L2cache->line_sz, dm_id ); + if (!wb_succeed) goto RETURN; //try again next cycle + } + + p_L2c->m_missTracker->miss_serviced(mf); + L2dramout[dm_id] = NULL; + wb_addr[dm_id] = -1; + } else { //service L2 write miss + p_L2c->m_missTracker->miss_serviced(mf); + dramtoL2_service_write(mf); + L2dramout[dm_id] = NULL; + wb_addr[dm_id] = -1; + } + } + RETURN: + assert (L2dramout[dm_id] || wb_addr[dm_id] == (unsigned long long int)-1); +} + +// Writeback from L2 to DRAM: +// - Takes in memory address and their parameters and pushes to dram request queue +// - This is used only for L2 writeback +unsigned char L2c_write_back(unsigned long long int addr, int bsize, int dram_id ) +{ + addrdec_t tlx; + addrdec_tlx(addr,&tlx); + + assert(dram[dram_id]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[dram_id]->m_L2cache); + + if ( dq_full(p_L2c->L2todram_wbqueue) ) return 0; + + mem_fetch_t *mf; + + mf = (mem_fetch_t*) malloc(sizeof(mem_fetch_t)); + made_write_mfs++; + mf->request_uid = g_next_request_uid++; + mf->addr = addr; + mf->nbytes_L1 = bsize + READ_PACKET_SIZE; + mf->txbytes_L1 = 0; + mf->rxbytes_L1 = 0; + mf->nbytes_L2 = bsize; + mf->sid = gpu_n_shader; // (gpu_n_shader+1); + mf->wid = 0; + mf->txbytes_L2 = 0; + mf->rxbytes_L2 = 0; + mf->mshr = NULL; + mf->pc = -1; // disable ptx_file_line_stats + mf->write = 1; // it is writeback + mf->mem_acc = L2_WRBK_ACC; + memlatstat_start(mf); + mf->tlx = tlx; + mf->bank = mf->tlx.bk; + mf->chip = mf->tlx.chip; + + + //writeback + mf->type = L2_WTBK_DATA; + if (!dq_push(p_L2c->L2todram_wbqueue, mf)) assert(0); + gpgpu_n_sent_writes++; + return 1; +} + +unsigned int L2c_cache_flush ( dram_t* dram_p) { + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + shd_cache_t *cp = p_L2c->L2cache; + int dirty_lines_flushed = 0 ; + for (unsigned i = 0; i < cp->nset * cp->assoc ; i++) { + if ( (cp->lines[i].status & (DIRTY|VALID)) == (DIRTY|VALID) ) { + dirty_lines_flushed++; + } + cp->lines[i].status &= ~VALID; + cp->lines[i].status &= ~DIRTY; + } + return dirty_lines_flushed; +} + +void L2c_init_stat() +{ + L2_cbtoL2length = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_cbtoL2writelength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_L2tocblength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_dramtoL2length = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_dramtoL2writelength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_L2todramlength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); +} + +void L2c_update_stat( dram_t* dram_p) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + int i = dram_p->id; + + if (p_L2c->cbtoL2queue->length > L2_cbtoL2length[i]) + L2_cbtoL2length[i] = p_L2c->cbtoL2queue->length; + if (p_L2c->cbtoL2writequeue->length > L2_cbtoL2writelength[i]) + L2_cbtoL2writelength[i] = p_L2c->cbtoL2writequeue->length; + if (p_L2c->L2tocbqueue->length > L2_L2tocblength[i]) + L2_L2tocblength[i] = p_L2c->L2tocbqueue->length; + if (p_L2c->dramtoL2queue->length > L2_dramtoL2length[i]) + L2_dramtoL2length[i] = p_L2c->dramtoL2queue->length; + if (p_L2c->dramtoL2writequeue->length > L2_dramtoL2writelength[i]) + L2_dramtoL2writelength[i] = p_L2c->dramtoL2writequeue->length; + if (p_L2c->L2todramqueue->length > L2_L2todramlength[i]) + L2_L2todramlength[i] = p_L2c->L2todramqueue->length; +} + +void L2c_print_stat( ) +{ + unsigned i; + + printf(" "); + for (i=0;i<gpu_n_mem;i++) { + printf(" dram[%d]", i); + } + printf("\n"); + + printf("cbtoL2 queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_cbtoL2length[i]); + } + printf("\n"); + + printf("cbtoL2 write queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_cbtoL2writelength[i]); + } + printf("\n"); + + printf("L2tocb queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_L2tocblength[i]); + } + printf("\n"); + + printf("dramtoL2 queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_dramtoL2length[i]); + } + printf("\n"); + + printf("dramtoL2 write queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_dramtoL2writelength[i]); + } + printf("\n"); + + printf("L2todram queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_L2todramlength[i]); + } + printf("\n"); +} + +void L2c_print_cache_stat() +{ + unsigned i; + int j, k; + for (i=0,j=0,k=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + shd_cache_print(p_L2c->L2cache,stdout); + j += p_L2c->L2cache->miss; + k += p_L2c->L2cache->access; + p_L2c->m_mshr->print_stat(stdout); + p_L2c->m_missTracker->print_stat(stdout); + p_L2c->m_accessLocality->print_stat(stdout, false); + } + printf("L2 Cache Total Miss Rate = %0.3f\n", (float)j/k); +} + +void L2c_print_debug( ) +{ + unsigned i; + + printf(" "); + for (i=0;i<gpu_n_mem;i++) { + printf(" dram[%d]", i); + } + printf("\n"); + + printf("cbtoL2 queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->cbtoL2queue->length); + } + printf("\n"); + + printf("cbtoL2 write queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->cbtoL2writequeue->length); + } + printf("\n"); + + printf("L2tocb queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->L2tocbqueue->length); + } + printf("\n"); + + printf("dramtoL2 queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->dramtoL2queue->length); + } + printf("\n"); + + printf("dramtoL2 write queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->dramtoL2writequeue->length); + } + printf("\n"); + + printf("L2todram queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->L2todramqueue->length); + } + printf("\n"); + + printf("L2todram writeback queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->L2todram_wbqueue->length); + } + printf("\n"); +} + +#define CREATELOG 111 +#define SAMPLELOG 222 +#define DUMPLOG 333 + +void L2c_log(int task) +{ + unsigned i; + static void ** cbtol2_Dist ; + static void ** cbtoL2wr_Dist ; + static void ** L2tocb_Dist ; + static void ** dramtoL2_Dist ; + static void ** dramtoL2wr_Dist ; + static void ** L2todram_Dist ; + static void ** L2todram_wb_Dist ; + if (task == CREATELOG) { + cbtol2_Dist = (void **) calloc(gpu_n_mem,sizeof(void*)); + cbtoL2wr_Dist = (void **) calloc(gpu_n_mem,sizeof(void*)); + L2tocb_Dist = (void **) calloc(gpu_n_mem,sizeof(void*)); + dramtoL2_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + dramtoL2wr_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + L2todram_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + L2todram_wb_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + + for (i=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + cbtol2_Dist[i] = StatCreate("cbtoL2",1,p_L2c->cbtoL2queue->max_len); + cbtoL2wr_Dist[i] = StatCreate("cbtoL2write",1,p_L2c->cbtoL2writequeue->max_len); + L2tocb_Dist[i] = StatCreate("L2tocb",1,p_L2c->L2tocbqueue->max_len); + dramtoL2_Dist[i] = StatCreate("dramtoL2",1,p_L2c->dramtoL2queue->max_len); + dramtoL2wr_Dist[i] = StatCreate("dramtoL2write",1,p_L2c->dramtoL2writequeue->max_len); + L2todram_Dist[i] = StatCreate("L2todram",1,p_L2c->L2todramqueue->max_len); + L2todram_wb_Dist[i] = StatCreate("L2todram_wb",1,p_L2c->L2todram_wbqueue->max_len); + } + } else if (task == SAMPLELOG) { + for (i=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + StatAddSample(cbtol2_Dist[i], p_L2c->cbtoL2queue->length); + StatAddSample(cbtoL2wr_Dist[i], p_L2c->cbtoL2writequeue->length); + StatAddSample(L2tocb_Dist[i], p_L2c->L2tocbqueue->length); + StatAddSample(dramtoL2_Dist[i], p_L2c->dramtoL2queue->length); + StatAddSample(dramtoL2wr_Dist[i], p_L2c->dramtoL2writequeue->length); + StatAddSample(L2todram_Dist[i], p_L2c->L2todramqueue->length); + StatAddSample(L2todram_wb_Dist[i], p_L2c->L2todram_wbqueue->length); + } + } else if (task == DUMPLOG) { + for (i=0;i<gpu_n_mem;i++) { + printf ("Queue Length DRAM[%d] ",i); StatDisp(cbtol2_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(cbtoL2wr_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(L2tocb_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(dramtoL2_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(dramtoL2wr_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(L2todram_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(L2todram_wb_Dist[i]); + } + } +} + +void L2c_latency_log_dump() +{ + unsigned i; + for (i=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->cbtoL2queue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->cbtoL2writequeue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->L2tocbqueue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->dramtoL2queue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->dramtoL2writequeue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->L2todramqueue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->L2todram_wbqueue->lat_stat); + } +} + + |
