Improved arbiter with thread and channel queues.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user