diff --git a/src/mem/MemObject.py b/src/mem/MemObject.py --- a/src/mem/MemObject.py +++ b/src/mem/MemObject.py @@ -27,8 +27,13 @@ # Authors: Ron Dreslinski from ClockedObject import ClockedObject +from FlowControl import * +from m5.params import * class MemObject(ClockedObject): type = 'MemObject' abstract = True cxx_header = "mem/mem_object.hh" + + master_flow_control = Param.BaseFlowControl(NullFlowControl(), "") + slave_flow_control = Param.BaseFlowControl(NullFlowControl(), "") diff --git a/src/mem/SConscript b/src/mem/SConscript --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -39,6 +39,7 @@ SimObject('DRAMCtrl.py') SimObject('ExternalMaster.py') SimObject('ExternalSlave.py') +SimObject('FlowControl.py') SimObject('MemObject.py') SimObject('SimpleMemory.py') SimObject('XBar.py') @@ -53,6 +54,7 @@ Source('dram_ctrl.cc') Source('external_master.cc') Source('external_slave.cc') +Source('flow_control.cc') Source('mem_object.cc') Source('mport.cc') Source('noncoherent_xbar.cc') @@ -64,6 +66,7 @@ Source('simple_mem.cc') Source('snoop_filter.cc') Source('stack_dist_calc.cc') +Source('token_flow.cc') Source('tport.cc') Source('xbar.cc') Source('hmc_controller.cc') @@ -107,6 +110,7 @@ DebugFlag("DRAMSim2") DebugFlag('HMCController') DebugFlag('SerialLink') +DebugFlag('TokenFlow') DebugFlag("MemChecker") DebugFlag("MemCheckerMonitor") diff --git a/src/mem/dram_ctrl.cc b/src/mem/dram_ctrl.cc --- a/src/mem/dram_ctrl.cc +++ b/src/mem/dram_ctrl.cc @@ -50,6 +50,8 @@ #include "debug/DRAMState.hh" #include "debug/Drain.hh" #include "mem/dram_ctrl.hh" +#include "mem/flow_control.hh" +#include "mem/token_flow.hh" #include "sim/system.hh" using namespace std; @@ -269,6 +271,15 @@ // start of simulation busBusyUntil = curTick() + tRP + tRCD + tCL; } + + // if we are using flow control, setup it's parameters now + if (port.getFlowControl()) { + BaseFlowControl *flow_control = port.getFlowControl(); + + flow_control->setBufferSize(unifiedBufferSize); + flow_control->setFlitSize(64); + flow_control->init(); + } } Tick @@ -961,6 +972,11 @@ pkt->getAddr()); assert(unifiedPending.count(burstAlign(pkt->getAddr()))); unifiedPending.erase(burstAlign(pkt->getAddr())); + + // we freed a resource. Let flow control know this. + if (port.getFlowControl()) { + port.getFlowControl()->sendNotifyAvail(pkt); + } } bool needsResponse = pkt->needsResponse(); diff --git a/src/mem/flow_control.hh b/src/mem/flow_control.hh new file mode 100644 --- /dev/null +++ b/src/mem/flow_control.hh @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * For use for simulation and test purposes only + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Matthew Poremba + */ +#ifndef __MEM_FLOW_CONTROL_HH__ +#define __MEM_FLOW_CONTROL_HH__ + +#include +#include + +#include "mem/port.hh" +#include "params/NullFlowControl.hh" +#include "sim/sim_object.hh" + +class BaseFlowControl : public SimObject +{ + public: + /** + * Defines IDs for overloadable methods. These are used to simplify + * defining port behavior in individual MemObjects, since you do not + * need to define a class inheriting this class to call one function. + */ + enum FlowControlFunc : unsigned { + sendQueryAvailFunc, + recvQueryAvailFunc, + sendNotifyAvailFunc, + recvNotifyAvailFunc + }; + + BaseFlowControl(BaseFlowControlParams *params); + + // One of these must be called when the corresponding port is connected. + void setMasterPort(BaseMasterPort *master) { _master = master; } + void setSlavePort(BaseSlavePort *slave) { _slave = slave; } + + virtual void init() { } + + virtual void setFlitSize(int flitSize) { _flitSize = flitSize; } + virtual int getFlitSize() const { return _flitSize; } + + virtual void setBufferSize(int bufferSize) { _bufferSize = bufferSize; } + virtual int getBufferSize() const { return _bufferSize; } + + /* + * Inform peer port's flow control that we may send/recv to/from them. + */ + virtual void addProducer(const MasterID mid) { } + + /* + * Remove ourself from peer port's flow control producers. + */ + virtual void removeProducer(const MasterID mid) { } + + /** + * Send a request to reserve enough space for packet pkt. This should be + * called before sendTimingReq. + */ + virtual bool sendQueryAvail(PacketPtr pkt); + + /** + * The peer port is indictating it would like to send a packet. + */ + virtual bool recvQueryAvail(PacketPtr pkt); + + /** + * Send a message to peer port indicating we are now able to receive + * a packet. This should be called whenever resources are freed in the + * MemObject. + */ + virtual void sendNotifyAvail(PacketPtr pkt); + + /** + * Generic method to process a release sent by the peer port indicating + * it can now accept more packets from this master. + */ + virtual void recvNotifyAvail(PacketPtr pkt); + + /** + * If we are the flow controller for a master port, return a pointer to + * our peer slave port's flow controller. + */ + virtual BaseFlowControl *getSlaveFlowControl(); + + /** + * If we are the flow controller for a slave port, return a pointer to + * our peer master port's flow controller. + */ + virtual BaseFlowControl *getMasterFlowControl(); + + /** + * Specify a helper callback function to call by default. + */ + virtual void registerCallback(FlowControlFunc id, + std::function func); + + protected: + const BaseMasterPort *_master; + const BaseSlavePort *_slave; + + // Size of FLow control unIT in bytes. + int _flitSize; + + // amount of buffer space available in the port + int _bufferSize; + + // store any function callbacks + std::map> callbacks; +}; + +// This is used as the default class for python initialization. It simply +// overrides all virtual methods to do nothing (if they are not already). +class NullFlowControl : public BaseFlowControl +{ + public: + NullFlowControl(NullFlowControlParams *p) : BaseFlowControl(p) { } + + void registerCallback(FlowControlFunc id, + std::function func) + { } +}; + +#endif diff --git a/src/mem/flow_control.cc b/src/mem/flow_control.cc new file mode 100644 --- /dev/null +++ b/src/mem/flow_control.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * For use for simulation and test purposes only + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Matthew Poremba + */ +#include "mem/flow_control.hh" + +#include "mem/port.hh" + +BaseFlowControl::BaseFlowControl(BaseFlowControlParams *p) + : SimObject(p), _master(nullptr), _slave(nullptr), _flitSize(64), + _bufferSize(100) +{ +} + +bool +BaseFlowControl::sendQueryAvail(PacketPtr pkt) +{ + // Fallback to port's retry mechanism by default. This speculatively + // assumes there is always space in the slave port's buffer. + bool outcome = true; + if (callbacks.count(sendQueryAvailFunc)) { + outcome = callbacks[sendQueryAvailFunc](pkt); + } + return outcome; +} + +bool +BaseFlowControl::recvQueryAvail(PacketPtr pkt) +{ + // Fallback to port's retry mechanism by default. This speculatively + // assumes there is always space in the slave port's buffer. + bool outcome = true; + if (callbacks.count(recvQueryAvailFunc)) { + outcome = callbacks[recvQueryAvailFunc](pkt); + } + return outcome; +} + +void +BaseFlowControl::sendNotifyAvail(PacketPtr pkt) +{ + if (callbacks.count(sendNotifyAvailFunc)) { + (void)callbacks[sendNotifyAvailFunc](pkt); + } +} + +void +BaseFlowControl::recvNotifyAvail(PacketPtr pkt) +{ + if (callbacks.count(recvNotifyAvailFunc)) { + (void)callbacks[recvNotifyAvailFunc](pkt); + } +} + +BaseFlowControl* +BaseFlowControl::getSlaveFlowControl() +{ + return _master->getSlavePort().getFlowControl(); +} + +BaseFlowControl* +BaseFlowControl::getMasterFlowControl() +{ + return _slave->getMasterPort().getFlowControl(); +} + +void +BaseFlowControl::registerCallback(FlowControlFunc id, + std::function func) +{ + if (callbacks.count(id)) { + callbacks.erase(id); + } + + callbacks.insert(make_pair(id, func)); +} + +NullFlowControl* +NullFlowControlParams::create() +{ + return new NullFlowControl(this); +} diff --git a/src/mem/mem_object.hh b/src/mem/mem_object.hh --- a/src/mem/mem_object.hh +++ b/src/mem/mem_object.hh @@ -53,6 +53,8 @@ #include "params/MemObject.hh" #include "sim/clocked_object.hh" +class BaseFlowControl; + /** * The MemObject class extends the ClockedObject with accessor functions * to get its master and slave ports. @@ -91,6 +93,18 @@ */ virtual BaseSlavePort& getSlavePort(const std::string& if_name, PortID idx = InvalidPortID); + + /** + * Get a master flow controller associated with all master ports. + */ + BaseFlowControl* getMasterFlowControl() + { return params()->master_flow_control; } + + /** + * Get a slave flow controller associated with all slave ports. + */ + BaseFlowControl* getSlaveFlowControl() + { return params()->slave_flow_control; } }; #endif //__MEM_MEM_OBJECT_HH__ diff --git a/src/mem/port.hh b/src/mem/port.hh --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -55,6 +55,8 @@ class MemObject; +class BaseFlowControl; + /** * Ports are used to interface memory objects to each other. A port is * either a master or a slave and the connected peer is always of the @@ -79,6 +81,9 @@ /** A reference to the MemObject that owns this port. */ MemObject& owner; + /** Optional flow control for this port. */ + BaseFlowControl *_flowControl; + /** * Abstract base class for ports * @@ -101,6 +106,9 @@ /** Get the port id. */ PortID getId() const { return id; } + /** Get/set flow control pointers. */ + void setFlowControl(BaseFlowControl* flowControl); + BaseFlowControl *getFlowControl() const; }; /** Forward declaration */ diff --git a/src/mem/port.cc b/src/mem/port.cc --- a/src/mem/port.cc +++ b/src/mem/port.cc @@ -51,7 +51,7 @@ #include "mem/port.hh" Port::Port(const std::string &_name, MemObject& _owner, PortID _id) - : portName(_name), id(_id), owner(_owner) + : portName(_name), id(_id), owner(_owner), _flowControl(nullptr) { } @@ -59,6 +59,18 @@ { } +void +Port::setFlowControl(BaseFlowControl *flowControl) +{ + _flowControl = flowControl; +} + +BaseFlowControl* +Port::getFlowControl() const +{ + return _flowControl; +} + BaseMasterPort::BaseMasterPort(const std::string& name, MemObject* owner, PortID _id) : Port(name, *owner, _id), _baseSlavePort(NULL) diff --git a/src/mem/protocol/RubySlicc_Defines.sm b/src/mem/protocol/RubySlicc_Defines.sm --- a/src/mem/protocol/RubySlicc_Defines.sm +++ b/src/mem/protocol/RubySlicc_Defines.sm @@ -33,15 +33,6 @@ Cycles recycle_latency; // Functions implemented in the AbstractController class for -// making timing access to the memory maintained by the -// memory controllers. -void queueMemoryRead(MachineID id, Addr addr, Cycles latency); -void queueMemoryWrite(MachineID id, Addr addr, Cycles latency, - DataBlock block); -void queueMemoryWritePartial(MachineID id, Addr addr, Cycles latency, - DataBlock block, int size); - -// Functions implemented in the AbstractController class for // making functional access to the memory maintained by the // memory controllers. void functionalMemoryRead(Packet *pkt); diff --git a/src/mem/ruby/slicc_interface/AbstractController.hh b/src/mem/ruby/slicc_interface/AbstractController.hh --- a/src/mem/ruby/slicc_interface/AbstractController.hh +++ b/src/mem/ruby/slicc_interface/AbstractController.hh @@ -77,7 +77,8 @@ bool isBlocked(Addr); virtual MessageBuffer* getMandatoryQueue() const = 0; - virtual MessageBuffer* getMemoryQueue() const = 0; + virtual MessageBuffer* getMemReqQueue() const = 0; + virtual MessageBuffer* getMemRespQueue() const = 0; virtual AccessPermission getAccessPermission(const Addr &addr) = 0; virtual void print(std::ostream & out) const = 0; @@ -116,11 +117,6 @@ BaseMasterPort& getMasterPort(const std::string& if_name, PortID idx = InvalidPortID); - void queueMemoryRead(const MachineID &id, Addr addr, Cycles latency); - void queueMemoryWrite(const MachineID &id, Addr addr, Cycles latency, - const DataBlock &block); - void queueMemoryWritePartial(const MachineID &id, Addr addr, Cycles latency, - const DataBlock &block, int size); void recvTimingResp(PacketPtr pkt); public: @@ -140,6 +136,8 @@ void wakeUpBuffers(Addr addr); void wakeUpAllBuffers(Addr addr); void wakeUpAllBuffers(); + bool serviceMemoryQueue(PacketPtr pkt); + bool queryMemoryQueue(PacketPtr pkt); protected: const NodeID m_version; diff --git a/src/mem/ruby/slicc_interface/AbstractController.cc b/src/mem/ruby/slicc_interface/AbstractController.cc --- a/src/mem/ruby/slicc_interface/AbstractController.cc +++ b/src/mem/ruby/slicc_interface/AbstractController.cc @@ -29,10 +29,12 @@ #include "mem/ruby/slicc_interface/AbstractController.hh" #include "debug/RubyQueue.hh" +#include "mem/flow_control.hh" #include "mem/protocol/MemoryMsg.hh" #include "mem/ruby/system/RubySystem.hh" #include "mem/ruby/system/Sequencer.hh" #include "mem/ruby/system/GPUCoalescer.hh" +#include "mem/token_flow.hh" #include "sim/system.hh" AbstractController::AbstractController(const Params *p) @@ -61,6 +63,27 @@ m_delayVCHistogram.push_back(new Stats::Histogram()); m_delayVCHistogram[i]->init(10); } + + if (getMemReqQueue()) { + getMemReqQueue()->setConsumer(this); + } + + // if we are interacting with memory, setup flow-control if used + if (getMemReqQueue() && getMemRespQueue() && + memoryPort.getFlowControl()) { + auto flow_control = memoryPort.getFlowControl(); + flow_control->addProducer(m_masterId); + + // register callbacks to handle master port flow control events + auto notifyCall = std::bind(&AbstractController::serviceMemoryQueue, + this, std::placeholders::_1); + flow_control->registerCallback(BaseFlowControl::recvNotifyAvailFunc, + notifyCall); + auto queryCall = std::bind(&AbstractController::queryMemoryQueue, + this, std::placeholders::_1); + flow_control->registerCallback(BaseFlowControl::recvQueryAvailFunc, + queryCall); + } } void @@ -185,6 +208,76 @@ } } +bool +AbstractController::serviceMemoryQueue(PacketPtr) +{ + assert(getMemReqQueue()); + if (!getMemReqQueue()->isReady(clockEdge())) { + return false; + } + + const MemoryMsg *mem_msg = (const MemoryMsg*)getMemReqQueue()->peek(); + unsigned int req_size = RubySystem::getBlockSizeBytes(); + if (mem_msg->m_Len > 0) { + req_size = mem_msg->m_Len; + } + + RequestPtr req = new Request(mem_msg->m_addr, req_size, 0, m_masterId); + PacketPtr pkt; + if (mem_msg->getType() == MemoryRequestType_MEMORY_WB) { + pkt = Packet::createWrite(req); + uint8_t *newData = new uint8_t[req_size]; + pkt->dataDynamic(newData); + memcpy(newData, mem_msg->m_DataBlk.getData(0, req_size), req_size); + } else if (mem_msg->getType() == MemoryRequestType_MEMORY_READ) { + pkt = Packet::createRead(req); + uint8_t *newData = new uint8_t[req_size]; + pkt->dataDynamic(newData); + } else { + panic("Unknown memory request type (%s) for addr %p", + MemoryRequestType_to_string(mem_msg->getType()), + mem_msg->m_addr); + } + + SenderState *s = new SenderState(mem_msg->m_Sender); + pkt->pushSenderState(s); + + if (RubySystem::getWarmupEnabled()) { + // Use functional rather than timing accesses during warmup + getMemReqQueue()->dequeue(clockEdge()); + memoryPort.sendFunctional(pkt); + // Since the queue was popped the controller may be able + // to make more progress. Make sure it wakes up + scheduleEvent(Cycles(1)); + recvTimingResp(pkt); + } else if ((!memoryPort.getFlowControl()) || + (memoryPort.getFlowControl() && + memoryPort.getFlowControl()->sendQueryAvail(pkt))) { + getMemReqQueue()->dequeue(clockEdge()); + memoryPort.sendTimingReq(pkt); + // Since the queue was popped the controller may be able + // to make more progress. Make sure it wakes up + scheduleEvent(Cycles(1)); + } else { + delete pkt->req; + delete pkt; + } + + return true; +} + +bool +AbstractController::queryMemoryQueue(PacketPtr pkt) +{ + // Note: this is not currently used by any flow controls implemented in + // gem5, but should give an idea of how this query would be handled. + assert(memoryPort.getFlowControl()); + auto flow_control = memoryPort.getFlowControl(); + const int n = pkt->getSize() / flow_control->getFlitSize(); + + return getMemRespQueue()->areNSlotsAvailable(n, curTick()); +} + void AbstractController::blockOnQueue(Addr addr, MessageBuffer* port) { @@ -215,77 +308,6 @@ } void -AbstractController::queueMemoryRead(const MachineID &id, Addr addr, - Cycles latency) -{ - RequestPtr req = new Request(addr, RubySystem::getBlockSizeBytes(), 0, - m_masterId); - - PacketPtr pkt = Packet::createRead(req); - uint8_t *newData = new uint8_t[RubySystem::getBlockSizeBytes()]; - pkt->dataDynamic(newData); - - SenderState *s = new SenderState(id); - pkt->pushSenderState(s); - - // Use functional rather than timing accesses during warmup - if (RubySystem::getWarmupEnabled()) { - memoryPort.sendFunctional(pkt); - recvTimingResp(pkt); - return; - } - - memoryPort.schedTimingReq(pkt, clockEdge(latency)); -} - -void -AbstractController::queueMemoryWrite(const MachineID &id, Addr addr, - Cycles latency, const DataBlock &block) -{ - RequestPtr req = new Request(addr, RubySystem::getBlockSizeBytes(), 0, - m_masterId); - - PacketPtr pkt = Packet::createWrite(req); - uint8_t *newData = new uint8_t[RubySystem::getBlockSizeBytes()]; - pkt->dataDynamic(newData); - memcpy(newData, block.getData(0, RubySystem::getBlockSizeBytes()), - RubySystem::getBlockSizeBytes()); - - SenderState *s = new SenderState(id); - pkt->pushSenderState(s); - - // Use functional rather than timing accesses during warmup - if (RubySystem::getWarmupEnabled()) { - memoryPort.sendFunctional(pkt); - recvTimingResp(pkt); - return; - } - - // Create a block and copy data from the block. - memoryPort.schedTimingReq(pkt, clockEdge(latency)); -} - -void -AbstractController::queueMemoryWritePartial(const MachineID &id, Addr addr, - Cycles latency, - const DataBlock &block, int size) -{ - RequestPtr req = new Request(addr, RubySystem::getBlockSizeBytes(), 0, - m_masterId); - - PacketPtr pkt = Packet::createWrite(req); - uint8_t *newData = new uint8_t[size]; - pkt->dataDynamic(newData); - memcpy(newData, block.getData(getOffset(addr), size), size); - - SenderState *s = new SenderState(id); - pkt->pushSenderState(s); - - // Create a block and copy data from the block. - memoryPort.schedTimingReq(pkt, clockEdge(latency)); -} - -void AbstractController::functionalMemoryRead(PacketPtr pkt) { memoryPort.sendFunctional(pkt); @@ -309,7 +331,7 @@ void AbstractController::recvTimingResp(PacketPtr pkt) { - assert(getMemoryQueue()); + assert(getMemRespQueue()); assert(pkt->isResponse()); std::shared_ptr msg = std::make_shared(clockEdge()); @@ -334,7 +356,7 @@ panic("Incorrect packet type received from memory controller!"); } - getMemoryQueue()->enqueue(msg, clockEdge(), cyclesToTicks(Cycles(1))); + getMemRespQueue()->enqueue(msg, clockEdge(), cyclesToTicks(Cycles(1))); delete pkt->req; delete pkt; } diff --git a/src/mem/slicc/symbols/StateMachine.py b/src/mem/slicc/symbols/StateMachine.py --- a/src/mem/slicc/symbols/StateMachine.py +++ b/src/mem/slicc/symbols/StateMachine.py @@ -299,7 +299,8 @@ void init(); MessageBuffer *getMandatoryQueue() const; - MessageBuffer *getMemoryQueue() const; + MessageBuffer *getMemReqQueue() const; + MessageBuffer *getMemRespQueue() const; void initNetQueues(); void print(std::ostream& out) const; @@ -670,6 +671,11 @@ if port.code.find("mandatoryQueue_ptr") >= 0: mq_ident = "m_mandatoryQueue_ptr" + memoutq_ident = "NULL" + for param in self.config_parameters: + if param.ident.find("requestToMemory") >= 0: + memoutq_ident = "m_requestToMemory_ptr" + memq_ident = "NULL" for port in self.in_ports: if port.code.find("responseFromMemory_ptr") >= 0: @@ -852,7 +858,13 @@ } MessageBuffer* -$c_ident::getMemoryQueue() const +$c_ident::getMemReqQueue() const +{ + return $memoutq_ident; +} + +MessageBuffer* +$c_ident::getMemRespQueue() const { return $memq_ident; } @@ -1083,6 +1095,10 @@ void ${ident}_Controller::wakeup() { + if (getMemReqQueue() && getMemReqQueue()->isReady(clockEdge())) { + serviceMemoryQueue(nullptr); + } + int counter = 0; while (true) { unsigned char rejected[${{len(msg_bufs)}}]; diff --git a/src/mem/token_flow.hh b/src/mem/token_flow.hh new file mode 100644 --- /dev/null +++ b/src/mem/token_flow.hh @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * For use for simulation and test purposes only + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Matthew Poremba + */ +#ifndef __MEM_TOKEN_FLOW_HH__ +#define __MEM_TOKEN_FLOW_HH__ + +#include + +#include "mem/flow_control.hh" +#include "mem/port.hh" +#include "params/TokenMaster.hh" +#include "params/TokenSlave.hh" + +class TokenMaster : public BaseFlowControl +{ + public: + TokenMaster(TokenMasterParams* p) + : BaseFlowControl(p) { } + + /** + * Let slave's flow control know we exist. + */ + virtual void addProducer(const MasterID mid); + + /** + * Asks the slave if there are tokens available for this packet. + */ + virtual bool sendQueryAvail(PacketPtr pkt); +}; + + +class TokenSlave : public BaseFlowControl +{ + public: + TokenSlave(TokenSlaveParams *p) + : BaseFlowControl(p), _producerCount(0) { } + + virtual void init(); + + /** + * Increase the number of producers for this consumer + */ + virtual void addProducer(const MasterID mid); + + /** + * Determine if we have tokens available for the master port + */ + virtual bool recvQueryAvail(PacketPtr pkt); + + /** + * Return tokens back to the master port + */ + virtual void sendNotifyAvail(PacketPtr pkt); + + protected: + + int _producerCount; + std::unordered_map tokenMap; +}; + +#endif diff --git a/src/mem/token_flow.cc b/src/mem/token_flow.cc new file mode 100644 --- /dev/null +++ b/src/mem/token_flow.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * For use for simulation and test purposes only + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Matthew Poremba + */ +#include "mem/token_flow.hh" + +#include "base/trace.hh" +#include "debug/TokenFlow.hh" + +void +TokenMaster::addProducer(const MasterID mid) +{ + getSlaveFlowControl()->addProducer(mid); +} + +bool +TokenMaster::sendQueryAvail(PacketPtr pkt) +{ + return getSlaveFlowControl()->recvQueryAvail(pkt); +} + + +void +TokenSlave::init() +{ + // Here we use a simple scheme to divide tokens evenly into private + // tokens. Realisticall an asymmetric+dynamic allocation scheme may be + // desiable. + DPRINTF(TokenFlow, "Setting up %d tokens for %d producers\n", + getBufferSize(), _producerCount); + + // We need at least one token per producer + assert(getBufferSize() >= _producerCount); + for (auto& token : tokenMap) + token.second = getBufferSize() / _producerCount; +} + +void +TokenSlave::addProducer(const MasterID mid) +{ + _producerCount++; + tokenMap.insert(std::make_pair(mid, 0)); +} + +bool +TokenSlave::recvQueryAvail(PacketPtr pkt) +{ + // The number of tokens should be calculated by packet size / flit size + const int tokens = pkt->getSize() / getFlitSize(); + const MasterID mid = pkt->req->masterId(); + + assert(tokenMap.count(mid)); + DPRINTF(TokenFlow, "Request from %d for %d TOKENS with %d available\n", + mid, tokens, tokenMap[mid]); + + bool success = (tokenMap[mid] >= tokens); + if (success) { + tokenMap[mid] -= tokens; + DPRINTF(TokenFlow, "Removed %d tokens from %d. Left with %d\n", + tokens, mid, tokenMap[mid]); + } + + return success; +} + +void +TokenSlave::sendNotifyAvail(PacketPtr pkt) +{ + const MasterID mid = pkt->req->masterId(); + const int tokens = pkt->getSize() / getFlitSize(); + + assert(tokenMap.count(mid)); + tokenMap[mid] += tokens; + + DPRINTF(TokenFlow, "Returning %d tokens to %d. Available tokens: %d\n", + tokens, mid, tokenMap[mid]); + + getMasterFlowControl()->recvNotifyAvail(pkt); +} + +TokenMaster* +TokenMasterParams::create() +{ + return new TokenMaster(this); +} + +TokenSlave* +TokenSlaveParams::create() +{ + return new TokenSlave(this); +} diff --git a/src/python/swig/pyobject.cc b/src/python/swig/pyobject.cc --- a/src/python/swig/pyobject.cc +++ b/src/python/swig/pyobject.cc @@ -128,6 +128,10 @@ BaseMasterPort& masterPort = mo1->getMasterPort(name1, i1); BaseSlavePort& slavePort = mo2->getSlavePort(name2, i2); + // set the flow controllers corresponding to the master and slave port + masterPort.setFlowControl(mo1->getMasterFlowControl()); + slavePort.setFlowControl(mo2->getSlaveFlowControl()); + masterPort.bind(slavePort); return 1; # Node ID 2c8bd12f84a2ffcc91f6985fef2439d668b6bd1c # Parent 02756e2f20530d3de726d8b9ed7a40ee17e1ae8c diff --git a/src/mem/FlowControl.py b/src/mem/FlowControl.py new file mode 100644 --- /dev/null +++ b/src/mem/FlowControl.py @@ -0,0 +1,54 @@ +# +# Copyright (c) 2016 Advanced Micro Devices, Inc. +# All rights reserved. +# +# For use for simulation and test purposes only +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Author: Matthew Poremba +# + +from m5.SimObject import SimObject + +class BaseFlowControl(SimObject): + type = 'BaseFlowControl' + abstract = True + cxx_header = 'mem/flow_control.hh' + +class NullFlowControl(BaseFlowControl): + type = 'NullFlowControl' + cxx_header = 'mem/flow_control.hh' + +class TokenMaster(BaseFlowControl): + type = 'TokenMaster' + cxx_header = 'mem/token_flow.hh' + +class TokenSlave(BaseFlowControl): + type = 'TokenSlave' + cxx_header = 'mem/token_flow.hh'