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