diff options
| author | Tor Aamodt <[email protected]> | 2010-10-16 17:30:52 -0800 |
|---|---|---|
| committer | Tor Aamodt <[email protected]> | 2010-10-16 17:30:52 -0800 |
| commit | b577cbcdf229a2c02d1bf8584c6e82be7a14cb33 (patch) | |
| tree | 373ea8ec8ea8d7d9a7a1df0eaa17f15652df1306 /src/abstract_hardware_model.cc | |
| parent | 2072e7ff2037c19a0c346e60469949c9437569bf (diff) | |
1. creating cache_config object to encapsulate cache configuration information
(and parse it before creating the simulator objects).
2. creating core_config to hold only features of a shader_core that are high
level enough either (a) the functional simulator needs to know about them,
or (b) they affect memory *access* generation.
3. in config files only (so far) separate out notion of write-{through,back},
from notion of when a line is allocated... will use this to distinguish
different types of caches.
passing CUDA 3.1 regression
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 7870]
Diffstat (limited to 'src/abstract_hardware_model.cc')
| -rw-r--r-- | src/abstract_hardware_model.cc | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc index 7b67df9..2134cfc 100644 --- a/src/abstract_hardware_model.cc +++ b/src/abstract_hardware_model.cc @@ -24,3 +24,181 @@ gpgpu_t::gpgpu_t() m_dev_malloc=GLOBAL_HEAP_START; } +unsigned core_config::shmem_bank_func(address_type addr, unsigned) const +{ + return ((addr/WORD_SIZE) % gpgpu_n_shmem_bank); +} + +unsigned core_config::dcache_bank_func(address_type add, unsigned line_size) const +{ + if (gpgpu_no_dl1) return 1; //no banks + else return (add / line_size) & (gpgpu_n_cache_bank - 1); +} + +address_type null_tag_func(address_type address, unsigned line_size) +{ + return address; //no modification: each address is its own tag. +} + +address_type line_size_based_tag_func(address_type address, unsigned line_size) +{ + //gives the tag for an address based on a given line size + return ((address) & (~((address_type)line_size - 1))); +} + +void warp_inst_t::get_memory_access_list() +{ + // Calculates memory accesses generated by this warp + // Returns acesses which are "coalesced" + // Does not coalesce nor overlap bank accesses across warp "parts". + + // This is called once per warp_inst_t when the warp_inst_t enters the memory stage. + // It produces the set of distinct memory accesses that need to be peformed. + // These accessess are then performed over multiple cycles (stalling the pipeline) + // if the accessses cannot be performed all at once. + + // In hardware, these accesses would be created at the specific unit handling the type + // of memory access. We centralize the logic simply to reduce code duplication. + + // Below, accesses are assigned an "order" based on when that access may be issued. + // Accesses with the same order number may occur at the same time: they are to different banks. + // Later, when the queue is processed it will evaluate accesses of as many orders as + // ports on that cache/shmem. + // + // Accesses are placed in accessq sorted so that accesses of the same order are adjacent. + + typedef unsigned (core_config::*bank_func_t)(address_type add, unsigned line_size) const; + typedef address_type (*tag_func_t)(address_type add, unsigned line_size); + bank_func_t bank_func = NULL; + tag_func_t tag_func = NULL; + unsigned warp_parts = 0; + unsigned line_size = 0; + bool limit_broadcast = 0; + bool global_mem_access = false; + + switch( space.get_type() ) { + case shared_space: + bank_func = &core_config::shmem_bank_func; + tag_func = null_tag_func; + warp_parts = m_config->gpgpu_shmem_pipe_speedup; + line_size = 1; //shared memory doesn't care about line_size, needs to be at least 1; + limit_broadcast = true; // limit broadcasts to single cycle. + break; + case tex_space: + bank_func = &core_config::null_bank_func; + tag_func = line_size_based_tag_func; + warp_parts = 1; + line_size = m_config->gpgpu_cache_texl1_linesize; + limit_broadcast = false; + break; + case const_space: case param_space_kernel: + bank_func = &core_config::null_bank_func; + tag_func = line_size_based_tag_func; + warp_parts = 1; + line_size = m_config->gpgpu_cache_constl1_linesize; + limit_broadcast = false; + break; + case global_space: case local_space: case param_space_local: + global_mem_access=true; + warp_parts = 1; + line_size = m_config->gpgpu_cache_dl1_linesize; + if( m_config->gpgpu_coalesce_arch == 13 ){ + warp_parts = 2; + if( m_config->gpgpu_no_dl1 ) { + // line size is dependant on instruction; + switch (data_size) { + case 1: line_size = 32; break; + case 2: line_size = 64; break; + case 4: case 8: case 16: line_size = 128; break; + default: assert(0); + } + } + } + bank_func = &core_config::dcache_bank_func; + tag_func = line_size_based_tag_func; + limit_broadcast = false; + break; + default: + abort(); + } + + // bank_accs tracks bank accesses for sorting into generations; + // each entry is (bank #, number of accesses) + // the idea is that you can only access a bank a number of times each cycle equal to + // its number of ports in one cycle. + std::map<unsigned,unsigned> bank_accs; + + // keep track of broadcasts with unique orders if limit_broadcast + // the normally calculated orders will never be greater than warp_size + unsigned broadcast_order = warp_size(); + unsigned qbegin = get_accessq_size(); + unsigned qpartbegin = qbegin; + unsigned mem_pipe_size = warp_size() / warp_parts; + for (unsigned part = 0; part < warp_size(); part += mem_pipe_size) { + for (unsigned i = part; i < part + mem_pipe_size; i++) { + if ( !active(i) ) + continue; + new_addr_type addr = get_addr(i); + address_type lane_segment_address = tag_func(addr, line_size); + unsigned quarter = 0; + if( line_size>=4 ) + quarter = (addr / (line_size/4)) & 3; + bool match = false; + if( !isatomic() ) { //atomics must have own request + for( unsigned j = qpartbegin; j <get_accessq_size(); j++ ) { + if (lane_segment_address == accessq(j).addr) { + accessq(j).quarter_count[quarter]++; + accessq(j).warp_indices.push_back(i); + if (limit_broadcast) // two threads access this address, so its a broadcast. + accessq(j).order = ++broadcast_order; //do broadcast in its own cycle. + match = true; + break; + } + } + } + if (!match) { // does not match a previous request by another thread, so need a new request + assert( space != undefined_space ); + m_accessq.push_back( mem_access_t( lane_segment_address, line_size, quarter, i) ); + // Determine Bank Conflicts: + unsigned bank = (m_config->*bank_func)(get_addr(i), line_size); + // ensure no concurrent bank access accross warp parts. + // ie. order will be less than part for all previous loads in previous parts, so: + if (bank_accs[bank] < part) + bank_accs[bank]=part; + accessq_back().order = bank_accs[bank]; + bank_accs[bank]++; + } + } + qpartbegin = get_accessq_size(); //don't coalesce accross warp parts + } + //sort requests by order they will be processed in + std::stable_sort( m_accessq.begin()+qbegin,m_accessq.end()); + + if( global_mem_access ) { + // Now that we have the accesses, if we don't have a cache we can adjust request sizes to + // include only the data referenced by the threads + for (unsigned i = 0; i < get_accessq_size(); i++) { + if (m_config->gpgpu_coalesce_arch == 13 && m_config->gpgpu_no_dl1) { + // do coalescing here. + char* quarter_counts = accessq(i).quarter_count; + bool low = quarter_counts[0] or quarter_counts[1]; + bool high = quarter_counts[2] or quarter_counts[3]; + if (accessq(i).req_size == 128) { + if (low xor high) { //can reduce size + accessq(i).req_size = 64; + if (high) accessq(i).addr += 64; + low = quarter_counts[0] or quarter_counts[2]; //set low and high for next pass + high = quarter_counts[1] or quarter_counts[3]; + } + } + if (accessq(i).req_size == 64) { + if (low xor high) { //can reduce size + accessq(i).req_size = 32; + if (high) accessq(i).addr += 32; + } + } + } + } + } +} + |
