coherent_xbar.cc revision 4879
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#include "sim/builder.hh" 43 44Port * 45Bus::getPort(const std::string &if_name, int idx) 46{ 47 if (if_name == "default") { 48 if (defaultPort == NULL) { 49 defaultPort = new BusPort(csprintf("%s-default",name()), this, 50 defaultId); 51 cachedBlockSizeValid = false; 52 return defaultPort; 53 } else 54 fatal("Default port already set\n"); 55 } 56 int id; 57 if (if_name == "functional") { 58 if (!funcPort) { 59 id = maxId++; 60 funcPort = new BusPort(csprintf("%s-p%d-func", name(), id), this, id); 61 funcPortId = id; 62 interfaces[id] = funcPort; 63 } 64 return funcPort; 65 } 66 67 // if_name ignored? forced to be empty? 68 id = maxId++; 69 assert(maxId < std::numeric_limits<typeof(maxId)>::max()); 70 BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id); 71 interfaces[id] = bp; 72 cachedBlockSizeValid = false; 73 return bp; 74} 75 76void 77Bus::deletePortRefs(Port *p) 78{ 79 80 BusPort *bp = dynamic_cast<BusPort*>(p); 81 if (bp == NULL) 82 panic("Couldn't convert Port* to BusPort*\n"); 83 // If this is our one functional port 84 if (funcPort == bp) 85 return; 86 interfaces.erase(bp->getId()); 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 = 151 tickNextIdle + 152 pkt->isRequest() ? clock : 0 + 153 clock; 154 155 //Advance it numCycles bus cycles. 156 //XXX Should this use the repeated addition trick as well? 157 tickNextIdle += (numCycles * clock); 158 if (!busIdle.scheduled()) { 159 busIdle.schedule(tickNextIdle); 160 } else { 161 busIdle.reschedule(tickNextIdle); 162 } 163 DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n", 164 curTick, tickNextIdle); 165 166 // The bus will become idle once the current packet is delivered. 167 pkt->finishTime = tickNextIdle; 168} 169 170/** Function called by the port when the bus is receiving a Timing 171 * transaction.*/ 172bool 173Bus::recvTiming(PacketPtr pkt) 174{ 175 Port *port; 176 DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n", 177 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 178 179 BusPort *pktPort; 180 if (pkt->getSrc() == defaultId) 181 pktPort = defaultPort; 182 else pktPort = interfaces[pkt->getSrc()]; 183 184 // If the bus is busy, or other devices are in line ahead of the current 185 // one, put this device on the retry list. 186 if (tickNextIdle > curTick || 187 (retryList.size() && (!inRetry || pktPort != retryList.front()))) 188 { 189 addToRetryList(pktPort); 190 DPRINTF(Bus, "recvTiming: Bus is busy, returning false\n"); 191 return false; 192 } 193 194 short dest = pkt->getDest(); 195 196 // Make sure to clear the snoop commit flag so it doesn't think an 197 // access has been handled twice. 198 if (dest == Packet::Broadcast) { 199 port = findPort(pkt->getAddr(), pkt->getSrc()); 200 timingSnoop(pkt, port ? port : interfaces[pkt->getSrc()]); 201 202 if (pkt->memInhibitAsserted()) { 203 //Cache-Cache transfer occuring 204 if (inRetry) { 205 retryList.front()->onRetryList(false); 206 retryList.pop_front(); 207 inRetry = false; 208 } 209 occupyBus(pkt); 210 DPRINTF(Bus, "recvTiming: Packet sucessfully sent\n"); 211 return true; 212 } 213 } else { 214 assert(dest >= 0 && dest < maxId); 215 assert(dest != pkt->getSrc()); // catch infinite loops 216 port = interfaces[dest]; 217 } 218 219 occupyBus(pkt); 220 221 if (port) { 222 if (port->sendTiming(pkt)) { 223 // Packet was successfully sent. Return true. 224 // Also take care of retries 225 if (inRetry) { 226 DPRINTF(Bus, "Remove retry from list %d\n", 227 retryList.front()->getId()); 228 retryList.front()->onRetryList(false); 229 retryList.pop_front(); 230 inRetry = false; 231 } 232 return true; 233 } 234 235 // Packet not successfully sent. Leave or put it on the retry list. 236 DPRINTF(Bus, "Adding2 a retry to RETRY list %d\n", 237 pktPort->getId()); 238 addToRetryList(pktPort); 239 return false; 240 } 241 else { 242 //Forwarding up from responder, just return true; 243 DPRINTF(Bus, "recvTiming: can we be here?\n"); 244 return true; 245 } 246} 247 248void 249Bus::recvRetry(int id) 250{ 251 DPRINTF(Bus, "Received a retry from %s\n", id == -1 ? "self" : interfaces[id]->getPeer()->name()); 252 // If there's anything waiting, and the bus isn't busy... 253 if (retryList.size() && curTick >= tickNextIdle) { 254 //retryingPort = retryList.front(); 255 inRetry = true; 256 DPRINTF(Bus, "Sending a retry to %s\n", retryList.front()->getPeer()->name()); 257 retryList.front()->sendRetry(); 258 // If inRetry is still true, sendTiming wasn't called 259 if (inRetry) 260 { 261 retryList.front()->onRetryList(false); 262 retryList.pop_front(); 263 inRetry = false; 264 265 //Bring tickNextIdle up to the present 266 while (tickNextIdle < curTick) 267 tickNextIdle += clock; 268 269 //Burn a cycle for the missed grant. 270 tickNextIdle += clock; 271 272 busIdle.reschedule(tickNextIdle, true); 273 } 274 } 275 //If we weren't able to drain before, we might be able to now. 276 if (drainEvent && retryList.size() == 0 && curTick >= tickNextIdle) { 277 drainEvent->process(); 278 // Clear the drain event once we're done with it. 279 drainEvent = NULL; 280 } 281} 282 283Port * 284Bus::findPort(Addr addr, int id) 285{ 286 /* An interval tree would be a better way to do this. --ali. */ 287 int dest_id = -1; 288 289 PortIter i = portMap.find(RangeSize(addr,1)); 290 if (i != portMap.end()) 291 dest_id = i->second; 292 293 // Check if this matches the default range 294 if (dest_id == -1) { 295 for (AddrRangeIter iter = defaultRange.begin(); 296 iter != defaultRange.end(); iter++) { 297 if (*iter == addr) { 298 DPRINTF(Bus, " found addr %#llx on default\n", addr); 299 return defaultPort; 300 } 301 } 302 303 if (responderSet) { 304 panic("Unable to find destination for addr (user set default " 305 "responder): %#llx", addr); 306 } else { 307 DPRINTF(Bus, "Unable to find destination for addr: %#llx, will use " 308 "default port", addr); 309 310 return defaultPort; 311 } 312 } 313 314 315 // we shouldn't be sending this back to where it came from 316 // do the snoop access and then we should terminate 317 // the cyclical call. 318 if (dest_id == id) 319 return 0; 320 321 return interfaces[dest_id]; 322} 323 324void 325Bus::functionalSnoop(PacketPtr pkt, Port *responder) 326{ 327 // The packet may be changed by another bus on snoops, restore the 328 // id after each 329 int src_id = pkt->getSrc(); 330 331 assert(pkt->isRequest()); // hasn't already been satisfied 332 333 for (SnoopIter s_iter = snoopPorts.begin(); 334 s_iter != snoopPorts.end(); 335 s_iter++) { 336 BusPort *p = *s_iter; 337 if (p != responder && p->getId() != src_id) { 338 p->sendFunctional(pkt); 339 } 340 if (pkt->isResponse()) { 341 break; 342 } 343 pkt->setSrc(src_id); 344 } 345} 346 347bool 348Bus::timingSnoop(PacketPtr pkt, Port* responder) 349{ 350 for (SnoopIter s_iter = snoopPorts.begin(); 351 s_iter != snoopPorts.end(); 352 s_iter++) { 353 BusPort *p = *s_iter; 354 if (p != responder && p->getId() != pkt->getSrc()) { 355 bool success = p->sendTiming(pkt); 356 if (!success) 357 return false; 358 } 359 } 360 361 return true; 362} 363 364 365/** Function called by the port when the bus is receiving a Atomic 366 * transaction.*/ 367Tick 368Bus::recvAtomic(PacketPtr pkt) 369{ 370 DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", 371 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 372 assert(pkt->getDest() == Packet::Broadcast); 373 assert(pkt->isRequest()); 374 375 // Variables for recording original command and snoop response (if 376 // any)... if a snooper respondes, we will need to restore 377 // original command so that additional snoops can take place 378 // properly 379 MemCmd orig_cmd = pkt->cmd; 380 MemCmd snoop_response_cmd = MemCmd::InvalidCmd; 381 Tick snoop_response_latency = 0; 382 int orig_src = pkt->getSrc(); 383 384 Port *target_port = findPort(pkt->getAddr(), pkt->getSrc()); 385 386 SnoopIter s_end = snoopPorts.end(); 387 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { 388 BusPort *p = *s_iter; 389 // same port should not have both target addresses and snooping 390 assert(p != target_port); 391 if (p->getId() != pkt->getSrc()) { 392 Tick latency = p->sendAtomic(pkt); 393 if (pkt->isResponse()) { 394 // response from snoop agent 395 assert(pkt->cmd != orig_cmd); 396 assert(pkt->memInhibitAsserted()); 397 // should only happen once 398 assert(snoop_response_cmd == MemCmd::InvalidCmd); 399 // save response state 400 snoop_response_cmd = pkt->cmd; 401 snoop_response_latency = latency; 402 // restore original packet state for remaining snoopers 403 pkt->cmd = orig_cmd; 404 pkt->setSrc(orig_src); 405 pkt->setDest(Packet::Broadcast); 406 } 407 } 408 } 409 410 Tick response_latency = target_port->sendAtomic(pkt); 411 412 // if we got a response from a snooper, restore it here 413 if (snoop_response_cmd != MemCmd::InvalidCmd) { 414 // no one else should have responded 415 assert(!pkt->isResponse()); 416 assert(pkt->cmd == orig_cmd); 417 pkt->cmd = snoop_response_cmd; 418 response_latency = snoop_response_latency; 419 } 420 421 // why do we have this packet field and the return value both??? 422 pkt->finishTime = curTick + response_latency; 423 return response_latency; 424} 425 426/** Function called by the port when the bus is receiving a Functional 427 * transaction.*/ 428void 429Bus::recvFunctional(PacketPtr pkt) 430{ 431 DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", 432 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 433 assert(pkt->getDest() == Packet::Broadcast); 434 435 Port* port = findPort(pkt->getAddr(), pkt->getSrc()); 436 functionalSnoop(pkt, port ? port : interfaces[pkt->getSrc()]); 437 438 // If the snooping hasn't found what we were looking for, keep going. 439 if (!pkt->isResponse() && port) { 440 port->sendFunctional(pkt); 441 } 442} 443 444/** Function called by the port when the bus is receiving a status change.*/ 445void 446Bus::recvStatusChange(Port::Status status, int id) 447{ 448 AddrRangeList ranges; 449 bool snoops; 450 AddrRangeIter iter; 451 452 assert(status == Port::RangeChange && 453 "The other statuses need to be implemented."); 454 455 DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); 456 457 if (id == defaultId) { 458 defaultRange.clear(); 459 // Only try to update these ranges if the user set a default responder. 460 if (responderSet) { 461 defaultPort->getPeerAddressRanges(ranges, snoops); 462 assert(snoops == false); 463 for(iter = ranges.begin(); iter != ranges.end(); iter++) { 464 defaultRange.push_back(*iter); 465 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", 466 iter->start, iter->end); 467 } 468 } 469 } else { 470 471 assert((id < maxId && id >= 0) || id == defaultId); 472 BusPort *port = interfaces[id]; 473 474 // Clean out any previously existent ids 475 for (PortIter portIter = portMap.begin(); 476 portIter != portMap.end(); ) { 477 if (portIter->second == id) 478 portMap.erase(portIter++); 479 else 480 portIter++; 481 } 482 483 for (SnoopIter s_iter = snoopPorts.begin(); 484 s_iter != snoopPorts.end(); ) { 485 if ((*s_iter)->getId() == id) 486 s_iter = snoopPorts.erase(s_iter); 487 else 488 s_iter++; 489 } 490 491 port->getPeerAddressRanges(ranges, snoops); 492 493 if (snoops) { 494 DPRINTF(BusAddrRanges, "Adding id %d to snoop list\n", id); 495 snoopPorts.push_back(port); 496 } 497 498 for (iter = ranges.begin(); iter != ranges.end(); iter++) { 499 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", 500 iter->start, iter->end, id); 501 if (portMap.insert(*iter, id) == portMap.end()) 502 panic("Two devices with same range\n"); 503 504 } 505 } 506 DPRINTF(MMU, "port list has %d entries\n", portMap.size()); 507 508 // tell all our peers that our address range has changed. 509 // Don't tell the device that caused this change, it already knows 510 m5::hash_map<short,BusPort*>::iterator intIter; 511 512 for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) 513 if (intIter->first != id && intIter->first != funcPortId) 514 intIter->second->sendStatusChange(Port::RangeChange); 515 516 if (id != defaultId && defaultPort) 517 defaultPort->sendStatusChange(Port::RangeChange); 518} 519 520void 521Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id) 522{ 523 resp.clear(); 524 snoop = false; 525 526 DPRINTF(BusAddrRanges, "received address range request, returning:\n"); 527 528 for (AddrRangeIter dflt_iter = defaultRange.begin(); 529 dflt_iter != defaultRange.end(); dflt_iter++) { 530 resp.push_back(*dflt_iter); 531 DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start, 532 dflt_iter->end); 533 } 534 for (PortIter portIter = portMap.begin(); 535 portIter != portMap.end(); portIter++) { 536 bool subset = false; 537 for (AddrRangeIter dflt_iter = defaultRange.begin(); 538 dflt_iter != defaultRange.end(); dflt_iter++) { 539 if ((portIter->first.start < dflt_iter->start && 540 portIter->first.end >= dflt_iter->start) || 541 (portIter->first.start < dflt_iter->end && 542 portIter->first.end >= dflt_iter->end)) 543 fatal("Devices can not set ranges that itersect the default set\ 544 but are not a subset of the default set.\n"); 545 if (portIter->first.start >= dflt_iter->start && 546 portIter->first.end <= dflt_iter->end) { 547 subset = true; 548 DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", 549 portIter->first.start, portIter->first.end); 550 } 551 } 552 if (portIter->second != id && !subset) { 553 resp.push_back(portIter->first); 554 DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", 555 portIter->first.start, portIter->first.end); 556 } 557 } 558 559 for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end(); 560 s_iter++) { 561 if ((*s_iter)->getId() != id) { 562 snoop = true; 563 break; 564 } 565 } 566} 567 568int 569Bus::findBlockSize(int id) 570{ 571 if (cachedBlockSizeValid) 572 return cachedBlockSize; 573 574 int max_bs = -1; 575 576 for (PortIter portIter = portMap.begin(); 577 portIter != portMap.end(); portIter++) { 578 int tmp_bs = interfaces[portIter->second]->peerBlockSize(); 579 if (tmp_bs > max_bs) 580 max_bs = tmp_bs; 581 } 582 for (SnoopIter s_iter = snoopPorts.begin(); 583 s_iter != snoopPorts.end(); s_iter++) { 584 int tmp_bs = (*s_iter)->peerBlockSize(); 585 if (tmp_bs > max_bs) 586 max_bs = tmp_bs; 587 } 588 if (max_bs <= 0) 589 max_bs = defaultBlockSize; 590 591 if (max_bs != 64) 592 warn_once("Blocksize found to not be 64... hmm... probably not.\n"); 593 cachedBlockSize = max_bs; 594 cachedBlockSizeValid = true; 595 return max_bs; 596} 597 598 599unsigned int 600Bus::drain(Event * de) 601{ 602 //We should check that we're not "doing" anything, and that noone is 603 //waiting. We might be idle but have someone waiting if the device we 604 //contacted for a retry didn't actually retry. 605 if (curTick >= tickNextIdle && retryList.size() == 0) { 606 return 0; 607 } else { 608 drainEvent = de; 609 return 1; 610 } 611} 612 613void 614Bus::startup() 615{ 616 if (tickNextIdle < curTick) 617 tickNextIdle = (curTick / clock) * clock + clock; 618} 619 620BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus) 621 622 Param<int> bus_id; 623 Param<int> clock; 624 Param<int> width; 625 Param<bool> responder_set; 626 Param<int> block_size; 627 628END_DECLARE_SIM_OBJECT_PARAMS(Bus) 629 630BEGIN_INIT_SIM_OBJECT_PARAMS(Bus) 631 INIT_PARAM(bus_id, "a globally unique bus id"), 632 INIT_PARAM(clock, "bus clock speed"), 633 INIT_PARAM(width, "width of the bus (bits)"), 634 INIT_PARAM(responder_set, "Is a default responder set by the user"), 635 INIT_PARAM(block_size, "Default blocksize if no device has one") 636END_INIT_SIM_OBJECT_PARAMS(Bus) 637 638CREATE_SIM_OBJECT(Bus) 639{ 640 return new Bus(getInstanceName(), bus_id, clock, width, responder_set, 641 block_size); 642} 643 644REGISTER_SIM_OBJECT("Bus", Bus) 645