xbar.cc revision 8851
112339Sjason@lowepower.com/* 212339Sjason@lowepower.com * Copyright (c) 2011-2012 ARM Limited 312339Sjason@lowepower.com * All rights reserved 412339Sjason@lowepower.com * 512339Sjason@lowepower.com * The license below extends only to copyright in the software and shall 612339Sjason@lowepower.com * not be construed as granting a license to any other intellectual 712339Sjason@lowepower.com * property including but not limited to intellectual property relating 812339Sjason@lowepower.com * to a hardware implementation of the functionality of the software 912339Sjason@lowepower.com * licensed hereunder. You may use the software subject to the license 1012339Sjason@lowepower.com * terms below provided that you ensure that this notice is replicated 1112339Sjason@lowepower.com * unmodified and in its entirety in all distributions of the software, 1212339Sjason@lowepower.com * modified or unmodified, in source code or in binary form. 1312339Sjason@lowepower.com * 1412339Sjason@lowepower.com * Copyright (c) 2006 The Regents of The University of Michigan 1512339Sjason@lowepower.com * All rights reserved. 1612339Sjason@lowepower.com * 1712339Sjason@lowepower.com * Redistribution and use in source and binary forms, with or without 1812339Sjason@lowepower.com * modification, are permitted provided that the following conditions are 1912339Sjason@lowepower.com * met: redistributions of source code must retain the above copyright 2012339Sjason@lowepower.com * notice, this list of conditions and the following disclaimer; 2112339Sjason@lowepower.com * redistributions in binary form must reproduce the above copyright 2212339Sjason@lowepower.com * notice, this list of conditions and the following disclaimer in the 2312339Sjason@lowepower.com * documentation and/or other materials provided with the distribution; 2412339Sjason@lowepower.com * neither the name of the copyright holders nor the names of its 2512339Sjason@lowepower.com * contributors may be used to endorse or promote products derived from 2612339Sjason@lowepower.com * this software without specific prior written permission. 2712339Sjason@lowepower.com * 2812339Sjason@lowepower.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2912339Sjason@lowepower.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3012339Sjason@lowepower.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3112339Sjason@lowepower.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3212339Sjason@lowepower.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3312339Sjason@lowepower.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3412339Sjason@lowepower.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3512339Sjason@lowepower.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3612339Sjason@lowepower.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3712339Sjason@lowepower.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3812339Sjason@lowepower.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3912339Sjason@lowepower.com * 4012339Sjason@lowepower.com * Authors: Ali Saidi 4112339Sjason@lowepower.com * Andreas Hansson 4212339Sjason@lowepower.com */ 4312339Sjason@lowepower.com 4412339Sjason@lowepower.com/** 4512339Sjason@lowepower.com * @file 4612339Sjason@lowepower.com * Definition of a bus object. 4712339Sjason@lowepower.com */ 48 49#include "base/misc.hh" 50#include "base/trace.hh" 51#include "debug/Bus.hh" 52#include "debug/BusAddrRanges.hh" 53#include "debug/MMU.hh" 54#include "mem/bus.hh" 55 56Bus::Bus(const BusParams *p) 57 : MemObject(p), busId(p->bus_id), clock(p->clock), 58 headerCycles(p->header_cycles), width(p->width), tickNextIdle(0), 59 drainEvent(NULL), busIdle(this), inRetry(false), 60 nbrMasterPorts(p->port_master_connection_count), 61 defaultPortId(INVALID_PORT_ID), useDefaultRange(p->use_default_range), 62 defaultBlockSize(p->block_size), 63 cachedBlockSize(0), cachedBlockSizeValid(false) 64{ 65 //width, clock period, and header cycles must be positive 66 if (width <= 0) 67 fatal("Bus width must be positive\n"); 68 if (clock <= 0) 69 fatal("Bus clock period must be positive\n"); 70 if (headerCycles <= 0) 71 fatal("Number of header cycles must be positive\n"); 72 73 // create the ports based on the size of the master and slave 74 // vector ports, and the presence of the default master 75 76 // id used to index into interfaces which is a flat vector of all 77 // ports 78 int id = 0; 79 for (int i = 0; i < p->port_master_connection_count; ++i) { 80 std::string portName = csprintf("%s-p%d", name(), id); 81 interfaces.push_back(new BusPort(portName, this, id)); 82 ++id; 83 } 84 85 // note that the first slave port is now stored on index 86 // nbrMasterPorts in the vector 87 for (int i = 0; i < p->port_slave_connection_count; ++i) { 88 std::string portName = csprintf("%s-p%d", name(), id); 89 interfaces.push_back(new BusPort(portName, this, id)); 90 ++id; 91 } 92 93 // see if we have a default master connected and if so add the 94 // port at the end 95 if (p->port_default_connection_count) { 96 defaultPortId = id; 97 std::string portName = csprintf("%s-default", name()); 98 interfaces.push_back(new BusPort(portName, this, id)); 99 ++id; 100 } 101 102 clearPortCache(); 103} 104 105Port * 106Bus::getPort(const std::string &if_name, int idx) 107{ 108 if (if_name == "master") { 109 // the master index translates directly to the interfaces 110 // vector as they are stored first 111 return interfaces[idx]; 112 } else if (if_name == "slave") { 113 // the slaves are stored after the masters and we must thus 114 // offset the slave index with the number of master ports 115 return interfaces[nbrMasterPorts + idx]; 116 } else if (if_name == "default") { 117 return interfaces[defaultPortId]; 118 } else { 119 panic("No port %s %d on bus %s\n", if_name, idx, name()); 120 } 121} 122 123void 124Bus::init() 125{ 126 std::vector<BusPort*>::iterator intIter; 127 128 // iterate over our interfaces and determine which of our neighbours 129 // are snooping and add them as snoopers 130 for (intIter = interfaces.begin(); intIter != interfaces.end(); 131 intIter++) { 132 if ((*intIter)->getPeer()->isSnooping()) { 133 DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n", 134 (*intIter)->getPeer()->name()); 135 snoopPorts.push_back(*intIter); 136 } 137 } 138} 139 140Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) 141 : bus(_bus) 142{} 143 144void 145Bus::BusFreeEvent::process() 146{ 147 bus->recvRetry(-1); 148} 149 150const char * 151Bus::BusFreeEvent::description() const 152{ 153 return "bus became available"; 154} 155 156Tick 157Bus::calcPacketTiming(PacketPtr pkt) 158{ 159 // Bring tickNextIdle up to the present tick. 160 // There is some potential ambiguity where a cycle starts, which 161 // might make a difference when devices are acting right around a 162 // cycle boundary. Using a < allows things which happen exactly on 163 // a cycle boundary to take up only the following cycle. Anything 164 // that happens later will have to "wait" for the end of that 165 // cycle, and then start using the bus after that. 166 if (tickNextIdle < curTick()) { 167 tickNextIdle = curTick(); 168 if (tickNextIdle % clock != 0) 169 tickNextIdle = curTick() - (curTick() % clock) + clock; 170 } 171 172 Tick headerTime = tickNextIdle + headerCycles * clock; 173 174 // The packet will be sent. Figure out how long it occupies the bus, and 175 // how much of that time is for the first "word", aka bus width. 176 int numCycles = 0; 177 if (pkt->hasData()) { 178 // If a packet has data, it needs ceil(size/width) cycles to send it 179 int dataSize = pkt->getSize(); 180 numCycles += dataSize/width; 181 if (dataSize % width) 182 numCycles++; 183 } 184 185 // The first word will be delivered after the current tick, the delivery 186 // of the address if any, and one bus cycle to deliver the data 187 pkt->firstWordTime = headerTime + clock; 188 189 pkt->finishTime = headerTime + numCycles * clock; 190 191 return headerTime; 192} 193 194void Bus::occupyBus(Tick until) 195{ 196 if (until == 0) { 197 // shortcut for express snoop packets 198 return; 199 } 200 201 tickNextIdle = until; 202 reschedule(busIdle, tickNextIdle, true); 203 204 DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n", 205 curTick(), tickNextIdle); 206} 207 208/** Function called by the port when the bus is receiving a Timing 209 * transaction.*/ 210bool 211Bus::recvTiming(PacketPtr pkt) 212{ 213 short src = pkt->getSrc(); 214 215 BusPort *src_port = interfaces[src]; 216 217 // If the bus is busy, or other devices are in line ahead of the current 218 // one, put this device on the retry list. 219 if (!pkt->isExpressSnoop() && 220 (tickNextIdle > curTick() || 221 (retryList.size() && (!inRetry || src_port != retryList.front())))) 222 { 223 addToRetryList(src_port); 224 DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x BUSY\n", 225 src, pkt->getDest(), pkt->cmdString(), pkt->getAddr()); 226 return false; 227 } 228 229 DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x\n", 230 src, pkt->getDest(), pkt->cmdString(), pkt->getAddr()); 231 232 Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt); 233 Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime; 234 235 short dest = pkt->getDest(); 236 int dest_port_id; 237 Port *dest_port; 238 239 if (dest == Packet::Broadcast) { 240 dest_port_id = findPort(pkt->getAddr()); 241 dest_port = interfaces[dest_port_id]; 242 SnoopIter s_end = snoopPorts.end(); 243 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 244 BusPort *p = *s_iter; 245 if (p != dest_port && p != src_port) { 246 // cache is not allowed to refuse snoop 247 bool success M5_VAR_USED = p->sendTiming(pkt); 248 assert(success); 249 } 250 } 251 } else { 252 assert(dest < interfaces.size()); 253 assert(dest != src); // catch infinite loops 254 dest_port_id = dest; 255 dest_port = interfaces[dest_port_id]; 256 } 257 258 if (dest_port_id == src) { 259 // Must be forwarded snoop up from below... 260 assert(dest == Packet::Broadcast); 261 } else { 262 // send to actual target 263 if (!dest_port->sendTiming(pkt)) { 264 // Packet not successfully sent. Leave or put it on the retry list. 265 // illegal to block responses... can lead to deadlock 266 assert(!pkt->isResponse()); 267 // It's also illegal to force a transaction to retry after 268 // someone else has committed to respond. 269 assert(!pkt->memInhibitAsserted()); 270 DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x TGT RETRY\n", 271 src, pkt->getDest(), pkt->cmdString(), pkt->getAddr()); 272 addToRetryList(src_port); 273 occupyBus(headerFinishTime); 274 return false; 275 } 276 // send OK, fall through... pkt may have been deleted by 277 // target at this point, so it should *not* be referenced 278 // again. We'll set it to NULL here just to be safe. 279 pkt = NULL; 280 } 281 282 occupyBus(packetFinishTime); 283 284 // Packet was successfully sent. 285 // Also take care of retries 286 if (inRetry) { 287 DPRINTF(Bus, "Remove retry from list %d\n", src); 288 retryList.pop_front(); 289 inRetry = false; 290 } 291 return true; 292} 293 294void 295Bus::recvRetry(int id) 296{ 297 // If there's anything waiting, and the bus isn't busy... 298 if (retryList.size() && curTick() >= tickNextIdle) { 299 //retryingPort = retryList.front(); 300 inRetry = true; 301 DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name()); 302 retryList.front()->sendRetry(); 303 // If inRetry is still true, sendTiming wasn't called 304 if (inRetry) 305 { 306 retryList.pop_front(); 307 inRetry = false; 308 309 //Bring tickNextIdle up to the present 310 while (tickNextIdle < curTick()) 311 tickNextIdle += clock; 312 313 //Burn a cycle for the missed grant. 314 tickNextIdle += clock; 315 316 reschedule(busIdle, tickNextIdle, true); 317 } 318 } 319 //If we weren't able to drain before, we might be able to now. 320 if (drainEvent && retryList.size() == 0 && curTick() >= tickNextIdle) { 321 drainEvent->process(); 322 // Clear the drain event once we're done with it. 323 drainEvent = NULL; 324 } 325} 326 327int 328Bus::findPort(Addr addr) 329{ 330 /* An interval tree would be a better way to do this. --ali. */ 331 int dest_id; 332 333 dest_id = checkPortCache(addr); 334 if (dest_id != INVALID_PORT_ID) 335 return dest_id; 336 337 // Check normal port ranges 338 PortIter i = portMap.find(RangeSize(addr,1)); 339 if (i != portMap.end()) { 340 dest_id = i->second; 341 updatePortCache(dest_id, i->first.start, i->first.end); 342 return dest_id; 343 } 344 345 // Check if this matches the default range 346 if (useDefaultRange) { 347 AddrRangeIter a_end = defaultRange.end(); 348 for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) { 349 if (*i == addr) { 350 DPRINTF(Bus, " found addr %#llx on default\n", addr); 351 return defaultPortId; 352 } 353 } 354 } else if (defaultPortId != INVALID_PORT_ID) { 355 DPRINTF(Bus, "Unable to find destination for addr %#llx, " 356 "will use default port\n", addr); 357 return defaultPortId; 358 } 359 360 // we should use the range for the default port and it did not 361 // match, or the default port is not set 362 fatal("Unable to find destination for addr %#llx on bus %s\n", addr, 363 name()); 364} 365 366 367/** Function called by the port when the bus is receiving a Atomic 368 * transaction.*/ 369Tick 370Bus::recvAtomic(PacketPtr pkt) 371{ 372 DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", 373 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 374 assert(pkt->getDest() == Packet::Broadcast); 375 assert(pkt->isRequest()); 376 377 // Variables for recording original command and snoop response (if 378 // any)... if a snooper respondes, we will need to restore 379 // original command so that additional snoops can take place 380 // properly 381 MemCmd orig_cmd = pkt->cmd; 382 MemCmd snoop_response_cmd = MemCmd::InvalidCmd; 383 Tick snoop_response_latency = 0; 384 int orig_src = pkt->getSrc(); 385 386 int target_port_id = findPort(pkt->getAddr()); 387 BusPort *target_port = interfaces[target_port_id]; 388 389 SnoopIter s_end = snoopPorts.end(); 390 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 391 BusPort *p = *s_iter; 392 // same port should not have both target addresses and snooping 393 assert(p != target_port); 394 if (p->getId() != pkt->getSrc()) { 395 Tick latency = p->sendAtomic(pkt); 396 if (pkt->isResponse()) { 397 // response from snoop agent 398 assert(pkt->cmd != orig_cmd); 399 assert(pkt->memInhibitAsserted()); 400 // should only happen once 401 assert(snoop_response_cmd == MemCmd::InvalidCmd); 402 // save response state 403 snoop_response_cmd = pkt->cmd; 404 snoop_response_latency = latency; 405 // restore original packet state for remaining snoopers 406 pkt->cmd = orig_cmd; 407 pkt->setSrc(orig_src); 408 pkt->setDest(Packet::Broadcast); 409 } 410 } 411 } 412 413 Tick response_latency = 0; 414 415 // we can get requests sent up from the memory side of the bus for 416 // snooping... don't send them back down! 417 if (target_port_id != pkt->getSrc()) { 418 response_latency = target_port->sendAtomic(pkt); 419 } 420 421 // if we got a response from a snooper, restore it here 422 if (snoop_response_cmd != MemCmd::InvalidCmd) { 423 // no one else should have responded 424 assert(!pkt->isResponse()); 425 assert(pkt->cmd == orig_cmd); 426 pkt->cmd = snoop_response_cmd; 427 response_latency = snoop_response_latency; 428 } 429 430 // why do we have this packet field and the return value both??? 431 pkt->finishTime = curTick() + response_latency; 432 return response_latency; 433} 434 435/** Function called by the port when the bus is receiving a Functional 436 * transaction.*/ 437void 438Bus::recvFunctional(PacketPtr pkt) 439{ 440 assert(pkt->getDest() == Packet::Broadcast); 441 442 int port_id = findPort(pkt->getAddr()); 443 Port *port = interfaces[port_id]; 444 // The packet may be changed by another bus on snoops, restore the 445 // id after each 446 int src_id = pkt->getSrc(); 447 448 if (!pkt->isPrint()) { 449 // don't do DPRINTFs on PrintReq as it clutters up the output 450 DPRINTF(Bus, 451 "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", 452 src_id, port_id, pkt->getAddr(), 453 pkt->cmdString()); 454 } 455 456 assert(pkt->isRequest()); // hasn't already been satisfied 457 458 SnoopIter s_end = snoopPorts.end(); 459 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 460 BusPort *p = *s_iter; 461 if (p != port && p->getId() != src_id) { 462 p->sendFunctional(pkt); 463 } 464 if (pkt->isResponse()) { 465 break; 466 } 467 pkt->setSrc(src_id); 468 } 469 470 // If the snooping hasn't found what we were looking for and it is not 471 // a forwarded snoop from below, keep going. 472 if (!pkt->isResponse() && port_id != pkt->getSrc()) { 473 port->sendFunctional(pkt); 474 } 475} 476 477/** Function called by the port when the bus is receiving a range change.*/ 478void 479Bus::recvRangeChange(int id) 480{ 481 AddrRangeList ranges; 482 AddrRangeIter iter; 483 484 if (inRecvRangeChange.count(id)) 485 return; 486 inRecvRangeChange.insert(id); 487 488 DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); 489 490 clearPortCache(); 491 if (id == defaultPortId) { 492 defaultRange.clear(); 493 // Only try to update these ranges if the user set a default responder. 494 if (useDefaultRange) { 495 AddrRangeList ranges = interfaces[id]->getPeer()->getAddrRanges(); 496 for(iter = ranges.begin(); iter != ranges.end(); iter++) { 497 defaultRange.push_back(*iter); 498 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", 499 iter->start, iter->end); 500 } 501 } 502 } else { 503 504 assert(id < interfaces.size() && id >= 0); 505 BusPort *port = interfaces[id]; 506 507 // Clean out any previously existent ids 508 for (PortIter portIter = portMap.begin(); 509 portIter != portMap.end(); ) { 510 if (portIter->second == id) 511 portMap.erase(portIter++); 512 else 513 portIter++; 514 } 515 516 ranges = port->getPeer()->getAddrRanges(); 517 518 for (iter = ranges.begin(); iter != ranges.end(); iter++) { 519 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", 520 iter->start, iter->end, id); 521 if (portMap.insert(*iter, id) == portMap.end()) { 522 int conflict_id = portMap.find(*iter)->second; 523 fatal("%s has two ports with same range:\n\t%s\n\t%s\n", 524 name(), interfaces[id]->getPeer()->name(), 525 interfaces[conflict_id]->getPeer()->name()); 526 } 527 } 528 } 529 DPRINTF(MMU, "port list has %d entries\n", portMap.size()); 530 531 // tell all our peers that our address range has changed. 532 // Don't tell the device that caused this change, it already knows 533 std::vector<BusPort*>::const_iterator intIter; 534 535 for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) 536 if ((*intIter)->getId() != id) 537 (*intIter)->sendRangeChange(); 538 539 inRecvRangeChange.erase(id); 540} 541 542AddrRangeList 543Bus::getAddrRanges(int id) 544{ 545 AddrRangeList ranges; 546 547 DPRINTF(BusAddrRanges, "received address range request, returning:\n"); 548 549 for (AddrRangeIter dflt_iter = defaultRange.begin(); 550 dflt_iter != defaultRange.end(); dflt_iter++) { 551 ranges.push_back(*dflt_iter); 552 DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start, 553 dflt_iter->end); 554 } 555 for (PortIter portIter = portMap.begin(); 556 portIter != portMap.end(); portIter++) { 557 bool subset = false; 558 for (AddrRangeIter dflt_iter = defaultRange.begin(); 559 dflt_iter != defaultRange.end(); dflt_iter++) { 560 if ((portIter->first.start < dflt_iter->start && 561 portIter->first.end >= dflt_iter->start) || 562 (portIter->first.start < dflt_iter->end && 563 portIter->first.end >= dflt_iter->end)) 564 fatal("Devices can not set ranges that itersect the default set\ 565 but are not a subset of the default set.\n"); 566 if (portIter->first.start >= dflt_iter->start && 567 portIter->first.end <= dflt_iter->end) { 568 subset = true; 569 DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", 570 portIter->first.start, portIter->first.end); 571 } 572 } 573 if (portIter->second != id && !subset) { 574 ranges.push_back(portIter->first); 575 DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", 576 portIter->first.start, portIter->first.end); 577 } 578 } 579 580 return ranges; 581} 582 583bool 584Bus::isSnooping(int id) 585{ 586 // in essence, answer the question if there are other snooping 587 // ports rather than the port that is asking 588 bool snoop = false; 589 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end(); 590 s_iter++) { 591 if ((*s_iter)->getId() != id) { 592 snoop = true; 593 break; 594 } 595 } 596 return snoop; 597} 598 599unsigned 600Bus::findBlockSize(int id) 601{ 602 if (cachedBlockSizeValid) 603 return cachedBlockSize; 604 605 unsigned max_bs = 0; 606 607 PortIter p_end = portMap.end(); 608 for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) { 609 unsigned tmp_bs = interfaces[p_iter->second]->peerBlockSize(); 610 if (tmp_bs > max_bs) 611 max_bs = tmp_bs; 612 } 613 SnoopIter s_end = snoopPorts.end(); 614 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 615 unsigned tmp_bs = (*s_iter)->peerBlockSize(); 616 if (tmp_bs > max_bs) 617 max_bs = tmp_bs; 618 } 619 if (max_bs == 0) 620 max_bs = defaultBlockSize; 621 622 if (max_bs != 64) 623 warn_once("Blocksize found to not be 64... hmm... probably not.\n"); 624 cachedBlockSize = max_bs; 625 cachedBlockSizeValid = true; 626 return max_bs; 627} 628 629 630unsigned int 631Bus::drain(Event * de) 632{ 633 //We should check that we're not "doing" anything, and that noone is 634 //waiting. We might be idle but have someone waiting if the device we 635 //contacted for a retry didn't actually retry. 636 if (retryList.size() || (curTick() < tickNextIdle && busIdle.scheduled())) { 637 drainEvent = de; 638 return 1; 639 } 640 return 0; 641} 642 643void 644Bus::startup() 645{ 646 if (tickNextIdle < curTick()) 647 tickNextIdle = (curTick() / clock) * clock + clock; 648} 649 650Bus * 651BusParams::create() 652{ 653 return new Bus(this); 654} 655