X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmem%2Fbus.hh;h=705a3a999cd2a9d6df943b49cd8abd717a4db14d;hb=8573a69d8f7bf7b3f074e3e0ac64994801c551be;hp=bf64203bf925fe7428e6c0335f499105ae6e3ddc;hpb=beed20d7bc31512cc36304bd64876fe4af014641;p=gem5.git diff --git a/src/mem/bus.hh b/src/mem/bus.hh index bf64203bf..705a3a999 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 ARM Limited + * Copyright (c) 2011-2013 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -45,325 +45,250 @@ /** * @file - * Declaration of a bus object. + * Declaration of an abstract bus base class. */ #ifndef __MEM_BUS_HH__ #define __MEM_BUS_HH__ -#include +#include #include -#include -#include "base/range.hh" -#include "base/range_map.hh" +#include "base/addr_range_map.hh" #include "base/types.hh" #include "mem/mem_object.hh" -#include "mem/packet.hh" -#include "mem/port.hh" -#include "params/Bus.hh" -#include "sim/eventq.hh" +#include "params/BaseBus.hh" -class Bus : public MemObject +/** + * The base bus contains the common elements of the non-coherent and + * coherent bus. It is an abstract class that does not have any of the + * functionality relating to the actual reception and transmission of + * packets, as this is left for the subclasses. + * + * The BaseBus is responsible for the basic flow control (busy or + * not), the administration of retries, and the address decoding. + */ +class BaseBus : public MemObject { + + protected: + /** - * Declaration of the bus slave port type, one will be - * instantiated for each of the master interfaces connecting to - * the bus. + * A bus layer is an internal bus structure with its own flow + * control and arbitration. Hence, a single-layer bus mimics a + * traditional off-chip tri-state bus (like PCI), where only one + * set of wires are shared. For on-chip buses, a good starting + * point is to have three layers, for requests, responses, and + * snoop responses respectively (snoop requests are instantaneous + * and do not need any flow control or arbitration). This case is + * similar to AHB and some OCP configurations. + * + * As a further extensions beyond the three-layer bus, a future + * multi-layer bus has with one layer per connected slave port + * provides a full or partial crossbar, like AXI, OCP, PCIe etc. + * + * The template parameter, PortClass, indicates the destination + * port type for the bus. The retry list holds either master ports + * or slave ports, depending on the direction of the layer. Thus, + * a request layer has a retry list containing slave ports, + * whereas a response layer holds master ports. */ - class BusSlavePort : public SlavePort + template + class Layer : public Drainable { - private: - /** A pointer to the bus to which this port belongs. */ - Bus *bus; public: - /** Constructor for the BusSlavePort.*/ - BusSlavePort(const std::string &_name, Bus *_bus, Port::PortId _id) - : SlavePort(_name, _bus, _id), bus(_bus) - { } - - protected: - /** - * When receiving a timing request, pass it to the bus. + * Create a bus layer and give it a name. The bus layer uses + * the bus an event manager. + * + * @param _bus the bus this layer belongs to + * @param _name the layer's name */ - virtual bool recvTiming(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvTiming(pkt); } + Layer(BaseBus& _bus, const std::string& _name); /** - * When receiving a timing snoop response, pass it to the bus. + * Drain according to the normal semantics, so that the bus + * can tell the layer to drain, and pass an event to signal + * back when drained. + * + * @param de drain event to call once drained + * + * @return 1 if busy or waiting to retry, or 0 if idle */ - virtual bool recvTimingSnoop(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvTimingSnoop(pkt); } + unsigned int drain(DrainManager *dm); /** - * When receiving an atomic request, pass it to the bus. + * Get the bus layer's name */ - virtual Tick recvAtomic(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvAtomic(pkt); } + const std::string name() const { return bus.name() + _name; } + /** - * When receiving a functional request, pass it to the bus. + * Determine if the bus layer accepts a packet from a specific + * port. If not, the port in question is also added to the + * retry list. In either case the state of the layer is updated + * accordingly. + * + * @param port Source port resenting the packet + * + * @return True if the bus layer accepts the packet */ - virtual void recvFunctional(PacketPtr pkt) - { pkt->setSrc(id); bus->recvFunctional(pkt); } + bool tryTiming(PortClass* port); /** - * When receiving a retry, pass it to the bus. + * Deal with a destination port accepting a packet by potentially + * removing the source port from the retry list (if retrying) and + * occupying the bus layer accordingly. + * + * @param busy_time Time to spend as a result of a successful send */ - virtual void recvRetry() - { panic("Bus slave ports always succeed and should never retry.\n"); } - - // 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; - - public: - - /** Constructor for the BusMasterPort.*/ - BusMasterPort(const std::string &_name, Bus *_bus, Port::PortId _id) - : MasterPort(_name, _bus, _id), bus(_bus) - { } + void succeededTiming(Tick busy_time); /** - * Determine if this port should be considered a snooper. This - * is determined by the bus. + * Deal with a destination port not accepting a packet by + * potentially adding the source port to the retry list (if + * not already at the front) and occupying the bus layer + * accordingly. * - * @return a boolean that is true if this port is snooping + * @param busy_time Time to spend as a result of a failed send */ - virtual bool isSnooping() const - { return bus->isSnooping(id); } + void failedTiming(PortClass* port, Tick busy_time); - protected: + /** Occupy the bus layer until until */ + void occupyLayer(Tick until); /** - * When receiving a timing response, pass it to the bus. + * Send a retry to the port at the head of the retryList. The + * caller must ensure that the list is not empty. */ - virtual bool recvTiming(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvTiming(pkt); } + void retryWaiting(); /** - * When receiving a timing snoop request, pass it to the bus. + * Handler a retry from a neighbouring module. Eventually this + * should be all encapsulated in the bus. This wraps + * retryWaiting by verifying that there are ports waiting + * before calling retryWaiting. */ - virtual bool recvTimingSnoop(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvTimingSnoop(pkt); } + void recvRetry(); + + private: + + /** The bus this layer is a part of. */ + BaseBus& bus; + + /** A name for this layer. */ + std::string _name; /** - * When receiving an atomic snoop request, pass it to the bus. + * We declare an enum to track the state of the bus layer. The + * starting point is an idle state where the bus layer is + * waiting for a packet to arrive. Upon arrival, the bus layer + * transitions to the busy state, where it remains either + * until the packet transfer is done, or the header time is + * spent. Once the bus layer leaves the busy state, it can + * either go back to idle, if no packets have arrived while it + * was busy, or the bus layer goes on to retry the first port + * on the retryList. A similar transition takes place from + * idle to retry if the bus layer receives a retry from one of + * its connected ports. The retry state lasts until the port + * in questions calls sendTiming and returns control to the + * bus layer, or goes to a busy state if the port does not + * immediately react to the retry by calling sendTiming. */ - virtual Tick recvAtomicSnoop(PacketPtr pkt) - { pkt->setSrc(id); return bus->recvAtomicSnoop(pkt); } + enum State { IDLE, BUSY, RETRY }; + + /** track the state of the bus layer */ + State state; + + /** manager to signal when drained */ + DrainManager *drainManager; /** - * When receiving a functional snoop request, pass it to the bus. + * An array of ports that retry should be called + * on because the original send failed for whatever reason. */ - virtual void recvFunctionalSnoop(PacketPtr pkt) - { pkt->setSrc(id); bus->recvFunctionalSnoop(pkt); } + std::deque retryList; - /** When reciving a range change from the peer port (at id), - pass it to the bus. */ - virtual void recvRangeChange() - { bus->recvRangeChange(id); } - - /** When reciving a retry from the peer port (at id), - pass it to the bus. */ - virtual void recvRetry() - { bus->recvRetry(id); } + /** + * Release the bus layer after being occupied and return to an + * idle state where we proceed to send a retry to any + * potential waiting port, or drain if asked to do so. + */ + void releaseLayer(); - // 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); } + /** event used to schedule a release of the layer */ + EventWrapper releaseEvent; }; - /** the clock speed for the bus */ - int clock; /** cycles of overhead per transaction */ - int headerCycles; + const Cycles headerCycles; /** the width of the bus in bytes */ - int width; - /** the next tick at which the bus will be idle */ - Tick tickNextIdle; - - Event * drainEvent; - - typedef range_map::iterator PortIter; - range_map portMap; - - AddrRangeList defaultRange; - - std::vector snoopPorts; - - /** - * Store the outstanding requests so we can determine which ones - * we generated and which ones were merely forwarded. This is used - * in the coherent bus when coherency responses come back. - */ - std::set outstandingReq; - - /** Function called by the port when the bus is recieving a Timing - transaction.*/ - bool recvTiming(PacketPtr pkt); + const uint32_t width; - /** Function called by the port when the bus is recieving a timing - snoop transaction.*/ - bool recvTimingSnoop(PacketPtr pkt); + typedef AddrRangeMap::iterator PortMapIter; + typedef AddrRangeMap::const_iterator PortMapConstIter; + AddrRangeMap portMap; - /** - * Forward a timing packet to our snoopers, potentially excluding - * one of the connected coherent masters to avoid sending a packet - * back to where it came from. - * - * @param pkt Packet to forward - * @param exclude_slave_port_id Id of slave port to exclude - */ - void forwardTiming(PacketPtr pkt, Port::PortId exclude_slave_port_id); - - /** - * Determine if the bus is to be considered occupied when being - * presented with a packet from a specific port. If so, the port - * in question is also added to the retry list. - * - * @param pkt Incoming packet - * @param port Source port on the bus presenting the packet - * - * @return True if the bus is to be considered occupied - */ - bool isOccupied(PacketPtr pkt, Port* port); - - /** - * Deal with a destination port accepting a packet by potentially - * removing the source port from the retry list (if retrying) and - * occupying the bus accordingly. - * - * @param busy_time Time to spend as a result of a successful send - */ - void succeededTiming(Tick busy_time); - - /** Function called by the port when the bus is recieving a Atomic - transaction.*/ - Tick recvAtomic(PacketPtr pkt); + /** all contigous ranges seen by this bus */ + AddrRangeList busRanges; - /** Function called by the port when the bus is recieving an - atomic snoop transaction.*/ - Tick recvAtomicSnoop(PacketPtr pkt); + AddrRange defaultRange; /** - * Forward an atomic packet to our snoopers, potentially excluding - * one of the connected coherent masters to avoid sending a packet - * back to where it came from. - * - * @param pkt Packet to forward - * @param exclude_slave_port_id Id of slave port to exclude + * Function called by the port when the bus is recieving a range change. * - * @return a pair containing the snoop response and snoop latency + * @param master_port_id id of the port that received the change */ - std::pair forwardAtomic(PacketPtr pkt, - Port::PortId exclude_slave_port_id); - - /** Function called by the port when the bus is recieving a Functional - transaction.*/ - void recvFunctional(PacketPtr pkt); - - /** Function called by the port when the bus is recieving a functional - snoop transaction.*/ - void recvFunctionalSnoop(PacketPtr pkt); - - /** - * Forward a functional packet to our snoopers, potentially - * excluding one of the connected coherent masters to avoid - * sending a packet back to where it came from. - * - * @param pkt Packet to forward - * @param exclude_slave_port_id Id of slave port to exclude - */ - void forwardFunctional(PacketPtr pkt, Port::PortId exclude_slave_port_id); - - /** Timing function called by port when it is once again able to process - * requests. */ - void recvRetry(Port::PortId id); - - /** Function called by the port when the bus is recieving a range change.*/ - void recvRangeChange(Port::PortId id); + void recvRangeChange(PortID master_port_id); /** Find which port connected to this bus (if any) should be given a packet * with this address. * @param addr Address to find port for. * @return id of port that the packet should be sent out of. */ - int findPort(Addr addr); + PortID findPort(Addr addr); // Cache for the findPort function storing recently used ports from portMap struct PortCache { bool valid; - Port::PortId id; - Addr start; - Addr end; + PortID id; + AddrRange range; }; PortCache portCache[3]; // Checks the cache and returns the id of the port that has the requested // address within its range - inline int checkPortCache(Addr addr) { - if (portCache[0].valid && addr >= portCache[0].start && - addr < portCache[0].end) { + inline PortID checkPortCache(Addr addr) const { + if (portCache[0].valid && portCache[0].range.contains(addr)) { return portCache[0].id; } - if (portCache[1].valid && addr >= portCache[1].start && - addr < portCache[1].end) { + if (portCache[1].valid && portCache[1].range.contains(addr)) { return portCache[1].id; } - if (portCache[2].valid && addr >= portCache[2].start && - addr < portCache[2].end) { + if (portCache[2].valid && portCache[2].range.contains(addr)) { return portCache[2].id; } - return Port::INVALID_PORT_ID; + return InvalidPortID; } // Clears the earliest entry of the cache and inserts a new port entry - inline void updatePortCache(short id, Addr start, Addr end) { + inline void updatePortCache(short id, const AddrRange& range) { portCache[2].valid = portCache[1].valid; portCache[2].id = portCache[1].id; - portCache[2].start = portCache[1].start; - portCache[2].end = portCache[1].end; + portCache[2].range = portCache[1].range; portCache[1].valid = portCache[0].valid; portCache[1].id = portCache[0].id; - portCache[1].start = portCache[0].start; - portCache[1].end = portCache[0].end; + portCache[1].range = portCache[0].range; portCache[0].valid = true; portCache[0].id = id; - portCache[0].start = start; - portCache[0].end = end; + portCache[0].range = range; } // Clears the cache. Needs to be called in constructor. @@ -374,113 +299,75 @@ class Bus : public MemObject } /** - * Return the address ranges this port is responsible for. - * - * @param id id of the bus port that made the request + * Return the address ranges the bus is responsible for. * * @return a list of non-overlapping address ranges */ - AddrRangeList getAddrRanges(Port::PortId id); + AddrRangeList getAddrRanges() const; /** - * Determine if the bus port is snooping or not. - * - * @param id id of the bus port that made the request - * - * @return a boolean indicating if this port is snooping or not + * Calculate the timing parameters for the packet. Updates the + * busFirstWordDelay and busLastWordDelay fields of the packet + * object with the relative number of ticks required to transmit + * the header and the first word, and the last word, respectively. */ - bool isSnooping(Port::PortId id) const; - - /** Calculate the timing parameters for the packet. Updates the - * firstWordTime and finishTime fields of the packet object. - * Returns the tick at which the packet header is completed (which - * will be all that is sent if the target rejects the packet). - */ - Tick calcPacketTiming(PacketPtr pkt); - - /** Occupy the bus until until */ - void occupyBus(Tick until); + void calcPacketTiming(PacketPtr pkt); /** - * Release the bus after being occupied and return to an idle - * state where we proceed to send a retry to any potential waiting - * port, or drain if asked to do so. + * Ask everyone on the bus what their size is and determine the + * bus size as either the maximum, or if no device specifies a + * block size return the default. + * + * @return the max of all the sizes or the default if none is set */ - void releaseBus(); + unsigned deviceBlockSize() const; /** - * Send a retry to the port at the head of the retryList. The - * caller must ensure that the list is not empty. - */ - void retryWaiting(); - - /** Ask everyone on the bus what their size is - * @param id id of the busport that made the request - * @return the max of all the sizes + * Remember for each of the master ports of the bus if we got an + * address range from the connected slave. For convenience, also + * keep track of if we got ranges from all the slave modules or + * not. */ - unsigned findBlockSize(Port::PortId id); - - // event used to schedule a release of the bus - EventWrapper busIdleEvent; - - bool inRetry; - std::set inRecvRangeChange; + std::vector gotAddrRanges; + bool gotAllAddrRanges; /** The master and slave ports of the bus */ std::vector slavePorts; std::vector masterPorts; + /** Convenience typedefs. */ typedef std::vector::iterator SlavePortIter; + typedef std::vector::iterator MasterPortIter; typedef std::vector::const_iterator SlavePortConstIter; - - /** An array of pointers to ports that retry should be called on because the - * original send failed for whatever reason.*/ - std::list retryList; - - void addToRetryList(Port* port) - { - if (!inRetry) { - // The device wasn't retrying a packet, or wasn't at an - // appropriate time. - retryList.push_back(port); - } else { - if (!retryList.empty() && port == retryList.front()) { - // The device was retrying a packet. It didn't work, - // so we'll leave it at the head of the retry list. - inRetry = false; - } else { - // We are in retry, but not for this port, put it at - // the end. - retryList.push_back(port); - } - } - } + typedef std::vector::const_iterator MasterPortConstIter; /** Port that handles requests that don't match any of the interfaces.*/ - short defaultPortId; + PortID defaultPortID; /** If true, use address range provided by default device. Any address not handled by another port and not in default device's range will cause a fatal error. If false, just send all addresses not handled by another port to default device. */ - bool useDefaultRange; + const bool useDefaultRange; - unsigned defaultBlockSize; - unsigned cachedBlockSize; - bool cachedBlockSizeValid; + uint32_t blockSize; - public: + BaseBus(const BaseBusParams *p); - /** A function used to return the port associated with this bus object. */ - virtual MasterPort& getMasterPort(const std::string& if_name, int idx = -1); - virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1); + virtual ~BaseBus(); + + public: virtual void init(); - virtual void startup(); - unsigned int drain(Event *de); + /** A function used to return the port associated with this bus object. */ + BaseMasterPort& getMasterPort(const std::string& if_name, + PortID idx = InvalidPortID); + BaseSlavePort& getSlavePort(const std::string& if_name, + PortID idx = InvalidPortID); + + virtual unsigned int drain(DrainManager *dm) = 0; - Bus(const BusParams *p); }; #endif //__MEM_BUS_HH__