From f6752cb09ad573d6495c31e3eebf6058c0c51a0a Mon Sep 17 00:00:00 2001 From: Lukas Steiner Date: Tue, 27 Oct 2020 16:02:11 +0100 Subject: [PATCH] Improved arbiter with thread and channel queues. --- .../src/configuration/Configuration.cpp | 2 + .../library/src/configuration/Configuration.h | 1 + DRAMSys/library/src/controller/Controller.cpp | 12 +- DRAMSys/library/src/controller/Controller.h | 4 +- .../src/controller/ControllerRecordable.cpp | 5 +- DRAMSys/library/src/simulation/Arbiter.cpp | 182 ++++++++++-------- DRAMSys/library/src/simulation/Arbiter.h | 52 ++--- 7 files changed, 147 insertions(+), 111 deletions(-) diff --git a/DRAMSys/library/src/configuration/Configuration.cpp b/DRAMSys/library/src/configuration/Configuration.cpp index 3f6739dd..b4fb45b9 100644 --- a/DRAMSys/library/src/configuration/Configuration.cpp +++ b/DRAMSys/library/src/configuration/Configuration.cpp @@ -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; diff --git a/DRAMSys/library/src/configuration/Configuration.h b/DRAMSys/library/src/configuration/Configuration.h index a77a9fa1..cad71c04 100644 --- a/DRAMSys/library/src/configuration/Configuration.h +++ b/DRAMSys/library/src/configuration/Configuration.h @@ -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"; diff --git a/DRAMSys/library/src/controller/Controller.cpp b/DRAMSys/library/src/controller/Controller.cpp index bc99b197..f27a1511 100644 --- a/DRAMSys/library/src/controller/Controller.cpp +++ b/DRAMSys/library/src/controller/Controller.cpp @@ -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; } diff --git a/DRAMSys/library/src/controller/Controller.h b/DRAMSys/library/src/controller/Controller.h index 4fef0c65..7bcd2eee 100644 --- a/DRAMSys/library/src/controller/Controller.h +++ b/DRAMSys/library/src/controller/Controller.h @@ -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 ranksNumberOfPayloads; ReadyCommands readyCommands; - MemSpec *memSpec; - std::vector bankMachines; std::vector> bankMachinesOnRank; CmdMuxIF *cmdMux; diff --git a/DRAMSys/library/src/controller/ControllerRecordable.cpp b/DRAMSys/library/src/controller/ControllerRecordable.cpp index 37c33594..5d3264bf 100644 --- a/DRAMSys/library/src/controller/ControllerRecordable.cpp +++ b/DRAMSys/library/src/controller/ControllerRecordable.cpp @@ -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) diff --git a/DRAMSys/library/src/simulation/Arbiter.cpp b/DRAMSys/library/src/simulation/Arbiter.cpp index c472f0be..6c50b9be 100644 --- a/DRAMSys/library/src/simulation/Arbiter.cpp +++ b/DRAMSys/library/src/simulation/Arbiter.cpp @@ -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()); - 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()); + 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()); + 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(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(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(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(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(channelId)]->nb_transport_fw(payloadToSend, tPhase, tDelay); + sc_time tDelay = tCK; + iSocket[static_cast(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(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(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(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(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(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(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(threadId)]->nb_transport_bw(payloadToSend, tPhase, tDelay); + sc_time tDelay = tCK; + tlm_sync_enum returnValue = tSocket[static_cast(threadId)]->nb_transport_bw(tPayload, tPhase, tDelay); if (returnValue == TLM_UPDATED) - payloadEventQueue.notify(payloadToSend, tPhase, tDelay); + payloadEventQueue.notify(tPayload, tPhase, tDelay); } else { diff --git a/DRAMSys/library/src/simulation/Arbiter.h b/DRAMSys/library/src/simulation/Arbiter.h index 43201b25..61b26a06 100644 --- a/DRAMSys/library/src/simulation/Arbiter.h +++ b/DRAMSys/library/src/simulation/Arbiter.h @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -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 payloadEventQueue; - - std::vector 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> 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> pendingResponses; - std::unordered_map 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 threadIsBusy; + std::vector channelIsBusy; + + std::vector> pendingResponses; + std::vector> pendingRequests; + + std::vector nextThreadPayloadIDToAppend; std::vector nextChannelPayloadIDToAppend; - std::unordered_map nextThreadPayloadIDToAppend; + + std::vector activeTransactions; + const unsigned maxActiveTransactions; + + std::vector 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