diff --git a/DRAMSys/analyzer/scripts/plots.py b/DRAMSys/analyzer/scripts/plots.py old mode 100644 new mode 100755 index 4706b98d..e14b32c3 --- a/DRAMSys/analyzer/scripts/plots.py +++ b/DRAMSys/analyzer/scripts/plots.py @@ -7,6 +7,7 @@ import os plots = [] + def plot(function): plots.append(function) return function @@ -21,48 +22,52 @@ def memory_utilisation_window(connection, tracePath): # Besides, extracting the data from the memory specs, it is feasible to calculate the maximum data rate of the memory and then determine the bandwidth in Gbit/s. # The bandwidth data are then plotted in two graphics. + steps = 1000 cursor = connection.cursor() - cursor.execute(""" SELECT max(time) FROM Power """) - maxTime = cursor.fetchone() - cursor.execute(""" SELECT min(time) FROM Power where time > 0 """) - windowSize = cursor.fetchone()[0] - steps = ceil(float(maxTime[0])/float(windowSize)) + cursor.execute(""" SELECT max(DataStrobeEnd) FROM Transactions """) + total = cursor.fetchone() + windowSize = ceil(float(total[0])/float(steps)) + if (windowSize == 0): + windowSize = 1 + # print(steps) # All possible cases of data transfers inside a time window queryFull = """ SELECT sum(DataStrobeEnd - DataStrobeBegin) FROM transactions Where DataStrobeBegin > ? and DataStrobeEnd < ?""" # The data transfer begins and ends inside the time window queryEnd = """ SELECT sum(DataStrobeEnd - ?) FROM transactions Where DataStrobeBegin < ? and DataStrobeEnd > ? and DataStrobeEnd <=?""" # Only the end of the data transfer is inside the time window queryBegin = """ SELECT sum(? - DataStrobeBegin) FROM transactions Where DataStrobeBegin >= ? and DataStrobeBegin < ? and DataStrobeEnd > ?""" # Only the beginning of the data transfer is inside the time window queryPart = """ SELECT DataStrobeBegin FROM transactions Where DataStrobeBegin <= ? and DataStrobeEnd >= ?""" # The data transfer occupies all the time window maxDataRate = maximum_data_rate(connection) - bandwidthPercentage = [0] * (steps+1) - bandwidth = [0] * (steps+1) - bandwidthPercentage[0] = 0 - bandwidth[0] = 0 + # print(width) + # print(clk) + # print(rate) + bandwidthPercentage = [0] * steps + bandwidth = [0] * steps for i in range(steps): - bandwidthPercentage[i+1] = 0 + # print(i) + bandwidthPercentage[i] = 0 cursor.execute(queryPart, (i*windowSize, (i+1)*windowSize)) result = cursor.fetchone() if(result is None): cursor.execute(queryFull, (i*windowSize, (i+1)*windowSize)) result = cursor.fetchone() if(result[0] is not None): - bandwidthPercentage[i+1] += int(result[0]) + bandwidthPercentage[i] += int(result[0]) # print(bandwidthPercentage[i]) cursor.execute(queryEnd, (i*windowSize, i*windowSize, i*windowSize, (i+1)*windowSize)) result = cursor.fetchone() if(result[0] is not None): - bandwidthPercentage[i+1] += int(result[0]) + bandwidthPercentage[i] += int(result[0]) # print(bandwidthPercentage[i]) cursor.execute(queryBegin, ((i+1)*windowSize, i*windowSize, (i+1)*windowSize, (i+1)*windowSize)) result = cursor.fetchone() if(result[0] is not None): - bandwidthPercentage[i+1] += int(result[0]) + bandwidthPercentage[i] += int(result[0]) # print(bandwidthPercentage[i]) else: - bandwidthPercentage[i+1] = windowSize + bandwidthPercentage[i] = windowSize # print(bandwidthPercentage[i]) - bandwidthPercentage[i+1] = float(bandwidthPercentage[i+1]/windowSize) - bandwidth[i+1] = float(bandwidthPercentage[i+1])*float(maxDataRate)/1024 - bandwidthPercentage[i+1] *= 100 + bandwidthPercentage[i] = float(bandwidthPercentage[i]/windowSize) + bandwidth[i] = float(bandwidthPercentage[i])*float(maxDataRate)/1024 + bandwidthPercentage[i] *= 100 name = ntpath.basename(tracePath) basename, extension = os.path.splitext(name) @@ -74,7 +79,8 @@ def memory_utilisation_window(connection, tracePath): import numpy as np from matplotlib.backends.backend_pdf import PdfPages - time = np.arange(0, (steps+1)*windowSize, windowSize) + time = np.arange(0, steps*windowSize, windowSize) + plt.figure() subplotIndex = 211 @@ -82,7 +88,7 @@ def memory_utilisation_window(connection, tracePath): plt.plot(time/1000, bandwidthPercentage) plt.xlabel('Time (ns)') plt.ylabel('Bandwidth (%)') - plt.ylim(0, 120) + plt.ylim(0, 100) plt.grid(True) subplotIndex += 1 @@ -104,16 +110,16 @@ def power_window(connection, tracePath): cursor = connection.cursor() cursor.execute(""" SELECT max(time) FROM Power """) maxTime = cursor.fetchone() - cursor.execute(""" SELECT min(time) FROM Power where time > 0 """) - windowSize = cursor.fetchone()[0] - steps = ceil(float(maxTime[0])/float(windowSize)) + cursor.execute(""" SELECT min(time) FROM Power WHERE time > 0""") + windowSize = cursor.fetchone() + steps = ceil(float(maxTime[0])/float(windowSize[0])) cursor.execute(""" SELECT * FROM Power """) - time = [0] * (steps+1) - power = [0] * (steps+1) - for i in range((steps+1)): + time = [0] * steps + power = [0] * steps + for i in range(steps): result = cursor.fetchone() - time[i] = int(result[0])/1000 - power[i] = float(result[1]) + time[i] = float(result[0]) * 1000000000 # convertion of seconds to nanoseconds + power[i] = float(result[1]) # values are stored in mW name = ntpath.basename(tracePath) basename, extension = os.path.splitext(name) @@ -127,6 +133,7 @@ def power_window(connection, tracePath): plt.plot(time, power) plt.xlabel('Time (ns)') plt.ylabel('Power (mW)') + plt.gca().set_ylim(bottom=0) plt.grid(True) pdf = PdfPages(OUTPUT_FILE) pdf.savefig() diff --git a/DRAMSys/simulator/resources/scripts/createTraceDB.sql b/DRAMSys/simulator/resources/scripts/createTraceDB.sql index aa236f83..a8bad1fe 100644 --- a/DRAMSys/simulator/resources/scripts/createTraceDB.sql +++ b/DRAMSys/simulator/resources/scripts/createTraceDB.sql @@ -26,7 +26,7 @@ CREATE TABLE GeneralInfo( ); CREATE TABLE Power( - time INTEGER, + time DOUBLE, AveragePower DOUBLE ); diff --git a/DRAMSys/simulator/simulator.pro b/DRAMSys/simulator/simulator.pro index 1d5a6f40..bc65a145 100644 --- a/DRAMSys/simulator/simulator.pro +++ b/DRAMSys/simulator/simulator.pro @@ -49,13 +49,6 @@ INCLUDEPATH += src/common/third_party/DRAMPower/src/libdrampower DEFINES += TIXML_USE_STL DEFINES += SC_INCLUDE_DYNAMIC_PROCESSES -release { - DEFINES += NDEBUG -} - -#CONFIG += c++11 -#QMAKE_CXXFLAGS += -O0 -g -#QMAKE_CXXFLAGS += -std=c++0x -O0 -g unix:!macx { QMAKE_CXXFLAGS += -std=c++11 -O0 -g diff --git a/DRAMSys/simulator/src/common/TlmRecorder.cpp b/DRAMSys/simulator/src/common/TlmRecorder.cpp index c55b2c19..270f04ea 100644 --- a/DRAMSys/simulator/src/common/TlmRecorder.cpp +++ b/DRAMSys/simulator/src/common/TlmRecorder.cpp @@ -74,10 +74,10 @@ TlmRecorder::~TlmRecorder() closeConnection(); } -void TlmRecorder::recordPower(sc_time time, double averagePower) +void TlmRecorder::recordPower(double timeInSeconds, double averagePower) { if (TlmRecorder::recordingEnabled) { - sqlite3_bind_int64(insertPowerStatement, 1, time.value()); + sqlite3_bind_double(insertPowerStatement, 1, timeInSeconds); sqlite3_bind_double(insertPowerStatement, 2, averagePower); executeSqlStatement(insertPowerStatement); } diff --git a/DRAMSys/simulator/src/common/TlmRecorder.h b/DRAMSys/simulator/src/common/TlmRecorder.h index 1f5e499a..d0c0ad49 100644 --- a/DRAMSys/simulator/src/common/TlmRecorder.h +++ b/DRAMSys/simulator/src/common/TlmRecorder.h @@ -69,7 +69,7 @@ public: void recordTracenames(string traces){this->traces = traces;} void recordPhase(tlm::tlm_generic_payload &trans, tlm::tlm_phase phase, sc_time time); - void recordPower(sc_time time, double averagePower); + void recordPower(double timeInSeconds, double averagePower); void recordDebugMessage(std::string message, sc_time time); void updateDataStrobe(const sc_time& begin, const sc_time& end, tlm::tlm_generic_payload& trans); void closeConnection(); diff --git a/DRAMSys/simulator/src/simulation/Dram.h b/DRAMSys/simulator/src/simulation/Dram.h index c8f77a94..6125e25f 100644 --- a/DRAMSys/simulator/src/simulation/Dram.h +++ b/DRAMSys/simulator/src/simulation/Dram.h @@ -45,6 +45,7 @@ #include #include #include +#include #include "../common/DebugManager.h" #include "../common/dramExtension.h" #include "../controller/core/TimingCalculation.h" @@ -71,8 +72,7 @@ struct Dram : sc_module enum sc_time_unit pWindowUnit = Configuration::getInstance().PowerWindowUnit; sc_time powerWindowSize = sc_time(pWindowSize, pWindowUnit); libDRAMPower *DRAMPower; - double totalEnergy = 0; - double sumOfEnergyWindows = 0; + double sumOfEnergyWindows = 0.0; // Bandwith realted: unsigned long long int numberOfTransactionsServed; @@ -96,7 +96,7 @@ struct Dram : sc_module if(powerAnalysis == true) { sc_time clk = Configuration::getInstance().memSpec.clk; - + MemArchitectureSpec memArchSpec; memArchSpec.burstLength = Configuration::getInstance().memSpec.BurstLength; memArchSpec.dataRate = Configuration::getInstance().memSpec.DataRate; @@ -169,7 +169,7 @@ struct Dram : sc_module memPowerSpec.vdd2 = Configuration::getInstance().memSpec.vDD2; MemorySpecification memSpec; - memSpec.memTimingSpec = memTimingSpec; + memSpec.memTimingSpec = memTimingSpec; memSpec.memPowerSpec = memPowerSpec; memSpec.memArchSpec = memArchSpec; @@ -204,27 +204,18 @@ struct Dram : sc_module ~Dram() { - if(powerAnalysis == true) - { + if (powerAnalysis == true) { + // Obtain the residual energy which was not covered by previous windows DRAMPower->updateCounters(true); DRAMPower->calcEnergy(); - // Calculate the residual power and energy which was not covered by previous windows... - printDebugMessage(string("\tCurrent Energy: \t") + to_string(DRAMPower->getEnergy().total_energy - totalEnergy)); - printDebugMessage(string("\tAverage Power: \t") + to_string((DRAMPower->getEnergy().total_energy - totalEnergy)/powerWindowSize.value())); + double totalEnergy = sumOfEnergyWindows + DRAMPower->getEnergy().total_energy; + // The energy is given in [pJ] and divided by [s] resulting in [pW] then converted to [mW] + double averagePower = (totalEnergy / sc_time_stamp().to_seconds()) / 1e9; - double currentTotalEnergy = DRAMPower->getEnergy().total_energy - totalEnergy; - double currentAveragePower = currentTotalEnergy/powerWindowSize.value(); - sumOfEnergyWindows += currentTotalEnergy; - tlmRecorder->recordPower(sc_time_stamp(),currentAveragePower); - totalEnergy = DRAMPower->getEnergy().total_energy; - - // Make sure that the summed energy of the windows is the same as the final value provided by DRAMPower: - assert(sumOfEnergyWindows == totalEnergy); - - // Print Final Total Power Values: - cout << name() << string("\tTotal Energy: \t") + to_string(totalEnergy) << " pJ"<< endl; - cout << name() << string("\tAverage Power: \t") + to_string(DRAMPower->getPower().average_power) << " mW"<< endl; + // Print the final total energy and the average power for the simulation + cout << name() << string("\tTotal Energy: \t") + to_string(totalEnergy) + string("\t[pJ]") << endl; + cout << name() << string("\tAverage Power: \t") + to_string(averagePower) + string("\t[mW]") << endl; } // Bandwidth: @@ -238,9 +229,16 @@ struct Dram : sc_module for (auto e : ememory) { delete e; } - //std::cout << "Simulated Memory Size: " << memory.size() << endl; // TODO Aufrauemen } - + + // When working with floats, we have to decide ourselves what is an + // acceptable definition for "equal". Here the number is compared with a + // suitable error margin (0.00001). + bool is_equal(double a, double b, const double epsilon = 1e-05) + { + return std::fabs(a - b) < epsilon; + } + // This Thread is only triggered when Power Simulation is enabled. // It estimates the current average power which will be stored in the trace database for visualization purposes. void powerWindow() @@ -248,22 +246,47 @@ struct Dram : sc_module double currentAveragePower = 0; double currentTotalEnergy = 0; - do - { + do { + // At the very beginning (zero clock cycles) the energy is 0, so we wait first + wait(powerWindowSize); + + unsigned long long clk_cycles = sc_time_stamp().value() / Configuration::getInstance().memSpec.clk.value(); + + DRAMPower->doCommand(MemCommand::NOP, 0, clk_cycles); DRAMPower->updateCounters(false); DRAMPower->calcEnergy(); - currentTotalEnergy = DRAMPower->getEnergy().total_energy - totalEnergy; - currentAveragePower = currentTotalEnergy/powerWindowSize.value(); - tlmRecorder->recordPower(sc_time_stamp(),currentAveragePower); - totalEnergy = DRAMPower->getEnergy().total_energy; + + currentTotalEnergy = DRAMPower->getEnergy().total_energy; + currentAveragePower = DRAMPower->getPower().average_power; + + DRAMPower->clearCounters(clk_cycles); + + // During operation the energy should never be zero since the device is always consuming + assert(!is_equal(currentTotalEnergy, 0.0)); + + // Accumulate the energy since we are clearing the library + // counters. + // + // Here we use double values. The double type ensures 15 decimal + // digits to represent a number. It does not matter if the digits + // are before or after the comma. Thus we only have rounding for + // numbers represented with more than 15 decimal digits. + // + // In more technical terms: + // An IEEE double has 53 significant bits (see also DBL_MANT_DIG + // in ). That is approximately 15.95 decimal digits + // (log10(2^53)). The implementation sets the number of digits + // (DBL_DIG) to 15, not 16, because it has to round down. + // sumOfEnergyWindows += currentTotalEnergy; - printDebugMessage(string("\tCurrent Energy: \t") + to_string(currentTotalEnergy)); - printDebugMessage(string("\tAverage Power: \t") + to_string(currentAveragePower)); - wait(powerWindowSize); - } - while(true); + // Store the time (in seconds) and the current average power (in mW) into the database + tlmRecorder->recordPower(sc_time_stamp().to_seconds(), currentAveragePower); + // Here considering that DRAMPower provides the energy in pJ and the power in mW + printDebugMessage(string("\tCurrent Energy: \t") + to_string(currentTotalEnergy) + string("\t[pJ]")); + printDebugMessage(string("\tAverage Power: \t") + to_string(currentAveragePower) + string("\t[mW]")); + } while(true); } virtual tlm::tlm_sync_enum nb_transport_fw(tlm::tlm_generic_payload& payload, tlm::tlm_phase& phase, sc_time& delay) @@ -542,7 +565,7 @@ struct Dram : sc_module void setTlmRecorder(TlmRecorder *rec) { - tlmRecorder = rec; + tlmRecorder = rec; } };