/* * ControllerWrapper.h * * Created on: Mar 15, 2014 * Author: gernhard */ #ifndef CONTROLLERWRAPPER_H_ #define CONTROLLERWRAPPER_H_ #include #include #include #include #include #include #include #include #include "../common/Utils.h" #include "../common/protocol.h" #include "../common/TlmRecorder.h" #include "../common/DebugManager.h" #include "../core/IWrapperConnector.h" #include "../core/ControllerCore.h" #include "../scheduler/Scheduler.h" #include "../scheduler/Fifo.h" #include "../scheduler/Fr_Fcfs.h" #include "../scheduler/PARBS.h" using namespace std; using namespace tlm; using namespace core; using namespace scheduler; template struct Controller: public sc_module, public IWrapperConnector { public: tlm_utils::simple_initiator_socket iSocket; tlm_utils::simple_target_socket tSocket; Controller(sc_module_name name) : frontendPEQ(this, &Controller::frontendPEQCallback), dramPEQ(this, &Controller::dramPEQCallback), controllerPEQ(this, &Controller::controllerPEQCallback), debugManager(DebugManager::getInstance()) { controller = new ControllerCore(*this, numberOfPayloadsInSystem); buildScheduler(); inputBufferDelay = controller->config.Timings.clk; iSocket.register_nb_transport_bw(this, &Controller::nb_transport_bw); tSocket.register_nb_transport_fw(this, &Controller::nb_transport_fw); } ~Controller() { delete controller; delete scheduler; } void buildScheduler() { string selectedScheduler = Configuration::getInstance().Scheduler; if (selectedScheduler == "FR_FCFS") { scheduler = new FR_FCFS(controller->state.bankStates, Configuration::getInstance().RefreshAwareScheduling, Configuration::getInstance().AdaptiveOpenPagePolicy); } else if (selectedScheduler == "PAR_BS") { scheduler = new PAR_BS(controller->state.bankStates, Configuration::getInstance().RefreshAwareScheduling, Configuration::getInstance().Capsize); } else if (selectedScheduler == "FIFO") scheduler = new Fifo(); else reportFatal(name(), "unsupported scheduler: " + selectedScheduler); } void terminateSimulation() { for (Bank bank : controller->getBanks()) { controller->powerDownManager->wakeUp(bank, sc_time_stamp()); } } virtual void send(const ScheduledCommand& command, tlm_generic_payload& payload) override { sc_assert(command.getStart() >= sc_time_stamp()); TimeInterval dataStrobe; TlmRecorder& rec = TlmRecorder::getInstance(); switch (command.getCommand()) { case Command::Read: rec.recordPhase(payload, BEGIN_RD, command.getStart()); dataStrobe = command.getIntervalOnDataStrobe(); rec.updateDataStrobe(dataStrobe.start, dataStrobe.end, payload); rec.recordPhase(payload, END_RD, command.getEnd()); dramPEQ.notify(payload, BEGIN_RD, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_RD, command.getEnd() - sc_time_stamp()); break; case Command::ReadA: rec.recordPhase(payload, BEGIN_RDA, command.getStart()); dataStrobe = command.getIntervalOnDataStrobe(); rec.updateDataStrobe(dataStrobe.start, dataStrobe.end, payload); rec.recordPhase(payload, END_RDA, command.getEnd()); dramPEQ.notify(payload, BEGIN_RDA, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_RDA, command.getEnd() - sc_time_stamp()); break; case Command::Write: rec.recordPhase(payload, BEGIN_WR, command.getStart()); dataStrobe = command.getIntervalOnDataStrobe(); rec.updateDataStrobe(dataStrobe.start, dataStrobe.end, payload); rec.recordPhase(payload, END_WR, command.getEnd()); dramPEQ.notify(payload, BEGIN_WR, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_WR, command.getEnd() - sc_time_stamp()); break; case Command::WriteA: rec.recordPhase(payload, BEGIN_WRA, command.getStart()); dataStrobe = command.getIntervalOnDataStrobe(); rec.updateDataStrobe(dataStrobe.start, dataStrobe.end, payload); rec.recordPhase(payload, END_WRA, command.getEnd()); dramPEQ.notify(payload, BEGIN_WRA, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_WRA, command.getEnd() - sc_time_stamp()); break; case Command::AutoRefresh: rec.recordPhase(payload, BEGIN_AUTO_REFRESH, command.getStart()); rec.recordPhase(payload, END_AUTO_REFRESH, command.getEnd()); dramPEQ.notify(payload, BEGIN_AUTO_REFRESH, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_AUTO_REFRESH, command.getEnd() - sc_time_stamp()); break; case Command::Activate: rec.recordPhase(payload, BEGIN_ACT, command.getStart()); rec.recordPhase(payload, END_ACT, command.getEnd()); dramPEQ.notify(payload, BEGIN_ACT, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_ACT, command.getEnd() - sc_time_stamp()); break; case Command::Precharge: rec.recordPhase(payload, BEGIN_PRE, command.getStart()); rec.recordPhase(payload, END_PRE, command.getEnd()); dramPEQ.notify(payload, BEGIN_PRE, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_PRE, command.getEnd() - sc_time_stamp()); break; case Command::PrechargeAll: rec.recordPhase(payload, BEGIN_PRE_ALL, command.getStart()); rec.recordPhase(payload, END_PRE_ALL, command.getEnd()); dramPEQ.notify(payload, BEGIN_PRE_ALL, command.getStart() - sc_time_stamp()); dramPEQ.notify(payload, END_PRE_ALL, command.getEnd() - sc_time_stamp()); break; case Command::PDNA: dramPEQ.notify(payload, BEGIN_PDNA, command.getStart() - sc_time_stamp()); rec.recordPhase(payload, BEGIN_PDNA, command.getStart()); break; case Command::PDNP: dramPEQ.notify(payload, BEGIN_PDNP, command.getStart() - sc_time_stamp()); rec.recordPhase(payload, BEGIN_PDNP, command.getStart()); break; case Command::SREF: dramPEQ.notify(payload, BEGIN_SREF, command.getStart() - sc_time_stamp()); rec.recordPhase(payload, BEGIN_SREF, command.getStart()); break; case Command::PDNAX: dramPEQ.notify(payload, END_PDNA, command.getStart() - sc_time_stamp()); rec.recordPhase(payload, END_PDNA, command.getEnd()); break; case Command::PDNPX: dramPEQ.notify(payload, END_PDNP, command.getStart() - sc_time_stamp()); rec.recordPhase(payload, END_PDNP, command.getEnd()); break; case Command::SREFX: dramPEQ.notify(payload, END_SREF, command.getStart() - sc_time_stamp()); rec.recordPhase(payload, END_SREF, command.getEnd()); break; default: SC_REPORT_FATAL(0, "unsupported command in controller"); break; } } virtual void send(Trigger trigger, sc_time time, tlm_generic_payload& payload) override { sc_assert(time >= sc_time_stamp()); sc_time delay = time - sc_time_stamp(); if (trigger == Trigger::RefreshTrigger) { controllerPEQ.notify(payload, REFRESH_TRIGGER, delay); } else { SC_REPORT_FATAL("controller wrapper", "unknown trigger"); } } private: ControllerCore* controller; Scheduler* scheduler; std::map numberOfPayloadsInSystem; tlm::tlm_generic_payload* backpressure = NULL; tlm_utils::peq_with_cb_and_phase frontendPEQ; tlm_utils::peq_with_cb_and_phase dramPEQ; tlm_utils::peq_with_cb_and_phase controllerPEQ; sc_time inputBufferDelay; DebugManager& debugManager; unsigned int getNumberOfPayloadsInSystem() { unsigned int sum = 0; for (Bank bank : controller->getBanks()) { sum += numberOfPayloadsInSystem[bank]; } return sum; } void payloadEntersSystem(tlm_generic_payload& payload) { Bank bank = DramExtension::getExtension(payload).getBank(); printDebugMessage("Transaction enters system on bank " + to_string(bank.ID())); numberOfPayloadsInSystem[bank]++; } void payloadLeavesSystem(tlm_generic_payload& payload) { Bank bank = DramExtension::getExtension(payload).getBank(); numberOfPayloadsInSystem[bank]--; controller->powerDownManager->sleep(bank, sc_time_stamp()); } void scheduleNextPayload(Bank bank) { printDebugMessage("Triggering schedule next payload on bank " + to_string(bank.ID())); if (scheduler->hasTransactionForBank(bank)) { controller->powerDownManager->wakeUp(bank, sc_time_stamp()); if (controller->isBusy(sc_time_stamp(), bank)) { printDebugMessage("\t-> break: controller is busy"); return; } tlm_generic_payload* nextTransaction = scheduler->getTransactionForBank(bank); if (controller->scheduleRequest(sc_time_stamp(), *nextTransaction)) { scheduler->popTransactionForBank(bank, nextTransaction); printDebugMessage("\t-> payload was scheduled by core"); } else { printDebugMessage("\t-> break: payload was not scheduled by core (collision with refresh)"); } } else { printDebugMessage("\t-> break: no transaction for bank"); controller->powerDownManager->sleep(bank, sc_time_stamp()); } } tlm_sync_enum nb_transport_bw(tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay) { return TLM_ACCEPTED; } // Initiated by dram frontend tlm_sync_enum nb_transport_fw(tlm_generic_payload& payload, tlm_phase& phase, sc_time& fwDelay) { DramExtension::getExtension(payload); TlmRecorder::getInstance().recordPhase(payload, phase, sc_time_stamp()); if (phase == BEGIN_REQ) { payload.acquire(); payloadEntersSystem(payload); if (getNumberOfPayloadsInSystem() > controller->config.MaxNrOfTransactions) { printDebugMessage("##Backpressure: Max number of transactions in system reached"); backpressure = &payload; return TLM_ACCEPTED; } frontendPEQ.notify(payload, phase, clkAlign(sc_time_stamp()) - sc_time_stamp() + Configuration::getInstance().Timings.clk); } else if (phase == END_RESP) { if (backpressure != NULL) { printDebugMessage("##Backpressure released"); //already registered above frontendPEQ.notify(*backpressure, BEGIN_REQ, Configuration::getInstance().Timings.clk); backpressure = NULL; } payloadLeavesSystem(payload); payload.release(); } return TLM_ACCEPTED; } void frontendPEQCallback(tlm_generic_payload& payload, const tlm_phase& phase) { if (phase == BEGIN_REQ) { scheduler->schedule(&payload); scheduleNextPayload(DramExtension::getExtension(payload).getBank()); payload.set_response_status(tlm::TLM_OK_RESPONSE); TlmRecorder::getInstance().recordPhase(payload, END_REQ, sc_time_stamp()); sendToFrontend(payload, END_REQ, SC_ZERO_TIME); } else { SC_REPORT_FATAL(0, "Frontend PEQ event queue in controller wrapper was triggered with unknown phase"); } } void dramPEQCallback(tlm_generic_payload& payload, const tlm_phase& phase) { Bank bank = DramExtension::getExtension(payload).getBank(); if (phase == BEGIN_RD || phase == BEGIN_WR) { scheduleNextPayload(bank); sendToDram(payload, phase, SC_ZERO_TIME); } else if (phase == END_RD || phase == END_WR) { TlmRecorder::getInstance().recordPhase(payload, BEGIN_RESP, sc_time_stamp()); sendToFrontend(payload, BEGIN_RESP, SC_ZERO_TIME); } else if (phase == END_RDA || phase == END_WRA) { TlmRecorder::getInstance().recordPhase(payload, BEGIN_RESP, sc_time_stamp()); sendToFrontend(payload, BEGIN_RESP, SC_ZERO_TIME); scheduleNextPayload(bank); } else if (isIn(phase, { BEGIN_ACT, BEGIN_PRE, BEGIN_PRE_ALL, BEGIN_RDA, BEGIN_WRA })) { sendToDram(payload, phase, SC_ZERO_TIME); } else if (isIn(phase, { BEGIN_PDNA, BEGIN_PDNP, BEGIN_SREF })) { printDebugMessage("Entering PowerDown " + phaseNameToString(phase) + " on bank " + to_string(bank.ID())); sendToDram(payload, phase, SC_ZERO_TIME); } else if (isIn(phase, { END_PDNA, END_PDNP, END_SREF })) { printDebugMessage("Leaving PowerDown " + phaseNameToString(phase) + " on bank " + to_string(bank.ID())); sendToDram(payload, phase, SC_ZERO_TIME); } else if (phase == BEGIN_AUTO_REFRESH) { printDebugMessage("Entering auto refresh on bank " + to_string(bank.ID())); sendToDram(payload, phase, SC_ZERO_TIME); } else if (phase == END_AUTO_REFRESH) { printDebugMessage("Finished auto refresh on bank " + to_string(bank.ID())); scheduleNextPayload(DramExtension::getExtension(payload).getBank()); } else if (isIn(phase, { END_PRE, END_PRE_ALL, END_ACT })) { } else { ostringstream oss; oss << phase; string str = string("dramPEQCallback queue in controller wrapper was triggered with unknown phase ") + oss.str(); SC_REPORT_FATAL(0, str.c_str()); } } void controllerPEQCallback(tlm_generic_payload& payload, const tlm_phase& phase) { if (phase == REFRESH_TRIGGER) { controller->triggerRefresh(payload, sc_time_stamp()); } else { SC_REPORT_FATAL(0, "controllerPEQCallback queue in controller wrapper was triggered with unknown phase"); } } void sendToDram(tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) { DramExtension::getExtension(payload); tlm_phase TPhase = phase; sc_time TDelay = delay; iSocket->nb_transport_fw(payload, TPhase, TDelay); } void sendToFrontend(tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) { tlm_phase TPhase = phase; sc_time TDelay = delay; tSocket->nb_transport_bw(payload, TPhase, TDelay); } void printDebugMessage(string message) { debugManager.printDebugMessage(name(), message); } bool isIn(tlm_phase phase, std::vector phases) { for (tlm_phase p : phases) { if (p == phase) return true; } return false; } }; #endif /* CONTROLLERWRAPPER_H_ */