MEM: Add the communication monitor
authorAndreas Hansson <andreas.hansson@arm.com>
Wed, 9 May 2012 08:37:45 +0000 (04:37 -0400)
committerAndreas Hansson <andreas.hansson@arm.com>
Wed, 9 May 2012 08:37:45 +0000 (04:37 -0400)
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.

src/mem/CommMonitor.py [new file with mode: 0644]
src/mem/SConscript
src/mem/comm_monitor.cc [new file with mode: 0644]
src/mem/comm_monitor.hh [new file with mode: 0644]

diff --git a/src/mem/CommMonitor.py b/src/mem/CommMonitor.py
new file mode 100644 (file)
index 0000000..3621942
--- /dev/null
@@ -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")
index efb3c947a72a63c67139bb2cca24a5e1776144ea..3ffb3503aba77fd10d3d4077d78243a1ee4a3986 100644 (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')
diff --git a/src/mem/comm_monitor.cc b/src/mem/comm_monitor.cc
new file mode 100644 (file)
index 0000000..4255d58
--- /dev/null
@@ -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);
+}
diff --git a/src/mem/comm_monitor.hh b/src/mem/comm_monitor.hh
new file mode 100644 (file)
index 0000000..54f9690
--- /dev/null
@@ -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__