RubyPort.cc revision 8615:e66a566f2cfa
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/Ruby.hh" 31#include "mem/protocol/AccessPermission.hh" 32#include "mem/ruby/slicc_interface/AbstractController.hh" 33#include "mem/ruby/system/RubyPort.hh" 34#include "mem/physical.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 ruby_system = p->ruby_system; 55 waitingOnSequencer = false; 56} 57 58void 59RubyPort::init() 60{ 61 assert(m_controller != NULL); 62 m_mandatory_q_ptr = m_controller->getMandatoryQueue(); 63} 64 65Port * 66RubyPort::getPort(const std::string &if_name, int idx) 67{ 68 if (if_name == "port") { 69 return new M5Port(csprintf("%s-port%d", name(), idx), this, 70 ruby_system, access_phys_mem); 71 } 72 73 if (if_name == "pio_port") { 74 // ensure there is only one pio port 75 assert(pio_port == NULL); 76 77 pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), this); 78 79 return pio_port; 80 } 81 82 if (if_name == "physMemPort") { 83 // RubyPort should only have one port to physical memory 84 assert (physMemPort == NULL); 85 86 physMemPort = new M5Port(csprintf("%s-physMemPort", name()), this, 87 ruby_system, access_phys_mem); 88 89 return physMemPort; 90 } 91 92 if (if_name == "functional") { 93 // Calls for the functional port only want to access 94 // functional memory. Therefore, directly pass these calls 95 // ports to physmem. 96 assert(physmem != NULL); 97 return physmem->getPort(if_name, idx); 98 } 99 100 return NULL; 101} 102 103RubyPort::PioPort::PioPort(const std::string &_name, 104 RubyPort *_port) 105 : SimpleTimingPort(_name, _port) 106{ 107 DPRINTF(RubyPort, "creating port to ruby sequencer to cpu %s\n", _name); 108 ruby_port = _port; 109} 110 111RubyPort::M5Port::M5Port(const std::string &_name, RubyPort *_port, 112 RubySystem *_system, bool _access_phys_mem) 113 : SimpleTimingPort(_name, _port) 114{ 115 DPRINTF(RubyPort, "creating port from ruby sequcner to cpu %s\n", _name); 116 ruby_port = _port; 117 ruby_system = _system; 118 _onRetryList = false; 119 access_phys_mem = _access_phys_mem; 120} 121 122Tick 123RubyPort::PioPort::recvAtomic(PacketPtr pkt) 124{ 125 panic("RubyPort::PioPort::recvAtomic() not implemented!\n"); 126 return 0; 127} 128 129Tick 130RubyPort::M5Port::recvAtomic(PacketPtr pkt) 131{ 132 panic("RubyPort::M5Port::recvAtomic() not implemented!\n"); 133 return 0; 134} 135 136 137bool 138RubyPort::PioPort::recvTiming(PacketPtr pkt) 139{ 140 // In FS mode, ruby memory will receive pio responses from devices 141 // and it must forward these responses back to the particular CPU. 142 DPRINTF(RubyPort, "Pio response for address %#x\n", pkt->getAddr()); 143 144 assert(pkt->isResponse()); 145 146 // First we must retrieve the request port from the sender State 147 RubyPort::SenderState *senderState = 148 safe_cast<RubyPort::SenderState *>(pkt->senderState); 149 M5Port *port = senderState->port; 150 assert(port != NULL); 151 152 // pop the sender state from the packet 153 pkt->senderState = senderState->saved; 154 delete senderState; 155 156 port->sendTiming(pkt); 157 158 return true; 159} 160 161bool 162RubyPort::M5Port::recvTiming(PacketPtr pkt) 163{ 164 DPRINTF(RubyPort, 165 "Timing access caught for address %#x\n", pkt->getAddr()); 166 167 //dsm: based on SimpleTimingPort::recvTiming(pkt); 168 169 // The received packets should only be M5 requests, which should never 170 // get nacked. There used to be code to hanldle nacks here, but 171 // I'm pretty sure it didn't work correctly with the drain code, 172 // so that would need to be fixed if we ever added it back. 173 assert(pkt->isRequest()); 174 175 if (pkt->memInhibitAsserted()) { 176 warn("memInhibitAsserted???"); 177 // snooper will supply based on copy of packet 178 // still target's responsibility to delete packet 179 delete pkt; 180 return true; 181 } 182 183 // Save the port in the sender state object to be used later to 184 // route the response 185 pkt->senderState = new SenderState(this, pkt->senderState); 186 187 // Check for pio requests and directly send them to the dedicated 188 // pio port. 189 if (!isPhysMemAddress(pkt->getAddr())) { 190 assert(ruby_port->pio_port != NULL); 191 DPRINTF(RubyPort, 192 "Request for address 0x%#x is assumed to be a pio request\n", 193 pkt->getAddr()); 194 195 return ruby_port->pio_port->sendTiming(pkt); 196 } 197 198 assert(Address(pkt->getAddr()).getOffset() + pkt->getSize() <= 199 RubySystem::getBlockSizeBytes()); 200 201 // Submit the ruby request 202 RequestStatus requestStatus = ruby_port->makeRequest(pkt); 203 204 // If the request successfully issued then we should return true. 205 // Otherwise, we need to delete the senderStatus we just created and return 206 // false. 207 if (requestStatus == RequestStatus_Issued) { 208 DPRINTF(RubyPort, "Request %#x issued\n", pkt->getAddr()); 209 return true; 210 } 211 212 // 213 // Unless one is using the ruby tester, record the stalled M5 port for 214 // later retry when the sequencer becomes free. 215 // 216 if (!ruby_port->m_usingRubyTester) { 217 ruby_port->addToRetryList(this); 218 } 219 220 DPRINTF(RubyPort, 221 "Request for address %#x did not issue because %s\n", 222 pkt->getAddr(), RequestStatus_to_string(requestStatus)); 223 224 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState); 225 pkt->senderState = senderState->saved; 226 delete senderState; 227 return false; 228} 229 230bool 231RubyPort::M5Port::doFunctionalRead(PacketPtr pkt) 232{ 233 Address address(pkt->getAddr()); 234 Address line_address(address); 235 line_address.makeLineAddress(); 236 237 AccessPermission access_perm = AccessPermission_NotPresent; 238 int num_controllers = ruby_system->m_abs_cntrl_vec.size(); 239 240 DPRINTF(RubyPort, "Functional Read request for %s\n",address); 241 242 unsigned int num_ro = 0; 243 unsigned int num_rw = 0; 244 unsigned int num_busy = 0; 245 unsigned int num_backing_store = 0; 246 unsigned int num_invalid = 0; 247 248 // In this loop we count the number of controllers that have the given 249 // address in read only, read write and busy states. 250 for (int i = 0; i < num_controllers; ++i) { 251 access_perm = ruby_system->m_abs_cntrl_vec[i]-> 252 getAccessPermission(line_address); 253 if (access_perm == AccessPermission_Read_Only) 254 num_ro++; 255 else if (access_perm == AccessPermission_Read_Write) 256 num_rw++; 257 else if (access_perm == AccessPermission_Busy) 258 num_busy++; 259 else if (access_perm == AccessPermission_Backing_Store) 260 // See RubySlicc_Exports.sm for details, but Backing_Store is meant 261 // to represent blocks in memory *for Broadcast/Snooping protocols*, 262 // where memory has no idea whether it has an exclusive copy of data 263 // or not. 264 num_backing_store++; 265 else if (access_perm == AccessPermission_Invalid || 266 access_perm == AccessPermission_NotPresent) 267 num_invalid++; 268 } 269 assert(num_rw <= 1); 270 271 uint8* data = pkt->getPtr<uint8_t>(true); 272 unsigned int size_in_bytes = pkt->getSize(); 273 unsigned startByte = address.getAddress() - line_address.getAddress(); 274 275 // This if case is meant to capture what happens in a Broadcast/Snoop 276 // protocol where the block does not exist in the cache hierarchy. You 277 // only want to read from the Backing_Store memory if there is no copy in 278 // the cache hierarchy, otherwise you want to try to read the RO or RW 279 // copies existing in the cache hierarchy (covered by the else statement). 280 // The reason is because the Backing_Store memory could easily be stale, if 281 // there are copies floating around the cache hierarchy, so you want to read 282 // it only if it's not in the cache hierarchy at all. 283 if (num_invalid == (num_controllers - 1) && 284 num_backing_store == 1) 285 { 286 DPRINTF(RubyPort, "only copy in Backing_Store memory, read from it\n"); 287 for (int i = 0; i < num_controllers; ++i) { 288 access_perm = ruby_system->m_abs_cntrl_vec[i] 289 ->getAccessPermission(line_address); 290 if (access_perm == AccessPermission_Backing_Store) { 291 DataBlock& block = ruby_system->m_abs_cntrl_vec[i] 292 ->getDataBlock(line_address); 293 294 DPRINTF(RubyPort, "reading from %s block %s\n", 295 ruby_system->m_abs_cntrl_vec[i]->name(), block); 296 for (unsigned i = 0; i < size_in_bytes; ++i) { 297 data[i] = block.getByte(i + startByte); 298 } 299 return true; 300 } 301 } 302 } else { 303 // In Broadcast/Snoop protocols, this covers if you know the block 304 // exists somewhere in the caching hierarchy, then you want to read any 305 // valid RO or RW block. In directory protocols, same thing, you want 306 // to read any valid readable copy of the block. 307 DPRINTF(RubyPort, "num_busy = %d, num_ro = %d, num_rw = %d\n", 308 num_busy, num_ro, num_rw); 309 // In this loop, we try to figure which controller has a read only or 310 // a read write copy of the given address. Any valid copy would suffice 311 // for a functional read. 312 for(int i = 0;i < num_controllers;++i) { 313 access_perm = ruby_system->m_abs_cntrl_vec[i] 314 ->getAccessPermission(line_address); 315 if(access_perm == AccessPermission_Read_Only || 316 access_perm == AccessPermission_Read_Write) 317 { 318 DataBlock& block = ruby_system->m_abs_cntrl_vec[i] 319 ->getDataBlock(line_address); 320 321 DPRINTF(RubyPort, "reading from %s block %s\n", 322 ruby_system->m_abs_cntrl_vec[i]->name(), block); 323 for (unsigned i = 0; i < size_in_bytes; ++i) { 324 data[i] = block.getByte(i + startByte); 325 } 326 return true; 327 } 328 } 329 } 330 return false; 331} 332 333bool 334RubyPort::M5Port::doFunctionalWrite(PacketPtr pkt) 335{ 336 Address addr(pkt->getAddr()); 337 Address line_addr = line_address(addr); 338 AccessPermission access_perm = AccessPermission_NotPresent; 339 int num_controllers = ruby_system->m_abs_cntrl_vec.size(); 340 341 DPRINTF(RubyPort, "Functional Write request for %s\n",addr); 342 343 unsigned int num_ro = 0; 344 unsigned int num_rw = 0; 345 unsigned int num_busy = 0; 346 unsigned int num_backing_store = 0; 347 unsigned int num_invalid = 0; 348 349 // In this loop we count the number of controllers that have the given 350 // address in read only, read write and busy states. 351 for(int i = 0;i < num_controllers;++i) { 352 access_perm = ruby_system->m_abs_cntrl_vec[i]-> 353 getAccessPermission(line_addr); 354 if (access_perm == AccessPermission_Read_Only) 355 num_ro++; 356 else if (access_perm == AccessPermission_Read_Write) 357 num_rw++; 358 else if (access_perm == AccessPermission_Busy) 359 num_busy++; 360 else if (access_perm == AccessPermission_Backing_Store) 361 // See RubySlicc_Exports.sm for details, but Backing_Store is meant 362 // to represent blocks in memory *for Broadcast/Snooping protocols*, 363 // where memory has no idea whether it has an exclusive copy of data 364 // or not. 365 num_backing_store++; 366 else if (access_perm == AccessPermission_Invalid || 367 access_perm == AccessPermission_NotPresent) 368 num_invalid++; 369 } 370 371 // If the number of read write copies is more than 1, then there is bug in 372 // coherence protocol. Otherwise, if all copies are in stable states, i.e. 373 // num_busy == 0, we update all the copies. If there is at least one copy 374 // in busy state, then we check if there is read write copy. If yes, then 375 // also we let the access go through. Or, if there is no copy in the cache 376 // hierarchy at all, we still want to do the write to the memory 377 // (Backing_Store) instead of failing. 378 379 DPRINTF(RubyPort, "num_busy = %d, num_ro = %d, num_rw = %d\n", 380 num_busy, num_ro, num_rw); 381 assert(num_rw <= 1); 382 383 uint8* data = pkt->getPtr<uint8_t>(true); 384 unsigned int size_in_bytes = pkt->getSize(); 385 unsigned startByte = addr.getAddress() - line_addr.getAddress(); 386 387 if ((num_busy == 0 && num_ro > 0) || num_rw == 1 || 388 (num_invalid == (num_controllers - 1) && num_backing_store == 1)) 389 { 390 for(int i = 0; i < num_controllers;++i) { 391 access_perm = ruby_system->m_abs_cntrl_vec[i]-> 392 getAccessPermission(line_addr); 393 if(access_perm == AccessPermission_Read_Only || 394 access_perm == AccessPermission_Read_Write|| 395 access_perm == AccessPermission_Maybe_Stale || 396 access_perm == AccessPermission_Backing_Store) 397 { 398 DataBlock& block = ruby_system->m_abs_cntrl_vec[i] 399 ->getDataBlock(line_addr); 400 401 DPRINTF(RubyPort, "%s\n",block); 402 for (unsigned i = 0; i < size_in_bytes; ++i) { 403 block.setByte(i + startByte, data[i]); 404 } 405 DPRINTF(RubyPort, "%s\n",block); 406 } 407 } 408 return true; 409 } 410 return false; 411} 412 413void 414RubyPort::M5Port::recvFunctional(PacketPtr pkt) 415{ 416 DPRINTF(RubyPort, "Functional access caught for address %#x\n", 417 pkt->getAddr()); 418 419 // Check for pio requests and directly send them to the dedicated 420 // pio port. 421 if (!isPhysMemAddress(pkt->getAddr())) { 422 assert(ruby_port->pio_port != NULL); 423 DPRINTF(RubyPort, "Request for address 0x%#x is a pio request\n", 424 pkt->getAddr()); 425 panic("RubyPort::PioPort::recvFunctional() not implemented!\n"); 426 } 427 428 assert(pkt->getAddr() + pkt->getSize() <= 429 line_address(Address(pkt->getAddr())).getAddress() + 430 RubySystem::getBlockSizeBytes()); 431 432 bool accessSucceeded = false; 433 bool needsResponse = pkt->needsResponse(); 434 435 // Do the functional access on ruby memory 436 if (pkt->isRead()) { 437 accessSucceeded = doFunctionalRead(pkt); 438 } else if (pkt->isWrite()) { 439 accessSucceeded = doFunctionalWrite(pkt); 440 } else { 441 panic("RubyPort: unsupported functional command %s\n", 442 pkt->cmdString()); 443 } 444 445 // Unless the requester explicitly said otherwise, generate an error if 446 // the functional request failed 447 if (!accessSucceeded && !pkt->suppressFuncError()) { 448 fatal("Ruby functional %s failed for address %#x\n", 449 pkt->isWrite() ? "write" : "read", pkt->getAddr()); 450 } 451 452 if (access_phys_mem) { 453 // The attached physmem contains the official version of data. 454 // The following command performs the real functional access. 455 // This line should be removed once Ruby supplies the official version 456 // of data. 457 ruby_port->physMemPort->sendFunctional(pkt); 458 } 459 460 // turn packet around to go back to requester if response expected 461 if (needsResponse) { 462 pkt->setFunctionalResponseStatus(accessSucceeded); 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 513void 514RubyPort::M5Port::hitCallback(PacketPtr pkt) 515{ 516 bool needsResponse = pkt->needsResponse(); 517 518 // 519 // Unless specified at configuraiton, all responses except failed SC 520 // and Flush operations access M5 physical memory. 521 // 522 bool accessPhysMem = access_phys_mem; 523 524 if (pkt->isLLSC()) { 525 if (pkt->isWrite()) { 526 if (pkt->req->getExtraData() != 0) { 527 // 528 // Successful SC packets convert to normal writes 529 // 530 pkt->convertScToWrite(); 531 } else { 532 // 533 // Failed SC packets don't access physical memory and thus 534 // the RubyPort itself must convert it to a response. 535 // 536 accessPhysMem = false; 537 } 538 } else { 539 // 540 // All LL packets convert to normal loads so that M5 PhysMem does 541 // not lock the blocks. 542 // 543 pkt->convertLlToRead(); 544 } 545 } 546 547 // 548 // Flush requests don't access physical memory 549 // 550 if (pkt->isFlush()) { 551 accessPhysMem = false; 552 } 553 554 DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse); 555 556 if (accessPhysMem) { 557 ruby_port->physMemPort->sendAtomic(pkt); 558 } else if (needsResponse) { 559 pkt->makeResponse(); 560 } 561 562 // turn packet around to go back to requester if response expected 563 if (needsResponse) { 564 DPRINTF(RubyPort, "Sending packet back over port\n"); 565 sendTiming(pkt); 566 } else { 567 delete pkt; 568 } 569 DPRINTF(RubyPort, "Hit callback done!\n"); 570} 571 572bool 573RubyPort::M5Port::sendTiming(PacketPtr pkt) 574{ 575 //minimum latency, must be > 0 576 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 577 return true; 578} 579 580bool 581RubyPort::PioPort::sendTiming(PacketPtr pkt) 582{ 583 //minimum latency, must be > 0 584 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 585 return true; 586} 587 588bool 589RubyPort::M5Port::isPhysMemAddress(Addr addr) 590{ 591 AddrRangeList physMemAddrList; 592 bool snoop = false; 593 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop); 594 for (AddrRangeIter iter = physMemAddrList.begin(); 595 iter != physMemAddrList.end(); 596 iter++) { 597 if (addr >= iter->start && addr <= iter->end) { 598 DPRINTF(RubyPort, "Request found in %#llx - %#llx range\n", 599 iter->start, iter->end); 600 return true; 601 } 602 } 603 return false; 604} 605 606unsigned 607RubyPort::M5Port::deviceBlockSize() const 608{ 609 return (unsigned) RubySystem::getBlockSizeBytes(); 610} 611