MEM: Add the communication monitor

This patch adds a communication monitor MemObject that can be inserted
between a master and slave port to provide a range of statistics about
the communication passing through it. The communication monitor is
non-invasive and does not change any properties or timing of the
packets, with the exception of adding a sender state to be able to
track latency. The statistics are only collected in timing mode (not
atomic) to avoid slowing down any fast forwarding.

An example of the statistics captured by the monitor are: read/write
burst lengths, bandwidth, request-response latency, outstanding
transactions, inter transaction time, transaction count, and address
distribution. The monitor can be used in combination with periodic
resetting and dumping of stats (through schedStatEvent) to study the
behaviour over time.

In future patches, a selection of convenience scripts will be added to
aid in visualising the statistics collected by the monitor.
This commit is contained in:
Andreas Hansson 2012-05-09 04:37:45 -04:00
parent 18e8d7ed2d
commit ab23e29487
4 changed files with 1070 additions and 0 deletions

97
src/mem/CommMonitor.py Normal file
View file

@ -0,0 +1,97 @@
# Copyright (c) 2012 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.
#
# Authors: Thomas Grass
# Andreas Hansson
from m5.params import *
from MemObject import MemObject
# The communication monitor will most typically be used in combination
# with periodic dumping and resetting of stats using schedStatEvent
class CommMonitor(MemObject):
type = 'CommMonitor'
# one port in each direction
master = MasterPort("Master port")
slave = SlavePort("Slave port")
# control the sample period window length of this monitor
sample_period = Param.Clock("1ms", "Sample period for histograms")
# for each histogram, set the number of bins and enable the user
# to disable the measurement, reads and writes use the same
# parameters
# histogram of burst length of packets (not using sample period)
burst_length_bins = Param.Unsigned('20', "# bins in burst length " \
"histograms")
disable_burst_length_hists = Param.Bool(False, "Disable burst length " \
"histograms")
# bandwidth per sample period
bandwidth_bins = Param.Unsigned('20', "# bins in bandwidth histograms")
disable_bandwidth_hists = Param.Bool(False, "Disable bandwidth histograms")
# latency from request to response (not using sample period)
latency_bins = Param.Unsigned('20', "# bins in latency histograms")
disable_latency_hists = Param.Bool(False, "Disable latency histograms")
# inter transaction time (ITT) distributions in uniformly sized
# bins up to the maximum, independently for read-to-read,
# write-to-write and the combined request-to-request that does not
# separate read and write requests
itt_bins = Param.Unsigned('20', "# bins in ITT distributions")
itt_max_bin = Param.Latency('100ns', "Max bin of ITT distributions")
disable_itt_dists = Param.Bool(False, "Disable ITT distributions")
# outstanding requests (that did not yet get a response) per
# sample period
outstanding_bins = Param.Unsigned('20', "# bins in outstanding " \
"requests histograms")
disable_outstanding_hists = Param.Bool(False, "Disable outstanding " \
"requests histograms")
# transactions (requests) observed per sample period
transaction_bins = Param.Unsigned('20', "# bins in transaction " \
"count histograms")
disable_transaction_hists = Param.Bool(False, "Disable transaction count " \
"histograms")
# address distributions (heatmaps) with associated address masks
# to selectively only look at certain bits of the address
read_addr_mask = Param.Addr(MaxAddr, "Address mask for read address")
write_addr_mask = Param.Addr(MaxAddr, "Address mask for write address")
disable_addr_dists = Param.Bool(True, "Disable address distributions")

View file

@ -32,10 +32,12 @@ Import('*')
SimObject('Bridge.py')
SimObject('Bus.py')
SimObject('CommMonitor.py')
SimObject('MemObject.py')
Source('bridge.cc')
Source('bus.cc')
Source('comm_monitor.cc')
Source('mem_object.cc')
Source('mport.cc')
Source('packet.cc')
@ -57,6 +59,7 @@ if env['TARGET_ISA'] != 'no':
DebugFlag('Bus')
DebugFlag('BusAddrRanges')
DebugFlag('BusBridge')
DebugFlag('CommMonitor')
DebugFlag('LLSC')
DebugFlag('MMU')
DebugFlag('MemoryAccess')

538
src/mem/comm_monitor.cc Normal file
View file

@ -0,0 +1,538 @@
/*
* Copyright (c) 2012 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.
*
* Authors: Thomas Grass
* Andreas Hansson
*/
#include "debug/CommMonitor.hh"
#include "mem/comm_monitor.hh"
#include "sim/stats.hh"
CommMonitor::CommMonitor(Params* params)
: MemObject(params),
masterPort(name() + "-master", *this),
slavePort(name() + "-slave", *this),
samplePeriodicEvent(this),
samplePeriodTicks(params->sample_period),
readAddrMask(params->read_addr_mask),
writeAddrMask(params->write_addr_mask),
stats(params)
{
// keep track of the sample period both in ticks and absolute time
samplePeriod.setTick(params->sample_period);
DPRINTF(CommMonitor,
"Created monitor %s with sample period %d ticks (%f s)\n",
name(), samplePeriodTicks, samplePeriod);
}
CommMonitor*
CommMonitorParams::create()
{
return new CommMonitor(this);
}
void
CommMonitor::init()
{
// make sure both sides of the monitor are connected
if (!slavePort.isConnected() || !masterPort.isConnected())
fatal("Communication monitor is not connected on both sides.\n");
}
MasterPort&
CommMonitor::getMasterPort(const std::string& if_name, int idx)
{
if (if_name == "master") {
return masterPort;
} else {
return MemObject::getMasterPort(if_name, idx);
}
}
SlavePort&
CommMonitor::getSlavePort(const std::string& if_name, int idx)
{
if (if_name == "slave") {
return slavePort;
} else {
return MemObject::getSlavePort(if_name, idx);
}
}
void
CommMonitor::recvFunctional(PacketPtr pkt)
{
masterPort.sendFunctional(pkt);
}
void
CommMonitor::recvFunctionalSnoop(PacketPtr pkt)
{
slavePort.sendFunctionalSnoop(pkt);
}
Tick
CommMonitor::recvAtomic(PacketPtr pkt)
{
return masterPort.sendAtomic(pkt);
}
Tick
CommMonitor::recvAtomicSnoop(PacketPtr pkt)
{
return slavePort.sendAtomicSnoop(pkt);
}
bool
CommMonitor::recvTimingReq(PacketPtr pkt)
{
// should always see a request
assert(pkt->isRequest());
// Store relevant fields of packet, because packet may be modified
// or even deleted when sendTiming() is called.
bool isRead = pkt->isRead();
bool isWrite = pkt->isWrite();
unsigned size = pkt->getSize();
Addr addr = pkt->getAddr();
bool needsResponse = pkt->needsResponse();
bool memInhibitAsserted = pkt->memInhibitAsserted();
Packet::SenderState* senderState = pkt->senderState;
// If a cache miss is served by a cache, a monitor near the memory
// would see a request which needs a response, but this response
// would be inhibited and not come back from the memory. Therefore
// we additionally have to check the inhibit flag.
if (needsResponse && !memInhibitAsserted && !stats.disableLatencyHists) {
pkt->senderState = new CommMonitorSenderState(senderState,
curTick());
}
// Attempt to send the packet (always succeeds for inhibited
// packets)
bool successful = masterPort.sendTimingReq(pkt);
// If not successful, restore the sender state
if (!successful && needsResponse && !stats.disableLatencyHists) {
delete pkt->senderState;
pkt->senderState = senderState;
}
if (successful && isRead) {
DPRINTF(CommMonitor, "Forwarded read request\n");
// Increment number of observed read transactions
if (!stats.disableTransactionHists) {
++stats.readTrans;
}
// Get sample of burst length
if (!stats.disableBurstLengthHists) {
stats.readBurstLengthHist.sample(size);
}
// Sample the masked address
if (!stats.disableAddrDists) {
stats.readAddrDist.sample(addr & readAddrMask);
}
// If it needs a response increment number of outstanding read
// requests
if (!stats.disableOutstandingHists && needsResponse) {
++stats.outstandingReadReqs;
}
if (!stats.disableITTDists) {
// Sample value of read-read inter transaction time
if (stats.timeOfLastRead != 0) {
stats.ittReadRead.sample(curTick() - stats.timeOfLastRead);
}
stats.timeOfLastRead = curTick();
// Sample value of req-req inter transaction time
if (stats.timeOfLastReq != 0) {
stats.ittReqReq.sample(curTick() - stats.timeOfLastReq);
}
stats.timeOfLastReq = curTick();
}
} else if (successful && isWrite) {
DPRINTF(CommMonitor, "Forwarded write request\n");
// Same as for reads
if (!stats.disableTransactionHists) {
++stats.writeTrans;
}
if (!stats.disableBurstLengthHists) {
stats.writeBurstLengthHist.sample(size);
}
// Update the bandwidth stats on the request
if (!stats.disableBandwidthHists) {
stats.writtenBytes += size;
stats.totalWrittenBytes += size;
}
// Sample the masked write address
if (!stats.disableAddrDists) {
stats.writeAddrDist.sample(addr & writeAddrMask);
}
if (!stats.disableOutstandingHists && needsResponse) {
++stats.outstandingWriteReqs;
}
if (!stats.disableITTDists) {
// Sample value of write-to-write inter transaction time
if (stats.timeOfLastWrite != 0) {
stats.ittWriteWrite.sample(curTick() - stats.timeOfLastWrite);
}
stats.timeOfLastWrite = curTick();
// Sample value of req-to-req inter transaction time
if (stats.timeOfLastReq != 0) {
stats.ittReqReq.sample(curTick() - stats.timeOfLastReq);
}
stats.timeOfLastReq = curTick();
}
} else if (successful) {
DPRINTF(CommMonitor, "Forwarded non read/write request\n");
}
return successful;
}
bool
CommMonitor::recvTimingResp(PacketPtr pkt)
{
// should always see responses
assert(pkt->isResponse());
// Store relevant fields of packet, because packet may be modified
// or even deleted when sendTiming() is called.
bool isRead = pkt->isRead();
bool isWrite = pkt->isWrite();
unsigned size = pkt->getSize();
Tick latency = 0;
CommMonitorSenderState* commReceivedState =
dynamic_cast<CommMonitorSenderState*>(pkt->senderState);
if (!stats.disableLatencyHists) {
// Restore initial sender state
if (commReceivedState == NULL)
panic("Monitor got a response without monitor sender state\n");
// Restore the sate
pkt->senderState = commReceivedState->origSenderState;
}
// Attempt to send the packet
bool successful = slavePort.sendTimingResp(pkt);
if (!stats.disableLatencyHists) {
// If packet successfully send, sample value of latency,
// afterwards delete sender state, otherwise restore state
if (successful) {
latency = curTick() - commReceivedState->transmitTime;
DPRINTF(CommMonitor, "Latency: %d\n", latency);
delete commReceivedState;
} else {
// Don't delete anything and let the packet look like we
// did not touch it
pkt->senderState = commReceivedState;
}
}
if (successful && isRead) {
// Decrement number of outstanding read requests
DPRINTF(CommMonitor, "Received read response\n");
if (!stats.disableOutstandingHists) {
assert(stats.outstandingReadReqs != 0);
--stats.outstandingReadReqs;
}
if (!stats.disableLatencyHists) {
stats.readLatencyHist.sample(latency);
}
// Update the bandwidth stats based on responses for reads
if (!stats.disableBandwidthHists) {
stats.readBytes += size;
stats.totalReadBytes += size;
}
} else if (successful && isWrite) {
// Decrement number of outstanding write requests
DPRINTF(CommMonitor, "Received write response\n");
if (!stats.disableOutstandingHists) {
assert(stats.outstandingWriteReqs != 0);
--stats.outstandingWriteReqs;
}
if (!stats.disableLatencyHists) {
stats.writeLatencyHist.sample(latency);
}
} else if (successful) {
DPRINTF(CommMonitor, "Received non read/write response\n");
}
return successful;
}
void
CommMonitor::recvTimingSnoopReq(PacketPtr pkt)
{
slavePort.sendTimingSnoopReq(pkt);
}
bool
CommMonitor::recvTimingSnoopResp(PacketPtr pkt)
{
return masterPort.sendTimingSnoopResp(pkt);
}
bool
CommMonitor::isSnooping() const
{
return slavePort.getMasterPort().isSnooping();
}
unsigned
CommMonitor::deviceBlockSizeMaster()
{
return slavePort.peerBlockSize();
}
unsigned
CommMonitor::deviceBlockSizeSlave()
{
return masterPort.peerBlockSize();
}
AddrRangeList
CommMonitor::getAddrRanges()
{
return masterPort.getSlavePort().getAddrRanges();
}
void
CommMonitor::recvRetryMaster()
{
slavePort.sendRetry();
}
void
CommMonitor::recvRetrySlave()
{
masterPort.sendRetry();
}
void
CommMonitor::recvRangeChange()
{
slavePort.sendRangeChange();
}
void
CommMonitor::regStats()
{
// Initialise all the monitor stats
using namespace Stats;
stats.readBurstLengthHist
.init(params()->burst_length_bins)
.name(name() + ".readBurstLengthHist")
.desc("Histogram of burst lengths of transmitted packets")
.flags(stats.disableBurstLengthHists ? nozero : pdf);
stats.writeBurstLengthHist
.init(params()->burst_length_bins)
.name(name() + ".writeBurstLengthHist")
.desc("Histogram of burst lengths of transmitted packets")
.flags(stats.disableBurstLengthHists ? nozero : pdf);
// Stats based on received responses
stats.readBandwidthHist
.init(params()->bandwidth_bins)
.name(name() + ".readBandwidthHist")
.desc("Histogram of read bandwidth per sample period (bytes/s)")
.flags(stats.disableBandwidthHists ? nozero : pdf);
stats.averageReadBW
.name(name() + ".averageReadBandwidth")
.desc("Average read bandwidth (bytes/s)")
.flags(stats.disableBandwidthHists ? nozero : pdf);
stats.totalReadBytes
.name(name() + ".totalReadBytes")
.desc("Number of bytes read")
.flags(stats.disableBandwidthHists ? nozero : pdf);
stats.averageReadBW = stats.totalReadBytes / simSeconds;
// Stats based on successfully sent requests
stats.writeBandwidthHist
.init(params()->bandwidth_bins)
.name(name() + ".writeBandwidthHist")
.desc("Histogram of write bandwidth (bytes/s)")
.flags(stats.disableBandwidthHists ? (pdf | nozero) : pdf);
stats.averageWriteBW
.name(name() + ".averageWriteBandwidth")
.desc("Average write bandwidth (bytes/s)")
.flags(stats.disableBandwidthHists ? nozero : pdf);
stats.totalWrittenBytes
.name(name() + ".totalWrittenBytes")
.desc("Number of bytes written")
.flags(stats.disableBandwidthHists ? nozero : pdf);
stats.averageWriteBW = stats.totalWrittenBytes / simSeconds;
stats.readLatencyHist
.init(params()->latency_bins)
.name(name() + ".readLatencyHist")
.desc("Read request-response latency")
.flags(stats.disableLatencyHists ? nozero : pdf);
stats.writeLatencyHist
.init(params()->latency_bins)
.name(name() + ".writeLatencyHist")
.desc("Write request-response latency")
.flags(stats.disableLatencyHists ? nozero : pdf);
stats.ittReadRead
.init(1, params()->itt_max_bin, params()->itt_max_bin /
params()->itt_bins)
.name(name() + ".ittReadRead")
.desc("Read-to-read inter transaction time")
.flags(stats.disableITTDists ? nozero : pdf);
stats.ittWriteWrite
.init(1, params()->itt_max_bin, params()->itt_max_bin /
params()->itt_bins)
.name(name() + ".ittWriteWrite")
.desc("Write-to-write inter transaction time")
.flags(stats.disableITTDists ? nozero : pdf);
stats.ittReqReq
.init(1, params()->itt_max_bin, params()->itt_max_bin /
params()->itt_bins)
.name(name() + ".ittReqReq")
.desc("Request-to-request inter transaction time")
.flags(stats.disableITTDists ? nozero : pdf);
stats.outstandingReadsHist
.init(params()->outstanding_bins)
.name(name() + ".outstandingReadsHist")
.desc("Outstanding read transactions")
.flags(stats.disableOutstandingHists ? nozero : pdf);
stats.outstandingWritesHist
.init(params()->outstanding_bins)
.name(name() + ".outstandingWritesHist")
.desc("Outstanding write transactions")
.flags(stats.disableOutstandingHists ? nozero : pdf);
stats.readTransHist
.init(params()->transaction_bins)
.name(name() + ".readTransHist")
.desc("Histogram of read transactions per sample period")
.flags(stats.disableTransactionHists ? nozero : pdf);
stats.writeTransHist
.init(params()->transaction_bins)
.name(name() + ".writeTransHist")
.desc("Histogram of read transactions per sample period")
.flags(stats.disableTransactionHists ? nozero : pdf);
stats.readAddrDist
.init(0)
.name(name() + ".readAddrDist")
.desc("Read address distribution")
.flags(stats.disableAddrDists ? nozero : pdf);
stats.writeAddrDist
.init(0)
.name(name() + ".writeAddrDist")
.desc("Write address distribution")
.flags(stats.disableAddrDists ? nozero : pdf);
}
void
CommMonitor::samplePeriodic()
{
// the periodic stats update runs on the granularity of sample
// periods, but in combination with this there may also be a
// external resets and dumps of the stats (through schedStatEvent)
// causing the stats themselves to capture less than a sample
// period
// only capture if we have not reset the stats during the last
// sample period
if (simTicks.value() >= samplePeriodTicks) {
if (!stats.disableTransactionHists) {
stats.readTransHist.sample(stats.readTrans);
stats.writeTransHist.sample(stats.writeTrans);
}
if (!stats.disableBandwidthHists) {
stats.readBandwidthHist.sample(stats.readBytes / samplePeriod);
stats.writeBandwidthHist.sample(stats.writtenBytes / samplePeriod);
}
if (!stats.disableOutstandingHists) {
stats.outstandingReadsHist.sample(stats.outstandingReadReqs);
stats.outstandingWritesHist.sample(stats.outstandingWriteReqs);
}
}
// reset the sampled values
stats.readTrans = 0;
stats.writeTrans = 0;
stats.readBytes = 0;
stats.writtenBytes = 0;
schedule(samplePeriodicEvent, curTick() + samplePeriodTicks);
}
void
CommMonitor::startup()
{
schedule(samplePeriodicEvent, curTick() + samplePeriodTicks);
}

432
src/mem/comm_monitor.hh Normal file
View file

@ -0,0 +1,432 @@
/*
* Copyright (c) 2012 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.
*
* Authors: Thomas Grass
* Andreas Hansson
*/
#ifndef __MEM_COMM_MONITOR_HH__
#define __MEM_COMM_MONITOR_HH__
#include "base/statistics.hh"
#include "base/time.hh"
#include "mem/mem_object.hh"
#include "params/CommMonitor.hh"
/**
* The communication monitor is a MemObject which can monitor statistics of
* the communication happening between two ports in the memory system.
*
* Currently the following stats are implemented: Histograms of read/write
* transactions, read/write burst lengths, read/write bandwidth,
* outstanding read/write requests, read latency and inter transaction time
* (read-read, write-write, read/write-read/write). Furthermore it allows
* to capture the number of accesses to an address over time ("heat map").
* All stats can be disabled from Python.
*/
class CommMonitor : public MemObject
{
public:
/** Parameters of communication monitor */
typedef CommMonitorParams Params;
const Params* params() const
{ return reinterpret_cast<const Params*>(_params); }
/**
* Constructor based on the Python params
*
* @param params Python parameters
*/
CommMonitor(Params* params);
/** Destructor */
~CommMonitor() { }
virtual MasterPort& getMasterPort(const std::string& if_name,
int idx = -1);
virtual SlavePort& getSlavePort(const std::string& if_name,
int idx = -1);
virtual void init();
/** Register statistics */
void regStats();
private:
/**
* Sender state class for the monitor so that we can annotate
* packets with a transmit time and receive time.
*/
class CommMonitorSenderState : public Packet::SenderState
{
public:
/**
* Construct a new sender state and remember the original one
* so that we can implement a stack.
*
* @param _origSenderState Sender state to remember
* @param _transmitTime Time of packet transmission
*/
CommMonitorSenderState(SenderState* _origSenderState,
Tick _transmitTime)
: origSenderState(_origSenderState), transmitTime(_transmitTime)
{ }
/** Destructor */
~CommMonitorSenderState() { }
/** Pointer to old sender state of packet */
SenderState* origSenderState;
/** Tick when request is transmitted */
Tick transmitTime;
};
/**
* This is the master port of the communication monitor. All recv
* functions call a function in CommMonitor, where the
* send function of the slave port is called. Besides this, these
* functions can also perform actions for capturing statistics.
*/
class MonitorMasterPort : public MasterPort
{
public:
MonitorMasterPort(const std::string& _name, CommMonitor& _mon)
: MasterPort(_name, &_mon), mon(_mon)
{ }
protected:
void recvFunctionalSnoop(PacketPtr pkt)
{
mon.recvFunctionalSnoop(pkt);
}
Tick recvAtomicSnoop(PacketPtr pkt)
{
return mon.recvAtomicSnoop(pkt);
}
bool recvTimingResp(PacketPtr pkt)
{
return mon.recvTimingResp(pkt);
}
void recvTimingSnoopReq(PacketPtr pkt)
{
mon.recvTimingSnoopReq(pkt);
}
void recvRangeChange()
{
mon.recvRangeChange();
}
bool isSnooping() const
{
return mon.isSnooping();
}
unsigned deviceBlockSize() const
{
return mon.deviceBlockSizeMaster();
}
void recvRetry()
{
mon.recvRetryMaster();
}
private:
CommMonitor& mon;
};
/** Instance of master port, facing the memory side */
MonitorMasterPort masterPort;
/**
* This is the slave port of the communication monitor. All recv
* functions call a function in CommMonitor, where the
* send function of the master port is called. Besides this, these
* functions can also perform actions for capturing statistics.
*/
class MonitorSlavePort : public SlavePort
{
public:
MonitorSlavePort(const std::string& _name, CommMonitor& _mon)
: SlavePort(_name, &_mon), mon(_mon)
{ }
protected:
void recvFunctional(PacketPtr pkt)
{
mon.recvFunctional(pkt);
}
Tick recvAtomic(PacketPtr pkt)
{
return mon.recvAtomic(pkt);
}
bool recvTimingReq(PacketPtr pkt)
{
return mon.recvTimingReq(pkt);
}
bool recvTimingSnoopResp(PacketPtr pkt)
{
return mon.recvTimingSnoopResp(pkt);
}
unsigned deviceBlockSize() const
{
return mon.deviceBlockSizeSlave();
}
AddrRangeList getAddrRanges()
{
return mon.getAddrRanges();
}
void recvRetry()
{
mon.recvRetrySlave();
}
private:
CommMonitor& mon;
};
/** Instance of slave port, i.e. on the CPU side */
MonitorSlavePort slavePort;
void recvFunctional(PacketPtr pkt);
void recvFunctionalSnoop(PacketPtr pkt);
Tick recvAtomic(PacketPtr pkt);
Tick recvAtomicSnoop(PacketPtr pkt);
bool recvTimingReq(PacketPtr pkt);
bool recvTimingResp(PacketPtr pkt);
void recvTimingSnoopReq(PacketPtr pkt);
bool recvTimingSnoopResp(PacketPtr pkt);
unsigned deviceBlockSizeMaster();
unsigned deviceBlockSizeSlave();
AddrRangeList getAddrRanges();
bool isSnooping() const;
void recvRetryMaster();
void recvRetrySlave();
void recvRangeChange();
void periodicTraceDump();
/** Stats declarations, all in a struct for convenience. */
struct MonitorStats
{
/** Disable flag for burst length historgrams **/
bool disableBurstLengthHists;
/** Histogram of read burst lengths */
Stats::Histogram readBurstLengthHist;
/** Histogram of write burst lengths */
Stats::Histogram writeBurstLengthHist;
/** Disable flag for the bandwidth histograms */
bool disableBandwidthHists;
/**
* Histogram for read bandwidth per sample window. The
* internal counter is an unsigned int rather than a stat.
*/
unsigned int readBytes;
Stats::Histogram readBandwidthHist;
Stats::Formula averageReadBW;
Stats::Scalar totalReadBytes;
/**
* Histogram for write bandwidth per sample window. The
* internal counter is an unsigned int rather than a stat.
*/
unsigned int writtenBytes;
Stats::Histogram writeBandwidthHist;
Stats::Formula averageWriteBW;
Stats::Scalar totalWrittenBytes;
/** Disable flag for latency histograms. */
bool disableLatencyHists;
/** Histogram of read request-to-response latencies */
Stats::Histogram readLatencyHist;
/** Histogram of write request-to-response latencies */
Stats::Histogram writeLatencyHist;
/** Disable flag for ITT distributions. */
bool disableITTDists;
/**
* Inter transaction time (ITT) distributions. There are
* histograms of the time between two read, write or arbitrary
* accesses. The time of a request is the tick at which the
* request is forwarded by the monitor.
*/
Stats::Distribution ittReadRead;
Stats::Distribution ittWriteWrite;
Stats::Distribution ittReqReq;
Tick timeOfLastRead;
Tick timeOfLastWrite;
Tick timeOfLastReq;
/** Disable flag for outstanding histograms. */
bool disableOutstandingHists;
/**
* Histogram of outstanding read requests. Counter for
* outstanding read requests is an unsigned integer because
* it should not be reset when stats are reset.
*/
Stats::Histogram outstandingReadsHist;
unsigned int outstandingReadReqs;
/**
* Histogram of outstanding write requests. Counter for
* outstanding write requests is an unsigned integer because
* it should not be reset when stats are reset.
*/
Stats::Histogram outstandingWritesHist;
unsigned int outstandingWriteReqs;
/** Disable flag for transaction histograms. */
bool disableTransactionHists;
/** Histogram of number of read transactions per time bin */
Stats::Histogram readTransHist;
unsigned int readTrans;
/** Histogram of number of timing write transactions per time bin */
Stats::Histogram writeTransHist;
unsigned int writeTrans;
/** Disable flag for address distributions. */
bool disableAddrDists;
/**
* Histogram of number of read accesses to addresses over
* time.
*/
Stats::SparseHistogram readAddrDist;
/**
* Histogram of number of write accesses to addresses over
* time.
*/
Stats::SparseHistogram writeAddrDist;
/**
* Create the monitor stats and initialise all the members
* that are not statistics themselves, but used to control the
* stats or track values during a sample period.
*/
MonitorStats(const CommMonitorParams* params) :
disableBurstLengthHists(params->disable_burst_length_hists),
disableBandwidthHists(params->disable_bandwidth_hists),
readBytes(0), writtenBytes(0),
disableLatencyHists(params->disable_latency_hists),
disableITTDists(params->disable_itt_dists),
timeOfLastRead(0), timeOfLastWrite(0), timeOfLastReq(0),
disableOutstandingHists(params->disable_outstanding_hists),
outstandingReadReqs(0), outstandingWriteReqs(0),
disableTransactionHists(params->disable_transaction_hists),
readTrans(0), writeTrans(0),
disableAddrDists(params->disable_addr_dists)
{ }
};
/** This function is called periodically at the end of each time bin */
void samplePeriodic();
/** Schedule the first periodic event */
void startup();
/** Periodic event called at the end of each simulation time bin */
EventWrapper<CommMonitor, &CommMonitor::samplePeriodic> samplePeriodicEvent;
/** Length of simulation time bin*/
Tick samplePeriodTicks;
Time samplePeriod;
/** Address mask for sources of read accesses to be captured */
Addr readAddrMask;
/** Address mask for sources of write accesses to be captured */
Addr writeAddrMask;
/** Instantiate stats */
MonitorStats stats;
};
#endif //__MEM_COMM_MONITOR_HH__