440 lines
14 KiB
C++
440 lines
14 KiB
C++
/*
|
|
* ControllerWrapper.h
|
|
*
|
|
* Created on: Mar 15, 2014
|
|
* Author: gernhard
|
|
*/
|
|
|
|
#ifndef CONTROLLERWRAPPER_H_
|
|
#define CONTROLLERWRAPPER_H_
|
|
|
|
#include <tlm.h>
|
|
#include <systemc.h>
|
|
#include <tlm_utils/simple_target_socket.h>
|
|
#include <tlm_utils/simple_initiator_socket.h>
|
|
#include <tlm_utils/peq_with_cb_and_phase.h>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
#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<unsigned int BUSWIDTH = 128>
|
|
struct Controller: public sc_module, public IWrapperConnector
|
|
{
|
|
public:
|
|
|
|
tlm_utils::simple_initiator_socket<Controller, BUSWIDTH, tlm::tlm_base_protocol_types> iSocket;
|
|
tlm_utils::simple_target_socket<Controller, BUSWIDTH, tlm::tlm_base_protocol_types> 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<Bank, int> numberOfPayloadsInSystem;
|
|
|
|
tlm::tlm_generic_payload* backpressure = NULL;
|
|
|
|
tlm_utils::peq_with_cb_and_phase<Controller> frontendPEQ;
|
|
tlm_utils::peq_with_cb_and_phase<Controller> dramPEQ;
|
|
tlm_utils::peq_with_cb_and_phase<Controller> 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<tlm_phase> phases)
|
|
{
|
|
for (tlm_phase p : phases)
|
|
{
|
|
if (p == phase)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
};
|
|
|
|
#endif /* CONTROLLERWRAPPER_H_ */
|