Merge branch 'work/vcd_export' into 'develop'
Add export option for VCD dumps See merge request ems/astdm/dram.sys!299
This commit is contained in:
@@ -108,6 +108,7 @@ add_executable(TraceAnalyzer
|
||||
scripts/metrics.py
|
||||
scripts/tests.py
|
||||
scripts/plots.py
|
||||
scripts/vcdExport.py
|
||||
scripts/sonification.pl
|
||||
scripts/dataExtractForNN.pl
|
||||
)
|
||||
|
||||
@@ -56,7 +56,9 @@ PythonCaller::PythonCaller() :
|
||||
pathToScripts(QApplication::applicationDirPath().toStdString() +
|
||||
"/../../DRAMSys/traceAnalyzer/scripts/"),
|
||||
plotsModuleName("plots"),
|
||||
plotsFunctionName("generatePlots")
|
||||
plotsFunctionName("generatePlots"),
|
||||
vcdExportModuleName("vcdExport"),
|
||||
vcdExportFunctionName("dumpVcd")
|
||||
{
|
||||
Py_Initialize();
|
||||
PyObject *sysPath = PySys_GetObject((char *)"path");
|
||||
@@ -67,6 +69,7 @@ PythonCaller::PythonCaller() :
|
||||
qDebug() << "Test:" << testModuleName.c_str() << testFunctionName.c_str();
|
||||
qDebug() << "Metric:" << metricModuleName.c_str() << metricFunctionName.c_str();
|
||||
qDebug() << "Plot:" << plotsModuleName.c_str() << plotsFunctionName.c_str();
|
||||
qDebug() << "VcdExport:" << vcdExportModuleName.c_str() << vcdExportFunctionName.c_str();
|
||||
qDebug() << "Script: " << pathToScripts.c_str();
|
||||
|
||||
pRunTestsFunction = loadFunctionFromModule(testModuleName, testFunctionName);
|
||||
@@ -75,6 +78,8 @@ PythonCaller::PythonCaller() :
|
||||
pGenPlotsFunction = loadFunctionFromModule(plotsModuleName, plotsFunctionName);
|
||||
pGetMetricsFunction = loadFunctionFromModule(metricModuleName,
|
||||
getMetricFunctionName);
|
||||
|
||||
pVcdExportFunction = loadFunctionFromModule(vcdExportModuleName, vcdExportFunctionName);
|
||||
}
|
||||
|
||||
|
||||
@@ -220,3 +225,12 @@ QString PythonCaller::generatePlotsOnTrace(QString pathToTrace)
|
||||
return outputFiles;
|
||||
|
||||
}
|
||||
|
||||
QString PythonCaller::exportAsVcd(QString pathToTrace)
|
||||
{
|
||||
PyObject *pResult = callFunctionWithStringArgument(pVcdExportFunction, pathToTrace);
|
||||
|
||||
QString dump(PyUnicode_AsUTF8(pResult));
|
||||
Py_DECREF(pResult);
|
||||
return dump;
|
||||
}
|
||||
|
||||
@@ -62,15 +62,20 @@ public:
|
||||
std::vector<std::string> getMetrics(QString pathToTrace);
|
||||
QString generatePlotsOnTrace(QString pathToTrace);
|
||||
|
||||
QString exportAsVcd(QString pathToTrace);
|
||||
|
||||
private:
|
||||
PyObject *pRunTestsFunction, *pCalculateMetricsFunction, *pGetMetricsFunction;
|
||||
PyObject *pGenPlotsFunction;
|
||||
PyObject *pVcdExportFunction;
|
||||
PyObject *loadFunctionFromModule(std::string moduleName,
|
||||
std::string functionName);
|
||||
std::string testModuleName, testFunctionName, metricModuleName,
|
||||
metricFunctionName, getMetricFunctionName, pathToScripts;
|
||||
std::string plotsModuleName;
|
||||
std::string plotsFunctionName;
|
||||
std::string vcdExportModuleName;
|
||||
std::string vcdExportFunctionName;
|
||||
|
||||
PyObject *callFunctionWithStringArgument(PyObject *function, QString argument);
|
||||
PyObject *callMetricsFunction(PyObject *function, QString argument,
|
||||
|
||||
@@ -48,6 +48,11 @@ def getClock(dbconnection):
|
||||
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 getNumberOfBanks(dbconnection):
|
||||
cursor = dbconnection.cursor()
|
||||
@@ -55,6 +60,17 @@ def getNumberOfBanks(dbconnection):
|
||||
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 getWindowSize(connection):
|
||||
cursor = connection.cursor()
|
||||
@@ -116,3 +132,9 @@ def get_total_time_in_phase(connection, phase):
|
||||
if (totalTime is None):
|
||||
totalTime = 0.0
|
||||
return totalTime
|
||||
|
||||
def getCommandLengthForPhase(connection, phase):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT " + phase + " FROM CommandLengths")
|
||||
length = cursor.fetchone()
|
||||
return length[0]
|
||||
|
||||
248
DRAMSys/traceAnalyzer/scripts/vcdExport.py
Executable file
248
DRAMSys/traceAnalyzer/scripts/vcdExport.py
Executable file
@@ -0,0 +1,248 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sqlite3
|
||||
import io
|
||||
import sys
|
||||
import enum
|
||||
import datetime
|
||||
from abc import ABC, abstractmethod
|
||||
from memUtil import *
|
||||
from vcd import VCDWriter
|
||||
|
||||
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, dataStrobeBegin, dataStrobeEnd):
|
||||
self.rank = rank
|
||||
self.bankgroup = bankgroup
|
||||
self.bank = bank
|
||||
self.dataStrobeBegin = dataStrobeBegin
|
||||
self.dataStrobeEnd = dataStrobeEnd
|
||||
|
||||
class Granularity(enum.Enum):
|
||||
Bankwise = 0
|
||||
Groupwise = 1
|
||||
Rankwise = 2
|
||||
|
||||
def getGranularity(phase):
|
||||
if phase == "PRESB" or phase == "REFSB":
|
||||
return Granularity.Groupwise
|
||||
elif phase == "PREA" or phase == "REFA" 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 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):
|
||||
setOfPhases = set()
|
||||
|
||||
setOfPhases.add(NumericSignal("REQ"))
|
||||
setOfPhases.add(NumericSignal("RESP"))
|
||||
|
||||
(ranks, bankgroups, banks) = getRanksBankgroupsBanks(connection)
|
||||
|
||||
for name in getBankNames(ranks, bankgroups, banks):
|
||||
setOfPhases.add(NumericSignal(name))
|
||||
|
||||
setOfPhases.add(StringSignal("Command_Bus"))
|
||||
setOfPhases.add(NumericSignal("Data_Bus"))
|
||||
|
||||
return setOfPhases
|
||||
|
||||
def getDataBusEvents(connection, eventDict):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT ID, DataStrobeBegin, DataStrobeEnd FROM Transactions")
|
||||
|
||||
transactions = getNumberOfTransactions(connection)
|
||||
|
||||
for transactionId, begin, end in cursor.fetchall():
|
||||
if eventDict.get(begin) == None:
|
||||
eventDict[begin] = []
|
||||
|
||||
if eventDict.get(end) == None:
|
||||
eventDict[end] = []
|
||||
|
||||
eventDict[begin].append(Event("Data_Bus", transactionId))
|
||||
eventDict[end].append(Event("Data_Bus", "z"))
|
||||
|
||||
def getCommandBusEvents(connection, eventDict, transactionDict):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT PhaseName, PhaseBegin, PhaseEnd, Transact FROM Phases")
|
||||
|
||||
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))
|
||||
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 _bank in range(banks):
|
||||
currentBanks.append((rank, bankgroup, _bank))
|
||||
|
||||
else:
|
||||
currentBanks.append((rank, bankgroup, bank))
|
||||
|
||||
for _rank, _bankgroup, _bank in currentBanks:
|
||||
currentBankName = getBankName(_rank, _bankgroup, _bank)
|
||||
|
||||
eventDict[begin].append(Event(currentBankName, transactionId))
|
||||
eventDict[end].append(Event(currentBankName, "z"))
|
||||
|
||||
def getReqAndRespPhases(connection, eventDict):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT PhaseName, PhaseBegin, PhaseEnd, Transact FROM Phases")
|
||||
|
||||
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] = []
|
||||
|
||||
eventDict[begin].append(Event(phase, transactionId))
|
||||
eventDict[end].append(Event(phase, "z"))
|
||||
|
||||
def getTransactions(connection, transactionDict):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT ID, TRank, TBankgroup, TBank, DataStrobeBegin, DataStrobeEnd FROM Transactions")
|
||||
|
||||
(ranks, bankgroups, banks) = getRanksBankgroupsBanks(connection)
|
||||
|
||||
for transactionId, rank, bankgroup, bank, dataStrobeBegin, dataStrobeEnd in cursor.fetchall():
|
||||
rank = rank % ranks
|
||||
bankgroup = bankgroup % bankgroups
|
||||
bank = bank % banks
|
||||
|
||||
currentTransaction = Transaction(rank, bankgroup, bank, dataStrobeBegin, dataStrobeEnd)
|
||||
|
||||
transactionDict[transactionId] = currentTransaction
|
||||
|
||||
def dumpVcd(pathToTrace):
|
||||
connection = sqlite3.connect(pathToTrace)
|
||||
|
||||
eventDict = {}
|
||||
transactionDict = {}
|
||||
|
||||
getTransactions(connection, transactionDict)
|
||||
signalList = getOccurringSignals(connection)
|
||||
getReqAndRespPhases(connection, eventDict)
|
||||
getDataBusEvents(connection, eventDict)
|
||||
getCommandBusEvents(connection, eventDict, transactionDict)
|
||||
|
||||
# Sort the eventDict so that VCDWriter can work with it.
|
||||
eventDict = sorted(eventDict.items(), key=lambda x: x[0])
|
||||
|
||||
with io.StringIO() as f:
|
||||
currentDate = datetime.date.today().strftime("%B %d, %Y")
|
||||
with VCDWriter(f, timescale='1 ps', 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 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:
|
||||
print("Usage:", sys.argv[0], "<tracefile>")
|
||||
else:
|
||||
dump = dumpVcd(sys.argv[1])
|
||||
print(dump)
|
||||
@@ -66,6 +66,7 @@ TraceAnalyzer::TraceAnalyzer(QWidget *parent) :
|
||||
// Disable actions except for "Open" until some file is open.
|
||||
ui->actionReload_all->setEnabled(false);
|
||||
ui->actionSaveChangesToDB->setEnabled(false);
|
||||
ui->actionExportAsVCD->setEnabled(false);
|
||||
ui->actionClose_all->setEnabled(false);
|
||||
ui->actionTest->setEnabled(false);
|
||||
ui->actionMetrics->setEnabled(false);
|
||||
@@ -120,6 +121,7 @@ void TraceAnalyzer::openTracefile(const QString &path)
|
||||
// Enable actions
|
||||
ui->actionReload_all->setEnabled(true);
|
||||
ui->actionSaveChangesToDB->setEnabled(true);
|
||||
ui->actionExportAsVCD->setEnabled(true);
|
||||
ui->actionClose_all->setEnabled(true);
|
||||
ui->actionTest->setEnabled(true);
|
||||
ui->actionMetrics->setEnabled(true);
|
||||
@@ -144,6 +146,7 @@ void TraceAnalyzer::on_actionClose_all_triggered()
|
||||
// All files closed. Disable actions except for "Open".
|
||||
ui->actionReload_all->setEnabled(false);
|
||||
ui->actionSaveChangesToDB->setEnabled(false);
|
||||
ui->actionExportAsVCD->setEnabled(false);
|
||||
ui->actionClose_all->setEnabled(false);
|
||||
ui->actionTest->setEnabled(false);
|
||||
ui->actionMetrics->setEnabled(false);
|
||||
@@ -199,6 +202,12 @@ void TraceAnalyzer::on_actionSaveChangesToDB_triggered()
|
||||
}
|
||||
}
|
||||
|
||||
void TraceAnalyzer::on_actionExportAsVCD_triggered()
|
||||
{
|
||||
TraceFileTab *traceFileTab = static_cast<TraceFileTab *>(ui->traceFileTabs->currentWidget());
|
||||
traceFileTab->exportAsVCD();
|
||||
}
|
||||
|
||||
void TraceAnalyzer::statusChanged(QString message, bool saveChangesEnable)
|
||||
{
|
||||
statusLabel->setText(message + QTime::currentTime().toString());
|
||||
|
||||
@@ -76,6 +76,7 @@ private Q_SLOTS:
|
||||
void on_actionOpen_triggered();
|
||||
void on_actionReload_all_triggered();
|
||||
void on_actionSaveChangesToDB_triggered();
|
||||
void on_actionExportAsVCD_triggered();
|
||||
void on_traceFileTabs_tabCloseRequested(int index);
|
||||
void on_actionClose_all_triggered();
|
||||
void on_actionTest_triggered();
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>22</height>
|
||||
<height>36</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
@@ -59,6 +59,7 @@
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionReload_all"/>
|
||||
<addaction name="actionSaveChangesToDB"/>
|
||||
<addaction name="actionExportAsVCD"/>
|
||||
<addaction name="actionClose_all"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionTest"/>
|
||||
@@ -141,6 +142,14 @@
|
||||
<string>Ctrl+M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportAsVCD">
|
||||
<property name="text">
|
||||
<string>Export as VCD</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+E</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -41,10 +41,12 @@
|
||||
#include "queryeditor.h"
|
||||
#include "QFileInfo"
|
||||
#include "qmessagebox.h"
|
||||
#include "businessObjects/pythoncaller.h"
|
||||
#include <iostream>
|
||||
#include <QStandardItemModel>
|
||||
#include <QString>
|
||||
#include <QItemDelegate>
|
||||
#include <QFileDialog>
|
||||
#include "qwt_plot_histogram.h"
|
||||
#include "qwt_plot_curve.h"
|
||||
#include "qwt_plot_layout.h"
|
||||
@@ -88,6 +90,23 @@ void TraceFileTab::commitChangesToDB()
|
||||
navigator->commitChangesToDB();
|
||||
}
|
||||
|
||||
void TraceFileTab::exportAsVCD()
|
||||
{
|
||||
QString filename = QFileDialog::getSaveFileName(this, "Export to VCD", "",
|
||||
"VCD files (*.vcd)");
|
||||
|
||||
PythonCaller pythonCaller;
|
||||
QString dump = pythonCaller.exportAsVcd(path);
|
||||
|
||||
if (filename != "") {
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
QTextStream out(&file);
|
||||
out << dump;
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceFileTab::setUpTraceplotScrollbar()
|
||||
{
|
||||
QObject::connect(ui->traceplotScrollbar, SIGNAL(valueChanged(int)),
|
||||
|
||||
@@ -67,6 +67,7 @@ public:
|
||||
return path;
|
||||
}
|
||||
void commitChangesToDB(void);
|
||||
void exportAsVCD();
|
||||
|
||||
private:
|
||||
QString path;
|
||||
|
||||
Reference in New Issue
Block a user