import sys import traceback import sqlite3 import os import json from memUtil import * class DramConfig(object): memoryType = "" scheduler = "" bankwiseLogic = 0 RefMode = 1 clk = 0 unitOfTime = "" dataRate = 0 nActivateWindow = numberOfBanks = 0 clk = 0 tRP = 0 # precharge-time (pre -> act same bank) tRAS = 0 # active-time (act -> pre same bank) tRC = 0 # RAS-cycle-time (min time bw 2 succesive ACT to same bank) tCCD_S = 0 # TODO: relevant? max(bl, tCCD) tCCD_L = 0 tRTP = 0 # Read to precharge tRRD_S = 0 # min time between 2 succesive ACT to different banks (different bank group) tRRD_L = 0 # min time between 2 succesive ACT to different banks (same bank group) tRCD = 0 # act -> read/write tNAW = 0 # n activate window tRL = 0 # read latency (read command start to data strobe) tWL = 0 # write latency tWR = 0 # write recovery (write to precharge) tWTR_S = 0 # write to read (different bank group) tWTR_L = 0 # write to read (same bank group) tCKESR = 0 # min time in sref tCKE = 0 # min time in pdna or pdnp tXP = 0 # min delay to row access command after pdnpx pdnax tXPDLL = 0 # min delay to row access command after pdnpx pdnax for dll commands tXS = 0 # min delay to row access command after srefx tXSDLL = 0 # min delay to row access command after srefx for dll commands tAL = 0 # additive delay (delayed execution in dram) tRFC = 0 # min ref->act delay tREFI = 0 # time between REF commands def readConfigFromFiles(self, connection): print("Parsing dram configuration") mcconfig = MCConfig(connection) memspec = MemSpec(connection) clkWithUnit = getClock(connection) self.clk = clkWithUnit[0] self.unitOfTime = clkWithUnit[1].lower() self.bankwiseLogic = 0 self.RefMode = 0 self.scheduler = mcconfig.getValue("Scheduler") self.numberOfBanks = memspec.getIntValue("memarchitecturespec","nbrOfBanks") self.burstLength = memspec.getIntValue("memarchitecturespec","burstLength") self.memoryType = memspec.getValue("memoryType") self.dataRate = memspec.getIntValue("memarchitecturespec","dataRate") if (self.memoryType == "WIDEIO_SDR"): self.nActivateWindow = 2 self.tRP = self.clk * memspec.getIntValue("memtimingspec","RP") self.tRAS = self.clk * memspec.getIntValue("memtimingspec","RAS") self.tRC = self.clk * memspec.getIntValue("memtimingspec","RC") self.tRRD_S = self.clk * memspec.getIntValue("memtimingspec","RRD") self.tRRD_L = self.tRRD_S self.tCCD_S = self.clk * memspec.getIntValue("memtimingspec","CCD") self.tCCD_L = self.tCCD_S self.tRCD = self.clk * memspec.getIntValue("memtimingspec","RCD") self.tNAW = self.clk * memspec.getIntValue("memtimingspec","TAW") self.tRL = self.clk * memspec.getIntValue("memtimingspec","RL") self.tWL = self.clk * memspec.getIntValue("memtimingspec","WL") self.tWR = self.clk * memspec.getIntValue("memtimingspec","WR") self.tWTR_S = self.clk * memspec.getIntValue("memtimingspec","WTR") self.tWTR_L = self.tWTR_S self.tRTP = self.clk * memspec.getIntValue("memtimingspec","RTP") self.tCKESR = self.clk * memspec.getIntValue("memtimingspec","CKESR") self.tCKE = self.clk * memspec.getIntValue("memtimingspec","CKE") self.tXP = self.clk * memspec.getIntValue("memtimingspec","XP") self.tXPDLL = self.tXP self.tXS = self.clk * memspec.getIntValue("memtimingspec","XS") self.tXSDLL = self.tXS self.tAL = self.clk * memspec.getIntValue("memtimingspec","AL") self.tRFC = self.clk * memspec.getIntValue("memtimingspec","RFC") self.tREFI = self.clk * memspec.getIntValue("memtimingspec","REFI") elif (self. memoryType == "DDR4"): self.nActivateWindow = 4 self.tRP = self.clk * memspec.getIntValue("memtimingspec","RP") self.tRAS = self.clk * memspec.getIntValue("memtimingspec","RAS") self.tRC = self.clk * memspec.getIntValue("memtimingspec","RC") self.tRTP = self.clk * memspec.getIntValue("memtimingspec","RTP") self.tRRD_S = self.clk * memspec.getIntValue("memtimingspec","RRD_S") self.tRRD_L = self.clk * memspec.getIntValue("memtimingspec","RRD_L") self.tCCD_S = self.clk * memspec.getIntValue("memtimingspec","CCD_S") self.tCCD_L = self.clk * memspec.getIntValue("memtimingspec","CCD_L") self.tRCD = self.clk * memspec.getIntValue("memtimingspec","RCD") self.tNAW = self.clk * memspec.getIntValue("memtimingspec","FAW") self.tRL = self.clk * memspec.getIntValue("memtimingspec","RL") self.tWL = self.clk * memspec.getIntValue("memtimingspec","WL") self.tWR = self.clk * memspec.getIntValue("memtimingspec","WR") self.tWTR_S = self.clk * memspec.getIntValue("memtimingspec","WTR_S") self.tWTR_L = self.clk * memspec.getIntValue("memtimingspec","WTR_L") self.tCKESR = self.clk * memspec.getIntValue("memtimingspec","CKESR") self.tCKE = self.clk * memspec.getIntValue("memtimingspec","CKE") self.tXP = self.clk * memspec.getIntValue("memtimingspec","XP") self.tXPDLL = self.clk * memspec.getIntValue("memtimingspec","XPDLL") self.tXS = self.clk * memspec.getIntValue("memtimingspec","XS") self.tXSDLL = self.clk * memspec.getIntValue("memtimingspec","XSDLL") self.tAL = self.clk * memspec.getIntValue("memtimingspec","AL") if (self.RefMode == "4"): self.tRFC = self.clk * memspec.getIntValue("memtimingspec","RFC4") self.tREFI = self.clk * (memspec.getIntValue("memtimingspec","REFI") / 4) elif (self.RefMode == "2"): self.tRFC = self.clk * memspec.getIntValue("memtimingspec","RFC2") self.tREFI = self.clk * (memspec.getIntValue("memtimingspec","REFI") / 2) else: self.tRFC = self.clk * memspec.getIntValue("memtimingspec","RFC") self.tREFI = self.clk * memspec.getIntValue("memtimingspec","REFI") elif (self. memoryType == "DDR3"): self.nActivateWindow = 4 self.tRP = self.clk * memspec.getIntValue("memtimingspec","RP") self.tRAS = self.clk * memspec.getIntValue("memtimingspec","RAS") self.tRC = self.clk * memspec.getIntValue("memtimingspec","RC") self.tRTP = self.clk * memspec.getIntValue("memtimingspec","RTP") self.tRRD_S = self.clk * memspec.getIntValue("memtimingspec","RRD") self.tRRD_L = self.clk * memspec.getIntValue("memtimingspec","RRD") self.tCCD_S = self.clk * memspec.getIntValue("memtimingspec","CCD") self.tCCD_L = self.clk * memspec.getIntValue("memtimingspec","CCD") self.tRCD = self.clk * memspec.getIntValue("memtimingspec","RCD") self.tNAW = self.clk * memspec.getIntValue("memtimingspec","FAW") self.tRL = self.clk * memspec.getIntValue("memtimingspec","RL") self.tWL = self.clk * memspec.getIntValue("memtimingspec","WL") self.tWR = self.clk * memspec.getIntValue("memtimingspec","WR") self.tWTR_S = self.clk * memspec.getIntValue("memtimingspec","WTR") self.tWTR_L = self.clk * memspec.getIntValue("memtimingspec","WTR") self.tCKESR = self.clk * memspec.getIntValue("memtimingspec","CKESR") self.tCKE = self.clk * memspec.getIntValue("memtimingspec","CKE") self.tXP = self.clk * memspec.getIntValue("memtimingspec","XP") self.tXPDLL = self.clk * memspec.getIntValue("memtimingspec","XPDLL") self.tXS = self.clk * memspec.getIntValue("memtimingspec","XS") self.tXSDLL = self.clk * memspec.getIntValue("memtimingspec","XSDLL") self.tAL = self.clk * memspec.getIntValue("memtimingspec","AL") self.tRFC = self.clk * memspec.getIntValue("memtimingspec","RFC") self.tREFI = self.clk * memspec.getIntValue("memtimingspec","REFI") else: raise Exception("MemoryType not supported yet. Insert a coin into the coin machine and try again") def clkAlign(self, value): return math.ceil(1.0*value/self.clk)*self.clk def getWriteAccessTime(self): if (self.dataRate == 1): return self.clk*(self.burstLength - 1) elif (self.memoryType == "DDR4"): return self.clk*self.burstLength/self.dataRate else: # DEFAULT return self.clk*self.burstLength/self.dataRate def getReadAccessTime(self): return self.burstLength/self.dataRate * dramconfig.clk def __init__(self): pass dramconfig = DramConfig() def calculateReadLength(burstLength): return dramconfig.tRL + burstLength * dramconfig.clk def calculateWriteLength(burstLength): return dramconfig.tWL + burstLength * dramconfig.clk # ----------- test utils --------------------------------------- tests = [] def test(function): tests.append(function) return function class TestResult(object): passed = True message = '' def __init__(self, passed=True, message=''): self.passed = passed self.message = message def TestSuceeded(): return TestResult() def TestFailed(message): return TestResult(False, message) def formatTime(time): return ('{0} {1}'.format(time, dramconfig.unitOfTime)) # ----------- checks --------------------------------------- @test def commands_are_clockaligned(connection): """Checks that all commands on the command bus are aligned to the system clock""" cursor = connection.cursor() query = "select ID,PhaseBegin,PhaseEnd from phases where phaseName NOT IN ('REQ','RESP') AND (phaseBegin%:clk!=0 OR phaseEnd%:clk!=0)" cursor.execute(query, {"clk": dramconfig.clk}) result = cursor.fetchone() if (result is not None): return TestFailed("Command with PhaseID {0} starts at {1} and ends at. One of those times. is not aligned to system clock ({2})".format(result[0], formatTime(result[1]), formatTime(result[2]), formatTime(dramconfig.clk))) return TestSuceeded() @test def commandbus_slots_are_used_once(connection): """Checks that no two phases on the command bus start at the same time""" cursor = connection.cursor() if (dramconfig.bankwiseLogic == "1"): excludedPhases = "('REQ','RESP','PREAB')" else: excludedPhases = "('REQ','RESP','PREAB','PDNA','PDNP','SREF','REFAB')" query = """SELECT PhaseBegin,count FROM (SELECT phaseBegin,count(phasebegin) AS count FROM Phases WHERE PhaseName NOT IN """ + excludedPhases + """ AND phasebegin>0 GROUP BY phaseBegin) WHERE count>1""" cursor.execute(query) result = cursor.fetchone() if (result is not None): return TestFailed("Slot on commandbus at time {0} is used multiple times".format(formatTime(result[0]))) return TestSuceeded() @test def phase_transitions_are_valid(connection): """Checks that all transition of two consequtive phases on the same bank are valid""" cursor = connection.cursor() validTransitions = {} # validTransitions tells you which phases are allowed to follow the last transaction. if (dramconfig.bankwiseLogic == "1"): validTransitions['PREPB'] = set(['ACT', 'REFPB', 'SREFB']) validTransitions['ACT'] = set(['RD', 'RDA', 'WR', 'WRA', 'PREPB', 'PREAB']) validTransitions['RD'] = set(['PREPB', 'RD', 'RDA', 'WR', 'WRA', 'PDNAB']) validTransitions['WR'] = set(['PREPB', 'RD', 'RDA', 'WR', 'WRA', 'PDNAB']) validTransitions['RDA'] = set(['ACT', 'REFPB', 'PDNPB']) validTransitions['WRA'] = set(['ACT', 'REFPB', 'PDNPB']) validTransitions['REFPB'] = set(['ACT', 'REFPB', 'PDNPB', 'SREFB']) validTransitions['PDNAB'] = set(['PREPB', 'RD', 'RDA', 'WR', 'WRA', 'REFPB']) validTransitions['PDNPB'] = set(['ACT', 'REFPB', 'SREFB']) validTransitions['SREFB'] = set(['ACT', 'REFPB']) else: validTransitions['PREPB'] = set(['ACT', 'PREAB', 'REFAB']) validTransitions['PREAB'] = set(['REFAB', 'SREF']) validTransitions['ACT'] = set(['RD', 'RDA', 'WR', 'WRA', 'PREAB']) validTransitions['RD'] = set(['PREPB', 'PREAB', 'RD', 'RDA', 'WR', 'WRA', 'PDNA']) validTransitions['WR'] = set(['PREPB', 'PREAB', 'RD', 'RDA', 'WR', 'WRA', 'PDNA']) validTransitions['RDA'] = set(['PREAB', 'ACT', 'REFAB', 'PDNA', 'PDNP']) validTransitions['WRA'] = set(['PREAB', 'ACT', 'REFAB', 'PDNA', 'PDNP']) validTransitions['REFAB'] = set(['PREAB', 'ACT', 'REFAB', 'PDNA', 'PDNP', 'SREF']) validTransitions['PDNA'] = set(['PREPB', 'PREAB', 'ACT', 'RD', 'RDA', 'WR', 'WRA', 'REFAB', 'PDNA', 'PDNP']) validTransitions['PDNP'] = set(['PREAB', 'ACT', 'REFAB', 'PDNA', 'PDNP', 'SREF']) validTransitions['SREF'] = set(['PREAB', 'ACT', 'REFAB', 'PDNA', 'PDNP']) if (dramconfig.bankwiseLogic == "1"): query = """SELECT PhaseName, phases.ID FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE (TBank=:bank) AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" else: # REFAB, PREAB, PDNA, PDNP and SREF are attributed to Bank 0 therefore this must be added to the order evaluation: query = """SELECT PhaseName, phases.ID FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE ((TBank=:bank) OR PhaseName IN ('PREAB', 'SREF', 'PDNP', 'PDNA', 'REFAB')) AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" for bankNumber in range(dramconfig.numberOfBanks): cursor.execute(query, {"bank": bankNumber}) lastRow = cursor.fetchone() for currentRow in cursor: currentPhase = currentRow[0] lastPhase = lastRow[0] if (currentPhase not in validTransitions[lastPhase]): return TestFailed("Phase {0}({1}) is not allowed to follow phase {2}({3})".format(currentRow[1], currentPhase, lastRow[1], lastPhase)) lastRow = currentRow return TestSuceeded() def timing_constraint(FirstPhase, SecondPhase): FirstPhaseName = FirstPhase[0] SecondPhaseName = SecondPhase[0] if ((FirstPhaseName == "PREPB" or FirstPhaseName == "PREAB") and SecondPhaseName != "PREAB"): return dramconfig.tRP elif (FirstPhaseName == "ACT"): return dramconfig.tRCD elif (FirstPhaseName == "RD"): if (SecondPhaseName in ["PREPB, PREAB"]): return dramconfig.tRTP elif (SecondPhaseName in ["RD, RDA"]): return max(dramconfig.tCCD_L, dramconfig.getReadAccessTime()) elif (SecondPhase in ["WR", "WRA"]): return dramconfig.tRL + dramconfig.getReadAccessTime() - dramconfig.tWL + 2 * dramconfig.clk elif (SecondPhase == "PDNA"): return dramconfig.tRL + dramconfig.getReadAccessTime() + dramconfig.clk elif (FirstPhaseName == "WR"): if (SecondPhaseName in ["PREPB, PREAB", "PDNA"]): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWR elif (SecondPhaseName in ["RD, RDA"]): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWTR_L elif (SecondPhaseName in ["WR, WRA"]): return max(dramconfig.tCCD_L, burstlength/dramconfig.dataRate) elif (FirstPhaseName == "RDA"): if (SecondPhaseName in ["ACT", "REFAB"]): return dramconfig.tRTP + dramconfig.tRP elif (SecondPhaseName == "PREAB"): return dramconfig.tRTP elif (SecondPhaseName in ["PDNA", "PDNP"]): return dramconfig.tRL + dramconfig.getReadAccessTime() + dramconfig.clk elif (FirstPhaseName == "WRA"): if (SecondPhaseName in ["ACT", "REFAB"]): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWR + dramconfig.tRP elif (SecondPhaseName == "PREAB"): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWR elif (SecondPhaseName in ["PDNA", "PDNP"]): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWR + dramconfig.clk elif (FirstPhaseName == "REFAB"): return dramconfig.tRFC elif (FirstPhaseName in ["PDNA", "PDNP"]): # print("{0}".format(FirstPhaseName)) # print("{0}".format(formatTime(FirstPhase[3]))) # print("{0}".format(formatTime(FirstPhase[2]))) # print("{0}".format(formatTime(dramconfig.tXP))) # print("{0}".format(formatTime(dramconfig.clk))) return (FirstPhase[3] - FirstPhase[2]) + dramconfig.tXP - dramconfig.clk elif (FirstPhaseName == "SREF"): return (FirstPhase[3] - FirstPhase[2]) + dramconfig.tXS - dramconfig.clk return 0 @test def timing_constraits_on_the_same_bank_hold(connection): """Checks that all transitions of two consecutive phases on the same bank meet their timing constraints""" cursor = connection.cursor() validTransitions = {} if (dramconfig.bankwiseLogic == "1"): query = """SELECT PhaseName, phases.ID, PhaseBegin, PhaseEnd FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE TBank=:bank AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" else: query = """SELECT PhaseName, phases.ID, PhaseBegin, PhaseEnd FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE ((TBank=:bank) OR PhaseName IN ('PREAB', 'SREF', 'PDNP', 'PDNA', 'REFAB')) AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" for bankNumber in range(dramconfig.numberOfBanks): cursor.execute(query, {"bank": bankNumber}) lastRow = cursor.fetchone() for currentRow in cursor: constraint = timing_constraint(lastRow, currentRow) if (currentRow[2] - (lastRow[2] + constraint) < 0): return TestFailed("Phase {0}({1}) starts {2} after Start of Phase {3}({4}). Minimal time is {5}".format(currentRow[1], currentRow[0], formatTime(currentRow[2] - lastRow[2]), lastRow[1], lastRow[0], formatTime(constraint))) lastRow = currentRow return TestSuceeded() @test def row_buffer_is_used_correctly(connection): """Checks that each bank's row buffer is used correctly""" cursor = connection.cursor() if (dramconfig.bankwiseLogic == "1"): query = """SELECT PhaseName, phases.ID FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE ((TBank=:bank) OR (PhaseNAME = "REFAB" AND TBank=0) OR (PhaseNAME = "PREAB" AND TBank=0)) AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" else: # REFAB, PREAB, PDNA, PDNP and SREF are stored to bank0 for all the other banks we have also to grep this command: # PhaseName IN ('PREAB', 'SREF', 'PDNP', 'PDNA', 'REFAB') query = """SELECT PhaseName, phases.ID FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE ((TBank=:bank) OR PhaseName IN ('PREAB', 'SREF', 'PDNP', 'PDNA', 'REFAB')) AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" # phases that precharge the bank and close the rowbuffer prechargingPhases = set(['PREPB', 'PREAB', 'RDA', 'WRA']) # phases that require the bank to be in active state and the rowbuffer to be opened accessingPhases = set(['RD', 'RDA', 'WR', 'WRA', 'PREPB']) # phases that require the bank to be in precharged state and the robuffer to be closed idlePhases = set(['ACT', 'PDNP', 'REFAB', 'SREF']) for bankNumber in range(dramconfig.numberOfBanks): cursor.execute(query, {"bank": bankNumber}) rowBufferIsClosed = True for currentRow in cursor: if ((currentRow[0] in accessingPhases) and (rowBufferIsClosed is True)): return TestFailed("Phase {0}({1}) acesses a closed rowbuffer".format(currentRow[1], currentRow[0])) if ((currentRow[0] in idlePhases) and (rowBufferIsClosed is False)): return TestFailed("Phase {0}({1}) needs a closed rowbuffer".format(currentRow[1], currentRow[0])) if (currentRow[0] == 'ACT'): rowBufferIsClosed = False if (currentRow[0] in prechargingPhases): rowBufferIsClosed = True return TestSuceeded() @test def no_commands_during_refresh(connection): """Checks that no command was scheduled during refresh period""" cursor = connection.cursor() if (dramconfig.bankwiseLogic == "1"): query = """SELECT PhaseBegin, PhaseEnd, TBank FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE PhaseName = 'REFPB' """ test_query = """SELECT PhaseName FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE ((PhaseBegin >= ? and PhaseEnd <= ?) or (PhaseBegin <= ? and PhaseEnd > ?) or (PhaseBegin < ? and PhaseEnd >= ?)) and PhaseName NOT IN ('REQ','RESP','REFPB') and TBank = ?""" else: query = """SELECT PhaseBegin, PhaseEnd FROM phases WHERE PhaseName = 'REFAB' """ test_query = """SELECT PhaseName FROM phases WHERE ((PhaseBegin >= ? and PhaseEnd <= ?) or (PhaseBegin <= ? and PhaseEnd > ?) or (PhaseBegin < ? and PhaseEnd >= ?)) and PhaseName NOT IN ('REQ','RESP','REFAB')""" cursor.execute(query) result = cursor.fetchall() for row in result: if(dramconfig.bankwiseLogic == "1"): cursor.execute(test_query, (row[0], row[1], row[0], row[0], row[1], row[1], row[2])) else: cursor.execute(test_query, (row[0], row[1], row[0], row[0], row[1], row[1])) test = cursor.fetchone() if(test is not None): return TestFailed("A Command {0} was scheduled during a refresh period".format(test[0])) return TestSuceeded() @test def max_number_ref_burst(connection): """Checks that the maximum number of REFAB commands in a burst is not exceeded""" cursor = connection.cursor() query = """SELECT PhaseBegin, PhaseEnd FROM phases WHERE PhaseName = 'REFAB' """ prevrow = [0] * 2 cnt = 0 flexibleRef = getFlexibleRef(connection) maxRefBurst = getMaxRefBurst(connection) cursor.execute(query) result = cursor.fetchall() if (flexibleRef): maxRefBurst = maxRefBurst - 1 # Since the intersections will be used for this test, use -1 from the max for row in result: if (prevrow[1] == row[0]): cnt += 1 else: cnt = 0 # Reset the counter every time a burst ends prevrow = row if(cnt > maxRefBurst): return TestFailed("Maximum number of REFAB in a burst was exceeded at {0} with {1} REFAB in sequence. Maximum allowed is {2}.".format(formatTime(row[0]), cnt, maxRefBurst)) return TestSuceeded() @test @test def max_time_without_ref(connection): """Checks that the maximum time allowed between REFAB/SREF commands is not exceeded""" cursor = connection.cursor() query = """SELECT PhaseBegin, PhaseEnd FROM phases WHERE PhaseName = 'REFAB' OR PhaseName = 'SREF' """ prevrow = [0] * 2 flexibleRef = getFlexibleRef(connection) maxRefBurst = getMaxRefBurst(connection) cursor.execute(query) result = cursor.fetchall() if (flexibleRef): maxTimeWithoutRef = ((maxRefBurst + 1) * dramconfig.tREFI) + dramconfig.tRP # Bursts are possible, so max should be the possible burst size + 1 else: maxTimeWithoutRef = dramconfig.tREFI + dramconfig.tRP tolerance = 0.05 maxTimeWithoutRef = maxTimeWithoutRef + dramconfig.tREFI*tolerance for row in result: timeBetweenRefs = row[0] - prevrow[1] if (timeBetweenRefs > maxTimeWithoutRef): return TestFailed("Maximum time between REF commands was exceeded at {0} with {1} between REFs. Maximum allowed is {2}.".format(formatTime(row[0]), formatTime(timeBetweenRefs), formatTime(maxTimeWithoutRef))) prevrow = row return TestSuceeded() # ----------- activate checks --------------------------------------- @test def activate_to_activate_holds(connection): """Checks that all activates are far enough apart(JESD229 229, P. 27)""" cursor = connection.cursor() cursor.execute("SELECT phases.ID,PhaseBegin,TBankGroup FROM Phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") lastRow = cursor.fetchone() for currentRow in cursor: timeBetweenActivates = currentRow[1] - lastRow[1] if (currentRow[2] == lastRow[2]): minTime = dramconfig.tRRD_L else: minTime = dramconfig.tRRD_S if (timeBetweenActivates < minTime): return TestFailed("Activates with PhaseIDs {0} and {1} are {2} apart. Minimum time between two activates is {3}".format(currentRow[0], lastRow[0], formatTime(timeBetweenActivates), formatTime(minTime))) lastRow = currentRow return TestSuceeded() @test def activate_to_activate_on_same_bank_holds(connection): """Checks that all activates on the same bank are far enough apart (JEDEC 229, P. 27)""" cursor = connection.cursor() query = "SELECT Phases.ID,PhaseBegin from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName = 'ACT' AND TBANK = :bank ORDER BY PhaseBegin" for bankNumber in range(dramconfig.numberOfBanks): cursor.execute(query, {"bank": bankNumber}) lastRow = cursor.fetchone() for currentRow in cursor: timeBetweenActivates = currentRow[1] - lastRow[1] if (timeBetweenActivates < dramconfig.tRC): return TestFailed("Activates with PhaseIDs {0} and {1} are {2} apart. Minimum time between two activates is {3}, since they are on the same bank({4})".format(currentRow[0], lastRow[0], formatTime(timeBetweenActivates), dramconfig.tRC)) else: lastRow = currentRow return TestSuceeded() @test def n_activate_window_holds(connection): """Checks that the n-Activate constraint is met everywhere(JEDEC 229, P. 27)""" cursor = connection.cursor() cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") activateWindow = [] for currentRow in cursor: activateWindow.append(currentRow[1]) if (len(activateWindow) > dramconfig.nActivateWindow + 1): activateWindow.pop(0) if (activateWindow[dramconfig.nActivateWindow] - activateWindow[0] < dramconfig.tNAW): return TestFailed("Activate with PhaseID {0} and the {1} preceeding activates violate the '{1} activate window' constraint. No more than {1} activates should be in rolling time window of {2}".format(currentRow[0], dramconfig.nActivateWindow, formatTime(dramconfig.tNAW))) return TestSuceeded() # ----------- read/write checks --------------------------------------- @test def read_to_read_holds(connection): """Check that the read operations do not intefere with each other on the data bus""" cursor = connection.cursor() cursor.execute("SELECT phases.ID,PhaseBegin,TBankGroup FROM Phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE PhaseName IN ('RD','RDA') ORDER BY PhaseBegin") lastRow = cursor.fetchone() for currentRow in cursor: timeBetweenReads = currentRow[1] - lastRow[1] if (currentRow[2] == lastRow[2]): minTime = max(dramconfig.tCCD_L, dramconfig.getReadAccessTime()) else: minTime = max(dramconfig.tCCD_S, dramconfig.getReadAccessTime()) if (timeBetweenReads < minTime): return TestFailed("Reads with PhaseIDs {0} and {1} are {2} apart. Minimum time between two reads is {3}".format(currentRow[0], lastRow[0], formatTime(timeBetweenReads), minTime)) lastRow = currentRow return TestSuceeded() @test def write_to_write_holds(connection): """Check that the write operations do not intefere with each other on the data bus""" cursor = connection.cursor() cursor.execute("SELECT phases.ID,PhaseBegin,TBankGroup FROM Phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE PhaseName IN ('WR','WRA') ORDER BY PhaseBegin") lastRow = cursor.fetchone() for currentRow in cursor: timeBetweenWrites = currentRow[1] - lastRow[1] if (currentRow[2] == lastRow[2]): minTime = max(dramconfig.tCCD_L, dramconfig.getWriteAccessTime()) else: minTime = max(dramconfig.tCCD_S, dramconfig.getWriteAccessTime()) if (timeBetweenWrites < minTime): return TestFailed("Writes with PhaseIDs {0} and {1} are {2} apart. Minimum time between two writes is {3}".format(currentRow[0], lastRow[0], formatTime(timeBetweenWrites), minTime)) lastRow = currentRow return TestSuceeded() @test def write_to_read_and_read_to_write_hold(connection): """Checks that read and write operation do not interfere with each other on the data bus and that the write-to-read constraint is met""" cursor = connection.cursor() query = """SELECT Phases.ID,PhaseBegin,PhaseName,TBankGroup from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('RD','WR','RDA','WRA') ORDER BY PhaseBegin""" cursor.execute(query) lastRow = cursor.fetchone() for currentRow in cursor: if (currentRow[2] in ["RD", "RDA"] and lastRow[2] in ["WR", "WRA"]): # write to read if (currentRow[3] == lastRow[3]): tWTR = dramconfig.tWTR_L else: tWTR = dramconfig.tWTR_S minWriteToRead = dramconfig.tWL + dramconfig.getWriteAccessTime() + tWTR writeToRead = currentRow[1] - lastRow[1] if (writeToRead < minWriteToRead): return TestFailed("Read {0} starts {1} after start of write {2}. Minimum time is {3}".format(currentRow[0], formatTime(writeToRead), lastRow[0], formatTime(minWriteToRead))) elif (currentRow[2] in ["WR", "WRA"] and lastRow[2] in ["RD", "RDA"]): # read to write minReadToWrite = dramconfig.tRL + dramconfig.getReadAccessTime() - dramconfig.tWL + dramconfig.clk * 2 readToWrite = currentRow[1] - lastRow[1] if (readToWrite < minReadToWrite): return TestFailed("Write {0} starts {1} after start of read {2}. Minimum time is {3}".format(currentRow[0], formatTime(readToWrite), lastRow[0], formatTime(minWriteToRead))) lastRow = currentRow return TestSuceeded() # TODO: Check if this test still is correct! @test def read_holds_dll_constraint_after_sref(connection): """Checks that all read operations are delayed long enough after the end of the self refresh powerdown state""" cursor = connection.cursor() query = """SELECT Phases.ID,PhaseBegin,PhaseName,TBankGroup from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID AND TBank = :bank WHERE PhaseName IN ('RD', 'RDA', 'SREF') ORDER BY PhaseBegin""" for bankNumber in range(dramconfig.numberOfBanks): cursor.execute(query, {"bank": bankNumber}) lastRow = cursor.fetchone() for currentRow in cursor: if (currentRow[2] in ["RD", "RDA"] and lastRow[2] == 'SREF'): srefEndToRead = currentRow[1] - (lastRow[1] - dramconfig.clk) if (srefEndToRead < dramconfig.tXSDLL): return TestFailed("Read {0} starts {1} after end of sref {2}. Minimum time is {3}".format(currentRow[0], formatTime(srefEndToRead), lastRow[0], formatTime(dramconfig.tXSDLL))) lastRow = currentRow return TestSuceeded() @test def strict_transaction_order(connection): """Checks that all transactions are processed in the right order""" cursor = connection.cursor() query = """SELECT distinct t2.ID FROM Transactions t1, Transactions t2 where t2.ID > t1.ID and t2.DataStrobeBegin < t1.DataStrobeBegin and t1.DataStrobeBegin != 0 and t2.DataStrobeBegin !=0 and t1.TThread == t2.TThread;""" cursor.execute(query) transactions = "" for currentRow in cursor: transactions += str(currentRow[0]) + "," if (transactions != ""): if (dramconfig.scheduler == "FifoStrict"): return TestFailed("Transactions {0} is/are not in Order ".format(transactions)) else: return TestResult(True, "Transactions are not in Order, however this is okay since no FifoStrict was choosen") return TestSuceeded() # ----------- powerdown checks --------------------------------------- # @test # def sref_active_for_minimal_time(connection): # """Checks that after entering self refresh powerndown state, the state is active for a minimal time (JEDEC 229, P. 41)""" # # cursor = connection.cursor() # cursor.execute("SELECT ID, PhaseEnd-clk-PhaseBegin FROM Phases, GeneralInfo WHERE PhaseName = 'SREF'") # for currentRow in cursor: # if (currentRow[1] < dramconfig.tCKESR): # return TestFailed("SREF with ID {0} is {1} long. Minimal time in SREF is {2}".format(currentRow[0], formatTime(currentRow[1]), dramconfig.tCKESR)) # return TestSuceeded() # @test # def pdna_pdnp_active_for_minimal_time(connection): # """Checks that after entering active/precharged powerdown, the state is active for a minimal time (JEDEC 229, P. 41)""" # # cursor = connection.cursor() # cursor.execute("SELECT ID,PhaseName, PhaseEnd-PhaseBegin FROM Phases, GeneralInfo WHERE PhaseName IN ('PDNA', 'PDNP') ") # for currentRow in cursor: # if (currentRow[2] < dramconfig.tCKE): # return TestFailed("{0} with ID {1} is {2} long. Minimal time in SREF is {3}".format(currentRow[1], currentRow[0], formatTime(currentRow[2]), dramconfig.tCKE)) # return TestSuceeded() # -------------------------- interface methods -------------------- def runTests(pathToTrace): connection = sqlite3.connect(pathToTrace) dramconfig.readConfigFromFiles(connection) testResults = [] numberOfFailedTest = 0 print("================================") print("RUNNING TEST ON {0}".format(pathToTrace)) print("-----------------------------\n") for test in tests: testResult = test(connection) testName = test.__name__.replace("_", " ") testResults.append((testName, testResult.passed, testResult.message)) if (testResult.passed): print("[passed] {0}".format(testName)) else: print("[failed] {0} failed. Message: {1}".format(testName, testResult.message)) numberOfFailedTest = numberOfFailedTest + 1 print("\n-----------------------------") if (numberOfFailedTest == 0): print("All tests passed") else: print("{0} of {1} tests passed".format(len(tests) - numberOfFailedTest, len(tests))) print("================================") connection.close() return testResults if __name__ == "__main__": sys.stdout = os.fdopen(sys.stdout.fileno(), 'w') for i in range(1, len(sys.argv)): runTests(sys.argv[i])