Files
DRAMSys/analyzer/analyzer/scripts/tests.py
gernhard2 f11adf51dc Relocated the python scripts. They now live in the analyzer directory and are deployed to the output folder when building the analyzer.
Major change to simulation logic in dramSys: Commands in a transaction are now scheduled one at a time, instead of
scheduling a whole transaction at once. Since single commands (e.g. Pre or Act) are not that long, refreshes are allowed to be delayed
to allow a command to finsh. Consequently, the whole loop in the ControllerCore about trying to scheduleding a transaction and aborting it when
it collides with a refresh could be ommitted. Lastly, Fifo_Strict has been added, which is a Fifo Scheduler that forces the read and write transactions, even
between different banks to be executed in order. Fifo and FR_FCFS have been modified to fit into the new scheduling logic.
2015-02-16 08:21:27 +01:00

618 lines
27 KiB
Python

import sys
import traceback
import sqlite3
import os
import xml.etree.ElementTree as ET
def getPathToConfigs():
return os.path.dirname(os.path.abspath(__file__).replace("/scripts","/configs"))
def getValueFromConfigXML(root, id):
return root.findall(".//parameter[@id='{0}']".format(id))[0].attrib['value']
def getIntValueFromConfigXML(root, id):
return int(root.findall(".//parameter[@id='{0}']".format(id))[0].attrib['value'])
def getMemconfig(connection):
cursor = connection.cursor()
cursor.execute("SELECT Memconfig FROM GeneralInfo")
result = cursor.fetchone()
memconfig = result[0]
return ET.parse(memconfig)
def getMemspec(connection):
cursor = connection.cursor()
cursor.execute("SELECT Memspec FROM GeneralInfo")
result = cursor.fetchone()
memspec = result[0]
return ET.parse(memspec)
def getClock(connection):
cursor = connection.cursor()
cursor.execute("SELECT clk, UnitOfTime FROM GeneralInfo")
result = cursor.fetchone()
return (result[0],result[1])
class DramConfig(object):
memoryType = ""
bankwiseLogic = 0
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 bw 2 succesive ACT to different banks (different bank group)
tRRD_L = 0 #.. (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 #.. (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
tXSR = 0 #min delay to row access command after srefx
tXSRDLL = 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
def readConfigFromFiles(self, connection):
print("Parsing dram configuration")
memspec = getMemspec(connection)
clkWithUnit = getClock(connection)
self.clk = clkWithUnit[0]
self.unitOfTime = clkWithUnit[1].lower()
print(getMemconfig(connection))
self.bankwiseLogic = getMemconfig(connection).findall("BankwiseLogic")[0].attrib['value']
self.numberOfBanks = getIntValueFromConfigXML(memspec, "nbrOfBanks")
self.burstLength = getIntValueFromConfigXML(memspec, "burstLength")
self.memoryType = getValueFromConfigXML(memspec, "memoryType")
self.dataRate = getIntValueFromConfigXML(memspec, "dataRate")
if(self.memoryType == "WIDEIO_SDR"):
self.nActivateWindow = 2;
self.tRP = self.clk * getIntValueFromConfigXML(memspec, "RP")
self.tRAS = self.clk * getIntValueFromConfigXML(memspec, "RAS")
self.tRC = self.clk * getIntValueFromConfigXML(memspec, "RC")
self.tRRD_S = self.clk * getIntValueFromConfigXML(memspec, "RRD")
self.tRRD_L = self.tRRD_S
self.tCCD_S = self.clk * getIntValueFromConfigXML(memspec, "CCD")
self.tCCD_L = self.tCCD_S
self.tRCD = self.clk * getIntValueFromConfigXML(memspec, "RCD")
self.tNAW = self.clk * getIntValueFromConfigXML(memspec, "TAW")
self.tRL = self.clk * getIntValueFromConfigXML(memspec, "RL")
self.tWL = self.clk * getIntValueFromConfigXML(memspec, "WL")
self.tWR = self.clk * getIntValueFromConfigXML(memspec, "WR")
self.tWTR_S = self.clk * getIntValueFromConfigXML(memspec, "WTR")
self.tWTR_L = self.tWTR_S
self.tRTP = self.clk * getIntValueFromConfigXML(memspec, "RTP");
self.tCKESR = self.clk * getIntValueFromConfigXML(memspec, "CKESR")
self.tCKE = self.clk * getIntValueFromConfigXML(memspec, "CKE")
self.tXP = self.clk * getIntValueFromConfigXML(memspec, "XP")
self.tXPDLL = self.tXP
self.tXSR = self.clk * getIntValueFromConfigXML(memspec, "XS")
self.tXSRDLL = self.tXSR
self.tAL = self.clk * getIntValueFromConfigXML(memspec, "AL")
self.tRFC = self.clk * getIntValueFromConfigXML(memspec, "RFC")
elif(self. memoryType == "DDR4"):
self.nActivateWindow = 4;
self.tRP = self.clk * getIntValueFromConfigXML(memspec, "RP");
self.tRAS = self.clk * getIntValueFromConfigXML(memspec, "RAS");
self.tRC = self.clk * getIntValueFromConfigXML(memspec, "RC");
self.tRTP = self.clk * getIntValueFromConfigXML(memspec, "RTP");
self.tRRD_S = self.clk * getIntValueFromConfigXML(memspec, "RRD_S");
self.tRRD_L = self.clk * getIntValueFromConfigXML(memspec, "RRD_L");
self.tCCD_S = self.clk * getIntValueFromConfigXML(memspec, "CCD_S");
self.tCCD_L = self.clk * getIntValueFromConfigXML(memspec, "CCD_L");
self.tRCD = self.clk * getIntValueFromConfigXML(memspec, "RCD");
self.tNAW = self.clk * getIntValueFromConfigXML(memspec, "FAW");
self.tRL = self.clk * getIntValueFromConfigXML(memspec, "RL");
self.tWL = self.clk * getIntValueFromConfigXML(memspec, "WL");
self.tWR = self.clk * getIntValueFromConfigXML(memspec, "WR");
self.tWTR_S = self.clk * getIntValueFromConfigXML(memspec, "WTR_S");
self.tWTR_L = self.clk * getIntValueFromConfigXML(memspec, "WTR_L");
self.tCKESR = self.clk * getIntValueFromConfigXML(memspec, "CKESR");
self.tCKE = self.clk * getIntValueFromConfigXML(memspec, "CKE");
self.tXP = self.clk * getIntValueFromConfigXML(memspec, "XP");
self.tXPDLL = self.clk * getIntValueFromConfigXML(memspec, "XPDLL");
self.tXSR = self.clk * getIntValueFromConfigXML(memspec, "XS");
self.tXSRDLL = self.clk * getIntValueFromConfigXML(memspec, "XSDLL");
self.tAL = self.clk * getIntValueFromConfigXML(memspec, "AL");
self.tRFC = self.clk * getIntValueFromConfigXML(memspec, "RFC");
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
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 != 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:
excludedPhases = "('REQ','RESP','PRE_ALL')"
else:
excludedPhases = "('REQ','RESP','PRE_ALL','PDNA','PDNP','SREF','AUTO_REFRESH')"
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 != 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 = {}
if(dramconfig.bankwiseLogic):
validTransitions['PRE'] = set(['ACT', 'AUTO_REFRESH'])
validTransitions['ACT'] = set(['RD', 'RDA', 'WR', 'WRA', 'PRE', 'PRE_ALL'])
validTransitions['RD'] = set(['PRE','RD','RDA', 'WR', 'WRA', 'PDNA'])
validTransitions['WR'] = set(['PRE', 'RD','RDA', 'WR', 'WRA', 'PDNA'])
validTransitions['RDA'] = set(['ACT', 'AUTO_REFRESH', 'PDNP'])
validTransitions['WRA'] = set(['ACT', 'AUTO_REFRESH', 'PDNP'])
validTransitions['AUTO_REFRESH'] = set(['ACT', 'PDNP', 'SREF'])
validTransitions['PDNA'] = set(['PRE', 'RD','RDA', 'WR', 'WRA', 'AUTO_REFRESH'])
validTransitions['PDNP'] = set(['ACT', 'AUTO_REFRESH'])
validTransitions['SREF'] = set(['ACT'])
else:
validTransitions['PRE'] = set(['ACT'])
validTransitions['PRE_ALL'] = set(['AUTO_REFRESH'])
validTransitions['ACT'] = set(['RD', 'RDA', 'WR', 'WRA', 'PRE_ALl'])
validTransitions['RD'] = set(['PRE', 'PRE_ALL','RD','RDA', 'WR', 'WRA', 'PDNA'])
validTransitions['WR'] = set(['PRE', 'PRE_ALL','RD','RDA', 'WR', 'WRA', 'PDNA'])
validTransitions['RDA'] = set(['PRE_ALL', 'ACT', 'AUTO_REFRESH', 'PDNA', 'PDNP'])
validTransitions['WRA'] = set(['PRE_ALL', 'ACT', 'AUTO_REFRESH', 'PDNA', 'PDNP'])
validTransitions['AUTO_REFRESH'] = set(['PRE_ALL', 'ACT','AUTO_REFRESH', 'PDNA', 'PDNP', 'SREF'])
validTransitions['PDNA'] = set(['PRE','PRE_ALL','ACT', 'RD', 'RDA', 'WR', 'WRA', 'AUTO_REFRESH', 'PDNA', 'PDNP'])
validTransitions['PDNP'] = set(['PRE_ALL', 'ACT', 'AUTO_REFRESH', 'PDNA', 'PDNP'])
validTransitions['SREF'] = set(['PRE_ALL', 'ACT', 'AUTO_REFRESH', 'PDNA', 'PDNP'])
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"""
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 == "PRE" or FirstPhaseName == "PRE_ALL"):
return dramconfig.tRP
elif(FirstPhaseName == "ACT"):
return dramconfig.tRCD
elif(FirstPhaseName == "RD"):
if(SecondPhaseName in ["PRE, PRE_ALL"]):
return dramconfig.tRTP
elif(SecondPhaseName in ["RD, RDA"]):
return max(dramconfig.tCCD_L, getReadAccessTime())
elif(SecondPhase in ["WR","WRA"]):
return dramconfig.tRL + getReadAccessTime() - dramconfig.tWL + 2*dramconfig.clk
elif(SecondPhase == "PDNA" ):
return dramconfig.tRL + getReadAccessTime() + dramconfig.clk
elif(FirstPhaseName == "WR"):
if(SecondPhaseName in ["PRE, PRE_ALL", "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", "PRE_ALL", "AUTO_REFRESH"]):
return dramconfig.tRTP + dramconfig.tRP
elif(SecondPhaseName in ["PDNA","PDNP"]):
return dramconfig.tRL + getReadAccessTime() + dramconfig.clk
elif(FirstPhaseName == "WRA"):
if(SecondPhaseName in ["ACT", "PRE_ALL", "AUTO_REFRESH"]):
return dramconfig.tWL + getWriteAccessTime() + dramconfig.tWR + dramconfig.tRP
elif(SecondPhaseName in ["PDNA","PDNP"]):
return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWR + dramconfig.clk
elif(FirstPhaseName == "AUTO_REFRESH"):
return dramconfig.tRFC
elif(FirstPhaseName in ["PDNA","PDNP"]):
return (FirstPhase[3] - FirstPhase[2]) + dramconfig.tXP - dramconfig.clk
elif(FirstPhaseName == "SREF"):
return (FirstPhase[3] - FirstPhase[2]) + dramconfig.tXSR - dramconfig.clk
return 0
@test
def timing_constraits_on_the_same_bank_hold(connection):
"""Checks that all transitions of two consequtive phases on the same bank meet their timing constraints"""
cursor = connection.cursor()
validTransitions = {}
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"""
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()
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"""
#phases that precharge the bank and close the rowbuffer
prechargingPhases = set(['PRE','PRE_ALL','RDA','WRA'])
#phases that require the bank to be in active state and the rowbuffer to be opened
accessingPhases = set(['RD','RDA', 'WR', 'WRA', 'PRE'])
#phases that require the bank to be in precharged state and the robuffer to be closed
idlePhases = set(['ACT', 'PDNP', 'AUTO_REFRESH', '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 == True):
return TestFailed("Phase {0}({1}) acesses a closed rowbuffer".format(currentRow[1], currentRow[0]))
if(currentRow[0] in idlePhases and rowBufferIsClosed == 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()
#----------- 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()
@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.tXSRDLL ):
return TestFailed("Read {0} starts {1} after end of sref {2}. Minimum time is {3}".
format(currentRow[0],formatTime(srefEndToRead),lastRow[0], formatTime(dramconfig.tXSRDLL)))
lastRow = currentRow
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])