From 87e4da5fc6086c3d0a661af1929255a8cbd728d7 Mon Sep 17 00:00:00 2001 From: Tor Aamodt Date: Mon, 18 Oct 2010 02:43:17 -0800 Subject: Re-designed cache model: - read only cache model with integrated mshrs (no L1D, yet); new cache interface should be easily extendable to support texture cache with latency fifo and separate tag/data arrays, though this is not yet added (currently tags and data arrays are not decoupled for texture) - new partition model using the above removes all old MSHRs, L1D etc... passing CUDA 3.1 regression [git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 7875] --- src/gpgpu-sim/gpu-cache.h | 443 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 366 insertions(+), 77 deletions(-) (limited to 'src/gpgpu-sim/gpu-cache.h') diff --git a/src/gpgpu-sim/gpu-cache.h b/src/gpgpu-sim/gpu-cache.h index 89a2ed0..440b98b 100644 --- a/src/gpgpu-sim/gpu-cache.h +++ b/src/gpgpu-sim/gpu-cache.h @@ -1,5 +1,5 @@ /* - * gpu-cache.c + * gpu-cache.h * * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, * George L. Yuan and the @@ -64,57 +64,84 @@ * Vancouver, BC V6T 1Z4 */ +#ifndef GPU_CACHE_H +#define GPU_CACHE_H + #include #include +#include "gpu-misc.h" +#include "mem_fetch.h" #include "../abstract_hardware_model.h" +#include "../tr1_hash_map.h" -#ifndef GPU_CACHE_H -#define GPU_CACHE_H +class mem_fetch; // mem_fetch opaque to cache and mshrs -#define VALID 0x01 // block is valid (and present in cache) -#define DIRTY 0x02 // block is dirty -#define RESERVED 0x04 // there is an outstanding request for this block, but it has not returned yet +enum cache_block_state { + INVALID, + RESERVED, + VALID +}; enum cache_request_status { HIT, - HIT_W_WT, // Hit, but write through cache, still needs to send to memory - MISS_NO_WB, // miss, but witeback not necessary - MISS_W_WB, // miss, must do writeback - WB_HIT_ON_MISS, // request hit on a reservation in wb cache - RESERVATION_FAIL, - NUM_CACHE_REQUEST_STATUS + HIT_RESERVED, + MISS, + RESERVATION_FAIL }; struct cache_block_t { cache_block_t() { - tag=0; - addr=0; - fetch_time=0; - last_used=0; - status=0; + m_tag=0; + m_block_addr=0; + m_alloc_time=0; + m_fill_time=0; + m_last_access_time=0; + m_status=INVALID; + } + void allocate( new_addr_type tag, new_addr_type block_addr, unsigned time ) + { + m_tag=tag; + m_block_addr=block_addr; + m_alloc_time=time; + m_last_access_time=time; + m_fill_time=0; + m_status=RESERVED; + } + void fill( unsigned time ) + { + assert( m_status == RESERVED ); + m_status=VALID; + m_fill_time=time; } - new_addr_type tag; - new_addr_type addr; - unsigned fetch_time; - unsigned last_used; - unsigned char status; /* valid, dirty... etc */ + + new_addr_type m_tag; + new_addr_type m_block_addr; + unsigned m_alloc_time; + unsigned m_last_access_time; + unsigned m_fill_time; + cache_block_state m_status; }; -enum replacement_policy { +enum replacement_policy_t { LRU, FIFO }; -enum write_policy { - no_writes, // line replacement when new line arrives - write_back, // line replacement when new line arrives - write_through // reservation based, use much handle reservation full error. +enum write_policy_t { + READ_ONLY, + WRITE_BACK, + WRITE_THROUGH }; -enum allocation_policy { - on_miss, - on_fill +enum allocation_policy_t { + ON_MISS, + ON_FILL +}; + +enum mshr_config_t { + TEX_FIFO, + ASSOC // normal cache }; class cache_config { @@ -127,94 +154,356 @@ public: void init() { assert( m_config_string ); - int ntok = sscanf(m_config_string,"%d:%d:%d:%c:%c:%c", &nset, &line_sz, &assoc, &replacement_policy, &write_policy, &alloc_policy); - if( ntok != 6 ) { - printf("GPGPU-Sim uArch: cache configuration parsing error (%s)\n", m_config_string ); - abort(); + char rp, wp, ap, mshr_type; + int ntok = sscanf(m_config_string,"%u:%u:%u:%c:%c:%c,%c:%u:%u,%u", + &m_nset, &m_line_sz, &m_assoc, &rp, &wp, &ap, + &mshr_type,&m_mshr_entries,&m_mshr_max_merge,&m_miss_queue_size); + if( ntok < 10 ) + exit_parse_error(); + switch(rp) { + case 'L': m_replacement_policy = LRU; break; + case 'F': m_replacement_policy = FIFO; break; + default: exit_parse_error(); + } + switch(wp) { + case 'R': m_write_policy = READ_ONLY; break; + case 'B': m_write_policy = WRITE_BACK; break; + case 'T': m_write_policy = WRITE_THROUGH; break; + default: exit_parse_error(); } + switch(ap) { + case 'm': m_alloc_policy = ON_MISS; break; + case 'f': m_alloc_policy = ON_FILL; break; + default: exit_parse_error(); + } + switch(mshr_type) { + case 'F': m_mshr_type = TEX_FIFO; break; + case 'A': m_mshr_type = ASSOC; break; + default: exit_parse_error(); + } + m_line_sz_log2 = LOGB2(m_line_sz); + m_nset_log2 = LOGB2(m_nset); m_valid = true; } unsigned get_line_sz() const { assert( m_valid ); - return line_sz; + return m_line_sz; } unsigned get_num_lines() const { assert( m_valid ); - return nset * assoc; + return m_nset * m_assoc; } - enum write_policy get_write_policy() const + void print( FILE *fp ) const { - if( write_policy == 'R' ) - return no_writes; - else if( write_policy == 'B' ) - return write_back; - else if( write_policy == 'T' ) - return write_through; - else - abort(); + fprintf( fp, "Size = %d B (%d Set x %d-way x %d byte line)\n", + m_line_sz * m_nset * m_assoc, + m_nset, m_assoc, m_line_sz ); + } + + unsigned set_index( new_addr_type addr ) const + { + return (addr >> m_line_sz_log2) & (m_nset-1); + } + new_addr_type tag( new_addr_type addr ) const + { + return addr >> (m_line_sz_log2+m_nset_log2); + } + new_addr_type block_addr( new_addr_type addr ) const + { + return addr & ~(m_line_sz-1); } char *m_config_string; private: + void exit_parse_error() + { + printf("GPGPU-Sim uArch: cache configuration parsing error (%s)\n", m_config_string ); + abort(); + } + bool m_valid; - unsigned int nset; - unsigned int line_sz; - unsigned int assoc; - unsigned char replacement_policy; // 'L' = LRU, 'F' = FIFO, 'R' = RANDOM - unsigned char write_policy; // 'T' = write through, 'B' = write back, 'R' = read only - unsigned char alloc_policy; // 'm' = allocate on miss, 'f' = allocate on fill + unsigned m_line_sz; + unsigned m_line_sz_log2; + unsigned m_nset; + unsigned m_nset_log2; + unsigned m_assoc; + + enum replacement_policy_t m_replacement_policy; // 'L' = LRU, 'F' = FIFO + enum write_policy_t m_write_policy; // 'T' = write through, 'B' = write back, 'R' = read only + enum allocation_policy_t m_alloc_policy; // 'm' = allocate on miss, 'f' = allocate on fill + enum mshr_config_t m_mshr_type; + unsigned m_mshr_entries; + unsigned m_mshr_max_merge; + unsigned m_miss_queue_size; + + friend class tag_array; friend class cache_t; }; -class cache_t { +class tag_array { public: - cache_t( const char *name, const cache_config &config, int core_id, int type_id ); - ~cache_t(); + tag_array( const cache_config &config, int core_id, int type_id ); + ~tag_array(); - enum cache_request_status access( new_addr_type addr, - bool write, - unsigned int sim_cycle, - address_type *wb_address ); + enum cache_request_status probe( new_addr_type addr, unsigned &idx ) const; + enum cache_request_status access( new_addr_type addr, unsigned time, unsigned &idx ); - new_addr_type fill( new_addr_type addr, unsigned int sim_cycle ); + void fill( new_addr_type addr, unsigned time ); + void fill( unsigned idx, unsigned time ); - unsigned flush(); - - void print( FILE *stream, unsigned &total_access, unsigned &total_misses ); - float windowed_cache_miss_rate(int); - void new_window(); + void flush(); // flash invalidate all entries + void new_window(); + + void print( FILE *stream, unsigned &total_access, unsigned &total_misses ) const; + float windowed_miss_rate( bool minus_pending_hit ) const; + +protected: -private: - std::string m_name; const cache_config &m_config; cache_block_t *m_lines; /* nbanks x nset x assoc lines in total */ - unsigned m_n_banks; - unsigned m_nset; - unsigned m_nset_log2; - unsigned m_assoc; - unsigned m_line_sz; // bytes - unsigned m_line_sz_log2; - - enum write_policy m_write_policy; - unsigned char m_replacement_policy; unsigned m_access; unsigned m_miss; - unsigned m_merge_hit; // number of cache miss that hit the same line (and merged as a result) + unsigned m_pending_hit; // number of cache miss that hit a line that is allocated but not filled // performance counters for calculating the amount of misses within a time window unsigned m_prev_snapshot_access; unsigned m_prev_snapshot_miss; - unsigned m_prev_snapshot_merge_hit; + unsigned m_prev_snapshot_pending_hit; int m_core_id; // which shader core is using this int m_type_id; // what kind of cache is this (normal, texture, constant) }; +class mshr_table { +public: + mshr_table( unsigned num_entries, unsigned max_merged ) + : m_num_entries(num_entries), + m_max_merged(max_merged), +#ifndef USE_MAP + m_data(2*num_entries) +#endif + { + } + + // is there a pending request to the lower memory level already? + bool probe( new_addr_type block_addr ) const + { + table::const_iterator a = m_data.find(block_addr); + return a != m_data.end(); + } + + // is there space for tracking a new memory access? + bool full( new_addr_type block_addr ) const + { + table::const_iterator i=m_data.find(block_addr); + if( i != m_data.end() ) + return i->second.size() >= m_max_merged; + else + return m_data.size() >= m_num_entries; + } + + // add or merge this access + void add( new_addr_type block_addr, mem_fetch *mf ) + { + m_data[block_addr].push_back(mf); + assert( m_data.size() <= m_num_entries ); + assert( m_data[block_addr].size() <= m_max_merged ); + } + + // true if cannot accept new fill responses + bool busy() const + { + return false; + } + + // accept a new cache fill response: mark entry ready for processing + void mark_ready( new_addr_type block_addr ) + { + assert( !busy() ); + table::iterator a = m_data.find(block_addr); + assert( a != m_data.end() ); // don't remove same request twice + m_current_response.push_back( block_addr ); + assert( m_current_response.size() <= m_data.size() ); + } + + // true if ready accesses exist + bool access_ready() const + { + return !m_current_response.empty(); + } + + // next ready access + mem_fetch *next_access() + { + assert( access_ready() ); + new_addr_type block_addr = m_current_response.front(); + assert( !m_data[block_addr].empty() ); + mem_fetch *result = m_data[block_addr].front(); + m_data[block_addr].pop_front(); + if( m_data[block_addr].empty() ) { + // release entry + m_data.erase(block_addr); + m_current_response.pop_front(); + } + return result; + } + + void display( FILE *fp ) const + { + fprintf(fp,"MSHR contents\n"); + for( table::const_iterator e=m_data.begin(); e!=m_data.end(); ++e ) { + unsigned block_addr = e->first; + fprintf(fp,"MSHR: tag=0x%06x, %zu entries : ", block_addr, e->second.size()); + if( !e->second.empty() ) { + mem_fetch *mf = e->second.front(); + fprintf(fp,"%p :",mf); + mf->print(fp); + } else { + fprintf(fp," no memory requests???\n"); + } + } + } + +private: + + // finite sized, fully associative table, with a finite maximum number of merged requests + const unsigned m_num_entries; + const unsigned m_max_merged; + + typedef std::list entry; + typedef my_hash_map table; + table m_data; + + // it may take several cycles to process the merged requests + bool m_current_response_ready; + std::list m_current_response; +}; + +class cache_t { +public: + cache_t( const char *name, const cache_config &config, int core_id, int type_id, mem_fetch_interface *memport ) + : m_config(config), m_tag_array(config,core_id,type_id), m_mshrs(config.m_mshr_entries,config.m_mshr_max_merge) + { + m_name = name; + assert(config.m_mshr_type == ASSOC); + assert(config.m_write_policy == READ_ONLY); + m_memport=memport; + } + + // access cache: returns RESERVATION_FAIL if request could not be accepted (for any reason) + enum cache_request_status access( new_addr_type addr, mem_fetch *mf, unsigned time ) + { + new_addr_type block_addr = m_config.block_addr(addr); + unsigned cache_index = (unsigned)-1; + enum cache_request_status status = m_tag_array.probe(block_addr,cache_index); + if( status == HIT ) + return HIT; + if( status != RESERVATION_FAIL ) { + bool mshr_hit = m_mshrs.probe(block_addr); + bool mshr_avail = !m_mshrs.full(block_addr); + if( mshr_hit && mshr_avail ) { + m_tag_array.access(addr,time,cache_index); + m_mshrs.add(block_addr,mf); + return MISS; + } else if( !mshr_hit && mshr_avail && (m_miss_queue.size() < m_config.m_miss_queue_size) ) { + m_tag_array.access(addr,time,cache_index); + m_mshrs.add(block_addr,mf); + m_extra_mf_fields[mf] = extra_mf_fields(block_addr,cache_index); + m_miss_queue.push_back(mf); + return MISS; + } + } + return RESERVATION_FAIL; + } + + void cycle() + { + // send next request to lower level of memory + if( !m_miss_queue.empty() ) { + mem_fetch *mf = m_miss_queue.front(); + if( !m_memport->full(mf->get_data_size(),mf->get_is_write()) ) { + m_miss_queue.pop_front(); + m_memport->push(mf); + } + } + } + + // interface for response from lower memory level (model bandwidth restictions in caller) + void fill( mem_fetch *mf, unsigned time ) + { + extra_mf_fields_lookup::iterator e = m_extra_mf_fields.find(mf); + assert( e != m_extra_mf_fields.end() ); + assert( e->second.m_valid ); + if ( m_config.m_alloc_policy == ON_MISS ) + m_tag_array.fill(e->second.m_cache_index,time); + else if ( m_config.m_alloc_policy == ON_FILL ) + m_tag_array.fill(e->second.m_block_addr,time); + else abort(); + m_mshrs.mark_ready(e->second.m_block_addr); + m_extra_mf_fields.erase(mf); + } + + // are any (accepted) accesses that had to wait for memory now ready? (does not include accesses that "HIT") + bool access_ready() const + { + return m_mshrs.access_ready(); + } + + // pop next ready access (does not include accesses that "HIT") + mem_fetch *next_access() + { + return m_mshrs.next_access(); + } + + // flash invalidate all entries in cache + void flush() + { + m_tag_array.flush(); + } + + void print(FILE *fp, unsigned &accesses, unsigned &misses) const + { + fprintf( fp, "Cache %s:\t", m_name.c_str() ); + m_tag_array.print(fp,accesses,misses); + } + + void display_state( FILE *fp ) const + { + fprintf(fp,"Cache %s:\n", m_name.c_str() ); + m_mshrs.display(fp); + fprintf(fp,"\n"); + } + +private: + std::string m_name; + const cache_config &m_config; + tag_array m_tag_array; + mshr_table m_mshrs; + std::list m_miss_queue; + mem_fetch_interface *m_memport; + + struct extra_mf_fields { + extra_mf_fields() { m_valid = false; } + extra_mf_fields( new_addr_type a, unsigned i ) + { + m_block_addr = a; + m_cache_index = i; + m_valid = true; + } + bool m_valid; + new_addr_type m_block_addr; + unsigned m_cache_index; + }; + typedef std::map extra_mf_fields_lookup; + extra_mf_fields_lookup m_extra_mf_fields; +}; + + #endif -- cgit v1.3