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