RubyPort.cc revision 8914
1/* 2 * Copyright (c) 2009 Advanced Micro Devices, Inc. 3 * Copyright (c) 2011 Mark D. Hill and David A. Wood 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer; 10 * redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution; 13 * neither the name of the copyright holders nor the names of its 14 * contributors may be used to endorse or promote products derived from 15 * this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include "cpu/testers/rubytest/RubyTester.hh" 31#include "debug/Config.hh" 32#include "debug/Ruby.hh" 33#include "mem/protocol/AccessPermission.hh" 34#include "mem/ruby/slicc_interface/AbstractController.hh" 35#include "mem/ruby/system/RubyPort.hh" 36 37RubyPort::RubyPort(const Params *p) 38 : MemObject(p), pio_port(csprintf("%s-pio-port", name()), this), 39 physMemPort(csprintf("%s-physMemPort", name()), this) 40{ 41 m_version = p->version; 42 assert(m_version != -1); 43 44 physmem = p->physmem; 45 46 m_controller = NULL; 47 m_mandatory_q_ptr = NULL; 48 49 m_request_cnt = 0; 50 51 m_usingRubyTester = p->using_ruby_tester; 52 access_phys_mem = p->access_phys_mem; 53 54 drainEvent = NULL; 55 56 ruby_system = p->ruby_system; 57 waitingOnSequencer = false; 58} 59 60void 61RubyPort::init() 62{ 63 assert(m_controller != NULL); 64 m_mandatory_q_ptr = m_controller->getMandatoryQueue(); 65} 66 67Port * 68RubyPort::getPort(const std::string &if_name, int idx) 69{ 70 // used by the CPUs to connect the caches to the interconnect, and 71 // for the x86 case also the interrupt master 72 if (if_name == "slave") { 73 M5Port* cpuPort = new M5Port(csprintf("%s-slave%d", name(), idx), 74 this, ruby_system, access_phys_mem); 75 cpu_ports.push_back(cpuPort); 76 return cpuPort; 77 } 78 79 // used by the x86 CPUs to connect the interrupt PIO and interrupt slave 80 // port 81 if (if_name == "master") { 82 PioPort* masterPort = new PioPort(csprintf("%s-master%d", name(), idx), 83 this); 84 85 return masterPort; 86 } 87 88 if (if_name == "pio_port") { 89 return &pio_port; 90 } 91 92 if (if_name == "physMemPort") { 93 return &physMemPort; 94 } 95 96 return NULL; 97} 98 99RubyPort::PioPort::PioPort(const std::string &_name, 100 RubyPort *_port) 101 : QueuedPort(_name, _port, queue), queue(*_port, *this), ruby_port(_port) 102{ 103 DPRINTF(RubyPort, "creating port to ruby sequencer to cpu %s\n", _name); 104} 105 106RubyPort::M5Port::M5Port(const std::string &_name, RubyPort *_port, 107 RubySystem *_system, bool _access_phys_mem) 108 : QueuedPort(_name, _port, queue), queue(*_port, *this), 109 ruby_port(_port), ruby_system(_system), 110 _onRetryList(false), access_phys_mem(_access_phys_mem) 111{ 112 DPRINTF(RubyPort, "creating port from ruby sequcner to cpu %s\n", _name); 113} 114 115Tick 116RubyPort::PioPort::recvAtomic(PacketPtr pkt) 117{ 118 panic("RubyPort::PioPort::recvAtomic() not implemented!\n"); 119 return 0; 120} 121 122Tick 123RubyPort::M5Port::recvAtomic(PacketPtr pkt) 124{ 125 panic("RubyPort::M5Port::recvAtomic() not implemented!\n"); 126 return 0; 127} 128 129 130bool 131RubyPort::PioPort::recvTiming(PacketPtr pkt) 132{ 133 // In FS mode, ruby memory will receive pio responses from devices 134 // and it must forward these responses back to the particular CPU. 135 DPRINTF(RubyPort, "Pio response for address %#x\n", pkt->getAddr()); 136 137 assert(pkt->isResponse()); 138 139 // First we must retrieve the request port from the sender State 140 RubyPort::SenderState *senderState = 141 safe_cast<RubyPort::SenderState *>(pkt->senderState); 142 M5Port *port = senderState->port; 143 assert(port != NULL); 144 145 // pop the sender state from the packet 146 pkt->senderState = senderState->saved; 147 delete senderState; 148 149 port->sendTiming(pkt); 150 151 return true; 152} 153 154bool 155RubyPort::M5Port::recvTiming(PacketPtr pkt) 156{ 157 DPRINTF(RubyPort, 158 "Timing access caught for address %#x\n", pkt->getAddr()); 159 160 //dsm: based on SimpleTimingPort::recvTiming(pkt); 161 162 // The received packets should only be M5 requests, which should never 163 // get nacked. There used to be code to hanldle nacks here, but 164 // I'm pretty sure it didn't work correctly with the drain code, 165 // so that would need to be fixed if we ever added it back. 166 assert(pkt->isRequest()); 167 168 if (pkt->memInhibitAsserted()) { 169 warn("memInhibitAsserted???"); 170 // snooper will supply based on copy of packet 171 // still target's responsibility to delete packet 172 delete pkt; 173 return true; 174 } 175 176 // Save the port in the sender state object to be used later to 177 // route the response 178 pkt->senderState = new SenderState(this, pkt->senderState); 179 180 // Check for pio requests and directly send them to the dedicated 181 // pio port. 182 if (!isPhysMemAddress(pkt->getAddr())) { 183 assert(ruby_port->pio_port.isConnected()); 184 DPRINTF(RubyPort, 185 "Request for address 0x%#x is assumed to be a pio request\n", 186 pkt->getAddr()); 187 188 return ruby_port->pio_port.sendNextCycle(pkt); 189 } 190 191 assert(Address(pkt->getAddr()).getOffset() + pkt->getSize() <= 192 RubySystem::getBlockSizeBytes()); 193 194 // Submit the ruby request 195 RequestStatus requestStatus = ruby_port->makeRequest(pkt); 196 197 // If the request successfully issued then we should return true. 198 // Otherwise, we need to delete the senderStatus we just created and return 199 // false. 200 if (requestStatus == RequestStatus_Issued) { 201 DPRINTF(RubyPort, "Request %#x issued\n", pkt->getAddr()); 202 return true; 203 } 204 205 // 206 // Unless one is using the ruby tester, record the stalled M5 port for 207 // later retry when the sequencer becomes free. 208 // 209 if (!ruby_port->m_usingRubyTester) { 210 ruby_port->addToRetryList(this); 211 } 212 213 DPRINTF(RubyPort, 214 "Request for address %#x did not issue because %s\n", 215 pkt->getAddr(), RequestStatus_to_string(requestStatus)); 216 217 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState); 218 pkt->senderState = senderState->saved; 219 delete senderState; 220 return false; 221} 222 223bool 224RubyPort::M5Port::doFunctionalRead(PacketPtr pkt) 225{ 226 Address address(pkt->getAddr()); 227 Address line_address(address); 228 line_address.makeLineAddress(); 229 230 AccessPermission access_perm = AccessPermission_NotPresent; 231 int num_controllers = ruby_system->m_abs_cntrl_vec.size(); 232 233 DPRINTF(RubyPort, "Functional Read request for %s\n",address); 234 235 unsigned int num_ro = 0; 236 unsigned int num_rw = 0; 237 unsigned int num_busy = 0; 238 unsigned int num_backing_store = 0; 239 unsigned int num_invalid = 0; 240 241 // In this loop we count the number of controllers that have the given 242 // address in read only, read write and busy states. 243 for (int i = 0; i < num_controllers; ++i) { 244 access_perm = ruby_system->m_abs_cntrl_vec[i]-> 245 getAccessPermission(line_address); 246 if (access_perm == AccessPermission_Read_Only) 247 num_ro++; 248 else if (access_perm == AccessPermission_Read_Write) 249 num_rw++; 250 else if (access_perm == AccessPermission_Busy) 251 num_busy++; 252 else if (access_perm == AccessPermission_Backing_Store) 253 // See RubySlicc_Exports.sm for details, but Backing_Store is meant 254 // to represent blocks in memory *for Broadcast/Snooping protocols*, 255 // where memory has no idea whether it has an exclusive copy of data 256 // or not. 257 num_backing_store++; 258 else if (access_perm == AccessPermission_Invalid || 259 access_perm == AccessPermission_NotPresent) 260 num_invalid++; 261 } 262 assert(num_rw <= 1); 263 264 uint8* data = pkt->getPtr<uint8_t>(true); 265 unsigned int size_in_bytes = pkt->getSize(); 266 unsigned startByte = address.getAddress() - line_address.getAddress(); 267 268 // This if case is meant to capture what happens in a Broadcast/Snoop 269 // protocol where the block does not exist in the cache hierarchy. You 270 // only want to read from the Backing_Store memory if there is no copy in 271 // the cache hierarchy, otherwise you want to try to read the RO or RW 272 // copies existing in the cache hierarchy (covered by the else statement). 273 // The reason is because the Backing_Store memory could easily be stale, if 274 // there are copies floating around the cache hierarchy, so you want to read 275 // it only if it's not in the cache hierarchy at all. 276 if (num_invalid == (num_controllers - 1) && 277 num_backing_store == 1) 278 { 279 DPRINTF(RubyPort, "only copy in Backing_Store memory, read from it\n"); 280 for (int i = 0; i < num_controllers; ++i) { 281 access_perm = ruby_system->m_abs_cntrl_vec[i] 282 ->getAccessPermission(line_address); 283 if (access_perm == AccessPermission_Backing_Store) { 284 DataBlock& block = ruby_system->m_abs_cntrl_vec[i] 285 ->getDataBlock(line_address); 286 287 DPRINTF(RubyPort, "reading from %s block %s\n", 288 ruby_system->m_abs_cntrl_vec[i]->name(), block); 289 for (unsigned i = 0; i < size_in_bytes; ++i) { 290 data[i] = block.getByte(i + startByte); 291 } 292 return true; 293 } 294 } 295 } else { 296 // In Broadcast/Snoop protocols, this covers if you know the block 297 // exists somewhere in the caching hierarchy, then you want to read any 298 // valid RO or RW block. In directory protocols, same thing, you want 299 // to read any valid readable copy of the block. 300 DPRINTF(RubyPort, "num_busy = %d, num_ro = %d, num_rw = %d\n", 301 num_busy, num_ro, num_rw); 302 // In this loop, we try to figure which controller has a read only or 303 // a read write copy of the given address. Any valid copy would suffice 304 // for a functional read. 305 for(int i = 0;i < num_controllers;++i) { 306 access_perm = ruby_system->m_abs_cntrl_vec[i] 307 ->getAccessPermission(line_address); 308 if(access_perm == AccessPermission_Read_Only || 309 access_perm == AccessPermission_Read_Write) 310 { 311 DataBlock& block = ruby_system->m_abs_cntrl_vec[i] 312 ->getDataBlock(line_address); 313 314 DPRINTF(RubyPort, "reading from %s block %s\n", 315 ruby_system->m_abs_cntrl_vec[i]->name(), block); 316 for (unsigned i = 0; i < size_in_bytes; ++i) { 317 data[i] = block.getByte(i + startByte); 318 } 319 return true; 320 } 321 } 322 } 323 return false; 324} 325 326bool 327RubyPort::M5Port::doFunctionalWrite(PacketPtr pkt) 328{ 329 Address addr(pkt->getAddr()); 330 Address line_addr = line_address(addr); 331 AccessPermission access_perm = AccessPermission_NotPresent; 332 int num_controllers = ruby_system->m_abs_cntrl_vec.size(); 333 334 DPRINTF(RubyPort, "Functional Write request for %s\n",addr); 335 336 unsigned int num_ro = 0; 337 unsigned int num_rw = 0; 338 unsigned int num_busy = 0; 339 unsigned int num_backing_store = 0; 340 unsigned int num_invalid = 0; 341 342 // In this loop we count the number of controllers that have the given 343 // address in read only, read write and busy states. 344 for(int i = 0;i < num_controllers;++i) { 345 access_perm = ruby_system->m_abs_cntrl_vec[i]-> 346 getAccessPermission(line_addr); 347 if (access_perm == AccessPermission_Read_Only) 348 num_ro++; 349 else if (access_perm == AccessPermission_Read_Write) 350 num_rw++; 351 else if (access_perm == AccessPermission_Busy) 352 num_busy++; 353 else if (access_perm == AccessPermission_Backing_Store) 354 // See RubySlicc_Exports.sm for details, but Backing_Store is meant 355 // to represent blocks in memory *for Broadcast/Snooping protocols*, 356 // where memory has no idea whether it has an exclusive copy of data 357 // or not. 358 num_backing_store++; 359 else if (access_perm == AccessPermission_Invalid || 360 access_perm == AccessPermission_NotPresent) 361 num_invalid++; 362 } 363 364 // If the number of read write copies is more than 1, then there is bug in 365 // coherence protocol. Otherwise, if all copies are in stable states, i.e. 366 // num_busy == 0, we update all the copies. If there is at least one copy 367 // in busy state, then we check if there is read write copy. If yes, then 368 // also we let the access go through. Or, if there is no copy in the cache 369 // hierarchy at all, we still want to do the write to the memory 370 // (Backing_Store) instead of failing. 371 372 DPRINTF(RubyPort, "num_busy = %d, num_ro = %d, num_rw = %d\n", 373 num_busy, num_ro, num_rw); 374 assert(num_rw <= 1); 375 376 uint8* data = pkt->getPtr<uint8_t>(true); 377 unsigned int size_in_bytes = pkt->getSize(); 378 unsigned startByte = addr.getAddress() - line_addr.getAddress(); 379 380 if ((num_busy == 0 && num_ro > 0) || num_rw == 1 || 381 (num_invalid == (num_controllers - 1) && num_backing_store == 1)) 382 { 383 for(int i = 0; i < num_controllers;++i) { 384 access_perm = ruby_system->m_abs_cntrl_vec[i]-> 385 getAccessPermission(line_addr); 386 if(access_perm == AccessPermission_Read_Only || 387 access_perm == AccessPermission_Read_Write|| 388 access_perm == AccessPermission_Maybe_Stale || 389 access_perm == AccessPermission_Backing_Store) 390 { 391 DataBlock& block = ruby_system->m_abs_cntrl_vec[i] 392 ->getDataBlock(line_addr); 393 394 DPRINTF(RubyPort, "%s\n",block); 395 for (unsigned i = 0; i < size_in_bytes; ++i) { 396 block.setByte(i + startByte, data[i]); 397 } 398 DPRINTF(RubyPort, "%s\n",block); 399 } 400 } 401 return true; 402 } 403 return false; 404} 405 406void 407RubyPort::M5Port::recvFunctional(PacketPtr pkt) 408{ 409 DPRINTF(RubyPort, "Functional access caught for address %#x\n", 410 pkt->getAddr()); 411 412 // Check for pio requests and directly send them to the dedicated 413 // pio port. 414 if (!isPhysMemAddress(pkt->getAddr())) { 415 assert(ruby_port->pio_port.isConnected()); 416 DPRINTF(RubyPort, "Request for address 0x%#x is a pio request\n", 417 pkt->getAddr()); 418 panic("RubyPort::PioPort::recvFunctional() not implemented!\n"); 419 } 420 421 assert(pkt->getAddr() + pkt->getSize() <= 422 line_address(Address(pkt->getAddr())).getAddress() + 423 RubySystem::getBlockSizeBytes()); 424 425 bool accessSucceeded = false; 426 bool needsResponse = pkt->needsResponse(); 427 428 // Do the functional access on ruby memory 429 if (pkt->isRead()) { 430 accessSucceeded = doFunctionalRead(pkt); 431 } else if (pkt->isWrite()) { 432 accessSucceeded = doFunctionalWrite(pkt); 433 } else { 434 panic("RubyPort: unsupported functional command %s\n", 435 pkt->cmdString()); 436 } 437 438 // Unless the requester explicitly said otherwise, generate an error if 439 // the functional request failed 440 if (!accessSucceeded && !pkt->suppressFuncError()) { 441 fatal("Ruby functional %s failed for address %#x\n", 442 pkt->isWrite() ? "write" : "read", pkt->getAddr()); 443 } 444 445 if (access_phys_mem) { 446 // The attached physmem contains the official version of data. 447 // The following command performs the real functional access. 448 // This line should be removed once Ruby supplies the official version 449 // of data. 450 ruby_port->physMemPort.sendFunctional(pkt); 451 } 452 453 // turn packet around to go back to requester if response expected 454 if (needsResponse) { 455 pkt->setFunctionalResponseStatus(accessSucceeded); 456 457 // @todo There should not be a reverse call since the response is 458 // communicated through the packet pointer 459 // DPRINTF(RubyPort, "Sending packet back over port\n"); 460 // sendFunctional(pkt); 461 } 462 DPRINTF(RubyPort, "Functional access %s!\n", 463 accessSucceeded ? "successful":"failed"); 464} 465 466void 467RubyPort::ruby_hit_callback(PacketPtr pkt) 468{ 469 // Retrieve the request port from the sender State 470 RubyPort::SenderState *senderState = 471 safe_cast<RubyPort::SenderState *>(pkt->senderState); 472 M5Port *port = senderState->port; 473 assert(port != NULL); 474 475 // pop the sender state from the packet 476 pkt->senderState = senderState->saved; 477 delete senderState; 478 479 port->hitCallback(pkt); 480 481 // 482 // If we had to stall the M5Ports, wake them up because the sequencer 483 // likely has free resources now. 484 // 485 if (waitingOnSequencer) { 486 // 487 // Record the current list of ports to retry on a temporary list before 488 // calling sendRetry on those ports. sendRetry will cause an 489 // immediate retry, which may result in the ports being put back on the 490 // list. Therefore we want to clear the retryList before calling 491 // sendRetry. 492 // 493 std::list<M5Port*> curRetryList(retryList); 494 495 retryList.clear(); 496 waitingOnSequencer = false; 497 498 for (std::list<M5Port*>::iterator i = curRetryList.begin(); 499 i != curRetryList.end(); ++i) { 500 DPRINTF(RubyPort, 501 "Sequencer may now be free. SendRetry to port %s\n", 502 (*i)->name()); 503 (*i)->onRetryList(false); 504 (*i)->sendRetry(); 505 } 506 } 507 508 testDrainComplete(); 509} 510 511void 512RubyPort::testDrainComplete() 513{ 514 //If we weren't able to drain before, we might be able to now. 515 if (drainEvent != NULL) { 516 unsigned int drainCount = getDrainCount(drainEvent); 517 DPRINTF(Config, "Drain count: %u\n", drainCount); 518 if (drainCount == 0) { 519 drainEvent->process(); 520 // Clear the drain event once we're done with it. 521 drainEvent = NULL; 522 } 523 } 524} 525 526unsigned int 527RubyPort::getDrainCount(Event *de) 528{ 529 int count = 0; 530 // 531 // If the sequencer is not empty, then requests need to drain. 532 // The outstandingCount is the number of requests outstanding and thus the 533 // number of times M5's timing port will process the drain event. 534 // 535 count += outstandingCount(); 536 537 DPRINTF(Config, "outstanding count %d\n", outstandingCount()); 538 539 // To simplify the draining process, the sequencer's deadlock detection 540 // event should have been descheduled. 541 assert(isDeadlockEventScheduled() == false); 542 543 if (pio_port.isConnected()) { 544 count += pio_port.drain(de); 545 DPRINTF(Config, "count after pio check %d\n", count); 546 } 547 if (physMemPort.isConnected()) { 548 count += physMemPort.drain(de); 549 DPRINTF(Config, "count after physmem check %d\n", count); 550 } 551 552 for (CpuPortIter p_iter = cpu_ports.begin(); p_iter != cpu_ports.end(); 553 p_iter++) { 554 M5Port* cpu_port = *p_iter; 555 count += cpu_port->drain(de); 556 DPRINTF(Config, "count after cpu port check %d\n", count); 557 } 558 559 DPRINTF(Config, "final count %d\n", count); 560 561 return count; 562} 563 564unsigned int 565RubyPort::drain(Event *de) 566{ 567 if (isDeadlockEventScheduled()) { 568 descheduleDeadlockEvent(); 569 } 570 571 int count = getDrainCount(de); 572 573 // Set status 574 if (count != 0) { 575 drainEvent = de; 576 577 changeState(SimObject::Draining); 578 return count; 579 } 580 581 changeState(SimObject::Drained); 582 return 0; 583} 584 585void 586RubyPort::M5Port::hitCallback(PacketPtr pkt) 587{ 588 bool needsResponse = pkt->needsResponse(); 589 590 // 591 // Unless specified at configuraiton, all responses except failed SC 592 // and Flush operations access M5 physical memory. 593 // 594 bool accessPhysMem = access_phys_mem; 595 596 if (pkt->isLLSC()) { 597 if (pkt->isWrite()) { 598 if (pkt->req->getExtraData() != 0) { 599 // 600 // Successful SC packets convert to normal writes 601 // 602 pkt->convertScToWrite(); 603 } else { 604 // 605 // Failed SC packets don't access physical memory and thus 606 // the RubyPort itself must convert it to a response. 607 // 608 accessPhysMem = false; 609 } 610 } else { 611 // 612 // All LL packets convert to normal loads so that M5 PhysMem does 613 // not lock the blocks. 614 // 615 pkt->convertLlToRead(); 616 } 617 } 618 619 // 620 // Flush requests don't access physical memory 621 // 622 if (pkt->isFlush()) { 623 accessPhysMem = false; 624 } 625 626 DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse); 627 628 if (accessPhysMem) { 629 ruby_port->physMemPort.sendAtomic(pkt); 630 } else if (needsResponse) { 631 pkt->makeResponse(); 632 } 633 634 // turn packet around to go back to requester if response expected 635 if (needsResponse) { 636 DPRINTF(RubyPort, "Sending packet back over port\n"); 637 sendNextCycle(pkt); 638 } else { 639 delete pkt; 640 } 641 DPRINTF(RubyPort, "Hit callback done!\n"); 642} 643 644bool 645RubyPort::M5Port::sendNextCycle(PacketPtr pkt) 646{ 647 //minimum latency, must be > 0 648 queue.schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 649 return true; 650} 651 652bool 653RubyPort::PioPort::sendNextCycle(PacketPtr pkt) 654{ 655 //minimum latency, must be > 0 656 queue.schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 657 return true; 658} 659 660bool 661RubyPort::M5Port::isPhysMemAddress(Addr addr) 662{ 663 AddrRangeList physMemAddrList = 664 ruby_port->physMemPort.getPeer()->getAddrRanges(); 665 for (AddrRangeIter iter = physMemAddrList.begin(); 666 iter != physMemAddrList.end(); 667 iter++) { 668 if (addr >= iter->start && addr <= iter->end) { 669 DPRINTF(RubyPort, "Request found in %#llx - %#llx range\n", 670 iter->start, iter->end); 671 return true; 672 } 673 } 674 return false; 675} 676 677unsigned 678RubyPort::M5Port::deviceBlockSize() const 679{ 680 return (unsigned) RubySystem::getBlockSizeBytes(); 681} 682 683void 684RubyPort::ruby_eviction_callback(const Address& address) 685{ 686 DPRINTF(RubyPort, "Sending invalidations.\n"); 687 Request req(address.getAddress(), 0, 0, Request::funcMasterId); 688 for (CpuPortIter it = cpu_ports.begin(); it != cpu_ports.end(); it++) { 689 Packet *pkt = new Packet(&req, MemCmd::InvalidationReq, -1); 690 (*it)->sendNextCycle(pkt); 691 } 692} 693