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