#!/usr/bin/python # ----------------------------------------------------------------------------- # Memory simulation # # (c) 2004 Jason Bittel # ----------------------------------------------------------------------------- import os import sys import string import linecache # ----------------------------------------------------------------------------- class g: # Global constants ACCESS_TIME = 10 # Number of clocks for read/write CYCLE_TIME = 40 # Memory cycle time MEM_BANK_BITS = 0 # Number of bits specifying memory bank (cmd line) NUM_MEM_BANKS = 0 # Amount of interleaving # Global variables cycles = 0 # Clock cycle count inFile = "" # Filename of input file (cmd line) memBankSet = {} # Dictionary (hash) of memory banks bus = 0 # Instance of memory bus class # ----------------------------------------------------------------------------- class Bank: """ Representation of an individual memory bank. This contains several elements: a unique id, active memory reference, a busy indicator, and multiple flags indicating which stage the bank is occupied in. In addition, there are methods for manipulating and returning the status of these values. """ def __init__(self, id): self.id = id self.address = 0 self.busy = 0 self.readRequest = 0 self.writeRequest = 0 self.readCycle = 0 self.writeCycle = 0 # Begin read request def setReadRequest(self, address, at): self.setFlag("rr") self.setBankTime(at) # Begin write request def setWriteRequest(self, address, at): self.setFlag("wr") self.setBankTime(at) # Begin read cycle def setReadCycle(self, ct): self.setFlag("rc") self.setBankTime(ct) # Begin write cycle def setWriteCycle(self, ct): self.setFlag("wc") self.setBankTime(ct) # Clear all status flags def clearFlag(self): self.readRequest = 0 self.writeRequest = 0 self.readCycle = 0 self.writeCycle = 0 # Set specified status flag. Flag is one of: # read request, write request, read cycle, write cycle def setFlag(self, flag): self.clearFlag() if flag == "rr": self.readRequest = 1 elif flag == "wr": self.writeRequest = 1 elif flag == "rc": self.readCycle = 1 elif flag == "wc": self.writeCycle = 1 # Return busy status def isBankBusy(self): return self.busy # Return if bank is completely idle def isBankFree(self): if self.readRequest == 0 and self.writeRequest == 0 and \ self.readCycle == 0 and self.writeCycle == 0: return 1 else: return 0 # Set busy time def setBankTime(self, time): self.busy = time # Decrement busy time by 1 def decBankTime(self): if self.busy > 0: self.busy -= 1 # ----------------------------------------------------------------------------- class Bus: """ Representation of the memory bus. Contains a single value indicating the remaining time until the bus is free. """ def __init__(self): self.busy = 0 # Return busy status def isBusBusy(self): return self.busy # Set busy time def setBusTime(self, time): self.busy = time # Decrement busy time by 1 def decBusTime(self): if self.busy > 0: self.busy -= 1 # ----------------------------------------------------------------------------- def initMemoryBanks(): """ Builds an array of memory bank objects. The total number of memory banks is determined by a global constant. Each bank is an object stored into a dictionary, with the unique key being the numeric order in which they were created. This key is also stored within the object. """ for n in range(g.NUM_MEM_BANKS): g.memBankSet[n] = Bank(n) # ----------------------------------------------------------------------------- def readReference(inFile, lineNum): """ Get specified line from input file. The incoming line is split into subfields, only two of which are returned. If the line does not exist in the file, a zero tuple is returned to indicate failure. """ line = linecache.getline(inFile, lineNum) if line != '': (rw, addressHex, addressOct, addressDec) = string.splitfields(line, " ") return (rw, int(addressDec)) else: return (0, 0) # ----------------------------------------------------------------------------- def getBank(address): """ Takes an address and returns the correct memory bank it maps to. The mapping is determined by extracting the least significant N bits from the address, where N is a global constant specifying how many bits to use. """ if g.MEM_BANK_BITS == 0: return 0 else: return int(address & int("1" * g.MEM_BANK_BITS, 2)) # ----------------------------------------------------------------------------- def decBanks(): """ Decrement busy time in all memory banks. As the banks are stored in a dictionary, iterate through it and decrement the time using the class method. """ for num, bank in g.memBankSet.iteritems(): bank.decBankTime() # ----------------------------------------------------------------------------- def isCompleted(address): """ Check to see if the simulation needs to halt. This is determined by checking to see that all memory banks are devoid of time, the bus is clear, and the input file has reached the end. """ timeLeft = 0 for num, bank in g.memBankSet.iteritems(): timeLeft += bank.isBankBusy() if timeLeft == 0 and g.bus.isBusBusy() == 0 and address == -1: return 1 else: return 0 # ----------------------------------------------------------------------------- def printSummary(): """ Print results of program execution. This is called at the end of the program run to provide a summary of what settings were used and the resulting time. """ print "o--------------------------o" print "| Input file:", g.inFile print "| Access time:", repr(g.ACCESS_TIME) print "| Cycle time:", repr(g.CYCLE_TIME) print "| Interleaving:", repr(g.NUM_MEM_BANKS) print "| Clock cycles:", repr(g.cycles) print "o--------------------------o" # ============================================================================= def main(): """ Main program; contains primary clock loop and core logic. """ if not len(sys.argv) == 3: # Check for command line arguments print "Usage: %s [ memory bits ] [ filename ]\n" % \ os.path.basename(sys.argv[0]) sys.exit(0) # Read parameters from command line g.MEM_BANK_BITS = int(sys.argv[1]) # Number of bits specifying memory bank g.inFile = sys.argv[2] # Input text file # Initialize variables g.NUM_MEM_BANKS = 2 ** g.MEM_BANK_BITS # Init number of memory banks to use g.bus = Bus() # Set instance of bus class rw = 0 # Reference read/write flag address = 0 # Reference address line = 1 # Current line number in input file # Build memory banks initMemoryBanks() while 1: # Read references ------------------------------------------------------- if address == 0: # No reference pending, so get next reference (rw, address) = readReference(g.inFile, line) if address != 0: # We have a valid reference line += 1 memBank = getBank(address) else: address = -1 # Set flag for EOF # Reference events ------------------------------------------------------ if g.memBankSet[memBank].isBankFree() == 1 and \ g.bus.isBusBusy() == 0 and address > 0: if rw == 'R': # Handle read reference g.memBankSet[memBank].setReadRequest(address, g.ACCESS_TIME) g.bus.setBusTime(1) elif rw == 'W': # Handle write reference g.memBankSet[memBank].setWriteRequest(address, g.ACCESS_TIME) g.bus.setBusTime(1) rw = address = 0 # Clear out reference # Memory events --------------------------------------------------------- for num, bank in g.memBankSet.iteritems(): if not bank.isBankBusy() > 0 and not bank.isBankFree() == 1: # Handle appropriate memory event if bank.readRequest == 1 and g.bus.isBusBusy() == 0: bank.setReadCycle(g.CYCLE_TIME) g.bus.setBusTime(g.CYCLE_TIME) elif bank.writeRequest == 1: bank.setWriteCycle(g.CYCLE_TIME) elif bank.readCycle == 1: bank.address = 0 bank.clearFlag() elif bank.writeCycle == 1: bank.address = 0 bank.clearFlag() # Terminate loop -------------------------------------------------------- if isCompleted(address) == 1: break # Cycle ----------------------------------------------------------------- decBanks() g.bus.decBusTime() g.cycles += 1 # Display results of program run printSummary() if __name__ == "__main__": main()