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