def populate(self, address, cache: Cache, dirty=False): base_address = address & cache.get_base_address_mask() block = Block(base_address, dirty, self._replacement_policy) error = cache.put(block) if error is not None: raise EnvironmentError( """Cold placement of the following address caused an eviction in the cache. You probably didn't want this. Address: {} was placed into the {} cache. Address: {} was evicted as a result! """.format(hex(base_address), cache.name, error.base_address))
class ThreeLevelSUUInclusiveCacheSystem: def __init__(self, space: AddressSpace, policy: ReplacementPolicy, level_sizes: list, level_associativites: list, blocksize, level_latencies: list = None): self._space = space self._replacement_policy = policy self._blocksize = blocksize if not isinstance(level_sizes, list) or len(level_sizes) != 3: raise AttributeError( "Field 'level_sizes' must be a list of length 3 indicating I/DL1, UL2, and UL3 cache sizes" ) if not isinstance(level_associativites, list) or len(level_associativites) != 3: raise AttributeError( "Field 'level_associativites' must be a list of length 3 indicating I/DL1, UL2, and UL3 associativity" ) if level_latencies: if not isinstance(level_latencies, list) or len(level_latencies) != 4: raise AttributeError( "Field 'level_latencies' must be a list of length 4 indicating I/DL1, UL2, UL3, and MEM latencies" ) for level in level_latencies: if not isinstance(level, tuple) or len(level) != 2: raise AttributeError( "Field 'level_latencies' must be a list of tuples indicating (read_latency, write_latency)" ) self.DL1 = Cache(space, level_sizes[0], level_associativites[0], blocksize, policy, name='DL1', rlatency=level_latencies[0][0], wlatency=level_latencies[0][1]) self.IL1 = Cache(space, level_sizes[0], level_associativites[0], blocksize, policy, name='IL1', rlatency=level_latencies[0][0], wlatency=level_latencies[0][1]) self.UL2 = Cache(space, level_sizes[1], level_associativites[1], blocksize, policy, name='UL2', rlatency=level_latencies[1][0], wlatency=level_latencies[1][1]) self.UL3 = Cache(space, level_sizes[2], level_associativites[2], blocksize, policy, name='UL3', rlatency=level_latencies[2][0], wlatency=level_latencies[2][1]) self.MEM = Cache(space, blocksize, 1, blocksize, policy, name='MEM', rlatency=level_latencies[3][0], wlatency=level_latencies[3][1]) else: self.DL1 = Cache(space, level_sizes[0], level_associativites[0], blocksize, policy, name='DL1') self.IL1 = Cache(space, level_sizes[0], level_associativites[0], blocksize, policy, name='IL1') self.UL2 = Cache(space, level_sizes[1], level_associativites[1], blocksize, policy, name='UL2') self.UL3 = Cache(space, level_sizes[2], level_associativites[2], blocksize, policy, name='UL3') self.MEM = Cache(space, blocksize, 1, blocksize, policy, name='MEM') self.stats = CacheMetrics([ self.IL1.name, self.DL1.name, self.UL2.name, self.UL3.name, self.MEM.name ], [ (self.DL1.name, self.DL1.name), (self.DL1.name, self.UL2.name), (self.DL1.name, self.UL3.name), (self.DL1.name, self.MEM.name), (self.UL2.name, self.UL2.name), (self.UL2.name, self.UL3.name), (self.UL2.name, self.MEM.name), (self.UL3.name, self.UL3.name), (self.UL3.name, self.MEM.name), (self.MEM.name, self.MEM.name), (self.MEM.name, self.UL3.name), (self.MEM.name, self.UL2.name), (self.MEM.name, self.DL1.name), (self.UL3.name, self.UL2.name), (self.UL3.name, self.DL1.name), (self.UL2.name, self.DL1.name), (self.IL1.name, self.IL1.name), (self.IL1.name, self.UL2.name), (self.IL1.name, self.UL3.name), (self.IL1.name, self.MEM.name), (self.MEM.name, self.IL1.name), (self.UL3.name, self.IL1.name), (self.UL2.name, self.IL1.name), ]) def perform(self, address, for_data, is_fetch): cache = self.DL1 if for_data else self.IL1 block = cache.get(address) self.stats.add_latency( cache.read_latency if is_fetch else cache.write_latency, is_fetch) hit_in = cache if block is None: self.stats.add_miss(cache.name) cache = self.UL2 block = cache.get(address) self.stats.add_latency( cache.read_latency if is_fetch else cache.write_latency, is_fetch) hit_in = self.UL2 if block is None: self.stats.add_miss(cache.name) cache = self.UL3 block = cache.get(address) self.stats.add_latency( cache.read_latency if is_fetch else cache.write_latency, is_fetch) hit_in = self.UL3 if block is None: self.stats.add_miss(cache.name) # Not in the cache, fetch from memory self.stats.add_latency( self.MEM.read_latency if is_fetch else self.MEM.write_latency, is_fetch) # Allocate new block from MEM to L3 block = Block(address & self.UL3.get_base_address_mask(), False, self.UL3.get_policy()) if is_fetch: block.read() else: block.write() hit_in = self.MEM cache = self.UL3 evicted = cache.put(block) if evicted: # If the evicted block is in L1, transition from L1 to MEM if self.DL1.get(evicted.base_address()): self.stats.add_transition( self.DL1.name, self.MEM.name, evicted.base_address(), total_size=self.DL1.get_block_size()) # If the evicted block is in L1, evict it too, and from L2 self.DL1.remove_base(evicted.base_address()) self.UL2.remove_base(evicted.base_address()) elif self.IL1.get(evicted.base_address()): self.stats.add_transition( self.IL1.name, self.MEM.name, evicted.base_address(), total_size=self.IL1.get_block_size()) # If the evicted block is in L1, evict it too, and from L2 self.IL1.remove_base(evicted.base_address()) self.UL2.remove_base(evicted.base_address()) elif self.UL2.get(evicted.base_address()): self.stats.add_transition( self.UL2.name, self.MEM.name, evicted.base_address(), total_size=self.UL2.get_block_size()) # If the evicted block is in L2, evict it self.UL2.remove_base(evicted.base_address()) else: self.stats.add_transition( self.UL3.name, self.MEM.name, evicted.base_address(), total_size=self.UL3.get_block_size()) else: if is_fetch: block.read() else: block.write() # Allocate new block from L3 to L2 block = Block(address & self.UL2.get_base_address_mask(), block.is_dirty(), self.UL2.get_policy()) if is_fetch: block.read() else: block.write() cache = self.UL2 evicted = cache.put(block) if evicted: # If the evicted block is in L1, transition from L1 to L3, else L2 to L3 if self.DL1.get(evicted.base_address()): self.stats.add_transition( self.DL1.name, self.UL3.name, evicted.base_address(), total_size=self.DL1.get_block_size()) # If the evicted block is in L1, evict it too self.DL1.remove_base(evicted.base_address()) elif self.IL1.get(evicted.base_address()): self.stats.add_transition( self.IL1.name, self.UL3.name, evicted.base_address(), total_size=self.IL1.get_block_size()) # If the evicted block is in L1, evict it too self.IL1.remove_base(evicted.base_address()) else: self.stats.add_transition( self.UL2.name, self.UL3.name, evicted.base_address(), total_size=self.UL2.get_block_size()) else: if is_fetch: block.read() else: block.write() # Guaranteed by inclusivity self.UL3.get(address).touch() # Allocate new block from L2 to L1 block = Block( address & (self.DL1 if for_data else self.IL1).get_base_address_mask(), block.is_dirty(), (self.DL1 if for_data else self.IL1).get_policy()) if is_fetch: block.read() else: block.write() cache = self.DL1 if for_data else self.IL1 evicted = cache.put(block) if evicted: self.stats.add_transition( (self.DL1 if for_data else self.IL1).name, self.UL2.name, evicted.base_address(), total_size=(self.DL1 if for_data else self.IL1).get_block_size()) else: if is_fetch: block.read() else: block.write() # Guaranteed by inclusivity self.UL2.get(address).touch() self.UL3.get(address).touch() self._replacement_policy.step() self.stats.add_hit(address, hit_in.name, is_fetch, not for_data) self.stats.add_transition(hit_in.name, cache.name, address) return cache.name, hit_in.name, block def perform_fetch(self, address, for_data=True): self.perform(address, for_data, True) def perform_set(self, address, for_data=True): self.perform(address, for_data, False) def populate(self, address, cache: Cache, dirty=False): base_address = address & cache.get_base_address_mask() block = Block(base_address, dirty, self._replacement_policy) error = cache.put(block) if error is not None: raise EnvironmentError( """Cold placement of the following address caused an eviction in the cache. You probably didn't want this. Address: {} was placed into the {} cache. Address: {} was evicted as a result! """.format(hex(base_address), cache.name, error.base_address))