Example #1
0
class ETLB:
    

    def __init__(self, nLines=64, associativity=8, pageSize=0x1000, tlb=None, cache=None, hub=None):
        """Simple associative cache.

        Parameters
        ----------

        size (int):
            Cache size in bytes. (Default 0x8000 (32 kB))
        associtivilty (int):
            Number of ways for an associative cache, -1 for fully associative.
        cacheLine (int):
            Number of bytes per cache line, determiines the number of offset bits.
        child (Cache):
            The next level of cache, must be a hub for etlb, default is None, which means default Hub.
        """
        self.nLines = nLines
        self.associativity = associativity
        self.hub = hub
        self.cache = cache
        self.tlb = tlb
        self.pageSize = pageSize


        if self.associativity == -1:
            self.associativity = self.nLines

        if self.hub is None:
            self.hub = Hub(associativity=self.associativity, pageSize=self.pageSize)
        if self.cache is None:
            self.cache = Cache(size=0x8000, associativity=16)
            self.cache.accessEnergy = 0.0111033
            self.cache.accessTime = 4
            self.cache.tagTime = 1
            self.cache.tagEnergy = 0.000539962

        self.hub.eTLB = self

        self.cacheLine = self.cache.cacheLine

        self.nSets = self.nLines // self.associativity

        self.offsetBits = int(math.ceil(math.log2(self.cacheLine)))
        self.wayBits = int(math.ceil(math.log2(self.associativity)))
        self.pageBits = int(math.ceil(math.log2(self.pageSize))) - self.offsetBits
        self.setBits = int(math.ceil(math.log2(self.nSets)))
        self.tagBits = 48 - self.setBits - self.pageBits - self.offsetBits

        if self.tlb is None:
            self.tlb = TLB(512, self.tagBits + self.setBits)

        self.freeList = [list(range(self.associativity)) for i in range(self.nSets)]

        self.counter = 0
        self.entries = [[ETLBEntry(self.pageSize, self.cacheLine) for i in range(self.associativity)] for j in range(self.nSets)]

        self.hit = [0,0,0,0] #DRAM,L1I,L1D,L2  Note: L1 is actually a unified cache at present, for forward compatability if separate caches are ever implemented
        self.miss = 0
        self.cycles = 0
        self.energy = 0.

        
    def access(self, address, write=False, count=True, countTime=None, countEnergy=None):
        """Access a given address.

        Parameters
        ----------
        address (int):
            The address which is accessed.
        write (bool):
            True if the access is a write, False for a read (default read).
            This parameter is unused currently, but maintained for future use.
        count (bool):
            Whether hit/miss rate should be counted (default is True).
        """
        # Step 1 in fig2/3d
        offset = address % self.cacheLine
        pageIndex = (address >> self.offsetBits) % (1 << self.pageBits)
        setIndex = (address >> (self.offsetBits + self.pageBits))  % self.nSets

        tag = address >> (self.setBits + self.pageBits + self.offsetBits)

        if countTime is None:
            countTime = count

        if countEnergy is None:
            countEnergy = count

        #eTLB Hit
        hit = False
        for i,entry in enumerate(self.entries[setIndex]):
            if entry.valid and entry.vtag == tag:
                hit = True
                loc = entry.location[pageIndex]
                way = entry.way[pageIndex]
                if count:
                    self.hit[loc] += 1
                # Not in cache fig2c
                if loc == 0:
                    # Access to DRAM not simulated, send to CPU (step 2/3)
                    # ((entry.paddr << self.pageBits) + pageIndex ) <<self.offsetBits) + offset
                    # Evict from L1 (step 4)
                    L1Set = (address >> (self.offsetBits + self.pageBits))  % self.cache.nSets
                    if len(self.cache.freeList[L1Set]) == 0:
                        self.evictCache(L1Set, countEnergy=countEnergy)
                    # Update Hub pointer, place data (step 5)
                    L1Way = self.cache.freeList[L1Set].pop()
                    self.cache.accessDirect(L1Set, L1Way, countTime=False, countEnergy=countEnergy)
                    etlbPointer = (i << self.setBits) + setIndex
                    hubWay = 0
                    hubSet = entry.paddr % self.hub.nSets
                    for j in range(self.hub.associativity):
                        if self.hub.entries[hubSet][j].valid and self.hub.entries[hubSet][j].eTLBPointer == etlbPointer:
                            hubWay = j
                            break
                    self.cache.tags[L1Set][L1Way] = (hubWay << self.hub.setBits) + (entry.paddr % self.hub.nSets)
    
                    # Update the CLT (step 6)
                    entry.location[pageIndex] = 2 #L1D (unified L1)
                    entry.way[pageIndex] = L1Way
                # In L1 (data and instruction caches unified, this needs to be split if those are split) fig2a
                elif loc == 1 or loc == 2:
                    # access the L1 cache entry, send to CPU (step 2/3)
                    cacheSetIndex = (address >> self.offsetBits) % self.cache.nSets
                    self.cache.accessDirect(cacheSetIndex, way, countTime=countTime, countEnergy=countEnergy)
                # In L2 fig2b
                elif loc == 3:
                    # access the L2 cache entry, send to CPU (step 2/3)
                    cacheSetIndex = entry.paddr % self.hub.cache.nSets
                    self.hub.cache.accessDirect(cacheSetIndex, way, countTime=countTime, countEnergy=countEnergy)
                    # Evict from L1 (step 4)
                    L1Set = (address >> (self.offsetBits + self.pageBits))  % self.cache.nSets
                    if len(self.cache.freeList[L1Set]) == 0:
                        self.evictCache(L1Set, countEnergy=countEnergy)
                    # Update Hub pointer, place data (step 5)
                    L1Way = self.cache.freeList[L1Set].pop()
                    self.cache.accessDirect(L1Set, L1Way, countTime=False, countEnergy=countEnergy)
                    self.cache.tags[L1Set][L1Way] = self.hub.cache.tags[cacheSetIndex][way]
    
                    # Update the CLT (step 6)
                    entry.location[pageIndex] = 2 #L1D (unified L1)
                    entry.way[pageIndex] = L1Way

                    # Free the L2 entry so it can be used again (Only one copy, which is now in L1)
                    self.hub.cache.evict(cacheSetIndex, way, countEnergy=countEnergy)
                # Invalid location
                else:
                    raise ValueError("Location in CLT is invalid, expected 2 bit int, got %d"%loc)
                way = i
                break
        
        #eTLB Miss fig3d
        if not hit:
            if count:
                self.miss += 1
            # Evict if necessary (step 2)
            if len(self.freeList[setIndex]) == 0:
                self.evict(setIndex)
            way = self.freeList[setIndex].pop()
            entry = self.entries[setIndex][way]

            # Update the virtual and physical address, calling the TLB (step 3)
            entry.vtag = tag
            entry.paddr = self.tlb.translateVirt((tag << self.setBits) + setIndex)
            addr = (((entry.paddr << self.pageBits) + pageIndex) << self.offsetBits) + offset
            # access the Hub (step 4)
            hubEntry = self.hub.access(addr, write=write, count=count, countEnergy=countEnergy, countTime=countTime)

            # Copy the CLT (step 5)
            entry.way = hubEntry.way.copy()
            entry.location = hubEntry.location.copy()
            entry.valid = True

            hubSet = entry.paddr % self.hub.nSets

            # Update the eTLBPointer, and Valid bit (step 6)
            hubEntry.eTLBValid = True
            hubEntry.eTLBPointer = (way << self.setBits) + setIndex
            self.access(address, write, count=False, countEnergy=True, countTime=False)
        
        self.counter += 1
        self.entries[setIndex][way].lastAccess = self.counter

    def evict(self, setNumber, way=None):
        """Evict (i.e. add to the free list) a cache line.
        
        If `way` is None, LRU replacement policy is used among occupied lines.
        If `way` is an integer, that integer is added to the free list.
        """
        if way is not None:
            if way not in self.freeList[setNumber]:
                self.freeList[setNumber].append(way)
            return way
        else:
            way = 0
            minAccess = self.entries[setNumber][0].lastAccess
            entry = self.entries[setNumber][0]
            for i,ent in enumerate(self.entries[setNumber]):
                if i not in self.freeList[setNumber] and self.entries[setNumber][i].lastAccess < minAccess:
                    way = i
                    minAccess = self.entries[setNumber][i].lastAccess
                    entry = self.entries[setNumber][i]

            eTLBPointer = (way << self.setBits) + setNumber
            hubSet = entry.paddr % self.hub.nSets
            hubWay = -1
            for i in range(self.hub.associativity):
                if self.hub.entries[hubSet][i].eTLBPointer == eTLBPointer:
                    hubWay = i
                    break
            if hubWay == -1:
                raise RuntimeError("Entry not in hub when expected")
            self.hub.entries[hubSet][hubWay].location = entry.location.copy()
            self.hub.entries[hubSet][hubWay].way = entry.way.copy()
            self.hub.entries[hubSet][hubWay].eTLBValid = False

            if way not in self.freeList[setNumber]:
                self.freeList[setNumber].append(way)
            return way

    def evictCache(self, setNumber, way=None, countEnergy=True):
        # Fig3f
        # Select A Victim, acess its hub pointer (step 1)
        if way == None:
            way = self.cache.selectEviction(setNumber)
        hubPointer = self.cache.tags[setNumber][way]

        # Find the set (step 2)
        L2Set = hubPointer % self.hub.cache.nSets

        # If needed, evict a line (step 3)
        if len(self.hub.cache.freeList[L2Set]) == 0:
            self.hub.evictCache(L2Set, countEnergy=countEnergy)
        # Move the data/hub pointer (step 4)
        L2Way = self.hub.cache.freeList[L2Set].pop()
        self.hub.cache.accessDirect(L2Set, L2Way, countTime=False, countEnergy=countEnergy)
        self.hub.cache.tags[L2Set][L2Way] = hubPointer

        # Update the active CLT (step 5)
        hubSet = hubPointer % self.hub.nSets
        hubWay = hubPointer >> self.hub.setBits

        hubEntry = self.hub.entries[hubSet][hubWay]

        if hubEntry.eTLBValid:
            etlbSet = hubEntry.eTLBPointer % self.nSets
            etlbWay = hubEntry.eTLBPointer >> self.setBits
            
            entry = self.entries[etlbSet][etlbWay]
            for pageIndex in range(entry.nEntries):
                if entry.location[pageIndex] == 2 and entry.location[pageIndex] == way:
                    entry.location[pageIndex] = 3 #L2
                    entry.way[pageIndex] = L2Way
        else:
            for pageIndex in range(hubEntry.nEntries):
                if hubEntry.location[pageIndex] == 2 and hubEntry.location[pageIndex] == way:
                    hubEntry.location[pageIndex] = 3 #L2
                    hubEntry.way[pageIndex] = L2Way
        # Actually evict
        self.cache.evict(setNumber, way, countEnergy=countEnergy)    
Example #2
0
class Hub:
    def __init__(self,
                 nLines=0x1000,
                 associativity=8,
                 pageSize=0x1000,
                 cache=None):
        """Simple associative cache.

        Parameters
        ----------

        size (int):
            Cache size in bytes. (Default 0x8000 (32 kB))
        associtivilty (int):
            Number of ways for an associative cache, -1 for fully associative.
        cacheLine (int):
            Number of bytes per cache line, determiines the number of offset bits.
        child (Cache):
            The next level of cache, must be a hub for etlb, default is None, which means default Hub.
        """
        self.nLines = nLines
        self.associativity = associativity
        self.pageSize = pageSize
        self.cache = cache
        self.eTLB = None  #set in ETLB, circular refererence

        if self.associativity == -1:
            self.associativity = self.nLines

        if self.cache is None:
            self.cache = Cache(size=0x100000, associativity=16)
            self.cache.accessTime = 7
            self.cache.tagTime = 3
            self.cache.accessEnergy = 0.136191
            self.cache.tagEnergy = 0.00221937

        self.cacheLine = self.cache.cacheLine

        self.nSets = self.nLines // self.associativity

        self.offsetBits = int(math.ceil(math.log2(self.cacheLine)))
        self.pageBits = int(math.ceil(math.log2(
            self.pageSize))) - self.offsetBits
        self.setBits = int(math.ceil(math.log2(self.nSets)))
        self.tagBits = 48 - self.setBits - self.pageBits - self.offsetBits

        self.freeList = [
            list(range(self.associativity)) for i in range(self.nSets)
        ]

        self.counter = 0
        self.entries = [[
            HubEntry(self.pageSize, self.cacheLine)
            for i in range(self.associativity)
        ] for j in range(self.nSets)]

        self.hit = [
            0, 0, 0, 0
        ]  #DRAM,L1I,L1D,L2  Note: L1 is actually a unified cache at present, for forward compatability if separate caches are ever implemented
        self.miss = 0

    def access(self,
               address,
               write=False,
               count=True,
               countTime=None,
               countEnergy=None):
        """Access a given address.

        Parameters
        ----------
        address (int):
            The address which is accessed.
        write (bool):
            True if the access is a write, False for a read (default read).
            This parameter is unused currently, but maintained for future use.
        count (bool):
            Whether hit/miss rate should be counted (default is True).
        """
        offset = address % self.cacheLine
        pageIndex = (address >> self.offsetBits) % (1 << self.pageBits)
        setIndex = (address >> (self.offsetBits + self.pageBits)) % self.nSets

        tag = address >> (self.setBits + self.pageBits + self.offsetBits)

        if countTime is None:
            countTime = count

        if countEnergy is None:
            countEnergy = count

        # Hub Hit
        hit = False
        for i, entry in enumerate(self.entries[setIndex]):
            if entry.ptag == tag:
                hit = True
                loc = entry.location[pageIndex]
                way = entry.way[pageIndex]
                if count:
                    self.hit[loc] += 1
                return entry

        # Hub Miss fig3e
        if not hit:
            if count:
                self.miss += 1

            # select a victim step 1
            if len(self.freeList[setIndex]) == 0:
                way = self.evict(setIndex)
                hubEntry = self.entrys[setIndex][way]
                L1Set = hubEntry.eTLBPointer % self.eTLB.cache.nSets
                L2Set = hubEntry.ptag % self.cache.nSets

                # Walk CLT, and evict (step 2)
                for i, loc, w in zip(range(hubEntry.nEntries),
                                     hubentry.location, hubentry.way):
                    if loc == 0:  # not in cache
                        pass
                    elif loc == 1 or loc == 2:  # In L1, combined instr/data, split if caches split
                        self.eTLB.evictCache(L1Set, w, countEnergy=countEnergy)
                    elif loc == 3:  # In L2
                        self.evictCache(L2Set, w, countEnergy=countEnergy)

                etlbSet = hubEntry.eTLBPointer % self.eTLB.nSets
                etlbWay = hubEntry.eTLBPointer >> self.eTLB.setBits

                # invalidate the eTLB CLT (step 3)
                self.eTLB.entries[etlbSet][etlbWay].valid = False

            # Install the new page (step 4)
            way = self.freeList[setIndex].pop()
            entry = self.entries[setIndex][way]
            entry.ptag = tag
            entry.eTLBValid = False
            entry.location = [0] * entry.nEntries
            entry.valid = True

        self.counter += 1
        self.entries[setIndex][way].lastAccess = self.counter
        return self.entries[setIndex][way]

    def evict(self, setNumber, way=None):
        """Evict (i.e. add to the free list) a cache line.
        
        If `way` is None, LRU replacement policy is used among occupied lines.
        If `way` is an integer, that integer is added to the free list.
        """
        if way is not None:
            self.freeList[setNumber].append(way)
            return way
        else:
            index = 0
            minAccess = self.entries[setNumber][0].lastAccess
            for i, acc in enumerate(self.entries[setNumber]):
                if i not in self.freeList[setNumber] and self.entries[setNumber][
                        i].lastAccess < minAccess:  # and self.entries[setNumber][i].etlbValid == False:
                    index = i
                    minAccess = self.entries[setNumber][i].lastAccess
            if index not in self.freeList[setNumber]:
                self.freeList[setNumber].append(index)
            self.entries[setNumber][index].eTLBValid = False
            return index

    def evictCache(self, setNumber, way=None, countEnergy=True):
        # Fig3f
        # Select A Victim, acess its hub pointer (step 1)
        if way == None:
            way = self.cache.selectEviction(setNumber)
        hubPointer = self.cache.tags[setNumber][way]

        # Move the data/hub pointer (step 4)
        # Access to DRAM not simultated
        self.cache.accessDirect(setNumber,
                                way,
                                write=False,
                                countEnergy=countEnergy)

        # Update the active CLT (step 5)
        hubSet = hubPointer % self.nSets
        hubWay = hubPointer >> self.setBits

        hubEntry = self.entries[hubSet][hubWay]

        if hubEntry.eTLBValid:
            etlbSet = hubEntry.eTLBPointer % self.nSets
            etlbWay = hubEntry.eTLBPointer >> self.setBits

            entry = self.entries[etlbSet][etlbWay]
            for pageIndex in range(entry.nEntries):
                if entry.location[pageIndex] == 2 and entry.location[
                        pageIndex] == way:
                    entry.location[pageIndex] = 0  #NIC
        else:
            for pageIndex in range(hubEntry.nEntries):
                if hubEntry.location[pageIndex] == 2 and hubEntry.location[
                        pageIndex] == way:
                    hubEntry.location[pageIndex] = 0  #NIC
        # Actually evict
        self.cache.evict(setNumber, way, countEnergy=countEnergy)