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