coherent_xbar.cc revision 9032
16166Ssteve.reinhardt@amd.com/*
26166Ssteve.reinhardt@amd.com * Copyright (c) 2011-2012 ARM Limited
36166Ssteve.reinhardt@amd.com * All rights reserved
46166Ssteve.reinhardt@amd.com *
56166Ssteve.reinhardt@amd.com * The license below extends only to copyright in the software and shall
66166Ssteve.reinhardt@amd.com * not be construed as granting a license to any other intellectual
76166Ssteve.reinhardt@amd.com * property including but not limited to intellectual property relating
86166Ssteve.reinhardt@amd.com * to a hardware implementation of the functionality of the software
96166Ssteve.reinhardt@amd.com * licensed hereunder.  You may use the software subject to the license
106166Ssteve.reinhardt@amd.com * terms below provided that you ensure that this notice is replicated
116166Ssteve.reinhardt@amd.com * unmodified and in its entirety in all distributions of the software,
126166Ssteve.reinhardt@amd.com * modified or unmodified, in source code or in binary form.
136166Ssteve.reinhardt@amd.com *
146166Ssteve.reinhardt@amd.com * Copyright (c) 2006 The Regents of The University of Michigan
156166Ssteve.reinhardt@amd.com * All rights reserved.
166166Ssteve.reinhardt@amd.com *
176166Ssteve.reinhardt@amd.com * Redistribution and use in source and binary forms, with or without
186166Ssteve.reinhardt@amd.com * modification, are permitted provided that the following conditions are
196166Ssteve.reinhardt@amd.com * met: redistributions of source code must retain the above copyright
206166Ssteve.reinhardt@amd.com * notice, this list of conditions and the following disclaimer;
216166Ssteve.reinhardt@amd.com * redistributions in binary form must reproduce the above copyright
226166Ssteve.reinhardt@amd.com * notice, this list of conditions and the following disclaimer in the
236166Ssteve.reinhardt@amd.com * documentation and/or other materials provided with the distribution;
246166Ssteve.reinhardt@amd.com * neither the name of the copyright holders nor the names of its
256166Ssteve.reinhardt@amd.com * contributors may be used to endorse or promote products derived from
266166Ssteve.reinhardt@amd.com * this software without specific prior written permission.
276166Ssteve.reinhardt@amd.com *
286166Ssteve.reinhardt@amd.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
296166Ssteve.reinhardt@amd.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
306166Ssteve.reinhardt@amd.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
316166Ssteve.reinhardt@amd.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
326166Ssteve.reinhardt@amd.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3313718Sandreas.sandberg@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
346166Ssteve.reinhardt@amd.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
356289Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
366870Sdrh5@cs.wisc.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
376289Snate@binkert.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
386166Ssteve.reinhardt@amd.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3910720Sandreas.hansson@arm.com *
409793Sakash.bagdia@arm.com * Authors: Ali Saidi
419793Sakash.bagdia@arm.com *          Andreas Hansson
429793Sakash.bagdia@arm.com *          William Wang
439793Sakash.bagdia@arm.com */
449793Sakash.bagdia@arm.com
459793Sakash.bagdia@arm.com/**
466166Ssteve.reinhardt@amd.com * @file
476166Ssteve.reinhardt@amd.com * Definition of a bus object.
488876Sandreas.hansson@arm.com */
498876Sandreas.hansson@arm.com
507876Sgblack@eecs.umich.edu#include "base/misc.hh"
519793Sakash.bagdia@arm.com#include "base/trace.hh"
529793Sakash.bagdia@arm.com#include "debug/Bus.hh"
536166Ssteve.reinhardt@amd.com#include "debug/BusAddrRanges.hh"
546166Ssteve.reinhardt@amd.com#include "mem/bus.hh"
558839Sandreas.hansson@arm.com
566166Ssteve.reinhardt@amd.comBus::Bus(const BusParams *p)
578732Sandreas.hansson@arm.com    : MemObject(p), clock(p->clock),
588839Sandreas.hansson@arm.com      headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
596166Ssteve.reinhardt@amd.com      drainEvent(NULL), busIdleEvent(this), inRetry(false),
606166Ssteve.reinhardt@amd.com      defaultPortID(InvalidPortID),
616166Ssteve.reinhardt@amd.com      useDefaultRange(p->use_default_range),
626166Ssteve.reinhardt@amd.com      defaultBlockSize(p->block_size),
636166Ssteve.reinhardt@amd.com      cachedBlockSize(0), cachedBlockSizeValid(false)
648801Sgblack@eecs.umich.edu{
656166Ssteve.reinhardt@amd.com    //width, clock period, and header cycles must be positive
66    if (width <= 0)
67        fatal("Bus width must be positive\n");
68    if (clock <= 0)
69        fatal("Bus clock period must be positive\n");
70    if (headerCycles <= 0)
71        fatal("Number of header cycles must be positive\n");
72
73    // create the ports based on the size of the master and slave
74    // vector ports, and the presence of the default port, the ports
75    // are enumerated starting from zero
76    for (int i = 0; i < p->port_master_connection_count; ++i) {
77        std::string portName = csprintf("%s-p%d", name(), i);
78        MasterPort* bp = new BusMasterPort(portName, this, i);
79        masterPorts.push_back(bp);
80    }
81
82    // see if we have a default slave device connected and if so add
83    // our corresponding master port
84    if (p->port_default_connection_count) {
85        defaultPortID = masterPorts.size();
86        std::string portName = csprintf("%s-default", name());
87        MasterPort* bp = new BusMasterPort(portName, this, defaultPortID);
88        masterPorts.push_back(bp);
89    }
90
91    // create the slave ports, once again starting at zero
92    for (int i = 0; i < p->port_slave_connection_count; ++i) {
93        std::string portName = csprintf("%s-p%d", name(), i);
94        SlavePort* bp = new BusSlavePort(portName, this, i);
95        slavePorts.push_back(bp);
96    }
97
98    clearPortCache();
99}
100
101MasterPort &
102Bus::getMasterPort(const std::string &if_name, int idx)
103{
104    if (if_name == "master" && idx < masterPorts.size()) {
105        // the master port index translates directly to the vector position
106        return *masterPorts[idx];
107    } else  if (if_name == "default") {
108        return *masterPorts[defaultPortID];
109    } else {
110        return MemObject::getMasterPort(if_name, idx);
111    }
112}
113
114SlavePort &
115Bus::getSlavePort(const std::string &if_name, int idx)
116{
117    if (if_name == "slave" && idx < slavePorts.size()) {
118        // the slave port index translates directly to the vector position
119        return *slavePorts[idx];
120    } else {
121        return MemObject::getSlavePort(if_name, idx);
122    }
123}
124
125void
126Bus::init()
127{
128    // iterate over our slave ports and determine which of our
129    // neighbouring master ports are snooping and add them as snoopers
130    for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
131         ++p) {
132        if ((*p)->getMasterPort().isSnooping()) {
133            DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n",
134                    (*p)->getMasterPort().name());
135            snoopPorts.push_back(*p);
136        }
137    }
138}
139
140Tick
141Bus::calcPacketTiming(PacketPtr pkt)
142{
143    // determine the current time rounded to the closest following
144    // clock edge
145    Tick now = curTick();
146    if (now % clock != 0) {
147        now = ((now / clock) + 1) * clock;
148    }
149
150    Tick headerTime = now + headerCycles * clock;
151
152    // The packet will be sent. Figure out how long it occupies the bus, and
153    // how much of that time is for the first "word", aka bus width.
154    int numCycles = 0;
155    if (pkt->hasData()) {
156        // If a packet has data, it needs ceil(size/width) cycles to send it
157        int dataSize = pkt->getSize();
158        numCycles += dataSize/width;
159        if (dataSize % width)
160            numCycles++;
161    }
162
163    // The first word will be delivered after the current tick, the delivery
164    // of the address if any, and one bus cycle to deliver the data
165    pkt->firstWordTime = headerTime + clock;
166
167    pkt->finishTime = headerTime + numCycles * clock;
168
169    return headerTime;
170}
171
172void Bus::occupyBus(Tick until)
173{
174    if (until == 0) {
175        // shortcut for express snoop packets
176        return;
177    }
178
179    tickNextIdle = until;
180    reschedule(busIdleEvent, tickNextIdle, true);
181
182    DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
183            curTick(), tickNextIdle);
184}
185
186bool
187Bus::isOccupied(PacketPtr pkt, Port* port)
188{
189    // first we see if the next idle tick is in the future, next the
190    // bus is considered occupied if there are ports on the retry list
191    // and we are not in a retry with the current port
192    if (tickNextIdle > curTick() ||
193        (!retryList.empty() && !(inRetry && port == retryList.front()))) {
194        addToRetryList(port);
195        return true;
196    }
197    return false;
198}
199
200bool
201Bus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
202{
203    // determine the source port based on the id
204    SlavePort *src_port = slavePorts[slave_port_id];
205
206    // test if the bus should be considered occupied for the current
207    // packet, and exclude express snoops from the check
208    if (!pkt->isExpressSnoop() && isOccupied(pkt, src_port)) {
209        DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x BUSY\n",
210                src_port->name(), pkt->cmdString(), pkt->getAddr());
211        return false;
212    }
213
214    DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x\n",
215            src_port->name(), pkt->cmdString(), pkt->getAddr());
216
217    // set the source port for routing of the response
218    pkt->setSrc(slave_port_id);
219
220    Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
221    Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
222
223    // uncacheable requests need never be snooped
224    if (!pkt->req->isUncacheable()) {
225        // the packet is a memory-mapped request and should be
226        // broadcasted to our snoopers but the source
227        forwardTiming(pkt, slave_port_id);
228    }
229
230    // remember if we add an outstanding req so we can undo it if
231    // necessary, if the packet needs a response, we should add it
232    // as outstanding and express snoops never fail so there is
233    // not need to worry about them
234    bool add_outstanding = !pkt->isExpressSnoop() && pkt->needsResponse();
235
236    // keep track that we have an outstanding request packet
237    // matching this request, this is used by the coherency
238    // mechanism in determining what to do with snoop responses
239    // (in recvTimingSnoop)
240    if (add_outstanding) {
241        // we should never have an exsiting request outstanding
242        assert(outstandingReq.find(pkt->req) == outstandingReq.end());
243        outstandingReq.insert(pkt->req);
244    }
245
246    // since it is a normal request, determine the destination
247    // based on the address and attempt to send the packet
248    bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
249
250    if (!success)  {
251        // inhibited packets should never be forced to retry
252        assert(!pkt->memInhibitAsserted());
253
254        // if it was added as outstanding and the send failed, then
255        // erase it again
256        if (add_outstanding)
257            outstandingReq.erase(pkt->req);
258
259        DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x RETRY\n",
260                src_port->name(), pkt->cmdString(), pkt->getAddr());
261
262        addToRetryList(src_port);
263        occupyBus(headerFinishTime);
264
265        return false;
266    }
267
268    succeededTiming(packetFinishTime);
269
270    return true;
271}
272
273bool
274Bus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
275{
276    // determine the source port based on the id
277    MasterPort *src_port = masterPorts[master_port_id];
278
279    // test if the bus should be considered occupied for the current
280    // packet
281    if (isOccupied(pkt, src_port)) {
282        DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x BUSY\n",
283                src_port->name(), pkt->cmdString(), pkt->getAddr());
284        return false;
285    }
286
287    DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x\n",
288            src_port->name(), pkt->cmdString(), pkt->getAddr());
289
290    calcPacketTiming(pkt);
291    Tick packetFinishTime = pkt->finishTime;
292
293    // the packet is a normal response to a request that we should
294    // have seen passing through the bus
295    assert(outstandingReq.find(pkt->req) != outstandingReq.end());
296
297    // remove it as outstanding
298    outstandingReq.erase(pkt->req);
299
300    // send the packet to the destination through one of our slave
301    // ports, as determined by the destination field
302    bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
303
304    // currently it is illegal to block responses... can lead to
305    // deadlock
306    assert(success);
307
308    succeededTiming(packetFinishTime);
309
310    return true;
311}
312
313void
314Bus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
315{
316    DPRINTF(Bus, "recvTimingSnoopReq: src %s %s 0x%x\n",
317            masterPorts[master_port_id]->name(), pkt->cmdString(),
318            pkt->getAddr());
319
320    // we should only see express snoops from caches
321    assert(pkt->isExpressSnoop());
322
323    // set the source port for routing of the response
324    pkt->setSrc(master_port_id);
325
326    // forward to all snoopers
327    forwardTiming(pkt, InvalidPortID);
328
329    // a snoop request came from a connected slave device (one of
330    // our master ports), and if it is not coming from the slave
331    // device responsible for the address range something is
332    // wrong, hence there is nothing further to do as the packet
333    // would be going back to where it came from
334    assert(master_port_id == findPort(pkt->getAddr()));
335
336    // this is an express snoop and is never forced to retry
337    assert(!inRetry);
338}
339
340bool
341Bus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
342{
343    // determine the source port based on the id
344    SlavePort* src_port = slavePorts[slave_port_id];
345
346    if (isOccupied(pkt, src_port)) {
347        DPRINTF(Bus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
348                src_port->name(), pkt->cmdString(), pkt->getAddr());
349        return false;
350    }
351
352    DPRINTF(Bus, "recvTimingSnoop: src %s %s 0x%x\n",
353            src_port->name(), pkt->cmdString(), pkt->getAddr());
354
355    // get the destination from the packet
356    PortID dest = pkt->getDest();
357
358    // responses are never express snoops
359    assert(!pkt->isExpressSnoop());
360
361    calcPacketTiming(pkt);
362    Tick packetFinishTime = pkt->finishTime;
363
364    // determine if the response is from a snoop request we
365    // created as the result of a normal request (in which case it
366    // should be in the outstandingReq), or if we merely forwarded
367    // someone else's snoop request
368    if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
369        // this is a snoop response to a snoop request we
370        // forwarded, e.g. coming from the L1 and going to the L2
371        // this should be forwarded as a snoop response
372        bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
373        assert(success);
374    } else {
375        // we got a snoop response on one of our slave ports,
376        // i.e. from a coherent master connected to the bus, and
377        // since we created the snoop request as part of
378        // recvTiming, this should now be a normal response again
379        outstandingReq.erase(pkt->req);
380
381        // this is a snoop response from a coherent master, with a
382        // destination field set on its way through the bus as
383        // request, hence it should never go back to where the
384        // snoop response came from, but instead to where the
385        // original request came from
386        assert(slave_port_id != dest);
387
388        // as a normal response, it should go back to a master
389        // through one of our slave ports
390        bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
391
392        // currently it is illegal to block responses... can lead
393        // to deadlock
394        assert(success);
395    }
396
397    succeededTiming(packetFinishTime);
398
399    return true;
400}
401
402
403void
404Bus::succeededTiming(Tick busy_time)
405{
406    // occupy the bus accordingly
407    occupyBus(busy_time);
408
409    // if a retrying port succeeded, also take it off the retry list
410    if (inRetry) {
411        DPRINTF(Bus, "Remove retry from list %s\n",
412                retryList.front()->name());
413        retryList.pop_front();
414        inRetry = false;
415    }
416}
417
418void
419Bus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
420{
421    for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
422        SlavePort *p = *s;
423        // we could have gotten this request from a snooping master
424        // (corresponding to our own slave port that is also in
425        // snoopPorts) and should not send it back to where it came
426        // from
427        if (exclude_slave_port_id == InvalidPortID ||
428            p->getId() != exclude_slave_port_id) {
429            // cache is not allowed to refuse snoop
430            p->sendTimingSnoopReq(pkt);
431        }
432    }
433}
434
435void
436Bus::releaseBus()
437{
438    // releasing the bus means we should now be idle
439    assert(curTick() >= tickNextIdle);
440
441    // bus is now idle, so if someone is waiting we can retry
442    if (!retryList.empty()) {
443        // note that we block (return false on recvTiming) both
444        // because the bus is busy and because the destination is
445        // busy, and in the latter case the bus may be released before
446        // we see a retry from the destination
447        retryWaiting();
448    }
449
450    //If we weren't able to drain before, we might be able to now.
451    if (drainEvent && retryList.empty() && curTick() >= tickNextIdle) {
452        drainEvent->process();
453        // Clear the drain event once we're done with it.
454        drainEvent = NULL;
455    }
456}
457
458void
459Bus::retryWaiting()
460{
461    // this should never be called with an empty retry list
462    assert(!retryList.empty());
463
464    // send a retry to the port at the head of the retry list
465    inRetry = true;
466
467    // note that we might have blocked on the receiving port being
468    // busy (rather than the bus itself) and now call retry before the
469    // destination called retry on the bus
470    retryList.front()->sendRetry();
471
472    // If inRetry is still true, sendTiming wasn't called in zero time
473    // (e.g. the cache does this)
474    if (inRetry) {
475        retryList.pop_front();
476        inRetry = false;
477
478        //Bring tickNextIdle up to the present
479        while (tickNextIdle < curTick())
480            tickNextIdle += clock;
481
482        //Burn a cycle for the missed grant.
483        tickNextIdle += clock;
484
485        reschedule(busIdleEvent, tickNextIdle, true);
486    }
487}
488
489void
490Bus::recvRetry()
491{
492    // we got a retry from a peer that we tried to send something to
493    // and failed, but we sent it on the account of someone else, and
494    // that source port should be on our retry list, however if the
495    // bus is released before this happens and the retry (from the bus
496    // point of view) is successful then this no longer holds and we
497    // could in fact have an empty retry list
498    if (retryList.empty())
499        return;
500
501    // if the bus isn't busy
502    if (curTick() >= tickNextIdle) {
503        // note that we do not care who told us to retry at the moment, we
504        // merely let the first one on the retry list go
505        retryWaiting();
506    }
507}
508
509PortID
510Bus::findPort(Addr addr)
511{
512    /* An interval tree would be a better way to do this. --ali. */
513    PortID dest_id = checkPortCache(addr);
514    if (dest_id != InvalidPortID)
515        return dest_id;
516
517    // Check normal port ranges
518    PortIter i = portMap.find(RangeSize(addr,1));
519    if (i != portMap.end()) {
520        dest_id = i->second;
521        updatePortCache(dest_id, i->first.start, i->first.end);
522        return dest_id;
523    }
524
525    // Check if this matches the default range
526    if (useDefaultRange) {
527        AddrRangeIter a_end = defaultRange.end();
528        for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) {
529            if (*i == addr) {
530                DPRINTF(Bus, "  found addr %#llx on default\n", addr);
531                return defaultPortID;
532            }
533        }
534    } else if (defaultPortID != InvalidPortID) {
535        DPRINTF(Bus, "Unable to find destination for addr %#llx, "
536                "will use default port\n", addr);
537        return defaultPortID;
538    }
539
540    // we should use the range for the default port and it did not
541    // match, or the default port is not set
542    fatal("Unable to find destination for addr %#llx on bus %s\n", addr,
543          name());
544}
545
546Tick
547Bus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
548{
549    DPRINTF(Bus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
550            slavePorts[slave_port_id]->name(), pkt->getAddr(),
551            pkt->cmdString());
552
553    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
554    Tick snoop_response_latency = 0;
555
556    // uncacheable requests need never be snooped
557    if (!pkt->req->isUncacheable()) {
558        // forward to all snoopers but the source
559        std::pair<MemCmd, Tick> snoop_result =
560            forwardAtomic(pkt, slave_port_id);
561        snoop_response_cmd = snoop_result.first;
562        snoop_response_latency = snoop_result.second;
563    }
564
565    // even if we had a snoop response, we must continue and also
566    // perform the actual request at the destination
567    PortID dest_id = findPort(pkt->getAddr());
568
569    // forward the request to the appropriate destination
570    Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
571
572    // if we got a response from a snooper, restore it here
573    if (snoop_response_cmd != MemCmd::InvalidCmd) {
574        // no one else should have responded
575        assert(!pkt->isResponse());
576        pkt->cmd = snoop_response_cmd;
577        response_latency = snoop_response_latency;
578    }
579
580    pkt->finishTime = curTick() + response_latency;
581    return response_latency;
582}
583
584Tick
585Bus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
586{
587    DPRINTF(Bus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
588            masterPorts[master_port_id]->name(), pkt->getAddr(),
589            pkt->cmdString());
590
591    // forward to all snoopers
592    std::pair<MemCmd, Tick> snoop_result =
593        forwardAtomic(pkt, InvalidPortID);
594    MemCmd snoop_response_cmd = snoop_result.first;
595    Tick snoop_response_latency = snoop_result.second;
596
597    if (snoop_response_cmd != MemCmd::InvalidCmd)
598        pkt->cmd = snoop_response_cmd;
599
600    pkt->finishTime = curTick() + snoop_response_latency;
601    return snoop_response_latency;
602}
603
604std::pair<MemCmd, Tick>
605Bus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id)
606{
607    // the packet may be changed on snoops, record the original
608    // command to enable us to restore it between snoops so that
609    // additional snoops can take place properly
610    MemCmd orig_cmd = pkt->cmd;
611    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
612    Tick snoop_response_latency = 0;
613
614    for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
615        SlavePort *p = *s;
616        // we could have gotten this request from a snooping master
617        // (corresponding to our own slave port that is also in
618        // snoopPorts) and should not send it back to where it came
619        // from
620        if (exclude_slave_port_id == InvalidPortID ||
621            p->getId() != exclude_slave_port_id) {
622            Tick latency = p->sendAtomicSnoop(pkt);
623            // in contrast to a functional access, we have to keep on
624            // going as all snoopers must be updated even if we get a
625            // response
626            if (pkt->isResponse()) {
627                // response from snoop agent
628                assert(pkt->cmd != orig_cmd);
629                assert(pkt->memInhibitAsserted());
630                // should only happen once
631                assert(snoop_response_cmd == MemCmd::InvalidCmd);
632                // save response state
633                snoop_response_cmd = pkt->cmd;
634                snoop_response_latency = latency;
635                // restore original packet state for remaining snoopers
636                pkt->cmd = orig_cmd;
637            }
638        }
639    }
640
641    // the packet is restored as part of the loop and any potential
642    // snoop response is part of the returned pair
643    return std::make_pair(snoop_response_cmd, snoop_response_latency);
644}
645
646void
647Bus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
648{
649    if (!pkt->isPrint()) {
650        // don't do DPRINTFs on PrintReq as it clutters up the output
651        DPRINTF(Bus,
652                "recvFunctional: packet src %s addr 0x%x cmd %s\n",
653                slavePorts[slave_port_id]->name(), pkt->getAddr(),
654                pkt->cmdString());
655    }
656
657    // uncacheable requests need never be snooped
658    if (!pkt->req->isUncacheable()) {
659        // forward to all snoopers but the source
660        forwardFunctional(pkt, slave_port_id);
661    }
662
663    // there is no need to continue if the snooping has found what we
664    // were looking for and the packet is already a response
665    if (!pkt->isResponse()) {
666        PortID dest_id = findPort(pkt->getAddr());
667
668        masterPorts[dest_id]->sendFunctional(pkt);
669    }
670}
671
672void
673Bus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
674{
675    if (!pkt->isPrint()) {
676        // don't do DPRINTFs on PrintReq as it clutters up the output
677        DPRINTF(Bus,
678                "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
679                masterPorts[master_port_id]->name(), pkt->getAddr(),
680                pkt->cmdString());
681    }
682
683    // forward to all snoopers
684    forwardFunctional(pkt, InvalidPortID);
685}
686
687void
688Bus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
689{
690    for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
691        SlavePort *p = *s;
692        // we could have gotten this request from a snooping master
693        // (corresponding to our own slave port that is also in
694        // snoopPorts) and should not send it back to where it came
695        // from
696        if (exclude_slave_port_id == InvalidPortID ||
697            p->getId() != exclude_slave_port_id)
698            p->sendFunctionalSnoop(pkt);
699
700        // if we get a response we are done
701        if (pkt->isResponse()) {
702            break;
703        }
704    }
705}
706
707/** Function called by the port when the bus is receiving a range change.*/
708void
709Bus::recvRangeChange(PortID master_port_id)
710{
711    AddrRangeList ranges;
712    AddrRangeIter iter;
713
714    if (inRecvRangeChange.count(master_port_id))
715        return;
716    inRecvRangeChange.insert(master_port_id);
717
718    DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n",
719            master_port_id);
720
721    clearPortCache();
722    if (master_port_id == defaultPortID) {
723        defaultRange.clear();
724        // Only try to update these ranges if the user set a default responder.
725        if (useDefaultRange) {
726            AddrRangeList ranges =
727                masterPorts[master_port_id]->getSlavePort().getAddrRanges();
728            for(iter = ranges.begin(); iter != ranges.end(); iter++) {
729                defaultRange.push_back(*iter);
730                DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n",
731                        iter->start, iter->end);
732            }
733        }
734    } else {
735
736        assert(master_port_id < masterPorts.size() && master_port_id >= 0);
737        MasterPort *port = masterPorts[master_port_id];
738
739        // Clean out any previously existent ids
740        for (PortIter portIter = portMap.begin();
741             portIter != portMap.end(); ) {
742            if (portIter->second == master_port_id)
743                portMap.erase(portIter++);
744            else
745                portIter++;
746        }
747
748        ranges = port->getSlavePort().getAddrRanges();
749
750        for (iter = ranges.begin(); iter != ranges.end(); iter++) {
751            DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n",
752                    iter->start, iter->end, master_port_id);
753            if (portMap.insert(*iter, master_port_id) == portMap.end()) {
754                PortID conflict_id = portMap.find(*iter)->second;
755                fatal("%s has two ports with same range:\n\t%s\n\t%s\n",
756                      name(),
757                      masterPorts[master_port_id]->getSlavePort().name(),
758                      masterPorts[conflict_id]->getSlavePort().name());
759            }
760        }
761    }
762    DPRINTF(BusAddrRanges, "port list has %d entries\n", portMap.size());
763
764    // tell all our neighbouring master ports that our address range
765    // has changed
766    for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
767         ++p)
768        (*p)->sendRangeChange();
769
770    inRecvRangeChange.erase(master_port_id);
771}
772
773AddrRangeList
774Bus::getAddrRanges()
775{
776    AddrRangeList ranges;
777
778    DPRINTF(BusAddrRanges, "received address range request, returning:\n");
779
780    for (AddrRangeIter dflt_iter = defaultRange.begin();
781         dflt_iter != defaultRange.end(); dflt_iter++) {
782        ranges.push_back(*dflt_iter);
783        DPRINTF(BusAddrRanges, "  -- Dflt: %#llx : %#llx\n",dflt_iter->start,
784                dflt_iter->end);
785    }
786    for (PortIter portIter = portMap.begin();
787         portIter != portMap.end(); portIter++) {
788        bool subset = false;
789        for (AddrRangeIter dflt_iter = defaultRange.begin();
790             dflt_iter != defaultRange.end(); dflt_iter++) {
791            if ((portIter->first.start < dflt_iter->start &&
792                portIter->first.end >= dflt_iter->start) ||
793               (portIter->first.start < dflt_iter->end &&
794                portIter->first.end >= dflt_iter->end))
795                fatal("Devices can not set ranges that itersect the default set\
796                        but are not a subset of the default set.\n");
797            if (portIter->first.start >= dflt_iter->start &&
798                portIter->first.end <= dflt_iter->end) {
799                subset = true;
800                DPRINTF(BusAddrRanges, "  -- %#llx : %#llx is a SUBSET\n",
801                    portIter->first.start, portIter->first.end);
802            }
803        }
804        if (!subset) {
805            ranges.push_back(portIter->first);
806            DPRINTF(BusAddrRanges, "  -- %#llx : %#llx\n",
807                    portIter->first.start, portIter->first.end);
808        }
809    }
810
811    return ranges;
812}
813
814bool
815Bus::isSnooping() const
816{
817    // in essence, answer the question if there are snooping ports
818    return !snoopPorts.empty();
819}
820
821unsigned
822Bus::findBlockSize()
823{
824    if (cachedBlockSizeValid)
825        return cachedBlockSize;
826
827    unsigned max_bs = 0;
828
829    PortIter p_end = portMap.end();
830    for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) {
831        unsigned tmp_bs = masterPorts[p_iter->second]->peerBlockSize();
832        if (tmp_bs > max_bs)
833            max_bs = tmp_bs;
834    }
835
836    for (SlavePortConstIter s = snoopPorts.begin(); s != snoopPorts.end();
837         ++s) {
838        unsigned tmp_bs = (*s)->peerBlockSize();
839        if (tmp_bs > max_bs)
840            max_bs = tmp_bs;
841    }
842    if (max_bs == 0)
843        max_bs = defaultBlockSize;
844
845    if (max_bs != 64)
846        warn_once("Blocksize found to not be 64... hmm... probably not.\n");
847    cachedBlockSize = max_bs;
848    cachedBlockSizeValid = true;
849    return max_bs;
850}
851
852
853unsigned int
854Bus::drain(Event * de)
855{
856    //We should check that we're not "doing" anything, and that noone is
857    //waiting. We might be idle but have someone waiting if the device we
858    //contacted for a retry didn't actually retry.
859    if (!retryList.empty() || (curTick() < tickNextIdle &&
860                               busIdleEvent.scheduled())) {
861        drainEvent = de;
862        return 1;
863    }
864    return 0;
865}
866
867void
868Bus::startup()
869{
870    if (tickNextIdle < curTick())
871        tickNextIdle = (curTick() / clock) * clock + clock;
872}
873
874Bus *
875BusParams::create()
876{
877    return new Bus(this);
878}
879