mem: Add a simple QoS-aware Memory Controller
This patch implements QoSMemorySink: a simple generic QoS-aware memory controller which inherits from QoS::MemCtrl. Change-Id: I537a4e2d4cb8f54fa0002eb088b2c6957afb9973 Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com> Reviewed-on: https://gem5-review.googlesource.com/11971 Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com> Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com> Reviewed-by: Matthew Poremba <porembam@gmail.com>
This commit is contained in:
committed by
Giacomo Travaglini
parent
66f80b5a73
commit
aa103566d6
63
src/mem/qos/QoSMemSinkCtrl.py
Normal file
63
src/mem/qos/QoSMemSinkCtrl.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# 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
|
||||
|
||||
from m5.params import *
|
||||
from QoSMemCtrl import *
|
||||
|
||||
class QoSMemSinkCtrl(QoSMemCtrl):
|
||||
type = 'QoSMemSinkCtrl'
|
||||
cxx_header = "mem/qos/mem_sink.hh"
|
||||
cxx_class = "QoS::MemSinkCtrl"
|
||||
port = SlavePort("Slave ports")
|
||||
|
||||
# the basic configuration of the controller architecture, note
|
||||
# that each entry corresponds to a burst for the specific DRAM
|
||||
# configuration (e.g. x32 with burst length 8 is 32 bytes) and not
|
||||
# the cacheline size or request/packet size
|
||||
write_buffer_size = Param.Unsigned(64, "Number of write queue entries")
|
||||
read_buffer_size = Param.Unsigned(32, "Number of read queue entries")
|
||||
|
||||
# memory packet size
|
||||
memory_packet_size = Param.MemorySize("32B", "Memory packet size")
|
||||
|
||||
# request latency - minimum timing between requests
|
||||
request_latency = Param.Latency("20ns", "Memory latency between requests")
|
||||
|
||||
# response latency - time to issue a response once a request is serviced
|
||||
response_latency = Param.Latency("20ns", "Memory response latency")
|
||||
|
||||
|
||||
@@ -38,9 +38,11 @@
|
||||
Import('*')
|
||||
|
||||
SimObject('QoSMemCtrl.py')
|
||||
SimObject('QoSMemSinkCtrl.py')
|
||||
SimObject('QoSPolicy.py')
|
||||
SimObject('QoSTurnaround.py')
|
||||
|
||||
Source('policy.cc')
|
||||
Source('q_policy.cc')
|
||||
Source('mem_ctrl.cc')
|
||||
Source('mem_sink.cc')
|
||||
|
||||
392
src/mem/qos/mem_sink.cc
Normal file
392
src/mem/qos/mem_sink.cc
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
248
src/mem/qos/mem_sink.hh
Normal file
248
src/mem/qos/mem_sink.hh
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __MEM_QOS_MEM_SINK_HH__
|
||||
#define __MEM_QOS_MEM_SINK_HH__
|
||||
|
||||
#include "mem/qos/mem_ctrl.hh"
|
||||
#include "mem/qport.hh"
|
||||
#include "params/QoSMemSinkCtrl.hh"
|
||||
|
||||
namespace QoS {
|
||||
|
||||
/**
|
||||
* QoS Memory Sink
|
||||
*
|
||||
* The QoS Memory Sink is a lightweight memory controller with QoS
|
||||
* support. It is meant to provide a QoS aware simple memory system
|
||||
* without the need of using a complex DRAM memory controller
|
||||
*/
|
||||
class MemSinkCtrl : public MemCtrl
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* The Request packets are store in a multiple dequeue structure,
|
||||
* based on their QoS priority
|
||||
*/
|
||||
using PacketQueue = std::deque<PacketPtr>;
|
||||
|
||||
private:
|
||||
class MemoryPort : public QueuedSlavePort
|
||||
{
|
||||
private:
|
||||
/** reference to parent memory object */
|
||||
MemSinkCtrl& memory;
|
||||
|
||||
/** Outgoing packet responses queue */
|
||||
RespPacketQueue queue;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param n port name
|
||||
* @param m reference to ProfileGen parent object
|
||||
*/
|
||||
MemoryPort(const std::string&, MemSinkCtrl&);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Receive a Packet in Atomic mode
|
||||
*
|
||||
* @param pkt pointer to memory packet
|
||||
* @return packet access latency in ticks
|
||||
*/
|
||||
Tick recvAtomic(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Receive a Packet in Functional mode
|
||||
*
|
||||
* @param pkt pointer to memory packet
|
||||
*/
|
||||
void recvFunctional(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Receive a Packet in Timing mode
|
||||
*
|
||||
* @param pkt pointer to memory packet
|
||||
* @return true if the request was accepted
|
||||
*/
|
||||
bool recvTimingReq(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Gets the configured address ranges for this port
|
||||
* @return the configured address ranges for this port
|
||||
*/
|
||||
AddrRangeList getAddrRanges() const;
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* QoS Memory Sink Constructor
|
||||
*
|
||||
* @param p QoS Memory Sink configuration parameters
|
||||
*/
|
||||
MemSinkCtrl(const QoSMemSinkCtrlParams*);
|
||||
|
||||
virtual ~MemSinkCtrl();
|
||||
|
||||
/**
|
||||
* Checks and return the Drain state of this SimObject
|
||||
* @return current Drain state
|
||||
*/
|
||||
DrainState drain() override;
|
||||
|
||||
/**
|
||||
* Getter method to access this memory's slave port
|
||||
*
|
||||
* @param interface interface name
|
||||
* @param idx port ID number
|
||||
* @return reference to this memory's slave port
|
||||
*/
|
||||
BaseSlavePort& getSlavePort(const std::string&,
|
||||
PortID = InvalidPortID) override;
|
||||
|
||||
/**
|
||||
* Initializes this object
|
||||
*/
|
||||
void init() override;
|
||||
|
||||
protected:
|
||||
/** Memory between requests latency (ticks) */
|
||||
const Tick requestLatency;
|
||||
|
||||
/** Memory response latency (ticks) */
|
||||
const Tick responseLatency;
|
||||
|
||||
/** Memory packet size in bytes */
|
||||
const uint64_t memoryPacketSize;
|
||||
|
||||
/** Read request packets queue buffer size in #packets */
|
||||
const uint64_t readBufferSize;
|
||||
|
||||
/** Write request packets queue buffer size in #packets */
|
||||
const uint64_t writeBufferSize;
|
||||
|
||||
/** Memory slave port */
|
||||
MemoryPort port;
|
||||
|
||||
/** Read request pending */
|
||||
bool retryRdReq;
|
||||
|
||||
/** Write request pending */
|
||||
bool retryWrReq;
|
||||
|
||||
/** Next request service time */
|
||||
Tick nextRequest;
|
||||
|
||||
/** Count the number of read retries */
|
||||
Stats::Scalar numReadRetries;
|
||||
|
||||
/** Count the number of write retries */
|
||||
Stats::Scalar numWriteRetries;
|
||||
|
||||
/**
|
||||
* QoS-aware (per priority) incoming read requests packets queue
|
||||
*/
|
||||
std::vector<PacketQueue> readQueue;
|
||||
|
||||
/**
|
||||
* QoS-aware (per priority) incoming read requests packets queue
|
||||
*/
|
||||
std::vector<PacketQueue> writeQueue;
|
||||
|
||||
/**
|
||||
* Processes the next Request event according to configured
|
||||
* request latency
|
||||
*/
|
||||
void processNextReqEvent();
|
||||
|
||||
/** Event wrapper to schedule next request handler function */
|
||||
EventWrapper<
|
||||
MemSinkCtrl,
|
||||
&MemSinkCtrl::processNextReqEvent> nextReqEvent;
|
||||
|
||||
/**
|
||||
* Check if the read queue has room for more entries
|
||||
*
|
||||
* @param packets The number of entries needed in the read queue
|
||||
* @return true if read queue is full, false otherwise
|
||||
*/
|
||||
inline bool readQueueFull(const uint64_t packets) const;
|
||||
|
||||
/**
|
||||
* Check if the write queue has room for more entries
|
||||
*
|
||||
* @param packets The number of entries needed in the write queue
|
||||
* @return true if write queue is full, false otherwise
|
||||
*/
|
||||
inline bool writeQueueFull(const uint64_t packets) const;
|
||||
|
||||
/**
|
||||
* Receive a Packet in Atomic mode
|
||||
*
|
||||
* @param pkt pointer to memory packet
|
||||
* @return packet access latency in ticks
|
||||
*/
|
||||
Tick recvAtomic(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Receive a Packet in Functional mode
|
||||
*
|
||||
* @param pkt pointer to memory packet
|
||||
*/
|
||||
void recvFunctional(PacketPtr pkt);
|
||||
|
||||
/**
|
||||
* Receive a Packet in Timing mode
|
||||
*
|
||||
* @param pkt pointer to memory packet
|
||||
* @return true if the request was accepted
|
||||
*/
|
||||
bool recvTimingReq(PacketPtr pkt);
|
||||
|
||||
/** Registers statistics */
|
||||
void regStats() override;
|
||||
};
|
||||
|
||||
} // namespace QoS
|
||||
|
||||
#endif /* __MEM_QOS_MEM_SINK_HH__ */
|
||||
Reference in New Issue
Block a user