Improved arbiter with thread and channel queues.

This commit is contained in:
Lukas Steiner
2020-10-27 16:02:11 +01:00
parent 5d6042a16a
commit f6752cb09a
7 changed files with 147 additions and 111 deletions

View File

@@ -163,6 +163,8 @@ void Configuration::setParameter(std::string name, nlohmann::json value)
}
else if (name == "PowerDownTimeout")
powerDownTimeout = value;
else if (name == "MaxActiveTransactions")
maxActiveTransactions = value;
//SimConfig------------------------------------------------
else if (name == "SimulationName")
simulationName = value;

View File

@@ -80,6 +80,7 @@ public:
unsigned int refreshMaxPulledin = 0;
enum class PowerDownPolicy {NoPowerDown, Staggered} powerDownPolicy;
unsigned int powerDownTimeout = 3;
unsigned int maxActiveTransactions = 64;
// SimConfig
std::string simulationName = "default";

View File

@@ -318,24 +318,22 @@ void Controller::controllerMethod()
tlm_sync_enum Controller::nb_transport_fw(tlm_generic_payload &trans,
tlm_phase &phase, sc_time &delay)
{
sc_time notificationDelay = delay + Configuration::getInstance().memSpec->tCK;
if (phase == BEGIN_REQ)
{
transToAcquire.payload = &trans;
transToAcquire.time = sc_time_stamp() + notificationDelay;
beginReqEvent.notify(notificationDelay);
transToAcquire.time = sc_time_stamp() + delay;
beginReqEvent.notify(delay);
}
else if (phase == END_RESP)
{
transToRelease.time = sc_time_stamp() + notificationDelay;
endRespEvent.notify(notificationDelay);
transToRelease.time = sc_time_stamp() + delay;
endRespEvent.notify(delay);
}
else
SC_REPORT_FATAL("Controller", "nb_transport_fw in controller was triggered with unknown phase");
PRINTDEBUGMESSAGE(name(), "[fw] " + getPhaseName(phase) + " notification in " +
notificationDelay.to_string());
delay.to_string());
return TLM_ACCEPTED;
}

View File

@@ -72,13 +72,13 @@ protected:
virtual void sendToFrontend(tlm::tlm_generic_payload *, tlm::tlm_phase);
virtual void sendToDram(Command, tlm::tlm_generic_payload *);
MemSpec *memSpec;
private:
unsigned totalNumberOfPayloads = 0;
std::vector<unsigned> ranksNumberOfPayloads;
ReadyCommands readyCommands;
MemSpec *memSpec;
std::vector<BankMachine *> bankMachines;
std::vector<std::vector<BankMachine *>> bankMachinesOnRank;
CmdMuxIF *cmdMux;

View File

@@ -65,7 +65,10 @@ void ControllerRecordable::sendToDram(Command command, tlm_generic_payload *payl
TimeInterval dataStrobe = Configuration::getInstance().memSpec->getIntervalOnDataStrobe(command);
tlmRecorder->updateDataStrobe(dataStrobe.start, dataStrobe.end, *payload);
}
Controller::sendToDram(command, payload);
sc_time delay = SC_ZERO_TIME;
tlm_phase phase = commandToPhase(command);
iSocket->nb_transport_fw(*payload, phase, delay);
}
void ControllerRecordable::recordPhase(tlm_generic_payload &trans, tlm_phase phase, sc_time delay)

View File

@@ -42,38 +42,47 @@
using namespace tlm;
Arbiter::Arbiter(sc_module_name name, std::string pathToAddressMapping) :
sc_module(name), payloadEventQueue(this, &Arbiter::peqCallback)
sc_module(name), payloadEventQueue(this, &Arbiter::peqCallback),
maxActiveTransactions(Configuration::getInstance().maxActiveTransactions),
tCK(Configuration::getInstance().memSpec->tCK)
{
// The arbiter communicates with one or more memory unity through one or more sockets (one or more memory channels).
// Each of the arbiter's initiator sockets is bound to a memory controller's target socket.
// Anytime an transaction comes from a memory unity to the arbiter the "bw" callback is called.
iSocket.register_nb_transport_bw(this, &Arbiter::nb_transport_bw);
for (size_t i = 0; i < Configuration::getInstance().memSpec->numberOfChannels; ++i)
{
channelIsBusy.push_back(false);
pendingRequests.push_back(std::queue<tlm_generic_payload *>());
nextChannelPayloadIDToAppend.push_back(0);
}
// One or more devices can accesss all the memory units through the arbiter.
// Devices' initiator sockets are bound to arbiter's target sockets.
// As soon the arbiter receives a request in any of its target sockets it should treat and forward it to the proper memory channel.
tSocket.register_nb_transport_fw(this, &Arbiter::nb_transport_fw);
tSocket.register_transport_dbg(this, &Arbiter::transport_dbg);
addressDecoder = new AddressDecoder(pathToAddressMapping);
addressDecoder->print();
}
// Initiated by initiator side
// This function is called when an arbiter's target socket receives a transaction from a device
Arbiter::~Arbiter()
{
delete addressDecoder;
}
void Arbiter::end_of_elaboration()
{
for (unsigned i = 0; i < tSocket.size(); i++) // initiator side
{
threadIsBusy.push_back(false);
pendingResponses.push_back(std::queue<tlm_generic_payload *>());
nextThreadPayloadIDToAppend.push_back(0);
activeTransactions.push_back(0);
outstandingEndReq.push_back(nullptr);
}
for (unsigned i = 0; i < iSocket.size(); i++) // channel side
{
channelIsBusy.push_back(false);
pendingRequests.push_back(std::queue<tlm_generic_payload *>());
nextChannelPayloadIDToAppend.push_back(0);
}
}
tlm_sync_enum Arbiter::nb_transport_fw(int id, tlm_generic_payload &payload,
tlm_phase &phase, sc_time &fwDelay)
{
sc_time notDelay = std::ceil((sc_time_stamp() + fwDelay) / Configuration::getInstance().memSpec->tCK)
* Configuration::getInstance().memSpec->tCK - sc_time_stamp();
sc_time notDelay = std::ceil((sc_time_stamp() + fwDelay) / tCK)
* tCK - sc_time_stamp();
if (phase == BEGIN_REQ)
{
@@ -82,16 +91,9 @@ tlm_sync_enum Arbiter::nb_transport_fw(int id, tlm_generic_payload &payload,
payload.set_address(payload.get_address() -
Configuration::getInstance().addressOffset);
// In the begin request phase the socket ID is appended to the payload.
// It will extracted from the payload and used later.
appendDramExtension(id, payload, fwDelay);
payload.acquire();
}
else if (phase == END_RESP)
{
// TODO: why one additional cycle?
notDelay += Configuration::getInstance().memSpec->tCK;
}
PRINTDEBUGMESSAGE(name(), "[fw] " + getPhaseName(phase) + " notification in " +
notDelay.to_string());
@@ -99,8 +101,6 @@ tlm_sync_enum Arbiter::nb_transport_fw(int id, tlm_generic_payload &payload,
return TLM_ACCEPTED;
}
// Initiated by dram side
// This function is called when an arbiter's initiator socket receives a transaction from a memory controller
tlm_sync_enum Arbiter::nb_transport_bw(int, tlm_generic_payload &payload,
tlm_phase &phase, sc_time &bwDelay)
{
@@ -112,7 +112,6 @@ tlm_sync_enum Arbiter::nb_transport_bw(int, tlm_generic_payload &payload,
unsigned int Arbiter::transport_dbg(int /*id*/, tlm::tlm_generic_payload &trans)
{
// adjust address offset:
trans.set_address(trans.get_address() -
Configuration::getInstance().addressOffset);
@@ -125,87 +124,120 @@ void Arbiter::peqCallback(tlm_generic_payload &payload, const tlm_phase &phase)
unsigned int threadId = DramExtension::getExtension(payload).getThread().ID();
unsigned int channelId = DramExtension::getExtension(payload).getChannel().ID();
// Phases initiated by the intiator side from arbiter's point of view (devices performing memory requests to the arbiter)
if (phase == BEGIN_REQ)
if (phase == BEGIN_REQ) // from initiator
{
if (!channelIsBusy[channelId])
{
tlm_phase tPhase = BEGIN_REQ;
sc_time tDelay = SC_ZERO_TIME;
iSocket[static_cast<int>(channelId)]->nb_transport_fw(payload, tPhase, tDelay);
activeTransactions[threadId]++;
// This channel was available. Forward the new transaction to the memory controller.
channelIsBusy[channelId] = true;
}
else
{
// This channel is busy. Enqueue the new transaction which phase is BEGIN_REQ.
pendingRequests[channelId].push(&payload);
}
}
// Phases initiated by the target side from arbiter's point of view (memory side)
else if (phase == END_REQ)
{
// The arbiter receives a transaction which phase is END_REQ from memory controller and forwards it to the requester device.
if (activeTransactions[threadId] < maxActiveTransactions)
{
tlm_phase tPhase = END_REQ;
sc_time tDelay = SC_ZERO_TIME;
tSocket[static_cast<int>(threadId)]->nb_transport_bw(payload, tPhase, tDelay);
}
else
{
outstandingEndReq[threadId] = &payload;
}
// This channel is now free! Dispatch a new transaction (phase is BEGIN_REQ) from the queue, if any. Send it to the memory controller.
if (!channelIsBusy[channelId])
{
channelIsBusy[channelId] = true;
tlm_phase tPhase = BEGIN_REQ;
sc_time tDelay = tCK;
if (pendingRequests[channelId].empty())
{
iSocket[static_cast<int>(channelId)]->nb_transport_fw(payload, tPhase, tDelay);
}
else
{
tlm_generic_payload &tPayload = *pendingRequests[channelId].front();
pendingRequests[channelId].pop();
pendingRequests[channelId].push(&payload);
iSocket[static_cast<int>(channelId)]->nb_transport_fw(tPayload, tPhase, tDelay);
}
}
else
{
pendingRequests[channelId].push(&payload);
}
}
else if (phase == END_REQ) // from memory controller
{
if (!pendingRequests[channelId].empty())
{
// Send ONE of the enqueued new transactions (phase is BEGIN_REQ) through this channel.
tlm_generic_payload &payloadToSend = *pendingRequests[channelId].front();
tlm_generic_payload &tPayload = *pendingRequests[channelId].front();
pendingRequests[channelId].pop();
tlm_phase tPhase = BEGIN_REQ;
sc_time tDelay = SC_ZERO_TIME;
iSocket[static_cast<int>(channelId)]->nb_transport_fw(payloadToSend, tPhase, tDelay);
sc_time tDelay = tCK;
iSocket[static_cast<int>(channelId)]->nb_transport_fw(tPayload, tPhase, tDelay);
}
else
{
channelIsBusy[channelId] = false;
}
}
else if (phase == BEGIN_RESP)
else if (phase == BEGIN_RESP) // from memory controller
{
// TODO: use early completion
{
tlm_phase tPhase = END_RESP;
sc_time tDelay = SC_ZERO_TIME;
iSocket[static_cast<int>(channelId)]->nb_transport_fw(payload, tPhase, tDelay);
}
if (!threadIsBusy[threadId])
{
tlm_phase tPhase = BEGIN_RESP;
sc_time tDelay = SC_ZERO_TIME;
tlm_sync_enum returnValue = tSocket[static_cast<int>(threadId)]->nb_transport_bw(payload, tPhase, tDelay);
if (returnValue == TLM_UPDATED)
payloadEventQueue.notify(payload, tPhase, tDelay);
threadIsBusy[threadId] = true;
tlm_phase tPhase = BEGIN_RESP;
sc_time tDelay = tCK;
if (pendingResponses[threadId].empty())
{
tlm_sync_enum returnValue = tSocket[static_cast<int>(threadId)]->nb_transport_bw(payload, tPhase, tDelay);
if (returnValue == TLM_UPDATED)
payloadEventQueue.notify(payload, tPhase, tDelay);
}
else
{
tlm_generic_payload &tPayload = *pendingResponses[threadId].front();
pendingResponses[threadId].pop();
pendingResponses[threadId].push(&payload);
tlm_sync_enum returnValue = tSocket[static_cast<int>(threadId)]->nb_transport_bw(tPayload, tPhase, tDelay);
if (returnValue == TLM_UPDATED)
payloadEventQueue.notify(tPayload, tPhase, tDelay);
}
}
else
{
pendingResponses[threadId].push(&payload);
}
}
else if (phase == END_RESP)
else if (phase == END_RESP) // from initiator
{
// Send the END_RESP message to the memory
{
tlm_phase tPhase = END_RESP;
sc_time tDelay = SC_ZERO_TIME;
iSocket[static_cast<int>(channelId)]->nb_transport_fw(payload, tPhase, tDelay);
}
// Drop one element of the queue of BEGIN_RESP from memory to this device
payload.release();
// Check if there are queued transactoins with phase BEGIN_RESP from memory to this device
if (activeTransactions[threadId] == maxActiveTransactions)
{
tlm_phase tPhase = END_REQ;
sc_time tDelay = SC_ZERO_TIME;
tlm_generic_payload &tPayload = *outstandingEndReq[threadId];
tSocket[static_cast<int>(threadId)]->nb_transport_bw(tPayload, tPhase, tDelay);
}
activeTransactions[threadId]--;
if (!pendingResponses[threadId].empty())
{
// The queue is not empty.
tlm_generic_payload &payloadToSend = *pendingResponses[threadId].front();
tlm_generic_payload &tPayload = *pendingResponses[threadId].front();
pendingResponses[threadId].pop();
tlm_phase tPhase = BEGIN_RESP;
sc_time tDelay = SC_ZERO_TIME;
tlm_sync_enum returnValue = tSocket[static_cast<int>(threadId)]->nb_transport_bw(payloadToSend, tPhase, tDelay);
sc_time tDelay = tCK;
tlm_sync_enum returnValue = tSocket[static_cast<int>(threadId)]->nb_transport_bw(tPayload, tPhase, tDelay);
if (returnValue == TLM_UPDATED)
payloadEventQueue.notify(payloadToSend, tPhase, tDelay);
payloadEventQueue.notify(tPayload, tPhase, tDelay);
}
else
{

View File

@@ -43,7 +43,6 @@
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <tlm_utils/multi_passthrough_target_socket.h>
#include <tlm_utils/multi_passthrough_initiator_socket.h>
#include <tlm_utils/peq_with_cb_and_phase.h>
@@ -59,38 +58,39 @@ public:
Arbiter(sc_module_name, std::string);
SC_HAS_PROCESS(Arbiter);
virtual ~Arbiter();
private:
virtual void end_of_elaboration() override;
AddressDecoder *addressDecoder;
tlm_utils::peq_with_cb_and_phase<Arbiter> payloadEventQueue;
std::vector<bool> channelIsBusy;
// used to account for the request_accept_delay in the dram controllers
// This is a queue of new transactions. The phase of a new request is BEGIN_REQ.
std::vector<std::queue<tlm::tlm_generic_payload *>> pendingRequests;
// used to account for the response_accept_delay in the initiators (traceplayer, core etc.)
// This is a queue of responses comming from the memory side. The phase of these transactions is BEGIN_RESP.
std::unordered_map<unsigned int, std::queue<tlm::tlm_generic_payload *>> pendingResponses;
std::unordered_map<unsigned int, bool> threadIsBusy;
// Initiated by initiator side
// This function is called when an arbiter's target socket receives a transaction from a device
tlm::tlm_sync_enum nb_transport_fw(int id, tlm::tlm_generic_payload &payload,
tlm::tlm_phase &phase, sc_time &fwDelay);
// Initiated by dram side
// This function is called when an arbiter's initiator socket receives a transaction from a memory controller
tlm::tlm_sync_enum nb_transport_bw(int, tlm::tlm_generic_payload &payload,
tlm::tlm_phase &phase, sc_time &bwDelay);
unsigned int transport_dbg(int /*id*/, tlm::tlm_generic_payload &trans);
void peqCallback(tlm::tlm_generic_payload &payload, const tlm::tlm_phase &phase);
void appendDramExtension(int socketId, tlm::tlm_generic_payload &payload, sc_time delay);
std::vector<bool> threadIsBusy;
std::vector<bool> channelIsBusy;
std::vector<std::queue<tlm::tlm_generic_payload *>> pendingResponses;
std::vector<std::queue<tlm::tlm_generic_payload *>> pendingRequests;
std::vector<uint64_t> nextThreadPayloadIDToAppend;
std::vector<uint64_t> nextChannelPayloadIDToAppend;
std::unordered_map<unsigned int, uint64_t> nextThreadPayloadIDToAppend;
std::vector<unsigned int> activeTransactions;
const unsigned maxActiveTransactions;
std::vector<tlm::tlm_generic_payload *> outstandingEndReq;
tlm::tlm_sync_enum nb_transport_fw(int id, tlm::tlm_generic_payload &payload,
tlm::tlm_phase &phase, sc_time &fwDelay);
tlm::tlm_sync_enum nb_transport_bw(int, tlm::tlm_generic_payload &payload,
tlm::tlm_phase &phase, sc_time &bwDelay);
unsigned int transport_dbg(int /*id*/, tlm::tlm_generic_payload &trans);
void appendDramExtension(int socketId, tlm::tlm_generic_payload &payload, sc_time delay);
sc_time tCK;
};
#endif // ARBITER_H