feat(python): introduce dramsys Python package
Move all Python scripts out of the Trace Analyzer into a generic dramsys package. To use the dramsys Python module in the Trace Analyzer, the user is expected to install the package into a virtual environment together with its dependencies. The Python package also makes the follwing binaries available: - dramsys_metrics - dramsys_plots - dramsys_tests - dramsys_vcd_export
This commit is contained in:
@@ -1,174 +0,0 @@
|
||||
import json
|
||||
from sqlite3.dbapi2 import Cursor
|
||||
|
||||
class MCConfig(object):
|
||||
""" Memory Controller Configuration Class
|
||||
|
||||
The format used in memory specification XML files differs from the
|
||||
format used in memory controller configuration XML files. Each class
|
||||
uses the proper format when searching for elements.
|
||||
"""
|
||||
def getValue(self, id):
|
||||
return self.jsonMCConfig['mcconfig'][id]
|
||||
|
||||
def getIntValue(self, id):
|
||||
return int(self.getValue(id))
|
||||
|
||||
def __init__(self, dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT MCconfig FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
self.jsonMCConfig = json.loads(result[0])
|
||||
|
||||
|
||||
class MemSpec(object):
|
||||
""" Memory Specification Class
|
||||
|
||||
The format used in memory specification XML files differs from the
|
||||
format used in memory configuration XML files. Each class uses the
|
||||
proper format when searching for elements.
|
||||
"""
|
||||
def getValue(self, id):
|
||||
val = self.jsonMemSpec['memspec'][id]
|
||||
return val
|
||||
|
||||
def getIntValue(self, group, id):
|
||||
val = self.jsonMemSpec['memspec'][group][id]
|
||||
return int(val)
|
||||
|
||||
def __init__(self, dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT Memspec FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
self.jsonMemSpec = json.loads(result[0])
|
||||
|
||||
|
||||
def getClock(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT clk, UnitOfTime FROM GeneralInfo")
|
||||
clock, unit = cursor.fetchone()
|
||||
return (clock, unit)
|
||||
|
||||
def getNumberOfTransactions(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT NumberOfTransactions FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
def getTraceEndTime(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT MAX(PhaseEnd) FROM Phases")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
def getNumberOfBanks(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT NumberOfBanks FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
def getNumberOfRanks(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT NumberOfRanks FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
def getNumberOfBankGroups(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT NumberOfBankGroups FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
def getPer2BankOffset(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
cursor.execute("SELECT Per2BankOffset FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
def getWindowSize(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT WindowSize FROM GeneralInfo")
|
||||
windowSize = float(cursor.fetchone()[0])
|
||||
return windowSize
|
||||
|
||||
def getRowColumnCommandBus(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT RowColumnCommandBus FROM GeneralInfo")
|
||||
return bool(cursor.fetchone()[0])
|
||||
|
||||
def getPseudoChannelMode(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT PseudoChannelMode FROM GeneralInfo")
|
||||
return bool(cursor.fetchone()[0])
|
||||
|
||||
|
||||
def maximum_data_rate(connection):
|
||||
memspec = MemSpec(connection)
|
||||
try:
|
||||
width = memspec.getIntValue("memarchitecturespec", "nbrOfDevices") * memspec.getIntValue("memarchitecturespec", "width")
|
||||
except:
|
||||
width = memspec.getIntValue("memarchitecturespec", "width")
|
||||
|
||||
# Backwards compatibility for traces where clkMHz was not yet replaced with tCK
|
||||
clk = None
|
||||
try:
|
||||
clk = 1000000 / memspec.jsonMemSpec['memspec']['memtimingspec']['tCK']
|
||||
except:
|
||||
clk = memspec.getIntValue("memtimingspec", "clkMhz")
|
||||
|
||||
rate = memspec.getIntValue("memarchitecturespec", "dataRate")
|
||||
if getPseudoChannelMode(connection):
|
||||
maxDataRate = float(clk) * float(width) * float(rate) * 2
|
||||
else:
|
||||
maxDataRate = float(clk) * float(width) * float(rate)
|
||||
return maxDataRate
|
||||
|
||||
|
||||
def getFlexibleRef(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(" SELECT FlexibleRefresh FROM GeneralInfo ")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
|
||||
def getMaxRefBurst(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(" SELECT MaxRefBurst FROM GeneralInfo ")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
|
||||
def getControllerThread(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT ControllerThread FROM GeneralInfo")
|
||||
result = cursor.fetchone()
|
||||
return result[0]
|
||||
|
||||
|
||||
def get_phase_occurrences(connection, phase):
|
||||
cursor = connection.cursor()
|
||||
query = "SELECT count(*) FROM Phases WHERE PhaseName = :phase"
|
||||
cursor.execute(query, {"phase": phase})
|
||||
r = cursor.fetchone()
|
||||
cnt = r[0]
|
||||
if cnt is None:
|
||||
cnt = 0
|
||||
return cnt
|
||||
|
||||
|
||||
def get_total_time_in_phase(connection, phase):
|
||||
cursor = connection.cursor()
|
||||
query = "SELECT SUM(PhaseEnd - PhaseBegin) / 1000 FROM Phases WHERE PhaseName = :phase"
|
||||
cursor.execute(query, {"phase": phase})
|
||||
time = cursor.fetchone()
|
||||
totalTime = time[0]
|
||||
if totalTime is None:
|
||||
totalTime = 0.0
|
||||
return totalTime
|
||||
|
||||
|
||||
def getCommandLengthForPhase(connection, phase):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(f"SELECT Length FROM CommandLengths WHERE Command=\"{phase}\"")
|
||||
length = cursor.fetchone()
|
||||
return length[0]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,657 +0,0 @@
|
||||
import sys
|
||||
import sqlite3
|
||||
import ntpath
|
||||
import os
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from memUtil import *
|
||||
from math import *
|
||||
from matplotlib.backends.backend_pdf import PdfPages
|
||||
|
||||
numberOfBins = "auto"
|
||||
latencyRange = None
|
||||
|
||||
plots = []
|
||||
|
||||
def plot(function):
|
||||
plots.append(function)
|
||||
return function
|
||||
|
||||
def getThreads(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT DISTINCT(Thread) FROM transactions WHERE Thread != 0 ORDER BY Thread")
|
||||
result = []
|
||||
for currentRow in cursor:
|
||||
result.append(currentRow[0])
|
||||
return result
|
||||
|
||||
|
||||
def createOutputFilename(tracePath, plot_type, target_measurement, file_type):
|
||||
name = ntpath.basename(tracePath)
|
||||
basename, extension = os.path.splitext(name)
|
||||
outputFileName = plot_type + '_' + target_measurement + basename + '.' + file_type
|
||||
return outputFileName, target_measurement + basename
|
||||
|
||||
|
||||
def accessDatabase(connection, query):
|
||||
cursor = connection.cursor()
|
||||
# cursor.execute(" ")
|
||||
cursor.execute(query)
|
||||
resultArray = []
|
||||
while True:
|
||||
result = cursor.fetchone()
|
||||
if (result is not None):
|
||||
resultArray.append(result[0])
|
||||
else:
|
||||
break
|
||||
return resultArray
|
||||
|
||||
|
||||
def calculate_bandwidth_util(connection, windowSize, steps, queryFull, queryEnd, queryBegin, queryPart):
|
||||
cursor = connection.cursor()
|
||||
maxDataRate = maximum_data_rate(connection)
|
||||
maximumPercentage = 0
|
||||
bandwidthPercentage = [0] * (steps+1)
|
||||
bandwidth = [0] * (steps+1)
|
||||
bandwidthPercentage[0] = 0
|
||||
bandwidth[0] = 0
|
||||
for i in range(steps):
|
||||
bandwidthPercentage[i+1] = 0
|
||||
cursor.execute(queryPart, (i*windowSize, (i+1)*windowSize))
|
||||
result = cursor.fetchone()
|
||||
if(result is None):
|
||||
cursor.execute(queryFull, (i*windowSize, (i+1)*windowSize))
|
||||
result = cursor.fetchone()
|
||||
if(result[0] is not None):
|
||||
bandwidthPercentage[i+1] += int(result[0])
|
||||
cursor.execute(queryEnd, (i*windowSize, i*windowSize, i*windowSize, (i+1)*windowSize))
|
||||
result = cursor.fetchone()
|
||||
if(result[0] is not None):
|
||||
bandwidthPercentage[i+1] += int(result[0])
|
||||
cursor.execute(queryBegin, ((i+1)*windowSize, i*windowSize, (i+1)*windowSize, (i+1)*windowSize))
|
||||
result = cursor.fetchone()
|
||||
if(result[0] is not None):
|
||||
bandwidthPercentage[i+1] += int(result[0])
|
||||
else:
|
||||
bandwidthPercentage[i+1] = windowSize
|
||||
bandwidthPercentage[i+1] = float(bandwidthPercentage[i+1]/windowSize)
|
||||
bandwidth[i+1] = float(bandwidthPercentage[i+1])*float(maxDataRate)/1024
|
||||
bandwidthPercentage[i+1] *= 100
|
||||
if(maximumPercentage < 100 and maximumPercentage < bandwidthPercentage[i+1]):
|
||||
maximumPercentage = bandwidthPercentage[i+1]
|
||||
|
||||
return bandwidthPercentage, bandwidth, maximumPercentage
|
||||
|
||||
|
||||
def memory_utilisation_window_thread(connection, tracePath, steps, thread_ID):
|
||||
## All possible cases of data transfers inside a time window:
|
||||
|
||||
# The data transfer begins and ends inside the time window
|
||||
queryFull = """
|
||||
SELECT
|
||||
SUM(DataStrobeEnd - DataStrobeBegin)
|
||||
FROM
|
||||
PHASES
|
||||
INNER JOIN
|
||||
Transactions
|
||||
ON Phases.Transact = Transactions.ID
|
||||
WHERE
|
||||
DataStrobeBegin >= ?
|
||||
AND DataStrobeEnd <= ?
|
||||
AND Thread = {0}
|
||||
"""
|
||||
|
||||
# Only the end of the data transfer is inside the time window
|
||||
queryEnd = """
|
||||
SELECT
|
||||
SUM(DataStrobeEnd - ? )
|
||||
FROM
|
||||
PHASES
|
||||
INNER JOIN
|
||||
Transactions
|
||||
ON Phases.Transact = Transactions.ID
|
||||
WHERE
|
||||
DataStrobeBegin < ?
|
||||
AND DataStrobeEnd > ?
|
||||
AND DataStrobeEnd <=?
|
||||
AND Thread = {0}
|
||||
"""
|
||||
|
||||
# Only the beginning of the data transfer is inside the time window
|
||||
queryBegin = """
|
||||
SELECT
|
||||
SUM( ? - DataStrobeBegin)
|
||||
FROM
|
||||
PHASES
|
||||
INNER JOIN
|
||||
Transactions
|
||||
ON Phases.Transact = Transactions.ID
|
||||
WHERE
|
||||
DataStrobeBegin >= ?
|
||||
AND DataStrobeBegin < ?
|
||||
AND DataStrobeEnd > ?
|
||||
AND Thread = {0}
|
||||
"""
|
||||
|
||||
# The data transfer occupies all the time window
|
||||
queryPart = """
|
||||
SELECT
|
||||
DataStrobeBegin
|
||||
FROM
|
||||
PHASES
|
||||
INNER JOIN
|
||||
Transactions
|
||||
ON Phases.Transact = Transactions.ID
|
||||
WHERE
|
||||
DataStrobeBegin <= ?
|
||||
AND DataStrobeEnd >= ?
|
||||
AND Thread = {0}
|
||||
"""
|
||||
|
||||
queryFull = queryFull.format(thread_ID)
|
||||
queryEnd = queryEnd.format(thread_ID)
|
||||
queryBegin = queryBegin.format(thread_ID)
|
||||
queryPart = queryPart.format(thread_ID)
|
||||
|
||||
windowSize = getWindowSize(connection)
|
||||
|
||||
bandwidthPercentage, bandwidth, maximumPercentage = calculate_bandwidth_util(connection, windowSize, steps, queryFull, queryEnd, queryBegin, queryPart)
|
||||
|
||||
outputFileNameBWMatlab, basename = createOutputFilename(tracePath, 'memory_utilization_percent', 'thread_' + str(thread_ID) + '_', 'txt')
|
||||
return bandwidthPercentage, bandwidth, outputFileNameBWMatlab
|
||||
|
||||
@plot
|
||||
def memory_utilisation_window(connection, tracePath, steps):
|
||||
# This function determines the average memory bandwidth over time in
|
||||
# percentage and in Gbit/s. The average bandwidth over time is done
|
||||
# dividing the time into windows of the same length and getting the average
|
||||
# bandwidth in each window. Through data from the database, DataStrobeEnd
|
||||
# and DataStrobeBegin, it is possible to access when a data transfer begins
|
||||
# or ends. Hence, it is achievable to check when a data transfer happens
|
||||
# and if it occupies or is inside a time window. Then, it is attainable to
|
||||
# determine the average bandwidth in percentage. Besides, extracting the
|
||||
# data from the memory specs, it is feasible to calculate the maximum data
|
||||
# rate of the memory and then determine the bandwidth in Gbit/s. The
|
||||
# bandwidth data are then plotted in two graphics.
|
||||
|
||||
windowSize = getWindowSize(connection)
|
||||
maxDataRate = maximum_data_rate(connection)
|
||||
|
||||
## All possible cases of data transfers inside a time window:
|
||||
|
||||
# The data transfer begins and ends inside the time window
|
||||
queryFull = """
|
||||
SELECT
|
||||
SUM(DataStrobeEnd - DataStrobeBegin)
|
||||
FROM
|
||||
Phases
|
||||
WHERE
|
||||
DataStrobeBegin >= ?
|
||||
AND DataStrobeEnd <= ?
|
||||
"""
|
||||
|
||||
# Only the end of the data transfer is inside the time window
|
||||
queryEnd = """
|
||||
SELECT
|
||||
SUM(DataStrobeEnd - ?)
|
||||
FROM
|
||||
Phases
|
||||
WHERE
|
||||
DataStrobeBegin < ?
|
||||
AND DataStrobeEnd > ?
|
||||
AND DataStrobeEnd <= ?
|
||||
"""
|
||||
|
||||
# Only the beginning of the data transfer is inside the time window
|
||||
queryBegin = """
|
||||
SELECT
|
||||
SUM(? - DataStrobeBegin)
|
||||
FROM
|
||||
Phases
|
||||
WHERE
|
||||
DataStrobeBegin >= ?
|
||||
AND DataStrobeBegin < ?
|
||||
AND DataStrobeEnd > ?
|
||||
"""
|
||||
|
||||
# The data transfer occupies all the time window
|
||||
queryPart = """
|
||||
SELECT
|
||||
DataStrobeBegin
|
||||
FROM
|
||||
Phases
|
||||
WHERE
|
||||
DataStrobeBegin <= ?
|
||||
AND DataStrobeEnd >= ?
|
||||
"""
|
||||
|
||||
bandwidthPercentage, bandwidth, maximumPercentage = calculate_bandwidth_util(connection, windowSize, steps, queryFull, queryEnd, queryBegin, queryPart)
|
||||
|
||||
outputFileNameGBPS, basename = createOutputFilename(tracePath, 'memory_utilization_gbps', '', 'pdf')
|
||||
outputFileNamePercent, basename = createOutputFilename(tracePath, 'memory_utilization_percent', '', 'pdf')
|
||||
outputFileNameBWMatlab, basename = createOutputFilename(tracePath, 'memory_utilization_percent', '', 'txt')
|
||||
outputFiles = "{0}\n\t{1}\n\t{2}\n\t".format(outputFileNameGBPS, outputFileNamePercent, outputFileNameBWMatlab)
|
||||
|
||||
# windowSize/1000: picoseconds to nanoseconds conversion
|
||||
time = np.arange(0, (steps+1)*windowSize/1000, windowSize/1000)
|
||||
maxBandwidth = [maxDataRate/1024] * (steps+1)
|
||||
|
||||
f = open(outputFileNameBWMatlab, 'w')
|
||||
for i in range(steps):
|
||||
line = "{} {}\n".format(time[i], bandwidthPercentage[i])
|
||||
f.write(line)
|
||||
|
||||
# Plot Bandwidth in Percent
|
||||
BWPercentageFigure = plt.figure()
|
||||
BWPercentageFigurePlot = BWPercentageFigure.add_subplot(111)
|
||||
BWPercentageFigurePlot.set_xlabel('Time [ns]')
|
||||
BWPercentageFigurePlot.set_ylabel('Bandwidth [%]')
|
||||
BWPercentageFigurePlot.set_ylim(-1, maximumPercentage + (10 - maximumPercentage % 10))
|
||||
BWPercentageFigurePlot.set_title('Memory Utilization in % ' + str(basename))
|
||||
BWPercentageFigurePlot.grid(True)
|
||||
BWPercentageFigurePlot.plot(time, bandwidthPercentage, label='Total')
|
||||
BWPercentageFigurePlot.legend(loc="upper left")
|
||||
|
||||
# Plot absolute bandwidth
|
||||
BWFigure = plt.figure()
|
||||
BWFigurePlot = BWFigure.add_subplot(111)
|
||||
BWFigurePlot.set_xlabel('Time [ns]')
|
||||
BWFigurePlot.set_ylabel('Bandwidth [Gibit/s]')
|
||||
BWFigurePlot.set_title('Memory Utilization in Gbps ' + str(basename))
|
||||
BWFigurePlot.grid(True)
|
||||
BWFigurePlot.plot(time, bandwidth, label='Total')
|
||||
BWFigurePlot.legend(loc="upper left")
|
||||
|
||||
# plt.ylim((-0.01)*float(maxDataRate)/1024, ((maximumPercentage + (10 - maximumPercentage%10))/100)*float(maxDataRate)/1024)
|
||||
|
||||
threads = getThreads(connection)
|
||||
if (len(threads) > 1):
|
||||
for thread in threads:
|
||||
if thread == -1:
|
||||
continue
|
||||
|
||||
threadStr = "Thread " + str(thread)
|
||||
bandwidthPercentage, bandwidth, outputFileNameBWMatlab = memory_utilisation_window_thread(connection, tracePath, steps, thread)
|
||||
BWPercentageFigurePlot.plot(time, bandwidthPercentage, label=threadStr)
|
||||
BWPercentageFigurePlot.legend(loc="upper left")
|
||||
BWFigurePlot.plot(time, bandwidth, label=threadStr)
|
||||
BWFigurePlot.legend(loc="upper left")
|
||||
f = open(outputFileNameBWMatlab, 'w')
|
||||
for i in range(steps):
|
||||
line = "{} {}\n".format(time[i], bandwidthPercentage[i])
|
||||
f.write(line)
|
||||
outputFiles += "{0}\n\t".format(outputFileNameBWMatlab)
|
||||
|
||||
# Save to PDF files
|
||||
pdf = PdfPages(outputFileNamePercent)
|
||||
pdf.savefig(BWPercentageFigure)
|
||||
pdf.close()
|
||||
BWPercentageFigure.clear()
|
||||
pdf = PdfPages(outputFileNameGBPS)
|
||||
BWFigurePlot.plot(time, maxBandwidth)
|
||||
pdf.savefig(BWFigure)
|
||||
pdf.close()
|
||||
BWFigurePlot.clear()
|
||||
plt.close()
|
||||
|
||||
return outputFiles
|
||||
|
||||
@plot
|
||||
def response_latency_window(connection, tracePath, steps):
|
||||
windowSize = getWindowSize(connection)
|
||||
|
||||
cursor = connection.cursor()
|
||||
query = """
|
||||
SELECT
|
||||
avg(RESP.PHASEBEGIN - REQ.PHASEBEGIN) / 1000
|
||||
FROM
|
||||
PHASES REQ,
|
||||
PHASES RESP
|
||||
WHERE
|
||||
REQ.PHASENAME = 'REQ'
|
||||
AND RESP.PHASENAME = 'RESP'
|
||||
AND REQ.TRANSACT = RESP.TRANSACT
|
||||
AND RESP.PHASEBEGIN >= ? and RESP.PHASEEND <= ?
|
||||
"""
|
||||
outputFileName, basename = createOutputFilename(tracePath, 'response_latency', '', 'pdf')
|
||||
outputFile = "{0}\n\t".format(outputFileName)
|
||||
|
||||
LatencyFigure = plt.figure(figsize=(10, 5), dpi=300)
|
||||
LatencyFigurePlot = LatencyFigure.add_subplot(111)
|
||||
LatencyFigurePlot.set_xlabel('Time [ns]')
|
||||
LatencyFigurePlot.set_ylabel('Response Latency [ns]')
|
||||
LatencyFigurePlot.set_title('Average Response Latency: ' + str(basename))
|
||||
LatencyFigurePlot.grid(True)
|
||||
|
||||
time = [None] * steps
|
||||
latency = [None] * steps
|
||||
|
||||
for i in range(steps):
|
||||
cursor.execute(query, (i * windowSize, (i + 1) * windowSize))
|
||||
result = cursor.fetchone()[0]
|
||||
time[i] = ((i * windowSize) - (windowSize / 2)) / 1000 # ps to ns
|
||||
latency[i] = result
|
||||
|
||||
LatencyFigurePlot.plot(time, latency, linewidth=0.5, label="Latency")
|
||||
LatencyFigurePlot.legend(loc="upper left")
|
||||
|
||||
pdf = PdfPages(outputFileName)
|
||||
pdf.savefig(LatencyFigure)
|
||||
pdf.close()
|
||||
LatencyFigurePlot.clear()
|
||||
plt.close()
|
||||
|
||||
return outputFile
|
||||
|
||||
@plot
|
||||
def wr_response_latency_window(connection, tracePath, steps):
|
||||
windowSize = getWindowSize(connection)
|
||||
|
||||
cursor = connection.cursor()
|
||||
query = """
|
||||
SELECT
|
||||
avg(RESP.PHASEBEGIN - REQ.PHASEBEGIN) / 1000
|
||||
FROM
|
||||
PHASES REQ,
|
||||
PHASES RESP
|
||||
INNER JOIN
|
||||
Transactions
|
||||
ON REQ.TRANSACT = Transactions.ID
|
||||
WHERE
|
||||
REQ.PHASENAME = 'REQ'
|
||||
AND RESP.PHASENAME = 'RESP'
|
||||
AND REQ.TRANSACT = RESP.TRANSACT
|
||||
AND RESP.PHASEBEGIN >= ? and RESP.PHASEEND <= ?
|
||||
AND Transactions.Command = "W"
|
||||
"""
|
||||
outputFileName, basename = createOutputFilename(tracePath, 'wr_response_latency', '', 'pdf')
|
||||
outputFile = "{0}\n\t".format(outputFileName)
|
||||
|
||||
LatencyFigure = plt.figure(figsize=(10, 5), dpi=300)
|
||||
LatencyFigurePlot = LatencyFigure.add_subplot(111)
|
||||
LatencyFigurePlot.set_xlabel('Time [ns]')
|
||||
LatencyFigurePlot.set_ylabel('Response Latency [ns]')
|
||||
LatencyFigurePlot.set_title('Average Write Response Latency: ' + str(basename))
|
||||
LatencyFigurePlot.grid(True)
|
||||
|
||||
time = [None] * steps
|
||||
latency = [None] * steps
|
||||
|
||||
for i in range(steps):
|
||||
cursor.execute(query, (i * windowSize, (i + 1) * windowSize))
|
||||
result = cursor.fetchone()[0]
|
||||
time[i] = ((i * windowSize) - (windowSize / 2)) / 1000 # ps to ns
|
||||
latency[i] = result
|
||||
|
||||
LatencyFigurePlot.plot(time, latency, linewidth=0.5, label="Latency")
|
||||
LatencyFigurePlot.legend(loc="upper left")
|
||||
|
||||
pdf = PdfPages(outputFileName)
|
||||
pdf.savefig(LatencyFigure)
|
||||
pdf.close()
|
||||
LatencyFigurePlot.clear()
|
||||
plt.close()
|
||||
|
||||
return outputFile
|
||||
|
||||
@plot
|
||||
def rd_response_latency_window(connection, tracePath, steps):
|
||||
windowSize = getWindowSize(connection)
|
||||
|
||||
cursor = connection.cursor()
|
||||
query = """
|
||||
SELECT
|
||||
avg(RESP.PHASEBEGIN - REQ.PHASEBEGIN) / 1000
|
||||
FROM
|
||||
PHASES REQ,
|
||||
PHASES RESP
|
||||
INNER JOIN
|
||||
Transactions
|
||||
ON REQ.TRANSACT = Transactions.ID
|
||||
WHERE
|
||||
REQ.PHASENAME = 'REQ'
|
||||
AND RESP.PHASENAME = 'RESP'
|
||||
AND REQ.TRANSACT = RESP.TRANSACT
|
||||
AND RESP.PHASEBEGIN >= ? and RESP.PHASEEND <= ?
|
||||
AND Transactions.Command = "R"
|
||||
"""
|
||||
outputFileName, basename = createOutputFilename(tracePath, 'rd_response_latency', '', 'pdf')
|
||||
outputFile = "{0}\n\t".format(outputFileName)
|
||||
|
||||
LatencyFigure = plt.figure(figsize=(10, 5), dpi=300)
|
||||
LatencyFigurePlot = LatencyFigure.add_subplot(111)
|
||||
LatencyFigurePlot.set_xlabel('Time [ns]')
|
||||
LatencyFigurePlot.set_ylabel('Response Latency [ns]')
|
||||
LatencyFigurePlot.set_title('Average Read Response Latency: ' + str(basename))
|
||||
LatencyFigurePlot.grid(True)
|
||||
|
||||
time = [None] * steps
|
||||
latency = [None] * steps
|
||||
|
||||
for i in range(steps):
|
||||
cursor.execute(query, (i * windowSize, (i + 1) * windowSize))
|
||||
result = cursor.fetchone()[0]
|
||||
time[i] = ((i * windowSize) - (windowSize / 2)) / 1000 # ps to ns
|
||||
latency[i] = result
|
||||
|
||||
LatencyFigurePlot.plot(time, latency, linewidth=0.5, label="Latency")
|
||||
LatencyFigurePlot.legend(loc="upper left")
|
||||
|
||||
pdf = PdfPages(outputFileName)
|
||||
pdf.savefig(LatencyFigure)
|
||||
pdf.close()
|
||||
LatencyFigurePlot.clear()
|
||||
plt.close()
|
||||
|
||||
return outputFile
|
||||
|
||||
@plot
|
||||
def command_bus_utilisation_window(connection, tracePath, steps):
|
||||
windowSize = getWindowSize(connection)
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Query that sums all lengths that are completely contained in the window
|
||||
query_full = """
|
||||
SELECT
|
||||
SUM(CommandLengths.Length)
|
||||
FROM
|
||||
Phases,
|
||||
GeneralInfo
|
||||
INNER JOIN
|
||||
CommandLengths
|
||||
ON Phases.PhaseName = CommandLengths.Command
|
||||
WHERE
|
||||
Phases.PhaseBegin >= ?
|
||||
AND (Phases.PhaseBegin + (CommandLengths.Length * GeneralInfo.clk)) < ?
|
||||
"""
|
||||
|
||||
# Gets the PhaseBegin of the command that reaches out of the window
|
||||
# query_border = """
|
||||
# SELECT
|
||||
# Phases.PhaseBegin
|
||||
# FROM
|
||||
# Phases,
|
||||
# GeneralInfo
|
||||
# INNER JOIN
|
||||
# CommandLengths
|
||||
# ON Phases.PhaseName = CommandLengths.Command
|
||||
# WHERE
|
||||
# Phases.PhaseBegin >= ?
|
||||
# AND Phases.PhaseBegin < ?
|
||||
# AND (Phases.PhaseBegin + (CommandLengths.Length * GeneralInfo.clk)) >= ?
|
||||
# """
|
||||
|
||||
outputFileName, basename = createOutputFilename(tracePath, 'command_bus_utilisation', '', 'pdf')
|
||||
outputFile = "{0}\n\t".format(outputFileName)
|
||||
|
||||
LatencyFigure = plt.figure(figsize=(10, 5), dpi=300)
|
||||
LatencyFigurePlot = LatencyFigure.add_subplot(111)
|
||||
LatencyFigurePlot.set_xlabel('Time [ns]')
|
||||
LatencyFigurePlot.set_ylabel('Utilization [%]')
|
||||
LatencyFigurePlot.set_title('Command Bus Utilization: ' + str(basename))
|
||||
LatencyFigurePlot.grid(True)
|
||||
|
||||
clk, _ = getClock(connection)
|
||||
|
||||
time = [None] * steps
|
||||
utilization = [None] * steps
|
||||
|
||||
for i in range(steps):
|
||||
left_limit = i * windowSize
|
||||
right_limit = (i + 1) * windowSize
|
||||
|
||||
cursor.execute(query_full, (left_limit, right_limit))
|
||||
result = cursor.fetchone()[0]
|
||||
|
||||
if (result is None):
|
||||
result = 0
|
||||
|
||||
cmdBusOccupied = result * clk
|
||||
|
||||
time[i] = ((i * windowSize) - (windowSize / 2)) / 1000 # ps to ns
|
||||
utilization[i] = cmdBusOccupied / windowSize * 100
|
||||
|
||||
if (utilization[i] > 100):
|
||||
print(left_limit, right_limit)
|
||||
|
||||
LatencyFigurePlot.plot(time, utilization, linewidth=0.5, label="Utilization")
|
||||
LatencyFigurePlot.legend(loc="upper left")
|
||||
|
||||
pdf = PdfPages(outputFileName)
|
||||
pdf.savefig(LatencyFigure)
|
||||
pdf.close()
|
||||
LatencyFigurePlot.clear()
|
||||
plt.close()
|
||||
|
||||
return outputFile
|
||||
|
||||
@plot
|
||||
def queue_window(connection, tracePath, steps):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("select max(BufferNumber) from BufferDepth;")
|
||||
bufferNumber = int(cursor.fetchone()[0]) + 1
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("select MaxBufferDepth from GeneralInfo;")
|
||||
maxBufferDepth = int(cursor.fetchone()[0])
|
||||
|
||||
outputFile = ""
|
||||
outputFileName, basename = createOutputFilename(tracePath, 'queue', '', 'pdf')
|
||||
outputFile = "{0}\n\t".format(outputFileName)
|
||||
|
||||
QueueFigure = plt.figure(figsize=(10, 5), dpi=300)
|
||||
QueueFigurePlot = QueueFigure.add_subplot(111)
|
||||
QueueFigurePlot.set_xlabel('Time [s]')
|
||||
QueueFigurePlot.set_ylabel('Queue Utilization')
|
||||
QueueFigurePlot.set_title('Average Queue Utilization: ' + str(basename))
|
||||
QueueFigurePlot.grid(True)
|
||||
|
||||
|
||||
for b in range(bufferNumber):
|
||||
cursor.execute("select Time, AverageBufferDepth from BufferDepth where BufferNumber = {};".format(b))
|
||||
time = [None] * steps
|
||||
queue = [None] * steps
|
||||
for i in range(steps-1):
|
||||
result = cursor.fetchone()
|
||||
time[i] = result[0]
|
||||
queue[i] = result[1]
|
||||
|
||||
QueueFigurePlot.plot(time, queue, linewidth=0.5, label="Queue {}".format(b))
|
||||
|
||||
QueueFigurePlot.legend(loc="upper left")
|
||||
|
||||
x1,x2,y1,y2 = QueueFigurePlot.axis()
|
||||
QueueFigurePlot.axis((x1,x2,0,maxBufferDepth))
|
||||
|
||||
pdf = PdfPages(outputFileName)
|
||||
pdf.savefig(QueueFigure)
|
||||
pdf.close()
|
||||
QueueFigurePlot.clear()
|
||||
plt.close()
|
||||
|
||||
return outputFile
|
||||
|
||||
#@plot
|
||||
def power_window(connection, tracePath, steps):
|
||||
|
||||
windowSize = getWindowSize(connection)
|
||||
|
||||
outputFile = ""
|
||||
cursor = connection.cursor()
|
||||
|
||||
cursor.execute(" SELECT * FROM Power")
|
||||
|
||||
window = float(windowSize) / pow(10, 12)
|
||||
time = np.arange(0, (windowSize * (steps + 1)) / pow(10, 6), windowSize / pow(10, 6))
|
||||
power = np.full(len(time), 0)
|
||||
|
||||
for i in range(steps):
|
||||
sum = 0.0
|
||||
counter = 0
|
||||
result = cursor.fetchone()
|
||||
|
||||
while (result is not None):
|
||||
sum += float(result[1])
|
||||
counter = counter + 1
|
||||
if(result[0] > window*i):
|
||||
break
|
||||
result = cursor.fetchone()
|
||||
|
||||
if(counter == 0):
|
||||
break
|
||||
|
||||
sum = sum / counter
|
||||
power[i+1] = sum
|
||||
|
||||
outputFileName, basename = createOutputFilename(tracePath, 'power', '', 'pdf')
|
||||
outputFile = "{0}\n\t".format(outputFileName)
|
||||
|
||||
PowFigure = plt.figure(figsize=(10, 5), dpi=300)
|
||||
PowFigurePlot = PowFigure.add_subplot(111)
|
||||
PowFigurePlot.set_xlabel('Time [us]')
|
||||
PowFigurePlot.set_ylabel('Power [mW]')
|
||||
PowFigurePlot.set_title('Power Consumption ' + str(basename))
|
||||
PowFigurePlot.grid(True)
|
||||
PowFigurePlot.plot(time, power, linewidth=0.5)
|
||||
|
||||
pdf = PdfPages(outputFileName)
|
||||
pdf.savefig(PowFigure)
|
||||
pdf.close()
|
||||
PowFigurePlot.clear()
|
||||
plt.close()
|
||||
|
||||
return outputFile
|
||||
|
||||
def generatePlots(pathToTrace):
|
||||
connection = sqlite3.connect(pathToTrace)
|
||||
|
||||
print("================================")
|
||||
print("Generating plots for {0}".format(pathToTrace))
|
||||
|
||||
outputFiles = "Output files are:\n\t"
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(" SELECT WindowSize FROM GeneralInfo")
|
||||
windowSize = float(cursor.fetchone()[0])
|
||||
if(windowSize == 0):
|
||||
outputFiles = "No output file created. Check WindowSize and EnableWindowing configs."
|
||||
else:
|
||||
traceEnd = getTraceEndTime(connection)
|
||||
steps = int(ceil(traceEnd/windowSize))
|
||||
for p in plots:
|
||||
outputFiles += p(connection, pathToTrace, steps)
|
||||
|
||||
connection.close()
|
||||
|
||||
print(outputFiles)
|
||||
|
||||
return outputFiles
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
path = sys.argv[1]
|
||||
if ((len(sys.argv)) > 2):
|
||||
latencyRange = (0, int(sys.argv[2])) # Optional argument to use a different range
|
||||
if ((len(sys.argv)) > 3):
|
||||
numberOfBins = int(sys.argv[3]) # Optional argument to use a different number of bins
|
||||
generatePlots(path)
|
||||
@@ -1,787 +0,0 @@
|
||||
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])
|
||||
@@ -1,357 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2025 Fraunhofer IESE. All rights reserved.
|
||||
#
|
||||
# Authors:
|
||||
# Derek Christ
|
||||
|
||||
|
||||
import sqlite3
|
||||
import io
|
||||
import sys
|
||||
import enum
|
||||
import math
|
||||
import datetime
|
||||
from abc import ABC, abstractmethod
|
||||
from memUtil import *
|
||||
from tqdm import tqdm
|
||||
from vcd import VCDWriter
|
||||
|
||||
TIME_STEP = 1_000_000
|
||||
|
||||
class Signal(ABC):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
@abstractmethod
|
||||
def getNeutralValue(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def getSignalType(self):
|
||||
pass
|
||||
|
||||
class NumericSignal(Signal):
|
||||
def getNeutralValue(self):
|
||||
return "z"
|
||||
|
||||
def getSignalType(self):
|
||||
return "integer"
|
||||
|
||||
class StringSignal(Signal):
|
||||
def getNeutralValue(self):
|
||||
return ""
|
||||
|
||||
def getSignalType(self):
|
||||
return "string"
|
||||
|
||||
class Event():
|
||||
def __init__(self, signal, value):
|
||||
self.signal = signal
|
||||
self.value = value
|
||||
|
||||
class Transaction():
|
||||
def __init__(self, rank, bankgroup, bank, command):
|
||||
self.rank = rank
|
||||
self.bankgroup = bankgroup
|
||||
self.bank = bank
|
||||
self.command = command
|
||||
|
||||
class Granularity(enum.Enum):
|
||||
Bankwise = 0
|
||||
TwoBankwise = 1
|
||||
Groupwise = 2
|
||||
Rankwise = 3
|
||||
|
||||
class TimeWindow():
|
||||
def __init__(self, windowSize, lastTimestamp):
|
||||
self.currentTime = 0
|
||||
self.windowSize = windowSize
|
||||
self.lastTimestamp = lastTimestamp
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
currentRange = (self.currentTime, self.currentTime + self.windowSize)
|
||||
|
||||
if self.currentTime <= self.lastTimestamp:
|
||||
self.currentTime += self.windowSize
|
||||
return currentRange
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
def numberOfIterations(self):
|
||||
return int(self.lastTimestamp / self.windowSize)
|
||||
|
||||
|
||||
def getGranularity(phase):
|
||||
if phase == "PRESB" or phase == "REFSB" or phase == "RFMSB":
|
||||
return Granularity.Groupwise
|
||||
elif phase == "REFP2B" or phase == "RFMP2B":
|
||||
return Granularity.TwoBankwise
|
||||
elif phase == "PREAB" or phase == "PREA" or phase == "REFAB" or phase == "REFA" or phase == "RFMAB" \
|
||||
or phase == "PDNA" or phase == "PDNP" or phase == "SREF":
|
||||
return Granularity.Rankwise
|
||||
else:
|
||||
return Granularity.Bankwise
|
||||
|
||||
|
||||
def getAmountOfCommandBusSpans(phase):
|
||||
if phase == "PDNA" or phase == "PDNAB" or phase == "PDNP" or phase == "PDNPB" or phase == "SREF" or phase == "SREFB":
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def getUnitOfTime(connection):
|
||||
_, unit = getClock(connection)
|
||||
return unit.lower()
|
||||
|
||||
|
||||
def getLastTimestamp(connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT DataStrobeEnd FROM Phases ORDER BY DataStrobeEnd DESC LIMIT 1")
|
||||
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
|
||||
def getRanksBankgroupsBanks(connection):
|
||||
ranks = getNumberOfRanks(connection)
|
||||
bankgroups = int(getNumberOfBankGroups(connection) / ranks)
|
||||
banks = int(getNumberOfBanks(connection) / (bankgroups * ranks))
|
||||
|
||||
return (ranks, bankgroups, banks)
|
||||
|
||||
|
||||
def getBankName(rank, bankgroup, bank):
|
||||
return "RA" + str(rank) + "_BG" + str(bankgroup) + "_BA" + str(bank)
|
||||
|
||||
|
||||
def getBankNames(ranks, bankgroups, banks):
|
||||
names = []
|
||||
for rank in range(ranks):
|
||||
for bankgroup in range(bankgroups):
|
||||
for bank in range(banks):
|
||||
names.append(getBankName(rank, bankgroup, bank))
|
||||
|
||||
return names
|
||||
|
||||
|
||||
def getOccurringSignals(connection):
|
||||
setOfSignals = set()
|
||||
|
||||
setOfSignals.add(StringSignal("REQ"))
|
||||
setOfSignals.add(StringSignal("RESP"))
|
||||
|
||||
(ranks, bankgroups, banks) = getRanksBankgroupsBanks(connection)
|
||||
|
||||
for name in getBankNames(ranks, bankgroups, banks):
|
||||
setOfSignals.add(StringSignal(name))
|
||||
|
||||
setOfSignals.add(StringSignal("Command_Bus"))
|
||||
setOfSignals.add(StringSignal("Data_Bus"))
|
||||
|
||||
return setOfSignals
|
||||
|
||||
|
||||
def getDataBusEvents(connection, eventDict, windowRange):
|
||||
beginWindow, endWindow = windowRange
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT Transactions.ID, DataStrobeBegin, DataStrobeEnd, Command FROM Phases INNER JOIN Transactions ON Transactions.ID=Phases.Transact " +
|
||||
"WHERE DataStrobeBegin BETWEEN " + str(beginWindow) + " AND " + str(endWindow) +
|
||||
" AND DataStrobeEnd BETWEEN " + str(beginWindow) + " AND " + str(endWindow))
|
||||
|
||||
for transactionId, begin, end, command in cursor.fetchall():
|
||||
if eventDict.get(begin) == None:
|
||||
eventDict[begin] = []
|
||||
|
||||
if eventDict.get(end) == None:
|
||||
eventDict[end] = []
|
||||
|
||||
eventDict[begin].append(Event("Data_Bus", command + " " + str(transactionId)))
|
||||
eventDict[end].append(Event("Data_Bus", ""))
|
||||
|
||||
|
||||
def getCommandBusEvents(connection, eventDict, transactionDict, windowRange):
|
||||
beginWindow, endWindow = windowRange
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT PhaseName, PhaseBegin, PhaseEnd, Transact FROM Phases " +
|
||||
"WHERE PhaseBegin BETWEEN " + str(beginWindow) + " AND " + str(endWindow) +
|
||||
" AND PhaseEnd BETWEEN " + str(beginWindow) + " AND " + str(endWindow))
|
||||
|
||||
for phase, phaseBegin, phaseEnd, transactionId in cursor.fetchall():
|
||||
if phase == "REQ" or phase == "RESP":
|
||||
continue
|
||||
|
||||
timespans = []
|
||||
commandLengthTime = getCommandLengthForPhase(connection, "RD") * getClock(connection)[0]
|
||||
|
||||
if getAmountOfCommandBusSpans(phase) == 1:
|
||||
timespans.append((phaseBegin, phaseBegin + commandLengthTime))
|
||||
else:
|
||||
timespans.append((phaseBegin, phaseBegin + commandLengthTime))
|
||||
timespans.append((phaseEnd - commandLengthTime, phaseEnd))
|
||||
|
||||
for begin, end in timespans:
|
||||
if eventDict.get(begin) == None:
|
||||
eventDict[begin] = []
|
||||
|
||||
if eventDict.get(end) == None:
|
||||
eventDict[end] = []
|
||||
|
||||
eventDict[begin].append(Event("Command_Bus", phase + " " + str(transactionId)))
|
||||
eventDict[end].append(Event("Command_Bus", ""))
|
||||
|
||||
currentTransaction = transactionDict[transactionId]
|
||||
|
||||
rank = currentTransaction.rank
|
||||
bankgroup = currentTransaction.bankgroup
|
||||
bank = currentTransaction.bank
|
||||
|
||||
(ranks, bankgroups, banks) = getRanksBankgroupsBanks(connection)
|
||||
|
||||
currentBanks = []
|
||||
|
||||
if getGranularity(phase) == Granularity.Rankwise:
|
||||
rank = currentTransaction.rank
|
||||
for _bankgroup in range(bankgroups):
|
||||
for _bank in range(banks):
|
||||
currentBanks.append((rank, _bankgroup, _bank))
|
||||
elif getGranularity(phase) == Granularity.Groupwise:
|
||||
for _bankgroup in range(bankgroups):
|
||||
currentBanks.append((rank, _bankgroup, bank))
|
||||
elif getGranularity(phase) == Granularity.TwoBankwise:
|
||||
currentBanks.append((rank, bankgroup, bank))
|
||||
per2BankOffset = getPer2BankOffset(connection)
|
||||
bankgroupOffset = per2BankOffset // banks
|
||||
bankOffset = per2BankOffset % banks
|
||||
currentBanks.append((rank, bankgroup + bankgroupOffset, bank + bankOffset))
|
||||
else:
|
||||
currentBanks.append((rank, bankgroup, bank))
|
||||
|
||||
for _rank, _bankgroup, _bank in currentBanks:
|
||||
currentBankName = getBankName(_rank, _bankgroup, _bank)
|
||||
|
||||
eventDict[begin].append(Event(currentBankName, phase + " " + str(transactionId)))
|
||||
eventDict[end].append(Event(currentBankName, ""))
|
||||
|
||||
|
||||
def getTransactionRange(connection, transactionRange, windowRange):
|
||||
beginWindow, endWindow = windowRange
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT Transact FROM Phases" +
|
||||
" WHERE PhaseBegin BETWEEN " + str(beginWindow) + " AND " + str(endWindow) +
|
||||
" AND PhaseEnd BETWEEN " + str(beginWindow) + " AND " + str(endWindow))
|
||||
|
||||
minTransaction, maxTransaction = float('inf'), 0
|
||||
|
||||
for transactionId in cursor.fetchall():
|
||||
maxTransaction = max(maxTransaction, transactionId[0])
|
||||
minTransaction = min(minTransaction, transactionId[0])
|
||||
|
||||
if minTransaction == float('inf'):
|
||||
minTransaction = 0
|
||||
|
||||
transactionRange.append(minTransaction)
|
||||
transactionRange.append(maxTransaction)
|
||||
|
||||
|
||||
def getReqAndRespPhases(connection, eventDict, transactionDict, windowRange):
|
||||
beginWindow, endWindow = windowRange
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT PhaseName, PhaseBegin, PhaseEnd, Transact " +
|
||||
"FROM Phases WHERE PhaseBegin BETWEEN " + str(beginWindow) + " AND " + str(endWindow) +
|
||||
" AND PhaseEnd BETWEEN " + str(beginWindow) + " AND " + str(endWindow))
|
||||
|
||||
for phase, begin, end, transactionId in cursor.fetchall():
|
||||
if phase != "REQ" and phase != "RESP":
|
||||
continue
|
||||
|
||||
if eventDict.get(begin) == None:
|
||||
eventDict[begin] = []
|
||||
|
||||
if eventDict.get(end) == None:
|
||||
eventDict[end] = []
|
||||
|
||||
currentTransaction = transactionDict[transactionId]
|
||||
command = currentTransaction.command
|
||||
|
||||
eventDict[begin].append(Event(phase, command + " " + str(transactionId)))
|
||||
eventDict[end].append(Event(phase, ""))
|
||||
|
||||
|
||||
def getTransactions(connection, transactionDict, transactionRange):
|
||||
minTransaction, maxTransaction = transactionRange
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT Transactions.ID, Rank, Bankgroup, Bank, Command FROM Transactions INNER JOIN Phases ON Transactions.ID=Phases.Transact" +
|
||||
" WHERE Transactions.ID BETWEEN " + str(minTransaction) + " AND " + str(maxTransaction))
|
||||
|
||||
for transactionId, rank, bankgroup, bank, command in cursor.fetchall():
|
||||
(ranks, bankgroups, banks) = getRanksBankgroupsBanks(connection)
|
||||
|
||||
rank = rank % ranks
|
||||
bankgroup = bankgroup % bankgroups
|
||||
bank = bank % banks
|
||||
|
||||
currentTransaction = Transaction(rank, bankgroup, bank, command)
|
||||
transactionDict[transactionId] = currentTransaction
|
||||
|
||||
|
||||
def dumpVcd(pathToTrace):
|
||||
connection = sqlite3.connect(pathToTrace)
|
||||
|
||||
signalList = getOccurringSignals(connection)
|
||||
|
||||
window = TimeWindow(TIME_STEP, getLastTimestamp(connection))
|
||||
|
||||
with io.StringIO() as f:
|
||||
currentDate = datetime.date.today().strftime("%B %d, %Y")
|
||||
unit = getUnitOfTime(connection)
|
||||
with VCDWriter(f, timescale="1" + unit, date=currentDate) as writer:
|
||||
variableDict = {}
|
||||
|
||||
for signal in signalList:
|
||||
neutralValue = signal.getNeutralValue()
|
||||
signalType = signal.getSignalType()
|
||||
variableDict[signal.name] = writer.register_var("DRAMSys", signal.name, signalType, init=neutralValue)
|
||||
|
||||
for windowRange in tqdm(window, total=window.numberOfIterations(), desc="VCD export"):
|
||||
eventDict = {}
|
||||
transactionDict = {}
|
||||
transactionRange = []
|
||||
|
||||
getTransactionRange(connection, transactionRange, windowRange)
|
||||
getTransactions(connection, transactionDict, transactionRange)
|
||||
getReqAndRespPhases(connection, eventDict, transactionDict, windowRange)
|
||||
getCommandBusEvents(connection, eventDict, transactionDict, windowRange)
|
||||
getDataBusEvents(connection, eventDict, windowRange)
|
||||
|
||||
# Sort the eventDict so that VCDWriter can work with it.
|
||||
eventDict = sorted(eventDict.items(), key=lambda x: x[0])
|
||||
|
||||
for timestamp, eventList in eventDict:
|
||||
for event in eventList:
|
||||
value_to_change = variableDict.get(event.signal)
|
||||
if value_to_change != None:
|
||||
writer.change(value_to_change, timestamp, event.value)
|
||||
|
||||
f.seek(0)
|
||||
return f.read()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 2:
|
||||
dump = dumpVcd(sys.argv[1])
|
||||
print(dump)
|
||||
elif len(sys.argv) == 3:
|
||||
dump = dumpVcd(sys.argv[1])
|
||||
with open(sys.argv[2], 'x') as outputFile:
|
||||
outputFile.write(dump)
|
||||
else:
|
||||
print("Usage: ", sys.argv[0], "<trace_file> [output_file_name]")
|
||||
Reference in New Issue
Block a user