diff --git a/.gitignore b/.gitignore index 06b27a1f..f1ef5004 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ build*/ ._.DS_Store .DS_Store *.swp +*.swo cscope* +DRAMSys/analyzer/scripts/__pycache__/ diff --git a/DRAMSys/analyzer/data/tracedb.cpp b/DRAMSys/analyzer/data/tracedb.cpp index 06f35f9b..89cd17cb 100644 --- a/DRAMSys/analyzer/data/tracedb.cpp +++ b/DRAMSys/analyzer/data/tracedb.cpp @@ -52,10 +52,18 @@ using namespace std; -TraceDB::TraceDB(QString path,bool openExisting) +TraceDB::TraceDB(QString path, bool openExisting) { this->pathToDB = path; - database = QSqlDatabase::addDatabase("QSQLITE",path); + + database = QSqlDatabase::database(path); + if (database.isValid() && database.isOpen()) { + // Close the database connection if it exists and was not closed yet. + database.removeDatabase(path); + database.close(); + } + + database = QSqlDatabase::addDatabase("QSQLITE", path); database.setDatabaseName(path); database.open(); if(!openExisting) diff --git a/DRAMSys/analyzer/scripts/memUtil.py b/DRAMSys/analyzer/scripts/memUtil.py new file mode 100644 index 00000000..2bc5a26e --- /dev/null +++ b/DRAMSys/analyzer/scripts/memUtil.py @@ -0,0 +1,57 @@ +import sys +import sqlite3 +import xml.etree.ElementTree as ET + + +class MemConfig(object): + """ Memory Configuration 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): + return self.xmlMemConfig.findall(id)[0].attrib['value'] + + def getIntValue(self, id): + return int(self.getValue(id)) + + def __init__(self, dbconnection): + cursor = dbconnection.cursor() + cursor.execute("SELECT Memconfig FROM GeneralInfo") + result = cursor.fetchone() + self.xmlMemConfig = ET.parse(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): + return self.xmlMemSpec.findall(".//parameter[@id='{0}']".format(id))[0].attrib['value'] + + def getIntValue(self, id): + return int(self.getValue(id)) + + def __init__(self, dbconnection): + cursor = dbconnection.cursor() + cursor.execute("SELECT Memspec FROM GeneralInfo") + result = cursor.fetchone() + self.xmlMemSpec = ET.parse(result[0]) + + +def getClock(dbconnection): + cursor = dbconnection.cursor() + cursor.execute("SELECT clk, UnitOfTime FROM GeneralInfo") + clock, unit = cursor.fetchone() + return (clock, unit) + + +def getNumberOfBanks(dbconnection): + cursor = dbconnection.cursor() + cursor.execute("SELECT NumberOfBanks FROM generalInfo") + result = cursor.fetchone() + return result[0] diff --git a/DRAMSys/analyzer/scripts/metrics.py b/DRAMSys/analyzer/scripts/metrics.py index 5a8e01c6..80b01972 100644 --- a/DRAMSys/analyzer/scripts/metrics.py +++ b/DRAMSys/analyzer/scripts/metrics.py @@ -1,63 +1,51 @@ import sys import sqlite3 +from memUtil import * metrics = [] threadMetrics = [] + def metric(function): - metrics.append(function) - return function + metrics.append(function) + return function + def threadMetric(function): - threadMetrics.append(function) - return function + threadMetrics.append(function) + return function + def getThreads(connection): - cursor = connection.cursor() - cursor.execute("SELECT DISTINCT(TThread) FROM transactions WHERE TThread != 0 ORDER BY TThread") - result = [] - for currentRow in cursor: - result.append(currentRow[0]) - return result + cursor = connection.cursor() + cursor.execute("SELECT DISTINCT(TThread) FROM transactions WHERE TThread != 0 ORDER BY TThread") + result = [] + for currentRow in cursor: + result.append(currentRow[0]) + return result -def getNumberOfBanks(connection): - cursor = connection.cursor() - cursor.execute("SELECT NumberOfBanks FROM generalInfo") - result = cursor.fetchone() - return result[0] -def getTraceLength(connection): - cursor = connection.cursor() - cursor.execute("SELECT TraceEnd FROM GeneralInfo") - result = cursor.fetchone() - return result[0] +# @metric +# def latency_histogram(connection): +# cursor = connection.cursor() +# cursor.execute("SELECT ((p2.PhaseEnd - p1.PhaseEnd)/1000) FROM Transactions t, Phases p1, Phases p2 WHERE t.id = p1.Transact and t.id = p2.Transact and p1.PhaseName = \"REQ\" and p2.PhaseName = \"RESP\" ") +# result = cursor.fetchall() +# #result.sort() +# #print(max(result)[0]) +# import matplotlib.pyplot as plt +# plt.hist(result, bins=max(result)[0], histtype='barstacked') +# plt.savefig('hist.png') +# return "Saved as hist.png" -def getClock(connection): - cursor = connection.cursor() - cursor.execute("SELECT clk FROM GeneralInfo") - result = cursor.fetchone() - return result[0] -#@metric -#def latency_histogram(connection): -# cursor = connection.cursor() -# cursor.execute("SELECT ((p2.PhaseEnd - p1.PhaseEnd)/1000) FROM Transactions t, Phases p1, Phases p2 WHERE t.id = p1.Transact and t.id = p2.Transact and p1.PhaseName = \"REQ\" and p2.PhaseName = \"RESP\" ") -# result = cursor.fetchall() -# #result.sort() -# #print(max(result)[0]) -# import matplotlib.pyplot as plt -# plt.hist(result, bins=max(result)[0], histtype='barstacked') -# plt.savefig('hist.png') -# return "Saved as hist.png" - -#@metric -#def average_response_latency_in_ns(connection): -# cursor = connection.cursor() -# cursor.execute("""SELECT avg(PhaseBegin-timeOfGeneration)/1000 FROM transactions INNER JOIN Phases -# ON phases.transact = transactions.ID WHERE PhaseName='RESP' """) +# @metric +# def average_response_latency_in_ns(connection): +# cursor = connection.cursor() +# cursor.execute("""SELECT avg(PhaseBegin-timeOfGeneration)/1000 FROM transactions INNER JOIN Phases ON phases.transact = transactions.ID WHERE PhaseName='RESP' """) # -# result = cursor.fetchone() -# return round(result[0],1) +# result = cursor.fetchone() +# return round(result[0],1) + @metric def trace_length_in_ns(connection): @@ -66,300 +54,372 @@ def trace_length_in_ns(connection): result = cursor.fetchone() return result[0] + @metric def average_response_latency_in_ns(connection): - cursor = connection.cursor() - cursor.execute("""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 """) + cursor = connection.cursor() + cursor.execute("""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 """) + result = cursor.fetchone() + return round(result[0], 1) - result = cursor.fetchone() - return round(result[0],1) @metric def trans_with_max_response_latency(connection): - cursor = connection.cursor() - cursor.execute(""" SELECT REQ.TRANSACT, max(RESP.PHASEBEGIN - REQ.PHASEBEGIN)/1000 FROM PHASES REQ, PHASES RESP WHERE REQ.PHASENAME = 'REQ' AND RESP.PHASENAME='RESP' AND REQ.TRANSACT = RESP.TRANSACT """) + cursor = connection.cursor() + cursor.execute(""" SELECT REQ.TRANSACT, max(RESP.PHASEBEGIN - REQ.PHASEBEGIN)/1000 FROM PHASES REQ, PHASES RESP WHERE REQ.PHASENAME = 'REQ' AND RESP.PHASENAME='RESP' AND REQ.TRANSACT = RESP.TRANSACT """) + result = cursor.fetchone() + return result[0] - result = cursor.fetchone() - return result[0] @metric def memory_active(connection): - cursor = connection.cursor() - cursor.execute(""" SELECT sum(DataStrobeEnd - DataStrobeBegin) FROM transactions """) - active = cursor.fetchone() - cursor = connection.cursor() - cursor.execute(""" SELECT clk FROM GeneralInfo """) - clk = cursor.fetchone() - return (active[0]/clk[0]) + cursor = connection.cursor() + cursor.execute(""" SELECT sum(DataStrobeEnd - DataStrobeBegin) FROM transactions """) + active = cursor.fetchone() + cursor = connection.cursor() + cursor.execute(""" SELECT clk FROM GeneralInfo """) + clk = cursor.fetchone() + return (active[0]/clk[0]) + @metric def memory_total(connection): - cursor = connection.cursor() - cursor.execute(""" SELECT max(DataStrobeEnd) FROM Transactions """) - total = cursor.fetchone() - cursor = connection.cursor() - cursor.execute(""" SELECT clk FROM GeneralInfo """) - clk = cursor.fetchone() - return (total[0]/clk[0]) + cursor = connection.cursor() + cursor.execute(""" SELECT max(DataStrobeEnd) FROM Transactions """) + total = cursor.fetchone() + cursor = connection.cursor() + cursor.execute(""" SELECT clk FROM GeneralInfo """) + clk = cursor.fetchone() + return (total[0]/clk[0]) + @metric def memory_idle(connection): - cursor = connection.cursor() - cursor.execute(""" SELECT sum(p1.PhaseEnd - p2.PhaseBegin) FROM Phases p1, Phases p2 Where p1.PhaseName = "REQ" and p2.PhaseName = "RESP" and ((p1.Transact-1) = (p2.Transact)) and (p1.PhaseEnd > p2.PhaseBegin) """) - idle = cursor.fetchone() - cursor = connection.cursor() - cursor.execute(""" SELECT clk FROM GeneralInfo """) - clk = cursor.fetchone() - if idle[0] == None: - return 0; - else: - return (idle[0]/clk[0]) + cursor = connection.cursor() + cursor.execute(""" SELECT sum(p1.PhaseEnd - p2.PhaseBegin) FROM Phases p1, Phases p2 Where p1.PhaseName = "REQ" and p2.PhaseName = "RESP" and ((p1.Transact-1) = (p2.Transact)) and (p1.PhaseEnd > p2.PhaseBegin) """) + idle = cursor.fetchone() + cursor = connection.cursor() + cursor.execute(""" SELECT clk FROM GeneralInfo """) + clk = cursor.fetchone() + if (idle[0] is None): + return 0 + else: + return (idle[0]/clk[0]) + @metric def memory_utilisation_percent_new(connection): - total = memory_total(connection) - active = memory_active(connection) - idle = memory_idle(connection) - return (active/(total-idle))*100 + total = memory_total(connection) + active = memory_active(connection) + idle = memory_idle(connection) + return (active/(total-idle))*100 + @metric def memory_utilisation_percent_old(connection): - cursor = connection.cursor() - cursor.execute(""" SELECT sum(DataStrobeEnd - DataStrobeBegin) FROM transactions """) - active = cursor.fetchone() - cursor = connection.cursor() - cursor.execute(""" SELECT max(DataStrobeEnd) FROM Transactions """) - total = cursor.fetchone() - return (active[0]/total[0])*100 + cursor = connection.cursor() + cursor.execute(""" SELECT sum(DataStrobeEnd - DataStrobeBegin) FROM transactions """) + active = cursor.fetchone() + cursor = connection.cursor() + cursor.execute(""" SELECT max(DataStrobeEnd) FROM Transactions """) + total = cursor.fetchone() + return (active[0]/total[0])*100 +def refreshMissDecision(connection, calculatedMetrics): + cursor = connection.cursor() + cursor.execute("""SELECT phases.ID,PhaseBegin,PhaseEnd,TBank FROM Phases INNER JOIN transactions on transactions.id = phases.transact WHERE PhaseName='AUTO_REFRESH' """) + queryMinREQ = """SELECT id,min(PhaseBegin) FROM (SELECT transactions.id, PhaseBegin FROM transactions + inner join ranges on ranges.id = transactions.range inner join phases on phases.transact = transactions.id + where tthread != 0 and tbank = :bank and PhaseName = "REQ" and ranges.begin<:begin and ranges.end>:end)""" -def refreshMissDecision(connection,calculatedMetrics): - cursor = connection.cursor() - cursor.execute("""SELECT phases.ID,PhaseBegin,PhaseEnd,TBank FROM Phases INNER JOIN transactions on transactions.id = phases.transact WHERE PhaseName='AUTO_REFRESH' """) - queryMinREQ = """SELECT id,min(PhaseBegin) FROM (SELECT transactions.id, PhaseBegin FROM transactions - inner join ranges on ranges.id = transactions.range inner join phases on phases.transact = transactions.id - where tthread != 0 and tbank = :bank and PhaseName = "REQ" and ranges.begin<:begin and ranges.end>:end)""" - - queryMinRESP = """SELECT id,min(PhaseBegin) FROM (SELECT transactions.id, PhaseBegin FROM transactions - inner join ranges on ranges.id = transactions.range inner join phases on phases.transact = transactions.id - where tthread != 0 and tbank = :bank and PhaseName = "RESP" and ranges.begin<:begin and ranges.end>:end) """ + queryMinRESP = """SELECT id,min(PhaseBegin) FROM (SELECT transactions.id, PhaseBegin FROM transactions + inner join ranges on ranges.id = transactions.range inner join phases on phases.transact = transactions.id + where tthread != 0 and tbank = :bank and PhaseName = "RESP" and ranges.begin<:begin and ranges.end>:end) """ - missDecisions = 0 - totalDecisios = 0 + missDecisions = 0 + totalDecisios = 0 - for refresh in cursor: - id = refresh[0] - begin = refresh[1] - end = refresh[2] - bank = refresh[3] - #print('Refresh: {0} {1} {2} {3}'.format(id,begin,end,bank)) - - cursorMinREQ = connection.cursor() - cursorMinRESP = connection.cursor() - - cursorMinREQ.execute(queryMinREQ, {"bank":bank, "begin":begin, "end":end}) - cursorMinRESP.execute(queryMinRESP, {"bank":bank, "begin":begin, "end":end}) + for refresh in cursor: + id = refresh[0] + begin = refresh[1] + end = refresh[2] + bank = refresh[3] + # print('Refresh: {0} {1} {2} {3}'.format(id,begin,end,bank)) - earliestReq = cursorMinREQ.fetchone() - earliestResp = cursorMinRESP.fetchone() - if(earliestReq[0] != None): - totalDecisios = totalDecisios + 1 + cursorMinREQ = connection.cursor() + cursorMinRESP = connection.cursor() + + cursorMinREQ.execute(queryMinREQ, {"bank": bank, "begin": begin, "end": end}) + cursorMinRESP.execute(queryMinRESP, {"bank": bank, "begin": begin, "end": end}) + + earliestReq = cursorMinREQ.fetchone() + earliestResp = cursorMinRESP.fetchone() + if (earliestReq[0] is not None): + totalDecisios = totalDecisios + 1 if(earliestReq[0] != earliestResp[0]): - missDecisions = missDecisions + 1 - #print("earliest Req: {0}| earliest Res: {1}".format(earliestReq[0], earliestResp[0])) - end + missDecisions = missDecisions + 1 + # print("earliest Req: {0}| earliest Res: {1}".format(earliestReq[0], earliestResp[0])) + if (totalDecisios != 0): + # calculatedMetrics.append(("Total Missdecisions", missDecisions)) + calculatedMetrics.append(("Relative Missdecisions", 1.0*missDecisions/totalDecisios)) + else: + calculatedMetrics.append(("Total Missdecisions", 0)) + calculatedMetrics.append(("Relative Missdecisions", 0)) - - if(totalDecisios != 0): - #calculatedMetrics.append(("Total Missdecisions", missDecisions)) - calculatedMetrics.append(("Relative Missdecisions", 1.0*missDecisions/totalDecisios)) - else: - calculatedMetrics.append(("Total Missdecisions", 0)) - calculatedMetrics.append(("Relative Missdecisions", 0)) @threadMetric def average_response_latency_in_ns(connection, thread): - cursor = connection.cursor() - query = """SELECT avg(PhaseBegin-timeOfGeneration)/1000 FROM transactions INNER JOIN Phases - ON phases.transact = transactions.ID WHERE PhaseName='RESP' AND TThread = :Thread """ + cursor = connection.cursor() + query = """SELECT avg(PhaseBegin-timeOfGeneration)/1000 FROM transactions INNER JOIN Phases + ON phases.transact = transactions.ID WHERE PhaseName='RESP' AND TThread = :Thread """ - cursor.execute(query, {"Thread": thread}) - result = cursor.fetchone() - return round(result[0],1) + cursor.execute(query, {"Thread": thread}) + result = cursor.fetchone() + return round(result[0], 1) -def addStallTime(times,begin,end): - time = begin - while time <= end: - if(time in times): - times[time] = times[time] + 1 - else: - times[time] = 1 - time = time + 1 -#@threadMetric +def addStallTime(times, begin, end): + time = begin + while time <= end: + if(time in times): + times[time] = times[time] + 1 + else: + times[time] = 1 + time = time + 1 + + +# @threadMetric def paralellism(connection, thread): - cursor = connection.cursor() - stalltimes = {} - query = """SELECT transactions.ID,MIN(phaseBegin)/:clk,MAX(phaseEnd)/:clk - from phases inner join transactions on phases.transact=transactions.id - where phaseName Not in ('REQ','RESP') and tthread=:Thread group by transactions.ID """ + cursor = connection.cursor() + stalltimes = {} + query = """SELECT transactions.ID,MIN(phaseBegin)/:clk,MAX(phaseEnd)/:clk + from phases inner join transactions on phases.transact=transactions.id + where phaseName Not in ('REQ','RESP') and tthread=:Thread group by transactions.ID """ + + clk, unit = getClock(connection) + cursor.execute(query, {"Thread": thread, "clk": clk}) + for currentRow in cursor: + addStallTime(stalltimes, currentRow[1], currentRow[2]) + para = 0 + for time in stalltimes: + para = para + stalltimes[time] + return round(para/len(stalltimes), 2) - cursor.execute(query, {"Thread": thread, "clk" : getClock(connection)}) - for currentRow in cursor: - addStallTime(stalltimes,currentRow[1],currentRow[2]) - para = 0 - for time in stalltimes: - para = para + stalltimes[time] - return round(para/len(stalltimes),2) @metric def number_of_activates(connection): - cursor = connection.cursor() - cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName = 'ACT'") - result = cursor.fetchone() - return result[0] + cursor = connection.cursor() + cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName = 'ACT'") + result = cursor.fetchone() + return result[0] + @metric def number_of_accesses(connection): - cursor = connection.cursor() - cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName IN ('REQ')") - result = cursor.fetchone() - return result[0] + cursor = connection.cursor() + cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName IN ('REQ')") + result = cursor.fetchone() + return result[0] + # @metric # def number_of_precharges(connection): -# cursor = connection.cursor() -# cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName IN ('PRE','PRE_ALL','RDA','WRA')") -# result = cursor.fetchone() -# return result[0] +# cursor = connection.cursor() +# cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName IN ('PRE','PRE_ALL','RDA','WRA')") +# result = cursor.fetchone() +# return result[0] + @metric def accesses_per_activate(connection): - cursor = connection.cursor() - cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName IN ('REQ')") - result = cursor.fetchone() - return round(result[0]*1.0/number_of_activates(connection),1) + cursor = connection.cursor() + cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName IN ('REQ')") + result = cursor.fetchone() + return round(result[0]*1.0/number_of_activates(connection), 1) + + +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 time_in_PDNA_in_ns(connection): + return get_total_time_in_phase(connection, "PDNA") + + +def time_in_PDNA_percent(connection): + return (time_in_PDNA_in_ns(connection) * 1.0 / trace_length_in_ns(connection)) * 100 + + +def time_in_PDNP_in_ns(connection): + return get_total_time_in_phase(connection, "PDNP") + + +def time_in_PDNP_percent(connection): + return (time_in_PDNP_in_ns(connection) * 1.0 / trace_length_in_ns(connection)) * 100 + + +def time_in_SREF_in_ns(connection): + return get_total_time_in_phase(connection, "SREF") + + +def time_in_SREF_percent(connection): + return (time_in_SREF_in_ns(connection) * 1.0 / trace_length_in_ns(connection)) * 100 + + +def time_in_PDNAB_in_ns(connection): + return get_total_time_in_phase(connection, "PDNAB") + + +def time_in_PDNAB_percent(connection): + totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) + return (time_in_PDNAB_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 + + +def time_in_PDNPB_in_ns(connection): + return get_total_time_in_phase(connection, "PDNPB") + + +def time_in_PDNPB_percent(connection): + totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) + return (time_in_PDNPB_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 + + +def time_in_SREFB_in_ns(connection): + return get_total_time_in_phase(connection, "SREFB") + + +def time_in_SREFB_percent(connection): + totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) + return (time_in_SREFB_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 + @metric -def timeInPowerStates(connection): - totalTimeAllBanks = getTraceLength(connection)#*getNumberOfBanks(connection) - cursor = connection.cursor() - result = [] +def time_in_power_down_states_in_ns(connection): + memconfig = MemConfig(connection) + bankwiseLogic = memconfig.getValue("BankwiseLogic") + if bankwiseLogic == "0": + totalTimeInPDNA = time_in_PDNA_in_ns(connection) + totalTimeInPDNP = time_in_PDNP_in_ns(connection) + totalTimeInSREF = time_in_SREF_in_ns(connection) + totalTimePdnStates = totalTimeInPDNA + totalTimeInPDNP + totalTimeInSREF + else: + totalTimeInPDNAB = time_in_PDNAB_in_ns(connection) + totalTimeInPDNPB = time_in_PDNPB_in_ns(connection) + totalTimeInSREFB = time_in_SREFB_in_ns(connection) + totalTimePdnStates = totalTimeInPDNAB + totalTimeInPDNPB + totalTimeInSREFB - cursor.execute("SELECT SUM(PhaseEnd-PhaseBegin) from Phases where PhaseName = 'PDNA'") - timeInPDNA = cursor.fetchone() - totalTimeInPDNA = timeInPDNA[0] - if(totalTimeInPDNA == None): - totalTimeInPDNA = 0.0 - fractionInPDNA = totalTimeInPDNA*1.0/totalTimeAllBanks - result.append(("Time in PDNA (%)", fractionInPDNA*100)) - print("{0} {1}".format(result[-1][0],result[-1][1])) + return totalTimePdnStates - cursor.execute("SELECT SUM(PhaseEnd-PhaseBegin) from Phases where PhaseName = 'PDNP'") - timeInPDNP = cursor.fetchone() - totalTimeInPDNP = timeInPDNP[0] - if(totalTimeInPDNP == None): - totalTimeInPDNP = 0.0 - fractionInPDNP = totalTimeInPDNP*1.0/totalTimeAllBanks - result.append(("Time in PDNP (%)", fractionInPDNP*100)) - print("{0} {1}".format(result[-1][0],result[-1][1])) - cursor.execute("SELECT SUM(PhaseEnd-PhaseBegin) from Phases where PhaseName = 'SREF'") - timeInSREF = cursor.fetchone() - totalTimeInSREF = timeInSREF[0] - if(totalTimeInSREF == None): - totalTimeInSREF = 0.0 - fractionInSREF = totalTimeInSREF*1.0/totalTimeAllBanks - result.append(("Time in SREF (%)", fractionInSREF*100)) - print("{0} {1}".format(result[-1][0],result[-1][1])) - result.insert(0,("Active time (%)", (1-fractionInPDNA-fractionInPDNP-fractionInSREF)*100)) - print("{0} {1}".format(result[0][0],result[0][1])) +@metric +def time_in_power_down_states_percent(connection): + memconfig = MemConfig(connection) + bankwiseLogic = memconfig.getValue("BankwiseLogic") + if bankwiseLogic == "0": + totalTimeAllBanks = trace_length_in_ns(connection) + else: + totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) + return (time_in_power_down_states_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 - return result def passRatio(connection): - numberOfPassWins = {} - numberOfPassLosses = {} + numberOfPassWins = {} + numberOfPassLosses = {} - for thread in getThreads(connection): - numberOfPassWins[thread] = 0 - numberOfPassLosses[thread] = 0 + for thread in getThreads(connection): + numberOfPassWins[thread] = 0 + numberOfPassLosses[thread] = 0 - for bankNumber in range(getNumberOfBanks(connection)): - cursor = connection.cursor() - query = """SELECT begin,end,transactions.ID,TThread, - TBank from transactions inner join ranges on transactions.range = ranges.id WHERE TBank = :Bank AND TThread != 0""" - cursor.execute(query, {"Bank":bankNumber}) + for bankNumber in range(getNumberOfBanks(connection)): + cursor = connection.cursor() + query = """SELECT begin,end,transactions.ID,TThread, + TBank from transactions inner join ranges on transactions.range = ranges.id WHERE TBank = :Bank AND TThread != 0""" + cursor.execute(query, {"Bank": bankNumber}) - for passedRequest in cursor: - passedBegin = passedRequest[0] - passedEnd = passedRequest[1] - passedId = passedRequest[2] - passedThread = passedRequest[3] - - cursor2 = connection.cursor() - query2 = """SELECT begin,end,transactions.ID,TThread, - TBank from transactions inner join ranges on transactions.range = ranges.id - WHERE TBank = :Bank AND :Begin 0): - passRatio = numberOfPassWins[thread]*1.0/(numberOfPassWins[thread]+numberOfPassLosses[thread]) - else: - passRatio = 0.5 - print("Thread {0} passed other threads {1} times and was passed {2} times. Pass ratio is {3}".format - (thread, numberOfPassWins[thread], numberOfPassLosses[thread],passRatio)) - result.append(("Thread {0} pass ratio".format(thread), passRatio)) - return result + for passedRequest in cursor: + passedBegin = passedRequest[0] + passedEnd = passedRequest[1] + passedId = passedRequest[2] + passedThread = passedRequest[3] + cursor2 = connection.cursor() + query2 = """SELECT begin,end,transactions.ID,TThread, + TBank from transactions inner join ranges on transactions.range = ranges.id + WHERE TBank = :Bank AND :Begin 0): + passRatio = numberOfPassWins[thread]*1.0/(numberOfPassWins[thread]+numberOfPassLosses[thread]) + else: + passRatio = 0.5 + print("Thread {0} passed other threads {1} times and was passed {2} times. Pass ratio is {3}".format(thread, numberOfPassWins[thread], numberOfPassLosses[thread], passRatio)) + result.append(("Thread {0} pass ratio".format(thread), passRatio)) + return result def calculateMetrics(pathToTrace): - connection = sqlite3.connect(pathToTrace) - calculatedMetrics = [] + calculatedMetrics = [] + connection = sqlite3.connect(pathToTrace) - print("================================") - print("Calculating metrics for {0}".format(pathToTrace)) + memconfig = MemConfig(connection) + bankwiseLogic = memconfig.getValue("BankwiseLogic") + if bankwiseLogic == "0": + pdnMetrics = [time_in_PDNA_in_ns, time_in_PDNA_percent, time_in_PDNP_in_ns, time_in_PDNP_percent, time_in_SREF_in_ns, time_in_SREF_percent] + else: + pdnMetrics = [time_in_PDNAB_in_ns, time_in_PDNAB_percent, time_in_PDNPB_in_ns, time_in_PDNPB_percent, time_in_SREFB_in_ns, time_in_SREFB_percent] - if(len(getThreads(connection))==1): - for metric in metrics: - res = (metric.__name__.replace("_"," "), metric(connection)) - print("{0}: {1}".format(res[0],res[1])) - calculatedMetrics.append(res) + for m in pdnMetrics: + if m not in metrics: + metrics.append(m) - if(len(getThreads(connection))>1): - # for thread in getThreads(connection): - # for metric in threadMetrics: - # res = ("Thread " + str(thread) + " " + metric.__name__.replace("_"," "), metric(connection, thread)) - # print("{0}: {1}".format(res[0],res[1])) - # calculatedMetrics.append(res) - calculatedMetrics.extend(passRatio(connection)) - #refreshMissDecision(connection, calculatedMetrics) + print("================================") + print("Calculating metrics for {0}".format(pathToTrace)) - #calculatedMetrics.extend(timeInPowerStates(connection)) - #print(calculatedMetrics[-1]) - #print(calculatedMetrics[-2]) - - #refreshMissDecision(connection, calculatedMetrics) - print(calculatedMetrics[-1]) - print(calculatedMetrics[-2]) - connection.close() - return calculatedMetrics + print("Number of threads is {0}".format(len(getThreads(connection)))) + + if (len(getThreads(connection)) == 1): + for metric in metrics: + res = (metric.__name__.replace("_", " "), metric(connection)) + print("{0}: {1}".format(res[0], res[1])) + calculatedMetrics.append(res) + + if (len(getThreads(connection)) > 1): + # for thread in getThreads(connection): + # for metric in threadMetrics: + # res = ("Thread " + str(thread) + " " + metric.__name__.replace("_"," "), metric(connection, thread)) + # print("{0}: {1}".format(res[0],res[1])) + # calculatedMetrics.append(res) + calculatedMetrics.extend(passRatio(connection)) + # refreshMissDecision(connection, calculatedMetrics) + + if (len(getThreads(connection)) == 0): + res = ("No accesses were performed for this channel, number of metrics generated", 0.0) + calculatedMetrics.append(res) + + # refreshMissDecision(connection, calculatedMetrics) + connection.close() + return calculatedMetrics if __name__ == "__main__": - path = sys.argv[1] - calculateMetrics(path) - + path = sys.argv[1] + calculateMetrics(path) diff --git a/DRAMSys/analyzer/scripts/tests.py b/DRAMSys/analyzer/scripts/tests.py index 37d9baa1..ee844236 100644 --- a/DRAMSys/analyzer/scripts/tests.py +++ b/DRAMSys/analyzer/scripts/tests.py @@ -3,35 +3,8 @@ import traceback import sqlite3 import os import xml.etree.ElementTree as ET +from memUtil import * -def getPathToConfigs(): - return os.path.dirname(os.path.abspath(__file__).replace("/scripts","/configs")) - -def getValueFromConfigXML(root, id): - return root.findall(".//parameter[@id='{0}']".format(id))[0].attrib['value'] - -def getIntValueFromConfigXML(root, id): - return int(root.findall(".//parameter[@id='{0}']".format(id))[0].attrib['value']) - -def getMemconfig(connection): - cursor = connection.cursor() - cursor.execute("SELECT Memconfig FROM GeneralInfo") - result = cursor.fetchone() - memconfig = result[0] - return ET.parse(memconfig) - -def getMemspec(connection): - cursor = connection.cursor() - cursor.execute("SELECT Memspec FROM GeneralInfo") - result = cursor.fetchone() - memspec = result[0] - return ET.parse(memspec) - -def getClock(connection): - cursor = connection.cursor() - cursor.execute("SELECT clk, UnitOfTime FROM GeneralInfo") - result = cursor.fetchone() - return (result[0],result[1]) class DramConfig(object): memoryType = "" @@ -43,98 +16,99 @@ class DramConfig(object): 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) + tRP = 0 # precharge-time (pre -> act same bank) + tRAS = 0 # active-time (act -> pre same bank) + tRC = 0 # RAS-cycle-time (min time bw 2 succesive ACT to same bank) + tCCD_S = 0 # TODO: relevant? max(bl, tCCD) tCCD_L = 0 - tRTP = 0 #Read to precharge - tRRD_S = 0 #min time bw 2 succesive ACT to different banks (different bank group) - tRRD_L = 0 #.. (same bank group) - tRCD = 0 #act -> read/write - tNAW = 0 #n activate window - tRL = 0 #read latency (read command start to data strobe) - tWL = 0 #write latency - tWR = 0 #write recovery (write to precharge) - tWTR_S = 0 #write to read (different bank group) - tWTR_L = 0 #.. (same bank group) - tCKESR = 0 #min time in sref - tCKE = 0 #min time in pdna or pdnp - tXP = 0 #min delay to row access command after pdnpx pdnax - tXPDLL = 0 #min delay to row access command after pdnpx pdnax for dll commands - tXSR = 0 #min delay to row access command after srefx - tXSRDLL = 0 #min delay to row access command after srefx for dll commands - tAL = 0 #additive delay (delayed execution in dram) - tRFC = 0 #min ref->act delay - + 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 + tXSR = 0 # min delay to row access command after srefx + tXSRDLL = 0 # min delay to row access command after srefx for dll commands + tAL = 0 # additive delay (delayed execution in dram) + tRFC = 0 # min ref->act delay def readConfigFromFiles(self, connection): print("Parsing dram configuration") - memspec = getMemspec(connection) + + memconfig = MemConfig(connection) + memspec = MemSpec(connection) clkWithUnit = getClock(connection) self.clk = clkWithUnit[0] self.unitOfTime = clkWithUnit[1].lower() - print(getMemconfig(connection)) - self.bankwiseLogic = getMemconfig(connection).findall("BankwiseLogic")[0].attrib['value'] - self.scheduler = getMemconfig(connection).findall("Scheduler")[0].attrib['value'] - self.numberOfBanks = getIntValueFromConfigXML(memspec, "nbrOfBanks") - self.burstLength = getIntValueFromConfigXML(memspec, "burstLength") - self.memoryType = getValueFromConfigXML(memspec, "memoryType") - self.dataRate = getIntValueFromConfigXML(memspec, "dataRate") + self.bankwiseLogic = memconfig.getValue("BankwiseLogic") + self.scheduler = memconfig.getValue("Scheduler") - if(self.memoryType == "WIDEIO_SDR"): - self.nActivateWindow = 2; - self.tRP = self.clk * getIntValueFromConfigXML(memspec, "RP") - self.tRAS = self.clk * getIntValueFromConfigXML(memspec, "RAS") - self.tRC = self.clk * getIntValueFromConfigXML(memspec, "RC") - self.tRRD_S = self.clk * getIntValueFromConfigXML(memspec, "RRD") + self.numberOfBanks = memspec.getIntValue("nbrOfBanks") + self.burstLength = memspec.getIntValue("burstLength") + self.memoryType = memspec.getValue("memoryType") + self.dataRate = memspec.getIntValue("dataRate") + + if (self.memoryType == "WIDEIO_SDR"): + self.nActivateWindow = 2 + self.tRP = self.clk * memspec.getIntValue("RP") + self.tRAS = self.clk * memspec.getIntValue("RAS") + self.tRC = self.clk * memspec.getIntValue("RC") + self.tRRD_S = self.clk * memspec.getIntValue("RRD") self.tRRD_L = self.tRRD_S - self.tCCD_S = self.clk * getIntValueFromConfigXML(memspec, "CCD") + self.tCCD_S = self.clk * memspec.getIntValue("CCD") self.tCCD_L = self.tCCD_S - self.tRCD = self.clk * getIntValueFromConfigXML(memspec, "RCD") - self.tNAW = self.clk * getIntValueFromConfigXML(memspec, "TAW") - self.tRL = self.clk * getIntValueFromConfigXML(memspec, "RL") - self.tWL = self.clk * getIntValueFromConfigXML(memspec, "WL") - self.tWR = self.clk * getIntValueFromConfigXML(memspec, "WR") - self.tWTR_S = self.clk * getIntValueFromConfigXML(memspec, "WTR") + self.tRCD = self.clk * memspec.getIntValue("RCD") + self.tNAW = self.clk * memspec.getIntValue("TAW") + self.tRL = self.clk * memspec.getIntValue("RL") + self.tWL = self.clk * memspec.getIntValue("WL") + self.tWR = self.clk * memspec.getIntValue("WR") + self.tWTR_S = self.clk * memspec.getIntValue("WTR") self.tWTR_L = self.tWTR_S - self.tRTP = self.clk * getIntValueFromConfigXML(memspec, "RTP"); - self.tCKESR = self.clk * getIntValueFromConfigXML(memspec, "CKESR") - self.tCKE = self.clk * getIntValueFromConfigXML(memspec, "CKE") - self.tXP = self.clk * getIntValueFromConfigXML(memspec, "XP") + self.tRTP = self.clk * memspec.getIntValue("RTP") + self.tCKESR = self.clk * memspec.getIntValue("CKESR") + self.tCKE = self.clk * memspec.getIntValue("CKE") + self.tXP = self.clk * memspec.getIntValue("XP") self.tXPDLL = self.tXP - self.tXSR = self.clk * getIntValueFromConfigXML(memspec, "XS") + self.tXSR = self.clk * memspec.getIntValue("XS") self.tXSRDLL = self.tXSR - self.tAL = self.clk * getIntValueFromConfigXML(memspec, "AL") - self.tRFC = self.clk * getIntValueFromConfigXML(memspec, "RFC") + self.tAL = self.clk * memspec.getIntValue("AL") + self.tRFC = self.clk * memspec.getIntValue("RFC") - elif(self. memoryType == "DDR4"): - self.nActivateWindow = 4; - self.tRP = self.clk * getIntValueFromConfigXML(memspec, "RP"); - self.tRAS = self.clk * getIntValueFromConfigXML(memspec, "RAS"); - self.tRC = self.clk * getIntValueFromConfigXML(memspec, "RC"); - self.tRTP = self.clk * getIntValueFromConfigXML(memspec, "RTP"); - self.tRRD_S = self.clk * getIntValueFromConfigXML(memspec, "RRD_S"); - self.tRRD_L = self.clk * getIntValueFromConfigXML(memspec, "RRD_L"); - self.tCCD_S = self.clk * getIntValueFromConfigXML(memspec, "CCD_S"); - self.tCCD_L = self.clk * getIntValueFromConfigXML(memspec, "CCD_L"); - self.tRCD = self.clk * getIntValueFromConfigXML(memspec, "RCD"); - self.tNAW = self.clk * getIntValueFromConfigXML(memspec, "FAW"); - self.tRL = self.clk * getIntValueFromConfigXML(memspec, "RL"); - self.tWL = self.clk * getIntValueFromConfigXML(memspec, "WL"); - self.tWR = self.clk * getIntValueFromConfigXML(memspec, "WR"); - self.tWTR_S = self.clk * getIntValueFromConfigXML(memspec, "WTR_S"); - self.tWTR_L = self.clk * getIntValueFromConfigXML(memspec, "WTR_L"); - self.tCKESR = self.clk * getIntValueFromConfigXML(memspec, "CKESR"); - self.tCKE = self.clk * getIntValueFromConfigXML(memspec, "CKE"); - self.tXP = self.clk * getIntValueFromConfigXML(memspec, "XP"); - self.tXPDLL = self.clk * getIntValueFromConfigXML(memspec, "XPDLL"); - self.tXSR = self.clk * getIntValueFromConfigXML(memspec, "XS"); - self.tXSRDLL = self.clk * getIntValueFromConfigXML(memspec, "XSDLL"); - self.tAL = self.clk * getIntValueFromConfigXML(memspec, "AL"); - self.tRFC = self.clk * getIntValueFromConfigXML(memspec, "RFC"); + elif (self. memoryType == "DDR4"): + self.nActivateWindow = 4 + self.tRP = self.clk * memspec.getIntValue("RP") + self.tRAS = self.clk * memspec.getIntValue("RAS") + self.tRC = self.clk * memspec.getIntValue("RC") + self.tRTP = self.clk * memspec.getIntValue("RTP") + self.tRRD_S = self.clk * memspec.getIntValue("RRD_S") + self.tRRD_L = self.clk * memspec.getIntValue("RRD_L") + self.tCCD_S = self.clk * memspec.getIntValue("CCD_S") + self.tCCD_L = self.clk * memspec.getIntValue("CCD_L") + self.tRCD = self.clk * memspec.getIntValue("RCD") + self.tNAW = self.clk * memspec.getIntValue("FAW") + self.tRL = self.clk * memspec.getIntValue("RL") + self.tWL = self.clk * memspec.getIntValue("WL") + self.tWR = self.clk * memspec.getIntValue("WR") + self.tWTR_S = self.clk * memspec.getIntValue("WTR_S") + self.tWTR_L = self.clk * memspec.getIntValue("WTR_L") + self.tCKESR = self.clk * memspec.getIntValue("CKESR") + self.tCKE = self.clk * memspec.getIntValue("CKE") + self.tXP = self.clk * memspec.getIntValue("XP") + self.tXPDLL = self.clk * memspec.getIntValue("XPDLL") + self.tXSR = self.clk * memspec.getIntValue("XS") + self.tXSRDLL = self.clk * memspec.getIntValue("XSDLL") + self.tAL = self.clk * memspec.getIntValue("AL") + self.tRFC = self.clk * memspec.getIntValue("RFC") else: raise Exception("MemoryType not supported yet. Insert a coin into the coin machine and try again") @@ -144,7 +118,7 @@ class DramConfig(object): return math.ceil(1.0*value/self.clk)*self.clk def getWriteAccessTime(self): - if(self.dataRate == 1): + if (self.dataRate == 1): return self.clk*(self.burstLength - 1) elif (self.memoryType == "DDR4"): return self.clk*self.burstLength/self.dataRate @@ -152,44 +126,54 @@ class DramConfig(object): 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 = ''): + + def __init__(self, passed=True, message=''): self.passed = passed self.message = message + def TestSuceeded(): return TestResult() + def TestFailed(message): - return TestResult(False,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""" @@ -200,11 +184,11 @@ def commands_are_clockaligned(connection): result = cursor.fetchone() - if(result != None): - return TestFailed("Command with PhaseID {0} starts at {1} and ends at. One of those times. is not aligned to system clock ({2})" - .format(result[0], formatTime(result[1]), formatTime(result[2]), formatTime(dramconfig.clk))) + 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""" @@ -221,7 +205,7 @@ def commandbus_slots_are_used_once(connection): cursor.execute(query) result = cursor.fetchone() - if(result != None): + if (result is not None): return TestFailed("Slot on commandbus at time {0} is used multiple times".format(formatTime(result[0]))) return TestSuceeded() @@ -235,37 +219,36 @@ def phase_transitions_are_valid(connection): # validTransitions tells you which phases are allowed to follow the last transaction. - if(dramconfig.bankwiseLogic == "1"): + if (dramconfig.bankwiseLogic == "1"): validTransitions['PRE'] = set(['ACT', 'REFB']) validTransitions['ACT'] = set(['RD', 'RDA', 'WR', 'WRA', 'PRE', 'PRE_ALL']) - validTransitions['RD'] = set(['PRE','RD','RDA', 'WR', 'WRA', 'PDNAB']) - validTransitions['WR'] = set(['PRE', 'RD','RDA', 'WR', 'WRA', 'PDNAB']) + validTransitions['RD'] = set(['PRE', 'RD', 'RDA', 'WR', 'WRA', 'PDNAB']) + validTransitions['WR'] = set(['PRE', 'RD', 'RDA', 'WR', 'WRA', 'PDNAB']) validTransitions['RDA'] = set(['ACT', 'REFB', 'PDNPB']) validTransitions['WRA'] = set(['ACT', 'REFB', 'PDNPB']) validTransitions['REFB'] = set(['ACT', 'PDNPB', 'SREFB']) - validTransitions['PDNAB'] = set(['PRE', 'RD','RDA', 'WR', 'WRA', 'REFB']) + validTransitions['PDNAB'] = set(['PRE', 'RD', 'RDA', 'WR', 'WRA', 'REFB']) validTransitions['PDNPB'] = set(['ACT', 'REFB']) validTransitions['SREFB'] = set(['ACT']) else: - validTransitions['PRE'] = set(['ACT','PRE_ALL']) + validTransitions['PRE'] = set(['ACT', 'PRE_ALL']) validTransitions['PRE_ALL'] = set(['REFA']) validTransitions['ACT'] = set(['RD', 'RDA', 'WR', 'WRA', 'PRE_ALL']) - validTransitions['RD'] = set(['PRE', 'PRE_ALL','RD','RDA', 'WR', 'WRA', 'PDNA']) - validTransitions['WR'] = set(['PRE', 'PRE_ALL','RD','RDA', 'WR', 'WRA', 'PDNA']) + validTransitions['RD'] = set(['PRE', 'PRE_ALL', 'RD', 'RDA', 'WR', 'WRA', 'PDNA']) + validTransitions['WR'] = set(['PRE', 'PRE_ALL', 'RD', 'RDA', 'WR', 'WRA', 'PDNA']) validTransitions['RDA'] = set(['PRE_ALL', 'ACT', 'REFA', 'PDNA', 'PDNP']) validTransitions['WRA'] = set(['PRE_ALL', 'ACT', 'REFA', 'PDNA', 'PDNP']) - validTransitions['REFA'] = set(['PRE_ALL', 'ACT','REFA', 'PDNA', 'PDNP', 'SREF']) + validTransitions['REFA'] = set(['PRE_ALL', 'ACT', 'REFA', 'PDNA', 'PDNP', 'SREF']) - validTransitions['PDNA'] = set(['PRE','PRE_ALL','ACT', 'RD', 'RDA', 'WR', 'WRA', 'REFA', 'PDNA', 'PDNP']) + validTransitions['PDNA'] = set(['PRE', 'PRE_ALL', 'ACT', 'RD', 'RDA', 'WR', 'WRA', 'REFA', 'PDNA', 'PDNP']) validTransitions['PDNP'] = set(['PRE_ALL', 'ACT', 'REFA', 'PDNA', 'PDNP']) validTransitions['SREF'] = set(['PRE_ALL', 'ACT', 'REFA', 'PDNA', 'PDNP']) - # This was the original query: # 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""" # However, refreshes and pre_all are attributed to Bank 0 therefore this must be added to the order evaluation: @@ -282,10 +265,10 @@ def phase_transitions_are_valid(connection): lastRow = cursor.fetchone() for currentRow in cursor: currentPhase = currentRow[0] - lastPhase = lastRow[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)) + 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() @@ -295,60 +278,66 @@ def timing_constraint(FirstPhase, SecondPhase): FirstPhaseName = FirstPhase[0] SecondPhaseName = SecondPhase[0] - if(FirstPhaseName == "PRE" or FirstPhaseName == "PRE_ALL"): + if (FirstPhaseName == "PRE" or FirstPhaseName == "PRE_ALL"): return dramconfig.tRP - elif(FirstPhaseName == "ACT"): + elif (FirstPhaseName == "ACT"): return dramconfig.tRCD - elif(FirstPhaseName == "RD"): - if(SecondPhaseName in ["PRE, PRE_ALL"]): + elif (FirstPhaseName == "RD"): + if (SecondPhaseName in ["PRE, PRE_ALL"]): return dramconfig.tRTP - elif(SecondPhaseName in ["RD, RDA"]): + elif (SecondPhaseName in ["RD, RDA"]): return max(dramconfig.tCCD_L, getReadAccessTime()) - elif(SecondPhase in ["WR","WRA"]): - return dramconfig.tRL + getReadAccessTime() - dramconfig.tWL + 2*dramconfig.clk - elif(SecondPhase == "PDNA" ): + elif (SecondPhase in ["WR", "WRA"]): + return dramconfig.tRL + getReadAccessTime() - dramconfig.tWL + 2 * dramconfig.clk + elif (SecondPhase == "PDNA"): return dramconfig.tRL + getReadAccessTime() + dramconfig.clk - elif(FirstPhaseName == "WR"): - if(SecondPhaseName in ["PRE, PRE_ALL", "PDNA"]): + elif (FirstPhaseName == "WR"): + if (SecondPhaseName in ["PRE, PRE_ALL", "PDNA"]): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWR - elif(SecondPhaseName in ["RD, RDA"]): + elif (SecondPhaseName in ["RD, RDA"]): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWTR_L - elif(SecondPhaseName in ["WR, WRA"]): + elif (SecondPhaseName in ["WR, WRA"]): return max(dramconfig.tCCD_L, burstlength/dramconfig.dataRate) - elif(FirstPhaseName == "RDA"): - if(SecondPhaseName in ["ACT", "PRE_ALL", "REFA"]): + elif (FirstPhaseName == "RDA"): + if (SecondPhaseName in ["ACT", "PRE_ALL", "REFA"]): return dramconfig.tRTP + dramconfig.tRP - elif(SecondPhaseName in ["PDNA","PDNP"]): + elif (SecondPhaseName in ["PDNA", "PDNP"]): return dramconfig.tRL + getReadAccessTime() + dramconfig.clk - elif(FirstPhaseName == "WRA"): - if(SecondPhaseName in ["ACT", "PRE_ALL", "REFA"]): + elif (FirstPhaseName == "WRA"): + if (SecondPhaseName in ["ACT", "PRE_ALL", "REFA"]): return dramconfig.tWL + getWriteAccessTime() + dramconfig.tWR + dramconfig.tRP - elif(SecondPhaseName in ["PDNA","PDNP"]): + elif (SecondPhaseName in ["PDNA", "PDNP"]): return dramconfig.tWL + dramconfig.getWriteAccessTime() + dramconfig.tWR + dramconfig.clk - elif(FirstPhaseName == "REFA"): + elif (FirstPhaseName == "REFA"): return dramconfig.tRFC - elif(FirstPhaseName in ["PDNA","PDNP"]): + 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"): + elif (FirstPhaseName == "SREF"): return (FirstPhase[3] - FirstPhase[2]) + dramconfig.tXSR - dramconfig.clk return 0 + @test def timing_constraits_on_the_same_bank_hold(connection): - """Checks that all transitions of two consequtive phases on the same bank meet their timing constraints""" + """Checks that all transitions of two consecutive phases on the same bank meet their timing constraints""" cursor = connection.cursor() validTransitions = {} - query = """SELECT PhaseName, phases.ID,PhaseBegin,PhaseEnd FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE TBank=:bank + query = """SELECT PhaseName, phases.ID, PhaseBegin, PhaseEnd FROM phases INNER JOIN transactions ON phases.transact=transactions.ID WHERE TBank=:bank AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" for bankNumber in range(dramconfig.numberOfBanks): @@ -356,13 +345,14 @@ def timing_constraits_on_the_same_bank_hold(connection): 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))) + 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""" @@ -379,39 +369,37 @@ def row_buffer_is_used_correctly(connection): ((TBank=:bank) OR (PhaseNAME = "REFA" AND TBank=0) OR (PhaseNAME = "PRE_ALL" AND TBank=0)) AND PhaseName NOT IN ('REQ','RESP') ORDER BY PhaseBegin""" + # phases that precharge the bank and close the rowbuffer + prechargingPhases = set(['PRE', 'PRE_ALL', 'RDA', 'WRA']) - #phases that precharge the bank and close the rowbuffer - prechargingPhases = set(['PRE','PRE_ALL','RDA','WRA']) + # phases that require the bank to be in active state and the rowbuffer to be opened + accessingPhases = set(['RD', 'RDA', 'WR', 'WRA', 'PRE']) - #phases that require the bank to be in active state and the rowbuffer to be opened - accessingPhases = set(['RD','RDA', 'WR', 'WRA', 'PRE']) - - #phases that require the bank to be in precharged state and the robuffer to be closed - idlePhases = set(['ACT', 'PDNP', 'REFA', 'SREF']) + # phases that require the bank to be in precharged state and the robuffer to be closed + idlePhases = set(['ACT', 'PDNP', 'REFA', 'SREF']) for bankNumber in range(dramconfig.numberOfBanks): - cursor.execute(query,{"bank": bankNumber}) + cursor.execute(query, {"bank": bankNumber}) rowBufferIsClosed = True - - for currentRow in cursor: - if(currentRow[0] in accessingPhases and rowBufferIsClosed == True): + 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 == False): + 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'): + if (currentRow[0] == 'ACT'): rowBufferIsClosed = False - if(currentRow[0] in prechargingPhases): + if (currentRow[0] in prechargingPhases): rowBufferIsClosed = True return TestSuceeded() -#----------- activate checks --------------------------------------- + +# ----------- activate checks --------------------------------------- @test def activate_to_activate_holds(connection): """Checks that all activates are far enough apart(JESD229 229, P. 27)""" @@ -420,14 +408,13 @@ def activate_to_activate_holds(connection): lastRow = cursor.fetchone() for currentRow in cursor: - timeBetweenActivates = currentRow[1] - lastRow[1] + 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))) + 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 @@ -441,19 +428,19 @@ def activate_to_activate_on_same_bank_holds(connection): 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}) + 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)) + 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)""" @@ -463,15 +450,14 @@ def n_activate_window_holds(connection): for currentRow in cursor: activateWindow.append(currentRow[1]) - if(len(activateWindow) > dramconfig.nActivateWindow + 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))) + 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 --------------------------------------- +# ----------- 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""" @@ -480,13 +466,13 @@ def read_to_read_holds(connection): lastRow = cursor.fetchone() for currentRow in cursor: - timeBetweenReads = currentRow[1] - lastRow[1] + timeBetweenReads = currentRow[1] - lastRow[1] if (currentRow[2] == lastRow[2]): - minTime = max(dramconfig.tCCD_L,dramconfig.getReadAccessTime()) + 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)) + 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() @@ -501,16 +487,17 @@ def write_to_write_holds(connection): lastRow = cursor.fetchone() for currentRow in cursor: - timeBetweenWrites = currentRow[1] - lastRow[1] + timeBetweenWrites = currentRow[1] - lastRow[1] if (currentRow[2] == lastRow[2]): - minTime = max(dramconfig.tCCD_L,dramconfig.getWriteAccessTime()) + 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)) + 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 @@ -524,9 +511,8 @@ def write_to_read_and_read_to_write_hold(connection): lastRow = cursor.fetchone() for currentRow in cursor: - - if(currentRow[2] in ["RD","RDA"] and lastRow[2] in ["WR","WRA"]): - #write to read + 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: @@ -535,22 +521,21 @@ def write_to_read_and_read_to_write_hold(connection): 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))) + 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 + 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))) + 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): @@ -561,17 +546,17 @@ def read_holds_dll_constraint_after_sref(connection): WHERE PhaseName IN ('RD', 'RDA', 'SREF') ORDER BY PhaseBegin""" for bankNumber in range(dramconfig.numberOfBanks): - cursor.execute(query,{"bank": bankNumber}) + cursor.execute(query, {"bank": bankNumber}) lastRow = cursor.fetchone() for currentRow in cursor: - if(currentRow[2] in ["RD","RDA"] and lastRow[2] == 'SREF'): + if (currentRow[2] in ["RD", "RDA"] and lastRow[2] == 'SREF'): srefEndToRead = currentRow[1] - (lastRow[1] - dramconfig.clk) - if(srefEndToRead < dramconfig.tXSRDLL ): - return TestFailed("Read {0} starts {1} after end of sref {2}. Minimum time is {3}". - format(currentRow[0],formatTime(srefEndToRead),lastRow[0], formatTime(dramconfig.tXSRDLL))) + if (srefEndToRead < dramconfig.tXSRDLL): + return TestFailed("Read {0} starts {1} after end of sref {2}. Minimum time is {3}".format(currentRow[0], formatTime(srefEndToRead), lastRow[0], formatTime(dramconfig.tXSRDLL))) lastRow = currentRow return TestSuceeded() + @test def strict_transaction_order(connection): """Checks that all transactions are processed in the right order""" @@ -584,14 +569,14 @@ def strict_transaction_order(connection): for currentRow in cursor: transactions += str(currentRow[0]) + "," - if(transactions != ""): - if(dramconfig.scheduler == "FIFO_STRICT"): + if (transactions != ""): + if (dramconfig.scheduler == "FIFO_STRICT"): 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 FIFO_STRICT was choosen"); + return TestResult(True, "Transactions are not in Order, however this is okay since no FIFO_STRICT was choosen") return TestSuceeded() - # ----------- powerdown checks --------------------------------------- +# ----------- powerdown checks --------------------------------------- # @test # def sref_active_for_minimal_time(connection): @@ -600,7 +585,7 @@ def strict_transaction_order(connection): # 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): +# 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() @@ -611,48 +596,47 @@ def strict_transaction_order(connection): # 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): +# 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) + connection = sqlite3.connect(pathToTrace) + dramconfig.readConfigFromFiles(connection) - testResults = [] - numberOfFailedTest = 0 - print("================================") - print("RUNNING TEST ON {0}".format(pathToTrace)) + testResults = [] + numberOfFailedTest = 0 + print("================================") + print("RUNNING TEST ON {0}".format(pathToTrace)) - print("-----------------------------\n") + print("-----------------------------\n") - for test in tests: - testResult = test(connection) - testName = test.__name__.replace("_"," ") - testResults.append((testName, testResult.passed,testResult.message)) + 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 - if(testResult.passed): - print("[passed] {0}".format(testName)) - else: - print("[failed] {0} failed. Message: {1}".format(testName, testResult.message)) - numberOfFailedTest = numberOfFailedTest + 1 + print("\n-----------------------------") - print("\n-----------------------------") - - if(numberOfFailedTest == 0): + if (numberOfFailedTest == 0): print("All tests passed") - else: - print("{0} of {1} tests passed".format(len(tests) - numberOfFailedTest,len(tests))) + else: + print("{0} of {1} tests passed".format(len(tests) - numberOfFailedTest, len(tests))) - print("================================") - connection.close() + print("================================") + connection.close() - return testResults + return testResults if __name__ == "__main__": sys.stdout = os.fdopen(sys.stdout.fileno(), 'w') - for i in range(1,len(sys.argv)): + for i in range(1, len(sys.argv)): runTests(sys.argv[i]) - diff --git a/DRAMSys/analyzer/traceanalyzer.cpp b/DRAMSys/analyzer/traceanalyzer.cpp index 11b96392..c3e9e3e2 100644 --- a/DRAMSys/analyzer/traceanalyzer.cpp +++ b/DRAMSys/analyzer/traceanalyzer.cpp @@ -63,9 +63,14 @@ TraceAnalyzer::TraceAnalyzer(QWidget *parent) : ui(new Ui::TraceAnalyzer) { setUpGui(); + // Disable actions except for "Open" until some file is open. + ui->actionReload_all->setEnabled(false); + ui->actionSaveChangesToDB->setEnabled(false); + ui->actionClose_all->setEnabled(false); + ui->actionTest->setEnabled(false); + ui->actionMetrics->setEnabled(false); } - TraceAnalyzer::TraceAnalyzer(QSet paths, StartupOption option, QWidget *parent): QMainWindow(parent), ui(new Ui::TraceAnalyzer) @@ -104,9 +109,18 @@ void TraceAnalyzer::openTracefile(const QString &path) return; TraceFileTab* tab = new TraceFileTab(this, path); - connect(tab, SIGNAL(statusChanged(QString)), this, SLOT(statusChanged(QString))); + connect(tab, SIGNAL(statusChanged(QString, bool)), this, SLOT(statusChanged(QString, bool))); ui->traceFileTabs->addTab(tab,QFileInfo(path).baseName()); openedTraceFiles.insert(path); + + // Enable actions + ui->actionReload_all->setEnabled(true); + ui->actionSaveChangesToDB->setEnabled(true); + ui->actionClose_all->setEnabled(true); + ui->actionTest->setEnabled(true); + ui->actionMetrics->setEnabled(true); + + statusLabel->clear(); } void TraceAnalyzer::on_traceFileTabs_tabCloseRequested(int index) @@ -121,13 +135,65 @@ void TraceAnalyzer::on_actionClose_all_triggered() { while(ui->traceFileTabs->count()) on_traceFileTabs_tabCloseRequested(0); + + // All files closed. Disable actions except for "Open". + ui->actionReload_all->setEnabled(false); + ui->actionSaveChangesToDB->setEnabled(false); + ui->actionClose_all->setEnabled(false); + ui->actionTest->setEnabled(false); + ui->actionMetrics->setEnabled(false); + + statusLabel->clear(); } - - -void TraceAnalyzer::statusChanged(QString message) +void TraceAnalyzer::on_actionReload_all_triggered() { - statusLabel->setText(message + " - (" + QTime::currentTime().toString() + ")"); + TraceFileTab *traceFileTab; + + // Remove all tabs + int tabIndex = 0; + while (ui->traceFileTabs->count() != 0) { + traceFileTab = static_cast(ui->traceFileTabs->widget(0)); + std::cout << "Closing tab #" << tabIndex << " \"" << traceFileTab->getPathToTraceFile().toStdString() << "\"" << std::endl; + + ui->traceFileTabs->removeTab(0); + delete traceFileTab; + + tabIndex++; + } + + QList list = openedTraceFiles.toList(); + qSort(list); + + // Recreate all tabs + tabIndex = 0; + for (auto path : list) { + std::cout << "Reopening tab# " << tabIndex << " \"" << path.toStdString() << "\"" << std::endl; + + traceFileTab = new TraceFileTab(this, path); + connect(traceFileTab, SIGNAL(statusChanged(QString, bool)), this, SLOT(statusChanged(QString, bool))); + ui->traceFileTabs->addTab(traceFileTab, QFileInfo(path).baseName()); + + tabIndex++; + } + + this->statusChanged(QString("All databases reloaded "), true); +} + +void TraceAnalyzer::on_actionSaveChangesToDB_triggered() +{ + for (int index = 0; index < ui->traceFileTabs->count(); index++) { + // Changes in the database files will trigger the file watchers from + // the TraceFileTab class. They generate signals connected to TraceAnalyzer::statusChanged(). + TraceFileTab *traceFileTab = static_cast(ui->traceFileTabs->widget(index)); + traceFileTab->commitChangesToDB(); + } +} + +void TraceAnalyzer::statusChanged(QString message, bool saveChangesEnable) +{ + statusLabel->setText(message + QTime::currentTime().toString()); + ui->actionSaveChangesToDB->setEnabled(saveChangesEnable); } void TraceAnalyzer::on_actionTest_triggered() @@ -142,5 +208,5 @@ void TraceAnalyzer::on_actionMetrics_triggered() evaluationTool.raise(); evaluationTool.activateWindow(); evaluationTool.showAndEvaluateMetrics(openedTraceFiles.toList()); - } + diff --git a/DRAMSys/analyzer/traceanalyzer.h b/DRAMSys/analyzer/traceanalyzer.h index e0b16a0f..f1c04887 100644 --- a/DRAMSys/analyzer/traceanalyzer.h +++ b/DRAMSys/analyzer/traceanalyzer.h @@ -73,6 +73,8 @@ private: private Q_SLOTS: void on_actionOpen_triggered(); + void on_actionReload_all_triggered(); + void on_actionSaveChangesToDB_triggered(); void on_traceFileTabs_tabCloseRequested(int index); void on_actionClose_all_triggered(); void on_actionTest_triggered(); @@ -80,10 +82,11 @@ private Q_SLOTS: void on_actionMetrics_triggered(); public Q_SLOTS: - void statusChanged(QString message); + void statusChanged(QString message, bool saveChangesEnable = false); private: Ui::TraceAnalyzer *ui; }; #endif // TRACEANALYZER_H + diff --git a/DRAMSys/analyzer/traceanalyzer.ui b/DRAMSys/analyzer/traceanalyzer.ui index d99934c7..9dec9288 100644 --- a/DRAMSys/analyzer/traceanalyzer.ui +++ b/DRAMSys/analyzer/traceanalyzer.ui @@ -57,6 +57,8 @@ File + + @@ -81,10 +83,29 @@ Ctrl+O + + + Reload databases + + + CTRL+R + + + + + Save changes to DB + + + CTRL+S + + Close all + + CTRL+Q + diff --git a/DRAMSys/analyzer/tracefiletab.cpp b/DRAMSys/analyzer/tracefiletab.cpp index eb7353da..e2132d84 100644 --- a/DRAMSys/analyzer/tracefiletab.cpp +++ b/DRAMSys/analyzer/tracefiletab.cpp @@ -41,14 +41,15 @@ #include "QFileInfo" #include "qmessagebox.h" #include -#include TraceFileTab::TraceFileTab(QWidget *parent,const QString& path) : - QWidget(parent), - ui(new Ui::TraceFileTab) + QWidget(parent), ui(new Ui::TraceFileTab), savingChangesToDB(false) { ui->setupUi(this); this->path = path; + + std::cout << "Opening new tab for \"" << path.toStdString() << "\"" << std::endl; + initNavigatorAndItsDependentWidgets(path); setUpFileWatcher(path); ui->fileDescriptionEdit->setPlainText(navigator->GeneralTraceInfo().description); @@ -57,10 +58,15 @@ TraceFileTab::TraceFileTab(QWidget *parent,const QString& path) : TraceFileTab::~TraceFileTab() { - navigator->commitChangesToDB(); delete ui; } +void TraceFileTab::commitChangesToDB() +{ + savingChangesToDB = true; + navigator->commitChangesToDB(); +} + void TraceFileTab::initNavigatorAndItsDependentWidgets(QString path) { navigator = new TraceNavigator(path,this); @@ -82,15 +88,20 @@ void TraceFileTab::setUpFileWatcher(QString path) QObject::connect(fileWatcher,SIGNAL(fileChanged(QString)),this,SLOT(tracefileChanged())); } - - void TraceFileTab::tracefileChanged() { - Q_EMIT statusChanged(QString("Last Database Refresh")); + if (savingChangesToDB == true) { + // Database has changed due to user action (e.g., saving comments). + // No need to disable the "Save changes to DB" menu. + savingChangesToDB = false; + Q_EMIT statusChanged(QString("Changes saved "), true); + } else { + // External event changed the database file (e.g., the database file + // was overwritten when running a new test). + // The "Save changes to DB" menu must be disabled to avoid saving + // changes to a corrupted or inconsistent file. + Q_EMIT statusChanged(QString("At least one database has changed on disk "), false); + } navigator->refreshData(); } - - - - diff --git a/DRAMSys/analyzer/tracefiletab.h b/DRAMSys/analyzer/tracefiletab.h index bf57bd88..5acaa96a 100644 --- a/DRAMSys/analyzer/tracefiletab.h +++ b/DRAMSys/analyzer/tracefiletab.h @@ -60,6 +60,7 @@ public: void setUpFileWatcher(QString filename); void initNavigatorAndItsDependentWidgets(QString path); QString getPathToTraceFile(){return path;} + void commitChangesToDB(void); private: QString path; @@ -67,14 +68,15 @@ private: TraceNavigator *navigator; QFileSystemWatcher *fileWatcher; void setUpQueryEditor(QString path); + bool savingChangesToDB; public Q_SLOTS: void tracefileChanged(); Q_SIGNALS: - void statusChanged(QString); + void statusChanged(QString message, bool saveChangesEnable = false); void colorGroupingChanged(ColorGrouping colorgrouping); - }; #endif // TRACEFILETAB_H + diff --git a/DRAMSys/simulator/src/simulation/Simulation.cpp b/DRAMSys/simulator/src/simulation/Simulation.cpp index 8ff59ee2..07832216 100644 --- a/DRAMSys/simulator/src/simulation/Simulation.cpp +++ b/DRAMSys/simulator/src/simulation/Simulation.cpp @@ -110,7 +110,7 @@ void Simulation::setupTlmRecorders(const string &traceName, const string &pathTo void Simulation::instantiateModules(const string &traceName, const string &pathToResources, const std::vector &devices) { // The first call to getInstance() creates the Temperature Controller. - // The same instance will can be accessed by all other modules. + // The same instance will be accessed by all other modules. TemperatureController::getInstance(); for (size_t i = 0; i < Configuration::getInstance().NumberOfTracePlayers; i++) { diff --git a/DRAMSys/simulator/src/simulation/StlDataPlayer.h b/DRAMSys/simulator/src/simulation/StlDataPlayer.h index fbe6a67a..d4b55638 100644 --- a/DRAMSys/simulator/src/simulation/StlDataPlayer.h +++ b/DRAMSys/simulator/src/simulation/StlDataPlayer.h @@ -49,18 +49,21 @@ template struct StlDataPlayer: public TracePlayer { public: - StlDataPlayer(sc_module_name /*name*/, string pathToTrace, unsigned int clkMhz, - TracePlayerListener* listener); + StlDataPlayer(sc_module_name /*name*/, string pathToTrace, unsigned int clkMhz, TracePlayerListener* listener); + virtual void nextPayload() override { - string line; - while(line.empty() && file) - { + std::string line; + while (line.empty() && file) { std::getline(file, line); + + // Ignore lines which begin with '#' (commented lines) + if (!line.empty() && line.at(0) == '#') { + line.clear(); + } } - if(!file) - { + if(!file) { this->terminate(); return; } @@ -118,26 +121,19 @@ public: dataElement[i] = std::stoi(byteString.c_str(), 0, 16); } } - } - else - { - SC_REPORT_FATAL(0, - (string("Corrupted tracefile, command ") + command + string(" unknown")).c_str()); + } else { + SC_REPORT_FATAL(0, (string("Corrupted tracefile, command ") + command + string(" unknown")).c_str()); } sc_time sendingTime = std::stoull(time.c_str())*clk; - if (sendingTime <= sc_time_stamp()) - { + if (sendingTime <= sc_time_stamp()) { this->payloadEventQueue.notify(*payload, BEGIN_REQ, SC_ZERO_TIME); - } - else - { + } else { this->payloadEventQueue.notify(*payload, BEGIN_REQ, sendingTime - sc_time_stamp()); } } - private: ifstream file; unsigned int burstlength; @@ -164,3 +160,4 @@ StlDataPlayer::StlDataPlayer(sc_module_name /*name*/, string pathToTra } #endif // STLDATAPLAYER_H + diff --git a/DRAMSys/simulator/src/simulation/StlPlayer.h b/DRAMSys/simulator/src/simulation/StlPlayer.h index 355e7e5e..918eae84 100644 --- a/DRAMSys/simulator/src/simulation/StlPlayer.h +++ b/DRAMSys/simulator/src/simulation/StlPlayer.h @@ -49,23 +49,25 @@ template struct StlPlayer: public TracePlayer { public: - StlPlayer(sc_module_name /*name*/, string pathToTrace, unsigned int clkMhz, - TracePlayerListener* listener); + StlPlayer(sc_module_name /*name*/, string pathToTrace, unsigned int clkMhz, TracePlayerListener *listener); + virtual void nextPayload() override { - string line; - while(line.empty() && file) - { + std::string line; + while (line.empty() && file) { std::getline(file, line); + + // Ignore lines which begin with '#' (commented lines) + if (!line.empty() && line.at(0) == '#') { + line.clear(); + } } - if(!file) - { + if(!file) { this->terminate(); return; } - std::istringstream iss(line); string time, command, address; iss >> time >> command >> address; @@ -105,26 +107,19 @@ public: dataElement[i] = std::stoi(byteString.c_str(), 0, 16); } } - } - else - { - SC_REPORT_FATAL(0, - (string("Corrupted tracefile, command ") + command + string(" unknown")).c_str()); + } else { + SC_REPORT_FATAL(0, (string("Corrupted tracefile, command ") + command + string(" unknown")).c_str()); } sc_time sendingTime = std::stoull(time.c_str())*clk; - if (sendingTime <= sc_time_stamp()) - { + if (sendingTime <= sc_time_stamp()) { this->payloadEventQueue.notify(*payload, BEGIN_REQ, SC_ZERO_TIME); - } - else - { + } else { this->payloadEventQueue.notify(*payload, BEGIN_REQ, sendingTime - sc_time_stamp()); } } - private: ifstream file; unsigned int burstlength; @@ -151,3 +146,4 @@ StlPlayer::StlPlayer(sc_module_name /*name*/, string pathToTrace, unsi } #endif // STLPLAYER_H +