diff --git a/src/mem/CommMonitor.py b/src/mem/CommMonitor.py new file mode 100644 index 000000000..3621942d9 --- /dev/null +++ b/src/mem/CommMonitor.py @@ -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") diff --git a/src/mem/SConscript b/src/mem/SConscript index efb3c947a..3ffb3503a 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -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 index 000000000..4255d58ad --- /dev/null +++ b/src/mem/comm_monitor.cc @@ -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(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 index 000000000..54f9690ed --- /dev/null +++ b/src/mem/comm_monitor.hh @@ -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(_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 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__