diff --git a/dram/resources/scripts/metrics.py b/dram/resources/scripts/metrics.py index aa5b7a52..c5303e55 100644 --- a/dram/resources/scripts/metrics.py +++ b/dram/resources/scripts/metrics.py @@ -1,23 +1,42 @@ import sys import sqlite3 -def the_answer_to_life_the_universe_and_everything(connection): - return 42 -def number_of_precharges(connection): - return 21 +metrics = [] + +def metric(function): + metrics.append(function) + return function + +@metric +def average_response_latency(connection): + cursor = connection.cursor() + cursor.execute("SELECT avg(end-begin) FROM ranges") + result = cursor.fetchone() + return round(result[0],1) + +@metric +def number_of_activates(connection): + cursor = connection.cursor() + cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName = 'ACT'") + result = cursor.fetchone() + return result[0] def calculateMetrics(pathToTrace): connection = sqlite3.connect(pathToTrace) - metrics = [the_answer_to_life_the_universe_and_everything, number_of_precharges] - result = [] + calculatedMetrics = [] + + print("================================") + print("Calculating metrics for {0}".format(pathToTrace)) + for metric in metrics: - result.append((metric.__name__.replace("_"," "), metric(connection))) + res = (metric.__name__.replace("_"," "), metric(connection)) + print("{0}: {1}".format(res[0],res[1])) + calculatedMetrics.append(res) connection.close() - print(result) - return result + return calculatedMetrics if __name__ == "__main__": path = sys.argv[1] diff --git a/dram/resources/scripts/tests.py b/dram/resources/scripts/tests.py index 8d415dbc..426a853a 100644 --- a/dram/resources/scripts/tests.py +++ b/dram/resources/scripts/tests.py @@ -19,6 +19,7 @@ class DramConfig(object): self.numberOfBanks = 8 self.nActivateWindow = 2 self.burstLength = 2 + self.tRP = 3*self.clk self.tRAS = 6*self.clk self.tRRD = 2*self.clk @@ -99,138 +100,138 @@ def commandbus_slots_are_used_once(connection): # ----------- activate checks --------------------------------------- -@test -def precharge_before_activate(connection): - """Checks that all activate commands are preceeded by a precharge or a refresh or a powerdown""" - cursor = connection.cursor() - query = """SELECT Phases.ID, PhaseName, PhaseBegin FROM Transactions INNER JOIN Phases ON Phases.Transact = Transactions.ID - WHERE (TBank = :bank AND PhaseName IN ('ACT','PRE','REFB')) OR PhaseName IN ('REFA','SREF','PDNP','PDNA') ORDER BY PhaseBegin""" +# @test +# def precharge_before_activate(connection): +# """Checks that all activate commands are preceeded by a precharge or a refresh or a powerdown""" +# cursor = connection.cursor() +# query = """SELECT Phases.ID, PhaseName, PhaseBegin FROM Transactions INNER JOIN Phases ON Phases.Transact = Transactions.ID +# WHERE (TBank = :bank AND PhaseName IN ('ACT','PRE','REFB')) OR PhaseName IN ('REFA','SREF','PDNP','PDNA') ORDER BY PhaseBegin""" - for bankNumber in range(dramconfig.numberOfBanks): - cursor.execute(query,{"bank": bankNumber}) - lastRow = cursor.fetchone() +# for bankNumber in range(dramconfig.numberOfBanks): +# cursor.execute(query,{"bank": bankNumber}) +# lastRow = cursor.fetchone() - for currentRow in cursor: - if(lastRow[1] != currentRow[1] or currentRow[1] != 'ACT'): - lastRow = currentRow - else: - return TestFailed("No precharge between activates with PhaseID {0} and {1}".format(lastRow[0], currentRow[0])) - return TestSuceeded() +# for currentRow in cursor: +# if(lastRow[1] != currentRow[1] or currentRow[1] != 'ACT'): +# lastRow = currentRow +# else: +# return TestFailed("No precharge between activates with PhaseID {0} and {1}".format(lastRow[0], currentRow[0])) +# return TestSuceeded() -@test -def activate_to_activate(connection): - """Checks minimal time between two activates (JEDEC 229, P. 27)""" - cursor = connection.cursor() - cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") - lastRow = cursor.fetchone() +# @test +# def activate_to_activate(connection): +# """Checks minimal time between two activates (JEDEC 229, P. 27)""" +# cursor = connection.cursor() +# cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") +# lastRow = cursor.fetchone() - for currentRow in cursor: - timeBetweenActivates = currentRow[1] - lastRow[1]; - if(timeBetweenActivates < dramconfig.tRRD): - 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), dramconfig.tRRD)) - else: - lastRow = currentRow - return TestSuceeded() +# for currentRow in cursor: +# timeBetweenActivates = currentRow[1] - lastRow[1]; +# if(timeBetweenActivates < dramconfig.tRRD): +# 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), dramconfig.tRRD)) +# else: +# lastRow = currentRow +# return TestSuceeded() -@test -def activate_to_activate_on_same_bank(connection): - """Checks minimal time between two activates on the same bank (JEDEC 229, P. 27)""" - cursor = connection.cursor() - query = "SELECT Phases.ID,PhaseBegin from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName = 'ACT' AND TBANK = :bank ORDER BY PhaseBegin" +# @test +# def activate_to_activate_on_same_bank(connection): +# """Checks minimal time between two activates on the same bank (JEDEC 229, P. 27)""" +# cursor = connection.cursor() +# query = "SELECT Phases.ID,PhaseBegin from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName = 'ACT' AND TBANK = :bank ORDER BY PhaseBegin" - for bankNumber in range(dramconfig.numberOfBanks): - cursor.execute(query,{"bank": bankNumber}) - lastRow = cursor.fetchone() +# for bankNumber in range(dramconfig.numberOfBanks): +# cursor.execute(query,{"bank": bankNumber}) +# lastRow = cursor.fetchone() - for currentRow in cursor: - timeBetweenActivates = currentRow[1] - lastRow[1]; - if(timeBetweenActivates < dramconfig.tRC): - return TestFailed("Activates with PhaseIDs {0} and {1} are {2} apart. Minimum time between two activates is {3}, since they are on the same bank({4})". - format(currentRow[0], lastRow[0],formatTime(timeBetweenActivates), dramconfig.tRC)) - else: - lastRow = currentRow +# for currentRow in cursor: +# timeBetweenActivates = currentRow[1] - lastRow[1]; +# if(timeBetweenActivates < dramconfig.tRC): +# return TestFailed("Activates with PhaseIDs {0} and {1} are {2} apart. Minimum time between two activates is {3}, since they are on the same bank({4})". +# format(currentRow[0], lastRow[0],formatTime(timeBetweenActivates), dramconfig.tRC)) +# else: +# lastRow = currentRow - return TestSuceeded() +# return TestSuceeded() -@test -def n_activate_window(connection): - """Checks n-Activate constraint (JEDEC 229, P. 27)""" - cursor = connection.cursor() - cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") - activateWindow = [] +# @test +# def n_activate_window(connection): +# """Checks n-Activate constraint (JEDEC 229, P. 27)""" +# cursor = connection.cursor() +# cursor.execute("SELECT ID,PhaseBegin from Phases WHERE PhaseName = 'ACT' ORDER BY PhaseBegin") +# activateWindow = [] - for currentRow in cursor: - activateWindow.append(currentRow[1]) - if(len(activateWindow) > dramconfig.nActivateWindow + 1): - activateWindow.pop(0) - if(activateWindow[dramconfig.nActivateWindow] - activateWindow[0] < dramconfig.tTAW): - 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.tTAW))) - return TestSuceeded() +# for currentRow in cursor: +# activateWindow.append(currentRow[1]) +# if(len(activateWindow) > dramconfig.nActivateWindow + 1): +# activateWindow.pop(0) +# if(activateWindow[dramconfig.nActivateWindow] - activateWindow[0] < dramconfig.tTAW): +# 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.tTAW))) +# return TestSuceeded() -# ----------- read checks --------------------------------------- +# # ----------- read checks --------------------------------------- -@test -def activate_to_read(connection): - """Checks minimal time bewteen activate and following read (JEDEC 229, P. 29)""" - cursor = connection.cursor() - query = "SELECT Phases.ID,PhaseBegin,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('ACT','RD') AND TBANK = :bank ORDER BY PhaseBegin" +# @test +# def activate_to_read(connection): +# """Checks minimal time bewteen activate and following read (JEDEC 229, P. 29)""" +# cursor = connection.cursor() +# query = "SELECT Phases.ID,PhaseBegin,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('ACT','RD') AND TBANK = :bank ORDER BY PhaseBegin" - for bankNumber in range(dramconfig.numberOfBanks): +# for bankNumber in range(dramconfig.numberOfBanks): - cursor.execute(query,{"bank": bankNumber}) - lastRow = cursor.fetchone() +# cursor.execute(query,{"bank": bankNumber}) +# lastRow = cursor.fetchone() - for currentRow in cursor: - if(currentRow[2] == "RD" and lastRow[2] == "ACT"): - actToReadTime = currentRow[1] - lastRow[1]; - if(actToReadTime < dramconfig.tRCD): - return TestFailed("Read with PhaseID {0} starts {1} after activate {2}. Minimum activate to read time is {3}". - format(currentRow[0],formatTime(actToReadTime),lastRow[0], formatTime(dramconfig.tRCD))) - lastRow = currentRow +# for currentRow in cursor: +# if(currentRow[2] == "RD" and lastRow[2] == "ACT"): +# actToReadTime = currentRow[1] - lastRow[1]; +# if(actToReadTime < dramconfig.tRCD): +# return TestFailed("Read with PhaseID {0} starts {1} after activate {2}. Minimum activate to read time is {3}". +# format(currentRow[0],formatTime(actToReadTime),lastRow[0], formatTime(dramconfig.tRCD))) +# lastRow = currentRow - return TestSuceeded() +# return TestSuceeded() -@test -def read_to_read(connection): - """Checks minimal time between two reads(JEDEC 229, P. 29)""" - cursor = connection.cursor() - cursor.execute("SELECT Phases.ID, PhaseBegin, PhaseEnd from Phases WHERE PhaseName = 'RD' ORDER BY PhaseBegin") - lastRow = cursor.fetchone() +# @test +# def read_to_read(connection): +# """Checks minimal time between two reads(JEDEC 229, P. 29)""" +# cursor = connection.cursor() +# cursor.execute("SELECT Phases.ID, PhaseBegin, PhaseEnd from Phases WHERE PhaseName = 'RD' ORDER BY PhaseBegin") +# lastRow = cursor.fetchone() - for currentRow in cursor: - if(currentRow[1] < lastRow[2]): - timeBetweenReads = currentRow[1] - lastRow[1]; - clocksBetweenReads = round(timeBetweenReads/dramconfig.clk) - if(clocksBetweenReads % 2 == 1): - return TestFailed("Read with PhaseID {0} interrupts read {1}. They are {2} clocks ({3}) apart. Numbers of clock between interrupting reads must be even.". - format(currentRow[0], lastRow[0], clocksBetweenReads, formatTime(timeBetweenReads))) - lastRow = currentRow +# for currentRow in cursor: +# if(currentRow[1] < lastRow[2]): +# timeBetweenReads = currentRow[1] - lastRow[1]; +# clocksBetweenReads = round(timeBetweenReads/dramconfig.clk) +# if(clocksBetweenReads % 2 == 1): +# return TestFailed("Read with PhaseID {0} interrupts read {1}. They are {2} clocks ({3}) apart. Numbers of clock between interrupting reads must be even.". +# format(currentRow[0], lastRow[0], clocksBetweenReads, formatTime(timeBetweenReads))) +# lastRow = currentRow - return TestSuceeded() +# return TestSuceeded() -@test -def write_to_read(connection): - """Checks minimal time between write and following read (JEDEC 229, P. 34)""" - cursor = connection.cursor() - query = "SELECT Phases.ID,PhaseBegin,PhaseEnd,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('RD','WR') AND TBANK = :bank ORDER BY PhaseBegin" +# @test +# def write_to_read(connection): +# """Checks minimal time between write and following read (JEDEC 229, P. 34)""" +# cursor = connection.cursor() +# query = "SELECT Phases.ID,PhaseBegin,PhaseEnd,PhaseName from Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID WHERE PhaseName IN ('RD','WR') AND TBANK = :bank ORDER BY PhaseBegin" - for bankNumber in range(dramconfig.numberOfBanks): +# for bankNumber in range(dramconfig.numberOfBanks): - cursor.execute(query,{"bank": bankNumber}) - lastRow = cursor.fetchone() +# cursor.execute(query,{"bank": bankNumber}) +# lastRow = cursor.fetchone() - for currentRow in cursor: - if(currentRow[3] == "RD" and lastRow[3] == "WR"): - writeEndToReadBegin = currentRow[1] - lastRow[2]; - if(writeEndToReadBegin < dramconfig.tWTR ): - return TestFailed("Read with PhaseID {0} starts {1} after end of write {2}. Minimum time between end of write and start of read is {3}". - format(currentRow[0],formatTime(writeEndToReadBegin),lastRow[0], formatTime(dramconfig.tWTR ))) - lastRow = currentRow +# for currentRow in cursor: +# if(currentRow[3] == "RD" and lastRow[3] == "WR"): +# writeEndToReadBegin = currentRow[1] - lastRow[2]; +# if(writeEndToReadBegin < dramconfig.tWTR ): +# return TestFailed("Read with PhaseID {0} starts {1} after end of write {2}. Minimum time between end of write and start of read is {3}". +# format(currentRow[0],formatTime(writeEndToReadBegin),lastRow[0], formatTime(dramconfig.tWTR ))) +# lastRow = currentRow - return TestSuceeded() +# return TestSuceeded() diff --git a/dram/src/core/BankStates.h b/dram/src/core/BankStates.h index dfa3432d..3ddf7bcd 100644 --- a/dram/src/core/BankStates.h +++ b/dram/src/core/BankStates.h @@ -29,6 +29,8 @@ public: void closeRowBuffer(const Bank &bank); void closeAllRowBuffers(); + + private: std::vector banks; std::vector rowsInRowBuffers; diff --git a/dram/src/core/TimingConfiguration.h b/dram/src/core/TimingConfiguration.h index 774793ba..2488aa6f 100644 --- a/dram/src/core/TimingConfiguration.h +++ b/dram/src/core/TimingConfiguration.h @@ -46,7 +46,7 @@ struct TimingConfiguration tRL = 3*clk; //read latency (read command start to data strobe) tWL = 1*clk; //write latency - tTAW = 48*clk; //two activate window + tTAW = clkAlign(sc_time(50,SC_NS), clk); //two activate window tRR = 2*clk; //min read to read on same rank tWW = 1*clk; //min write to write on same rank tWTR = 3*clk;//write to read diff --git a/dram/src/scheduler/Fr_Fcfs.cpp b/dram/src/scheduler/Fr_Fcfs.cpp index 5d432f11..1dc5dcaa 100644 --- a/dram/src/scheduler/Fr_Fcfs.cpp +++ b/dram/src/scheduler/Fr_Fcfs.cpp @@ -1,26 +1,51 @@ -//#include "Fr_Fcfs.h" -//#include "assert.h" -//#include "../common/dramExtension.h" -// -//using namespace tlm; -//using namespace std; -// -//namespace scheduler { -// -//bool FR_FCFS::hasScheduledTransactions(Bank bank) -//{ -// return rowHitBuffer.hasBufferedTransaction(bank); -//} -// -//gp* FR_FCFS::getNextScheduledTransaction(Bank bank) -//{ -// gp* result = rowHitBuffer.getRowHitOrOldest(bank); -// return result; -//} -// -//void FR_FCFS::schedule(gp* payload) -//{ -// rowHitBuffer.bufferTransaction(payload); -//} -// -//} +#include "Fr_Fcfs.h" +#include "../common/dramExtension.h" +#include + +using namespace std; + +namespace scheduler { +bool scheduler::FR_FCFS::hasTransactionForBank(Bank bank) +{ + return (buffer.at(bank.ID()).size() > 0); +} + +void scheduler::FR_FCFS::schedule(gp* payload) +{ + buffer.at(DramExtension::getExtension(payload).getBank().ID()).emplace_back(payload); +} + +gp* scheduler::FR_FCFS::getTransactionForBank(Bank bank) +{ + sc_assert(hasTransactionForBank(bank)); + + list& payloads = buffer.at(bank.ID()); + auto found = + find_if(payloads.begin(), payloads.end(), + [&](gp* payload) + { return DramExtension::getExtension(payload).getRow() == bankstates.getRowInRowBuffer(bank);}); + + if (found != payloads.end()) + return *found; + else + return payloads.front(); +} + +void scheduler::FR_FCFS::popTransactionForBank(Bank bank) +{ + sc_assert(hasTransactionForBank(bank)); + + list& payloads = buffer.at(bank.ID()); + auto found = + find_if(payloads.begin(), payloads.end(), + [&](gp* payload) + { return DramExtension::getExtension(payload).getRow() == bankstates.getRowInRowBuffer(bank);}); + + if (found != payloads.end()) + payloads.erase(found); + else + payloads.erase(payloads.begin()); +} + +} + diff --git a/dram/src/scheduler/Fr_Fcfs.h b/dram/src/scheduler/Fr_Fcfs.h index 5b237bf6..372762ee 100644 --- a/dram/src/scheduler/Fr_Fcfs.h +++ b/dram/src/scheduler/Fr_Fcfs.h @@ -1,26 +1,31 @@ -//#ifndef FR_FCFS_H -//#define FR_FCFS_H -//#include -//#include -//#include "Scheduler.h" -//#include "RowHitBuffer.h" -//#include "../core/BankStates.h" -// -//namespace scheduler{ -// -//class FR_FCFS : public Scheduler -//{ -//public: -// FR_FCFS(const core::BankStates& bankstates): rowHitBuffer(bankstates){} -// virtual ~FR_FCFS(){} -// virtual bool hasScheduledTransactions(Bank bank) override; -// virtual void schedule(gp* payload) override; -// virtual gp* getNextScheduledTransaction(Bank bank) override; -// -//private: -// RowHitBuffer rowHitBuffer; -//}; -// -//} -// -//#endif // FR_FCFS_H +#ifndef FR_FCFS_H_ +#define FR_FCFS_H_ + +#include "Scheduler.h" +#include "../core/BankStates.h" +#include +#include + +namespace scheduler { + +class FR_FCFS : public Scheduler +{ +public: + FR_FCFS(const core::BankStates& bankstates) : bankstates(bankstates), buffer(bankstates.getNumberOfBanks()) + {} + virtual ~FR_FCFS() + {} + + virtual bool hasTransactionForBank(Bank bank) override; + virtual void schedule(gp* payload) override; + virtual gp* getTransactionForBank(Bank bank) override; + virtual void popTransactionForBank(Bank bank) override; + +private: + std::vector> buffer; + const core::BankStates& bankstates; +}; + +} /* namespace scheduler */ + +#endif diff --git a/dram/src/simulation/controllerwrapper.h b/dram/src/simulation/controllerwrapper.h index 43684f4c..86e813cb 100644 --- a/dram/src/simulation/controllerwrapper.h +++ b/dram/src/simulation/controllerwrapper.h @@ -24,6 +24,7 @@ #include "../core/Controller.h" #include "../scheduler/Scheduler.h" #include "../scheduler/Fifo.h" +#include "../scheduler/Fr_Fcfs.h" using namespace std; using namespace tlm; @@ -44,7 +45,7 @@ public: &ControllerWrapper::controllerPEQCallback), recorder(recorder), debugManager(DebugManager::getInstance()) { controller = new Controller(*this, recorder); - scheduler = new Fifo(controller->getBankStates()); + scheduler = new FR_FCFS(controller->getBankStates()); inputBufferDelay = controller->config.Timings.clk; iSocket.register_nb_transport_bw(this, &ControllerWrapper::nb_transport_bw); tSocket.register_nb_transport_fw(this, &ControllerWrapper::nb_transport_fw);