RubyPort.cc revision 7558:6c3f81b176da
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/rubytest/RubyTester.hh" 30#include "mem/physical.hh" 31#include "mem/ruby/slicc_interface/AbstractController.hh" 32#include "mem/ruby/system/RubyPort.hh" 33 34RubyPort::RubyPort(const Params *p) 35 : MemObject(p) 36{ 37 m_version = p->version; 38 assert(m_version != -1); 39 40 physmem = p->physmem; 41 42 m_controller = NULL; 43 m_mandatory_q_ptr = NULL; 44 45 m_request_cnt = 0; 46 pio_port = NULL; 47 physMemPort = NULL; 48} 49 50void 51RubyPort::init() 52{ 53 assert(m_controller != NULL); 54 m_mandatory_q_ptr = m_controller->getMandatoryQueue(); 55} 56 57Port * 58RubyPort::getPort(const std::string &if_name, int idx) 59{ 60 if (if_name == "port") { 61 return new M5Port(csprintf("%s-port%d", name(), idx), this); 62 } 63 64 if (if_name == "pio_port") { 65 // ensure there is only one pio port 66 assert(pio_port == NULL); 67 68 pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), this); 69 70 return pio_port; 71 } 72 73 if (if_name == "physMemPort") { 74 // RubyPort should only have one port to physical memory 75 assert (physMemPort == NULL); 76 77 physMemPort = new M5Port(csprintf("%s-physMemPort", name()), this); 78 79 return physMemPort; 80 } 81 82 if (if_name == "functional") { 83 // Calls for the functional port only want to access 84 // functional memory. Therefore, directly pass these calls 85 // ports to physmem. 86 assert(physmem != NULL); 87 return physmem->getPort(if_name, idx); 88 } 89 90 return NULL; 91} 92 93RubyPort::PioPort::PioPort(const std::string &_name, 94 RubyPort *_port) 95 : SimpleTimingPort(_name, _port) 96{ 97 DPRINTF(Ruby, "creating port to ruby sequencer to cpu %s\n", _name); 98 ruby_port = _port; 99} 100 101RubyPort::M5Port::M5Port(const std::string &_name, 102 RubyPort *_port) 103 : SimpleTimingPort(_name, _port) 104{ 105 DPRINTF(Ruby, "creating port from ruby sequcner to cpu %s\n", _name); 106 ruby_port = _port; 107} 108 109Tick 110RubyPort::PioPort::recvAtomic(PacketPtr pkt) 111{ 112 panic("RubyPort::PioPort::recvAtomic() not implemented!\n"); 113 return 0; 114} 115 116Tick 117RubyPort::M5Port::recvAtomic(PacketPtr pkt) 118{ 119 panic("RubyPort::M5Port::recvAtomic() not implemented!\n"); 120 return 0; 121} 122 123 124bool 125RubyPort::PioPort::recvTiming(PacketPtr pkt) 126{ 127 // In FS mode, ruby memory will receive pio responses from devices 128 // and it must forward these responses back to the particular CPU. 129 DPRINTF(MemoryAccess, "Pio response for address %#x\n", pkt->getAddr()); 130 131 assert(pkt->isResponse()); 132 133 // First we must retrieve the request port from the sender State 134 RubyPort::SenderState *senderState = 135 safe_cast<RubyPort::SenderState *>(pkt->senderState); 136 M5Port *port = senderState->port; 137 assert(port != NULL); 138 139 // pop the sender state from the packet 140 pkt->senderState = senderState->saved; 141 delete senderState; 142 143 port->sendTiming(pkt); 144 145 return true; 146} 147 148bool 149RubyPort::M5Port::recvTiming(PacketPtr pkt) 150{ 151 DPRINTF(MemoryAccess, 152 "Timing access caught for address %#x\n", pkt->getAddr()); 153 154 //dsm: based on SimpleTimingPort::recvTiming(pkt); 155 156 // The received packets should only be M5 requests, which should never 157 // get nacked. There used to be code to hanldle nacks here, but 158 // I'm pretty sure it didn't work correctly with the drain code, 159 // so that would need to be fixed if we ever added it back. 160 assert(pkt->isRequest()); 161 162 if (pkt->memInhibitAsserted()) { 163 warn("memInhibitAsserted???"); 164 // snooper will supply based on copy of packet 165 // still target's responsibility to delete packet 166 delete pkt; 167 return true; 168 } 169 170 // Save the port in the sender state object to be used later to 171 // route the response 172 pkt->senderState = new SenderState(this, pkt->senderState); 173 174 // Check for pio requests and directly send them to the dedicated 175 // pio port. 176 if (!isPhysMemAddress(pkt->getAddr())) { 177 assert(ruby_port->pio_port != NULL); 178 DPRINTF(MemoryAccess, 179 "Request for address 0x%#x is assumed to be a pio request\n", 180 pkt->getAddr()); 181 182 return ruby_port->pio_port->sendTiming(pkt); 183 } 184 185 // For DMA and CPU requests, translate them to ruby requests before 186 // sending them to our assigned ruby port. 187 RubyRequestType type = RubyRequestType_NULL; 188 189 // If valid, copy the pc to the ruby request 190 Addr pc = 0; 191 if (pkt->req->hasPC()) { 192 pc = pkt->req->getPC(); 193 } 194 195 if (pkt->isLLSC()) { 196 if (pkt->isWrite()) { 197 DPRINTF(MemoryAccess, "Issuing SC\n"); 198 type = RubyRequestType_Locked_Write; 199 } else { 200 DPRINTF(MemoryAccess, "Issuing LL\n"); 201 assert(pkt->isRead()); 202 type = RubyRequestType_Locked_Read; 203 } 204 } else { 205 if (pkt->isRead()) { 206 if (pkt->req->isInstFetch()) { 207 type = RubyRequestType_IFETCH; 208 } else { 209 type = RubyRequestType_LD; 210 } 211 } else if (pkt->isWrite()) { 212 type = RubyRequestType_ST; 213 } else if (pkt->isReadWrite()) { 214 // Fix me. This conditional will never be executed 215 // because isReadWrite() is just an OR of isRead() and 216 // isWrite(). Furthermore, just because the packet is a 217 // read/write request does not necessary mean it is a 218 // read-modify-write atomic operation. 219 type = RubyRequestType_RMW_Write; 220 } else { 221 panic("Unsupported ruby packet type\n"); 222 } 223 } 224 225 RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(), 226 pkt->getSize(), pc, type, 227 RubyAccessMode_Supervisor, pkt); 228 229 // Submit the ruby request 230 RequestStatus requestStatus = ruby_port->makeRequest(ruby_request); 231 232 // If the request successfully issued then we should return true. 233 // Otherwise, we need to delete the senderStatus we just created and return 234 // false. 235 if (requestStatus == RequestStatus_Issued) { 236 return true; 237 } 238 239 DPRINTF(MemoryAccess, 240 "Request for address #x did not issue because %s\n", 241 pkt->getAddr(), RequestStatus_to_string(requestStatus)); 242 243 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState); 244 pkt->senderState = senderState->saved; 245 delete senderState; 246 return false; 247} 248 249void 250RubyPort::ruby_hit_callback(PacketPtr pkt) 251{ 252 // Retrieve the request port from the sender State 253 RubyPort::SenderState *senderState = 254 safe_cast<RubyPort::SenderState *>(pkt->senderState); 255 M5Port *port = senderState->port; 256 assert(port != NULL); 257 258 // pop the sender state from the packet 259 pkt->senderState = senderState->saved; 260 delete senderState; 261 262 port->hitCallback(pkt); 263} 264 265void 266RubyPort::M5Port::hitCallback(PacketPtr pkt) 267{ 268 bool needsResponse = pkt->needsResponse(); 269 270 // 271 // All responses except failed SC operations access M5 physical memory 272 // 273 bool accessPhysMem = true; 274 275 if (pkt->isLLSC()) { 276 if (pkt->isWrite()) { 277 if (pkt->req->getExtraData() != 0) { 278 // 279 // Successful SC packets convert to normal writes 280 // 281 pkt->convertScToWrite(); 282 } else { 283 // 284 // Failed SC packets don't access physical memory and thus 285 // the RubyPort itself must convert it to a response. 286 // 287 accessPhysMem = false; 288 pkt->makeAtomicResponse(); 289 } 290 } else { 291 // 292 // All LL packets convert to normal loads so that M5 PhysMem does 293 // not lock the blocks. 294 // 295 pkt->convertLlToRead(); 296 } 297 } 298 DPRINTF(MemoryAccess, "Hit callback needs response %d\n", needsResponse); 299 300 if (accessPhysMem) { 301 ruby_port->physMemPort->sendAtomic(pkt); 302 } 303 304 // turn packet around to go back to requester if response expected 305 if (needsResponse) { 306 // sendAtomic() should already have turned packet into 307 // atomic response 308 assert(pkt->isResponse()); 309 DPRINTF(MemoryAccess, "Sending packet back over port\n"); 310 sendTiming(pkt); 311 } else { 312 delete pkt; 313 } 314 DPRINTF(MemoryAccess, "Hit callback done!\n"); 315} 316 317bool 318RubyPort::M5Port::sendTiming(PacketPtr pkt) 319{ 320 //minimum latency, must be > 0 321 schedSendTiming(pkt, curTick + (1 * g_eventQueue_ptr->getClock())); 322 return true; 323} 324 325bool 326RubyPort::PioPort::sendTiming(PacketPtr pkt) 327{ 328 //minimum latency, must be > 0 329 schedSendTiming(pkt, curTick + (1 * g_eventQueue_ptr->getClock())); 330 return true; 331} 332 333bool 334RubyPort::M5Port::isPhysMemAddress(Addr addr) 335{ 336 AddrRangeList physMemAddrList; 337 bool snoop = false; 338 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop); 339 for (AddrRangeIter iter = physMemAddrList.begin(); 340 iter != physMemAddrList.end(); 341 iter++) { 342 if (addr >= iter->start && addr <= iter->end) { 343 DPRINTF(MemoryAccess, "Request found in %#llx - %#llx range\n", 344 iter->start, iter->end); 345 return true; 346 } 347 } 348 return false; 349} 350