X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmem%2Fbus.hh;h=705a3a999cd2a9d6df943b49cd8abd717a4db14d;hb=8573a69d8f7bf7b3f074e3e0ac64994801c551be;hp=06ccd4ac0c844220f8a8f7101515582be604e6f3;hpb=f20ba92a76c97d520b625d9ff6a962df1a875f23;p=gem5.git diff --git a/src/mem/bus.hh b/src/mem/bus.hh index 06ccd4ac0..705a3a999 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011-2013 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. * @@ -27,250 +39,334 @@ * * 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 -#include "base/range.hh" -#include "base/hashmap.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 "mem/request.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 - { - bool _onRetryList; - /** 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), _onRetryList(false), bus(_bus), id(_id) - { } - - bool onRetryList() - { return _onRetryList; } - - void onRetryList(bool newVal) - { _onRetryList = newVal; } - - int getId() { 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 status changefrom the peer port (at id), - pass it to the bus. */ - virtual void recvStatusChange(Status status) - { bus->recvStatusChange(status, 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 void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { bus->addressRanges(resp, snoop, 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 int deviceBlockSize() - { return bus->findBlockSize(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; } + + + /** + * 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); + + /** + * 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 + */ + 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); + + /** Occupy the bus layer until until */ + void occupyLayer(Tick until); + + /** + * Send a retry to the port at the head of the retryList. The + * caller must ensure that the list is not empty. + */ + void retryWaiting(); + + /** + * 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(); + + private: + + /** The bus this layer is a part of. */ + BaseBus& bus; + + /** A name for this layer. */ + std::string _name; + + /** + * 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 }; + + /** track the state of the bus layer */ + State state; + + /** manager to signal when drained */ + DrainManager *drainManager; + + /** + * 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; }; - class BusFreeEvent : public Event - { - Bus * bus; + /** cycles of overhead per transaction */ + const Cycles headerCycles; + /** the width of the bus in bytes */ + const uint32_t width; - public: - BusFreeEvent(Bus * _bus); - void process(); - const char *description(); - }; + typedef AddrRangeMap::iterator PortMapIter; + typedef AddrRangeMap::const_iterator PortMapConstIter; + AddrRangeMap portMap; - /** a globally unique id for this bus. */ - int busId; - /** the clock speed for the bus */ - int clock; - /** the width of the bus in bytes */ - int width; - /** the next tick at which the bus will be idle */ - Tick tickNextIdle; + /** all contigous ranges seen by this bus */ + AddrRangeList busRanges; - Event * drainEvent; + 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); - static const int defaultId = -3; //Make it unique from Broadcast + /** 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. + */ + PortID findPort(Addr addr); - typedef range_map::iterator PortIter; - range_map portMap; + // Cache for the findPort function storing recently used ports from portMap + struct PortCache { + bool valid; + PortID id; + AddrRange range; + }; - AddrRangeList defaultRange; + PortCache portCache[3]; - typedef std::vector::iterator SnoopIter; - std::vector snoopPorts; + // Checks the cache and returns the id of the port that has the requested + // address within its range + inline PortID checkPortCache(Addr addr) const { + if (portCache[0].valid && portCache[0].range.contains(addr)) { + return portCache[0].id; + } + if (portCache[1].valid && portCache[1].range.contains(addr)) { + return portCache[1].id; + } + if (portCache[2].valid && portCache[2].range.contains(addr)) { + return portCache[2].id; + } - /** Function called by the port when the bus is recieving a Timing - transaction.*/ - bool recvTiming(PacketPtr pkt); + return InvalidPortID; + } - /** Function called by the port when the bus is recieving a Atomic - transaction.*/ - Tick recvAtomic(PacketPtr pkt); + // Clears the earliest entry of the cache and inserts a new port entry + inline void updatePortCache(short id, const AddrRange& range) { + portCache[2].valid = portCache[1].valid; + portCache[2].id = portCache[1].id; + portCache[2].range = portCache[1].range; - /** Function called by the port when the bus is recieving a Functional - transaction.*/ - void recvFunctional(PacketPtr pkt); + portCache[1].valid = portCache[0].valid; + portCache[1].id = portCache[0].id; + portCache[1].range = portCache[0].range; - /** Timing function called by port when it is once again able to process - * requests. */ - void recvRetry(int id); + portCache[0].valid = true; + portCache[0].id = id; + portCache[0].range = range; + } - /** Function called by the port when the bus is recieving a status change.*/ - void recvStatusChange(Port::Status status, int id); + // Clears the cache. Needs to be called in constructor. + inline void clearPortCache() { + portCache[2].valid = false; + portCache[1].valid = false; + portCache[0].valid = false; + } - /** 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. + /** + * Return the address ranges the bus is responsible for. + * + * @return a list of non-overlapping address ranges */ - int findPort(Addr addr); + AddrRangeList getAddrRanges() const; - /** Process address range request. - * @param resp addresses that we can respond to - * @param snoop addresses that we would like to snoop - * @param id ide of the busport that made the request. + /** + * 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. */ - void addressRanges(AddrRangeList &resp, bool &snoop, int id); - - /** Occupy the bus with transmitting the packet pkt */ - void occupyBus(PacketPtr pkt); - - /** 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 + void calcPacketTiming(PacketPtr pkt); + + /** + * 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 */ - int findBlockSize(int id); - - BusFreeEvent busIdle; - - bool inRetry; - - /** max number of bus ids we've handed out so far */ - short maxId; + unsigned deviceBlockSize() const; - /** An array of pointers to the peer port interfaces - connected to this bus.*/ - m5::hash_map interfaces; + /** + * 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. + */ + std::vector gotAddrRanges; + bool gotAllAddrRanges; - /** An array of pointers to ports that retry should be called on because the - * original send failed for whatever reason.*/ - std::list retryList; + /** The master and slave ports of the bus */ + std::vector slavePorts; + std::vector masterPorts; - void addToRetryList(BusPort * port) - { - if (!inRetry) { - // The device wasn't retrying a packet, or wasn't at an appropriate - // time. - assert(!port->onRetryList()); - port->onRetryList(true); - retryList.push_back(port); - } else { - if (port->onRetryList()) { - // The device was retrying a packet. It didn't work, so we'll leave - // it at the head of the retry list. - assert(port == retryList.front()); - inRetry = false; - } - else { - port->onRetryList(true); - 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.*/ - BusPort *defaultPort; + PortID defaultPortID; - BusPort *funcPort; - int funcPortId; + /** 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. */ + const bool useDefaultRange; - /** Has the user specified their own default responder? */ - bool responderSet; + uint32_t blockSize; - int defaultBlockSize; - int cachedBlockSize; - bool cachedBlockSizeValid; + BaseBus(const BaseBusParams *p); - public: + virtual ~BaseBus(); - /** A function used to return the port associated with this bus object. */ - virtual Port *getPort(const std::string &if_name, int idx = -1); - virtual void deletePortRefs(Port *p); + 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); - Bus(const std::string &n, int bus_id, int _clock, int _width, - bool responder_set, int dflt_blk_size) - : MemObject(n), busId(bus_id), clock(_clock), width(_width), - tickNextIdle(0), drainEvent(NULL), busIdle(this), inRetry(false), - maxId(0), defaultPort(NULL), funcPort(NULL), funcPortId(-4), - responderSet(responder_set), defaultBlockSize(dflt_blk_size), - cachedBlockSize(0), cachedBlockSizeValid(false) - { - //Both the width and clock period must be positive - if (width <= 0) - fatal("Bus width must be positive\n"); - if (clock <= 0) - fatal("Bus clock period must be positive\n"); - } + virtual unsigned int drain(DrainManager *dm) = 0; };