xbar.cc revision 5057
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() 109{ 110 return "bus became available"; 111} 112 113void Bus::occupyBus(PacketPtr pkt) 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 // The packet will be sent. Figure out how long it occupies the bus, and 128 // how much of that time is for the first "word", aka bus width. 129 int numCycles = 0; 130 // Requests need one cycle to send an address 131 if (pkt->isRequest()) 132 numCycles++; 133 else if (pkt->isResponse() || pkt->hasData()) { 134 // If a packet has data, it needs ceil(size/width) cycles to send it 135 // We're using the "adding instead of dividing" trick again here 136 if (pkt->hasData()) { 137 int dataSize = pkt->getSize(); 138 numCycles += dataSize/width; 139 if (dataSize % width) 140 numCycles++; 141 } else { 142 // If the packet didn't have data, it must have been a response. 143 // Those use the bus for one cycle to send their data. 144 numCycles++; 145 } 146 } 147 148 // The first word will be delivered after the current tick, the delivery 149 // of the address if any, and one bus cycle to deliver the data 150 pkt->firstWordTime = tickNextIdle + (pkt->isRequest() ? clock : 0) + clock; 151 152 //Advance it numCycles bus cycles. 153 //XXX Should this use the repeated addition trick as well? 154 tickNextIdle += (numCycles * clock); 155 if (!busIdle.scheduled()) { 156 busIdle.schedule(tickNextIdle); 157 } else { 158 busIdle.reschedule(tickNextIdle); 159 } 160 DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n", 161 curTick, tickNextIdle); 162 163 // The bus will become idle once the current packet is delivered. 164 pkt->finishTime = tickNextIdle; 165} 166 167/** Function called by the port when the bus is receiving a Timing 168 * transaction.*/ 169bool 170Bus::recvTiming(PacketPtr pkt) 171{ 172 short src = pkt->getSrc(); 173 DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n", 174 src, pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 175 176 BusPort *src_port; 177 if (src == defaultId) 178 src_port = defaultPort; 179 else { 180 src_port = checkBusCache(src); 181 if (src_port == NULL) { 182 src_port = interfaces[src]; 183 updateBusCache(src, src_port); 184 } 185 } 186 187 // If the bus is busy, or other devices are in line ahead of the current 188 // one, put this device on the retry list. 189 if (!pkt->isExpressSnoop() && 190 (tickNextIdle > curTick || 191 (retryList.size() && (!inRetry || src_port != retryList.front())))) 192 { 193 addToRetryList(src_port); 194 DPRINTF(Bus, "recvTiming: Bus is busy, returning false\n"); 195 return false; 196 } 197 198 if (!pkt->isExpressSnoop()) { 199 occupyBus(pkt); 200 } 201 202 short dest = pkt->getDest(); 203 int dest_port_id; 204 Port *dest_port; 205 206 if (dest == Packet::Broadcast) { 207 dest_port_id = findPort(pkt->getAddr()); 208 dest_port = (dest_port_id == defaultId) ? 209 defaultPort : interfaces[dest_port_id]; 210 SnoopIter s_end = snoopPorts.end(); 211 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 212 BusPort *p = *s_iter; 213 if (p != dest_port && p != src_port) { 214 // cache is not allowed to refuse snoop 215 bool success M5_VAR_USED = p->sendTiming(pkt); 216 assert(success); 217 } 218 } 219 } else { 220 assert(dest >= 0 && dest < maxId); 221 assert(dest != src); // catch infinite loops 222 dest_port_id = dest; 223 if (dest_port_id == defaultId) 224 dest_port = defaultPort; 225 else { 226 dest_port = checkBusCache(dest); 227 if (dest_port == NULL) { 228 dest_port = interfaces[dest_port_id]; 229 // updateBusCache(dest_port_id, dest_port); 230 } 231 } 232 dest_port = (dest_port_id == defaultId) ? 233 defaultPort : interfaces[dest_port_id]; 234 } 235 236 if (dest_port_id == src) { 237 // Must be forwarded snoop up from below... 238 assert(dest == Packet::Broadcast); 239 assert(src != defaultId); // catch infinite loops 240 } else { 241 // send to actual target 242 if (!dest_port->sendTiming(pkt)) { 243 // Packet not successfully sent. Leave or put it on the retry list. 244 // illegal to block responses... can lead to deadlock 245 assert(!pkt->isResponse()); 246 DPRINTF(Bus, "Adding2 a retry to RETRY list %d\n", src); 247 addToRetryList(src_port); 248 return false; 249 } 250 // send OK, fall through 251 } 252 253 // Packet was successfully sent. 254 // Also take care of retries 255 if (inRetry) { 256 DPRINTF(Bus, "Remove retry from list %d\n", src); 257 retryList.front()->onRetryList(false); 258 retryList.pop_front(); 259 inRetry = false; 260 } 261 return true; 262} 263 264void 265Bus::recvRetry(int id) 266{ 267 // If there's anything waiting, and the bus isn't busy... 268 if (retryList.size() && curTick >= tickNextIdle) { 269 //retryingPort = retryList.front(); 270 inRetry = true; 271 DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name()); 272 retryList.front()->sendRetry(); 273 // If inRetry is still true, sendTiming wasn't called 274 if (inRetry) 275 { 276 retryList.front()->onRetryList(false); 277 retryList.pop_front(); 278 inRetry = false; 279 280 //Bring tickNextIdle up to the present 281 while (tickNextIdle < curTick) 282 tickNextIdle += clock; 283 284 //Burn a cycle for the missed grant. 285 tickNextIdle += clock; 286 287 busIdle.reschedule(tickNextIdle, true); 288 } 289 } 290 //If we weren't able to drain before, we might be able to now. 291 if (drainEvent && retryList.size() == 0 && curTick >= tickNextIdle) { 292 drainEvent->process(); 293 // Clear the drain event once we're done with it. 294 drainEvent = NULL; 295 } 296} 297 298int 299Bus::findPort(Addr addr) 300{ 301 /* An interval tree would be a better way to do this. --ali. */ 302 int dest_id = -1; 303 304 dest_id = checkPortCache(addr); 305 if (dest_id == -1) { 306 PortIter i = portMap.find(RangeSize(addr,1)); 307 if (i != portMap.end()) 308 dest_id = i->second; 309 updatePortCache(dest_id, i->first.start, i->first.end); 310 } 311 312 // Check if this matches the default range 313 if (dest_id == -1) { 314 AddrRangeIter a_end = defaultRange.end(); 315 for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) { 316 if (*i == addr) { 317 DPRINTF(Bus, " found addr %#llx on default\n", addr); 318 return defaultId; 319 } 320 } 321 322 if (responderSet) { 323 panic("Unable to find destination for addr (user set default " 324 "responder): %#llx", addr); 325 } else { 326 DPRINTF(Bus, "Unable to find destination for addr: %#llx, will use " 327 "default port", addr); 328 329 return defaultId; 330 } 331 } 332 333 return dest_id; 334} 335 336 337/** Function called by the port when the bus is receiving a Atomic 338 * transaction.*/ 339Tick 340Bus::recvAtomic(PacketPtr pkt) 341{ 342 DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", 343 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 344 assert(pkt->getDest() == Packet::Broadcast); 345 assert(pkt->isRequest()); 346 347 // Variables for recording original command and snoop response (if 348 // any)... if a snooper respondes, we will need to restore 349 // original command so that additional snoops can take place 350 // properly 351 MemCmd orig_cmd = pkt->cmd; 352 MemCmd snoop_response_cmd = MemCmd::InvalidCmd; 353 Tick snoop_response_latency = 0; 354 int orig_src = pkt->getSrc(); 355 356 int target_port_id = findPort(pkt->getAddr()); 357 BusPort *target_port; 358 if (target_port_id == defaultId) 359 target_port = defaultPort; 360 else { 361 target_port = checkBusCache(target_port_id); 362 if (target_port == NULL) { 363 target_port = interfaces[target_port_id]; 364 updateBusCache(target_port_id, target_port); 365 } 366 } 367 368 SnoopIter s_end = snoopPorts.end(); 369 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 370 BusPort *p = *s_iter; 371 // same port should not have both target addresses and snooping 372 assert(p != target_port); 373 if (p->getId() != pkt->getSrc()) { 374 Tick latency = p->sendAtomic(pkt); 375 if (pkt->isResponse()) { 376 // response from snoop agent 377 assert(pkt->cmd != orig_cmd); 378 assert(pkt->memInhibitAsserted()); 379 // should only happen once 380 assert(snoop_response_cmd == MemCmd::InvalidCmd); 381 // save response state 382 snoop_response_cmd = pkt->cmd; 383 snoop_response_latency = latency; 384 // restore original packet state for remaining snoopers 385 pkt->cmd = orig_cmd; 386 pkt->setSrc(orig_src); 387 pkt->setDest(Packet::Broadcast); 388 } 389 } 390 } 391 392 Tick response_latency = 0; 393 394 // we can get requests sent up from the memory side of the bus for 395 // snooping... don't send them back down! 396 if (target_port_id != pkt->getSrc()) { 397 response_latency = target_port->sendAtomic(pkt); 398 } 399 400 // if we got a response from a snooper, restore it here 401 if (snoop_response_cmd != MemCmd::InvalidCmd) { 402 // no one else should have responded 403 assert(!pkt->isResponse()); 404 assert(pkt->cmd == orig_cmd); 405 pkt->cmd = snoop_response_cmd; 406 response_latency = snoop_response_latency; 407 } 408 409 // why do we have this packet field and the return value both??? 410 pkt->finishTime = curTick + response_latency; 411 return response_latency; 412} 413 414/** Function called by the port when the bus is receiving a Functional 415 * transaction.*/ 416void 417Bus::recvFunctional(PacketPtr pkt) 418{ 419 DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", 420 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 421 assert(pkt->getDest() == Packet::Broadcast); 422 423 int port_id = findPort(pkt->getAddr()); 424 Port *port = (port_id == defaultId) ? defaultPort : interfaces[port_id]; 425 // The packet may be changed by another bus on snoops, restore the 426 // id after each 427 int src_id = pkt->getSrc(); 428 429 assert(pkt->isRequest()); // hasn't already been satisfied 430 431 SnoopIter s_end = snoopPorts.end(); 432 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 433 BusPort *p = *s_iter; 434 if (p != port && p->getId() != src_id) { 435 p->sendFunctional(pkt); 436 } 437 if (pkt->isResponse()) { 438 break; 439 } 440 pkt->setSrc(src_id); 441 } 442 443 // If the snooping hasn't found what we were looking for, keep going. 444 if (!pkt->isResponse() && port_id != pkt->getSrc()) { 445 port->sendFunctional(pkt); 446 } 447} 448 449/** Function called by the port when the bus is receiving a status change.*/ 450void 451Bus::recvStatusChange(Port::Status status, int id) 452{ 453 AddrRangeList ranges; 454 bool snoops; 455 AddrRangeIter iter; 456 457 if (inRecvStatusChange.count(id)) 458 return; 459 inRecvStatusChange.insert(id); 460 461 assert(status == Port::RangeChange && 462 "The other statuses need to be implemented."); 463 464 DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); 465 466 clearPortCache(); 467 if (id == defaultId) { 468 defaultRange.clear(); 469 // Only try to update these ranges if the user set a default responder. 470 if (responderSet) { 471 defaultPort->getPeerAddressRanges(ranges, snoops); 472 assert(snoops == false); 473 for(iter = ranges.begin(); iter != ranges.end(); iter++) { 474 defaultRange.push_back(*iter); 475 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", 476 iter->start, iter->end); 477 } 478 } 479 } else { 480 481 assert((id < maxId && id >= 0) || id == defaultId); 482 BusPort *port = interfaces[id]; 483 484 // Clean out any previously existent ids 485 for (PortIter portIter = portMap.begin(); 486 portIter != portMap.end(); ) { 487 if (portIter->second == id) 488 portMap.erase(portIter++); 489 else 490 portIter++; 491 } 492 493 for (SnoopIter s_iter = snoopPorts.begin(); 494 s_iter != snoopPorts.end(); ) { 495 if ((*s_iter)->getId() == id) 496 s_iter = snoopPorts.erase(s_iter); 497 else 498 s_iter++; 499 } 500 501 port->getPeerAddressRanges(ranges, snoops); 502 503 if (snoops) { 504 DPRINTF(BusAddrRanges, "Adding id %d to snoop list\n", id); 505 snoopPorts.push_back(port); 506 } 507 508 for (iter = ranges.begin(); iter != ranges.end(); iter++) { 509 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", 510 iter->start, iter->end, id); 511 if (portMap.insert(*iter, id) == portMap.end()) 512 panic("Two devices with same range\n"); 513 514 } 515 } 516 DPRINTF(MMU, "port list has %d entries\n", portMap.size()); 517 518 // tell all our peers that our address range has changed. 519 // Don't tell the device that caused this change, it already knows 520 m5::hash_map<short,BusPort*>::iterator intIter; 521 522 for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) 523 if (intIter->first != id && intIter->first != funcPortId) 524 intIter->second->sendStatusChange(Port::RangeChange); 525 526 if (id != defaultId && defaultPort) 527 defaultPort->sendStatusChange(Port::RangeChange); 528 inRecvStatusChange.erase(id); 529} 530 531void 532Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id) 533{ 534 resp.clear(); 535 snoop = false; 536 537 DPRINTF(BusAddrRanges, "received address range request, returning:\n"); 538 539 for (AddrRangeIter dflt_iter = defaultRange.begin(); 540 dflt_iter != defaultRange.end(); dflt_iter++) { 541 resp.push_back(*dflt_iter); 542 DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start, 543 dflt_iter->end); 544 } 545 for (PortIter portIter = portMap.begin(); 546 portIter != portMap.end(); portIter++) { 547 bool subset = false; 548 for (AddrRangeIter dflt_iter = defaultRange.begin(); 549 dflt_iter != defaultRange.end(); dflt_iter++) { 550 if ((portIter->first.start < dflt_iter->start && 551 portIter->first.end >= dflt_iter->start) || 552 (portIter->first.start < dflt_iter->end && 553 portIter->first.end >= dflt_iter->end)) 554 fatal("Devices can not set ranges that itersect the default set\ 555 but are not a subset of the default set.\n"); 556 if (portIter->first.start >= dflt_iter->start && 557 portIter->first.end <= dflt_iter->end) { 558 subset = true; 559 DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", 560 portIter->first.start, portIter->first.end); 561 } 562 } 563 if (portIter->second != id && !subset) { 564 resp.push_back(portIter->first); 565 DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", 566 portIter->first.start, portIter->first.end); 567 } 568 } 569 570 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end(); 571 s_iter++) { 572 if ((*s_iter)->getId() != id) { 573 snoop = true; 574 break; 575 } 576 } 577} 578 579int 580Bus::findBlockSize(int id) 581{ 582 if (cachedBlockSizeValid) 583 return cachedBlockSize; 584 585 int max_bs = -1; 586 587 PortIter p_end = portMap.end(); 588 for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) { 589 int tmp_bs = interfaces[p_iter->second]->peerBlockSize(); 590 if (tmp_bs > max_bs) 591 max_bs = tmp_bs; 592 } 593 SnoopIter s_end = snoopPorts.end(); 594 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 595 int tmp_bs = (*s_iter)->peerBlockSize(); 596 if (tmp_bs > max_bs) 597 max_bs = tmp_bs; 598 } 599 if (max_bs <= 0) 600 max_bs = defaultBlockSize; 601 602 if (max_bs != 64) 603 warn_once("Blocksize found to not be 64... hmm... probably not.\n"); 604 cachedBlockSize = max_bs; 605 cachedBlockSizeValid = true; 606 return max_bs; 607} 608 609 610unsigned int 611Bus::drain(Event * de) 612{ 613 //We should check that we're not "doing" anything, and that noone is 614 //waiting. We might be idle but have someone waiting if the device we 615 //contacted for a retry didn't actually retry. 616 if (retryList.size() || (curTick < tickNextIdle && busIdle.scheduled())) { 617 drainEvent = de; 618 return 1; 619 } 620 return 0; 621} 622 623void 624Bus::startup() 625{ 626 if (tickNextIdle < curTick) 627 tickNextIdle = (curTick / clock) * clock + clock; 628} 629 630Bus * 631BusParams::create() 632{ 633 return new Bus(this); 634} 635