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