From b577cbcdf229a2c02d1bf8584c6e82be7a14cb33 Mon Sep 17 00:00:00 2001 From: Tor Aamodt Date: Sat, 16 Oct 2010 17:30:52 -0800 Subject: 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] --- src/abstract_hardware_model.cc | 178 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) (limited to 'src/abstract_hardware_model.cc') 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 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 *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; + } + } + } + } + } +} + -- cgit v1.3