coherent_xbar.cc revision 9032
112137Sar4jc@virginia.edu/*
212137Sar4jc@virginia.edu * Copyright (c) 2011-2012 ARM Limited
312137Sar4jc@virginia.edu * All rights reserved
412137Sar4jc@virginia.edu *
512137Sar4jc@virginia.edu * The license below extends only to copyright in the software and shall
612137Sar4jc@virginia.edu * not be construed as granting a license to any other intellectual
712137Sar4jc@virginia.edu * property including but not limited to intellectual property relating
812137Sar4jc@virginia.edu * to a hardware implementation of the functionality of the software
912137Sar4jc@virginia.edu * licensed hereunder.  You may use the software subject to the license
1012137Sar4jc@virginia.edu * terms below provided that you ensure that this notice is replicated
1112137Sar4jc@virginia.edu * unmodified and in its entirety in all distributions of the software,
1212137Sar4jc@virginia.edu * modified or unmodified, in source code or in binary form.
1312137Sar4jc@virginia.edu *
1412137Sar4jc@virginia.edu * Copyright (c) 2006 The Regents of The University of Michigan
1512137Sar4jc@virginia.edu * All rights reserved.
1612137Sar4jc@virginia.edu *
1712137Sar4jc@virginia.edu * Redistribution and use in source and binary forms, with or without
1812137Sar4jc@virginia.edu * modification, are permitted provided that the following conditions are
1912137Sar4jc@virginia.edu * met: redistributions of source code must retain the above copyright
2012137Sar4jc@virginia.edu * notice, this list of conditions and the following disclaimer;
2112137Sar4jc@virginia.edu * redistributions in binary form must reproduce the above copyright
2212137Sar4jc@virginia.edu * notice, this list of conditions and the following disclaimer in the
2312137Sar4jc@virginia.edu * documentation and/or other materials provided with the distribution;
2412137Sar4jc@virginia.edu * neither the name of the copyright holders nor the names of its
2512137Sar4jc@virginia.edu * contributors may be used to endorse or promote products derived from
2612137Sar4jc@virginia.edu * this software without specific prior written permission.
2712137Sar4jc@virginia.edu *
2812137Sar4jc@virginia.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2912137Sar4jc@virginia.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3012137Sar4jc@virginia.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3112137Sar4jc@virginia.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3212137Sar4jc@virginia.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3312137Sar4jc@virginia.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3412137Sar4jc@virginia.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3512137Sar4jc@virginia.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3612137Sar4jc@virginia.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3712137Sar4jc@virginia.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3812137Sar4jc@virginia.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3912137Sar4jc@virginia.edu *
4012137Sar4jc@virginia.edu * Authors: Ali Saidi
4112137Sar4jc@virginia.edu *          Andreas Hansson
4212137Sar4jc@virginia.edu *          William Wang
4312137Sar4jc@virginia.edu */
4412137Sar4jc@virginia.edu
4512137Sar4jc@virginia.edu/**
4612137Sar4jc@virginia.edu * @file
4712137Sar4jc@virginia.edu * Definition of a bus object.
4812137Sar4jc@virginia.edu */
4912137Sar4jc@virginia.edu
5012137Sar4jc@virginia.edu#include "base/misc.hh"
5112137Sar4jc@virginia.edu#include "base/trace.hh"
5212137Sar4jc@virginia.edu#include "debug/Bus.hh"
5312137Sar4jc@virginia.edu#include "debug/BusAddrRanges.hh"
5412137Sar4jc@virginia.edu#include "mem/bus.hh"
5512137Sar4jc@virginia.edu
5612137Sar4jc@virginia.eduBus::Bus(const BusParams *p)
5712137Sar4jc@virginia.edu    : MemObject(p), clock(p->clock),
5812137Sar4jc@virginia.edu      headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
5912137Sar4jc@virginia.edu      drainEvent(NULL), busIdleEvent(this), inRetry(false),
6012137Sar4jc@virginia.edu      defaultPortID(InvalidPortID),
6112137Sar4jc@virginia.edu      useDefaultRange(p->use_default_range),
6212137Sar4jc@virginia.edu      defaultBlockSize(p->block_size),
6312137Sar4jc@virginia.edu      cachedBlockSize(0), cachedBlockSizeValid(false)
6412137Sar4jc@virginia.edu{
6512137Sar4jc@virginia.edu    //width, clock period, and header cycles must be positive
6612137Sar4jc@virginia.edu    if (width <= 0)
6712137Sar4jc@virginia.edu        fatal("Bus width must be positive\n");
6812137Sar4jc@virginia.edu    if (clock <= 0)
6912137Sar4jc@virginia.edu        fatal("Bus clock period must be positive\n");
7012137Sar4jc@virginia.edu    if (headerCycles <= 0)
7112137Sar4jc@virginia.edu        fatal("Number of header cycles must be positive\n");
7212137Sar4jc@virginia.edu
7312137Sar4jc@virginia.edu    // create the ports based on the size of the master and slave
7412137Sar4jc@virginia.edu    // vector ports, and the presence of the default port, the ports
7512137Sar4jc@virginia.edu    // are enumerated starting from zero
7612137Sar4jc@virginia.edu    for (int i = 0; i < p->port_master_connection_count; ++i) {
7712137Sar4jc@virginia.edu        std::string portName = csprintf("%s-p%d", name(), i);
7812137Sar4jc@virginia.edu        MasterPort* bp = new BusMasterPort(portName, this, i);
7912137Sar4jc@virginia.edu        masterPorts.push_back(bp);
8012137Sar4jc@virginia.edu    }
8112137Sar4jc@virginia.edu
8212137Sar4jc@virginia.edu    // see if we have a default slave device connected and if so add
8312137Sar4jc@virginia.edu    // our corresponding master port
8412137Sar4jc@virginia.edu    if (p->port_default_connection_count) {
8512137Sar4jc@virginia.edu        defaultPortID = masterPorts.size();
8612137Sar4jc@virginia.edu        std::string portName = csprintf("%s-default", name());
8712137Sar4jc@virginia.edu        MasterPort* bp = new BusMasterPort(portName, this, defaultPortID);
8812137Sar4jc@virginia.edu        masterPorts.push_back(bp);
8912137Sar4jc@virginia.edu    }
9012137Sar4jc@virginia.edu
9112137Sar4jc@virginia.edu    // create the slave ports, once again starting at zero
9212137Sar4jc@virginia.edu    for (int i = 0; i < p->port_slave_connection_count; ++i) {
9312137Sar4jc@virginia.edu        std::string portName = csprintf("%s-p%d", name(), i);
9412137Sar4jc@virginia.edu        SlavePort* bp = new BusSlavePort(portName, this, i);
9512137Sar4jc@virginia.edu        slavePorts.push_back(bp);
9612137Sar4jc@virginia.edu    }
9712137Sar4jc@virginia.edu
9812137Sar4jc@virginia.edu    clearPortCache();
9912137Sar4jc@virginia.edu}
10012137Sar4jc@virginia.edu
10112137Sar4jc@virginia.eduMasterPort &
10212137Sar4jc@virginia.eduBus::getMasterPort(const std::string &if_name, int idx)
10312137Sar4jc@virginia.edu{
10412137Sar4jc@virginia.edu    if (if_name == "master" && idx < masterPorts.size()) {
10512137Sar4jc@virginia.edu        // the master port index translates directly to the vector position
10612137Sar4jc@virginia.edu        return *masterPorts[idx];
10712137Sar4jc@virginia.edu    } else  if (if_name == "default") {
10812137Sar4jc@virginia.edu        return *masterPorts[defaultPortID];
10912137Sar4jc@virginia.edu    } else {
11012137Sar4jc@virginia.edu        return MemObject::getMasterPort(if_name, idx);
11112137Sar4jc@virginia.edu    }
11212137Sar4jc@virginia.edu}
11312137Sar4jc@virginia.edu
11412137Sar4jc@virginia.eduSlavePort &
11512137Sar4jc@virginia.eduBus::getSlavePort(const std::string &if_name, int idx)
11612137Sar4jc@virginia.edu{
11712137Sar4jc@virginia.edu    if (if_name == "slave" && idx < slavePorts.size()) {
11812137Sar4jc@virginia.edu        // the slave port index translates directly to the vector position
11912137Sar4jc@virginia.edu        return *slavePorts[idx];
12012137Sar4jc@virginia.edu    } else {
12112137Sar4jc@virginia.edu        return MemObject::getSlavePort(if_name, idx);
12212137Sar4jc@virginia.edu    }
12312137Sar4jc@virginia.edu}
12412137Sar4jc@virginia.edu
12512137Sar4jc@virginia.eduvoid
12612137Sar4jc@virginia.eduBus::init()
12712137Sar4jc@virginia.edu{
12812137Sar4jc@virginia.edu    // iterate over our slave ports and determine which of our
12912137Sar4jc@virginia.edu    // neighbouring master ports are snooping and add them as snoopers
13012137Sar4jc@virginia.edu    for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
13112137Sar4jc@virginia.edu         ++p) {
13212137Sar4jc@virginia.edu        if ((*p)->getMasterPort().isSnooping()) {
13312137Sar4jc@virginia.edu            DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n",
13412137Sar4jc@virginia.edu                    (*p)->getMasterPort().name());
13512137Sar4jc@virginia.edu            snoopPorts.push_back(*p);
13612137Sar4jc@virginia.edu        }
13712137Sar4jc@virginia.edu    }
13812137Sar4jc@virginia.edu}
13912137Sar4jc@virginia.edu
14012137Sar4jc@virginia.eduTick
14112137Sar4jc@virginia.eduBus::calcPacketTiming(PacketPtr pkt)
14212137Sar4jc@virginia.edu{
14312137Sar4jc@virginia.edu    // determine the current time rounded to the closest following
14412137Sar4jc@virginia.edu    // clock edge
14512137Sar4jc@virginia.edu    Tick now = curTick();
14612137Sar4jc@virginia.edu    if (now % clock != 0) {
14712137Sar4jc@virginia.edu        now = ((now / clock) + 1) * clock;
14812137Sar4jc@virginia.edu    }
14912137Sar4jc@virginia.edu
15012137Sar4jc@virginia.edu    Tick headerTime = now + headerCycles * clock;
15112137Sar4jc@virginia.edu
15212137Sar4jc@virginia.edu    // The packet will be sent. Figure out how long it occupies the bus, and
15312137Sar4jc@virginia.edu    // how much of that time is for the first "word", aka bus width.
15412137Sar4jc@virginia.edu    int numCycles = 0;
15512137Sar4jc@virginia.edu    if (pkt->hasData()) {
15612137Sar4jc@virginia.edu        // If a packet has data, it needs ceil(size/width) cycles to send it
15712137Sar4jc@virginia.edu        int dataSize = pkt->getSize();
15812137Sar4jc@virginia.edu        numCycles += dataSize/width;
15912137Sar4jc@virginia.edu        if (dataSize % width)
16012137Sar4jc@virginia.edu            numCycles++;
16112137Sar4jc@virginia.edu    }
16212137Sar4jc@virginia.edu
16312137Sar4jc@virginia.edu    // The first word will be delivered after the current tick, the delivery
16412137Sar4jc@virginia.edu    // of the address if any, and one bus cycle to deliver the data
16512137Sar4jc@virginia.edu    pkt->firstWordTime = headerTime + clock;
16612137Sar4jc@virginia.edu
16712137Sar4jc@virginia.edu    pkt->finishTime = headerTime + numCycles * clock;
16812137Sar4jc@virginia.edu
16912137Sar4jc@virginia.edu    return headerTime;
17012137Sar4jc@virginia.edu}
17112137Sar4jc@virginia.edu
17212137Sar4jc@virginia.eduvoid Bus::occupyBus(Tick until)
17312137Sar4jc@virginia.edu{
17412137Sar4jc@virginia.edu    if (until == 0) {
17512137Sar4jc@virginia.edu        // shortcut for express snoop packets
17612137Sar4jc@virginia.edu        return;
17712137Sar4jc@virginia.edu    }
17812137Sar4jc@virginia.edu
17912137Sar4jc@virginia.edu    tickNextIdle = until;
18012137Sar4jc@virginia.edu    reschedule(busIdleEvent, tickNextIdle, true);
18112137Sar4jc@virginia.edu
18212137Sar4jc@virginia.edu    DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
18312137Sar4jc@virginia.edu            curTick(), tickNextIdle);
18412137Sar4jc@virginia.edu}
18512137Sar4jc@virginia.edu
18612137Sar4jc@virginia.edubool
18712137Sar4jc@virginia.eduBus::isOccupied(PacketPtr pkt, Port* port)
18812137Sar4jc@virginia.edu{
18912137Sar4jc@virginia.edu    // first we see if the next idle tick is in the future, next the
19012137Sar4jc@virginia.edu    // bus is considered occupied if there are ports on the retry list
19112137Sar4jc@virginia.edu    // and we are not in a retry with the current port
19212137Sar4jc@virginia.edu    if (tickNextIdle > curTick() ||
19312137Sar4jc@virginia.edu        (!retryList.empty() && !(inRetry && port == retryList.front()))) {
19412137Sar4jc@virginia.edu        addToRetryList(port);
19512137Sar4jc@virginia.edu        return true;
19612137Sar4jc@virginia.edu    }
19712137Sar4jc@virginia.edu    return false;
19812137Sar4jc@virginia.edu}
19912137Sar4jc@virginia.edu
20012137Sar4jc@virginia.edubool
20112137Sar4jc@virginia.eduBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
20212137Sar4jc@virginia.edu{
20312137Sar4jc@virginia.edu    // determine the source port based on the id
20412137Sar4jc@virginia.edu    SlavePort *src_port = slavePorts[slave_port_id];
20512137Sar4jc@virginia.edu
20612137Sar4jc@virginia.edu    // test if the bus should be considered occupied for the current
20712137Sar4jc@virginia.edu    // packet, and exclude express snoops from the check
20812137Sar4jc@virginia.edu    if (!pkt->isExpressSnoop() && isOccupied(pkt, src_port)) {
20912137Sar4jc@virginia.edu        DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x BUSY\n",
21012137Sar4jc@virginia.edu                src_port->name(), pkt->cmdString(), pkt->getAddr());
21112137Sar4jc@virginia.edu        return false;
21212137Sar4jc@virginia.edu    }
21312137Sar4jc@virginia.edu
21412137Sar4jc@virginia.edu    DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x\n",
21512137Sar4jc@virginia.edu            src_port->name(), pkt->cmdString(), pkt->getAddr());
21612137Sar4jc@virginia.edu
21712137Sar4jc@virginia.edu    // set the source port for routing of the response
21812137Sar4jc@virginia.edu    pkt->setSrc(slave_port_id);
21912137Sar4jc@virginia.edu
22012137Sar4jc@virginia.edu    Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
22112137Sar4jc@virginia.edu    Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
22212137Sar4jc@virginia.edu
22312137Sar4jc@virginia.edu    // uncacheable requests need never be snooped
22412137Sar4jc@virginia.edu    if (!pkt->req->isUncacheable()) {
22512137Sar4jc@virginia.edu        // the packet is a memory-mapped request and should be
22612137Sar4jc@virginia.edu        // broadcasted to our snoopers but the source
22712137Sar4jc@virginia.edu        forwardTiming(pkt, slave_port_id);
22812137Sar4jc@virginia.edu    }
22912137Sar4jc@virginia.edu
23012137Sar4jc@virginia.edu    // remember if we add an outstanding req so we can undo it if
23112137Sar4jc@virginia.edu    // necessary, if the packet needs a response, we should add it
23212137Sar4jc@virginia.edu    // as outstanding and express snoops never fail so there is
23312137Sar4jc@virginia.edu    // not need to worry about them
23412137Sar4jc@virginia.edu    bool add_outstanding = !pkt->isExpressSnoop() && pkt->needsResponse();
23512137Sar4jc@virginia.edu
23612137Sar4jc@virginia.edu    // keep track that we have an outstanding request packet
23712137Sar4jc@virginia.edu    // matching this request, this is used by the coherency
23812137Sar4jc@virginia.edu    // mechanism in determining what to do with snoop responses
23912137Sar4jc@virginia.edu    // (in recvTimingSnoop)
24012137Sar4jc@virginia.edu    if (add_outstanding) {
24112137Sar4jc@virginia.edu        // we should never have an exsiting request outstanding
24212137Sar4jc@virginia.edu        assert(outstandingReq.find(pkt->req) == outstandingReq.end());
24312137Sar4jc@virginia.edu        outstandingReq.insert(pkt->req);
24412137Sar4jc@virginia.edu    }
24512137Sar4jc@virginia.edu
24612137Sar4jc@virginia.edu    // since it is a normal request, determine the destination
24712137Sar4jc@virginia.edu    // based on the address and attempt to send the packet
24812137Sar4jc@virginia.edu    bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
24912137Sar4jc@virginia.edu
25012137Sar4jc@virginia.edu    if (!success)  {
25112137Sar4jc@virginia.edu        // inhibited packets should never be forced to retry
25212137Sar4jc@virginia.edu        assert(!pkt->memInhibitAsserted());
25312137Sar4jc@virginia.edu
25412137Sar4jc@virginia.edu        // if it was added as outstanding and the send failed, then
25512137Sar4jc@virginia.edu        // erase it again
25612137Sar4jc@virginia.edu        if (add_outstanding)
25712137Sar4jc@virginia.edu            outstandingReq.erase(pkt->req);
25812137Sar4jc@virginia.edu
25912137Sar4jc@virginia.edu        DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x RETRY\n",
26012137Sar4jc@virginia.edu                src_port->name(), pkt->cmdString(), pkt->getAddr());
26112137Sar4jc@virginia.edu
26212137Sar4jc@virginia.edu        addToRetryList(src_port);
26312137Sar4jc@virginia.edu        occupyBus(headerFinishTime);
26412137Sar4jc@virginia.edu
26512137Sar4jc@virginia.edu        return false;
26612137Sar4jc@virginia.edu    }
26712137Sar4jc@virginia.edu
26812137Sar4jc@virginia.edu    succeededTiming(packetFinishTime);
26912137Sar4jc@virginia.edu
27012137Sar4jc@virginia.edu    return true;
27112137Sar4jc@virginia.edu}
27212137Sar4jc@virginia.edu
27312137Sar4jc@virginia.edubool
27412137Sar4jc@virginia.eduBus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
27512137Sar4jc@virginia.edu{
27612137Sar4jc@virginia.edu    // determine the source port based on the id
27712137Sar4jc@virginia.edu    MasterPort *src_port = masterPorts[master_port_id];
27812137Sar4jc@virginia.edu
27912137Sar4jc@virginia.edu    // test if the bus should be considered occupied for the current
28012137Sar4jc@virginia.edu    // packet
28112137Sar4jc@virginia.edu    if (isOccupied(pkt, src_port)) {
28212137Sar4jc@virginia.edu        DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x BUSY\n",
28312137Sar4jc@virginia.edu                src_port->name(), pkt->cmdString(), pkt->getAddr());
28412137Sar4jc@virginia.edu        return false;
28512137Sar4jc@virginia.edu    }
28612137Sar4jc@virginia.edu
28712137Sar4jc@virginia.edu    DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x\n",
28812137Sar4jc@virginia.edu            src_port->name(), pkt->cmdString(), pkt->getAddr());
28912137Sar4jc@virginia.edu
29012137Sar4jc@virginia.edu    calcPacketTiming(pkt);
29112137Sar4jc@virginia.edu    Tick packetFinishTime = pkt->finishTime;
29212137Sar4jc@virginia.edu
29312137Sar4jc@virginia.edu    // the packet is a normal response to a request that we should
29412137Sar4jc@virginia.edu    // have seen passing through the bus
29512137Sar4jc@virginia.edu    assert(outstandingReq.find(pkt->req) != outstandingReq.end());
29612137Sar4jc@virginia.edu
29712137Sar4jc@virginia.edu    // remove it as outstanding
29812137Sar4jc@virginia.edu    outstandingReq.erase(pkt->req);
29912137Sar4jc@virginia.edu
30012137Sar4jc@virginia.edu    // send the packet to the destination through one of our slave
30112137Sar4jc@virginia.edu    // ports, as determined by the destination field
30212137Sar4jc@virginia.edu    bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
30312137Sar4jc@virginia.edu
30412137Sar4jc@virginia.edu    // currently it is illegal to block responses... can lead to
30512137Sar4jc@virginia.edu    // deadlock
30612137Sar4jc@virginia.edu    assert(success);
30712137Sar4jc@virginia.edu
30812137Sar4jc@virginia.edu    succeededTiming(packetFinishTime);
30912137Sar4jc@virginia.edu
31012137Sar4jc@virginia.edu    return true;
31112137Sar4jc@virginia.edu}
31212137Sar4jc@virginia.edu
31312137Sar4jc@virginia.eduvoid
31412137Sar4jc@virginia.eduBus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
31512137Sar4jc@virginia.edu{
31612137Sar4jc@virginia.edu    DPRINTF(Bus, "recvTimingSnoopReq: src %s %s 0x%x\n",
31712137Sar4jc@virginia.edu            masterPorts[master_port_id]->name(), pkt->cmdString(),
31812137Sar4jc@virginia.edu            pkt->getAddr());
31912137Sar4jc@virginia.edu
32012137Sar4jc@virginia.edu    // we should only see express snoops from caches
32112137Sar4jc@virginia.edu    assert(pkt->isExpressSnoop());
32212137Sar4jc@virginia.edu
32312137Sar4jc@virginia.edu    // set the source port for routing of the response
32412137Sar4jc@virginia.edu    pkt->setSrc(master_port_id);
32512137Sar4jc@virginia.edu
32612137Sar4jc@virginia.edu    // forward to all snoopers
32712137Sar4jc@virginia.edu    forwardTiming(pkt, InvalidPortID);
32812137Sar4jc@virginia.edu
32912137Sar4jc@virginia.edu    // a snoop request came from a connected slave device (one of
33012137Sar4jc@virginia.edu    // our master ports), and if it is not coming from the slave
33112137Sar4jc@virginia.edu    // device responsible for the address range something is
33212137Sar4jc@virginia.edu    // wrong, hence there is nothing further to do as the packet
33312137Sar4jc@virginia.edu    // would be going back to where it came from
33412137Sar4jc@virginia.edu    assert(master_port_id == findPort(pkt->getAddr()));
33512137Sar4jc@virginia.edu
33612137Sar4jc@virginia.edu    // this is an express snoop and is never forced to retry
33712137Sar4jc@virginia.edu    assert(!inRetry);
33812137Sar4jc@virginia.edu}
33912137Sar4jc@virginia.edu
34012137Sar4jc@virginia.edubool
34112137Sar4jc@virginia.eduBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
34212137Sar4jc@virginia.edu{
34312137Sar4jc@virginia.edu    // determine the source port based on the id
34412137Sar4jc@virginia.edu    SlavePort* src_port = slavePorts[slave_port_id];
34512137Sar4jc@virginia.edu
34612137Sar4jc@virginia.edu    if (isOccupied(pkt, src_port)) {
34712137Sar4jc@virginia.edu        DPRINTF(Bus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
34812137Sar4jc@virginia.edu                src_port->name(), pkt->cmdString(), pkt->getAddr());
34912137Sar4jc@virginia.edu        return false;
35012137Sar4jc@virginia.edu    }
35112137Sar4jc@virginia.edu
35212137Sar4jc@virginia.edu    DPRINTF(Bus, "recvTimingSnoop: src %s %s 0x%x\n",
35312137Sar4jc@virginia.edu            src_port->name(), pkt->cmdString(), pkt->getAddr());
35412137Sar4jc@virginia.edu
35512137Sar4jc@virginia.edu    // get the destination from the packet
35612137Sar4jc@virginia.edu    PortID dest = pkt->getDest();
35712137Sar4jc@virginia.edu
35812137Sar4jc@virginia.edu    // responses are never express snoops
35912137Sar4jc@virginia.edu    assert(!pkt->isExpressSnoop());
36012137Sar4jc@virginia.edu
36112137Sar4jc@virginia.edu    calcPacketTiming(pkt);
36212137Sar4jc@virginia.edu    Tick packetFinishTime = pkt->finishTime;
36312137Sar4jc@virginia.edu
36412137Sar4jc@virginia.edu    // determine if the response is from a snoop request we
36512137Sar4jc@virginia.edu    // created as the result of a normal request (in which case it
36612137Sar4jc@virginia.edu    // should be in the outstandingReq), or if we merely forwarded
36712137Sar4jc@virginia.edu    // someone else's snoop request
36812137Sar4jc@virginia.edu    if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
36912137Sar4jc@virginia.edu        // this is a snoop response to a snoop request we
37012137Sar4jc@virginia.edu        // forwarded, e.g. coming from the L1 and going to the L2
37112137Sar4jc@virginia.edu        // this should be forwarded as a snoop response
37212137Sar4jc@virginia.edu        bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
37312137Sar4jc@virginia.edu        assert(success);
37412137Sar4jc@virginia.edu    } else {
37512137Sar4jc@virginia.edu        // we got a snoop response on one of our slave ports,
37612137Sar4jc@virginia.edu        // i.e. from a coherent master connected to the bus, and
37712137Sar4jc@virginia.edu        // since we created the snoop request as part of
37812137Sar4jc@virginia.edu        // recvTiming, this should now be a normal response again
37912137Sar4jc@virginia.edu        outstandingReq.erase(pkt->req);
38012137Sar4jc@virginia.edu
38112137Sar4jc@virginia.edu        // this is a snoop response from a coherent master, with a
38212137Sar4jc@virginia.edu        // destination field set on its way through the bus as
38312137Sar4jc@virginia.edu        // request, hence it should never go back to where the
38412137Sar4jc@virginia.edu        // snoop response came from, but instead to where the
38512137Sar4jc@virginia.edu        // original request came from
38612137Sar4jc@virginia.edu        assert(slave_port_id != dest);
38712137Sar4jc@virginia.edu
38812137Sar4jc@virginia.edu        // as a normal response, it should go back to a master
38912137Sar4jc@virginia.edu        // through one of our slave ports
39012137Sar4jc@virginia.edu        bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
39112137Sar4jc@virginia.edu
39212137Sar4jc@virginia.edu        // currently it is illegal to block responses... can lead
39312137Sar4jc@virginia.edu        // to deadlock
39412137Sar4jc@virginia.edu        assert(success);
39512137Sar4jc@virginia.edu    }
39612137Sar4jc@virginia.edu
39712137Sar4jc@virginia.edu    succeededTiming(packetFinishTime);
39812137Sar4jc@virginia.edu
39912137Sar4jc@virginia.edu    return true;
40012137Sar4jc@virginia.edu}
40112137Sar4jc@virginia.edu
40212137Sar4jc@virginia.edu
40312137Sar4jc@virginia.eduvoid
40412137Sar4jc@virginia.eduBus::succeededTiming(Tick busy_time)
40512137Sar4jc@virginia.edu{
40612137Sar4jc@virginia.edu    // occupy the bus accordingly
40712137Sar4jc@virginia.edu    occupyBus(busy_time);
40812137Sar4jc@virginia.edu
40912137Sar4jc@virginia.edu    // if a retrying port succeeded, also take it off the retry list
41012137Sar4jc@virginia.edu    if (inRetry) {
41112137Sar4jc@virginia.edu        DPRINTF(Bus, "Remove retry from list %s\n",
41212137Sar4jc@virginia.edu                retryList.front()->name());
41312137Sar4jc@virginia.edu        retryList.pop_front();
41412137Sar4jc@virginia.edu        inRetry = false;
41512137Sar4jc@virginia.edu    }
41612137Sar4jc@virginia.edu}
41712137Sar4jc@virginia.edu
41812137Sar4jc@virginia.eduvoid
41912137Sar4jc@virginia.eduBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
42012137Sar4jc@virginia.edu{
42112137Sar4jc@virginia.edu    for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
42212137Sar4jc@virginia.edu        SlavePort *p = *s;
42312137Sar4jc@virginia.edu        // we could have gotten this request from a snooping master
42412137Sar4jc@virginia.edu        // (corresponding to our own slave port that is also in
42512137Sar4jc@virginia.edu        // snoopPorts) and should not send it back to where it came
42612137Sar4jc@virginia.edu        // from
42712137Sar4jc@virginia.edu        if (exclude_slave_port_id == InvalidPortID ||
42812137Sar4jc@virginia.edu            p->getId() != exclude_slave_port_id) {
42912137Sar4jc@virginia.edu            // cache is not allowed to refuse snoop
43012137Sar4jc@virginia.edu            p->sendTimingSnoopReq(pkt);
43112137Sar4jc@virginia.edu        }
43212137Sar4jc@virginia.edu    }
43312137Sar4jc@virginia.edu}
43412137Sar4jc@virginia.edu
43512137Sar4jc@virginia.eduvoid
43612137Sar4jc@virginia.eduBus::releaseBus()
43712137Sar4jc@virginia.edu{
43812137Sar4jc@virginia.edu    // releasing the bus means we should now be idle
43912137Sar4jc@virginia.edu    assert(curTick() >= tickNextIdle);
44012137Sar4jc@virginia.edu
44112137Sar4jc@virginia.edu    // bus is now idle, so if someone is waiting we can retry
44212137Sar4jc@virginia.edu    if (!retryList.empty()) {
44312137Sar4jc@virginia.edu        // note that we block (return false on recvTiming) both
44412137Sar4jc@virginia.edu        // because the bus is busy and because the destination is
44512137Sar4jc@virginia.edu        // busy, and in the latter case the bus may be released before
44612137Sar4jc@virginia.edu        // we see a retry from the destination
44712137Sar4jc@virginia.edu        retryWaiting();
44812137Sar4jc@virginia.edu    }
44912137Sar4jc@virginia.edu
45012137Sar4jc@virginia.edu    //If we weren't able to drain before, we might be able to now.
45112137Sar4jc@virginia.edu    if (drainEvent && retryList.empty() && curTick() >= tickNextIdle) {
45212137Sar4jc@virginia.edu        drainEvent->process();
45312137Sar4jc@virginia.edu        // Clear the drain event once we're done with it.
45412137Sar4jc@virginia.edu        drainEvent = NULL;
45512137Sar4jc@virginia.edu    }
45612137Sar4jc@virginia.edu}
45712137Sar4jc@virginia.edu
45812137Sar4jc@virginia.eduvoid
45912137Sar4jc@virginia.eduBus::retryWaiting()
46012137Sar4jc@virginia.edu{
46112137Sar4jc@virginia.edu    // this should never be called with an empty retry list
46212137Sar4jc@virginia.edu    assert(!retryList.empty());
46312137Sar4jc@virginia.edu
46412137Sar4jc@virginia.edu    // send a retry to the port at the head of the retry list
46512137Sar4jc@virginia.edu    inRetry = true;
46612137Sar4jc@virginia.edu
46712137Sar4jc@virginia.edu    // note that we might have blocked on the receiving port being
46812137Sar4jc@virginia.edu    // busy (rather than the bus itself) and now call retry before the
46912137Sar4jc@virginia.edu    // destination called retry on the bus
47012137Sar4jc@virginia.edu    retryList.front()->sendRetry();
47112137Sar4jc@virginia.edu
47212137Sar4jc@virginia.edu    // If inRetry is still true, sendTiming wasn't called in zero time
47312137Sar4jc@virginia.edu    // (e.g. the cache does this)
47412137Sar4jc@virginia.edu    if (inRetry) {
47512137Sar4jc@virginia.edu        retryList.pop_front();
47612137Sar4jc@virginia.edu        inRetry = false;
47712137Sar4jc@virginia.edu
47812137Sar4jc@virginia.edu        //Bring tickNextIdle up to the present
47912137Sar4jc@virginia.edu        while (tickNextIdle < curTick())
48012137Sar4jc@virginia.edu            tickNextIdle += clock;
48112137Sar4jc@virginia.edu
48212137Sar4jc@virginia.edu        //Burn a cycle for the missed grant.
48312137Sar4jc@virginia.edu        tickNextIdle += clock;
48412137Sar4jc@virginia.edu
48512137Sar4jc@virginia.edu        reschedule(busIdleEvent, tickNextIdle, true);
48612137Sar4jc@virginia.edu    }
48712137Sar4jc@virginia.edu}
48812137Sar4jc@virginia.edu
48912137Sar4jc@virginia.eduvoid
49012137Sar4jc@virginia.eduBus::recvRetry()
49112137Sar4jc@virginia.edu{
49212137Sar4jc@virginia.edu    // we got a retry from a peer that we tried to send something to
49312137Sar4jc@virginia.edu    // and failed, but we sent it on the account of someone else, and
49412137Sar4jc@virginia.edu    // that source port should be on our retry list, however if the
49512137Sar4jc@virginia.edu    // bus is released before this happens and the retry (from the bus
49612137Sar4jc@virginia.edu    // point of view) is successful then this no longer holds and we
49712137Sar4jc@virginia.edu    // could in fact have an empty retry list
49812137Sar4jc@virginia.edu    if (retryList.empty())
49912137Sar4jc@virginia.edu        return;
50012137Sar4jc@virginia.edu
50112137Sar4jc@virginia.edu    // if the bus isn't busy
50212137Sar4jc@virginia.edu    if (curTick() >= tickNextIdle) {
50312137Sar4jc@virginia.edu        // note that we do not care who told us to retry at the moment, we
50412137Sar4jc@virginia.edu        // merely let the first one on the retry list go
50512137Sar4jc@virginia.edu        retryWaiting();
50612137Sar4jc@virginia.edu    }
50712137Sar4jc@virginia.edu}
50812137Sar4jc@virginia.edu
50912137Sar4jc@virginia.eduPortID
51012137Sar4jc@virginia.eduBus::findPort(Addr addr)
51112137Sar4jc@virginia.edu{
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