From ae09bb105cb91501f6d12d576bbe71888086dfd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89der=20Ferreira=20Zulian?= Date: Fri, 12 Jun 2015 10:52:06 +0200 Subject: [PATCH] First steps on inplementing full support to multiple memory channels/controllers based on config Changes in the Arbiter block: - now multiple initiator sockets are possible. These intiator sockets connect to multiple memory controllers; - added lots of comments to the code in order to make it easier to understand. Changes in Simulaton[cpp,h]: - added TODO messages in some points that need to be changed to provide full support to multiple memory channles/controllers based on configuration. --- dram/src/simulation/Arbiter.h | 140 +++++++++++++++-------------- dram/src/simulation/Simulation.cpp | 23 ++++- dram/src/simulation/Simulation.h | 8 +- 3 files changed, 101 insertions(+), 70 deletions(-) diff --git a/dram/src/simulation/Arbiter.h b/dram/src/simulation/Arbiter.h index 41e187be..bdc1f1bc 100644 --- a/dram/src/simulation/Arbiter.h +++ b/dram/src/simulation/Arbiter.h @@ -53,34 +53,42 @@ using namespace std; using namespace tlm; -template -struct Arbiter: public sc_module -{ +template< + unsigned int NUMBER_OF_THREADS = 1, + unsigned int BUSWIDTH = 128, + unsigned int NUMBER_OF_CHANNELS = 1 +> +struct Arbiter: public sc_module { public: - tlm_utils::simple_initiator_socket iSocket; + tlm_utils::simple_initiator_socket_tagged iSocket[NUMBER_OF_CHANNELS]; tlm_utils::simple_target_socket_tagged tSockets[NUMBER_OF_THREADS]; - SC_CTOR(Arbiter) : - payloadEventQueue(this, &Arbiter::peqCallback), channelIsFree(true) - { - iSocket.register_nb_transport_bw(this, &Arbiter::nb_transport_bw); - - for (unsigned int i = 0; i < NUMBER_OF_THREADS; ++i) - { + SC_CTOR(Arbiter) : payloadEventQueue(this, &Arbiter::peqCallback) { + for (unsigned int i = 0; i < NUMBER_OF_CHANNELS; ++i) { + // The arbiter communicates with one or more memory controller through one or more sockets (one or more memory channels). + // Each of the arbiter's iniitator sockets is binded to a memory controller's target socket. + // In this case the arbiter is the initiator. + iSocket[i].register_nb_transport_bw(this, &Arbiter::nb_transport_bw, i); + channelIsFree[i] = true; + } + for (unsigned int i = 0; i < NUMBER_OF_THREADS; ++i) { + // One or more devices can accesss the memory throught the arbiter. A device can be a processor's core, the execution of a previously recorded trace file, etc. + // As soon the arbiter receives a request in any of its target sockets it should treat and forward it to the proper memory channel. + // In this case the arbiter acts as a target. tSockets[i].register_nb_transport_fw(this, &Arbiter::nb_transport_fw, i); } } private: tlm_utils::peq_with_cb_and_phase payloadEventQueue; - bool channelIsFree; + bool channelIsFree[NUMBER_OF_CHANNELS]; //used to account for the request_accept_delay in the dram controllers - deque pendingRequests; + deque pendingRequests[NUMBER_OF_CHANNELS]; //used to account for the response_accept_delay in the initiators (traceplayer,core etc.) - deque receivedResponses[NUMBER_OF_THREADS]; + deque receivedResponses[NUMBER_OF_THREADS]; // Initiated by dram side - tlm_sync_enum nb_transport_bw(tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay) + tlm_sync_enum nb_transport_bw(__attribute__((unused)) int socketId, tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay) { TlmRecorder::getInstance().recordPhase(payload, phase, bwDelay + sc_time_stamp()); payloadEventQueue.notify(payload, phase, bwDelay); @@ -88,16 +96,15 @@ private: } // Initiated by initiator side - tlm_sync_enum nb_transport_fw(int socketId, tlm_generic_payload& payload, tlm_phase& phase, - sc_time& fwDelay) + // Anytime the arbiter receives a message from a device this callback function is called + tlm_sync_enum nb_transport_fw(int socketId, tlm_generic_payload& payload, tlm_phase& phase, sc_time& fwDelay) { - if(phase == BEGIN_REQ) - { + if (phase == BEGIN_REQ) { + // In the begin request phase the socket ID is appended to the payload. + // It will extracted from the payload and used later. appendDramExtension(socketId, payload); payload.acquire(); - } - else if(phase == END_RESP) - { + } else if (phase == END_RESP) { payload.release(); } @@ -108,65 +115,67 @@ private: void peqCallback(tlm_generic_payload& payload, const tlm_phase& phase) { unsigned int initiatorSocket = DramExtension::getExtension(payload).getThread().ID()-1; + unsigned int channelId = DramExtension::getExtension(payload).getChannel().ID(); - - //Phases initiated by intiator side - if (phase == BEGIN_REQ) - { - if(channelIsFree) - { - channelIsFree = false; - sendToChannel(payload, phase, SC_ZERO_TIME ); + // Phases initiated by the intiator side from arbiter's point of view (devices performing memory requests to the arbiter) + if (phase == BEGIN_REQ) { + if (channelIsFree[channelId]) { + // If the channel is available forward the request to the memory controller + channelIsFree[channelId] = false; + sendToChannel(channelId, payload, phase, SC_ZERO_TIME); + } else { + // If the channel is not available put the request in a queue. + pendingRequests[channelId].push_back(&payload); } - else - { - pendingRequests.push_back(&payload); - } - } - - else if (phase == END_RESP) - { - sendToChannel(payload, phase, SC_ZERO_TIME); + } else if (phase == END_RESP) { + // Send the END_RESP message to the memory + sendToChannel(channelId, payload, phase, SC_ZERO_TIME); + // Drop one element of the queue of BEGIN_RESP from memory to this device receivedResponses[initiatorSocket].pop_front(); - if(!receivedResponses[initiatorSocket].empty()) - { + + // Check if there are queued BEGIN_RESP messages from memory to this device + if (!receivedResponses[initiatorSocket].empty()) { + // The queue is not empty. tlm_generic_payload* payloadToSend = receivedResponses[initiatorSocket].front(); - sendToInitiator(initiatorSocket,*payloadToSend,BEGIN_RESP,SC_ZERO_TIME); + // Send ONE extra BEGIN_RESP to the device + sendToInitiator(initiatorSocket, *payloadToSend, BEGIN_RESP, SC_ZERO_TIME); } } - //Phases initiated by dram side - else if (phase == END_REQ) - { - channelIsFree = true; + // Phases initiated by the target side from arbiter's point of view (memory controllers) + else if (phase == END_REQ) { + channelIsFree[channelId] = true; + // The arbiter receives an END_REQ from memory controller and + // forward it to the requester device. sendToInitiator(initiatorSocket, payload, phase, SC_ZERO_TIME); - if(!pendingRequests.empty()) - { - tlm_generic_payload* payloadToSend = pendingRequests.front(); - pendingRequests.pop_front(); - sendToChannel(*payloadToSend, BEGIN_REQ, SC_ZERO_TIME ); - channelIsFree = false; + // This channel is now free! Dispatch a queued BEGIN_REQ, if any. Send it to the memory controller. + if (!pendingRequests[channelId].empty()) { + tlm_generic_payload* payloadToSend = pendingRequests[channelId].front(); + pendingRequests[channelId].pop_front(); + // Send ONE enqueued BEGIN_REQ to throgh this channel. + sendToChannel(channelId, *payloadToSend, BEGIN_REQ, SC_ZERO_TIME); + // Mark the channel as busy again. + channelIsFree[channelId] = false; } - } - else if (phase == BEGIN_RESP) - { - if(receivedResponses[initiatorSocket].empty()) + } else if (phase == BEGIN_RESP) { + // The arbiter receives a BEGIN_RESP from memory controller and + // forwards it to the requester device + if (receivedResponses[initiatorSocket].empty()) sendToInitiator(initiatorSocket, payload, phase, SC_ZERO_TIME); + // Enqueue the BEGIN_RESP. It will be dequeued later on when the + // initiator device sends an END_RESP receivedResponses[initiatorSocket].push_back(&payload); - } - - else - { + } else { SC_REPORT_FATAL(0, "Payload event queue in arbiter was triggered with unknown phase"); } } - void sendToChannel(tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) + void sendToChannel(unsigned int channelId, tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) { tlm_phase TPhase = phase; sc_time TDelay = delay; - iSocket->nb_transport_fw(payload, TPhase, TDelay); + iSocket[channelId]->nb_transport_fw(payload, TPhase, TDelay); } void sendToInitiator(unsigned int id, tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay) @@ -180,13 +189,10 @@ private: { unsigned int burstlength = payload.get_streaming_width(); DecodedAddress decodedAddress = xmlAddressDecoder::getInstance().decodeAddress(payload.get_address()); - DramExtension* extension = new DramExtension(Thread(socketId+1), Channel(0), Bank(decodedAddress.bank), - BankGroup(decodedAddress.bankgroup), Row(decodedAddress.row), Column(decodedAddress.column),burstlength); + DramExtension* extension = new DramExtension(Thread(socketId+1), Channel(decodedAddress.channel), Bank(decodedAddress.bank), BankGroup(decodedAddress.bankgroup), Row(decodedAddress.row), Column(decodedAddress.column),burstlength); payload.set_auto_extension(extension); } }; - - - #endif /* ARBITER_H_ */ + diff --git a/dram/src/simulation/Simulation.cpp b/dram/src/simulation/Simulation.cpp index 139dd5dc..0cc92723 100644 --- a/dram/src/simulation/Simulation.cpp +++ b/dram/src/simulation/Simulation.cpp @@ -99,10 +99,23 @@ void Simulation::setupTlmRecorder(const string &traceName, const string &pathToR void Simulation::instantiateModules(const string &pathToResources, const std::vector& devices) { + // TODO: the number of Drams objects or the number of target sockets in a Dram object should be proportional to the number of controllers/channles (info from config) dram = new Dram<>("dram"); - arbiter = new Arbiter("arbiter"); + arbiter = new Arbiter("arbiter"); + + // TODO: the number of controllers should be the number of channesl, right?! + // each of the arbiter's initiators sockets should bind to a controller... + // The number of channels is equal the number of controllers and comes from config. controller = new Controller<>("controller"); + //TODO: this depends (or should depend) on config... Should be dynamic not fixed to four! +#if 0 + vector players; + for (int i = 0; i < NumberOfTracePlayers; i++) { + std::string playerStr = "player" + std::to_string(i); + players.push_back(new StlPlayer<>(playerStr, pathToResources + string("traces/") + devices[0].trace, devices[0].clkMhz, this)); + } +#endif player1 = new StlPlayer<>("player1", pathToResources + string("traces/") + devices[0].trace, devices[0].clkMhz, this); player2 = new StlPlayer<>("player2", pathToResources + string("traces/") + devices[1].trace, devices[1].clkMhz, this); player3 = new StlPlayer<>("player3", pathToResources + string("traces/") + devices[2].trace, devices[2].clkMhz, this); @@ -115,7 +128,11 @@ void Simulation::bindSockets() player2->iSocket.bind(arbiter->tSockets[1]); player3->iSocket.bind(arbiter->tSockets[2]); player4->iSocket.bind(arbiter->tSockets[3]); - arbiter->iSocket.bind(controller->tSocket); + + // TODO: the number of controllers should be the number of channesl, right?! + // each of the arbiter's initiators sockets should bind to a controller... + arbiter->iSocket[0].bind(controller->tSocket); + controller->iSocket.bind(dram->tSocket); } @@ -123,6 +140,7 @@ Simulation::~Simulation() { delete dram; delete arbiter; + // TODO: the number of components should come from config delete controller; delete player1; delete player2; @@ -138,6 +156,7 @@ void Simulation::start() report(" -> memspec: \t\t" + Configuration::getInstance().memSpec.MemoryId); cout << endl; simulationStartTime = clock(); + // TODO: the number of components should come from config player1->nextPayload(); player2->nextPayload(); player3->nextPayload(); diff --git a/dram/src/simulation/Simulation.h b/dram/src/simulation/Simulation.h index 1458d201..dd50e1d1 100644 --- a/dram/src/simulation/Simulation.h +++ b/dram/src/simulation/Simulation.h @@ -84,7 +84,10 @@ public: void stop(); virtual void tracePlayerTerminates() override; + // TODO: this information should be get from configuration constexpr static unsigned int NumberOfTracePlayers = 4; + // TODO: this information should be get from configuration + constexpr static unsigned int NumberOfMemChannels = 1; private: std::string traceName; @@ -92,11 +95,14 @@ private: sc_event terminateSimulation; + // TODO: here should be a vector or some other abstract data type and elements should be dynamically allocated based on configuration Dram<> *dram; - Arbiter *arbiter; + Arbiter *arbiter; + // TODO: here should be a vector or some other abstract data type and elements should be dynamically allocated based on configuration Controller<> *controller; ReorderBuffer<> *reorder; + // TODO: here should be a vector or some other abstract data type and elements should be dynamically allocated based on configuration TracePlayer<> *player1; TracePlayer<> *player2; TracePlayer<> *player3;