coherent_xbar.cc revision 10402
1/* 2 * Copyright (c) 2011-2013 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Copyright (c) 2006 The Regents of The University of Michigan 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions are 19 * met: redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer; 21 * redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution; 24 * neither the name of the copyright holders nor the names of its 25 * contributors may be used to endorse or promote products derived from 26 * this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 * 40 * Authors: Ali Saidi 41 * Andreas Hansson 42 * William Wang 43 */ 44 45/** 46 * @file 47 * Definition of a bus object. 48 */ 49 50#include "base/misc.hh" 51#include "base/trace.hh" 52#include "debug/BusAddrRanges.hh" 53#include "debug/CoherentBus.hh" 54#include "mem/coherent_bus.hh" 55#include "sim/system.hh" 56 57CoherentBus::CoherentBus(const CoherentBusParams *p) 58 : BaseBus(p), system(p->system), snoopFilter(p->snoop_filter) 59{ 60 // create the ports based on the size of the master and slave 61 // vector ports, and the presence of the default port, the ports 62 // are enumerated starting from zero 63 for (int i = 0; i < p->port_master_connection_count; ++i) { 64 std::string portName = csprintf("%s.master[%d]", name(), i); 65 MasterPort* bp = new CoherentBusMasterPort(portName, *this, i); 66 masterPorts.push_back(bp); 67 reqLayers.push_back(new ReqLayer(*bp, *this, 68 csprintf(".reqLayer%d", i))); 69 snoopLayers.push_back(new SnoopLayer(*bp, *this, 70 csprintf(".snoopLayer%d", i))); 71 } 72 73 // see if we have a default slave device connected and if so add 74 // our corresponding master port 75 if (p->port_default_connection_count) { 76 defaultPortID = masterPorts.size(); 77 std::string portName = name() + ".default"; 78 MasterPort* bp = new CoherentBusMasterPort(portName, *this, 79 defaultPortID); 80 masterPorts.push_back(bp); 81 reqLayers.push_back(new ReqLayer(*bp, *this, csprintf(".reqLayer%d", 82 defaultPortID))); 83 snoopLayers.push_back(new SnoopLayer(*bp, *this, 84 csprintf(".snoopLayer%d", 85 defaultPortID))); 86 } 87 88 // create the slave ports, once again starting at zero 89 for (int i = 0; i < p->port_slave_connection_count; ++i) { 90 std::string portName = csprintf("%s.slave[%d]", name(), i); 91 SlavePort* bp = new CoherentBusSlavePort(portName, *this, i); 92 slavePorts.push_back(bp); 93 respLayers.push_back(new RespLayer(*bp, *this, 94 csprintf(".respLayer%d", i))); 95 snoopRespPorts.push_back(new SnoopRespPort(*bp, *this)); 96 } 97 98 if (snoopFilter) 99 snoopFilter->setSlavePorts(slavePorts); 100 101 clearPortCache(); 102} 103 104CoherentBus::~CoherentBus() 105{ 106 for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l) 107 delete *l; 108 for (auto l = respLayers.begin(); l != respLayers.end(); ++l) 109 delete *l; 110 for (auto l = snoopLayers.begin(); l != snoopLayers.end(); ++l) 111 delete *l; 112 for (auto p = snoopRespPorts.begin(); p != snoopRespPorts.end(); ++p) 113 delete *p; 114} 115 116void 117CoherentBus::init() 118{ 119 // the base class is responsible for determining the block size 120 BaseBus::init(); 121 122 // iterate over our slave ports and determine which of our 123 // neighbouring master ports are snooping and add them as snoopers 124 for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end(); 125 ++p) { 126 // check if the connected master port is snooping 127 if ((*p)->isSnooping()) { 128 DPRINTF(BusAddrRanges, "Adding snooping master %s\n", 129 (*p)->getMasterPort().name()); 130 snoopPorts.push_back(*p); 131 } 132 } 133 134 if (snoopPorts.empty()) 135 warn("CoherentBus %s has no snooping ports attached!\n", name()); 136} 137 138bool 139CoherentBus::recvTimingReq(PacketPtr pkt, PortID slave_port_id) 140{ 141 // determine the source port based on the id 142 SlavePort *src_port = slavePorts[slave_port_id]; 143 144 // remember if the packet is an express snoop 145 bool is_express_snoop = pkt->isExpressSnoop(); 146 147 // determine the destination based on the address 148 PortID master_port_id = findPort(pkt->getAddr()); 149 150 // test if the bus should be considered occupied for the current 151 // port, and exclude express snoops from the check 152 if (!is_express_snoop && !reqLayers[master_port_id]->tryTiming(src_port)) { 153 DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x BUS BUSY\n", 154 src_port->name(), pkt->cmdString(), pkt->getAddr()); 155 return false; 156 } 157 158 DPRINTF(CoherentBus, "recvTimingReq: src %s %s expr %d 0x%x\n", 159 src_port->name(), pkt->cmdString(), is_express_snoop, 160 pkt->getAddr()); 161 162 // store size and command as they might be modified when 163 // forwarding the packet 164 unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0; 165 unsigned int pkt_cmd = pkt->cmdToIndex(); 166 167 // set the source port for routing of the response 168 pkt->setSrc(slave_port_id); 169 170 calcPacketTiming(pkt); 171 Tick packetFinishTime = pkt->busLastWordDelay + curTick(); 172 173 // uncacheable requests need never be snooped 174 if (!pkt->req->isUncacheable() && !system->bypassCaches()) { 175 // the packet is a memory-mapped request and should be 176 // broadcasted to our snoopers but the source 177 if (snoopFilter) { 178 // check with the snoop filter where to forward this packet 179 auto sf_res = snoopFilter->lookupRequest(pkt, *src_port); 180 packetFinishTime += sf_res.second * clockPeriod(); 181 DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x"\ 182 " SF size: %i lat: %i\n", src_port->name(), 183 pkt->cmdString(), pkt->getAddr(), sf_res.first.size(), 184 sf_res.second); 185 forwardTiming(pkt, slave_port_id, sf_res.first); 186 } else { 187 forwardTiming(pkt, slave_port_id); 188 } 189 } 190 191 // remember if we add an outstanding req so we can undo it if 192 // necessary, if the packet needs a response, we should add it 193 // as outstanding and express snoops never fail so there is 194 // not need to worry about them 195 bool add_outstanding = !is_express_snoop && pkt->needsResponse(); 196 197 // keep track that we have an outstanding request packet 198 // matching this request, this is used by the coherency 199 // mechanism in determining what to do with snoop responses 200 // (in recvTimingSnoop) 201 if (add_outstanding) { 202 // we should never have an exsiting request outstanding 203 assert(outstandingReq.find(pkt->req) == outstandingReq.end()); 204 outstandingReq.insert(pkt->req); 205 } 206 207 // Note: Cannot create a copy of the full packet, here. 208 MemCmd orig_cmd(pkt->cmd); 209 210 // since it is a normal request, attempt to send the packet 211 bool success = masterPorts[master_port_id]->sendTimingReq(pkt); 212 213 if (snoopFilter && !pkt->req->isUncacheable() 214 && !system->bypassCaches()) { 215 // The packet may already be overwritten by the sendTimingReq function. 216 // The snoop filter needs to see the original request *and* the return 217 // status of the send operation, so we need to recreate the original 218 // request. Atomic mode does not have the issue, as there the send 219 // operation and the response happen instantaneously and don't need two 220 // phase tracking. 221 MemCmd tmp_cmd(pkt->cmd); 222 pkt->cmd = orig_cmd; 223 // Let the snoop filter know about the success of the send operation 224 snoopFilter->updateRequest(pkt, *src_port, !success); 225 pkt->cmd = tmp_cmd; 226 } 227 228 // if this is an express snoop, we are done at this point 229 if (is_express_snoop) { 230 assert(success); 231 snoopDataThroughBus += pkt_size; 232 snoopsThroughBus++; 233 } else { 234 // for normal requests, check if successful 235 if (!success) { 236 // inhibited packets should never be forced to retry 237 assert(!pkt->memInhibitAsserted()); 238 239 // if it was added as outstanding and the send failed, then 240 // erase it again 241 if (add_outstanding) 242 outstandingReq.erase(pkt->req); 243 244 // undo the calculation so we can check for 0 again 245 pkt->busFirstWordDelay = pkt->busLastWordDelay = 0; 246 247 DPRINTF(CoherentBus, "recvTimingReq: src %s %s 0x%x RETRY\n", 248 src_port->name(), pkt->cmdString(), pkt->getAddr()); 249 250 // update the bus state and schedule an idle event 251 reqLayers[master_port_id]->failedTiming(src_port, 252 clockEdge(headerCycles)); 253 } else { 254 // update the bus state and schedule an idle event 255 reqLayers[master_port_id]->succeededTiming(packetFinishTime); 256 dataThroughBus += pkt_size; 257 } 258 } 259 260 // stats updates only consider packets that were successfully sent 261 if (success) { 262 pktCount[slave_port_id][master_port_id]++; 263 totPktSize[slave_port_id][master_port_id] += pkt_size; 264 transDist[pkt_cmd]++; 265 } 266 267 return success; 268} 269 270bool 271CoherentBus::recvTimingResp(PacketPtr pkt, PortID master_port_id) 272{ 273 // determine the source port based on the id 274 MasterPort *src_port = masterPorts[master_port_id]; 275 276 // determine the destination based on what is stored in the packet 277 PortID slave_port_id = pkt->getDest(); 278 279 // test if the bus should be considered occupied for the current 280 // port 281 if (!respLayers[slave_port_id]->tryTiming(src_port)) { 282 DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x BUSY\n", 283 src_port->name(), pkt->cmdString(), pkt->getAddr()); 284 return false; 285 } 286 287 DPRINTF(CoherentBus, "recvTimingResp: src %s %s 0x%x\n", 288 src_port->name(), pkt->cmdString(), pkt->getAddr()); 289 290 // store size and command as they might be modified when 291 // forwarding the packet 292 unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0; 293 unsigned int pkt_cmd = pkt->cmdToIndex(); 294 295 calcPacketTiming(pkt); 296 Tick packetFinishTime = pkt->busLastWordDelay + curTick(); 297 298 // the packet is a normal response to a request that we should 299 // have seen passing through the bus 300 assert(outstandingReq.find(pkt->req) != outstandingReq.end()); 301 302 if (snoopFilter && !pkt->req->isUncacheable() && !system->bypassCaches()) { 303 // let the snoop filter inspect the response and update its state 304 snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]); 305 } 306 307 // remove it as outstanding 308 outstandingReq.erase(pkt->req); 309 310 // send the packet through the destination slave port 311 bool success M5_VAR_USED = slavePorts[slave_port_id]->sendTimingResp(pkt); 312 313 // currently it is illegal to block responses... can lead to 314 // deadlock 315 assert(success); 316 317 respLayers[slave_port_id]->succeededTiming(packetFinishTime); 318 319 // stats updates 320 dataThroughBus += pkt_size; 321 pktCount[slave_port_id][master_port_id]++; 322 totPktSize[slave_port_id][master_port_id] += pkt_size; 323 transDist[pkt_cmd]++; 324 325 return true; 326} 327 328void 329CoherentBus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id) 330{ 331 DPRINTF(CoherentBus, "recvTimingSnoopReq: src %s %s 0x%x\n", 332 masterPorts[master_port_id]->name(), pkt->cmdString(), 333 pkt->getAddr()); 334 335 // update stats here as we know the forwarding will succeed 336 transDist[pkt->cmdToIndex()]++; 337 snoopDataThroughBus += pkt->hasData() ? pkt->getSize() : 0; 338 snoopsThroughBus++; 339 340 // we should only see express snoops from caches 341 assert(pkt->isExpressSnoop()); 342 343 // set the source port for routing of the response 344 pkt->setSrc(master_port_id); 345 346 if (snoopFilter) { 347 // let the Snoop Filter work its magic and guide probing 348 auto sf_res = snoopFilter->lookupSnoop(pkt); 349 // No timing here: packetFinishTime += sf_res.second * clockPeriod(); 350 DPRINTF(CoherentBus, "recvTimingSnoopReq: src %s %s 0x%x"\ 351 " SF size: %i lat: %i\n", masterPorts[master_port_id]->name(), 352 pkt->cmdString(), pkt->getAddr(), sf_res.first.size(), 353 sf_res.second); 354 355 // forward to all snoopers 356 forwardTiming(pkt, InvalidPortID, sf_res.first); 357 } else { 358 forwardTiming(pkt, InvalidPortID); 359 } 360 361 // a snoop request came from a connected slave device (one of 362 // our master ports), and if it is not coming from the slave 363 // device responsible for the address range something is 364 // wrong, hence there is nothing further to do as the packet 365 // would be going back to where it came from 366 assert(master_port_id == findPort(pkt->getAddr())); 367} 368 369bool 370CoherentBus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id) 371{ 372 // determine the source port based on the id 373 SlavePort* src_port = slavePorts[slave_port_id]; 374 375 // get the destination from the packet 376 PortID dest_port_id = pkt->getDest(); 377 378 // determine if the response is from a snoop request we 379 // created as the result of a normal request (in which case it 380 // should be in the outstandingReq), or if we merely forwarded 381 // someone else's snoop request 382 bool forwardAsSnoop = outstandingReq.find(pkt->req) == 383 outstandingReq.end(); 384 385 // test if the bus should be considered occupied for the current 386 // port, note that the check is bypassed if the response is being 387 // passed on as a normal response since this is occupying the 388 // response layer rather than the snoop response layer 389 if (forwardAsSnoop) { 390 if (!snoopLayers[dest_port_id]->tryTiming(src_port)) { 391 DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n", 392 src_port->name(), pkt->cmdString(), pkt->getAddr()); 393 return false; 394 } 395 } else { 396 // get the master port that mirrors this slave port internally 397 MasterPort* snoop_port = snoopRespPorts[slave_port_id]; 398 if (!respLayers[dest_port_id]->tryTiming(snoop_port)) { 399 DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n", 400 snoop_port->name(), pkt->cmdString(), pkt->getAddr()); 401 return false; 402 } 403 } 404 405 DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x\n", 406 src_port->name(), pkt->cmdString(), pkt->getAddr()); 407 408 // store size and command as they might be modified when 409 // forwarding the packet 410 unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0; 411 unsigned int pkt_cmd = pkt->cmdToIndex(); 412 413 // responses are never express snoops 414 assert(!pkt->isExpressSnoop()); 415 416 calcPacketTiming(pkt); 417 Tick packetFinishTime = pkt->busLastWordDelay + curTick(); 418 419 // forward it either as a snoop response or a normal response 420 if (forwardAsSnoop) { 421 // this is a snoop response to a snoop request we forwarded, 422 // e.g. coming from the L1 and going to the L2, and it should 423 // be forwarded as a snoop response 424 425 if (snoopFilter) { 426 // update the probe filter so that it can properly track the line 427 snoopFilter->updateSnoopForward(pkt, *slavePorts[slave_port_id], 428 *masterPorts[dest_port_id]); 429 } 430 431 bool success M5_VAR_USED = 432 masterPorts[dest_port_id]->sendTimingSnoopResp(pkt); 433 pktCount[slave_port_id][dest_port_id]++; 434 totPktSize[slave_port_id][dest_port_id] += pkt_size; 435 assert(success); 436 437 snoopLayers[dest_port_id]->succeededTiming(packetFinishTime); 438 } else { 439 // we got a snoop response on one of our slave ports, 440 // i.e. from a coherent master connected to the bus, and 441 // since we created the snoop request as part of 442 // recvTiming, this should now be a normal response again 443 outstandingReq.erase(pkt->req); 444 445 // this is a snoop response from a coherent master, with a 446 // destination field set on its way through the bus as 447 // request, hence it should never go back to where the 448 // snoop response came from, but instead to where the 449 // original request came from 450 assert(slave_port_id != dest_port_id); 451 452 if (snoopFilter) { 453 // update the probe filter so that it can properly track the line 454 snoopFilter->updateSnoopResponse(pkt, *slavePorts[slave_port_id], 455 *slavePorts[dest_port_id]); 456 } 457 458 DPRINTF(CoherentBus, "recvTimingSnoopResp: src %s %s 0x%x"\ 459 " FWD RESP\n", src_port->name(), pkt->cmdString(), 460 pkt->getAddr()); 461 462 // as a normal response, it should go back to a master through 463 // one of our slave ports, at this point we are ignoring the 464 // fact that the response layer could be busy and do not touch 465 // its state 466 bool success M5_VAR_USED = 467 slavePorts[dest_port_id]->sendTimingResp(pkt); 468 469 // @todo Put the response in an internal FIFO and pass it on 470 // to the response layer from there 471 472 // currently it is illegal to block responses... can lead 473 // to deadlock 474 assert(success); 475 476 respLayers[dest_port_id]->succeededTiming(packetFinishTime); 477 } 478 479 // stats updates 480 transDist[pkt_cmd]++; 481 snoopDataThroughBus += pkt_size; 482 snoopsThroughBus++; 483 484 return true; 485} 486 487 488void 489CoherentBus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id, 490 const std::vector<SlavePort*>& dests) 491{ 492 DPRINTF(CoherentBus, "%s for %s address %x size %d\n", __func__, 493 pkt->cmdString(), pkt->getAddr(), pkt->getSize()); 494 495 // snoops should only happen if the system isn't bypassing caches 496 assert(!system->bypassCaches()); 497 498 unsigned fanout = 0; 499 500 for (SlavePortConstIter s = dests.begin(); s != dests.end(); ++s) { 501 SlavePort *p = *s; 502 // we could have gotten this request from a snooping master 503 // (corresponding to our own slave port that is also in 504 // snoopPorts) and should not send it back to where it came 505 // from 506 if (exclude_slave_port_id == InvalidPortID || 507 p->getId() != exclude_slave_port_id) { 508 // cache is not allowed to refuse snoop 509 p->sendTimingSnoopReq(pkt); 510 fanout++; 511 } 512 } 513 514 // Stats for fanout of this forward operation 515 snoopFanout.sample(fanout); 516} 517 518void 519CoherentBus::recvRetry(PortID master_port_id) 520{ 521 // responses and snoop responses never block on forwarding them, 522 // so the retry will always be coming from a port to which we 523 // tried to forward a request 524 reqLayers[master_port_id]->recvRetry(); 525} 526 527Tick 528CoherentBus::recvAtomic(PacketPtr pkt, PortID slave_port_id) 529{ 530 DPRINTF(CoherentBus, "recvAtomic: packet src %s addr 0x%x cmd %s\n", 531 slavePorts[slave_port_id]->name(), pkt->getAddr(), 532 pkt->cmdString()); 533 534 // add the request data 535 dataThroughBus += pkt->hasData() ? pkt->getSize() : 0; 536 537 MemCmd snoop_response_cmd = MemCmd::InvalidCmd; 538 Tick snoop_response_latency = 0; 539 540 // uncacheable requests need never be snooped 541 if (!pkt->req->isUncacheable() && !system->bypassCaches()) { 542 // forward to all snoopers but the source 543 std::pair<MemCmd, Tick> snoop_result; 544 if (snoopFilter) { 545 // check with the snoop filter where to forward this packet 546 auto sf_res = 547 snoopFilter->lookupRequest(pkt, *slavePorts[slave_port_id]); 548 snoop_response_latency += sf_res.second * clockPeriod(); 549 DPRINTF(CoherentBus, "%s: src %s %s 0x%x"\ 550 " SF size: %i lat: %i\n", __func__, 551 slavePorts[slave_port_id]->name(), pkt->cmdString(), 552 pkt->getAddr(), sf_res.first.size(), sf_res.second); 553 snoop_result = forwardAtomic(pkt, slave_port_id, InvalidPortID, 554 sf_res.first); 555 } else { 556 snoop_result = forwardAtomic(pkt, slave_port_id); 557 } 558 snoop_response_cmd = snoop_result.first; 559 snoop_response_latency += snoop_result.second; 560 } 561 562 // even if we had a snoop response, we must continue and also 563 // perform the actual request at the destination 564 PortID dest_id = findPort(pkt->getAddr()); 565 566 // forward the request to the appropriate destination 567 Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt); 568 569 // Lower levels have replied, tell the snoop filter 570 if (snoopFilter && !pkt->req->isUncacheable() && !system->bypassCaches() && 571 pkt->isResponse()) { 572 snoopFilter->updateResponse(pkt, *slavePorts[slave_port_id]); 573 } 574 575 // if we got a response from a snooper, restore it here 576 if (snoop_response_cmd != MemCmd::InvalidCmd) { 577 // no one else should have responded 578 assert(!pkt->isResponse()); 579 pkt->cmd = snoop_response_cmd; 580 response_latency = snoop_response_latency; 581 } 582 583 // add the response data 584 if (pkt->isResponse()) 585 dataThroughBus += pkt->hasData() ? pkt->getSize() : 0; 586 587 // @todo: Not setting first-word time 588 pkt->busLastWordDelay = response_latency; 589 return response_latency; 590} 591 592Tick 593CoherentBus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id) 594{ 595 DPRINTF(CoherentBus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n", 596 masterPorts[master_port_id]->name(), pkt->getAddr(), 597 pkt->cmdString()); 598 599 // add the request snoop data 600 snoopDataThroughBus += pkt->hasData() ? pkt->getSize() : 0; 601 snoopsThroughBus++; 602 603 // forward to all snoopers 604 std::pair<MemCmd, Tick> snoop_result; 605 Tick snoop_response_latency = 0; 606 if (snoopFilter) { 607 auto sf_res = snoopFilter->lookupSnoop(pkt); 608 snoop_response_latency += sf_res.second * clockPeriod(); 609 DPRINTF(CoherentBus, "%s: src %s %s 0x%x SF size: %i lat: %i\n", 610 __func__, masterPorts[master_port_id]->name(), pkt->cmdString(), 611 pkt->getAddr(), sf_res.first.size(), sf_res.second); 612 snoop_result = forwardAtomic(pkt, InvalidPortID, master_port_id, 613 sf_res.first); 614 } else { 615 snoop_result = forwardAtomic(pkt, InvalidPortID); 616 } 617 MemCmd snoop_response_cmd = snoop_result.first; 618 snoop_response_latency += snoop_result.second; 619 620 if (snoop_response_cmd != MemCmd::InvalidCmd) 621 pkt->cmd = snoop_response_cmd; 622 623 // add the response snoop data 624 if (pkt->isResponse()) { 625 snoopDataThroughBus += pkt->hasData() ? pkt->getSize() : 0; 626 snoopsThroughBus++; 627 } 628 629 // @todo: Not setting first-word time 630 pkt->busLastWordDelay = snoop_response_latency; 631 return snoop_response_latency; 632} 633 634std::pair<MemCmd, Tick> 635CoherentBus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id, 636 PortID source_master_port_id, 637 const std::vector<SlavePort*>& dests) 638{ 639 // the packet may be changed on snoops, record the original 640 // command to enable us to restore it between snoops so that 641 // additional snoops can take place properly 642 MemCmd orig_cmd = pkt->cmd; 643 MemCmd snoop_response_cmd = MemCmd::InvalidCmd; 644 Tick snoop_response_latency = 0; 645 646 // snoops should only happen if the system isn't bypassing caches 647 assert(!system->bypassCaches()); 648 649 unsigned fanout = 0; 650 651 for (SlavePortConstIter s = dests.begin(); s != dests.end(); ++s) { 652 SlavePort *p = *s; 653 // we could have gotten this request from a snooping master 654 // (corresponding to our own slave port that is also in 655 // snoopPorts) and should not send it back to where it came 656 // from 657 if (exclude_slave_port_id != InvalidPortID && 658 p->getId() == exclude_slave_port_id) 659 continue; 660 661 Tick latency = p->sendAtomicSnoop(pkt); 662 fanout++; 663 664 // in contrast to a functional access, we have to keep on 665 // going as all snoopers must be updated even if we get a 666 // response 667 if (!pkt->isResponse()) 668 continue; 669 670 // response from snoop agent 671 assert(pkt->cmd != orig_cmd); 672 assert(pkt->memInhibitAsserted()); 673 // should only happen once 674 assert(snoop_response_cmd == MemCmd::InvalidCmd); 675 // save response state 676 snoop_response_cmd = pkt->cmd; 677 snoop_response_latency = latency; 678 679 if (snoopFilter) { 680 // Handle responses by the snoopers and differentiate between 681 // responses to requests from above and snoops from below 682 if (source_master_port_id != InvalidPortID) { 683 // Getting a response for a snoop from below 684 assert(exclude_slave_port_id == InvalidPortID); 685 snoopFilter->updateSnoopForward(pkt, *p, 686 *masterPorts[source_master_port_id]); 687 } else { 688 // Getting a response for a request from above 689 assert(source_master_port_id == InvalidPortID); 690 snoopFilter->updateSnoopResponse(pkt, *p, 691 *slavePorts[exclude_slave_port_id]); 692 } 693 } 694 // restore original packet state for remaining snoopers 695 pkt->cmd = orig_cmd; 696 } 697 698 // Stats for fanout 699 snoopFanout.sample(fanout); 700 701 // the packet is restored as part of the loop and any potential 702 // snoop response is part of the returned pair 703 return std::make_pair(snoop_response_cmd, snoop_response_latency); 704} 705 706void 707CoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id) 708{ 709 if (!pkt->isPrint()) { 710 // don't do DPRINTFs on PrintReq as it clutters up the output 711 DPRINTF(CoherentBus, 712 "recvFunctional: packet src %s addr 0x%x cmd %s\n", 713 slavePorts[slave_port_id]->name(), pkt->getAddr(), 714 pkt->cmdString()); 715 } 716 717 // uncacheable requests need never be snooped 718 if (!pkt->req->isUncacheable() && !system->bypassCaches()) { 719 // forward to all snoopers but the source 720 forwardFunctional(pkt, slave_port_id); 721 } 722 723 // there is no need to continue if the snooping has found what we 724 // were looking for and the packet is already a response 725 if (!pkt->isResponse()) { 726 PortID dest_id = findPort(pkt->getAddr()); 727 728 masterPorts[dest_id]->sendFunctional(pkt); 729 } 730} 731 732void 733CoherentBus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id) 734{ 735 if (!pkt->isPrint()) { 736 // don't do DPRINTFs on PrintReq as it clutters up the output 737 DPRINTF(CoherentBus, 738 "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n", 739 masterPorts[master_port_id]->name(), pkt->getAddr(), 740 pkt->cmdString()); 741 } 742 743 // forward to all snoopers 744 forwardFunctional(pkt, InvalidPortID); 745} 746 747void 748CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id) 749{ 750 // snoops should only happen if the system isn't bypassing caches 751 assert(!system->bypassCaches()); 752 753 for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) { 754 SlavePort *p = *s; 755 // we could have gotten this request from a snooping master 756 // (corresponding to our own slave port that is also in 757 // snoopPorts) and should not send it back to where it came 758 // from 759 if (exclude_slave_port_id == InvalidPortID || 760 p->getId() != exclude_slave_port_id) 761 p->sendFunctionalSnoop(pkt); 762 763 // if we get a response we are done 764 if (pkt->isResponse()) { 765 break; 766 } 767 } 768} 769 770unsigned int 771CoherentBus::drain(DrainManager *dm) 772{ 773 // sum up the individual layers 774 unsigned int total = 0; 775 for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l) 776 total += (*l)->drain(dm); 777 for (auto l = respLayers.begin(); l != respLayers.end(); ++l) 778 total += (*l)->drain(dm); 779 for (auto l = snoopLayers.begin(); l != snoopLayers.end(); ++l) 780 total += (*l)->drain(dm); 781 return total; 782} 783 784void 785CoherentBus::regStats() 786{ 787 // register the stats of the base class and our three bus layers 788 BaseBus::regStats(); 789 for (auto l = reqLayers.begin(); l != reqLayers.end(); ++l) 790 (*l)->regStats(); 791 for (auto l = respLayers.begin(); l != respLayers.end(); ++l) 792 (*l)->regStats(); 793 for (auto l = snoopLayers.begin(); l != snoopLayers.end(); ++l) 794 (*l)->regStats(); 795 796 dataThroughBus 797 .name(name() + ".data_through_bus") 798 .desc("Total data (bytes)") 799 ; 800 801 snoopDataThroughBus 802 .name(name() + ".snoop_data_through_bus") 803 .desc("Total snoop data (bytes)") 804 ; 805 806 snoopsThroughBus 807 .name(name() + ".snoops_through_bus") 808 .desc("Total snoops (count)") 809 ; 810 811 snoopFanout 812 .init(0, snoopPorts.size(), 1) 813 .name(name() + ".snoop_fanout") 814 .desc("Request fanout histogram") 815 ; 816 817 throughput 818 .name(name() + ".throughput") 819 .desc("Throughput (bytes/s)") 820 .precision(0) 821 ; 822 823 throughput = (dataThroughBus + snoopDataThroughBus) / simSeconds; 824} 825 826CoherentBus * 827CoherentBusParams::create() 828{ 829 return new CoherentBus(this); 830} 831