diff options
author | William Wang <william.wang@arm.com> | 2012-03-30 09:40:11 -0400 |
---|---|---|
committer | William Wang <william.wang@arm.com> | 2012-03-30 09:40:11 -0400 |
commit | f9d403a7b95c50a8b75f8442101eb87ca465f967 (patch) | |
tree | a8302eb02dd5947d53b9437cc19d552145267189 /src/mem | |
parent | a14013af3a9e04d68985aea7bcff6c1e70bdbb82 (diff) | |
download | gem5-f9d403a7b95c50a8b75f8442101eb87ca465f967.tar.xz |
MEM: Introduce the master/slave port sub-classes in C++
This patch introduces the notion of a master and slave port in the C++
code, thus bringing the previous classification from the Python
classes into the corresponding simulation objects and memory objects.
The patch enables us to classify behaviours into the two bins and add
assumptions and enfore compliance, also simplifying the two
interfaces. As a starting point, isSnooping is confined to a master
port, and getAddrRanges to slave ports. More of these specilisations
are to come in later patches.
The getPort function is not getMasterPort and getSlavePort, and
returns a port reference rather than a pointer as NULL would never be
a valid return value. The default implementation of these two
functions is placed in MemObject, and calls fatal.
The one drawback with this specific patch is that it requires some
code duplication, e.g. QueuedPort becomes QueuedMasterPort and
QueuedSlavePort, and BusPort becomes BusMasterPort and BusSlavePort
(avoiding multiple inheritance). With the later introduction of the
port interfaces, moving the functionality outside the port itself, a
lot of the duplicated code will disappear again.
Diffstat (limited to 'src/mem')
-rw-r--r-- | src/mem/bridge.cc | 45 | ||||
-rw-r--r-- | src/mem/bridge.hh | 21 | ||||
-rw-r--r-- | src/mem/bus.cc | 117 | ||||
-rw-r--r-- | src/mem/bus.hh | 103 | ||||
-rw-r--r-- | src/mem/cache/base.cc | 25 | ||||
-rw-r--r-- | src/mem/cache/base.hh | 11 | ||||
-rw-r--r-- | src/mem/cache/builder.cc | 1 | ||||
-rw-r--r-- | src/mem/cache/cache.hh | 2 | ||||
-rw-r--r-- | src/mem/cache/cache_impl.hh | 15 | ||||
-rw-r--r-- | src/mem/fs_translating_port_proxy.cc | 2 | ||||
-rw-r--r-- | src/mem/fs_translating_port_proxy.hh | 2 | ||||
-rw-r--r-- | src/mem/mem_object.cc | 25 | ||||
-rw-r--r-- | src/mem/mem_object.hh | 43 | ||||
-rw-r--r-- | src/mem/mport.cc | 17 | ||||
-rw-r--r-- | src/mem/mport.hh | 41 | ||||
-rw-r--r-- | src/mem/physical.cc | 23 | ||||
-rw-r--r-- | src/mem/physical.hh | 4 | ||||
-rw-r--r-- | src/mem/port.cc | 105 | ||||
-rw-r--r-- | src/mem/port.hh | 267 | ||||
-rw-r--r-- | src/mem/port_proxy.hh | 4 | ||||
-rw-r--r-- | src/mem/qport.hh | 50 | ||||
-rw-r--r-- | src/mem/ruby/system/RubyPort.cc | 137 | ||||
-rw-r--r-- | src/mem/ruby/system/RubyPort.hh | 25 | ||||
-rw-r--r-- | src/mem/se_translating_port_proxy.cc | 2 | ||||
-rw-r--r-- | src/mem/se_translating_port_proxy.hh | 2 | ||||
-rw-r--r-- | src/mem/tport.cc | 5 | ||||
-rw-r--r-- | src/mem/tport.hh | 2 |
27 files changed, 749 insertions, 347 deletions
diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc index 0733b6ea8..ebb37e792 100644 --- a/src/mem/bridge.cc +++ b/src/mem/bridge.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 ARM Limited + * Copyright (c) 2011-2012 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -59,7 +59,7 @@ Bridge::BridgeSlavePort::BridgeSlavePort(const std::string &_name, int _delay, int _nack_delay, int _resp_limit, std::vector<Range<Addr> > _ranges) - : Port(_name, _bridge), bridge(_bridge), masterPort(_masterPort), + : SlavePort(_name, _bridge), bridge(_bridge), masterPort(_masterPort), delay(_delay), nackDelay(_nack_delay), ranges(_ranges.begin(), _ranges.end()), outstandingResponses(0), inRetry(false), @@ -71,7 +71,7 @@ Bridge::BridgeMasterPort::BridgeMasterPort(const std::string &_name, Bridge* _bridge, BridgeSlavePort& _slavePort, int _delay, int _req_limit) - : Port(_name, _bridge), bridge(_bridge), slavePort(_slavePort), + : MasterPort(_name, _bridge), bridge(_bridge), slavePort(_slavePort), delay(_delay), inRetry(false), reqQueueLimit(_req_limit), sendEvent(*this) { @@ -88,19 +88,25 @@ Bridge::Bridge(Params *p) panic("No support for acknowledging writes\n"); } -Port* -Bridge::getPort(const std::string &if_name, int idx) +MasterPort& +Bridge::getMasterPort(const std::string &if_name, int idx) { - if (if_name == "slave") - return &slavePort; - else if (if_name == "master") - return &masterPort; - else { - panic("Bridge %s has no port named %s\n", name(), if_name); - return NULL; - } + if (if_name == "master") + return masterPort; + else + // pass it along to our super class + return MemObject::getMasterPort(if_name, idx); } +SlavePort& +Bridge::getSlavePort(const std::string &if_name, int idx) +{ + if (if_name == "slave") + return slavePort; + else + // pass it along to our super class + return MemObject::getSlavePort(if_name, idx); +} void Bridge::init() @@ -473,19 +479,6 @@ Bridge::BridgeMasterPort::checkFunctional(PacketPtr pkt) return found; } -/** Function called by the port when the bridge is receiving a range change.*/ -void -Bridge::BridgeMasterPort::recvRangeChange() -{ - // no need to forward as the bridge has a fixed set of ranges -} - -void -Bridge::BridgeSlavePort::recvRangeChange() -{ - // is a slave port so do nothing -} - AddrRangeList Bridge::BridgeSlavePort::getAddrRanges() { diff --git a/src/mem/bridge.hh b/src/mem/bridge.hh index 3e0040514..e7dbc0a28 100644 --- a/src/mem/bridge.hh +++ b/src/mem/bridge.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 ARM Limited + * Copyright (c) 2011-2012 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -122,7 +122,7 @@ class Bridge : public MemObject * is responsible for. The slave port also has a buffer for the * responses not yet sent. */ - class BridgeSlavePort : public Port + class BridgeSlavePort : public SlavePort { private: @@ -244,11 +244,6 @@ class Bridge : public MemObject pass it to the bridge. */ virtual void recvFunctional(PacketPtr pkt); - /** - * When receiving a range change on the slave side do nothing. - */ - virtual void recvRangeChange(); - /** When receiving a address range request the peer port, pass it to the bridge. */ virtual AddrRangeList getAddrRanges(); @@ -260,7 +255,7 @@ class Bridge : public MemObject * responses. The master port has a buffer for the requests not * yet sent. */ - class BridgeMasterPort : public Port + class BridgeMasterPort : public MasterPort { private: @@ -371,11 +366,6 @@ class Bridge : public MemObject /** When receiving a Functional request from the peer port, pass it to the bridge. */ virtual void recvFunctional(PacketPtr pkt); - - /** - * When receiving a range change, pass it through the bridge. - */ - virtual void recvRangeChange(); }; /** Slave port of the bridge. */ @@ -396,8 +386,9 @@ class Bridge : public MemObject public: const Params *params() const { return _params; } - /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1); + virtual MasterPort& getMasterPort(const std::string& if_name, + int idx = -1); + virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1); virtual void init(); diff --git a/src/mem/bus.cc b/src/mem/bus.cc index 0c0e3c3e2..c89455f02 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -39,6 +39,7 @@ * * Authors: Ali Saidi * Andreas Hansson + * William Wang */ /** @@ -72,12 +73,14 @@ Bus::Bus(const BusParams *p) // create the ports based on the size of the master and slave // vector ports, and the presence of the default master - // id used to index into interfaces which is a flat vector of all - // ports + // id used to index into master and slave ports, that currently + // has holes to be able to use the id to index into either int id = 0; for (int i = 0; i < p->port_master_connection_count; ++i) { std::string portName = csprintf("%s-p%d", name(), id); - interfaces.push_back(new BusPort(portName, this, id)); + BusMasterPort* bp = new BusMasterPort(portName, this, id); + masterPorts.push_back(bp); + slavePorts.push_back(NULL); ++id; } @@ -86,7 +89,9 @@ Bus::Bus(const BusParams *p) if (p->port_default_connection_count) { defaultPortId = id; std::string portName = csprintf("%s-default", name()); - interfaces.push_back(new BusPort(portName, this, id)); + BusMasterPort* bp = new BusMasterPort(portName, this, id); + masterPorts.push_back(bp); + slavePorts.push_back(NULL); ++id; // this is an additional master port ++nbrMasterPorts; @@ -96,44 +101,55 @@ Bus::Bus(const BusParams *p) // nbrMasterPorts in the vector for (int i = 0; i < p->port_slave_connection_count; ++i) { std::string portName = csprintf("%s-p%d", name(), id); - interfaces.push_back(new BusPort(portName, this, id)); + BusSlavePort* bp = new BusSlavePort(portName, this, id); + masterPorts.push_back(NULL); + slavePorts.push_back(bp); ++id; } clearPortCache(); } -Port * -Bus::getPort(const std::string &if_name, int idx) +MasterPort & +Bus::getMasterPort(const std::string &if_name, int idx) { if (if_name == "master") { // the master index translates directly to the interfaces // vector as they are stored first - return interfaces[idx]; - } else if (if_name == "slave") { - // the slaves are stored after the masters and we must thus - // offset the slave index with the number of master ports - return interfaces[nbrMasterPorts + idx]; + return *masterPorts[idx]; } else if (if_name == "default") { - return interfaces[defaultPortId]; + return *masterPorts[defaultPortId]; } else { - panic("No port %s %d on bus %s\n", if_name, idx, name()); + return MemObject::getMasterPort(if_name, idx); + } +} + +SlavePort & +Bus::getSlavePort(const std::string &if_name, int idx) +{ + if (if_name == "slave") { + return *slavePorts[nbrMasterPorts + idx]; + } else { + return MemObject::getSlavePort(if_name, idx); } } void Bus::init() { - std::vector<BusPort*>::iterator intIter; + std::vector<BusSlavePort*>::iterator intIter; // iterate over our interfaces and determine which of our neighbours // are snooping and add them as snoopers - for (intIter = interfaces.begin(); intIter != interfaces.end(); + for (intIter = slavePorts.begin(); intIter != slavePorts.end(); intIter++) { - if ((*intIter)->getPeer()->isSnooping()) { - DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n", - (*intIter)->getPeer()->name()); - snoopPorts.push_back(*intIter); + // since there are holes in the vector, check for NULL + if (*intIter != NULL) { + if ((*intIter)->getMasterPort().isSnooping()) { + DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n", + (*intIter)->getMasterPort().name()); + snoopPorts.push_back(*intIter); + } } } } @@ -194,7 +210,9 @@ Bus::recvTiming(PacketPtr pkt) // get the source id and port Packet::NodeID src_id = pkt->getSrc(); - BusPort *src_port = interfaces[src_id]; + // determine the source port based on the id + Port *src_port = slavePorts[src_id] ? + (Port*) slavePorts[src_id] : (Port*) masterPorts[src_id]; // If the bus is busy, or other devices are in line ahead of the current // one, put this device on the retry list. @@ -218,14 +236,14 @@ Bus::recvTiming(PacketPtr pkt) int dest_id; Port *dest_port; - if (dest == Packet::Broadcast) { + if (pkt->isRequest()) { // the packet is a memory-mapped request and should be broadcasted to // our snoopers - assert(pkt->isRequest()); + assert(dest == Packet::Broadcast); SnoopIter s_end = snoopPorts.end(); for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { - BusPort *p = *s_iter; + BusSlavePort *p = *s_iter; // we got this request from a snooping master // (corresponding to our own slave port that is also in // snoopPorts) and should not send it back to where it @@ -241,16 +259,28 @@ Bus::recvTiming(PacketPtr pkt) // determine the destination based on the address and forward // through the corresponding master port dest_id = findPort(pkt->getAddr()); - dest_port = interfaces[dest_id]; + dest_port = masterPorts[dest_id]; } else { // the packet is a response, and it should always go back to // the port determined by the destination field dest_id = dest; assert(dest_id != src_id); // catch infinite loops - assert(dest_id < interfaces.size()); - dest_port = interfaces[dest_id]; + dest_port = slavePorts[dest_id] ? + (Port*) slavePorts[dest_id] : (Port*) masterPorts[dest_id]; + + // a normal response from the memory system (i.e. from a + // connected slave) should always go back to the master + // that issued it through one of our slave ports, however + // if this is a snoop response it could go either way, for + // example, it could be coming from a slave port + // connecting an L1 with a coherent master and another L1 + // coherent master (one of our slave ports), or coming + // from the L1 and going to the L2 slave port (through one + // of our master ports) } + assert(dest_port != NULL); + // if this is a snoop from a slave (corresponding to our own // master), i.e. the memory side of the bus, then do not send it // back to where it came from @@ -318,8 +348,6 @@ Bus::retryWaiting() // send a retry to the port at the head of the retry list inRetry = true; - DPRINTF(Bus, "Sending a retry to %s\n", - retryList.front()->getPeer()->name()); // note that we might have blocked on the receiving port being // busy (rather than the bus itself) and now call retry before the @@ -427,7 +455,7 @@ Bus::recvAtomic(PacketPtr pkt) SnoopIter s_end = snoopPorts.end(); for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { - BusPort *p = *s_iter; + BusSlavePort *p = *s_iter; // we could have gotten this request from a snooping master // (corresponding to our own slave port that is also in // snoopPorts) and should not send it back to where it came @@ -464,7 +492,7 @@ Bus::recvAtomic(PacketPtr pkt) // master), i.e. the memory side of the bus, then do not send it // back to where it came from if (dest_id != src_id) { - response_latency = interfaces[dest_id]->sendAtomic(pkt); + response_latency = masterPorts[dest_id]->sendAtomic(pkt); } // if we got a response from a snooper, restore it here @@ -504,7 +532,7 @@ Bus::recvFunctional(PacketPtr pkt) SnoopIter s_end = snoopPorts.end(); for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { - BusPort *p = *s_iter; + BusSlavePort *p = *s_iter; // we could have gotten this request from a snooping master // (corresponding to our own slave port that is also in // snoopPorts) and should not send it back to where it came @@ -528,7 +556,7 @@ Bus::recvFunctional(PacketPtr pkt) // master), i.e. the memory side of the bus, then do not send // it back to where it came from, if (dest_id != src_id) { - interfaces[dest_id]->sendFunctional(pkt); + masterPorts[dest_id]->sendFunctional(pkt); } } } @@ -551,7 +579,8 @@ Bus::recvRangeChange(int id) defaultRange.clear(); // Only try to update these ranges if the user set a default responder. if (useDefaultRange) { - AddrRangeList ranges = interfaces[id]->getPeer()->getAddrRanges(); + AddrRangeList ranges = + masterPorts[id]->getSlavePort().getAddrRanges(); for(iter = ranges.begin(); iter != ranges.end(); iter++) { defaultRange.push_back(*iter); DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", @@ -560,8 +589,8 @@ Bus::recvRangeChange(int id) } } else { - assert(id < interfaces.size() && id >= 0); - BusPort *port = interfaces[id]; + assert(id < masterPorts.size() && id >= 0); + BusMasterPort *port = masterPorts[id]; // Clean out any previously existent ids for (PortIter portIter = portMap.begin(); @@ -572,7 +601,7 @@ Bus::recvRangeChange(int id) portIter++; } - ranges = port->getPeer()->getAddrRanges(); + ranges = port->getSlavePort().getAddrRanges(); for (iter = ranges.begin(); iter != ranges.end(); iter++) { DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", @@ -580,8 +609,8 @@ Bus::recvRangeChange(int id) if (portMap.insert(*iter, id) == portMap.end()) { int conflict_id = portMap.find(*iter)->second; fatal("%s has two ports with same range:\n\t%s\n\t%s\n", - name(), interfaces[id]->getPeer()->name(), - interfaces[conflict_id]->getPeer()->name()); + name(), masterPorts[id]->getSlavePort().name(), + masterPorts[conflict_id]->getSlavePort().name()); } } } @@ -589,10 +618,10 @@ Bus::recvRangeChange(int id) // tell all our peers that our address range has changed. // Don't tell the device that caused this change, it already knows - std::vector<BusPort*>::const_iterator intIter; + std::vector<BusSlavePort*>::const_iterator intIter; - for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) - if ((*intIter)->getId() != id) + for (intIter = slavePorts.begin(); intIter != slavePorts.end(); intIter++) + if (*intIter != NULL && (*intIter)->getId() != id) (*intIter)->sendRangeChange(); inRecvRangeChange.erase(id); @@ -640,7 +669,7 @@ Bus::getAddrRanges(int id) } bool -Bus::isSnooping(int id) +Bus::isSnooping(int id) const { // in essence, answer the question if there are snooping ports return !snoopPorts.empty(); @@ -656,7 +685,7 @@ Bus::findBlockSize(int id) PortIter p_end = portMap.end(); for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) { - unsigned tmp_bs = interfaces[p_iter->second]->peerBlockSize(); + unsigned tmp_bs = masterPorts[p_iter->second]->peerBlockSize(); if (tmp_bs > max_bs) max_bs = tmp_bs; } diff --git a/src/mem/bus.hh b/src/mem/bus.hh index 4ea92308a..e79e9df9e 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -40,6 +40,7 @@ * Authors: Ron Dreslinski * Ali Saidi * Andreas Hansson + * William Wang */ /** @@ -65,21 +66,85 @@ class Bus : public MemObject { - /** Declaration of the buses port type, one will be instantiated for each - of the interfaces connecting to the bus. */ - class BusPort : public Port + /** + * Declaration of the bus slave port type, one will be + * instantiated for each of the master interfaces connecting to + * the bus. + */ + class BusSlavePort : public SlavePort + { + private: + /** A pointer to the bus to which this port belongs. */ + Bus *bus; + + /** A id to keep track of the interface ID of this port. */ + int id; + + public: + + /** Constructor for the BusSlavePort.*/ + BusSlavePort(const std::string &_name, Bus *_bus, int _id) + : SlavePort(_name, _bus), bus(_bus), id(_id) + { } + + int getId() const { return id; } + + protected: + + /** When reciving a timing request from the peer port (at id), + pass it to the bus. */ + virtual bool recvTiming(PacketPtr pkt) + { pkt->setSrc(id); return bus->recvTiming(pkt); } + + /** When reciving a Atomic requestfrom the peer port (at id), + pass it to the bus. */ + virtual Tick recvAtomic(PacketPtr pkt) + { pkt->setSrc(id); return bus->recvAtomic(pkt); } + + /** When reciving a Functional requestfrom the peer port (at id), + pass it to the bus. */ + virtual void recvFunctional(PacketPtr pkt) + { pkt->setSrc(id); bus->recvFunctional(pkt); } + + /** When reciving a retry from the peer port (at id), + pass it to the bus. */ + virtual void recvRetry() + { bus->recvRetry(id); } + + // This should return all the 'owned' addresses that are + // downstream from this bus, yes? That is, the union of all + // the 'owned' address ranges of all the other interfaces on + // this bus... + virtual AddrRangeList getAddrRanges() + { return bus->getAddrRanges(id); } + + // Ask the bus to ask everyone on the bus what their block size is and + // take the max of it. This might need to be changed a bit if we ever + // support multiple block sizes. + virtual unsigned deviceBlockSize() const + { return bus->findBlockSize(id); } + + }; + + /** + * Declaration of the bus master port type, one will be + * instantiated for each of the slave interfaces connecting to the + * bus. + */ + class BusMasterPort : public MasterPort { + private: /** A pointer to the bus to which this port belongs. */ Bus *bus; - /** A id to keep track of the intercafe ID this port is connected to. */ + /** A id to keep track of the interface ID of this port. */ int id; public: - /** Constructor for the BusPort.*/ - BusPort(const std::string &_name, Bus *_bus, int _id) - : Port(_name, _bus), bus(_bus), id(_id) + /** Constructor for the BusMasterPort.*/ + BusMasterPort(const std::string &_name, Bus *_bus, int _id) + : MasterPort(_name, _bus), bus(_bus), id(_id) { } int getId() const { return id; } @@ -90,7 +155,7 @@ class Bus : public MemObject * * @return a boolean that is true if this port is snooping */ - virtual bool isSnooping() + virtual bool isSnooping() const { return bus->isSnooping(id); } protected: @@ -120,13 +185,6 @@ class Bus : public MemObject virtual void recvRetry() { bus->recvRetry(id); } - // This should return all the 'owned' addresses that are - // downstream from this bus, yes? That is, the union of all - // the 'owned' address ranges of all the other interfaces on - // this bus... - virtual AddrRangeList getAddrRanges() - { return bus->getAddrRanges(id); } - // Ask the bus to ask everyone on the bus what their block size is and // take the max of it. This might need to be changed a bit if we ever // support multiple block sizes. @@ -151,8 +209,8 @@ class Bus : public MemObject AddrRangeList defaultRange; - typedef std::vector<BusPort*>::iterator SnoopIter; - std::vector<BusPort*> snoopPorts; + typedef std::vector<BusSlavePort*>::iterator SnoopIter; + std::vector<BusSlavePort*> snoopPorts; /** Function called by the port when the bus is recieving a Timing transaction.*/ @@ -250,7 +308,7 @@ class Bus : public MemObject * * @return a boolean indicating if this port is snooping or not */ - bool isSnooping(int id); + bool isSnooping(int id) const; /** Calculate the timing parameters for the packet. Updates the * firstWordTime and finishTime fields of the packet object. @@ -292,9 +350,9 @@ class Bus : public MemObject // interfaces vector unsigned int nbrMasterPorts; - /** An ordered vector of pointers to the peer port interfaces - connected to this bus.*/ - std::vector<BusPort*> interfaces; + /** The master and slave ports of the bus */ + std::vector<BusSlavePort*> slavePorts; + std::vector<BusMasterPort*> masterPorts; /** An array of pointers to ports that retry should be called on because the * original send failed for whatever reason.*/ @@ -338,7 +396,8 @@ class Bus : public MemObject public: /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1); + virtual MasterPort& getMasterPort(const std::string& if_name, int idx = -1); + virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1); virtual void init(); virtual void startup(); diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc index a2cb59a76..4ae6376db 100644 --- a/src/mem/cache/base.cc +++ b/src/mem/cache/base.cc @@ -57,7 +57,7 @@ using namespace std; BaseCache::CacheSlavePort::CacheSlavePort(const std::string &_name, BaseCache *_cache, const std::string &_label) - : QueuedPort(_name, _cache, queue), queue(*_cache, *this, _label), + : QueuedSlavePort(_name, _cache, queue), queue(*_cache, *this, _label), blocked(false), mustSendRetry(false), sendRetryEvent(this) { } @@ -99,7 +99,7 @@ BaseCache::CacheSlavePort::clearBlocked() DPRINTF(CachePort, "Cache port %s sending retry\n", name()); mustSendRetry = false; // @TODO: need to find a better time (next bus cycle?) - owner->schedule(sendRetryEvent, curTick() + 1); + owner.schedule(sendRetryEvent, curTick() + 1); } } @@ -108,10 +108,29 @@ void BaseCache::init() { if (!cpuSidePort->isConnected() || !memSidePort->isConnected()) - panic("Cache %s not hooked up on both sides\n", name()); + fatal("Cache ports on %s are not connected\n", name()); cpuSidePort->sendRangeChange(); } +MasterPort & +BaseCache::getMasterPort(const std::string &if_name, int idx) +{ + if (if_name == "mem_side") { + return *memSidePort; + } else { + return MemObject::getMasterPort(if_name, idx); + } +} + +SlavePort & +BaseCache::getSlavePort(const std::string &if_name, int idx) +{ + if (if_name == "cpu_side") { + return *cpuSidePort; + } else { + return MemObject::getSlavePort(if_name, idx); + } +} void BaseCache::regStats() diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh index c13d27d42..47cbaf7a0 100644 --- a/src/mem/cache/base.hh +++ b/src/mem/cache/base.hh @@ -118,7 +118,7 @@ class BaseCache : public MemObject * and the sendDeferredPacket of the timing port is modified to * consider both the transmit list and the requests from the MSHR. */ - class CacheMasterPort : public QueuedPort + class CacheMasterPort : public QueuedMasterPort { public: @@ -149,7 +149,7 @@ class BaseCache : public MemObject CacheMasterPort(const std::string &_name, BaseCache *_cache, PacketQueue &_queue) : - QueuedPort(_name, _cache, _queue) + QueuedMasterPort(_name, _cache, _queue) { } /** @@ -157,7 +157,7 @@ class BaseCache : public MemObject * * @return always true */ - virtual bool isSnooping() { return true; } + virtual bool isSnooping() const { return true; } }; /** @@ -168,7 +168,7 @@ class BaseCache : public MemObject * incoming requests. If blocked, the port will issue a retry once * unblocked. */ - class CacheSlavePort : public QueuedPort + class CacheSlavePort : public QueuedSlavePort { public: @@ -444,6 +444,9 @@ class BaseCache : public MemObject virtual void init(); + virtual MasterPort &getMasterPort(const std::string &if_name, int idx = -1); + virtual SlavePort &getSlavePort(const std::string &if_name, int idx = -1); + /** * Query block size of a cache. * @return The block size diff --git a/src/mem/cache/builder.cc b/src/mem/cache/builder.cc index cc206b784..ca8c378fb 100644 --- a/src/mem/cache/builder.cc +++ b/src/mem/cache/builder.cc @@ -40,7 +40,6 @@ #include "mem/cache/base.hh" #include "mem/cache/cache.hh" #include "mem/config/cache.hh" -#include "mem/bus.hh" #include "params/BaseCache.hh" // Tag Templates diff --git a/src/mem/cache/cache.hh b/src/mem/cache/cache.hh index 782749aab..e745529a7 100644 --- a/src/mem/cache/cache.hh +++ b/src/mem/cache/cache.hh @@ -249,8 +249,6 @@ class Cache : public BaseCache /** Instantiates a basic cache object. */ Cache(const Params *p, TagStore *tags); - virtual Port *getPort(const std::string &if_name, int idx = -1); - void regStats(); /** diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh index 2463071de..3525e0777 100644 --- a/src/mem/cache/cache_impl.hh +++ b/src/mem/cache/cache_impl.hh @@ -92,19 +92,6 @@ Cache<TagStore>::regStats() } template<class TagStore> -Port * -Cache<TagStore>::getPort(const std::string &if_name, int idx) -{ - if (if_name == "cpu_side") { - return cpuSidePort; - } else if (if_name == "mem_side") { - return memSidePort; - } else { - panic("Port name %s unrecognized\n", if_name); - } -} - -template<class TagStore> void Cache<TagStore>::cmpAndSwap(BlkType *blk, PacketPtr pkt) { @@ -795,7 +782,7 @@ Cache<TagStore>::functionalAccess(PacketPtr pkt, bool fromCpuSide) // continues towards the memory side if (fromCpuSide) { memSidePort->sendFunctional(pkt); - } else if (forwardSnoops) { + } else if (forwardSnoops && cpuSidePort->getMasterPort().isSnooping()) { // if it came from the memory side, it must be a snoop request // and we should only forward it if we are forwarding snoops cpuSidePort->sendFunctional(pkt); diff --git a/src/mem/fs_translating_port_proxy.cc b/src/mem/fs_translating_port_proxy.cc index 2e9a0fa00..bf0c076c6 100644 --- a/src/mem/fs_translating_port_proxy.cc +++ b/src/mem/fs_translating_port_proxy.cc @@ -58,7 +58,7 @@ FSTranslatingPortProxy::FSTranslatingPortProxy(ThreadContext *tc) { } -FSTranslatingPortProxy::FSTranslatingPortProxy(Port &port) +FSTranslatingPortProxy::FSTranslatingPortProxy(MasterPort &port) : PortProxy(port), _tc(NULL) { } diff --git a/src/mem/fs_translating_port_proxy.hh b/src/mem/fs_translating_port_proxy.hh index 2217a46aa..c022f3d09 100644 --- a/src/mem/fs_translating_port_proxy.hh +++ b/src/mem/fs_translating_port_proxy.hh @@ -78,7 +78,7 @@ class FSTranslatingPortProxy : public PortProxy FSTranslatingPortProxy(ThreadContext* tc); - FSTranslatingPortProxy(Port &port); + FSTranslatingPortProxy(MasterPort &port); virtual ~FSTranslatingPortProxy(); diff --git a/src/mem/mem_object.cc b/src/mem/mem_object.cc index 111d3718c..ce8badbe7 100644 --- a/src/mem/mem_object.cc +++ b/src/mem/mem_object.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -26,6 +38,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Steve Reinhardt + * Andreas Hansson */ #include "mem/mem_object.hh" @@ -34,3 +47,15 @@ MemObject::MemObject(const Params *params) : SimObject(params) { } + +MasterPort& +MemObject::getMasterPort(const std::string& if_name, int idx) +{ + fatal("%s does not have any master port named %s\n", name(), if_name); +} + +SlavePort& +MemObject::getSlavePort(const std::string& if_name, int idx) +{ + fatal("%s does not have any slave port named %s\n", name(), if_name); +} diff --git a/src/mem/mem_object.hh b/src/mem/mem_object.hh index 5865fc935..d8e6bdcb0 100644 --- a/src/mem/mem_object.hh +++ b/src/mem/mem_object.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -26,11 +38,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Ron Dreslinski + * Andreas Hansson */ /** * @file - * Base Memory Object declaration. + * MemObject declaration. */ #ifndef __MEM_MEM_OBJECT_HH__ @@ -41,8 +54,8 @@ #include "sim/sim_object.hh" /** - * The base MemoryObject class, allows for an accesor function to a - * simobj that returns the Port. + * The MemObject class extends the SimObject with accessor functions + * to get its master and slave ports. */ class MemObject : public SimObject { @@ -53,9 +66,27 @@ class MemObject : public SimObject MemObject(const Params *params); - public: - /** Additional function to return the Port of a memory object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1) = 0; + /** + * Get a master port with a given name and index. + * + * @param if_name Port name + * @param idx Index in the case of a VectorPort + * + * @return A reference to the given port + */ + virtual MasterPort& getMasterPort(const std::string& if_name, + int idx = -1); + + /** + * Get a slave port with a given name and index. + * + * @param if_name Port name + * @param idx Index in the case of a VectorPort + * + * @return A reference to the given port + */ + virtual SlavePort& getSlavePort(const std::string& if_name, + int idx = -1); }; #endif //__MEM_MEM_OBJECT_HH__ diff --git a/src/mem/mport.cc b/src/mem/mport.cc index 2c57030b1..9af394d27 100644 --- a/src/mem/mport.cc +++ b/src/mem/mport.cc @@ -43,17 +43,26 @@ #include "mem/mport.hh" Tick -MessagePort::recvAtomic(PacketPtr pkt) +MessageSlavePort::recvAtomic(PacketPtr pkt) { if (pkt->cmd == MemCmd::MessageReq) { return recvMessage(pkt); - } else if (pkt->cmd == MemCmd::MessageResp) { + } else { + panic("%s received unexpected atomic command %s from %s.\n", + name(), pkt->cmd.toString(), getMasterPort().name()); + } +} + +Tick +MessageMasterPort::recvAtomic(PacketPtr pkt) +{ + if (pkt->cmd == MemCmd::MessageResp) { // normally we would never see responses in recvAtomic, but // since the timing port uses recvAtomic to implement - // recvTiming we have to deal with both cases + // recvTiming we have to deal with them here return recvResponse(pkt); } else { panic("%s received unexpected atomic command %s from %s.\n", - name(), pkt->cmd.toString(), getPeer()->name()); + name(), pkt->cmd.toString(), getSlavePort().name()); } } diff --git a/src/mem/mport.hh b/src/mem/mport.hh index 7f167c227..664acc559 100644 --- a/src/mem/mport.hh +++ b/src/mem/mport.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2008 The Regents of The University of Michigan * All rights reserved. * @@ -41,15 +53,15 @@ * the underpinnings of SimpleTimingPort, but it tweaks some of the external * functions. */ -class MessagePort : public SimpleTimingPort +class MessageSlavePort : public SimpleTimingPort { public: - MessagePort(const std::string &name, MemObject *owner) : + MessageSlavePort(const std::string &name, MemObject *owner) : SimpleTimingPort(name, owner) {} - virtual ~MessagePort() + virtual ~MessageSlavePort() {} protected: @@ -57,6 +69,29 @@ class MessagePort : public SimpleTimingPort Tick recvAtomic(PacketPtr pkt); virtual Tick recvMessage(PacketPtr pkt) = 0; +}; + +class MessageMasterPort : public QueuedMasterPort +{ + public: + + MessageMasterPort(const std::string &name, MemObject *owner) : + QueuedMasterPort(name, owner, queue), queue(*owner, *this) + {} + + virtual ~MessageMasterPort() + {} + + void recvFunctional(PacketPtr pkt) { assert(false); } + + Tick recvAtomic(PacketPtr pkt); + + bool recvTiming(PacketPtr pkt) { recvAtomic(pkt); return true; } + + protected: + + /** A packet queue for outgoing packets. */ + PacketQueue queue; // Accept and ignore responses. virtual Tick recvResponse(PacketPtr pkt) diff --git a/src/mem/physical.cc b/src/mem/physical.cc index f11fbb947..9c953e562 100644 --- a/src/mem/physical.cc +++ b/src/mem/physical.cc @@ -439,18 +439,18 @@ PhysicalMemory::doFunctionalAccess(PacketPtr pkt) } -Port * -PhysicalMemory::getPort(const std::string &if_name, int idx) +SlavePort & +PhysicalMemory::getSlavePort(const std::string &if_name, int idx) { if (if_name != "port") { - panic("PhysicalMemory::getPort: unknown port %s requested\n", if_name); - } + return MemObject::getSlavePort(if_name, idx); + } else { + if (idx >= static_cast<int>(ports.size())) { + fatal("PhysicalMemory::getSlavePort: unknown index %d\n", idx); + } - if (idx >= static_cast<int>(ports.size())) { - panic("PhysicalMemory::getPort: unknown index %d requested\n", idx); + return *ports[idx]; } - - return ports[idx]; } PhysicalMemory::MemoryPort::MemoryPort(const std::string &_name, @@ -458,13 +458,6 @@ PhysicalMemory::MemoryPort::MemoryPort(const std::string &_name, : SimpleTimingPort(_name, _memory), memory(_memory) { } -void -PhysicalMemory::MemoryPort::recvRangeChange() -{ - // memory is a slave and thus should never have to worry about its - // neighbours address ranges -} - AddrRangeList PhysicalMemory::MemoryPort::getAddrRanges() { diff --git a/src/mem/physical.hh b/src/mem/physical.hh index b447237c7..452ac3fc0 100644 --- a/src/mem/physical.hh +++ b/src/mem/physical.hh @@ -67,8 +67,6 @@ class PhysicalMemory : public MemObject virtual void recvFunctional(PacketPtr pkt); - virtual void recvRangeChange(); - virtual AddrRangeList getAddrRanges(); virtual unsigned deviceBlockSize() const; @@ -196,7 +194,7 @@ class PhysicalMemory : public MemObject public: unsigned deviceBlockSize() const; AddrRangeList getAddrRanges(); - virtual Port *getPort(const std::string &if_name, int idx = -1); + virtual SlavePort &getSlavePort(const std::string &if_name, int idx = -1); void virtual init(); unsigned int drain(Event *de); diff --git a/src/mem/port.cc b/src/mem/port.cc index 8edca16f7..3305541c7 100644 --- a/src/mem/port.cc +++ b/src/mem/port.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -26,6 +38,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Steve Reinhardt + * Andreas Hansson + * William Wang */ /** @@ -33,11 +47,10 @@ * Port object definitions. */ #include "base/trace.hh" -#include "debug/Config.hh" #include "mem/mem_object.hh" #include "mem/port.hh" -Port::Port(const std::string &_name, MemObject *_owner) +Port::Port(const std::string &_name, MemObject& _owner) : portName(_name), peer(NULL), owner(_owner) { } @@ -46,22 +59,53 @@ Port::~Port() { } -void -Port::setPeer(Port *port) +/** + * Master port + */ +MasterPort::MasterPort(const std::string& name, MemObject* owner) + : Port(name, *owner), _slavePort(NULL) { - DPRINTF(Config, "setting peer to %s\n", port->name()); +} - peer = port; +MasterPort::~MasterPort() +{ } -void -Port::setOwner(MemObject *_owner) +SlavePort& +MasterPort::getSlavePort() const { - owner = _owner; + if(_slavePort == NULL) + panic("Cannot getSlavePort on master port %s that is not connected\n", + name()); + + return *_slavePort; } void -Port::printAddr(Addr a) +MasterPort::bind(SlavePort& slave_port) +{ + // master port keeps track of the slave port + _slavePort = &slave_port; + peer = &slave_port; + + // slave port also keeps track of master port + _slavePort->bind(*this); +} + +bool +MasterPort::isConnected() const +{ + return _slavePort != NULL; +} + +unsigned +MasterPort::peerBlockSize() const +{ + return _slavePort->deviceBlockSize(); +} + + void +MasterPort::printAddr(Addr a) { Request req(a, 1, 0, Request::funcMasterId); Packet pkt(&req, MemCmd::PrintReq, Packet::Broadcast); @@ -70,3 +114,44 @@ Port::printAddr(Addr a) sendFunctional(&pkt); } + +/** + * Slave port + */ +SlavePort::SlavePort(const std::string& name, MemObject* owner) + : Port(name, *owner), _masterPort(NULL) +{ +} + +SlavePort::~SlavePort() +{ +} + +void +SlavePort::bind(MasterPort& master_port) +{ + _masterPort = &master_port; + peer = &master_port; +} + +MasterPort& +SlavePort::getMasterPort() const +{ + if (_masterPort == NULL) + panic("Cannot getMasterPort on slave port %s that is not connected\n", + name()); + + return *_masterPort; +} + +unsigned +SlavePort::peerBlockSize() const +{ + return _masterPort->deviceBlockSize(); +} + +bool +SlavePort::isConnected() const +{ + return _masterPort != NULL; +} diff --git a/src/mem/port.hh b/src/mem/port.hh index fef0c839d..80bb3b085 100644 --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -39,15 +39,12 @@ * * Authors: Ron Dreslinski * Andreas Hansson + * William Wang */ /** * @file - * Port Object Declaration. Ports are used to interface memory objects to - * each other. They will always come in pairs, and we refer to the other - * port object as the peer. These are used to make the design more - * modular so that a specific interface between every type of objcet doesn't - * have to be created. + * Port Object Declaration. */ #ifndef __MEM_PORT_HH__ @@ -58,7 +55,8 @@ #include "base/range.hh" #include "mem/packet.hh" -/** This typedef is used to clean up getAddrRanges(). It's declared +/** + * This typedef is used to clean up getAddrRanges(). It's declared * outside the Port object since it's also used by some mem objects. * Eventually we should move this typedef to wherever Addr is * defined. @@ -70,62 +68,47 @@ typedef std::list<Range<Addr> >::iterator AddrRangeIter; class MemObject; /** - * Ports are used to interface memory objects to - * each other. They will always come in pairs, and we refer to the other - * port object as the peer. These are used to make the design more - * modular so that a specific interface between every type of objcet doesn't - * have to be created. + * 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 + * opposite role. * - * Recv accesor functions are being called from the peer interface. - * Send accessor functions are being called from the device the port is - * associated with, and it will call the peer recv. accessor function. + * Each port has a name and an owner, and enables three basic types of + * accesses to the peer port: sendFunctional, sendAtomic and + * sendTiming. */ class Port { - protected: + + private: + /** Descriptive name (for DPRINTF output) */ - mutable std::string portName; + std::string portName; - /** A pointer to the peer port. Ports always come in pairs, that way they - can use a standardized interface to communicate between different - memory objects. */ - Port *peer; + protected: - /** A pointer to the MemObject that owns this port. This may not be set. */ - MemObject *owner; + /** A pointer to the peer port. */ + Port* peer; + + /** A reference to the MemObject that owns this port. */ + MemObject& owner; - public: /** - * Constructor. + * Abstract base class for ports * - * @param _name Port name for DPRINTF output. Should include name - * of memory system object to which the port belongs. - * @param _owner Pointer to the MemObject that owns this port. - * Will not necessarily be set. + * @param _name Port name including the owners name + * @param _owner The MemObject that is the structural owner of this port */ - Port(const std::string &_name, MemObject *_owner); - - /** Return port name (for DPRINTF). */ - const std::string &name() const { return portName; } + Port(const std::string& _name, MemObject& _owner); + /** + * Virtual destructor due to inheritance. + */ virtual ~Port(); - void setName(const std::string &name) - { portName = name; } - - /** Function to set the pointer for the peer port. */ - virtual void setPeer(Port *port); - - /** Function to get the pointer to the peer port. */ - Port *getPeer() { return peer; } - - /** Function to set the owner of this port. */ - void setOwner(MemObject *_owner); - - /** Function to return the owner of this port. */ - MemObject *getOwner() { return owner; } + public: - bool isConnected() { return peer != NULL; } + /** Return port name (for DPRINTF). */ + const std::string name() const { return portName; } protected: @@ -141,89 +124,109 @@ class Port /** Called to recive a functional call from the peer port. */ virtual void recvFunctional(PacketPtr pkt) = 0; - /** Called to recieve an address range change from the peer port. */ - virtual void recvRangeChange() = 0; + /** + * Called by a peer port if sendTiming was unsuccesful, and had to + * wait. + */ + virtual void recvRetry() = 0; - /** Called by a peer port if the send was unsuccesful, and had to - wait. This shouldn't be valid for response paths (IO Devices). - so it is set to panic if it isn't already defined. - */ - virtual void recvRetry() { panic("??"); } + public: - /** Called by a peer port in order to determine the block size of the - device connected to this port. It sometimes doesn't make sense for - this function to be called, so it just returns 0. Anytthing that is - concerned with the size should just ignore that. + /** + * Attempt to send a timing packet to the peer port by calling its + * receive function. If the send does not succeed, as indicated by + * the return value, then the sender must wait for a recvRetry at + * which point it can re-issue a sendTiming. + * + * @param pkt Packet to send. + * + * @return If the send was succesful or not. */ - virtual unsigned deviceBlockSize() const { return 0; } + bool sendTiming(PacketPtr pkt) { return peer->recvTiming(pkt); } - public: + /** + * Send a retry to a peer port that previously attempted a sendTiming + * which was unsuccessful. + */ + void sendRetry() { return peer->recvRetry(); } /** - * Get a list of the non-overlapping address ranges we are - * responsible for. The default implementation returns an empty - * list and thus no address ranges. Any slave port must override - * this function and return a populated list with at least one - * item. + * Send an atomic packet, where the data is moved and the state + * is updated in zero time, without interleaving with other + * memory accesses. * - * @return a list of ranges responded to + * @param pkt Packet to send. + * + * @return Estimated latency of access. */ - virtual AddrRangeList getAddrRanges() - { AddrRangeList ranges; return ranges; } + Tick sendAtomic(PacketPtr pkt) { return peer->recvAtomic(pkt); } /** - * Determine if this port is snooping or not. The default - * implementation returns false and thus tells the neighbour we - * are not snooping. Any port that is to snoop (e.g. a cache - * connected to a bus) has to override this function. + * Send a functional packet, where the data is instantly updated + * everywhere in the memory system, without affecting the current + * state of any block or moving the block. * - * @return true if the port should be considered a snooper + * @param pkt Packet to send. */ - virtual bool isSnooping() - { return false; } - - /** Function called by associated memory device (cache, memory, iodevice) - in order to send a timing request to the port. Simply calls the peer - port receive function. - @return This function returns if the send was succesful in it's - recieve. If it was a failure, then the port will wait for a recvRetry - at which point it can possibly issue a successful sendTiming. This is used in - case a cache has a higher priority request come in while waiting for - the bus to arbitrate. - */ - bool sendTiming(PacketPtr pkt) { return peer->recvTiming(pkt); } + void sendFunctional(PacketPtr pkt) { return peer->recvFunctional(pkt); } - /** Function called by the associated device to send an atomic - * access, an access in which the data is moved and the state is - * updated in one cycle, without interleaving with other memory - * accesses. Returns estimated latency of access. - */ - Tick sendAtomic(PacketPtr pkt) - { return peer->recvAtomic(pkt); } +}; - /** Function called by the associated device to send a functional access, - an access in which the data is instantly updated everywhere in the - memory system, without affecting the current state of any block or - moving the block. - */ - void sendFunctional(PacketPtr pkt) - { return peer->recvFunctional(pkt); } +/** Forward declaration */ +class SlavePort; + +/** + * A MasterPort is a specialisation of a port. In addition to the + * basic functionality of sending packets to its slave peer, it also + * has functions specific to a master, e.g. to receive range changes + * or determine if the port is snooping or not. + */ +class MasterPort : public Port +{ + + private: + + SlavePort* _slavePort; + + public: + + MasterPort(const std::string& name, MemObject* owner); + virtual ~MasterPort(); + + void bind(SlavePort& slave_port); + SlavePort& getSlavePort() const; + bool isConnected() const; /** - * Called by the associated device to send a status range to the - * peer interface. + * Called to receive an address range change from the peer slave + * port. the default implementation ignored the change and does + * nothing. Override this function in a derived class if the owner + * needs to be aware of he laesddress ranges, e.g. in an + * interconnect component like a bus. */ - void sendRangeChange() const { peer->recvRangeChange(); } + virtual void recvRangeChange() { } - /** When a timing access doesn't return a success, some time later the - Retry will be sent. - */ - void sendRetry() { return peer->recvRetry(); } + /** + * Determine if this master port is snooping or not. The default + * implementation returns false and thus tells the neighbour we + * are not snooping. Any master port that wants to receive snoop + * requests (e.g. a cache connected to a bus) has to override this + * function. + * + * @return true if the port should be considered a snooper + */ + virtual bool isSnooping() const { return false; } + + /** + * Called by a peer port in order to determine the block size of + * the owner of this port. + */ + virtual unsigned deviceBlockSize() const { return 0; } /** Called by the associated device if it wishes to find out the blocksize of the device on attached to the peer port. */ - unsigned peerBlockSize() const { return peer->deviceBlockSize(); } + unsigned peerBlockSize() const; /** Inject a PrintReq for the given address to print the state of * that address throughout the memory system. For debugging. @@ -231,4 +234,52 @@ class Port void printAddr(Addr a); }; +/** + * A SlavePort is a specialisation of a port. In addition to the + * basic functionality of sending packets to its master peer, it also + * has functions specific to a slave, e.g. to send range changes + * and get the address ranges that the port responds to. + */ +class SlavePort : public Port +{ + + private: + + MasterPort* _masterPort; + + public: + + SlavePort(const std::string& name, MemObject* owner); + virtual ~SlavePort(); + + void bind(MasterPort& master_port); + MasterPort& getMasterPort() const; + bool isConnected() const; + + /** + * Called by a peer port in order to determine the block size of + * the owner of this port. + */ + virtual unsigned deviceBlockSize() const { return 0; } + + /** Called by the associated device if it wishes to find out the blocksize + of the device on attached to the peer port. + */ + unsigned peerBlockSize() const; + + /** + * Called by the owner to send a range change + */ + void sendRangeChange() const { _masterPort->recvRangeChange(); } + + /** + * Get a list of the non-overlapping address ranges the owner is + * responsible for. All slave ports must override this function + * and return a populated list with at least one item. + * + * @return a list of ranges responded to + */ + virtual AddrRangeList getAddrRanges() = 0; +}; + #endif //__MEM_PORT_HH__ diff --git a/src/mem/port_proxy.hh b/src/mem/port_proxy.hh index b2b27312e..4600cd407 100644 --- a/src/mem/port_proxy.hh +++ b/src/mem/port_proxy.hh @@ -86,12 +86,12 @@ class PortProxy private: /** The actual physical port used by this proxy. */ - Port &_port; + MasterPort &_port; void blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd) const; public: - PortProxy(Port &port) : _port(port) { } + PortProxy(MasterPort &port) : _port(port) { } virtual ~PortProxy() { } /** diff --git a/src/mem/qport.hh b/src/mem/qport.hh index 39612d22f..6ee71a572 100644 --- a/src/mem/qport.hh +++ b/src/mem/qport.hh @@ -56,7 +56,7 @@ * queue is a parameter to allow tailoring of the queue implementation * (used in the cache). */ -class QueuedPort : public Port +class QueuedSlavePort : public SlavePort { protected: @@ -68,7 +68,46 @@ class QueuedPort : public Port * packet again. */ virtual void recvRetry() { queue.retry(); } - virtual void recvRangeChange() { } + public: + + /** + * Create a QueuedPort with a given name, owner, and a supplied + * implementation of a packet queue. The external definition of + * the queue enables e.g. the cache to implement a specific queue + * behaviuor in a subclass, and provide the latter to the + * QueuePort constructor. + */ + QueuedSlavePort(const std::string& name, MemObject* owner, + PacketQueue &queue) : + SlavePort(name, owner), queue(queue) + { } + + virtual ~QueuedSlavePort() { } + + /** Check the list of buffered packets against the supplied + * functional request. */ + bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); } + + /** + * Hook for draining the queued port. + * + * @param de an event which is used to signal back to the caller + * @returns a number indicating how many times process will be called + */ + unsigned int drain(Event *de) { return queue.drain(de); } +}; + +class QueuedMasterPort : public MasterPort +{ + + protected: + + /** Packet queue used to store outgoing requests and responses. */ + PacketQueue &queue; + + /** This function is notification that the device should attempt to send a + * packet again. */ + virtual void recvRetry() { queue.retry(); } public: @@ -79,11 +118,12 @@ class QueuedPort : public Port * behaviuor in a subclass, and provide the latter to the * QueuePort constructor. */ - QueuedPort(const std::string& name, MemObject* owner, PacketQueue &queue) : - Port(name, owner), queue(queue) + QueuedMasterPort(const std::string& name, MemObject* owner, + PacketQueue &queue) : + MasterPort(name, owner), queue(queue) { } - virtual ~QueuedPort() { } + virtual ~QueuedMasterPort() { } /** Check the list of buffered packets against the supplied * functional request. */ diff --git a/src/mem/ruby/system/RubyPort.cc b/src/mem/ruby/system/RubyPort.cc index aca6604c6..1769c51fd 100644 --- a/src/mem/ruby/system/RubyPort.cc +++ b/src/mem/ruby/system/RubyPort.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012 ARM Limited + * All rights reserved. + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2009 Advanced Micro Devices, Inc. * Copyright (c) 2011 Mark D. Hill and David A. Wood * All rights reserved. @@ -35,26 +47,27 @@ #include "mem/ruby/system/RubyPort.hh" RubyPort::RubyPort(const Params *p) - : MemObject(p), pio_port(csprintf("%s-pio-port", name()), this), - physMemPort(csprintf("%s-physMemPort", name()), this) + : MemObject(p), m_version(p->version), m_controller(NULL), + m_mandatory_q_ptr(NULL), + pio_port(csprintf("%s-pio-port", name()), this), + m_usingRubyTester(p->using_ruby_tester), m_request_cnt(0), + physMemPort(csprintf("%s-physMemPort", name()), this), + drainEvent(NULL), physmem(p->physmem), ruby_system(p->ruby_system), + waitingOnSequencer(false), access_phys_mem(p->access_phys_mem) { - m_version = p->version; assert(m_version != -1); - physmem = p->physmem; - - m_controller = NULL; - m_mandatory_q_ptr = NULL; - - m_request_cnt = 0; - - m_usingRubyTester = p->using_ruby_tester; - access_phys_mem = p->access_phys_mem; - - drainEvent = NULL; + // create the slave ports based on the number of connected ports + for (size_t i = 0; i < p->port_slave_connection_count; ++i) { + slave_ports.push_back(new M5Port(csprintf("%s-slave%d", name(), i), + this, ruby_system, access_phys_mem)); + } - ruby_system = p->ruby_system; - waitingOnSequencer = false; + // create the master ports based on the number of connected ports + for (size_t i = 0; i < p->port_master_connection_count; ++i) { + master_ports.push_back(new PioPort(csprintf("%s-master%d", name(), i), + this)); + } } void @@ -64,52 +77,63 @@ RubyPort::init() m_mandatory_q_ptr = m_controller->getMandatoryQueue(); } -Port * -RubyPort::getPort(const std::string &if_name, int idx) +MasterPort & +RubyPort::getMasterPort(const std::string &if_name, int 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") { - M5Port* cpuPort = new M5Port(csprintf("%s-slave%d", name(), idx), - this, ruby_system, access_phys_mem); - cpu_ports.push_back(cpuPort); - return cpuPort; + if (if_name == "pio_port") { + return pio_port; + } + + if (if_name == "physMemPort") { + return physMemPort; } // used by the x86 CPUs to connect the interrupt PIO and interrupt slave // port - if (if_name == "master") { - PioPort* masterPort = new PioPort(csprintf("%s-master%d", name(), idx), - this); + if (if_name != "master") { + // pass it along to our super class + return MemObject::getMasterPort(if_name, idx); + } else { + if (idx >= static_cast<int>(master_ports.size())) { + panic("RubyPort::getMasterPort: unknown index %d\n", idx); + } - return masterPort; + return *master_ports[idx]; } +} - if (if_name == "pio_port") { - return &pio_port; - } +SlavePort & +RubyPort::getSlavePort(const std::string &if_name, int 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 { + if (idx >= static_cast<int>(slave_ports.size())) { + panic("RubyPort::getSlavePort: unknown index %d\n", idx); + } - if (if_name == "physMemPort") { - return &physMemPort; + return *slave_ports[idx]; } - - return NULL; } RubyPort::PioPort::PioPort(const std::string &_name, RubyPort *_port) - : QueuedPort(_name, _port, queue), queue(*_port, *this), ruby_port(_port) + : QueuedMasterPort(_name, _port, queue), queue(*_port, *this), + ruby_port(_port) { - DPRINTF(RubyPort, "creating port to ruby sequencer to cpu %s\n", _name); + DPRINTF(RubyPort, "creating master port on ruby sequencer %s\n", _name); } RubyPort::M5Port::M5Port(const std::string &_name, RubyPort *_port, RubySystem *_system, bool _access_phys_mem) - : QueuedPort(_name, _port, queue), queue(*_port, *this), + : QueuedSlavePort(_name, _port, queue), queue(*_port, *this), ruby_port(_port), ruby_system(_system), _onRetryList(false), access_phys_mem(_access_phys_mem) { - DPRINTF(RubyPort, "creating port from ruby sequcner to cpu %s\n", _name); + DPRINTF(RubyPort, "creating slave port on ruby sequencer %s\n", _name); } Tick @@ -549,11 +573,15 @@ RubyPort::getDrainCount(Event *de) DPRINTF(Config, "count after physmem check %d\n", count); } - for (CpuPortIter p_iter = cpu_ports.begin(); p_iter != cpu_ports.end(); - p_iter++) { - M5Port* cpu_port = *p_iter; - count += cpu_port->drain(de); - DPRINTF(Config, "count after cpu port check %d\n", count); + for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) { + count += (*p)->drain(de); + DPRINTF(Config, "count after slave port check %d\n", count); + } + + for (std::vector<PioPort*>::iterator p = master_ports.begin(); + p != master_ports.end(); ++p) { + count += (*p)->drain(de); + DPRINTF(Config, "count after master port check %d\n", count); } DPRINTF(Config, "final count %d\n", count); @@ -657,11 +685,19 @@ RubyPort::PioPort::sendNextCycle(PacketPtr pkt) return true; } +AddrRangeList +RubyPort::M5Port::getAddrRanges() +{ + // at the moment the assumption is that the master does not care + AddrRangeList ranges; + return ranges; +} + bool RubyPort::M5Port::isPhysMemAddress(Addr addr) { AddrRangeList physMemAddrList = - ruby_port->physMemPort.getPeer()->getAddrRanges(); + ruby_port->physMemPort.getSlavePort().getAddrRanges(); for (AddrRangeIter iter = physMemAddrList.begin(); iter != physMemAddrList.end(); iter++) { @@ -684,9 +720,12 @@ void RubyPort::ruby_eviction_callback(const Address& address) { DPRINTF(RubyPort, "Sending invalidations.\n"); + // should this really be using funcMasterId? Request req(address.getAddress(), 0, 0, Request::funcMasterId); - for (CpuPortIter it = cpu_ports.begin(); it != cpu_ports.end(); it++) { - Packet *pkt = new Packet(&req, MemCmd::InvalidationReq, -1); - (*it)->sendNextCycle(pkt); + for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) { + if ((*p)->getMasterPort().isSnooping()) { + Packet *pkt = new Packet(&req, MemCmd::InvalidationReq, -1); + (*p)->sendNextCycle(pkt); + } } } diff --git a/src/mem/ruby/system/RubyPort.hh b/src/mem/ruby/system/RubyPort.hh index bef291d63..d97f6e69e 100644 --- a/src/mem/ruby/system/RubyPort.hh +++ b/src/mem/ruby/system/RubyPort.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012 ARM Limited + * All rights reserved. + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2009 Advanced Micro Devices, Inc. * Copyright (c) 2011 Mark D. Hill and David A. Wood * All rights reserved. @@ -46,7 +58,7 @@ class AbstractController; class RubyPort : public MemObject { public: - class M5Port : public QueuedPort + class M5Port : public QueuedSlavePort { private: @@ -74,6 +86,7 @@ class RubyPort : public MemObject virtual bool recvTiming(PacketPtr pkt); virtual Tick recvAtomic(PacketPtr pkt); virtual void recvFunctional(PacketPtr pkt); + virtual AddrRangeList getAddrRanges(); private: bool isPhysMemAddress(Addr addr); @@ -83,7 +96,7 @@ class RubyPort : public MemObject friend class M5Port; - class PioPort : public QueuedPort + class PioPort : public QueuedMasterPort { private: @@ -119,7 +132,8 @@ class RubyPort : public MemObject void init(); - Port *getPort(const std::string &if_name, int idx); + MasterPort &getMasterPort(const std::string &if_name, int idx); + SlavePort &getSlavePort(const std::string &if_name, int idx); virtual RequestStatus makeRequest(PacketPtr pkt) = 0; virtual int outstandingCount() const = 0; @@ -163,9 +177,10 @@ class RubyPort : public MemObject PioPort physMemPort; - /*! Vector of CPU Port attached to this Ruby port. */ + /** Vector of M5 Ports attached to this Ruby port. */ typedef std::vector<M5Port*>::iterator CpuPortIter; - std::vector<M5Port*> cpu_ports; + std::vector<M5Port*> slave_ports; + std::vector<PioPort*> master_ports; Event *drainEvent; diff --git a/src/mem/se_translating_port_proxy.cc b/src/mem/se_translating_port_proxy.cc index 2ab130744..72466655c 100644 --- a/src/mem/se_translating_port_proxy.cc +++ b/src/mem/se_translating_port_proxy.cc @@ -53,7 +53,7 @@ using namespace TheISA; -SETranslatingPortProxy::SETranslatingPortProxy(Port& port, Process *p, +SETranslatingPortProxy::SETranslatingPortProxy(MasterPort& port, Process *p, AllocType alloc) : PortProxy(port), pTable(p->pTable), process(p), allocating(alloc) diff --git a/src/mem/se_translating_port_proxy.hh b/src/mem/se_translating_port_proxy.hh index 31874e6b8..c0e522611 100644 --- a/src/mem/se_translating_port_proxy.hh +++ b/src/mem/se_translating_port_proxy.hh @@ -80,7 +80,7 @@ class SETranslatingPortProxy : public PortProxy AllocType allocating; public: - SETranslatingPortProxy(Port& port, Process* p, AllocType alloc); + SETranslatingPortProxy(MasterPort& port, Process* p, AllocType alloc); virtual ~SETranslatingPortProxy(); bool tryReadBlob(Addr addr, uint8_t *p, int size) const; diff --git a/src/mem/tport.cc b/src/mem/tport.cc index bf3d59a8f..db2c72bbc 100644 --- a/src/mem/tport.cc +++ b/src/mem/tport.cc @@ -46,7 +46,7 @@ SimpleTimingPort::SimpleTimingPort(const std::string& _name, MemObject* _owner) : - QueuedPort(_name, _owner, queue), queue(*_owner, *this) + QueuedSlavePort(_name, _owner, queue), queue(*_owner, *this) { } @@ -63,6 +63,9 @@ SimpleTimingPort::recvFunctional(PacketPtr pkt) bool SimpleTimingPort::recvTiming(PacketPtr pkt) { + // the port is a slave and should hence only get timing requests + assert(pkt->isRequest()); + if (pkt->memInhibitAsserted()) { // snooper will supply based on copy of packet // still target's responsibility to delete packet diff --git a/src/mem/tport.hh b/src/mem/tport.hh index c77166386..91706fbe9 100644 --- a/src/mem/tport.hh +++ b/src/mem/tport.hh @@ -57,7 +57,7 @@ * recvFunctional and recvTiming through recvAtomic. It is always a * slave port. */ -class SimpleTimingPort : public QueuedPort +class SimpleTimingPort : public QueuedSlavePort { protected: |