X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmem%2Fbus.hh;h=705a3a999cd2a9d6df943b49cd8abd717a4db14d;hb=8573a69d8f7bf7b3f074e3e0ac64994801c551be;hp=4ea92308a64debdf7d566a054441bf2fc93c20a9;hpb=9727b1be189ab9990d4cb6783c16a7dae1b43c97;p=gem5.git diff --git a/src/mem/bus.hh b/src/mem/bus.hh index 4ea92308a..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 @@ -40,191 +40,255 @@ * Authors: Ron Dreslinski * Ali Saidi * Andreas Hansson + * William Wang */ /** * @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 { - /** Declaration of the buses port type, one will be instantiated for each - of the interfaces connecting to the bus. */ - class BusPort : public Port - { - /** 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. */ - int id; + protected: + + /** + * 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. + */ + template + class Layer : public Drainable + { public: - /** Constructor for the BusPort.*/ - BusPort(const std::string &_name, Bus *_bus, int _id) - : Port(_name, _bus), bus(_bus), id(_id) - { } + /** + * 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 + */ + Layer(BaseBus& _bus, const std::string& _name); + + /** + * 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 + */ + unsigned int drain(DrainManager *dm); + + /** + * Get the bus layer's name + */ + const std::string name() const { return bus.name() + _name; } + - int getId() const { return id; } + /** + * 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 + */ + bool tryTiming(PortClass* port); /** - * Determine if this port should be considered a snooper. This - * is determined by 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. * - * @return a boolean that is true if this port is snooping + * @param busy_time Time to spend as a result of a successful send */ - virtual bool isSnooping() - { return bus->isSnooping(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 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); } - - // 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); } + void succeededTiming(Tick busy_time); - }; + /** + * 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. + * + * @param busy_time Time to spend as a result of a failed send + */ + void failedTiming(PortClass* port, Tick busy_time); - /** the clock speed for the bus */ - int clock; - /** cycles of overhead per transaction */ - int headerCycles; - /** the width of the bus in bytes */ - int width; - /** the next tick at which the bus will be idle */ - Tick tickNextIdle; + /** Occupy the bus layer until until */ + void occupyLayer(Tick until); - Event * drainEvent; + /** + * Send a retry to the port at the head of the retryList. The + * caller must ensure that the list is not empty. + */ + void retryWaiting(); - typedef range_map::iterator PortIter; - range_map portMap; + /** + * 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. + */ + void recvRetry(); - AddrRangeList defaultRange; + private: - typedef std::vector::iterator SnoopIter; - std::vector snoopPorts; + /** The bus this layer is a part of. */ + BaseBus& bus; - /** Function called by the port when the bus is recieving a Timing - transaction.*/ - bool recvTiming(PacketPtr pkt); + /** A name for this layer. */ + std::string _name; - /** Function called by the port when the bus is recieving a Atomic - transaction.*/ - Tick recvAtomic(PacketPtr pkt); + /** + * 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. + */ + enum State { IDLE, BUSY, RETRY }; - /** Function called by the port when the bus is recieving a Functional - transaction.*/ - void recvFunctional(PacketPtr pkt); + /** track the state of the bus layer */ + State state; - /** Timing function called by port when it is once again able to process - * requests. */ - void recvRetry(int id); + /** manager to signal when drained */ + DrainManager *drainManager; - /** Function called by the port when the bus is recieving a range change.*/ - void recvRangeChange(int id); + /** + * An array of ports that retry should be called + * on because the original send failed for whatever reason. + */ + std::deque retryList; + + /** + * 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(); + + /** event used to schedule a release of the layer */ + EventWrapper releaseEvent; + + }; + + /** cycles of overhead per transaction */ + const Cycles headerCycles; + /** the width of the bus in bytes */ + const uint32_t width; + + typedef AddrRangeMap::iterator PortMapIter; + typedef AddrRangeMap::const_iterator PortMapConstIter; + AddrRangeMap portMap; + + /** all contigous ranges seen by this bus */ + AddrRangeList busRanges; + + AddrRange defaultRange; + + /** + * Function called by the port when the bus is recieving a range change. + * + * @param master_port_id id of the port that received the change + */ + 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; - int 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 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. @@ -235,117 +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(int 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(int id); - - /** 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(int id); + std::vector gotAddrRanges; + bool gotAllAddrRanges; - // event used to schedule a release of the bus - EventWrapper busIdleEvent; + /** The master and slave ports of the bus */ + std::vector slavePorts; + std::vector masterPorts; - bool inRetry; - std::set inRecvRangeChange; - - // keep track of the number of master ports (not counting the - // default master) since we need this as an offset into the - // interfaces vector - unsigned int nbrMasterPorts; - - /** An ordered vector of pointers to the peer port interfaces - connected to this bus.*/ - std::vector interfaces; - - /** 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); - } - } - } + /** Convenience typedefs. */ + typedef std::vector::iterator SlavePortIter; + typedef std::vector::iterator MasterPortIter; + typedef std::vector::const_iterator SlavePortConstIter; + typedef std::vector::const_iterator MasterPortConstIter; /** Port that handles requests that don't match any of the interfaces.*/ - short defaultPortId; - - /** A symbolic name for a port id that denotes no port. */ - static const short INVALID_PORT_ID = -1; + 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 Port *getPort(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__