282 lines
10 KiB
Python
282 lines
10 KiB
Python
import sys
|
|
import sqlite3
|
|
import math
|
|
|
|
class DramConfig(object):
|
|
"""Holds the timing constraints in the standard and the configuration of the DRAM"""
|
|
|
|
unitOfTime = "ns"
|
|
nActivateWindow = 0
|
|
burstLengtht = 2
|
|
clk = numberOfBanks = 0
|
|
tRP = tRAS = tRC = tRRD = tRCD = tTAW = tRL = tWL = tWTR = 0
|
|
|
|
def clkAlign(self, value):
|
|
return math.ceil(value/self.clk)*self.clk
|
|
|
|
def parseFromXml(self):
|
|
self.clk = 6
|
|
self.numberOfBanks = 8
|
|
self.nActivateWindow = 2
|
|
self.burstLength = 2
|
|
|
|
self.tRP = 3*self.clk
|
|
self.tRAS = 6*self.clk
|
|
self.tRRD = 2*self.clk
|
|
self.tRC = self.tRP + self.tRAS
|
|
self.tRCD = 3*self.clk
|
|
self.tRL = 3*self.clk
|
|
self.tWL = 1*self.clk
|
|
self.tWTR = 3*self.clk
|
|
self.tTAW = self.clkAlign(50)
|
|
|
|
def __init__(self):
|
|
self.parseFromXml()
|
|
|
|
dramconfig = DramConfig()
|
|
|
|
# ----------- 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))
|
|
|
|
# ----------- command bus checks ---------------------------------------
|
|
|
|
@test
|
|
def commands_are_clockaligned(connection):
|
|
"""Checks that all commands on the command bus are aligned to the system clock"""
|
|
passedTest = True
|
|
cursor = connection.cursor()
|
|
cursor.execute("SELECT ID, PhaseBegin, PhaseEnd from Phases WHERE PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin")
|
|
lastRow = cursor.fetchone()
|
|
|
|
for currentRow in cursor:
|
|
phaseBegin = currentRow[1]
|
|
phaseEnd = currentRow[2]
|
|
if(phaseBegin % dramconfig.clk != 0):
|
|
return TestFailed("Command with PhaseID {0} starts at {1} and is not aligned to system clock ({2})".format(
|
|
currentRow[0], formatTime(phaseBegin), formatTime(dramconfig.clk)))
|
|
if(phaseEnd % dramconfig.clk != 0):
|
|
return TestFailed("Command with PhaseID {0} end at {1} and is not aligned to system clock ({2})".format(
|
|
currentRow[0], formatTime(phaseEnd), 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()
|
|
cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName NOT IN ('REQ', 'RESP') ORDER BY PhaseBegin")
|
|
lastRow = cursor.fetchone()
|
|
|
|
for currentRow in cursor:
|
|
if(lastRow[1] != currentRow[1]):
|
|
lastRow = currentRow
|
|
else:
|
|
return TestFailed("Commandbus slot is used twice by commands with PhaseID {0} and {1}".format(lastRow[0], currentRow[0]))
|
|
return TestSuceeded()
|
|
|
|
# ----------- precharge checks ---------------------------------------
|
|
|
|
|
|
# ----------- activate checks ---------------------------------------
|
|
|
|
# @test
|
|
# def precharge_before_activate(connection):
|
|
# """Checks that all activate commands are preceeded by a precharge or a refresh or a powerdown"""
|
|
# cursor = connection.cursor()
|
|
# query = """SELECT Phases.ID, PhaseName, PhaseBegin FROM Transactions INNER JOIN Phases ON Phases.Transact = Transactions.ID
|
|
# WHERE (TBank = :bank AND PhaseName IN ('ACT','PRE','REFB')) OR PhaseName IN ('REFA','SREF','PDNP','PDNA') ORDER BY PhaseBegin"""
|
|
|
|
# for bankNumber in range(dramconfig.numberOfBanks):
|
|
# cursor.execute(query,{"bank": bankNumber})
|
|
# lastRow = cursor.fetchone()
|
|
|
|
# for currentRow in cursor:
|
|
# if(lastRow[1] != currentRow[1] or currentRow[1] != 'ACT'):
|
|
# lastRow = currentRow
|
|
# else:
|
|
# return TestFailed("No precharge between activates with PhaseID {0} and {1}".format(lastRow[0], currentRow[0]))
|
|
# return TestSuceeded()
|
|
|
|
# @test
|
|
# def activate_to_activate(connection):
|
|
# """Checks minimal time between two activates (JEDEC 229, P. 27)"""
|
|
# cursor = connection.cursor()
|
|
# cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin")
|
|
# lastRow = cursor.fetchone()
|
|
|
|
# for currentRow in cursor:
|
|
# timeBetweenActivates = currentRow[1] - lastRow[1];
|
|
# if(timeBetweenActivates < dramconfig.tRRD):
|
|
# 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), dramconfig.tRRD))
|
|
# else:
|
|
# lastRow = currentRow
|
|
# return TestSuceeded()
|
|
|
|
|
|
# @test
|
|
# def activate_to_activate_on_same_bank(connection):
|
|
# """Checks minimal time between two activates on the same bank (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(connection):
|
|
# """Checks n-Activate constraint (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.tTAW):
|
|
# 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.tTAW)))
|
|
# return TestSuceeded()
|
|
|
|
|
|
# # ----------- read checks ---------------------------------------
|
|
|
|
# @test
|
|
# def activate_to_read(connection):
|
|
# """Checks minimal time bewteen activate and following read (JEDEC 229, P. 29)"""
|
|
# cursor = connection.cursor()
|
|
# query = "SELECT Phases.ID,PhaseBegin,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('ACT','RD') AND TBANK = :bank ORDER BY PhaseBegin"
|
|
|
|
# for bankNumber in range(dramconfig.numberOfBanks):
|
|
|
|
# cursor.execute(query,{"bank": bankNumber})
|
|
# lastRow = cursor.fetchone()
|
|
|
|
# for currentRow in cursor:
|
|
# if(currentRow[2] == "RD" and lastRow[2] == "ACT"):
|
|
# actToReadTime = currentRow[1] - lastRow[1];
|
|
# if(actToReadTime < dramconfig.tRCD):
|
|
# return TestFailed("Read with PhaseID {0} starts {1} after activate {2}. Minimum activate to read time is {3}".
|
|
# format(currentRow[0],formatTime(actToReadTime),lastRow[0], formatTime(dramconfig.tRCD)))
|
|
# lastRow = currentRow
|
|
|
|
# return TestSuceeded()
|
|
|
|
# @test
|
|
# def read_to_read(connection):
|
|
# """Checks minimal time between two reads(JEDEC 229, P. 29)"""
|
|
# cursor = connection.cursor()
|
|
# cursor.execute("SELECT Phases.ID, PhaseBegin, PhaseEnd from Phases WHERE PhaseName = 'RD' ORDER BY PhaseBegin")
|
|
# lastRow = cursor.fetchone()
|
|
|
|
# for currentRow in cursor:
|
|
# if(currentRow[1] < lastRow[2]):
|
|
# timeBetweenReads = currentRow[1] - lastRow[1];
|
|
# clocksBetweenReads = round(timeBetweenReads/dramconfig.clk)
|
|
# if(clocksBetweenReads % 2 == 1):
|
|
# return TestFailed("Read with PhaseID {0} interrupts read {1}. They are {2} clocks ({3}) apart. Numbers of clock between interrupting reads must be even.".
|
|
# format(currentRow[0], lastRow[0], clocksBetweenReads, formatTime(timeBetweenReads)))
|
|
# lastRow = currentRow
|
|
|
|
# return TestSuceeded()
|
|
|
|
# @test
|
|
# def write_to_read(connection):
|
|
# """Checks minimal time between write and following read (JEDEC 229, P. 34)"""
|
|
# cursor = connection.cursor()
|
|
# query = "SELECT Phases.ID,PhaseBegin,PhaseEnd,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('RD','WR') AND TBANK = :bank ORDER BY PhaseBegin"
|
|
|
|
# for bankNumber in range(dramconfig.numberOfBanks):
|
|
|
|
# cursor.execute(query,{"bank": bankNumber})
|
|
# lastRow = cursor.fetchone()
|
|
|
|
# for currentRow in cursor:
|
|
# if(currentRow[3] == "RD" and lastRow[3] == "WR"):
|
|
# writeEndToReadBegin = currentRow[1] - lastRow[2];
|
|
# if(writeEndToReadBegin < dramconfig.tWTR ):
|
|
# return TestFailed("Read with PhaseID {0} starts {1} after end of write {2}. Minimum time between end of write and start of read is {3}".
|
|
# format(currentRow[0],formatTime(writeEndToReadBegin),lastRow[0], formatTime(dramconfig.tWTR )))
|
|
# lastRow = currentRow
|
|
|
|
# return TestSuceeded()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -------------------------- interface methods --------------------
|
|
|
|
def runTests(pathToTrace):
|
|
connection = sqlite3.connect(pathToTrace)
|
|
|
|
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("{0} passed".format(testName))
|
|
else:
|
|
print(">>>>>>{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__":
|
|
path = sys.argv[1]
|
|
runTests(path)
|
|
|