diff options
Diffstat (limited to 'src/mem/qos/mem_sink.cc')
-rw-r--r-- | src/mem/qos/mem_sink.cc | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/src/mem/qos/mem_sink.cc b/src/mem/qos/mem_sink.cc new file mode 100644 index 000000000..a951daee2 --- /dev/null +++ b/src/mem/qos/mem_sink.cc @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2018 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Matteo Andreozzi + */ + +#include "debug/Drain.hh" +#include "debug/QOS.hh" +#include "mem_sink.hh" +#include "sim/system.hh" + +namespace QoS { + +MemSinkCtrl::MemSinkCtrl(const QoSMemSinkCtrlParams* p) + : MemCtrl(p), requestLatency(p->request_latency), + responseLatency(p->response_latency), + memoryPacketSize(p->memory_packet_size), + readBufferSize(p->read_buffer_size), + writeBufferSize(p->write_buffer_size), port(name() + ".port", *this), + retryRdReq(false), retryWrReq(false), nextRequest(0), nextReqEvent(this) +{ + // Resize read and write queue to allocate space + // for configured QoS priorities + readQueue.resize(numPriorities()); + writeQueue.resize(numPriorities()); +} + +MemSinkCtrl::~MemSinkCtrl() +{} + +void +MemSinkCtrl::init() +{ + MemCtrl::init(); + + // Allow unconnected memories as this is used in several ruby + // systems at the moment + if (port.isConnected()) { + port.sendRangeChange(); + } +} + +bool +MemSinkCtrl::readQueueFull(const uint64_t packets) const +{ + return (totalReadQueueSize + packets > readBufferSize); +} + +bool +MemSinkCtrl::writeQueueFull(const uint64_t packets) const +{ + return (totalWriteQueueSize + packets > writeBufferSize); +} + +Tick +MemSinkCtrl::recvAtomic(PacketPtr pkt) +{ + panic_if(pkt->cacheResponding(), + "%s Should not see packets where cache is responding\n", + __func__); + + access(pkt); + return responseLatency; +} + +void +MemSinkCtrl::recvFunctional(PacketPtr pkt) +{ + pkt->pushLabel(name()); + + functionalAccess(pkt); + + pkt->popLabel(); +} + +BaseSlavePort & +MemSinkCtrl::getSlavePort(const std::string &interface, PortID idx) +{ + if (interface != "port") { + return MemObject::getSlavePort(interface, idx); + } else { + return port; + } +} + +bool +MemSinkCtrl::recvTimingReq(PacketPtr pkt) +{ + // Request accepted + bool req_accepted = true; + + panic_if(!(pkt->isRead() || pkt->isWrite()), + "%s. Should only see " + "read and writes at memory controller\n", + __func__); + + panic_if(pkt->cacheResponding(), + "%s. Should not see packets where cache is responding\n", + __func__); + + DPRINTF(QOS, + "%s: MASTER %s request %s addr %lld size %d\n", + __func__, + _system->getMasterName(pkt->req->masterId()), + pkt->cmdString(), pkt->getAddr(), pkt->getSize()); + + uint64_t required_entries = divCeil(pkt->getSize(), memoryPacketSize); + + assert(required_entries); + + // Schedule packet + uint8_t pkt_priority = qosSchedule({&readQueue, &writeQueue}, + memoryPacketSize, pkt); + + if (pkt->isRead()) { + if (readQueueFull(required_entries)) { + DPRINTF(QOS, + "%s Read queue full, not accepting\n", __func__); + // Remember that we have to retry this port + retryRdReq = true; + numReadRetries++; + req_accepted = false; + } else { + // Enqueue the incoming packet into corresponding + // QoS priority queue + readQueue.at(pkt_priority).push_back(pkt); + queuePolicy->enqueuePacket(pkt); + } + } else { + if (writeQueueFull(required_entries)) { + DPRINTF(QOS, + "%s Write queue full, not accepting\n", __func__); + // Remember that we have to retry this port + retryWrReq = true; + numWriteRetries++; + req_accepted = false; + } else { + // Enqueue the incoming packet into corresponding QoS + // priority queue + writeQueue.at(pkt_priority).push_back(pkt); + queuePolicy->enqueuePacket(pkt); + } + } + + if (req_accepted) { + // The packet is accepted - log it + logRequest(pkt->isRead()? READ : WRITE, + pkt->req->masterId(), + pkt->qosValue(), + pkt->getAddr(), + required_entries); + } + + // Check if we have to process next request event + if (!nextReqEvent.scheduled()) { + DPRINTF(QOS, + "%s scheduling next request at " + "time %d (next is %d)\n", __func__, + std::max(curTick(), nextRequest), nextRequest); + schedule(nextReqEvent, std::max(curTick(), nextRequest)); + } + return req_accepted; +} + +void +MemSinkCtrl::processNextReqEvent() +{ + PacketPtr pkt = nullptr; + + // Evaluate bus direction + busStateNext = selectNextBusState(); + + // Record turnaround stats and update current state direction + recordTurnaroundStats(); + + // Set current bus state + setCurrentBusState(); + + // Access current direction buffer + std::vector<PacketQueue>* queue_ptr = (busState == READ ? &readQueue : + &writeQueue); + + DPRINTF(QOS, + "%s DUMPING %s queues status\n", __func__, + (busState == WRITE ? "WRITE" : "READ")); + + if (DTRACE(QOS)) { + for (uint8_t i = 0; i < numPriorities(); ++i) { + std::string plist = ""; + for (auto& e : (busState == WRITE ? writeQueue[i]: readQueue[i])) { + plist += (std::to_string(e->req->masterId())) + " "; + } + DPRINTF(QOS, + "%s priority Queue [%i] contains %i elements, " + "packets are: [%s]\n", __func__, i, + busState == WRITE ? writeQueueSizes[i] : + readQueueSizes[i], + plist); + } + } + + uint8_t curr_prio = numPriorities(); + + for (auto queue = (*queue_ptr).rbegin(); + queue != (*queue_ptr).rend(); ++queue) { + + curr_prio--; + + DPRINTF(QOS, + "%s checking %s queue [%d] priority [%d packets]\n", + __func__, (busState == READ? "READ" : "WRITE"), + curr_prio, queue->size()); + + if (!queue->empty()) { + // Call the queue policy to select packet from priority queue + auto p_it = queuePolicy->selectPacket(&(*queue)); + pkt = *p_it; + queue->erase(p_it); + + DPRINTF(QOS, + "%s scheduling packet address %d for master %s from " + "priority queue %d\n", __func__, pkt->getAddr(), + _system->getMasterName(pkt->req->masterId()), + curr_prio); + break; + } + } + + assert(pkt); + + // Setup next request service time - do it here as retry request + // hands over control to the port + nextRequest = curTick() + requestLatency; + + uint64_t removed_entries = divCeil(pkt->getSize(), memoryPacketSize); + + DPRINTF(QOS, + "%s scheduled packet address %d for master %s size is %d, " + "corresponds to %d memory packets\n", __func__, pkt->getAddr(), + _system->getMasterName(pkt->req->masterId()), + pkt->getSize(), removed_entries); + + // Schedule response + panic_if(!pkt->needsResponse(), + "%s response not required\n", __func__); + + // Do the actual memory access which also turns the packet + // into a response + access(pkt); + + // Log the response + logResponse(pkt->isRead()? READ : WRITE, + pkt->req->masterId(), + pkt->qosValue(), + pkt->getAddr(), + removed_entries, responseLatency); + + // Schedule the response + port.schedTimingResp(pkt, curTick() + responseLatency, true); + DPRINTF(QOS, + "%s response scheduled at time %d\n", + __func__, curTick() + responseLatency); + + // Finally - handle retry requests - this handles control + // to the port, so do it last + if (busState == READ && retryRdReq) { + retryRdReq = false; + port.sendRetryReq(); + } else if (busState == WRITE && retryWrReq) { + retryWrReq = false; + port.sendRetryReq(); + } + + // Check if we have to schedule another request event + if ((totalReadQueueSize || totalWriteQueueSize) && + !nextReqEvent.scheduled()) { + + schedule(nextReqEvent, curTick() + requestLatency); + DPRINTF(QOS, + "%s scheduling next request event at tick %d\n", + __func__, curTick() + requestLatency); + } +} + +DrainState +MemSinkCtrl::drain() +{ + if (totalReadQueueSize || totalWriteQueueSize) { + DPRINTF(Drain, + "%s queues have requests, waiting to drain\n", + __func__); + return DrainState::Draining; + } else { + return DrainState::Drained; + } +} + +void +MemSinkCtrl::regStats() +{ + MemCtrl::regStats(); + + // Initialize all the stats + using namespace Stats; + + numReadRetries.name(name() + ".numReadRetries") + .desc("Number of read retries"); + numWriteRetries.name(name() + ".numWriteRetries") + .desc("Number of write retries"); +} + +MemSinkCtrl::MemoryPort::MemoryPort(const std::string& n, + MemSinkCtrl& m) + : QueuedSlavePort(n, &m, queue), memory(m), queue(memory, *this) +{} + +AddrRangeList +MemSinkCtrl::MemoryPort::getAddrRanges() const +{ + AddrRangeList ranges; + ranges.push_back(memory.getAddrRange()); + return ranges; +} + +Tick +MemSinkCtrl::MemoryPort::recvAtomic(PacketPtr pkt) +{ + return memory.recvAtomic(pkt); +} + +void +MemSinkCtrl::MemoryPort::recvFunctional(PacketPtr pkt) +{ + pkt->pushLabel(memory.name()); + + if (!queue.trySatisfyFunctional(pkt)) { + // Default implementation of SimpleTimingPort::recvFunctional() + // calls recvAtomic() and throws away the latency; we can save a + // little here by just not calculating the latency. + memory.recvFunctional(pkt); + } + + pkt->popLabel(); +} + +bool +MemSinkCtrl::MemoryPort::recvTimingReq(PacketPtr pkt) +{ + return memory.recvTimingReq(pkt); +} + +} // namespace QoS + +QoS::MemSinkCtrl* +QoSMemSinkCtrlParams::create() +{ + return new QoS::MemSinkCtrl(this); +} + |