xbar.cc revision 3252
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 37#include "base/misc.hh" 38#include "base/trace.hh" 39#include "mem/bus.hh" 40#include "sim/builder.hh" 41 42Port * 43Bus::getPort(const std::string &if_name, int idx) 44{ 45 if (if_name == "default") 46 if (defaultPort == NULL) { 47 defaultPort = new BusPort(csprintf("%s-default",name()), this, 48 defaultId); 49 return defaultPort; 50 } else 51 fatal("Default port already set\n"); 52 53 // if_name ignored? forced to be empty? 54 int id = interfaces.size(); 55 BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id); 56 interfaces.push_back(bp); 57 return bp; 58} 59 60/** Get the ranges of anyone other buses that we are connected to. */ 61void 62Bus::init() 63{ 64 std::vector<BusPort*>::iterator intIter; 65 66 for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) 67 (*intIter)->sendStatusChange(Port::RangeChange); 68} 69 70Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus) 71{} 72 73void Bus::BusFreeEvent::process() 74{ 75 bus->recvRetry(-1); 76} 77 78const char * Bus::BusFreeEvent::description() 79{ 80 return "bus became available"; 81} 82 83void Bus::occupyBus(PacketPtr pkt) 84{ 85 //Bring tickNextIdle up to the present tick 86 //There is some potential ambiguity where a cycle starts, which might make 87 //a difference when devices are acting right around a cycle boundary. Using 88 //a < allows things which happen exactly on a cycle boundary to take up only 89 //the following cycle. Anthing that happens later will have to "wait" for 90 //the end of that cycle, and then start using the bus after that. 91 while (tickNextIdle < curTick) 92 tickNextIdle += clock; 93 94 // The packet will be sent. Figure out how long it occupies the bus, and 95 // how much of that time is for the first "word", aka bus width. 96 int numCycles = 0; 97 // Requests need one cycle to send an address 98 if (pkt->isRequest()) 99 numCycles++; 100 else if (pkt->isResponse() || pkt->hasData()) { 101 // If a packet has data, it needs ceil(size/width) cycles to send it 102 // We're using the "adding instead of dividing" trick again here 103 if (pkt->hasData()) { 104 int dataSize = pkt->getSize(); 105 for (int transmitted = 0; transmitted < dataSize; 106 transmitted += width) { 107 numCycles++; 108 } 109 } else { 110 // If the packet didn't have data, it must have been a response. 111 // Those use the bus for one cycle to send their data. 112 numCycles++; 113 } 114 } 115 116 // The first word will be delivered after the current tick, the delivery 117 // of the address if any, and one bus cycle to deliver the data 118 pkt->firstWordTime = 119 tickNextIdle + 120 pkt->isRequest() ? clock : 0 + 121 clock; 122 123 //Advance it numCycles bus cycles. 124 //XXX Should this use the repeated addition trick as well? 125 tickNextIdle += (numCycles * clock); 126 if (!busIdle.scheduled()) { 127 busIdle.schedule(tickNextIdle); 128 } else { 129 busIdle.reschedule(tickNextIdle); 130 } 131 DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n", 132 curTick, tickNextIdle); 133 134 // The bus will become idle once the current packet is delivered. 135 pkt->finishTime = tickNextIdle; 136} 137 138/** Function called by the port when the bus is receiving a Timing 139 * transaction.*/ 140bool 141Bus::recvTiming(Packet *pkt) 142{ 143 Port *port; 144 DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n", 145 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 146 147 BusPort *pktPort = interfaces[pkt->getSrc()]; 148 149 // If the bus is busy, or other devices are in line ahead of the current 150 // one, put this device on the retry list. 151 if (tickNextIdle > curTick || 152 (retryList.size() && (!inRetry || pktPort != retryList.front()))) { 153 addToRetryList(pktPort); 154 return false; 155 } 156 157 short dest = pkt->getDest(); 158 if (dest == Packet::Broadcast) { 159 if (timingSnoop(pkt)) { 160 pkt->flags |= SNOOP_COMMIT; 161 bool success = timingSnoop(pkt); 162 assert(success); 163 if (pkt->flags & SATISFIED) { 164 //Cache-Cache transfer occuring 165 if (inRetry) { 166 retryList.front()->onRetryList(false); 167 retryList.pop_front(); 168 inRetry = false; 169 } 170 occupyBus(pkt); 171 return true; 172 } 173 port = findPort(pkt->getAddr(), pkt->getSrc()); 174 } else { 175 //Snoop didn't succeed 176 addToRetryList(pktPort); 177 return false; 178 } 179 } else { 180 assert(dest >= 0 && dest < interfaces.size()); 181 assert(dest != pkt->getSrc()); // catch infinite loops 182 port = interfaces[dest]; 183 } 184 185 occupyBus(pkt); 186 187 if (port->sendTiming(pkt)) { 188 // Packet was successfully sent. Return true. 189 // Also take care of retries 190 if (inRetry) { 191 DPRINTF(Bus, "Remove retry from list %i\n", retryList.front()); 192 retryList.front()->onRetryList(false); 193 retryList.pop_front(); 194 inRetry = false; 195 } 196 return true; 197 } 198 199 // Packet not successfully sent. Leave or put it on the retry list. 200 DPRINTF(Bus, "Adding a retry to RETRY list %i\n", pktPort); 201 addToRetryList(pktPort); 202 return false; 203} 204 205void 206Bus::recvRetry(int id) 207{ 208 DPRINTF(Bus, "Received a retry\n"); 209 // If there's anything waiting... 210 if (retryList.size()) { 211 //retryingPort = retryList.front(); 212 inRetry = true; 213 DPRINTF(Bus, "Sending a retry\n"); 214 retryList.front()->sendRetry(); 215 // If inRetry is still true, sendTiming wasn't called 216 if (inRetry) 217 { 218 retryList.front()->onRetryList(false); 219 retryList.pop_front(); 220 inRetry = false; 221 222 //Bring tickNextIdle up to the present 223 while (tickNextIdle < curTick) 224 tickNextIdle += clock; 225 226 //Burn a cycle for the missed grant. 227 tickNextIdle += clock; 228 229 if (!busIdle.scheduled()) { 230 busIdle.schedule(tickNextIdle); 231 } else { 232 busIdle.reschedule(tickNextIdle); 233 } 234 } 235 } 236} 237 238Port * 239Bus::findPort(Addr addr, int id) 240{ 241 /* An interval tree would be a better way to do this. --ali. */ 242 int dest_id = -1; 243 int i = 0; 244 bool found = false; 245 AddrRangeIter iter; 246 247 while (i < portList.size() && !found) 248 { 249 if (portList[i].range == addr) { 250 dest_id = portList[i].portId; 251 found = true; 252 DPRINTF(Bus, " found addr %#llx on device %d\n", addr, dest_id); 253 } 254 i++; 255 } 256 257 // Check if this matches the default range 258 if (dest_id == -1) { 259 for (iter = defaultRange.begin(); iter != defaultRange.end(); iter++) { 260 if (*iter == addr) { 261 DPRINTF(Bus, " found addr %#llx on default\n", addr); 262 return defaultPort; 263 } 264 } 265 panic("Unable to find destination for addr: %#llx", addr); 266 } 267 268 269 // we shouldn't be sending this back to where it came from 270 assert(dest_id != id); 271 272 return interfaces[dest_id]; 273} 274 275std::vector<int> 276Bus::findSnoopPorts(Addr addr, int id) 277{ 278 int i = 0; 279 AddrRangeIter iter; 280 std::vector<int> ports; 281 282 while (i < portSnoopList.size()) 283 { 284 if (portSnoopList[i].range == addr && portSnoopList[i].portId != id) { 285 //Careful to not overlap ranges 286 //or snoop will be called more than once on the port 287 ports.push_back(portSnoopList[i].portId); 288// DPRINTF(Bus, " found snoop addr %#llx on device%d\n", addr, 289// portSnoopList[i].portId); 290 } 291 i++; 292 } 293 return ports; 294} 295 296void 297Bus::atomicSnoop(Packet *pkt) 298{ 299 std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); 300 301 while (!ports.empty()) 302 { 303 interfaces[ports.back()]->sendAtomic(pkt); 304 ports.pop_back(); 305 } 306} 307 308void 309Bus::functionalSnoop(Packet *pkt) 310{ 311 std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); 312 313 while (!ports.empty()) 314 { 315 interfaces[ports.back()]->sendFunctional(pkt); 316 ports.pop_back(); 317 } 318} 319 320bool 321Bus::timingSnoop(Packet *pkt) 322{ 323 std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); 324 bool success = true; 325 326 while (!ports.empty() && success) 327 { 328 success = interfaces[ports.back()]->sendTiming(pkt); 329 ports.pop_back(); 330 } 331 332 return success; 333} 334 335 336/** Function called by the port when the bus is receiving a Atomic 337 * transaction.*/ 338Tick 339Bus::recvAtomic(Packet *pkt) 340{ 341 DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", 342 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 343 assert(pkt->getDest() == Packet::Broadcast); 344 atomicSnoop(pkt); 345 return findPort(pkt->getAddr(), pkt->getSrc())->sendAtomic(pkt); 346} 347 348/** Function called by the port when the bus is receiving a Functional 349 * transaction.*/ 350void 351Bus::recvFunctional(Packet *pkt) 352{ 353 DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", 354 pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); 355 assert(pkt->getDest() == Packet::Broadcast); 356 functionalSnoop(pkt); 357 findPort(pkt->getAddr(), pkt->getSrc())->sendFunctional(pkt); 358} 359 360/** Function called by the port when the bus is receiving a status change.*/ 361void 362Bus::recvStatusChange(Port::Status status, int id) 363{ 364 AddrRangeList ranges; 365 AddrRangeList snoops; 366 int x; 367 AddrRangeIter iter; 368 369 assert(status == Port::RangeChange && 370 "The other statuses need to be implemented."); 371 372 DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); 373 374 if (id == defaultId) { 375 defaultRange.clear(); 376 defaultPort->getPeerAddressRanges(ranges, snoops); 377 assert(snoops.size() == 0); 378 for(iter = ranges.begin(); iter != ranges.end(); iter++) { 379 defaultRange.push_back(*iter); 380 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", 381 iter->start, iter->end); 382 } 383 } else { 384 385 assert((id < interfaces.size() && id >= 0) || id == -1); 386 Port *port = interfaces[id]; 387 std::vector<DevMap>::iterator portIter; 388 std::vector<DevMap>::iterator snoopIter; 389 390 // Clean out any previously existent ids 391 for (portIter = portList.begin(); portIter != portList.end(); ) { 392 if (portIter->portId == id) 393 portIter = portList.erase(portIter); 394 else 395 portIter++; 396 } 397 398 for (snoopIter = portSnoopList.begin(); snoopIter != portSnoopList.end(); ) { 399 if (snoopIter->portId == id) 400 snoopIter = portSnoopList.erase(snoopIter); 401 else 402 snoopIter++; 403 } 404 405 port->getPeerAddressRanges(ranges, snoops); 406 407 for(iter = snoops.begin(); iter != snoops.end(); iter++) { 408 DevMap dm; 409 dm.portId = id; 410 dm.range = *iter; 411 412 DPRINTF(BusAddrRanges, "Adding snoop range %#llx - %#llx for id %d\n", 413 dm.range.start, dm.range.end, id); 414 portSnoopList.push_back(dm); 415 } 416 417 for(iter = ranges.begin(); iter != ranges.end(); iter++) { 418 DevMap dm; 419 dm.portId = id; 420 dm.range = *iter; 421 422 DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", 423 dm.range.start, dm.range.end, id); 424 portList.push_back(dm); 425 } 426 } 427 DPRINTF(MMU, "port list has %d entries\n", portList.size()); 428 429 // tell all our peers that our address range has changed. 430 // Don't tell the device that caused this change, it already knows 431 for (x = 0; x < interfaces.size(); x++) 432 if (x != id) 433 interfaces[x]->sendStatusChange(Port::RangeChange); 434 435 if (id != defaultId && defaultPort) 436 defaultPort->sendStatusChange(Port::RangeChange); 437} 438 439void 440Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) 441{ 442 std::vector<DevMap>::iterator portIter; 443 AddrRangeIter dflt_iter; 444 bool subset; 445 446 resp.clear(); 447 snoop.clear(); 448 449 DPRINTF(BusAddrRanges, "received address range request, returning:\n"); 450 451 for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end(); 452 dflt_iter++) { 453 resp.push_back(*dflt_iter); 454 DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n",dflt_iter->start, 455 dflt_iter->end); 456 } 457 for (portIter = portList.begin(); portIter != portList.end(); portIter++) { 458 subset = false; 459 for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end(); 460 dflt_iter++) { 461 if ((portIter->range.start < dflt_iter->start && 462 portIter->range.end >= dflt_iter->start) || 463 (portIter->range.start < dflt_iter->end && 464 portIter->range.end >= dflt_iter->end)) 465 fatal("Devices can not set ranges that itersect the default set\ 466 but are not a subset of the default set.\n"); 467 if (portIter->range.start >= dflt_iter->start && 468 portIter->range.end <= dflt_iter->end) { 469 subset = true; 470 DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", 471 portIter->range.start, portIter->range.end); 472 } 473 } 474 if (portIter->portId != id && !subset) { 475 resp.push_back(portIter->range); 476 DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", 477 portIter->range.start, portIter->range.end); 478 } 479 } 480} 481 482BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus) 483 484 Param<int> bus_id; 485 Param<int> clock; 486 Param<int> width; 487 488END_DECLARE_SIM_OBJECT_PARAMS(Bus) 489 490BEGIN_INIT_SIM_OBJECT_PARAMS(Bus) 491 INIT_PARAM(bus_id, "a globally unique bus id"), 492 INIT_PARAM(clock, "bus clock speed"), 493 INIT_PARAM(width, "width of the bus (bits)") 494END_INIT_SIM_OBJECT_PARAMS(Bus) 495 496CREATE_SIM_OBJECT(Bus) 497{ 498 return new Bus(getInstanceName(), bus_id, clock, width); 499} 500 501REGISTER_SIM_OBJECT("Bus", Bus) 502