coherent_xbar.cc revision 5197
17405SAli.Saidi@ARM.com/*
27405SAli.Saidi@ARM.com * Copyright (c) 2006 The Regents of The University of Michigan
37405SAli.Saidi@ARM.com * All rights reserved.
47405SAli.Saidi@ARM.com *
57405SAli.Saidi@ARM.com * Redistribution and use in source and binary forms, with or without
67405SAli.Saidi@ARM.com * modification, are permitted provided that the following conditions are
77405SAli.Saidi@ARM.com * met: redistributions of source code must retain the above copyright
87405SAli.Saidi@ARM.com * notice, this list of conditions and the following disclaimer;
97405SAli.Saidi@ARM.com * redistributions in binary form must reproduce the above copyright
107405SAli.Saidi@ARM.com * notice, this list of conditions and the following disclaimer in the
117405SAli.Saidi@ARM.com * documentation and/or other materials provided with the distribution;
127405SAli.Saidi@ARM.com * neither the name of the copyright holders nor the names of its
137405SAli.Saidi@ARM.com * contributors may be used to endorse or promote products derived from
147405SAli.Saidi@ARM.com * this software without specific prior written permission.
157405SAli.Saidi@ARM.com *
167405SAli.Saidi@ARM.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
177405SAli.Saidi@ARM.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
187405SAli.Saidi@ARM.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
197405SAli.Saidi@ARM.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
207405SAli.Saidi@ARM.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
217405SAli.Saidi@ARM.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
227405SAli.Saidi@ARM.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
237405SAli.Saidi@ARM.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
247405SAli.Saidi@ARM.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
257405SAli.Saidi@ARM.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
267405SAli.Saidi@ARM.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
277405SAli.Saidi@ARM.com *
287405SAli.Saidi@ARM.com * Authors: Ali Saidi
297405SAli.Saidi@ARM.com */
307405SAli.Saidi@ARM.com
317405SAli.Saidi@ARM.com/**
327405SAli.Saidi@ARM.com * @file
337405SAli.Saidi@ARM.com * Definition of a bus object.
347405SAli.Saidi@ARM.com */
357405SAli.Saidi@ARM.com
367405SAli.Saidi@ARM.com#include <algorithm>
377405SAli.Saidi@ARM.com#include <limits>
387405SAli.Saidi@ARM.com
397405SAli.Saidi@ARM.com#include "base/misc.hh"
407405SAli.Saidi@ARM.com#include "base/trace.hh"
417405SAli.Saidi@ARM.com#include "mem/bus.hh"
427405SAli.Saidi@ARM.com
437405SAli.Saidi@ARM.comPort *
447405SAli.Saidi@ARM.comBus::getPort(const std::string &if_name, int idx)
457405SAli.Saidi@ARM.com{
467427Sgblack@eecs.umich.edu    if (if_name == "default") {
477427Sgblack@eecs.umich.edu        if (defaultPort == NULL) {
487427Sgblack@eecs.umich.edu            defaultPort = new BusPort(csprintf("%s-default",name()), this,
497427Sgblack@eecs.umich.edu                                      defaultId);
507427Sgblack@eecs.umich.edu            cachedBlockSizeValid = false;
517427Sgblack@eecs.umich.edu            return defaultPort;
527427Sgblack@eecs.umich.edu        } else
537427Sgblack@eecs.umich.edu            fatal("Default port already set\n");
547427Sgblack@eecs.umich.edu    }
557427Sgblack@eecs.umich.edu    int id;
567427Sgblack@eecs.umich.edu    if (if_name == "functional") {
577427Sgblack@eecs.umich.edu        if (!funcPort) {
587427Sgblack@eecs.umich.edu            id = maxId++;
597427Sgblack@eecs.umich.edu            funcPort = new BusPort(csprintf("%s-p%d-func", name(), id), this, id);
607427Sgblack@eecs.umich.edu            funcPortId = id;
617427Sgblack@eecs.umich.edu            interfaces[id] = funcPort;
627427Sgblack@eecs.umich.edu        }
637427Sgblack@eecs.umich.edu        return funcPort;
647427Sgblack@eecs.umich.edu    }
657427Sgblack@eecs.umich.edu
667427Sgblack@eecs.umich.edu    // if_name ignored?  forced to be empty?
677427Sgblack@eecs.umich.edu    id = maxId++;
687427Sgblack@eecs.umich.edu    assert(maxId < std::numeric_limits<typeof(maxId)>::max());
697427Sgblack@eecs.umich.edu    BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id);
707427Sgblack@eecs.umich.edu    interfaces[id] = bp;
717427Sgblack@eecs.umich.edu    cachedBlockSizeValid = false;
727427Sgblack@eecs.umich.edu    return bp;
737427Sgblack@eecs.umich.edu}
747427Sgblack@eecs.umich.edu
757427Sgblack@eecs.umich.eduvoid
767427Sgblack@eecs.umich.eduBus::deletePortRefs(Port *p)
777427Sgblack@eecs.umich.edu{
787427Sgblack@eecs.umich.edu
797427Sgblack@eecs.umich.edu    BusPort *bp =  dynamic_cast<BusPort*>(p);
807427Sgblack@eecs.umich.edu    if (bp == NULL)
817427Sgblack@eecs.umich.edu        panic("Couldn't convert Port* to BusPort*\n");
827427Sgblack@eecs.umich.edu    // If this is our one functional port
837427Sgblack@eecs.umich.edu    if (funcPort == bp)
847427Sgblack@eecs.umich.edu        return;
857427Sgblack@eecs.umich.edu    interfaces.erase(bp->getId());
867427Sgblack@eecs.umich.edu    clearBusCache();
877427Sgblack@eecs.umich.edu    delete bp;
887427Sgblack@eecs.umich.edu}
897427Sgblack@eecs.umich.edu
907427Sgblack@eecs.umich.edu/** Get the ranges of anyone other buses that we are connected to. */
917427Sgblack@eecs.umich.eduvoid
927427Sgblack@eecs.umich.eduBus::init()
937427Sgblack@eecs.umich.edu{
947427Sgblack@eecs.umich.edu    m5::hash_map<short,BusPort*>::iterator intIter;
957427Sgblack@eecs.umich.edu
967427Sgblack@eecs.umich.edu    for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
977427Sgblack@eecs.umich.edu        intIter->second->sendStatusChange(Port::RangeChange);
987427Sgblack@eecs.umich.edu}
997427Sgblack@eecs.umich.edu
1007427Sgblack@eecs.umich.eduBus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus)
1017427Sgblack@eecs.umich.edu{}
1027427Sgblack@eecs.umich.edu
1037427Sgblack@eecs.umich.eduvoid Bus::BusFreeEvent::process()
1047427Sgblack@eecs.umich.edu{
1057427Sgblack@eecs.umich.edu    bus->recvRetry(-1);
1067427Sgblack@eecs.umich.edu}
1077427Sgblack@eecs.umich.edu
1087427Sgblack@eecs.umich.educonst char * Bus::BusFreeEvent::description()
1097427Sgblack@eecs.umich.edu{
1107427Sgblack@eecs.umich.edu    return "bus became available";
1117427Sgblack@eecs.umich.edu}
1127427Sgblack@eecs.umich.edu
1137427Sgblack@eecs.umich.eduvoid Bus::occupyBus(PacketPtr pkt)
1147427Sgblack@eecs.umich.edu{
1157427Sgblack@eecs.umich.edu    //Bring tickNextIdle up to the present tick
1167427Sgblack@eecs.umich.edu    //There is some potential ambiguity where a cycle starts, which might make
1177427Sgblack@eecs.umich.edu    //a difference when devices are acting right around a cycle boundary. Using
1187427Sgblack@eecs.umich.edu    //a < allows things which happen exactly on a cycle boundary to take up
1197427Sgblack@eecs.umich.edu    //only the following cycle. Anything that happens later will have to "wait"
1207436Sdam.sunwoo@arm.com    //for the end of that cycle, and then start using the bus after that.
1217436Sdam.sunwoo@arm.com    if (tickNextIdle < curTick) {
1227436Sdam.sunwoo@arm.com        tickNextIdle = curTick;
1237436Sdam.sunwoo@arm.com        if (tickNextIdle % clock != 0)
1247436Sdam.sunwoo@arm.com            tickNextIdle = curTick - (curTick % clock) + clock;
1257436Sdam.sunwoo@arm.com    }
1267436Sdam.sunwoo@arm.com
1277436Sdam.sunwoo@arm.com    // The packet will be sent. Figure out how long it occupies the bus, and
1287436Sdam.sunwoo@arm.com    // how much of that time is for the first "word", aka bus width.
1297436Sdam.sunwoo@arm.com    int numCycles = 0;
1307436Sdam.sunwoo@arm.com    // Requests need one cycle to send an address
1317436Sdam.sunwoo@arm.com    if (pkt->isRequest())
1327436Sdam.sunwoo@arm.com        numCycles++;
1337436Sdam.sunwoo@arm.com    else if (pkt->isResponse() || pkt->hasData()) {
1347436Sdam.sunwoo@arm.com        // If a packet has data, it needs ceil(size/width) cycles to send it
1357436Sdam.sunwoo@arm.com        // We're using the "adding instead of dividing" trick again here
1367436Sdam.sunwoo@arm.com        if (pkt->hasData()) {
1377436Sdam.sunwoo@arm.com            int dataSize = pkt->getSize();
1387436Sdam.sunwoo@arm.com            numCycles += dataSize/width;
1397436Sdam.sunwoo@arm.com            if (dataSize % width)
1407436Sdam.sunwoo@arm.com                numCycles++;
1417436Sdam.sunwoo@arm.com        } else {
1427436Sdam.sunwoo@arm.com            // If the packet didn't have data, it must have been a response.
1437436Sdam.sunwoo@arm.com            // Those use the bus for one cycle to send their data.
1447436Sdam.sunwoo@arm.com            numCycles++;
1457436Sdam.sunwoo@arm.com        }
1467436Sdam.sunwoo@arm.com    }
1477436Sdam.sunwoo@arm.com
1487436Sdam.sunwoo@arm.com    // The first word will be delivered after the current tick, the delivery
1497436Sdam.sunwoo@arm.com    // of the address if any, and one bus cycle to deliver the data
1507436Sdam.sunwoo@arm.com    pkt->firstWordTime = tickNextIdle + (pkt->isRequest() ? clock : 0) + clock;
1517436Sdam.sunwoo@arm.com
1527427Sgblack@eecs.umich.edu    //Advance it numCycles bus cycles.
1537427Sgblack@eecs.umich.edu    //XXX Should this use the repeated addition trick as well?
1547427Sgblack@eecs.umich.edu    tickNextIdle += (numCycles * clock);
1557405SAli.Saidi@ARM.com    if (!busIdle.scheduled()) {
1567405SAli.Saidi@ARM.com        busIdle.schedule(tickNextIdle);
1577405SAli.Saidi@ARM.com    } else {
1587405SAli.Saidi@ARM.com        busIdle.reschedule(tickNextIdle);
1597405SAli.Saidi@ARM.com    }
1607405SAli.Saidi@ARM.com    DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
1617405SAli.Saidi@ARM.com            curTick, tickNextIdle);
1627405SAli.Saidi@ARM.com
1637405SAli.Saidi@ARM.com    // The bus will become idle once the current packet is delivered.
1647405SAli.Saidi@ARM.com    pkt->finishTime = tickNextIdle;
1657405SAli.Saidi@ARM.com}
1667405SAli.Saidi@ARM.com
1677405SAli.Saidi@ARM.com/** Function called by the port when the bus is receiving a Timing
1687405SAli.Saidi@ARM.com * transaction.*/
1697405SAli.Saidi@ARM.combool
1707405SAli.Saidi@ARM.comBus::recvTiming(PacketPtr pkt)
1717405SAli.Saidi@ARM.com{
1727405SAli.Saidi@ARM.com    short src = pkt->getSrc();
1737405SAli.Saidi@ARM.com
1747405SAli.Saidi@ARM.com    BusPort *src_port;
1757405SAli.Saidi@ARM.com    if (src == defaultId)
1767405SAli.Saidi@ARM.com        src_port = defaultPort;
1777405SAli.Saidi@ARM.com    else {
1787405SAli.Saidi@ARM.com        src_port = checkBusCache(src);
1797405SAli.Saidi@ARM.com        if (src_port == NULL) {
1807405SAli.Saidi@ARM.com            src_port = interfaces[src];
1817405SAli.Saidi@ARM.com            updateBusCache(src, src_port);
1827405SAli.Saidi@ARM.com        }
1837405SAli.Saidi@ARM.com    }
1847405SAli.Saidi@ARM.com
1857405SAli.Saidi@ARM.com    // If the bus is busy, or other devices are in line ahead of the current
1867405SAli.Saidi@ARM.com    // one, put this device on the retry list.
1877405SAli.Saidi@ARM.com    if (!pkt->isExpressSnoop() &&
1887405SAli.Saidi@ARM.com        (tickNextIdle > curTick ||
1897405SAli.Saidi@ARM.com         (retryList.size() && (!inRetry || src_port != retryList.front()))))
1907405SAli.Saidi@ARM.com    {
1917405SAli.Saidi@ARM.com        addToRetryList(src_port);
1927405SAli.Saidi@ARM.com        DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x BUSY\n",
1937405SAli.Saidi@ARM.com                src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
1947405SAli.Saidi@ARM.com        return false;
1957405SAli.Saidi@ARM.com    }
1967405SAli.Saidi@ARM.com
1977405SAli.Saidi@ARM.com    DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x\n",
1987405SAli.Saidi@ARM.com            src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
1997405SAli.Saidi@ARM.com
2007405SAli.Saidi@ARM.com    if (!pkt->isExpressSnoop()) {
2017405SAli.Saidi@ARM.com        occupyBus(pkt);
2027405SAli.Saidi@ARM.com    }
2037405SAli.Saidi@ARM.com
2047405SAli.Saidi@ARM.com    short dest = pkt->getDest();
2057405SAli.Saidi@ARM.com    int dest_port_id;
2067405SAli.Saidi@ARM.com    Port *dest_port;
2077405SAli.Saidi@ARM.com
2087405SAli.Saidi@ARM.com    if (dest == Packet::Broadcast) {
2097405SAli.Saidi@ARM.com        dest_port_id = findPort(pkt->getAddr());
2107405SAli.Saidi@ARM.com        dest_port = (dest_port_id == defaultId) ?
2117405SAli.Saidi@ARM.com            defaultPort : interfaces[dest_port_id];
2127405SAli.Saidi@ARM.com        SnoopIter s_end = snoopPorts.end();
2137405SAli.Saidi@ARM.com        for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
2147405SAli.Saidi@ARM.com            BusPort *p = *s_iter;
2157583SAli.Saidi@arm.com            if (p != dest_port && p != src_port) {
2167583SAli.Saidi@arm.com                // cache is not allowed to refuse snoop
2177583SAli.Saidi@arm.com                bool success M5_VAR_USED = p->sendTiming(pkt);
2187583SAli.Saidi@arm.com                assert(success);
2197583SAli.Saidi@arm.com            }
2207583SAli.Saidi@arm.com        }
2217583SAli.Saidi@arm.com    } else {
2227583SAli.Saidi@arm.com        assert(dest >= 0 && dest < maxId);
2237583SAli.Saidi@arm.com        assert(dest != src); // catch infinite loops
2247583SAli.Saidi@arm.com        dest_port_id = dest;
2257583SAli.Saidi@arm.com        if (dest_port_id == defaultId)
2267583SAli.Saidi@arm.com            dest_port = defaultPort;
2277583SAli.Saidi@arm.com        else {
2287583SAli.Saidi@arm.com            dest_port = checkBusCache(dest);
2297405SAli.Saidi@ARM.com            if (dest_port == NULL) {
2307405SAli.Saidi@ARM.com                dest_port = interfaces[dest_port_id];
2317405SAli.Saidi@ARM.com            // updateBusCache(dest_port_id, dest_port);
2327405SAli.Saidi@ARM.com            }
2337405SAli.Saidi@ARM.com        }
2347405SAli.Saidi@ARM.com        dest_port = (dest_port_id == defaultId) ?
2357405SAli.Saidi@ARM.com            defaultPort : interfaces[dest_port_id];
2367405SAli.Saidi@ARM.com    }
2377405SAli.Saidi@ARM.com
2387405SAli.Saidi@ARM.com    if (dest_port_id == src) {
2397405SAli.Saidi@ARM.com        // Must be forwarded snoop up from below...
2407405SAli.Saidi@ARM.com        assert(dest == Packet::Broadcast);
2417405SAli.Saidi@ARM.com        assert(src != defaultId); // catch infinite loops
2427405SAli.Saidi@ARM.com    } else {
2437405SAli.Saidi@ARM.com        // send to actual target
2447405SAli.Saidi@ARM.com        if (!dest_port->sendTiming(pkt))  {
2457405SAli.Saidi@ARM.com            // Packet not successfully sent. Leave or put it on the retry list.
2467405SAli.Saidi@ARM.com            // illegal to block responses... can lead to deadlock
2477405SAli.Saidi@ARM.com            assert(!pkt->isResponse());
2487405SAli.Saidi@ARM.com            DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x TGT RETRY\n",
2497405SAli.Saidi@ARM.com                    src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
2507405SAli.Saidi@ARM.com            addToRetryList(src_port);
2517405SAli.Saidi@ARM.com            return false;
2527405SAli.Saidi@ARM.com        }
2537405SAli.Saidi@ARM.com        // send OK, fall through
2547405SAli.Saidi@ARM.com    }
2557405SAli.Saidi@ARM.com
2567405SAli.Saidi@ARM.com    // Packet was successfully sent.
2577405SAli.Saidi@ARM.com    // Also take care of retries
2587405SAli.Saidi@ARM.com    if (inRetry) {
2597405SAli.Saidi@ARM.com        DPRINTF(Bus, "Remove retry from list %d\n", src);
2607405SAli.Saidi@ARM.com        retryList.front()->onRetryList(false);
2617405SAli.Saidi@ARM.com        retryList.pop_front();
2627405SAli.Saidi@ARM.com        inRetry = false;
2637405SAli.Saidi@ARM.com    }
2647405SAli.Saidi@ARM.com    return true;
2657405SAli.Saidi@ARM.com}
2667405SAli.Saidi@ARM.com
2677405SAli.Saidi@ARM.comvoid
2687405SAli.Saidi@ARM.comBus::recvRetry(int id)
2697405SAli.Saidi@ARM.com{
2707405SAli.Saidi@ARM.com    // If there's anything waiting, and the bus isn't busy...
2717405SAli.Saidi@ARM.com    if (retryList.size() && curTick >= tickNextIdle) {
2727405SAli.Saidi@ARM.com        //retryingPort = retryList.front();
2737405SAli.Saidi@ARM.com        inRetry = true;
2747405SAli.Saidi@ARM.com        DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name());
2757405SAli.Saidi@ARM.com        retryList.front()->sendRetry();
2767405SAli.Saidi@ARM.com        // If inRetry is still true, sendTiming wasn't called
2777405SAli.Saidi@ARM.com        if (inRetry)
2787405SAli.Saidi@ARM.com        {
2797405SAli.Saidi@ARM.com            retryList.front()->onRetryList(false);
2807405SAli.Saidi@ARM.com            retryList.pop_front();
2817405SAli.Saidi@ARM.com            inRetry = false;
2827405SAli.Saidi@ARM.com
2837405SAli.Saidi@ARM.com            //Bring tickNextIdle up to the present
2847405SAli.Saidi@ARM.com            while (tickNextIdle < curTick)
2857408Sgblack@eecs.umich.edu                tickNextIdle += clock;
2867405SAli.Saidi@ARM.com
2877405SAli.Saidi@ARM.com            //Burn a cycle for the missed grant.
2887405SAli.Saidi@ARM.com            tickNextIdle += clock;
2897408Sgblack@eecs.umich.edu
2907408Sgblack@eecs.umich.edu            busIdle.reschedule(tickNextIdle, true);
2917408Sgblack@eecs.umich.edu        }
2927408Sgblack@eecs.umich.edu    }
2937408Sgblack@eecs.umich.edu    //If we weren't able to drain before, we might be able to now.
2947408Sgblack@eecs.umich.edu    if (drainEvent && retryList.size() == 0 && curTick >= tickNextIdle) {
2957408Sgblack@eecs.umich.edu        drainEvent->process();
2967408Sgblack@eecs.umich.edu        // Clear the drain event once we're done with it.
2977408Sgblack@eecs.umich.edu        drainEvent = NULL;
2987408Sgblack@eecs.umich.edu    }
2997408Sgblack@eecs.umich.edu}
3007408Sgblack@eecs.umich.edu
3017405SAli.Saidi@ARM.comint
3027408Sgblack@eecs.umich.eduBus::findPort(Addr addr)
3037408Sgblack@eecs.umich.edu{
3047408Sgblack@eecs.umich.edu    /* An interval tree would be a better way to do this. --ali. */
3057408Sgblack@eecs.umich.edu    int dest_id = -1;
3067408Sgblack@eecs.umich.edu
3077408Sgblack@eecs.umich.edu    dest_id = checkPortCache(addr);
3087408Sgblack@eecs.umich.edu    if (dest_id == -1) {
3097408Sgblack@eecs.umich.edu        PortIter i = portMap.find(RangeSize(addr,1));
3107408Sgblack@eecs.umich.edu        if (i != portMap.end())
3117408Sgblack@eecs.umich.edu          dest_id = i->second;
3127408Sgblack@eecs.umich.edu        updatePortCache(dest_id, i->first.start, i->first.end);
3137408Sgblack@eecs.umich.edu    }
3147408Sgblack@eecs.umich.edu
3157408Sgblack@eecs.umich.edu    // Check if this matches the default range
3167408Sgblack@eecs.umich.edu    if (dest_id == -1) {
3177408Sgblack@eecs.umich.edu        AddrRangeIter a_end = defaultRange.end();
3187408Sgblack@eecs.umich.edu        for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) {
3197408Sgblack@eecs.umich.edu            if (*i == addr) {
3207408Sgblack@eecs.umich.edu                DPRINTF(Bus, "  found addr %#llx on default\n", addr);
3217408Sgblack@eecs.umich.edu                return defaultId;
3227408Sgblack@eecs.umich.edu            }
3237408Sgblack@eecs.umich.edu        }
3247408Sgblack@eecs.umich.edu
3257408Sgblack@eecs.umich.edu        if (responderSet) {
3267408Sgblack@eecs.umich.edu            panic("Unable to find destination for addr (user set default "
3277408Sgblack@eecs.umich.edu                  "responder): %#llx", addr);
3287408Sgblack@eecs.umich.edu        } else {
3297408Sgblack@eecs.umich.edu            DPRINTF(Bus, "Unable to find destination for addr: %#llx, will use "
3307408Sgblack@eecs.umich.edu                    "default port", addr);
3317408Sgblack@eecs.umich.edu
3327408Sgblack@eecs.umich.edu            return defaultId;
3337408Sgblack@eecs.umich.edu        }
3347408Sgblack@eecs.umich.edu    }
3357408Sgblack@eecs.umich.edu
3367408Sgblack@eecs.umich.edu    return dest_id;
3377408Sgblack@eecs.umich.edu}
3387408Sgblack@eecs.umich.edu
3397408Sgblack@eecs.umich.edu
3407408Sgblack@eecs.umich.edu/** Function called by the port when the bus is receiving a Atomic
3417408Sgblack@eecs.umich.edu * transaction.*/
3427408Sgblack@eecs.umich.eduTick
3437408Sgblack@eecs.umich.eduBus::recvAtomic(PacketPtr pkt)
3447408Sgblack@eecs.umich.edu{
3457408Sgblack@eecs.umich.edu    DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n",
3467408Sgblack@eecs.umich.edu            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
3477408Sgblack@eecs.umich.edu    assert(pkt->getDest() == Packet::Broadcast);
3487408Sgblack@eecs.umich.edu    assert(pkt->isRequest());
3497408Sgblack@eecs.umich.edu
3507408Sgblack@eecs.umich.edu    // Variables for recording original command and snoop response (if
3517408Sgblack@eecs.umich.edu    // any)... if a snooper respondes, we will need to restore
3527408Sgblack@eecs.umich.edu    // original command so that additional snoops can take place
3537408Sgblack@eecs.umich.edu    // properly
3547408Sgblack@eecs.umich.edu    MemCmd orig_cmd = pkt->cmd;
3557408Sgblack@eecs.umich.edu    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
3567408Sgblack@eecs.umich.edu    Tick snoop_response_latency = 0;
3577408Sgblack@eecs.umich.edu    int orig_src = pkt->getSrc();
3587408Sgblack@eecs.umich.edu
3597408Sgblack@eecs.umich.edu    int target_port_id = findPort(pkt->getAddr());
3607408Sgblack@eecs.umich.edu    BusPort *target_port;
3617408Sgblack@eecs.umich.edu    if (target_port_id == defaultId)
3627408Sgblack@eecs.umich.edu        target_port = defaultPort;
3637408Sgblack@eecs.umich.edu    else {
3647408Sgblack@eecs.umich.edu      target_port = checkBusCache(target_port_id);
3657408Sgblack@eecs.umich.edu      if (target_port == NULL) {
3667408Sgblack@eecs.umich.edu          target_port = interfaces[target_port_id];
3677408Sgblack@eecs.umich.edu          updateBusCache(target_port_id, target_port);
3687408Sgblack@eecs.umich.edu      }
3697408Sgblack@eecs.umich.edu    }
3707408Sgblack@eecs.umich.edu
3717408Sgblack@eecs.umich.edu    SnoopIter s_end = snoopPorts.end();
3727408Sgblack@eecs.umich.edu    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
3737408Sgblack@eecs.umich.edu        BusPort *p = *s_iter;
3747408Sgblack@eecs.umich.edu        // same port should not have both target addresses and snooping
3757408Sgblack@eecs.umich.edu        assert(p != target_port);
3767408Sgblack@eecs.umich.edu        if (p->getId() != pkt->getSrc()) {
3777408Sgblack@eecs.umich.edu            Tick latency = p->sendAtomic(pkt);
3787408Sgblack@eecs.umich.edu            if (pkt->isResponse()) {
3797408Sgblack@eecs.umich.edu                // response from snoop agent
3807408Sgblack@eecs.umich.edu                assert(pkt->cmd != orig_cmd);
3817408Sgblack@eecs.umich.edu                assert(pkt->memInhibitAsserted());
3827408Sgblack@eecs.umich.edu                // should only happen once
3837408Sgblack@eecs.umich.edu                assert(snoop_response_cmd == MemCmd::InvalidCmd);
3847408Sgblack@eecs.umich.edu                // save response state
3857408Sgblack@eecs.umich.edu                snoop_response_cmd = pkt->cmd;
3867408Sgblack@eecs.umich.edu                snoop_response_latency = latency;
3877408Sgblack@eecs.umich.edu                // restore original packet state for remaining snoopers
3887408Sgblack@eecs.umich.edu                pkt->cmd = orig_cmd;
3897408Sgblack@eecs.umich.edu                pkt->setSrc(orig_src);
3907408Sgblack@eecs.umich.edu                pkt->setDest(Packet::Broadcast);
3917408Sgblack@eecs.umich.edu            }
3927408Sgblack@eecs.umich.edu        }
3937408Sgblack@eecs.umich.edu    }
3947408Sgblack@eecs.umich.edu
3957408Sgblack@eecs.umich.edu    Tick response_latency = 0;
3967408Sgblack@eecs.umich.edu
3977408Sgblack@eecs.umich.edu    // we can get requests sent up from the memory side of the bus for
3987408Sgblack@eecs.umich.edu    // snooping... don't send them back down!
3997408Sgblack@eecs.umich.edu    if (target_port_id != pkt->getSrc()) {
4007408Sgblack@eecs.umich.edu        response_latency = target_port->sendAtomic(pkt);
4017408Sgblack@eecs.umich.edu    }
4027408Sgblack@eecs.umich.edu
4037408Sgblack@eecs.umich.edu    // if we got a response from a snooper, restore it here
4047408Sgblack@eecs.umich.edu    if (snoop_response_cmd != MemCmd::InvalidCmd) {
4057408Sgblack@eecs.umich.edu        // no one else should have responded
4067408Sgblack@eecs.umich.edu        assert(!pkt->isResponse());
4077408Sgblack@eecs.umich.edu        assert(pkt->cmd == orig_cmd);
4087408Sgblack@eecs.umich.edu        pkt->cmd = snoop_response_cmd;
4097408Sgblack@eecs.umich.edu        response_latency = snoop_response_latency;
4107405SAli.Saidi@ARM.com    }
4117583SAli.Saidi@arm.com
4127583SAli.Saidi@arm.com    // why do we have this packet field and the return value both???
4137583SAli.Saidi@arm.com    pkt->finishTime = curTick + response_latency;
4147583SAli.Saidi@arm.com    return response_latency;
4157583SAli.Saidi@arm.com}
4167583SAli.Saidi@arm.com
4177583SAli.Saidi@arm.com/** Function called by the port when the bus is receiving a Functional
4187583SAli.Saidi@arm.com * transaction.*/
4197583SAli.Saidi@arm.comvoid
4207436Sdam.sunwoo@arm.comBus::recvFunctional(PacketPtr pkt)
4217436Sdam.sunwoo@arm.com{
4227436Sdam.sunwoo@arm.com    DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n",
4237436Sdam.sunwoo@arm.com            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
4247436Sdam.sunwoo@arm.com    assert(pkt->getDest() == Packet::Broadcast);
4257436Sdam.sunwoo@arm.com
4267436Sdam.sunwoo@arm.com    int port_id = findPort(pkt->getAddr());
4277436Sdam.sunwoo@arm.com    Port *port = (port_id == defaultId) ? defaultPort : interfaces[port_id];
4287436Sdam.sunwoo@arm.com    // The packet may be changed by another bus on snoops, restore the
4297436Sdam.sunwoo@arm.com    // id after each
4307436Sdam.sunwoo@arm.com    int src_id = pkt->getSrc();
4317436Sdam.sunwoo@arm.com
4327436Sdam.sunwoo@arm.com    assert(pkt->isRequest()); // hasn't already been satisfied
4337436Sdam.sunwoo@arm.com
4347436Sdam.sunwoo@arm.com    SnoopIter s_end = snoopPorts.end();
4357436Sdam.sunwoo@arm.com    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
4367436Sdam.sunwoo@arm.com        BusPort *p = *s_iter;
4377436Sdam.sunwoo@arm.com        if (p != port && p->getId() != src_id) {
4387436Sdam.sunwoo@arm.com            p->sendFunctional(pkt);
4397436Sdam.sunwoo@arm.com        }
4407436Sdam.sunwoo@arm.com        if (pkt->isResponse()) {
4417436Sdam.sunwoo@arm.com            break;
4427436Sdam.sunwoo@arm.com        }
4437436Sdam.sunwoo@arm.com        pkt->setSrc(src_id);
4447436Sdam.sunwoo@arm.com    }
4457436Sdam.sunwoo@arm.com
4467436Sdam.sunwoo@arm.com    // If the snooping hasn't found what we were looking for, keep going.
4477436Sdam.sunwoo@arm.com    if (!pkt->isResponse() && port_id != pkt->getSrc()) {
4487436Sdam.sunwoo@arm.com        port->sendFunctional(pkt);
4497436Sdam.sunwoo@arm.com    }
4507442Ssaidi@eecs.umich.edu}
4517436Sdam.sunwoo@arm.com
4527436Sdam.sunwoo@arm.com/** Function called by the port when the bus is receiving a status change.*/
4537436Sdam.sunwoo@arm.comvoid
4547436Sdam.sunwoo@arm.comBus::recvStatusChange(Port::Status status, int id)
4557436Sdam.sunwoo@arm.com{
4567436Sdam.sunwoo@arm.com    AddrRangeList ranges;
4577436Sdam.sunwoo@arm.com    bool snoops;
4587436Sdam.sunwoo@arm.com    AddrRangeIter iter;
4597436Sdam.sunwoo@arm.com
4607436Sdam.sunwoo@arm.com    if (inRecvStatusChange.count(id))
4617436Sdam.sunwoo@arm.com        return;
4627436Sdam.sunwoo@arm.com    inRecvStatusChange.insert(id);
4637436Sdam.sunwoo@arm.com
4647436Sdam.sunwoo@arm.com    assert(status == Port::RangeChange &&
4657436Sdam.sunwoo@arm.com           "The other statuses need to be implemented.");
4667436Sdam.sunwoo@arm.com
4677436Sdam.sunwoo@arm.com    DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id);
4687436Sdam.sunwoo@arm.com
4697436Sdam.sunwoo@arm.com    clearPortCache();
4707436Sdam.sunwoo@arm.com    if (id == defaultId) {
4717436Sdam.sunwoo@arm.com        defaultRange.clear();
4727436Sdam.sunwoo@arm.com        // Only try to update these ranges if the user set a default responder.
4737436Sdam.sunwoo@arm.com        if (responderSet) {
4747405SAli.Saidi@ARM.com            defaultPort->getPeerAddressRanges(ranges, snoops);
4757405SAli.Saidi@ARM.com            assert(snoops == false);
4767405SAli.Saidi@ARM.com            for(iter = ranges.begin(); iter != ranges.end(); iter++) {
4777405SAli.Saidi@ARM.com                defaultRange.push_back(*iter);
4787405SAli.Saidi@ARM.com                DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n",
4797405SAli.Saidi@ARM.com                        iter->start, iter->end);
480            }
481        }
482    } else {
483
484        assert((id < maxId && id >= 0) || id == defaultId);
485        BusPort *port = interfaces[id];
486
487        // Clean out any previously existent ids
488        for (PortIter portIter = portMap.begin();
489             portIter != portMap.end(); ) {
490            if (portIter->second == id)
491                portMap.erase(portIter++);
492            else
493                portIter++;
494        }
495
496        for (SnoopIter s_iter = snoopPorts.begin();
497             s_iter != snoopPorts.end(); ) {
498            if ((*s_iter)->getId() == id)
499                s_iter = snoopPorts.erase(s_iter);
500            else
501                s_iter++;
502        }
503
504        port->getPeerAddressRanges(ranges, snoops);
505
506        if (snoops) {
507            DPRINTF(BusAddrRanges, "Adding id %d to snoop list\n", id);
508            snoopPorts.push_back(port);
509        }
510
511        for (iter = ranges.begin(); iter != ranges.end(); iter++) {
512            DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n",
513                    iter->start, iter->end, id);
514            if (portMap.insert(*iter, id) == portMap.end())
515                panic("Two devices with same range\n");
516
517        }
518    }
519    DPRINTF(MMU, "port list has %d entries\n", portMap.size());
520
521    // tell all our peers that our address range has changed.
522    // Don't tell the device that caused this change, it already knows
523    m5::hash_map<short,BusPort*>::iterator intIter;
524
525    for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
526        if (intIter->first != id && intIter->first != funcPortId)
527            intIter->second->sendStatusChange(Port::RangeChange);
528
529    if (id != defaultId && defaultPort)
530        defaultPort->sendStatusChange(Port::RangeChange);
531    inRecvStatusChange.erase(id);
532}
533
534void
535Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id)
536{
537    resp.clear();
538    snoop = false;
539
540    DPRINTF(BusAddrRanges, "received address range request, returning:\n");
541
542    for (AddrRangeIter dflt_iter = defaultRange.begin();
543         dflt_iter != defaultRange.end(); dflt_iter++) {
544        resp.push_back(*dflt_iter);
545        DPRINTF(BusAddrRanges, "  -- Dflt: %#llx : %#llx\n",dflt_iter->start,
546                dflt_iter->end);
547    }
548    for (PortIter portIter = portMap.begin();
549         portIter != portMap.end(); portIter++) {
550        bool subset = false;
551        for (AddrRangeIter dflt_iter = defaultRange.begin();
552             dflt_iter != defaultRange.end(); dflt_iter++) {
553            if ((portIter->first.start < dflt_iter->start &&
554                portIter->first.end >= dflt_iter->start) ||
555               (portIter->first.start < dflt_iter->end &&
556                portIter->first.end >= dflt_iter->end))
557                fatal("Devices can not set ranges that itersect the default set\
558                        but are not a subset of the default set.\n");
559            if (portIter->first.start >= dflt_iter->start &&
560                portIter->first.end <= dflt_iter->end) {
561                subset = true;
562                DPRINTF(BusAddrRanges, "  -- %#llx : %#llx is a SUBSET\n",
563                    portIter->first.start, portIter->first.end);
564            }
565        }
566        if (portIter->second != id && !subset) {
567            resp.push_back(portIter->first);
568            DPRINTF(BusAddrRanges, "  -- %#llx : %#llx\n",
569                    portIter->first.start, portIter->first.end);
570        }
571    }
572
573    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end();
574         s_iter++) {
575        if ((*s_iter)->getId() != id) {
576            snoop = true;
577            break;
578        }
579    }
580}
581
582int
583Bus::findBlockSize(int id)
584{
585    if (cachedBlockSizeValid)
586        return cachedBlockSize;
587
588    int max_bs = -1;
589
590    PortIter p_end = portMap.end();
591    for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) {
592        int tmp_bs = interfaces[p_iter->second]->peerBlockSize();
593        if (tmp_bs > max_bs)
594            max_bs = tmp_bs;
595    }
596    SnoopIter s_end = snoopPorts.end();
597    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
598        int tmp_bs = (*s_iter)->peerBlockSize();
599        if (tmp_bs > max_bs)
600            max_bs = tmp_bs;
601    }
602    if (max_bs <= 0)
603        max_bs = defaultBlockSize;
604
605    if (max_bs != 64)
606        warn_once("Blocksize found to not be 64... hmm... probably not.\n");
607    cachedBlockSize = max_bs;
608    cachedBlockSizeValid = true;
609    return max_bs;
610}
611
612
613unsigned int
614Bus::drain(Event * de)
615{
616    //We should check that we're not "doing" anything, and that noone is
617    //waiting. We might be idle but have someone waiting if the device we
618    //contacted for a retry didn't actually retry.
619    if (retryList.size() || (curTick < tickNextIdle && busIdle.scheduled())) {
620        drainEvent = de;
621        return 1;
622    }
623    return 0;
624}
625
626void
627Bus::startup()
628{
629    if (tickNextIdle < curTick)
630        tickNextIdle = (curTick / clock) * clock + clock;
631}
632
633Bus *
634BusParams::create()
635{
636    return new Bus(this);
637}
638