RubyPort.cc revision 8505:442804117f95
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 waitingOnSequencer = false; 60} 61 62void 63RubyPort::init() 64{ 65 assert(m_controller != NULL); 66 m_mandatory_q_ptr = m_controller->getMandatoryQueue(); 67} 68 69Port * 70RubyPort::getPort(const std::string &if_name, int idx) 71{ 72 if (if_name == "port") { 73 return new M5Port(csprintf("%s-port%d", name(), idx), this, 74 ruby_system, access_phys_mem); 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 // For DMA and CPU requests, translate them to ruby requests before 203 // sending them to our assigned ruby port. 204 RubyRequestType type = RubyRequestType_NULL; 205 206 // If valid, copy the pc to the ruby request 207 Addr pc = 0; 208 if (pkt->req->hasPC()) { 209 pc = pkt->req->getPC(); 210 } 211 212 if (pkt->isLLSC()) { 213 if (pkt->isWrite()) { 214 DPRINTF(RubyPort, "Issuing SC\n"); 215 type = RubyRequestType_Store_Conditional; 216 } else { 217 DPRINTF(RubyPort, "Issuing LL\n"); 218 assert(pkt->isRead()); 219 type = RubyRequestType_Load_Linked; 220 } 221 } else if (pkt->req->isLocked()) { 222 if (pkt->isWrite()) { 223 DPRINTF(RubyPort, "Issuing Locked RMW Write\n"); 224 type = RubyRequestType_Locked_RMW_Write; 225 } else { 226 DPRINTF(RubyPort, "Issuing Locked RMW Read\n"); 227 assert(pkt->isRead()); 228 type = RubyRequestType_Locked_RMW_Read; 229 } 230 } else { 231 if (pkt->isRead()) { 232 if (pkt->req->isInstFetch()) { 233 type = RubyRequestType_IFETCH; 234 } else { 235#if THE_ISA == X86_ISA 236 uint32_t flags = pkt->req->getFlags(); 237 bool storeCheck = flags & 238 (TheISA::StoreCheck << TheISA::FlagShift); 239#else 240 bool storeCheck = false; 241#endif // X86_ISA 242 if (storeCheck) { 243 type = RubyRequestType_RMW_Read; 244 } else { 245 type = RubyRequestType_LD; 246 } 247 } 248 } else if (pkt->isWrite()) { 249 // 250 // Note: M5 packets do not differentiate ST from RMW_Write 251 // 252 type = RubyRequestType_ST; 253 } else if (pkt->isFlush()) { 254 type = RubyRequestType_FLUSH; 255 } else { 256 panic("Unsupported ruby packet type\n"); 257 } 258 } 259 260 RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(true), 261 pkt->getSize(), pc, type, 262 RubyAccessMode_Supervisor, pkt); 263 264 assert(ruby_request.m_PhysicalAddress.getOffset() + ruby_request.m_Size <= 265 RubySystem::getBlockSizeBytes()); 266 267 // Submit the ruby request 268 RequestStatus requestStatus = ruby_port->makeRequest(ruby_request); 269 270 // If the request successfully issued then we should return true. 271 // Otherwise, we need to delete the senderStatus we just created and return 272 // false. 273 if (requestStatus == RequestStatus_Issued) { 274 DPRINTF(RubyPort, "Request %#x issued\n", pkt->getAddr()); 275 return true; 276 } 277 278 // 279 // Unless one is using the ruby tester, record the stalled M5 port for 280 // later retry when the sequencer becomes free. 281 // 282 if (!ruby_port->m_usingRubyTester) { 283 ruby_port->addToRetryList(this); 284 } 285 286 DPRINTF(RubyPort, 287 "Request for address %#x did not issue because %s\n", 288 pkt->getAddr(), RequestStatus_to_string(requestStatus)); 289 290 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState); 291 pkt->senderState = senderState->saved; 292 delete senderState; 293 return false; 294} 295 296bool 297RubyPort::M5Port::doFunctionalRead(PacketPtr pkt) 298{ 299 Address address(pkt->getAddr()); 300 Address line_address(address); 301 line_address.makeLineAddress(); 302 303 AccessPermission accessPerm = AccessPermission_NotPresent; 304 int num_controllers = ruby_system->m_abs_cntrl_vec.size(); 305 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 310 DPRINTF(RubyPort, "Functional Read request for %s\n",address); 311 for(int i = 0;i < num_controllers;++i) 312 { 313 accessPerm = ruby_system->m_abs_cntrl_vec[i] 314 ->getAccessPermission(line_address); 315 if(accessPerm == AccessPermission_Read_Only || 316 accessPerm == AccessPermission_Read_Write) 317 { 318 unsigned startByte = address.getAddress() - line_address.getAddress(); 319 320 uint8* data = pkt->getPtr<uint8_t>(true); 321 unsigned int size_in_bytes = pkt->getSize(); 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 { 329 data[i] = block.getByte(i + startByte); 330 } 331 return true; 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 accessPerm = 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 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 { 355 accessPerm = ruby_system->m_abs_cntrl_vec[i]-> 356 getAccessPermission(line_addr); 357 if(accessPerm == AccessPermission_Read_Only) num_ro++; 358 else if(accessPerm == AccessPermission_Read_Write) num_rw++; 359 else if(accessPerm == AccessPermission_Busy) num_busy++; 360 } 361 362 // If the number of read write copies is more than 1, then there is bug in 363 // coherence protocol. Otherwise, if all copies are in stable states, i.e. 364 // num_busy == 0, we update all the copies. If there is at least one copy 365 // in busy state, then we check if there is read write copy. If yes, then 366 // also we let the access go through. 367 368 DPRINTF(RubyPort, "num_busy = %d, num_ro = %d, num_rw = %d\n", 369 num_busy, num_ro, num_rw); 370 assert(num_rw <= 1); 371 if((num_busy == 0 && num_ro > 0) || num_rw == 1) 372 { 373 uint8* data = pkt->getPtr<uint8_t>(true); 374 unsigned int size_in_bytes = pkt->getSize(); 375 unsigned startByte = addr.getAddress() - line_addr.getAddress(); 376 377 for(int i = 0; i < num_controllers;++i) 378 { 379 accessPerm = ruby_system->m_abs_cntrl_vec[i]-> 380 getAccessPermission(line_addr); 381 if(accessPerm == AccessPermission_Read_Only || 382 accessPerm == AccessPermission_Read_Write|| 383 accessPerm == AccessPermission_Maybe_Stale) 384 { 385 DataBlock& block = ruby_system->m_abs_cntrl_vec[i] 386 ->getDataBlock(line_addr); 387 388 DPRINTF(RubyPort, "%s\n",block); 389 for (unsigned i = 0; i < size_in_bytes; ++i) 390 { 391 block.setByte(i + startByte, data[i]); 392 } 393 DPRINTF(RubyPort, "%s\n",block); 394 } 395 } 396 return true; 397 } 398 return false; 399} 400 401void 402RubyPort::M5Port::recvFunctional(PacketPtr pkt) 403{ 404 DPRINTF(RubyPort, "Functional access caught for address %#x\n", 405 pkt->getAddr()); 406 407 // Check for pio requests and directly send them to the dedicated 408 // pio port. 409 if (!isPhysMemAddress(pkt->getAddr())) { 410 assert(ruby_port->pio_port != NULL); 411 DPRINTF(RubyPort, "Request for address 0x%#x is a pio request\n", 412 pkt->getAddr()); 413 panic("RubyPort::PioPort::recvFunctional() not implemented!\n"); 414 } 415 416 assert(pkt->getAddr() + pkt->getSize() <= 417 line_address(Address(pkt->getAddr())).getAddress() + 418 RubySystem::getBlockSizeBytes()); 419 420 bool accessSucceeded = false; 421 bool needsResponse = pkt->needsResponse(); 422 423 // Do the functional access on ruby memory 424 if (pkt->isRead()) { 425 accessSucceeded = doFunctionalRead(pkt); 426 } else if (pkt->isWrite()) { 427 accessSucceeded = doFunctionalWrite(pkt); 428 } else { 429 panic("RubyPort: unsupported functional command %s\n", 430 pkt->cmdString()); 431 } 432 433 // Unless the requester explicitly said otherwise, generate an error if 434 // the functional request failed 435 if (!accessSucceeded && !pkt->suppressFuncError()) { 436 fatal("Ruby functional %s failed for address %#x\n", 437 pkt->isWrite() ? "write" : "read", pkt->getAddr()); 438 } 439 440 if (access_phys_mem) { 441 // The attached physmem contains the official version of data. 442 // The following command performs the real functional access. 443 // This line should be removed once Ruby supplies the official version 444 // of data. 445 ruby_port->physMemPort->sendFunctional(pkt); 446 } 447 448 // turn packet around to go back to requester if response expected 449 if (needsResponse) { 450 pkt->setFunctionalResponseStatus(accessSucceeded); 451 DPRINTF(RubyPort, "Sending packet back over port\n"); 452 sendFunctional(pkt); 453 } 454 DPRINTF(RubyPort, "Functional access %s!\n", 455 accessSucceeded ? "successful":"failed"); 456} 457 458void 459RubyPort::ruby_hit_callback(PacketPtr pkt) 460{ 461 // Retrieve the request port from the sender State 462 RubyPort::SenderState *senderState = 463 safe_cast<RubyPort::SenderState *>(pkt->senderState); 464 M5Port *port = senderState->port; 465 assert(port != NULL); 466 467 // pop the sender state from the packet 468 pkt->senderState = senderState->saved; 469 delete senderState; 470 471 port->hitCallback(pkt); 472 473 // 474 // If we had to stall the M5Ports, wake them up because the sequencer 475 // likely has free resources now. 476 // 477 if (waitingOnSequencer) { 478 // 479 // Record the current list of ports to retry on a temporary list before 480 // calling sendRetry on those ports. sendRetry will cause an 481 // immediate retry, which may result in the ports being put back on the 482 // list. Therefore we want to clear the retryList before calling 483 // sendRetry. 484 // 485 std::list<M5Port*> curRetryList(retryList); 486 487 retryList.clear(); 488 waitingOnSequencer = false; 489 490 for (std::list<M5Port*>::iterator i = curRetryList.begin(); 491 i != curRetryList.end(); ++i) { 492 DPRINTF(RubyPort, 493 "Sequencer may now be free. SendRetry to port %s\n", 494 (*i)->name()); 495 (*i)->onRetryList(false); 496 (*i)->sendRetry(); 497 } 498 } 499} 500 501void 502RubyPort::M5Port::hitCallback(PacketPtr pkt) 503{ 504 bool needsResponse = pkt->needsResponse(); 505 506 // 507 // Unless specified at configuraiton, all responses except failed SC 508 // and Flush operations access M5 physical memory. 509 // 510 bool accessPhysMem = access_phys_mem; 511 512 if (pkt->isLLSC()) { 513 if (pkt->isWrite()) { 514 if (pkt->req->getExtraData() != 0) { 515 // 516 // Successful SC packets convert to normal writes 517 // 518 pkt->convertScToWrite(); 519 } else { 520 // 521 // Failed SC packets don't access physical memory and thus 522 // the RubyPort itself must convert it to a response. 523 // 524 accessPhysMem = false; 525 } 526 } else { 527 // 528 // All LL packets convert to normal loads so that M5 PhysMem does 529 // not lock the blocks. 530 // 531 pkt->convertLlToRead(); 532 } 533 } 534 535 // 536 // Flush requests don't access physical memory 537 // 538 if (pkt->isFlush()) { 539 accessPhysMem = false; 540 } 541 542 DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse); 543 544 if (accessPhysMem) { 545 ruby_port->physMemPort->sendAtomic(pkt); 546 } else if (needsResponse) { 547 pkt->makeResponse(); 548 } 549 550 // turn packet around to go back to requester if response expected 551 if (needsResponse) { 552 DPRINTF(RubyPort, "Sending packet back over port\n"); 553 sendTiming(pkt); 554 } else { 555 delete pkt; 556 } 557 DPRINTF(RubyPort, "Hit callback done!\n"); 558} 559 560bool 561RubyPort::M5Port::sendTiming(PacketPtr pkt) 562{ 563 //minimum latency, must be > 0 564 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 565 return true; 566} 567 568bool 569RubyPort::PioPort::sendTiming(PacketPtr pkt) 570{ 571 //minimum latency, must be > 0 572 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 573 return true; 574} 575 576bool 577RubyPort::M5Port::isPhysMemAddress(Addr addr) 578{ 579 AddrRangeList physMemAddrList; 580 bool snoop = false; 581 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop); 582 for (AddrRangeIter iter = physMemAddrList.begin(); 583 iter != physMemAddrList.end(); 584 iter++) { 585 if (addr >= iter->start && addr <= iter->end) { 586 DPRINTF(RubyPort, "Request found in %#llx - %#llx range\n", 587 iter->start, iter->end); 588 return true; 589 } 590 } 591 return false; 592} 593 594unsigned 595RubyPort::M5Port::deviceBlockSize() const 596{ 597 return (unsigned) RubySystem::getBlockSizeBytes(); 598} 599