RubyPort.cc revision 8229:78bf55f23338
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 "mem/ruby/slicc_interface/AbstractController.hh" 35#include "mem/ruby/system/RubyPort.hh" 36#include "mem/physical.hh" 37 38RubyPort::RubyPort(const Params *p) 39 : MemObject(p) 40{ 41 m_version = p->version; 42 assert(m_version != -1); 43 44 physmem = p->physmem; 45 46 m_controller = NULL; 47 m_mandatory_q_ptr = NULL; 48 49 m_request_cnt = 0; 50 pio_port = NULL; 51 physMemPort = NULL; 52 53 m_usingRubyTester = p->using_ruby_tester; 54 access_phys_mem = p->access_phys_mem; 55} 56 57void 58RubyPort::init() 59{ 60 assert(m_controller != NULL); 61 m_mandatory_q_ptr = m_controller->getMandatoryQueue(); 62} 63 64Port * 65RubyPort::getPort(const std::string &if_name, int idx) 66{ 67 if (if_name == "port") { 68 return new M5Port(csprintf("%s-port%d", name(), idx), this, 69 access_phys_mem); 70 } 71 72 if (if_name == "pio_port") { 73 // ensure there is only one pio port 74 assert(pio_port == NULL); 75 76 pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), this); 77 78 return pio_port; 79 } 80 81 if (if_name == "physMemPort") { 82 // RubyPort should only have one port to physical memory 83 assert (physMemPort == NULL); 84 85 physMemPort = new M5Port(csprintf("%s-physMemPort", name()), this, 86 access_phys_mem); 87 88 return physMemPort; 89 } 90 91 if (if_name == "functional") { 92 // Calls for the functional port only want to access 93 // functional memory. Therefore, directly pass these calls 94 // ports to physmem. 95 assert(physmem != NULL); 96 return physmem->getPort(if_name, idx); 97 } 98 99 return NULL; 100} 101 102RubyPort::PioPort::PioPort(const std::string &_name, 103 RubyPort *_port) 104 : SimpleTimingPort(_name, _port) 105{ 106 DPRINTF(RubyPort, "creating port to ruby sequencer to cpu %s\n", _name); 107 ruby_port = _port; 108} 109 110RubyPort::M5Port::M5Port(const std::string &_name, 111 RubyPort *_port, bool _access_phys_mem) 112 : SimpleTimingPort(_name, _port) 113{ 114 DPRINTF(RubyPort, "creating port from ruby sequcner to cpu %s\n", _name); 115 ruby_port = _port; 116 _onRetryList = false; 117 access_phys_mem = _access_phys_mem; 118} 119 120Tick 121RubyPort::PioPort::recvAtomic(PacketPtr pkt) 122{ 123 panic("RubyPort::PioPort::recvAtomic() not implemented!\n"); 124 return 0; 125} 126 127Tick 128RubyPort::M5Port::recvAtomic(PacketPtr pkt) 129{ 130 panic("RubyPort::M5Port::recvAtomic() not implemented!\n"); 131 return 0; 132} 133 134 135bool 136RubyPort::PioPort::recvTiming(PacketPtr pkt) 137{ 138 // In FS mode, ruby memory will receive pio responses from devices 139 // and it must forward these responses back to the particular CPU. 140 DPRINTF(RubyPort, "Pio response for address %#x\n", pkt->getAddr()); 141 142 assert(pkt->isResponse()); 143 144 // First we must retrieve the request port from the sender State 145 RubyPort::SenderState *senderState = 146 safe_cast<RubyPort::SenderState *>(pkt->senderState); 147 M5Port *port = senderState->port; 148 assert(port != NULL); 149 150 // pop the sender state from the packet 151 pkt->senderState = senderState->saved; 152 delete senderState; 153 154 port->sendTiming(pkt); 155 156 return true; 157} 158 159bool 160RubyPort::M5Port::recvTiming(PacketPtr pkt) 161{ 162 DPRINTF(RubyPort, 163 "Timing access caught for address %#x\n", pkt->getAddr()); 164 165 //dsm: based on SimpleTimingPort::recvTiming(pkt); 166 167 // The received packets should only be M5 requests, which should never 168 // get nacked. There used to be code to hanldle nacks here, but 169 // I'm pretty sure it didn't work correctly with the drain code, 170 // so that would need to be fixed if we ever added it back. 171 assert(pkt->isRequest()); 172 173 if (pkt->memInhibitAsserted()) { 174 warn("memInhibitAsserted???"); 175 // snooper will supply based on copy of packet 176 // still target's responsibility to delete packet 177 delete pkt; 178 return true; 179 } 180 181 // Save the port in the sender state object to be used later to 182 // route the response 183 pkt->senderState = new SenderState(this, pkt->senderState); 184 185 // Check for pio requests and directly send them to the dedicated 186 // pio port. 187 if (!isPhysMemAddress(pkt->getAddr())) { 188 assert(ruby_port->pio_port != NULL); 189 DPRINTF(RubyPort, 190 "Request for address 0x%#x is assumed to be a pio request\n", 191 pkt->getAddr()); 192 193 return ruby_port->pio_port->sendTiming(pkt); 194 } 195 196 // For DMA and CPU requests, translate them to ruby requests before 197 // sending them to our assigned ruby port. 198 RubyRequestType type = RubyRequestType_NULL; 199 200 // If valid, copy the pc to the ruby request 201 Addr pc = 0; 202 if (pkt->req->hasPC()) { 203 pc = pkt->req->getPC(); 204 } 205 206 if (pkt->isLLSC()) { 207 if (pkt->isWrite()) { 208 DPRINTF(RubyPort, "Issuing SC\n"); 209 type = RubyRequestType_Store_Conditional; 210 } else { 211 DPRINTF(RubyPort, "Issuing LL\n"); 212 assert(pkt->isRead()); 213 type = RubyRequestType_Load_Linked; 214 } 215 } else if (pkt->req->isLocked()) { 216 if (pkt->isWrite()) { 217 DPRINTF(RubyPort, "Issuing Locked RMW Write\n"); 218 type = RubyRequestType_Locked_RMW_Write; 219 } else { 220 DPRINTF(RubyPort, "Issuing Locked RMW Read\n"); 221 assert(pkt->isRead()); 222 type = RubyRequestType_Locked_RMW_Read; 223 } 224 } else { 225 if (pkt->isRead()) { 226 if (pkt->req->isInstFetch()) { 227 type = RubyRequestType_IFETCH; 228 } else { 229#if THE_ISA == X86_ISA 230 uint32_t flags = pkt->req->getFlags(); 231 bool storeCheck = flags & 232 (TheISA::StoreCheck << TheISA::FlagShift); 233#else 234 bool storeCheck = false; 235#endif // X86_ISA 236 if (storeCheck) { 237 type = RubyRequestType_RMW_Read; 238 } else { 239 type = RubyRequestType_LD; 240 } 241 } 242 } else if (pkt->isWrite()) { 243 // 244 // Note: M5 packets do not differentiate ST from RMW_Write 245 // 246 type = RubyRequestType_ST; 247 } else if (pkt->isFlush()) { 248 type = RubyRequestType_FLUSH; 249 } else { 250 panic("Unsupported ruby packet type\n"); 251 } 252 } 253 254 RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(true), 255 pkt->getSize(), pc, type, 256 RubyAccessMode_Supervisor, pkt); 257 258 assert(ruby_request.m_PhysicalAddress.getOffset() + ruby_request.m_Size <= 259 RubySystem::getBlockSizeBytes()); 260 261 // Submit the ruby request 262 RequestStatus requestStatus = ruby_port->makeRequest(ruby_request); 263 264 // If the request successfully issued then we should return true. 265 // Otherwise, we need to delete the senderStatus we just created and return 266 // false. 267 if (requestStatus == RequestStatus_Issued) { 268 DPRINTF(RubyPort, "Request %#x issued\n", pkt->getAddr()); 269 return true; 270 } 271 272 // 273 // Unless one is using the ruby tester, record the stalled M5 port for 274 // later retry when the sequencer becomes free. 275 // 276 if (!ruby_port->m_usingRubyTester) { 277 ruby_port->addToRetryList(this); 278 } 279 280 DPRINTF(RubyPort, 281 "Request for address %#x did not issue because %s\n", 282 pkt->getAddr(), RequestStatus_to_string(requestStatus)); 283 284 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState); 285 pkt->senderState = senderState->saved; 286 delete senderState; 287 return false; 288} 289 290void 291RubyPort::ruby_hit_callback(PacketPtr pkt) 292{ 293 // Retrieve the request port from the sender State 294 RubyPort::SenderState *senderState = 295 safe_cast<RubyPort::SenderState *>(pkt->senderState); 296 M5Port *port = senderState->port; 297 assert(port != NULL); 298 299 // pop the sender state from the packet 300 pkt->senderState = senderState->saved; 301 delete senderState; 302 303 port->hitCallback(pkt); 304 305 // 306 // If we had to stall the M5Ports, wake them up because the sequencer 307 // likely has free resources now. 308 // 309 if (waitingOnSequencer) { 310 // 311 // Record the current list of ports to retry on a temporary list before 312 // calling sendRetry on those ports. sendRetry will cause an 313 // immediate retry, which may result in the ports being put back on the 314 // list. Therefore we want to clear the retryList before calling 315 // sendRetry. 316 // 317 std::list<M5Port*> curRetryList(retryList); 318 319 retryList.clear(); 320 waitingOnSequencer = false; 321 322 for (std::list<M5Port*>::iterator i = curRetryList.begin(); 323 i != curRetryList.end(); ++i) { 324 DPRINTF(RubyPort, 325 "Sequencer may now be free. SendRetry to port %s\n", 326 (*i)->name()); 327 (*i)->onRetryList(false); 328 (*i)->sendRetry(); 329 } 330 } 331} 332 333void 334RubyPort::M5Port::hitCallback(PacketPtr pkt) 335{ 336 bool needsResponse = pkt->needsResponse(); 337 338 // 339 // Unless specified at configuraiton, all responses except failed SC 340 // and Flush operations access M5 physical memory. 341 // 342 bool accessPhysMem = access_phys_mem; 343 344 if (pkt->isLLSC()) { 345 if (pkt->isWrite()) { 346 if (pkt->req->getExtraData() != 0) { 347 // 348 // Successful SC packets convert to normal writes 349 // 350 pkt->convertScToWrite(); 351 } else { 352 // 353 // Failed SC packets don't access physical memory and thus 354 // the RubyPort itself must convert it to a response. 355 // 356 accessPhysMem = false; 357 } 358 } else { 359 // 360 // All LL packets convert to normal loads so that M5 PhysMem does 361 // not lock the blocks. 362 // 363 pkt->convertLlToRead(); 364 } 365 } 366 367 // 368 // Flush requests don't access physical memory 369 // 370 if (pkt->isFlush()) { 371 accessPhysMem = false; 372 } 373 374 DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse); 375 376 if (accessPhysMem) { 377 ruby_port->physMemPort->sendAtomic(pkt); 378 } else if (needsResponse) { 379 pkt->makeResponse(); 380 } 381 382 // turn packet around to go back to requester if response expected 383 if (needsResponse) { 384 DPRINTF(RubyPort, "Sending packet back over port\n"); 385 sendTiming(pkt); 386 } else { 387 delete pkt; 388 } 389 DPRINTF(RubyPort, "Hit callback done!\n"); 390} 391 392bool 393RubyPort::M5Port::sendTiming(PacketPtr pkt) 394{ 395 //minimum latency, must be > 0 396 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 397 return true; 398} 399 400bool 401RubyPort::PioPort::sendTiming(PacketPtr pkt) 402{ 403 //minimum latency, must be > 0 404 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock())); 405 return true; 406} 407 408bool 409RubyPort::M5Port::isPhysMemAddress(Addr addr) 410{ 411 AddrRangeList physMemAddrList; 412 bool snoop = false; 413 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop); 414 for (AddrRangeIter iter = physMemAddrList.begin(); 415 iter != physMemAddrList.end(); 416 iter++) { 417 if (addr >= iter->start && addr <= iter->end) { 418 DPRINTF(RubyPort, "Request found in %#llx - %#llx range\n", 419 iter->start, iter->end); 420 return true; 421 } 422 } 423 return false; 424} 425 426unsigned 427RubyPort::M5Port::deviceBlockSize() const 428{ 429 return (unsigned) RubySystem::getBlockSizeBytes(); 430} 431