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:
2025-11-17 16:13:23 +01:00
parent d2b2558fc2
commit 2b51814441
16 changed files with 347 additions and 34 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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])

View File

@@ -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]")