#include <memory>
+#include "debug/Config.hh"
+#include "debug/Drain.hh"
#include "debug/RubyDma.hh"
#include "debug/RubyStats.hh"
#include "mem/protocol/SequencerMsg.hh"
-#include "mem/protocol/SequencerRequestType.hh"
#include "mem/ruby/system/DMASequencer.hh"
#include "mem/ruby/system/System.hh"
+#include "sim/system.hh"
DMASequencer::DMASequencer(const Params *p)
- : RubyPort(p)
+ : MemObject(p), m_version(p->version), m_controller(NULL),
+ m_mandatory_q_ptr(NULL), m_usingRubyTester(p->using_ruby_tester),
+ slave_port(csprintf("%s.slave", name()), this, access_phys_mem, 0),
+ drainManager(NULL), system(p->system), retry(false),
+ access_phys_mem(p->access_phys_mem)
{
+ assert(m_version != -1);
}
void
DMASequencer::init()
{
- RubyPort::init();
+ MemObject::init();
+ assert(m_controller != NULL);
+ m_mandatory_q_ptr = m_controller->getMandatoryQueue();
+ m_mandatory_q_ptr->setSender(this);
m_is_busy = false;
m_data_block_mask = ~ (~0 << RubySystem::getBlockSizeBits());
}
+BaseSlavePort &
+DMASequencer::getSlavePort(const std::string &if_name, PortID idx)
+{
+ // used by the CPUs to connect the caches to the interconnect, and
+ // for the x86 case also the interrupt master
+ if (if_name != "slave") {
+ // pass it along to our super class
+ return MemObject::getSlavePort(if_name, idx);
+ } else {
+ return slave_port;
+ }
+}
+
+DMASequencer::MemSlavePort::MemSlavePort(const std::string &_name,
+ DMASequencer *_port, bool _access_phys_mem, PortID id)
+ : QueuedSlavePort(_name, _port, queue, id), queue(*_port, *this),
+ access_phys_mem(_access_phys_mem)
+{
+ DPRINTF(RubyDma, "Created slave memport on ruby sequencer %s\n", _name);
+}
+
+bool
+DMASequencer::MemSlavePort::recvTimingReq(PacketPtr pkt)
+{
+ DPRINTF(RubyDma, "Timing request for address %#x on port %d\n",
+ pkt->getAddr(), id);
+ DMASequencer *seq = static_cast<DMASequencer *>(&owner);
+
+ if (pkt->memInhibitAsserted())
+ panic("DMASequencer should never see an inhibited request\n");
+
+ assert(isPhysMemAddress(pkt->getAddr()));
+ assert(Address(pkt->getAddr()).getOffset() + pkt->getSize() <=
+ RubySystem::getBlockSizeBytes());
+
+ // Submit the ruby request
+ RequestStatus requestStatus = seq->makeRequest(pkt);
+
+ // If the request successfully issued then we should return true.
+ // Otherwise, we need to tell the port to retry at a later point
+ // and return false.
+ if (requestStatus == RequestStatus_Issued) {
+ DPRINTF(RubyDma, "Request %s 0x%x issued\n", pkt->cmdString(),
+ pkt->getAddr());
+ return true;
+ }
+
+ // Unless one is using the ruby tester, record the stalled M5 port for
+ // later retry when the sequencer becomes free.
+ if (!seq->m_usingRubyTester) {
+ seq->retry = true;
+ }
+
+ DPRINTF(RubyDma, "Request for address %#x did not issued because %s\n",
+ pkt->getAddr(), RequestStatus_to_string(requestStatus));
+
+ return false;
+}
+
+void
+DMASequencer::ruby_hit_callback(PacketPtr pkt)
+{
+ DPRINTF(RubyDma, "Hit callback for %s 0x%x\n", pkt->cmdString(),
+ pkt->getAddr());
+
+ // The packet was destined for memory and has not yet been turned
+ // into a response
+ assert(system->isMemAddr(pkt->getAddr()));
+ assert(pkt->isRequest());
+ slave_port.hitCallback(pkt);
+
+ // If we had to stall the slave ports, wake it up because
+ // the sequencer likely has free resources now.
+ if (retry) {
+ retry = false;
+ DPRINTF(RubyDma,"Sequencer may now be free. SendRetry to port %s\n",
+ slave_port.name());
+ slave_port.sendRetry();
+ }
+
+ testDrainComplete();
+}
+
+void
+DMASequencer::testDrainComplete()
+{
+ //If we weren't able to drain before, we might be able to now.
+ if (drainManager != NULL) {
+ unsigned int drainCount = outstandingCount();
+ DPRINTF(Drain, "Drain count: %u\n", drainCount);
+ if (drainCount == 0) {
+ DPRINTF(Drain, "DMASequencer done draining, signaling drain done\n");
+ drainManager->signalDrainDone();
+ // Clear the drain manager once we're done with it.
+ drainManager = NULL;
+ }
+ }
+}
+
+unsigned int
+DMASequencer::getChildDrainCount(DrainManager *dm)
+{
+ int count = 0;
+ count += slave_port.drain(dm);
+ DPRINTF(Config, "count after slave port check %d\n", count);
+ return count;
+}
+
+unsigned int
+DMASequencer::drain(DrainManager *dm)
+{
+ if (isDeadlockEventScheduled()) {
+ descheduleDeadlockEvent();
+ }
+
+ // If the DMASequencer is not empty, then it needs to clear all outstanding
+ // requests before it should call drainManager->signalDrainDone()
+ DPRINTF(Config, "outstanding count %d\n", outstandingCount());
+ bool need_drain = outstandingCount() > 0;
+
+ //
+ // Also, get the number of child ports that will also need to clear
+ // their buffered requests before they call drainManager->signalDrainDone()
+ //
+ unsigned int child_drain_count = getChildDrainCount(dm);
+
+ // Set status
+ if (need_drain) {
+ drainManager = dm;
+
+ DPRINTF(Drain, "DMASequencer not drained\n");
+ setDrainState(Drainable::Draining);
+ return child_drain_count + 1;
+ }
+
+ drainManager = NULL;
+ setDrainState(Drainable::Drained);
+ return child_drain_count;
+}
+
+void
+DMASequencer::MemSlavePort::hitCallback(PacketPtr pkt)
+{
+ bool needsResponse = pkt->needsResponse();
+ bool accessPhysMem = access_phys_mem;
+
+ assert(!pkt->isLLSC());
+ assert(!pkt->isFlush());
+
+ DPRINTF(RubyDma, "Hit callback needs response %d\n", needsResponse);
+
+ if (accessPhysMem) {
+ DMASequencer *seq = static_cast<DMASequencer *>(&owner);
+ seq->system->getPhysMem().access(pkt);
+ } else if (needsResponse) {
+ pkt->makeResponse();
+ }
+
+ // turn packet around to go back to requester if response expected
+ if (needsResponse) {
+ DPRINTF(RubyDma, "Sending packet back over port\n");
+ // send next cycle
+ schedTimingResp(pkt, curTick() + g_system_ptr->clockPeriod());
+ } else {
+ delete pkt;
+ }
+ DPRINTF(RubyDma, "Hit callback done!\n");
+}
+
+bool
+DMASequencer::MemSlavePort::isPhysMemAddress(Addr addr) const
+{
+ DMASequencer *seq = static_cast<DMASequencer *>(&owner);
+ return seq->system->isMemAddr(addr);
+}
+
RequestStatus
DMASequencer::makeRequest(PacketPtr pkt)
{
}
void
-DMASequencer::recordRequestType(DMASequencerRequestType requestType) {
+DMASequencer::recordRequestType(DMASequencerRequestType requestType)
+{
DPRINTF(RubyStats, "Recorded statistic: %s\n",
DMASequencerRequestType_to_string(requestType));
}
#include <memory>
#include "mem/protocol/DMASequencerRequestType.hh"
+#include "mem/protocol/RequestStatus.hh"
#include "mem/ruby/common/DataBlock.hh"
-#include "mem/ruby/system/RubyPort.hh"
+#include "mem/ruby/network/MessageBuffer.hh"
+#include "mem/ruby/system/System.hh"
+#include "mem/mem_object.hh"
+#include "mem/tport.hh"
#include "params/DMASequencer.hh"
+class AbstractController;
+
struct DMARequest
{
uint64_t start_paddr;
PacketPtr pkt;
};
-class DMASequencer : public RubyPort
+class DMASequencer : public MemObject
{
public:
typedef DMASequencerParams Params;
DMASequencer(const Params *);
void init();
+
+ public:
+ class MemSlavePort : public QueuedSlavePort
+ {
+ private:
+ SlavePacketQueue queue;
+ bool access_phys_mem;
+
+ public:
+ MemSlavePort(const std::string &_name, DMASequencer *_port,
+ bool _access_phys_mem, PortID id);
+ void hitCallback(PacketPtr pkt);
+ void evictionCallback(const Address& address);
+
+ protected:
+ bool recvTimingReq(PacketPtr pkt);
+
+ Tick recvAtomic(PacketPtr pkt)
+ { panic("DMASequencer::MemSlavePort::recvAtomic() not implemented!\n"); }
+
+ void recvFunctional(PacketPtr pkt)
+ { panic("DMASequencer::MemSlavePort::recvFunctional() not implemented!\n"); }
+
+ AddrRangeList getAddrRanges() const
+ { AddrRangeList ranges; return ranges; }
+
+ private:
+ bool isPhysMemAddress(Addr addr) const;
+ };
+
+ BaseSlavePort &getSlavePort(const std::string &if_name,
+ PortID idx = InvalidPortID);
+
/* external interface */
RequestStatus makeRequest(PacketPtr pkt);
bool busy() { return m_is_busy;}
bool isDeadlockEventScheduled() const { return false; }
void descheduleDeadlockEvent() {}
+ // Called by the controller to give the sequencer a pointer.
+ // A pointer to the controller is needed for atomic support.
+ void setController(AbstractController* _cntrl) { m_controller = _cntrl; }
+ uint32_t getId() { return m_version; }
+ unsigned int drain(DrainManager *dm);
+
/* SLICC callback */
void dataCallback(const DataBlock & dblk);
void ackCallback();
private:
void issueNext();
+ void ruby_hit_callback(PacketPtr pkt);
+ void testDrainComplete();
+
+ /**
+ * Called by the PIO port when receiving a timing response.
+ *
+ * @param pkt Response packet
+ * @param master_port_id Port id of the PIO port
+ *
+ * @return Whether successfully sent
+ */
+ bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
+ unsigned int getChildDrainCount(DrainManager *dm);
private:
+ uint32_t m_version;
+ AbstractController* m_controller;
+ MessageBuffer* m_mandatory_q_ptr;
+ bool m_usingRubyTester;
+
+ MemSlavePort slave_port;
+
+ DrainManager *drainManager;
+ System* system;
+
+ bool retry;
+ bool access_phys_mem;
+
bool m_is_busy;
uint64_t m_data_block_mask;
DMARequest active_request;