coherent_xbar.cc revision 7523
16242Sgblack@eecs.umich.edu/*
27093Sgblack@eecs.umich.edu * Copyright (c) 2006 The Regents of The University of Michigan
37093Sgblack@eecs.umich.edu * All rights reserved.
47093Sgblack@eecs.umich.edu *
57093Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without
67093Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are
77093Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright
87093Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer;
97093Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
107093Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the
117093Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution;
127093Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its
137093Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
146242Sgblack@eecs.umich.edu * this software without specific prior written permission.
156242Sgblack@eecs.umich.edu *
166242Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
176242Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
186242Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
196242Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
206242Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
216242Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
226242Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236242Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
246242Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
256242Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
266242Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276242Sgblack@eecs.umich.edu *
286242Sgblack@eecs.umich.edu * Authors: Ali Saidi
296242Sgblack@eecs.umich.edu */
306242Sgblack@eecs.umich.edu
316242Sgblack@eecs.umich.edu/**
326242Sgblack@eecs.umich.edu * @file
336242Sgblack@eecs.umich.edu * Definition of a bus object.
346242Sgblack@eecs.umich.edu */
356242Sgblack@eecs.umich.edu
366242Sgblack@eecs.umich.edu#include <algorithm>
376242Sgblack@eecs.umich.edu#include <limits>
386242Sgblack@eecs.umich.edu
396242Sgblack@eecs.umich.edu#include "base/misc.hh"
406242Sgblack@eecs.umich.edu#include "base/trace.hh"
416242Sgblack@eecs.umich.edu#include "mem/bus.hh"
426242Sgblack@eecs.umich.edu
436242Sgblack@eecs.umich.eduBus::Bus(const BusParams *p)
446242Sgblack@eecs.umich.edu    : MemObject(p), busId(p->bus_id), clock(p->clock),
456242Sgblack@eecs.umich.edu      headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
466242Sgblack@eecs.umich.edu      drainEvent(NULL), busIdle(this), inRetry(false), maxId(0),
476242Sgblack@eecs.umich.edu      defaultPort(NULL), funcPort(NULL), funcPortId(-4),
486242Sgblack@eecs.umich.edu      useDefaultRange(p->use_default_range), defaultBlockSize(p->block_size),
496242Sgblack@eecs.umich.edu      cachedBlockSize(0), cachedBlockSizeValid(false)
506242Sgblack@eecs.umich.edu{
516242Sgblack@eecs.umich.edu    //width, clock period, and header cycles must be positive
526242Sgblack@eecs.umich.edu    if (width <= 0)
536242Sgblack@eecs.umich.edu        fatal("Bus width must be positive\n");
546242Sgblack@eecs.umich.edu    if (clock <= 0)
556242Sgblack@eecs.umich.edu        fatal("Bus clock period must be positive\n");
566242Sgblack@eecs.umich.edu    if (headerCycles <= 0)
576242Sgblack@eecs.umich.edu        fatal("Number of header cycles must be positive\n");
586242Sgblack@eecs.umich.edu    clearBusCache();
596242Sgblack@eecs.umich.edu    clearPortCache();
606242Sgblack@eecs.umich.edu}
616242Sgblack@eecs.umich.edu
626242Sgblack@eecs.umich.eduPort *
636242Sgblack@eecs.umich.eduBus::getPort(const std::string &if_name, int idx)
646242Sgblack@eecs.umich.edu{
657111Sgblack@eecs.umich.edu    if (if_name == "default") {
666242Sgblack@eecs.umich.edu        if (defaultPort == NULL) {
676242Sgblack@eecs.umich.edu            defaultPort = new BusPort(csprintf("%s-default",name()), this,
686242Sgblack@eecs.umich.edu                                      defaultId);
696242Sgblack@eecs.umich.edu            cachedBlockSizeValid = false;
707408Sgblack@eecs.umich.edu            return defaultPort;
716735Sgblack@eecs.umich.edu        } else
726242Sgblack@eecs.umich.edu            fatal("Default port already set\n");
736242Sgblack@eecs.umich.edu    }
746242Sgblack@eecs.umich.edu    int id;
756723Sgblack@eecs.umich.edu    if (if_name == "functional") {
766242Sgblack@eecs.umich.edu        if (!funcPort) {
776242Sgblack@eecs.umich.edu            id = maxId++;
786261Sgblack@eecs.umich.edu            funcPort = new BusPort(csprintf("%s-p%d-func", name(), id), this, id);
796403Sgblack@eecs.umich.edu            funcPortId = id;
806403Sgblack@eecs.umich.edu            interfaces[id] = funcPort;
816403Sgblack@eecs.umich.edu        }
827325Sgblack@eecs.umich.edu        return funcPort;
837325Sgblack@eecs.umich.edu    }
847400SAli.Saidi@ARM.com
857350SAli.Saidi@ARM.com    // if_name ignored?  forced to be empty?
867259Sgblack@eecs.umich.edu    id = maxId++;
877259Sgblack@eecs.umich.edu    assert(maxId < std::numeric_limits<typeof(maxId)>::max());
887259Sgblack@eecs.umich.edu    BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id);
897259Sgblack@eecs.umich.edu    interfaces[id] = bp;
907264Sgblack@eecs.umich.edu    cachedBlockSizeValid = false;
917267Sgblack@eecs.umich.edu    return bp;
927285Sgblack@eecs.umich.edu}
937265Sgblack@eecs.umich.edu
947266Sgblack@eecs.umich.eduvoid
957266Sgblack@eecs.umich.eduBus::deletePortRefs(Port *p)
967266Sgblack@eecs.umich.edu{
977268Sgblack@eecs.umich.edu
987272Sgblack@eecs.umich.edu    BusPort *bp =  dynamic_cast<BusPort*>(p);
997272Sgblack@eecs.umich.edu    if (bp == NULL)
1007271Sgblack@eecs.umich.edu        panic("Couldn't convert Port* to BusPort*\n");
1017273Sgblack@eecs.umich.edu    // If this is our one functional port
1027287Sgblack@eecs.umich.edu    if (funcPort == bp)
1037287Sgblack@eecs.umich.edu        return;
1047274Sgblack@eecs.umich.edu    interfaces.erase(bp->getId());
1057275Sgblack@eecs.umich.edu    clearBusCache();
1067276Sgblack@eecs.umich.edu    delete bp;
1077286Sgblack@eecs.umich.edu}
1087297Sgblack@eecs.umich.edu
1097297Sgblack@eecs.umich.edu/** Get the ranges of anyone other buses that we are connected to. */
1107298Sgblack@eecs.umich.eduvoid
1117352Sgblack@eecs.umich.eduBus::init()
1127352Sgblack@eecs.umich.edu{
1137354Sgblack@eecs.umich.edu    m5::hash_map<short,BusPort*>::iterator intIter;
1147353Sgblack@eecs.umich.edu
1157355Sgblack@eecs.umich.edu    for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
1167355Sgblack@eecs.umich.edu        intIter->second->sendStatusChange(Port::RangeChange);
1177355Sgblack@eecs.umich.edu}
1187355Sgblack@eecs.umich.edu
1197355Sgblack@eecs.umich.eduBus::BusFreeEvent::BusFreeEvent(Bus *_bus)
1207355Sgblack@eecs.umich.edu    : bus(_bus)
1217355Sgblack@eecs.umich.edu{}
1227355Sgblack@eecs.umich.edu
1237355Sgblack@eecs.umich.eduvoid
1247355Sgblack@eecs.umich.eduBus::BusFreeEvent::process()
1257355Sgblack@eecs.umich.edu{
1267355Sgblack@eecs.umich.edu    bus->recvRetry(-1);
1277355Sgblack@eecs.umich.edu}
1287355Sgblack@eecs.umich.edu
1297362Sgblack@eecs.umich.educonst char *
1307362Sgblack@eecs.umich.eduBus::BusFreeEvent::description() const
1317362Sgblack@eecs.umich.edu{
1327362Sgblack@eecs.umich.edu    return "bus became available";
1337390Sgblack@eecs.umich.edu}
1347404SAli.Saidi@ARM.com
1357404SAli.Saidi@ARM.comTick
1367404SAli.Saidi@ARM.comBus::calcPacketTiming(PacketPtr pkt)
1377404SAli.Saidi@ARM.com{
1387406SAli.Saidi@ARM.com    // Bring tickNextIdle up to the present tick.
1397406SAli.Saidi@ARM.com    // There is some potential ambiguity where a cycle starts, which
1407406SAli.Saidi@ARM.com    // might make a difference when devices are acting right around a
1417436Sdam.sunwoo@arm.com    // cycle boundary. Using a < allows things which happen exactly on
1427436Sdam.sunwoo@arm.com    // a cycle boundary to take up only the following cycle. Anything
1437436Sdam.sunwoo@arm.com    // that happens later will have to "wait" for the end of that
1447436Sdam.sunwoo@arm.com    // cycle, and then start using the bus after that.
1457436Sdam.sunwoo@arm.com    if (tickNextIdle < curTick) {
1467436Sdam.sunwoo@arm.com        tickNextIdle = curTick;
1477436Sdam.sunwoo@arm.com        if (tickNextIdle % clock != 0)
1487436Sdam.sunwoo@arm.com            tickNextIdle = curTick - (curTick % clock) + clock;
1497436Sdam.sunwoo@arm.com    }
1507583SAli.Saidi@arm.com
1517583SAli.Saidi@arm.com    Tick headerTime = tickNextIdle + headerCycles * clock;
1527583SAli.Saidi@arm.com
1537583SAli.Saidi@arm.com    // The packet will be sent. Figure out how long it occupies the bus, and
1547583SAli.Saidi@arm.com    // how much of that time is for the first "word", aka bus width.
1557583SAli.Saidi@arm.com    int numCycles = 0;
1567583SAli.Saidi@arm.com    if (pkt->hasData()) {
1577583SAli.Saidi@arm.com        // If a packet has data, it needs ceil(size/width) cycles to send it
1587583SAli.Saidi@arm.com        int dataSize = pkt->getSize();
1597583SAli.Saidi@arm.com        numCycles += dataSize/width;
1607583SAli.Saidi@arm.com        if (dataSize % width)
1617583SAli.Saidi@arm.com            numCycles++;
1627583SAli.Saidi@arm.com    }
1637583SAli.Saidi@arm.com
1647583SAli.Saidi@arm.com    // The first word will be delivered after the current tick, the delivery
1657583SAli.Saidi@arm.com    // of the address if any, and one bus cycle to deliver the data
1667259Sgblack@eecs.umich.edu    pkt->firstWordTime = headerTime + clock;
1677406SAli.Saidi@ARM.com
1687259Sgblack@eecs.umich.edu    pkt->finishTime = headerTime + numCycles * clock;
1697259Sgblack@eecs.umich.edu
1707259Sgblack@eecs.umich.edu    return headerTime;
1717259Sgblack@eecs.umich.edu}
1727259Sgblack@eecs.umich.edu
1737259Sgblack@eecs.umich.eduvoid Bus::occupyBus(Tick until)
1747259Sgblack@eecs.umich.edu{
1757259Sgblack@eecs.umich.edu    if (until == 0) {
1767259Sgblack@eecs.umich.edu        // shortcut for express snoop packets
1777259Sgblack@eecs.umich.edu        return;
1787259Sgblack@eecs.umich.edu    }
1797259Sgblack@eecs.umich.edu
1807259Sgblack@eecs.umich.edu    tickNextIdle = until;
1817259Sgblack@eecs.umich.edu    reschedule(busIdle, tickNextIdle, true);
1827259Sgblack@eecs.umich.edu
1837259Sgblack@eecs.umich.edu    DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
1847259Sgblack@eecs.umich.edu            curTick, tickNextIdle);
1857259Sgblack@eecs.umich.edu}
1867259Sgblack@eecs.umich.edu
1877351Sgblack@eecs.umich.edu/** Function called by the port when the bus is receiving a Timing
1887351Sgblack@eecs.umich.edu * transaction.*/
1897351Sgblack@eecs.umich.edubool
1907351Sgblack@eecs.umich.eduBus::recvTiming(PacketPtr pkt)
1917351Sgblack@eecs.umich.edu{
1927351Sgblack@eecs.umich.edu    short src = pkt->getSrc();
1937259Sgblack@eecs.umich.edu
1947259Sgblack@eecs.umich.edu    BusPort *src_port;
1957259Sgblack@eecs.umich.edu    if (src == defaultId)
1967259Sgblack@eecs.umich.edu        src_port = defaultPort;
1977259Sgblack@eecs.umich.edu    else {
1987259Sgblack@eecs.umich.edu        src_port = checkBusCache(src);
1997259Sgblack@eecs.umich.edu        if (src_port == NULL) {
2006735Sgblack@eecs.umich.edu            src_port = interfaces[src];
2016261Sgblack@eecs.umich.edu            updateBusCache(src, src_port);
2026261Sgblack@eecs.umich.edu        }
2037259Sgblack@eecs.umich.edu    }
2047259Sgblack@eecs.umich.edu
2057259Sgblack@eecs.umich.edu    // If the bus is busy, or other devices are in line ahead of the current
2066261Sgblack@eecs.umich.edu    // one, put this device on the retry list.
2077408Sgblack@eecs.umich.edu    if (!pkt->isExpressSnoop() &&
2087259Sgblack@eecs.umich.edu        (tickNextIdle > curTick ||
2097351Sgblack@eecs.umich.edu         (retryList.size() && (!inRetry || src_port != retryList.front()))))
2107400SAli.Saidi@ARM.com    {
2117285Sgblack@eecs.umich.edu        addToRetryList(src_port);
2127267Sgblack@eecs.umich.edu        DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x BUSY\n",
2137287Sgblack@eecs.umich.edu                src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
2147287Sgblack@eecs.umich.edu        return false;
2157297Sgblack@eecs.umich.edu    }
2167297Sgblack@eecs.umich.edu
2177355Sgblack@eecs.umich.edu    DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x\n",
2187355Sgblack@eecs.umich.edu            src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
2197355Sgblack@eecs.umich.edu
2207355Sgblack@eecs.umich.edu    Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
2217355Sgblack@eecs.umich.edu    Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
2227390Sgblack@eecs.umich.edu
2237436Sdam.sunwoo@arm.com    short dest = pkt->getDest();
2247436Sdam.sunwoo@arm.com    int dest_port_id;
2257436Sdam.sunwoo@arm.com    Port *dest_port;
2267436Sdam.sunwoo@arm.com
2277583SAli.Saidi@arm.com    if (dest == Packet::Broadcast) {
2287583SAli.Saidi@arm.com        dest_port_id = findPort(pkt->getAddr());
2297583SAli.Saidi@arm.com        dest_port = (dest_port_id == defaultId) ?
2307583SAli.Saidi@arm.com            defaultPort : interfaces[dest_port_id];
2317583SAli.Saidi@arm.com        SnoopIter s_end = snoopPorts.end();
2327583SAli.Saidi@arm.com        for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
2337406SAli.Saidi@ARM.com            BusPort *p = *s_iter;
2347404SAli.Saidi@ARM.com            if (p != dest_port && p != src_port) {
2357583SAli.Saidi@arm.com                // cache is not allowed to refuse snoop
2367259Sgblack@eecs.umich.edu                bool success M5_VAR_USED = p->sendTiming(pkt);
2377583SAli.Saidi@arm.com                assert(success);
2387362Sgblack@eecs.umich.edu            }
2397297Sgblack@eecs.umich.edu        }
2407272Sgblack@eecs.umich.edu    } else {
2417406SAli.Saidi@ARM.com        assert(dest < maxId);
2427404SAli.Saidi@ARM.com        assert(dest != src); // catch infinite loops
2437259Sgblack@eecs.umich.edu        dest_port_id = dest;
2446242Sgblack@eecs.umich.edu        if (dest_port_id == defaultId)
2456242Sgblack@eecs.umich.edu            dest_port = defaultPort;
2466242Sgblack@eecs.umich.edu        else {
2476242Sgblack@eecs.umich.edu            dest_port = checkBusCache(dest);
2486242Sgblack@eecs.umich.edu            if (dest_port == NULL) {
2496242Sgblack@eecs.umich.edu                dest_port = interfaces[dest_port_id];
2506242Sgblack@eecs.umich.edu            // updateBusCache(dest_port_id, dest_port);
2516242Sgblack@eecs.umich.edu            }
2526735Sgblack@eecs.umich.edu        }
2536242Sgblack@eecs.umich.edu        dest_port = (dest_port_id == defaultId) ?
2546242Sgblack@eecs.umich.edu            defaultPort : interfaces[dest_port_id];
2556735Sgblack@eecs.umich.edu    }
2566242Sgblack@eecs.umich.edu
2576242Sgblack@eecs.umich.edu    if (dest_port_id == src) {
2586242Sgblack@eecs.umich.edu        // Must be forwarded snoop up from below...
2596242Sgblack@eecs.umich.edu        assert(dest == Packet::Broadcast);
2606242Sgblack@eecs.umich.edu    } else {
2616242Sgblack@eecs.umich.edu        // send to actual target
2626242Sgblack@eecs.umich.edu        if (!dest_port->sendTiming(pkt))  {
2636735Sgblack@eecs.umich.edu            // Packet not successfully sent. Leave or put it on the retry list.
2647408Sgblack@eecs.umich.edu            // illegal to block responses... can lead to deadlock
2657408Sgblack@eecs.umich.edu            assert(!pkt->isResponse());
2667408Sgblack@eecs.umich.edu            // It's also illegal to force a transaction to retry after
2677408Sgblack@eecs.umich.edu            // someone else has committed to respond.
2687408Sgblack@eecs.umich.edu            assert(!pkt->memInhibitAsserted());
2697408Sgblack@eecs.umich.edu            DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x TGT RETRY\n",
2707408Sgblack@eecs.umich.edu                    src, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
2717408Sgblack@eecs.umich.edu            addToRetryList(src_port);
2726750Sgblack@eecs.umich.edu            occupyBus(headerFinishTime);
2736750Sgblack@eecs.umich.edu            return false;
2746750Sgblack@eecs.umich.edu        }
2756750Sgblack@eecs.umich.edu        // send OK, fall through... pkt may have been deleted by
2766735Sgblack@eecs.umich.edu        // target at this point, so it should *not* be referenced
2777360Sgblack@eecs.umich.edu        // again.  We'll set it to NULL here just to be safe.
2786735Sgblack@eecs.umich.edu        pkt = NULL;
2796735Sgblack@eecs.umich.edu    }
2806735Sgblack@eecs.umich.edu
2816735Sgblack@eecs.umich.edu    occupyBus(packetFinishTime);
2826735Sgblack@eecs.umich.edu
2836735Sgblack@eecs.umich.edu    // Packet was successfully sent.
2847406SAli.Saidi@ARM.com    // Also take care of retries
2856735Sgblack@eecs.umich.edu    if (inRetry) {
2866735Sgblack@eecs.umich.edu        DPRINTF(Bus, "Remove retry from list %d\n", src);
2877360Sgblack@eecs.umich.edu        retryList.front()->onRetryList(false);
2886735Sgblack@eecs.umich.edu        retryList.pop_front();
2897360Sgblack@eecs.umich.edu        inRetry = false;
2906735Sgblack@eecs.umich.edu    }
2916735Sgblack@eecs.umich.edu    return true;
2926735Sgblack@eecs.umich.edu}
2936735Sgblack@eecs.umich.edu
2946735Sgblack@eecs.umich.eduvoid
2956735Sgblack@eecs.umich.eduBus::recvRetry(int id)
2967406SAli.Saidi@ARM.com{
2976735Sgblack@eecs.umich.edu    // If there's anything waiting, and the bus isn't busy...
2986735Sgblack@eecs.umich.edu    if (retryList.size() && curTick >= tickNextIdle) {
2996735Sgblack@eecs.umich.edu        //retryingPort = retryList.front();
3006735Sgblack@eecs.umich.edu        inRetry = true;
3016735Sgblack@eecs.umich.edu        DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name());
3026735Sgblack@eecs.umich.edu        retryList.front()->sendRetry();
3037320Sgblack@eecs.umich.edu        // If inRetry is still true, sendTiming wasn't called
3047320Sgblack@eecs.umich.edu        if (inRetry)
3057320Sgblack@eecs.umich.edu        {
3067320Sgblack@eecs.umich.edu            retryList.front()->onRetryList(false);
3077320Sgblack@eecs.umich.edu            retryList.pop_front();
3087320Sgblack@eecs.umich.edu            inRetry = false;
3097320Sgblack@eecs.umich.edu
3107320Sgblack@eecs.umich.edu            //Bring tickNextIdle up to the present
3117320Sgblack@eecs.umich.edu            while (tickNextIdle < curTick)
3127320Sgblack@eecs.umich.edu                tickNextIdle += clock;
3137320Sgblack@eecs.umich.edu
3147320Sgblack@eecs.umich.edu            //Burn a cycle for the missed grant.
3157320Sgblack@eecs.umich.edu            tickNextIdle += clock;
3167320Sgblack@eecs.umich.edu
3177320Sgblack@eecs.umich.edu            reschedule(busIdle, tickNextIdle, true);
3187320Sgblack@eecs.umich.edu        }
3197320Sgblack@eecs.umich.edu    }
3207320Sgblack@eecs.umich.edu    //If we weren't able to drain before, we might be able to now.
3217320Sgblack@eecs.umich.edu    if (drainEvent && retryList.size() == 0 && curTick >= tickNextIdle) {
3227362Sgblack@eecs.umich.edu        drainEvent->process();
3237362Sgblack@eecs.umich.edu        // Clear the drain event once we're done with it.
3247362Sgblack@eecs.umich.edu        drainEvent = NULL;
3257362Sgblack@eecs.umich.edu    }
3267362Sgblack@eecs.umich.edu}
3277362Sgblack@eecs.umich.edu
3287362Sgblack@eecs.umich.eduint
3297362Sgblack@eecs.umich.eduBus::findPort(Addr addr)
3307376Sgblack@eecs.umich.edu{
3317376Sgblack@eecs.umich.edu    /* An interval tree would be a better way to do this. --ali. */
3327376Sgblack@eecs.umich.edu    int dest_id;
3337376Sgblack@eecs.umich.edu
3347376Sgblack@eecs.umich.edu    dest_id = checkPortCache(addr);
3357376Sgblack@eecs.umich.edu    if (dest_id != -1)
3367376Sgblack@eecs.umich.edu        return dest_id;
3377376Sgblack@eecs.umich.edu
3387376Sgblack@eecs.umich.edu    // Check normal port ranges
3397376Sgblack@eecs.umich.edu    PortIter i = portMap.find(RangeSize(addr,1));
3407376Sgblack@eecs.umich.edu    if (i != portMap.end()) {
3417376Sgblack@eecs.umich.edu        dest_id = i->second;
3427376Sgblack@eecs.umich.edu        updatePortCache(dest_id, i->first.start, i->first.end);
3437376Sgblack@eecs.umich.edu        return dest_id;
3447376Sgblack@eecs.umich.edu    }
3457376Sgblack@eecs.umich.edu
3467376Sgblack@eecs.umich.edu    // Check if this matches the default range
3477376Sgblack@eecs.umich.edu    if (useDefaultRange) {
3487376Sgblack@eecs.umich.edu        AddrRangeIter a_end = defaultRange.end();
3497376Sgblack@eecs.umich.edu        for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) {
3507376Sgblack@eecs.umich.edu            if (*i == addr) {
3517376Sgblack@eecs.umich.edu                DPRINTF(Bus, "  found addr %#llx on default\n", addr);
3527376Sgblack@eecs.umich.edu                return defaultId;
3537376Sgblack@eecs.umich.edu            }
3547376Sgblack@eecs.umich.edu        }
3557376Sgblack@eecs.umich.edu
3567383Sgblack@eecs.umich.edu        panic("Unable to find destination for addr %#llx\n", addr);
3577383Sgblack@eecs.umich.edu    }
3587383Sgblack@eecs.umich.edu
3597383Sgblack@eecs.umich.edu    DPRINTF(Bus, "Unable to find destination for addr %#llx, "
3607383Sgblack@eecs.umich.edu            "will use default port\n", addr);
3617383Sgblack@eecs.umich.edu    return defaultId;
3627383Sgblack@eecs.umich.edu}
3637383Sgblack@eecs.umich.edu
3647383Sgblack@eecs.umich.edu
3657383Sgblack@eecs.umich.edu/** Function called by the port when the bus is receiving a Atomic
3667383Sgblack@eecs.umich.edu * transaction.*/
3677383Sgblack@eecs.umich.eduTick
3687383Sgblack@eecs.umich.eduBus::recvAtomic(PacketPtr pkt)
3697383Sgblack@eecs.umich.edu{
3707383Sgblack@eecs.umich.edu    DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n",
3717383Sgblack@eecs.umich.edu            pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
3727383Sgblack@eecs.umich.edu    assert(pkt->getDest() == Packet::Broadcast);
3737383Sgblack@eecs.umich.edu    assert(pkt->isRequest());
3747383Sgblack@eecs.umich.edu
3757383Sgblack@eecs.umich.edu    // Variables for recording original command and snoop response (if
3767383Sgblack@eecs.umich.edu    // any)... if a snooper respondes, we will need to restore
3777383Sgblack@eecs.umich.edu    // original command so that additional snoops can take place
3787404SAli.Saidi@ARM.com    // properly
3797404SAli.Saidi@ARM.com    MemCmd orig_cmd = pkt->cmd;
3807404SAli.Saidi@ARM.com    MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
3817404SAli.Saidi@ARM.com    Tick snoop_response_latency = 0;
3827404SAli.Saidi@ARM.com    int orig_src = pkt->getSrc();
3837404SAli.Saidi@ARM.com
3847404SAli.Saidi@ARM.com    int target_port_id = findPort(pkt->getAddr());
3857404SAli.Saidi@ARM.com    BusPort *target_port;
3867404SAli.Saidi@ARM.com    if (target_port_id == defaultId)
3877404SAli.Saidi@ARM.com        target_port = defaultPort;
3887404SAli.Saidi@ARM.com    else {
3897404SAli.Saidi@ARM.com      target_port = checkBusCache(target_port_id);
3907404SAli.Saidi@ARM.com      if (target_port == NULL) {
3917404SAli.Saidi@ARM.com          target_port = interfaces[target_port_id];
3927404SAli.Saidi@ARM.com          updateBusCache(target_port_id, target_port);
3937404SAli.Saidi@ARM.com      }
3947404SAli.Saidi@ARM.com    }
3957404SAli.Saidi@ARM.com
3967404SAli.Saidi@ARM.com    SnoopIter s_end = snoopPorts.end();
3977404SAli.Saidi@ARM.com    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
3987404SAli.Saidi@ARM.com        BusPort *p = *s_iter;
3997404SAli.Saidi@ARM.com        // same port should not have both target addresses and snooping
4007404SAli.Saidi@ARM.com        assert(p != target_port);
4017404SAli.Saidi@ARM.com        if (p->getId() != pkt->getSrc()) {
4027404SAli.Saidi@ARM.com            Tick latency = p->sendAtomic(pkt);
4037404SAli.Saidi@ARM.com            if (pkt->isResponse()) {
4047404SAli.Saidi@ARM.com                // response from snoop agent
4057404SAli.Saidi@ARM.com                assert(pkt->cmd != orig_cmd);
4067404SAli.Saidi@ARM.com                assert(pkt->memInhibitAsserted());
4077404SAli.Saidi@ARM.com                // should only happen once
4087404SAli.Saidi@ARM.com                assert(snoop_response_cmd == MemCmd::InvalidCmd);
4097404SAli.Saidi@ARM.com                // save response state
4107404SAli.Saidi@ARM.com                snoop_response_cmd = pkt->cmd;
4117404SAli.Saidi@ARM.com                snoop_response_latency = latency;
4127404SAli.Saidi@ARM.com                // restore original packet state for remaining snoopers
4137404SAli.Saidi@ARM.com                pkt->cmd = orig_cmd;
4147404SAli.Saidi@ARM.com                pkt->setSrc(orig_src);
4157404SAli.Saidi@ARM.com                pkt->setDest(Packet::Broadcast);
4167404SAli.Saidi@ARM.com            }
4177404SAli.Saidi@ARM.com        }
4187404SAli.Saidi@ARM.com    }
4197404SAli.Saidi@ARM.com
4207404SAli.Saidi@ARM.com    Tick response_latency = 0;
4216242Sgblack@eecs.umich.edu
4226242Sgblack@eecs.umich.edu    // we can get requests sent up from the memory side of the bus for
4236242Sgblack@eecs.umich.edu    // snooping... don't send them back down!
424    if (target_port_id != pkt->getSrc()) {
425        response_latency = target_port->sendAtomic(pkt);
426    }
427
428    // if we got a response from a snooper, restore it here
429    if (snoop_response_cmd != MemCmd::InvalidCmd) {
430        // no one else should have responded
431        assert(!pkt->isResponse());
432        assert(pkt->cmd == orig_cmd);
433        pkt->cmd = snoop_response_cmd;
434        response_latency = snoop_response_latency;
435    }
436
437    // why do we have this packet field and the return value both???
438    pkt->finishTime = curTick + response_latency;
439    return response_latency;
440}
441
442/** Function called by the port when the bus is receiving a Functional
443 * transaction.*/
444void
445Bus::recvFunctional(PacketPtr pkt)
446{
447    if (!pkt->isPrint()) {
448        // don't do DPRINTFs on PrintReq as it clutters up the output
449        DPRINTF(Bus,
450                "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n",
451                pkt->getSrc(), pkt->getDest(), pkt->getAddr(),
452                pkt->cmdString());
453    }
454    assert(pkt->getDest() == Packet::Broadcast);
455
456    int port_id = findPort(pkt->getAddr());
457    Port *port = (port_id == defaultId) ? defaultPort : interfaces[port_id];
458    // The packet may be changed by another bus on snoops, restore the
459    // id after each
460    int src_id = pkt->getSrc();
461
462    assert(pkt->isRequest()); // hasn't already been satisfied
463
464    SnoopIter s_end = snoopPorts.end();
465    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
466        BusPort *p = *s_iter;
467        if (p != port && p->getId() != src_id) {
468            p->sendFunctional(pkt);
469        }
470        if (pkt->isResponse()) {
471            break;
472        }
473        pkt->setSrc(src_id);
474    }
475
476    // If the snooping hasn't found what we were looking for, keep going.
477    if (!pkt->isResponse() && port_id != pkt->getSrc()) {
478        port->sendFunctional(pkt);
479    }
480}
481
482/** Function called by the port when the bus is receiving a status change.*/
483void
484Bus::recvStatusChange(Port::Status status, int id)
485{
486    AddrRangeList ranges;
487    bool snoops;
488    AddrRangeIter iter;
489
490    if (inRecvStatusChange.count(id))
491        return;
492    inRecvStatusChange.insert(id);
493
494    assert(status == Port::RangeChange &&
495           "The other statuses need to be implemented.");
496
497    DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id);
498
499    clearPortCache();
500    if (id == defaultId) {
501        defaultRange.clear();
502        // Only try to update these ranges if the user set a default responder.
503        if (useDefaultRange) {
504            defaultPort->getPeerAddressRanges(ranges, snoops);
505            assert(snoops == false);
506            for(iter = ranges.begin(); iter != ranges.end(); iter++) {
507                defaultRange.push_back(*iter);
508                DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n",
509                        iter->start, iter->end);
510            }
511        }
512    } else {
513
514        assert((id < maxId && id >= 0) || id == defaultId);
515        BusPort *port = interfaces[id];
516
517        // Clean out any previously existent ids
518        for (PortIter portIter = portMap.begin();
519             portIter != portMap.end(); ) {
520            if (portIter->second == id)
521                portMap.erase(portIter++);
522            else
523                portIter++;
524        }
525
526        for (SnoopIter s_iter = snoopPorts.begin();
527             s_iter != snoopPorts.end(); ) {
528            if ((*s_iter)->getId() == id)
529                s_iter = snoopPorts.erase(s_iter);
530            else
531                s_iter++;
532        }
533
534        port->getPeerAddressRanges(ranges, snoops);
535
536        if (snoops) {
537            DPRINTF(BusAddrRanges, "Adding id %d to snoop list\n", id);
538            snoopPorts.push_back(port);
539        }
540
541        for (iter = ranges.begin(); iter != ranges.end(); iter++) {
542            DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n",
543                    iter->start, iter->end, id);
544            if (portMap.insert(*iter, id) == portMap.end()) {
545                int conflict_id = portMap.find(*iter)->second;
546                fatal("%s has two ports with same range:\n\t%s\n\t%s\n",
547                      name(), interfaces[id]->getPeer()->name(),
548                      interfaces[conflict_id]->getPeer()->name());
549            }
550        }
551    }
552    DPRINTF(MMU, "port list has %d entries\n", portMap.size());
553
554    // tell all our peers that our address range has changed.
555    // Don't tell the device that caused this change, it already knows
556    m5::hash_map<short,BusPort*>::iterator intIter;
557
558    for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++)
559        if (intIter->first != id && intIter->first != funcPortId)
560            intIter->second->sendStatusChange(Port::RangeChange);
561
562    if (id != defaultId && defaultPort)
563        defaultPort->sendStatusChange(Port::RangeChange);
564    inRecvStatusChange.erase(id);
565}
566
567void
568Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id)
569{
570    resp.clear();
571    snoop = false;
572
573    DPRINTF(BusAddrRanges, "received address range request, returning:\n");
574
575    for (AddrRangeIter dflt_iter = defaultRange.begin();
576         dflt_iter != defaultRange.end(); dflt_iter++) {
577        resp.push_back(*dflt_iter);
578        DPRINTF(BusAddrRanges, "  -- Dflt: %#llx : %#llx\n",dflt_iter->start,
579                dflt_iter->end);
580    }
581    for (PortIter portIter = portMap.begin();
582         portIter != portMap.end(); portIter++) {
583        bool subset = false;
584        for (AddrRangeIter dflt_iter = defaultRange.begin();
585             dflt_iter != defaultRange.end(); dflt_iter++) {
586            if ((portIter->first.start < dflt_iter->start &&
587                portIter->first.end >= dflt_iter->start) ||
588               (portIter->first.start < dflt_iter->end &&
589                portIter->first.end >= dflt_iter->end))
590                fatal("Devices can not set ranges that itersect the default set\
591                        but are not a subset of the default set.\n");
592            if (portIter->first.start >= dflt_iter->start &&
593                portIter->first.end <= dflt_iter->end) {
594                subset = true;
595                DPRINTF(BusAddrRanges, "  -- %#llx : %#llx is a SUBSET\n",
596                    portIter->first.start, portIter->first.end);
597            }
598        }
599        if (portIter->second != id && !subset) {
600            resp.push_back(portIter->first);
601            DPRINTF(BusAddrRanges, "  -- %#llx : %#llx\n",
602                    portIter->first.start, portIter->first.end);
603        }
604    }
605
606    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end();
607         s_iter++) {
608        if ((*s_iter)->getId() != id) {
609            snoop = true;
610            break;
611        }
612    }
613}
614
615unsigned
616Bus::findBlockSize(int id)
617{
618    if (cachedBlockSizeValid)
619        return cachedBlockSize;
620
621    unsigned max_bs = 0;
622
623    PortIter p_end = portMap.end();
624    for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) {
625        unsigned tmp_bs = interfaces[p_iter->second]->peerBlockSize();
626        if (tmp_bs > max_bs)
627            max_bs = tmp_bs;
628    }
629    SnoopIter s_end = snoopPorts.end();
630    for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
631        unsigned tmp_bs = (*s_iter)->peerBlockSize();
632        if (tmp_bs > max_bs)
633            max_bs = tmp_bs;
634    }
635    if (max_bs == 0)
636        max_bs = defaultBlockSize;
637
638    if (max_bs != 64)
639        warn_once("Blocksize found to not be 64... hmm... probably not.\n");
640    cachedBlockSize = max_bs;
641    cachedBlockSizeValid = true;
642    return max_bs;
643}
644
645
646unsigned int
647Bus::drain(Event * de)
648{
649    //We should check that we're not "doing" anything, and that noone is
650    //waiting. We might be idle but have someone waiting if the device we
651    //contacted for a retry didn't actually retry.
652    if (retryList.size() || (curTick < tickNextIdle && busIdle.scheduled())) {
653        drainEvent = de;
654        return 1;
655    }
656    return 0;
657}
658
659void
660Bus::startup()
661{
662    if (tickNextIdle < curTick)
663        tickNextIdle = (curTick / clock) * clock + clock;
664}
665
666Bus *
667BusParams::create()
668{
669    return new Bus(this);
670}
671