diff options
Diffstat (limited to 'src/mem/ruby/system/RubyPort.cc')
-rw-r--r-- | src/mem/ruby/system/RubyPort.cc | 275 |
1 files changed, 269 insertions, 6 deletions
diff --git a/src/mem/ruby/system/RubyPort.cc b/src/mem/ruby/system/RubyPort.cc index b83ba43e3..d8fb6b470 100644 --- a/src/mem/ruby/system/RubyPort.cc +++ b/src/mem/ruby/system/RubyPort.cc @@ -31,27 +31,290 @@ #include "mem/ruby/system/RubyPort.hh" #include "mem/ruby/slicc_interface/AbstractController.hh" -//void (*RubyPort::m_hit_callback)(int64_t) = NULL; uint16_t RubyPort::m_num_ports = 0; +RubyPort::RequestMap RubyPort::pending_cpu_requests; + RubyPort::RubyPort(const Params *p) - : MemObject(p) + : MemObject(p), + funcMemPort(csprintf("%s-funcmem_port", name()), this) { m_version = p->version; assert(m_version != -1); - m_controller = p->controller; - assert(m_controller != NULL); - m_mandatory_q_ptr = m_controller->getMandatoryQueue(); + m_controller = NULL; + m_mandatory_q_ptr = NULL; m_port_id = m_num_ports++; m_request_cnt = 0; - m_hit_callback = NULL; + m_hit_callback = ruby_hit_callback; + pio_port = NULL; assert(m_num_ports <= 2048); // see below for reason } +void RubyPort::init() +{ + assert(m_controller != NULL); + m_mandatory_q_ptr = m_controller->getMandatoryQueue(); +} + Port * RubyPort::getPort(const std::string &if_name, int idx) { + if (if_name == "port") { + return new M5Port(csprintf("%s-port%d", name(), idx), this); + } else if (if_name == "pio_port") { + // + // ensure there is only one pio port + // + assert(pio_port == NULL); + + pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), + this); + + return pio_port; + } else if (if_name == "funcmem_port") { + return &funcMemPort; + } return NULL; } + +RubyPort::PioPort::PioPort(const std::string &_name, + RubyPort *_port) + : SimpleTimingPort(_name, _port) +{ + DPRINTF(Ruby, "creating port to ruby sequencer to cpu %s\n", _name); + ruby_port = _port; +} + +RubyPort::M5Port::M5Port(const std::string &_name, + RubyPort *_port) + : SimpleTimingPort(_name, _port) +{ + DPRINTF(Ruby, "creating port from ruby sequcner to cpu %s\n", _name); + ruby_port = _port; +} + +Tick +RubyPort::PioPort::recvAtomic(PacketPtr pkt) +{ + panic("RubyPort::PioPort::recvAtomic() not implemented!\n"); + return 0; +} + + +Tick +RubyPort::M5Port::recvAtomic(PacketPtr pkt) +{ + panic("RubyPort::M5Port::recvAtomic() not implemented!\n"); + return 0; +} + + +bool +RubyPort::PioPort::recvTiming(PacketPtr pkt) +{ + // + // In FS mode, ruby memory will receive pio responses from devices and + // it must forward these responses back to the particular CPU. + // + DPRINTF(MemoryAccess, + "Pio response for address %#x\n", + pkt->getAddr()); + + assert(pkt->isResponse()); + + // + // First we must retrieve the request port from the sender State + // + RubyPort::SenderState *senderState = + safe_cast<RubyPort::SenderState *>(pkt->senderState); + M5Port *port = senderState->port; + assert(port != NULL); + + // pop the sender state from the packet + pkt->senderState = senderState->saved; + delete senderState; + + port->sendTiming(pkt); + + return true; +} + +bool +RubyPort::M5Port::recvTiming(PacketPtr pkt) +{ + DPRINTF(MemoryAccess, + "Timing access caught for address %#x\n", + pkt->getAddr()); + + //dsm: based on SimpleTimingPort::recvTiming(pkt); + + // + // After checking for pio responses, the remainder of packets + // received by ruby should only be M5 requests, which should never + // get nacked. There used to be code to hanldle nacks here, but + // I'm pretty sure it didn't work correctly with the drain code, + // so that would need to be fixed if we ever added it back. + // + assert(pkt->isRequest()); + + if (pkt->memInhibitAsserted()) { + warn("memInhibitAsserted???"); + // snooper will supply based on copy of packet + // still target's responsibility to delete packet + delete pkt; + return true; + } + + // + // Check for pio requests and directly send them to the dedicated + // pio port. + // + if (!isPhysMemAddress(pkt->getAddr())) { + assert(ruby_port->pio_port != NULL); + + // + // Save the port in the sender state object to be used later to + // route the response + // + pkt->senderState = new SenderState(this, pkt->senderState); + + return ruby_port->pio_port->sendTiming(pkt); + } + + // + // For DMA and CPU requests, translate them to ruby requests before + // sending them to our assigned ruby port. + // + RubyRequestType type = RubyRequestType_NULL; + Addr pc = 0; + if (pkt->isRead()) { + if (pkt->req->isInstFetch()) { + type = RubyRequestType_IFETCH; + pc = pkt->req->getPC(); + } else { + type = RubyRequestType_LD; + } + } else if (pkt->isWrite()) { + type = RubyRequestType_ST; + } else if (pkt->isReadWrite()) { + type = RubyRequestType_RMW_Write; + } + + RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(), + pkt->getSize(), pc, type, + RubyAccessMode_Supervisor); + + // Submit the ruby request + int64_t req_id = ruby_port->makeRequest(ruby_request); + if (req_id == -1) { + return false; + } + + // Save the request for the callback + RubyPort::pending_cpu_requests[req_id] = new RequestCookie(pkt, this); + + return true; +} + +void +RubyPort::ruby_hit_callback(int64_t req_id) +{ + // + // Note: This single fuction can be called by cpu and dma ports, + // as well as the functional port. + // + RequestMap::iterator i = pending_cpu_requests.find(req_id); + if (i == pending_cpu_requests.end()) + panic("could not find pending request %d\n", req_id); + + RequestCookie *cookie = i->second; + pending_cpu_requests.erase(i); + + Packet *pkt = cookie->pkt; + M5Port *port = cookie->m5Port; + delete cookie; + + port->hitCallback(pkt); +} + +void +RubyPort::M5Port::hitCallback(PacketPtr pkt) +{ + + bool needsResponse = pkt->needsResponse(); + + DPRINTF(MemoryAccess, "Hit callback needs response %d\n", + needsResponse); + + ruby_port->funcMemPort.sendFunctional(pkt); + + // turn packet around to go back to requester if response expected + if (needsResponse) { + // recvAtomic() should already have turned packet into + // atomic response + assert(pkt->isResponse()); + DPRINTF(MemoryAccess, "Sending packet back over port\n"); + sendTiming(pkt); + } else { + delete pkt; + } + DPRINTF(MemoryAccess, "Hit callback done!\n"); +} + +bool +RubyPort::M5Port::sendTiming(PacketPtr pkt) +{ + schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0 + return true; +} + +bool +RubyPort::PioPort::sendTiming(PacketPtr pkt) +{ + schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0 + return true; +} + +bool +RubyPort::M5Port::isPhysMemAddress(Addr addr) +{ + AddrRangeList physMemAddrList; + bool snoop = false; + ruby_port->funcMemPort.getPeerAddressRanges(physMemAddrList, snoop); + for(AddrRangeIter iter = physMemAddrList.begin(); + iter != physMemAddrList.end(); + iter++) { + if (addr >= iter->start && addr <= iter->end) { + DPRINTF(MemoryAccess, "Request found in %#llx - %#llx range\n", + iter->start, iter->end); + return true; + } + } + assert(isPioAddress(addr)); + return false; +} + +bool +RubyPort::M5Port::isPioAddress(Addr addr) +{ + AddrRangeList pioAddrList; + bool snoop = false; + if (ruby_port->pio_port == NULL) { + return false; + } + + ruby_port->pio_port->getPeerAddressRanges(pioAddrList, snoop); + for(AddrRangeIter iter = pioAddrList.begin(); + iter != pioAddrList.end(); + iter++) { + if (addr >= iter->start && addr <= iter->end) { + DPRINTF(MemoryAccess, "Pio request found in %#llx - %#llx range\n", + iter->start, iter->end); + return true; + } + } + return false; +} + |