coherent_xbar.hh revision 9032
1/*
2 * Copyright (c) 2011-2012 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2002-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Ron Dreslinski
41 *          Ali Saidi
42 *          Andreas Hansson
43 *          William Wang
44 */
45
46/**
47 * @file
48 * Declaration of a bus object.
49 */
50
51#ifndef __MEM_BUS_HH__
52#define __MEM_BUS_HH__
53
54#include <list>
55#include <set>
56#include <string>
57
58#include "base/range.hh"
59#include "base/range_map.hh"
60#include "base/types.hh"
61#include "mem/mem_object.hh"
62#include "mem/packet.hh"
63#include "mem/port.hh"
64#include "params/Bus.hh"
65#include "sim/eventq.hh"
66
67class Bus : public MemObject
68{
69
70    /**
71     * Declaration of the bus slave port type, one will be
72     * instantiated for each of the master interfaces connecting to
73     * the bus.
74     */
75    class BusSlavePort : public SlavePort
76    {
77      private:
78        /** A pointer to the bus to which this port belongs. */
79        Bus *bus;
80
81      public:
82
83        /** Constructor for the BusSlavePort.*/
84        BusSlavePort(const std::string &_name, Bus *_bus, PortID _id)
85            : SlavePort(_name, _bus, _id), bus(_bus)
86        { }
87
88      protected:
89
90        /**
91         * When receiving a timing request, pass it to the bus.
92         */
93        virtual bool recvTimingReq(PacketPtr pkt)
94        { return bus->recvTimingReq(pkt, id); }
95
96        /**
97         * When receiving a timing snoop response, pass it to the bus.
98         */
99        virtual bool recvTimingSnoopResp(PacketPtr pkt)
100        { return bus->recvTimingSnoopResp(pkt, id); }
101
102        /**
103         * When receiving an atomic request, pass it to the bus.
104         */
105        virtual Tick recvAtomic(PacketPtr pkt)
106        { return bus->recvAtomic(pkt, id); }
107
108        /**
109         * When receiving a functional request, pass it to the bus.
110         */
111        virtual void recvFunctional(PacketPtr pkt)
112        { bus->recvFunctional(pkt, id); }
113
114        /**
115         * When receiving a retry, pass it to the bus.
116         */
117        virtual void recvRetry()
118        { panic("Bus slave ports always succeed and should never retry.\n"); }
119
120        // This should return all the 'owned' addresses that are
121        // downstream from this bus, yes?  That is, the union of all
122        // the 'owned' address ranges of all the other interfaces on
123        // this bus...
124        virtual AddrRangeList getAddrRanges()
125        { return bus->getAddrRanges(); }
126
127        // Ask the bus to ask everyone on the bus what their block size is and
128        // take the max of it. This might need to be changed a bit if we ever
129        // support multiple block sizes.
130        virtual unsigned deviceBlockSize() const
131        { return bus->findBlockSize(); }
132
133    };
134
135    /**
136     * Declaration of the bus master port type, one will be
137     * instantiated for each of the slave interfaces connecting to the
138     * bus.
139     */
140    class BusMasterPort : public MasterPort
141    {
142      private:
143        /** A pointer to the bus to which this port belongs. */
144        Bus *bus;
145
146      public:
147
148        /** Constructor for the BusMasterPort.*/
149        BusMasterPort(const std::string &_name, Bus *_bus, PortID _id)
150            : MasterPort(_name, _bus, _id), bus(_bus)
151        { }
152
153        /**
154         * Determine if this port should be considered a snooper. This
155         * is determined by the bus.
156         *
157         * @return a boolean that is true if this port is snooping
158         */
159        virtual bool isSnooping() const
160        { return bus->isSnooping(); }
161
162      protected:
163
164        /**
165         * When receiving a timing response, pass it to the bus.
166         */
167        virtual bool recvTimingResp(PacketPtr pkt)
168        { return bus->recvTimingResp(pkt, id); }
169
170        /**
171         * When receiving a timing snoop request, pass it to the bus.
172         */
173        virtual void recvTimingSnoopReq(PacketPtr pkt)
174        { return bus->recvTimingSnoopReq(pkt, id); }
175
176        /**
177         * When receiving an atomic snoop request, pass it to the bus.
178         */
179        virtual Tick recvAtomicSnoop(PacketPtr pkt)
180        { return bus->recvAtomicSnoop(pkt, id); }
181
182        /**
183         * When receiving a functional snoop request, pass it to the bus.
184         */
185        virtual void recvFunctionalSnoop(PacketPtr pkt)
186        { bus->recvFunctionalSnoop(pkt, id); }
187
188        /** When reciving a range change from the peer port (at id),
189            pass it to the bus. */
190        virtual void recvRangeChange()
191        { bus->recvRangeChange(id); }
192
193        /** When reciving a retry from the peer port (at id),
194            pass it to the bus. */
195        virtual void recvRetry()
196        { bus->recvRetry(); }
197
198        // Ask the bus to ask everyone on the bus what their block size is and
199        // take the max of it. This might need to be changed a bit if we ever
200        // support multiple block sizes.
201        virtual unsigned deviceBlockSize() const
202        { return bus->findBlockSize(); }
203
204    };
205
206    /** the clock speed for the bus */
207    int clock;
208    /** cycles of overhead per transaction */
209    int headerCycles;
210    /** the width of the bus in bytes */
211    int width;
212    /** the next tick at which the bus will be idle */
213    Tick tickNextIdle;
214
215    Event * drainEvent;
216
217    typedef range_map<Addr, PortID>::iterator PortIter;
218    range_map<Addr, PortID> portMap;
219
220    AddrRangeList defaultRange;
221
222    std::vector<SlavePort*> snoopPorts;
223
224    /**
225     * Store the outstanding requests so we can determine which ones
226     * we generated and which ones were merely forwarded. This is used
227     * in the coherent bus when coherency responses come back.
228     */
229    std::set<RequestPtr> outstandingReq;
230
231    /** Function called by the port when the bus is recieving a Timing
232      request packet.*/
233    bool recvTimingReq(PacketPtr pkt, PortID slave_port_id);
234
235    /** Function called by the port when the bus is recieving a Timing
236      response packet.*/
237    bool recvTimingResp(PacketPtr pkt, PortID master_port_id);
238
239    /** Function called by the port when the bus is recieving a timing
240        snoop request.*/
241    void recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id);
242
243    /** Function called by the port when the bus is recieving a timing
244        snoop response.*/
245    bool recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id);
246
247    /**
248     * Forward a timing packet to our snoopers, potentially excluding
249     * one of the connected coherent masters to avoid sending a packet
250     * back to where it came from.
251     *
252     * @param pkt Packet to forward
253     * @param exclude_slave_port_id Id of slave port to exclude
254     */
255    void forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id);
256
257    /**
258     * Determine if the bus is to be considered occupied when being
259     * presented with a packet from a specific port. If so, the port
260     * in question is also added to the retry list.
261     *
262     * @param pkt Incoming packet
263     * @param port Source port on the bus presenting the packet
264     *
265     * @return True if the bus is to be considered occupied
266     */
267    bool isOccupied(PacketPtr pkt, Port* port);
268
269    /**
270     * Deal with a destination port accepting a packet by potentially
271     * removing the source port from the retry list (if retrying) and
272     * occupying the bus accordingly.
273     *
274     * @param busy_time Time to spend as a result of a successful send
275     */
276    void succeededTiming(Tick busy_time);
277
278    /** Function called by the port when the bus is recieving a Atomic
279      transaction.*/
280    Tick recvAtomic(PacketPtr pkt, PortID slave_port_id);
281
282    /** Function called by the port when the bus is recieving an
283        atomic snoop transaction.*/
284    Tick recvAtomicSnoop(PacketPtr pkt, PortID master_port_id);
285
286    /**
287     * Forward an atomic packet to our snoopers, potentially excluding
288     * one of the connected coherent masters to avoid sending a packet
289     * back to where it came from.
290     *
291     * @param pkt Packet to forward
292     * @param exclude_slave_port_id Id of slave port to exclude
293     *
294     * @return a pair containing the snoop response and snoop latency
295     */
296    std::pair<MemCmd, Tick> forwardAtomic(PacketPtr pkt,
297                                          PortID exclude_slave_port_id);
298
299    /** Function called by the port when the bus is recieving a Functional
300        transaction.*/
301    void recvFunctional(PacketPtr pkt, PortID slave_port_id);
302
303    /** Function called by the port when the bus is recieving a functional
304        snoop transaction.*/
305    void recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id);
306
307    /**
308     * Forward a functional packet to our snoopers, potentially
309     * excluding one of the connected coherent masters to avoid
310     * sending a packet back to where it came from.
311     *
312     * @param pkt Packet to forward
313     * @param exclude_slave_port_id Id of slave port to exclude
314     */
315    void forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id);
316
317    /** Timing function called by port when it is once again able to process
318     * requests. */
319    void recvRetry();
320
321    /**
322     * Function called by the port when the bus is recieving a range change.
323     *
324     * @param master_port_id id of the port that received the change
325     */
326    void recvRangeChange(PortID master_port_id);
327
328    /** Find which port connected to this bus (if any) should be given a packet
329     * with this address.
330     * @param addr Address to find port for.
331     * @return id of port that the packet should be sent out of.
332     */
333    PortID findPort(Addr addr);
334
335    // Cache for the findPort function storing recently used ports from portMap
336    struct PortCache {
337        bool valid;
338        PortID id;
339        Addr start;
340        Addr end;
341    };
342
343    PortCache portCache[3];
344
345    // Checks the cache and returns the id of the port that has the requested
346    // address within its range
347    inline PortID checkPortCache(Addr addr) {
348        if (portCache[0].valid && addr >= portCache[0].start &&
349            addr < portCache[0].end) {
350            return portCache[0].id;
351        }
352        if (portCache[1].valid && addr >= portCache[1].start &&
353                   addr < portCache[1].end) {
354            return portCache[1].id;
355        }
356        if (portCache[2].valid && addr >= portCache[2].start &&
357            addr < portCache[2].end) {
358            return portCache[2].id;
359        }
360
361        return InvalidPortID;
362    }
363
364    // Clears the earliest entry of the cache and inserts a new port entry
365    inline void updatePortCache(short id, Addr start, Addr end) {
366        portCache[2].valid = portCache[1].valid;
367        portCache[2].id    = portCache[1].id;
368        portCache[2].start = portCache[1].start;
369        portCache[2].end   = portCache[1].end;
370
371        portCache[1].valid = portCache[0].valid;
372        portCache[1].id    = portCache[0].id;
373        portCache[1].start = portCache[0].start;
374        portCache[1].end   = portCache[0].end;
375
376        portCache[0].valid = true;
377        portCache[0].id    = id;
378        portCache[0].start = start;
379        portCache[0].end   = end;
380    }
381
382    // Clears the cache. Needs to be called in constructor.
383    inline void clearPortCache() {
384        portCache[2].valid = false;
385        portCache[1].valid = false;
386        portCache[0].valid = false;
387    }
388
389    /**
390     * Return the address ranges the bus is responsible for.
391     *
392     * @return a list of non-overlapping address ranges
393     */
394    AddrRangeList getAddrRanges();
395
396    /**
397     * Determine if the bus port is snooping or not.
398     *
399     * @return a boolean indicating if this port is snooping or not
400     */
401    bool isSnooping() const;
402
403    /** Calculate the timing parameters for the packet.  Updates the
404     * firstWordTime and finishTime fields of the packet object.
405     * Returns the tick at which the packet header is completed (which
406     * will be all that is sent if the target rejects the packet).
407     */
408    Tick calcPacketTiming(PacketPtr pkt);
409
410    /** Occupy the bus until until */
411    void occupyBus(Tick until);
412
413    /**
414     * Release the bus after being occupied and return to an idle
415     * state where we proceed to send a retry to any potential waiting
416     * port, or drain if asked to do so.
417     */
418    void releaseBus();
419
420    /**
421     * Send a retry to the port at the head of the retryList. The
422     * caller must ensure that the list is not empty.
423     */
424    void retryWaiting();
425
426    /**
427     * Ask everyone on the bus what their size is
428     *
429     * @return the max of all the sizes
430     */
431    unsigned findBlockSize();
432
433    // event used to schedule a release of the bus
434    EventWrapper<Bus, &Bus::releaseBus> busIdleEvent;
435
436    bool inRetry;
437    std::set<PortID> inRecvRangeChange;
438
439    /** The master and slave ports of the bus */
440    std::vector<SlavePort*> slavePorts;
441    std::vector<MasterPort*> masterPorts;
442
443    typedef std::vector<SlavePort*>::iterator SlavePortIter;
444    typedef std::vector<SlavePort*>::const_iterator SlavePortConstIter;
445
446    /** An array of pointers to ports that retry should be called on because the
447     * original send failed for whatever reason.*/
448    std::list<Port*> retryList;
449
450    void addToRetryList(Port* port)
451    {
452        if (!inRetry) {
453            // The device wasn't retrying a packet, or wasn't at an
454            // appropriate time.
455            retryList.push_back(port);
456        } else {
457            if (!retryList.empty() && port == retryList.front()) {
458                // The device was retrying a packet. It didn't work,
459                // so we'll leave it at the head of the retry list.
460                inRetry = false;
461            } else {
462                // We are in retry, but not for this port, put it at
463                // the end.
464                retryList.push_back(port);
465            }
466        }
467    }
468
469    /** Port that handles requests that don't match any of the interfaces.*/
470    PortID defaultPortID;
471
472    /** If true, use address range provided by default device.  Any
473       address not handled by another port and not in default device's
474       range will cause a fatal error.  If false, just send all
475       addresses not handled by another port to default device. */
476    bool useDefaultRange;
477
478    unsigned defaultBlockSize;
479    unsigned cachedBlockSize;
480    bool cachedBlockSizeValid;
481
482  public:
483
484    /** A function used to return the port associated with this bus object. */
485    virtual MasterPort& getMasterPort(const std::string& if_name, int idx = -1);
486    virtual SlavePort& getSlavePort(const std::string& if_name, int idx = -1);
487
488    virtual void init();
489    virtual void startup();
490
491    unsigned int drain(Event *de);
492
493    Bus(const BusParams *p);
494};
495
496#endif //__MEM_BUS_HH__
497