Files
DRAMSys/dram/src/simulation/controllerwrapper.h

365 lines
11 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/Controller.h"
#include "../scheduler/Scheduler.h"
#include "../scheduler/Fifo.h"
#include "../scheduler/Fr_Fcfs.h"
using namespace std;
using namespace tlm;
using namespace core;
using namespace scheduler;
template<unsigned int BUSWIDTH = 128>
struct ControllerWrapper: public sc_module, public IWrapperConnector
{
public:
tlm_utils::simple_initiator_socket<ControllerWrapper, BUSWIDTH, tlm::tlm_base_protocol_types> iSocket;
tlm_utils::simple_target_socket<ControllerWrapper, BUSWIDTH, tlm::tlm_base_protocol_types> tSocket;
ControllerWrapper(sc_module_name name, TlmRecorder& recorder) :
frontendPEQ(this, &ControllerWrapper::frontendPEQCallback), dramPEQ(this,
&ControllerWrapper::dramPEQCallback), controllerPEQ(this,
&ControllerWrapper::controllerPEQCallback), recorder(recorder), debugManager(
DebugManager::getInstance())
{
controller = new Controller(*this, recorder, numberOfPayloads);
scheduler = new Fifo(*controller);
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);
}
~ControllerWrapper()
{
delete controller;
delete scheduler;
}
virtual void send(const ScheduledCommand& command, tlm_generic_payload& payload) override
{
assert(command.getStart() >= sc_time_stamp());
switch (command.getCommand())
{
case Command::Read:
dramPEQ.notify(payload, BEGIN_RD, command.getStart() - sc_time_stamp());
dramPEQ.notify(payload, END_RD, command.getEnd() - sc_time_stamp());
break;
case Command::Write:
dramPEQ.notify(payload, BEGIN_WR, command.getStart() - sc_time_stamp());
dramPEQ.notify(payload, END_WR, command.getEnd() - sc_time_stamp());
break;
case Command::AutoRefresh:
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:
dramPEQ.notify(payload, BEGIN_ACT, command.getStart() - sc_time_stamp());
dramPEQ.notify(payload, END_ACT, command.getEnd() - sc_time_stamp());
break;
case Command::Precharge:
dramPEQ.notify(payload, BEGIN_PRE, command.getStart() - sc_time_stamp());
dramPEQ.notify(payload, END_PRE, command.getEnd() - sc_time_stamp());
break;
case Command::PrechargeAll:
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());
break;
case Command::PDNP:
dramPEQ.notify(payload, BEGIN_PDNP, command.getStart() - sc_time_stamp());
break;
case Command::SREF:
dramPEQ.notify(payload, BEGIN_SREF, command.getStart() - sc_time_stamp());
break;
case Command::PDNAX:
dramPEQ.notify(payload, END_PDNA, command.getStart() - sc_time_stamp());
break;
case Command::PDNPX:
dramPEQ.notify(payload, END_PDNP, command.getStart() - sc_time_stamp());
break;
case Command::SREFX:
dramPEQ.notify(payload, END_SREF, command.getStart() - sc_time_stamp());
break;
default:
SC_REPORT_FATAL(0, "unsupported command in controller wrapper");
break;
}
}
virtual void send(Trigger trigger, sc_time time, tlm_generic_payload& payload) override
{
assert(time >= sc_time_stamp());
sc_time delay = time - sc_time_stamp();
if(trigger == Trigger::RefreshTrigger)
{
controllerPEQ.notify(payload, REFRESH_TRIGGER, delay);
}
else if(trigger == Trigger::WakeUpTrigger)
{
controllerPEQ.notify(payload, WAKEUP_TRIGGER, delay);
}
else
{
SC_REPORT_FATAL("controller wrapper", "unknown trigger");
}
}
private:
Controller* controller;
Scheduler* scheduler;
std::map<Bank, int> numberOfPayloads;
tlm_utils::peq_with_cb_and_phase<ControllerWrapper> frontendPEQ;
tlm_utils::peq_with_cb_and_phase<ControllerWrapper> dramPEQ;
tlm_utils::peq_with_cb_and_phase<ControllerWrapper> controllerPEQ;
sc_time inputBufferDelay;
TlmRecorder& recorder;
DebugManager& debugManager;
void payloadEntersSystem(tlm_generic_payload& payload)
{
printDebugMessage("Transaction enters system");
Bank bank = DramExtension::getExtension(payload).getBank();
numberOfPayloads[bank] = numberOfPayloads[bank] + 1;
scheduler->schedule(&payload);
}
void payloadLeavesSystem(tlm_generic_payload& payload)
{
Bank bank = DramExtension::getExtension(payload).getBank();
numberOfPayloads[bank] = numberOfPayloads[bank] - 1;
sc_assert(numberOfPayloads[bank] >= 0);
if (numberOfPayloads[bank] == 0)
controller->powerDownManager->sleep(bank, sc_time_stamp());
}
void scheduleNextPayload(Bank bank)
{
printDebugMessage("Try to schedule next payload on bank " + to_string(bank.ID()));
if (scheduler->hasTransactionForBank(bank))
{
if (controller->isBusy(sc_time_stamp(), bank))
{
printDebugMessage("\t-> break: controller is busy");
return;
}
if (controller->powerDownManager->isInPowerDown(bank))
{
controller->powerDownManager->wakeUp(bank, sc_time_stamp());
printDebugMessage("\t-> break: wake up power down");
return;
}
else if (controller->powerDownManager->isAwakeForRefresh(bank))
{
controller->powerDownManager->wakeUp(bank, sc_time_stamp());
}
tlm_generic_payload* nextTransaction = scheduler->getTransactionForBank(bank);
if (controller->scheduleRequest(sc_time_stamp(), *nextTransaction))
{
scheduler->popTransactionForBank(bank);
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");
}
}
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);
recorder.recordPhase(payload, phase, sc_time_stamp());
if (phase == BEGIN_REQ)
{
payload.acquire();
payloadEntersSystem(payload);
frontendPEQ.notify(payload, phase, inputBufferDelay);
}
else if (phase == END_RESP)
{
payloadLeavesSystem(payload);
payload.release();
}
return TLM_ACCEPTED;
}
void frontendPEQCallback(tlm_generic_payload& payload, const tlm_phase& phase)
{
if (phase == BEGIN_REQ)
{
scheduleNextPayload(DramExtension::getExtension(payload).getBank());
payload.set_response_status(tlm::TLM_OK_RESPONSE);
recorder.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)
{
recorder.recordPhase(payload, phase, sc_time_stamp());
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)
{
recorder.recordPhase(payload, BEGIN_RESP, sc_time_stamp());
sendToFrontend(payload, BEGIN_RESP, SC_ZERO_TIME);
}
else if (isIn(phase, {BEGIN_ACT, BEGIN_PRE, BEGIN_PRE_ALL }))
{
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);
if (phase == END_SREF)
controller->refreshManager->reInitialize(payload, sc_time_stamp());
scheduleNextPayload(bank);
}
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()));
if (numberOfPayloads[bank] == 0)
controller->powerDownManager->sleep(bank, sc_time_stamp());
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 if (phase == WAKEUP_TRIGGER)
{
controller->triggerWakeUp(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, Importance importance = Importance::Info)
{
debugManager.printDebugMessage(message, Sender::DramWrapper, importance);
}
bool isIn(tlm_phase phase, std::vector<tlm_phase> phases)
{
for (tlm_phase p : phases)
{
if (p == phase)
return true;
}
return false;
}
void stop()
{
for (Bank bank : controller->getBanks())
{
controller->powerDownManager->wakeUp(bank, sc_time_stamp());
}
}
};
#endif /* CONTROLLERWRAPPER_H_ */