From dd0f3943e20072021987780d6b15f531a4be2fca Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Tue, 23 Dec 2014 09:31:17 -0500 Subject: [PATCH] mem: Add MemChecker and MemCheckerMonitor This patch adds the MemChecker and MemCheckerMonitor classes. While MemChecker can be integrated anywhere in the system and is independent, the most convenient usage is through the MemCheckerMonitor -- this however, puts limitations on where the MemChecker is able to observe read/write transactions. --- src/mem/MemChecker.py | 58 ++++ src/mem/SConscript | 7 + src/mem/mem_checker.cc | 343 ++++++++++++++++++++ src/mem/mem_checker.hh | 568 +++++++++++++++++++++++++++++++++ src/mem/mem_checker_monitor.cc | 374 ++++++++++++++++++++++ src/mem/mem_checker_monitor.hh | 240 ++++++++++++++ 6 files changed, 1590 insertions(+) create mode 100644 src/mem/MemChecker.py create mode 100644 src/mem/mem_checker.cc create mode 100644 src/mem/mem_checker.hh create mode 100644 src/mem/mem_checker_monitor.cc create mode 100644 src/mem/mem_checker_monitor.hh diff --git a/src/mem/MemChecker.py b/src/mem/MemChecker.py new file mode 100644 index 000000000..5126f4364 --- /dev/null +++ b/src/mem/MemChecker.py @@ -0,0 +1,58 @@ +# Copyright (c) 2014 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: Marco Elver + +from MemObject import MemObject +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * + +class MemChecker(SimObject): + type = 'MemChecker' + cxx_header = "mem/mem_checker.hh" + +class MemCheckerMonitor(MemObject): + type = 'MemCheckerMonitor' + cxx_header = "mem/mem_checker_monitor.hh" + + # one port in each direction + master = MasterPort("Master port") + slave = SlavePort("Slave port") + cpu_side = SlavePort("Alias for slave") + mem_side = MasterPort("Alias for master") + warn_only = Param.Bool(False, "Warn about violations only") + memchecker = Param.MemChecker("Instance shared with other monitors") + diff --git a/src/mem/SConscript b/src/mem/SConscript index e7d2c1bac..e6973b1ac 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -79,6 +79,10 @@ if env['HAVE_DRAMSIM']: Source('dramsim2_wrapper.cc') Source('dramsim2.cc') +SimObject('MemChecker.py') +Source('mem_checker.cc') +Source('mem_checker_monitor.cc') + DebugFlag('AddrRanges') DebugFlag('BaseXBar') DebugFlag('CoherentXBar') @@ -99,3 +103,6 @@ DebugFlag('MemoryAccess') DebugFlag('PacketQueue') DebugFlag("DRAMSim2") + +DebugFlag("MemChecker") +DebugFlag("MemCheckerMonitor") diff --git a/src/mem/mem_checker.cc b/src/mem/mem_checker.cc new file mode 100644 index 000000000..0b8073660 --- /dev/null +++ b/src/mem/mem_checker.cc @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2014 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: Rune Holm + * Marco Elver + */ + +#include + +#include "mem/mem_checker.hh" + +void +MemChecker::WriteCluster::startWrite(MemChecker::Serial serial, Tick _start, + uint8_t data) +{ + assert(!isComplete()); + + if (start == TICK_FUTURE) { + // Initialize a fresh write cluster + start = _start; + } + chatty_assert(start <= _start, "WriteClusters must filled in order!"); + + ++numIncomplete; + + if (complete != TICK_FUTURE) { + // Reopen a closed write cluster + assert(_start < complete); // should open a new write cluster, instead; + // also somewhat fishy wrt causality / ordering of calls vs time + // progression TODO: Check me! + complete = TICK_FUTURE; + } + + // Create new transaction, and denote completion time to be in the future. + writes.insert(std::make_pair(serial, + MemChecker::Transaction(serial, _start, TICK_FUTURE, data))); +} + +void +MemChecker::WriteCluster::completeWrite(MemChecker::Serial serial, Tick _complete) +{ + auto it = writes.find(serial); + + if (it == writes.end()) { + warn("Could not locate write transaction: serial = %d, complete = %d\n", + serial, _complete); + return; + } + + // Record completion time of the write + assert(it->second.complete == TICK_FUTURE); + it->second.complete = _complete; + + // Update max completion time for the cluster + if (completeMax < _complete) { + completeMax = _complete; + } + + if (--numIncomplete == 0) { + // All writes have completed, this cluster is now complete and will be + // assigned the max of completion tick values among all writes. + // + // Note that we cannot simply keep updating complete, because that would + // count the cluster as closed already. Instead, we keep TICK_FUTURE + // until all writes have completed. + complete = completeMax; + } +} + +void +MemChecker::WriteCluster::abortWrite(MemChecker::Serial serial) +{ + if (!writes.erase(serial)) { + warn("Could not locate write transaction: serial = %d\n", serial); + return; + } + + if (--numIncomplete == 0 && !writes.empty()) { + // This write cluster is now complete, and we can assign the current + // completeMax value. + complete = completeMax; + } + + // Note: this WriteCluster is in pristine state if this was the only + // write present; the cluster will get reused through + // getIncompleteWriteCluster(). +} + +void +MemChecker::ByteTracker::startRead(MemChecker::Serial serial, Tick start) +{ + outstandingReads.insert(std::make_pair(serial, + MemChecker::Transaction(serial, start, TICK_FUTURE))); +} + +bool +MemChecker::ByteTracker::inExpectedData(Tick start, Tick complete, uint8_t data) +{ + _lastExpectedData.clear(); + + bool wc_overlap = true; + + // Find the last value read from the location + const Transaction& last_obs = + *lastCompletedTransaction(&readObservations, start); + bool last_obs_valid = (last_obs.complete != TICK_INITIAL); + + // Scan backwards through the write clusters to find the closest younger + // preceding & overlapping writes. + for (auto cluster = writeClusters.rbegin(); + cluster != writeClusters.rend() && wc_overlap; ++cluster) { + for (const auto& addr_write : cluster->writes) { + const Transaction& write = addr_write.second; + + if (write.complete < last_obs.start) { + // If this write transaction completed before the last + // observation, we ignore it as the last_observation has the + // correct value + continue; + } + + if (write.data == data) { + // Found a match, end search. + return true; + } + + // Record possible, but non-matching data for debugging + _lastExpectedData.push_back(write.data); + + if (write.complete > start) { + // This write overlapped with the transaction we want to check + // -> continue checking the overlapping write cluster + continue; + } + + // This write cluster has writes that have completed before the + // checked transaction. There is no need to check an earlier + // write-cluster -> set the exit condition for the outer loop + wc_overlap = false; + + if (last_obs.complete < write.start) { + // We found a write which started after the last observed read, + // therefore we can not longer consider the value seen by the + // last observation as a valid expected value. + // + // Once all writes have been iterated through, we can check if + // the last observation is still valid to compare against. + last_obs_valid = false; + } + } + } + + // We have not found any matching write, so far; check other sources of + // confirmation + if (last_obs_valid) { + // The last observation is not outdated according to the writes we have + // seen so far. + assert(last_obs.complete <= start); + if (last_obs.data == data) { + // Matched data from last observation -> all good + return true; + } + // Record non-matching, but possible value + _lastExpectedData.push_back(last_obs.data); + } + + if (_lastExpectedData.empty()) { + assert(last_obs.complete == TICK_INITIAL); + // We have not found any possible (non-matching data). Can happen in + // initial system state + DPRINTF(MemChecker, "no last observation nor write! start = %d, "\ + "complete = %d, data = %#x\n", start, complete, data); + return true; + } + return false; +} + +bool +MemChecker::ByteTracker::completeRead(MemChecker::Serial serial, + Tick complete, uint8_t data) +{ + auto it = outstandingReads.find(serial); + + if (it == outstandingReads.end()) { + // Can happen if concurrent with reset_address_range + warn("Could not locate read transaction: serial = %d, complete = %d\n", + serial, complete); + return true; + } + + Tick start = it->second.start; + outstandingReads.erase(it); + + // Verify data + const bool result = inExpectedData(start, complete, data); + + readObservations.emplace_back(serial, start, complete, data); + pruneTransactions(); + + return result; +} + +MemChecker::WriteCluster* +MemChecker::ByteTracker::getIncompleteWriteCluster() +{ + if (writeClusters.empty() || writeClusters.back().isComplete()) { + writeClusters.emplace_back(); + } + + return &writeClusters.back(); +} + +void +MemChecker::ByteTracker::startWrite(MemChecker::Serial serial, Tick start, + uint8_t data) +{ + getIncompleteWriteCluster()->startWrite(serial, start, data); +} + +void +MemChecker::ByteTracker::completeWrite(MemChecker::Serial serial, Tick complete) +{ + getIncompleteWriteCluster()->completeWrite(serial, complete); + pruneTransactions(); +} + +void +MemChecker::ByteTracker::abortWrite(MemChecker::Serial serial) +{ + getIncompleteWriteCluster()->abortWrite(serial); +} + +void +MemChecker::ByteTracker::pruneTransactions() +{ + // Obtain tick of first outstanding read. If there are no outstanding + // reads, we use curTick(), i.e. we will remove all readObservation except + // the most recent one. + const Tick before = outstandingReads.empty() ? curTick() : + outstandingReads.begin()->second.start; + + // Pruning of readObservations + readObservations.erase(readObservations.begin(), + lastCompletedTransaction(&readObservations, before)); + + // Pruning of writeClusters + if (!writeClusters.empty()) { + writeClusters.erase(writeClusters.begin(), + lastCompletedTransaction(&writeClusters, before)); + } +} + +bool +MemChecker::completeRead(MemChecker::Serial serial, Tick complete, + Addr addr, size_t size, uint8_t *data) +{ + bool result = true; + + DPRINTF(MemChecker, + "completing read: serial = %d, complete = %d, " + "addr = %#llx, size = %d\n", serial, complete, addr, size); + + for (size_t i = 0; i < size; ++i) { + ByteTracker *tracker = getByteTracker(addr + i); + + if (!tracker->completeRead(serial, complete, data[i])) { + // Generate error message, and aggregate all failures for the bytes + // considered in this transaction in one message. + if (result) { + result = false; + errorMessage = ""; + } else { + errorMessage += "\n"; + } + + errorMessage += csprintf(" Read transaction for address %#llx " + "failed: received %#x, expected ", + (unsigned long long)(addr + i), data[i]); + + for (size_t j = 0; j < tracker->lastExpectedData().size(); ++j) { + errorMessage += + csprintf("%#x%s", + tracker->lastExpectedData()[j], + (j == tracker->lastExpectedData().size() - 1) + ? "" : "|"); + } + } + } + + if (!result) { + DPRINTF(MemChecker, "read of %#llx @ cycle %d failed:\n%s\n", addr, + complete, errorMessage); + } + + return result; +} + +void +MemChecker::reset(Addr addr, size_t size) +{ + for (size_t i = 0; i < size; ++i) { + byte_trackers.erase(addr + i); + } +} + +MemChecker* +MemCheckerParams::create() +{ + return new MemChecker(this); +} diff --git a/src/mem/mem_checker.hh b/src/mem/mem_checker.hh new file mode 100644 index 000000000..0ec0f08df --- /dev/null +++ b/src/mem/mem_checker.hh @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2014 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: Rune Holm + * Marco Elver + */ + +#ifndef __MEM_MEM_CHECKER_HH__ +#define __MEM_MEM_CHECKER_HH__ + +#include +#include +#include +#include + +#include "base/hashmap.hh" +#include "base/misc.hh" +#include "base/types.hh" +#include "debug/MemChecker.hh" +#include "params/MemChecker.hh" +#include "sim/core.hh" +#include "sim/sim_object.hh" + +/** + * MemChecker. Verifies that reads observe the values from permissible writes. + * As memory operations have a start and completion time, we consider them as + * transactions which have a start and end time. Because of this, the lifetimes + * of transactions of memory operations may be overlapping -- we assume that if + * there is overlap between writes, they could be reordered by the memory + * subsystem, and a read could any of these. For more detail, see comments of + * inExpectedData(). + * + * For simplicity, the permissible values a read can observe are only dependent + * on the particular location, and we do not consider the effect of multi-byte + * reads or writes. This precludes us from discovering single-copy atomicity + * violations. +*/ +class MemChecker : public SimObject +{ + public: + /** + * The Serial type is used to be able to uniquely identify a transaction as + * it passes through the system. It's value is independent of any other + * system counters. + */ + typedef uint64_t Serial; + + static const Serial SERIAL_INITIAL = 0; //!< Initial serial + + /** + * The initial tick the system starts with. Must not be larger than the + * minimum value that curTick() could return at any time in the system's + * execution. + */ + static const Tick TICK_INITIAL = 0; + + /** + * The maximum value that curTick() could ever return. + */ + static const Tick TICK_FUTURE = MaxTick; + + /** + * Initial data value. No requirements. + */ + static const uint8_t DATA_INITIAL = 0x00; + + /** + * The Transaction class captures the lifetimes of read and write + * operations, and the values they consumed or produced respectively. + */ + class Transaction + { + public: + + Transaction(Serial _serial, + Tick _start, Tick _complete, + uint8_t _data = DATA_INITIAL) + : serial(_serial), + start(_start), complete(_complete), + data(_data) + {} + + public: + Serial serial; //!< Unique identifying serial + Tick start; //!< Start tick + Tick complete; //!< Completion tick + + /** + * Depending on the memory operation, the data value either represents: + * for writes, the value written upon start; for reads, the value read + * upon completion. + */ + uint8_t data; + + /** + * Orders Transactions for use with std::map. + */ + bool operator<(const Transaction& rhs) const + { return serial < rhs.serial; } + }; + + /** + * The WriteCluster class captures sets of writes where all writes are + * overlapping with at least one other write. Capturing writes in this way + * simplifies pruning of writes. + */ + class WriteCluster + { + public: + WriteCluster() + : start(TICK_FUTURE), complete(TICK_FUTURE), + completeMax(TICK_INITIAL), numIncomplete(0) + {} + + /** + * Starts a write transaction. + * + * @param serial Unique identifier of the write. + * @param _start When the write was sent off to the memory subsystem. + * @param data The data that this write passed to the memory + * subsystem. + */ + void startWrite(Serial serial, Tick _start, uint8_t data); + + /** + * Completes a write transaction. + * + * @param serial Unique identifier of a write *previously started*. + * @param _complete When the write was sent off to the memory + * subsystem. + */ + void completeWrite(Serial serial, Tick _complete); + + /** + * Aborts a write transaction. + * + * @param serial Unique identifier of a write *previously started*. + */ + void abortWrite(Serial serial); + + /** + * @return true if this cluster's write all completed, false otherwise. + */ + bool isComplete() const { return complete != TICK_FUTURE; } + + public: + Tick start; //!< Start of earliest write in cluster + Tick complete; //!< Completion of last write in cluster + + /** + * Map of Serial --> Transaction of all writes in cluster; contains + * all, in-flight or already completed. + */ + m5::hash_map writes; + + private: + Tick completeMax; + size_t numIncomplete; + }; + + typedef std::list TransactionList; + typedef std::list WriteClusterList; + + /** + * The ByteTracker keeps track of transactions for the *same byte* -- all + * outstanding reads, the completed reads (and what they observed) and write + * clusters (see WriteCluster). + */ + class ByteTracker : public Named + { + public: + + ByteTracker(Addr addr = 0, const MemChecker *parent = NULL) + : Named((parent != NULL ? parent->name() : "") + + csprintf(".ByteTracker@%#llx", addr)) + { + // The initial transaction has start == complete == TICK_INITIAL, + // indicating that there has been no real write to this location; + // therefore, upon checking, we do not expect any particular value. + readObservations.emplace_back( + Transaction(SERIAL_INITIAL, TICK_INITIAL, TICK_INITIAL, + DATA_INITIAL)); + } + + /** + * Starts a read transaction. + * + * @param serial Unique identifier for the read. + * @param start When the read was sent off to the memory subsystem. + */ + void startRead(Serial serial, Tick start); + + /** + * Given a start and end time (of any read transaction), this function + * iterates through all data that such a read is expected to see. The + * data parameter is the actual value that we observed, and the + * function immediately returns true when a match is found, false + * otherwise. + * + * The set of expected data are: + * + * 1. The last value observed by a read with a completion time before + * this start time (if any). + * + * 2. The data produced by write transactions with a completion after + * the last observed read start time. Only data produced in the + * closest overlapping / earlier write cluster relative to this check + * request is considered, as writes in separate clusters are not + * reordered. + * + * @param start Start time of transaction to validate. + * @param complete End time of transaction to validate. + * @param data The value that we have actually seen. + * + * @return True if a match is found, false otherwise. + */ + bool inExpectedData(Tick start, Tick complete, uint8_t data); + + /** + * Completes a read transaction that is still outstanding. + * + * @param serial Unique identifier of a read *previously started*. + * @param complete When the read got a response. + * @param data The data returned by the memory subsystem. + */ + bool completeRead(Serial serial, Tick complete, uint8_t data); + + /** + * Starts a write transaction. Wrapper to startWrite of WriteCluster + * instance. + * + * @param serial Unique identifier of the write. + * @param start When the write was sent off to the memory subsystem. + * @param data The data that this write passed to the memory + * subsystem. + */ + void startWrite(Serial serial, Tick start, uint8_t data); + + /** + * Completes a write transaction. Wrapper to startWrite of WriteCluster + * instance. + * + * @param serial Unique identifier of a write *previously started*. + * @param complete When the write was sent off to the memory subsystem. + */ + void completeWrite(Serial serial, Tick complete); + + /** + * Aborts a write transaction. Wrapper to abortWrite of WriteCluster + * instance. + * + * @param serial Unique identifier of a write *previously started*. + */ + void abortWrite(Serial serial); + + /** + * This function returns the expected data that inExpectedData iterated + * through in the last call. If inExpectedData last returned true, the + * set may be incomplete; if inExpectedData last returned false, the + * vector will contain the full set. + * + * @return Reference to internally maintained vector maintaining last + * expected data that inExpectedData iterated through. + */ + const std::vector& lastExpectedData() const + { return _lastExpectedData; } + + private: + + /** + * Convenience function to return the most recent incomplete write + * cluster. Instantiates new write cluster if the most recent one has + * been completed. + * + * @return The most recent incomplete write cluster. + */ + WriteCluster* getIncompleteWriteCluster(); + + /** + * Helper function to return an iterator to the entry of a container of + * Transaction compatible classes, before a certain tick. + * + * @param before Tick value which should be greater than the + * completion tick of the returned element. + * + * @return Iterator into container. + */ + template + typename TList::iterator lastCompletedTransaction(TList *l, Tick before) + { + assert(!l->empty()); + + // Scanning backwards increases the chances of getting a match + // quicker. + auto it = l->end(); + + for (--it; it != l->begin() && it->complete >= before; --it); + + return it; + } + + /** + * Prunes no longer needed transactions. We only keep up to the last / + * most recent of each, readObservations and writeClusters, before the + * first outstanding read. + * + * It depends on the contention / overlap between memory operations to + * the same location of a particular workload how large each of them + * would grow. + */ + void pruneTransactions(); + + private: + + /** + * Maintains a map of Serial -> Transaction for all outstanding reads. + * + * Use an ordered map here, as this makes pruneTransactions() more + * efficient (find first outstanding read). + */ + std::map outstandingReads; + + /** + * List of completed reads, i.e. observations of reads. + */ + TransactionList readObservations; + + /** + * List of write clusters for this address. + */ + WriteClusterList writeClusters; + + /** + * See lastExpectedData(). + */ + std::vector _lastExpectedData; + }; + + public: + + MemChecker(const MemCheckerParams *p) + : SimObject(p), + nextSerial(SERIAL_INITIAL) + {} + + virtual ~MemChecker() {} + + /** + * Starts a read transaction. + * + * @param start Tick this read was sent to the memory subsystem. + * @param addr Address for read. + * @param size Size of data expected. + * + * @return Serial representing the unique identifier for this transaction. + */ + Serial startRead(Tick start, Addr addr, size_t size); + + /** + * Starts a write transaction. + * + * @param start Tick when this write was sent to the memory subsystem. + * @param addr Address for write. + * @param size Size of data to be written. + * @param data Pointer to size bytes, containing data to be written. + * + * @return Serial representing the unique identifier for this transaction. + */ + Serial startWrite(Tick start, Addr addr, size_t size, const uint8_t *data); + + /** + * Completes a previously started read transaction. + * + * @param serial A serial of a read that was previously started and + * matches the address of the previously started read. + * @param complete Tick we received the response from the memory subsystem. + * @param addr Address for read. + * @param size Size of data received. + * @param data Pointer to size bytes, containing data received. + * + * @return True if the data we received is in the expected set, false + * otherwise. + */ + bool completeRead(Serial serial, Tick complete, + Addr addr, size_t size, uint8_t *data); + + /** + * Completes a previously started write transaction. + * + * @param serial A serial of a write that was previously started and + * matches the address of the previously started write. + * @param complete Tick we received acknowledgment of completion from the + * memory subsystem. + * @param addr Address for write. + * @param size The size of the data written. + */ + void completeWrite(Serial serial, Tick complete, Addr addr, size_t size); + + /** + * Aborts a previously started write transaction. + * + * @param serial A serial of a write that was previously started and + * matches the address of the previously started write. + * @param addr Address for write. + * @param size The size of the data written. + */ + void abortWrite(Serial serial, Addr addr, size_t size); + + /** + * Resets the entire checker. Note that if there are transactions + * in-flight, this will cause a warning to be issued if these are completed + * after the reset. This does not reset nextSerial to avoid such a race + * condition: where a transaction started before a reset with serial S, + * then reset() was called, followed by a start of a transaction with the + * same serial S and then receive a completion of the transaction before + * the reset with serial S. + */ + void reset() + { byte_trackers.clear(); } + + /** + * Resets an address-range. This may be useful in case other unmonitored + * parts of the system caused modification to this memory, but we cannot + * track their written values. + * + * @param addr Address base. + * @param size Size of range to be invalidated. + */ + void reset(Addr addr, size_t size); + + /** + * In completeRead, if an error is encountered, this does not print nor + * cause an error, but instead should be handled by the caller. However, to + * record information about the cause of an error, completeRead creates an + * errorMessage. This function returns the last error that was detected in + * completeRead. + * + * @return Reference to string of error message. + */ + const std::string& getErrorMessage() const { return errorMessage; } + + private: + /** + * Returns the instance of ByteTracker for the requested location. + */ + ByteTracker* getByteTracker(Addr addr) + { + auto it = byte_trackers.find(addr); + if (it == byte_trackers.end()) { + it = byte_trackers.insert( + std::make_pair(addr, ByteTracker(addr, this))).first; + } + return &it->second; + }; + + private: + /** + * Detailed error message of the last violation in completeRead. + */ + std::string errorMessage; + + /** + * Next distinct serial to be assigned to the next transaction to be + * started. + */ + Serial nextSerial; + + /** + * Maintain a map of address --> byte-tracker. Per-byte entries are + * initialized as needed. + * + * The required space for this obviously grows with the number of distinct + * addresses used for a particular workload. The used size is independent on + * the number of nodes in the system, those may affect the size of per-byte + * tracking information. + * + * Access via getByteTracker()! + */ + m5::hash_map byte_trackers; +}; + +inline MemChecker::Serial +MemChecker::startRead(Tick start, Addr addr, size_t size) +{ + DPRINTF(MemChecker, + "starting read: serial = %d, start = %d, addr = %#llx, " + "size = %d\n", nextSerial, start, addr , size); + + for (size_t i = 0; i < size; ++i) { + getByteTracker(addr + i)->startRead(nextSerial, start); + } + + return nextSerial++; +} + +inline MemChecker::Serial +MemChecker::startWrite(Tick start, Addr addr, size_t size, const uint8_t *data) +{ + DPRINTF(MemChecker, + "starting write: serial = %d, start = %d, addr = %#llx, " + "size = %d\n", nextSerial, start, addr, size); + + for (size_t i = 0; i < size; ++i) { + getByteTracker(addr + i)->startWrite(nextSerial, start, data[i]); + } + + return nextSerial++; +} + +inline void +MemChecker::completeWrite(MemChecker::Serial serial, Tick complete, + Addr addr, size_t size) +{ + DPRINTF(MemChecker, + "completing write: serial = %d, complete = %d, " + "addr = %#llx, size = %d\n", serial, complete, addr, size); + + for (size_t i = 0; i < size; ++i) { + getByteTracker(addr + i)->completeWrite(serial, complete); + } +} + +inline void +MemChecker::abortWrite(MemChecker::Serial serial, Addr addr, size_t size) +{ + DPRINTF(MemChecker, + "aborting write: serial = %d, addr = %#llx, size = %d\n", + serial, addr, size); + + for (size_t i = 0; i < size; ++i) { + getByteTracker(addr + i)->abortWrite(serial); + } +} + +#endif // __MEM_MEM_CHECKER_HH__ diff --git a/src/mem/mem_checker_monitor.cc b/src/mem/mem_checker_monitor.cc new file mode 100644 index 000000000..e0f863d99 --- /dev/null +++ b/src/mem/mem_checker_monitor.cc @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2012-2014 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 + * Marco Elver + */ + +#include + +#include "base/output.hh" +#include "base/trace.hh" +#include "debug/MemCheckerMonitor.hh" +#include "mem/mem_checker_monitor.hh" + +using namespace std; + +MemCheckerMonitor::MemCheckerMonitor(Params* params) + : MemObject(params), + masterPort(name() + "-master", *this), + slavePort(name() + "-slave", *this), + warnOnly(params->warn_only), + memchecker(params->memchecker) +{} + +MemCheckerMonitor::~MemCheckerMonitor() +{} + +MemCheckerMonitor* +MemCheckerMonitorParams::create() +{ + return new MemCheckerMonitor(this); +} + +void +MemCheckerMonitor::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"); +} + +BaseMasterPort& +MemCheckerMonitor::getMasterPort(const std::string& if_name, PortID idx) +{ + if (if_name == "master" || if_name == "mem_side") { + return masterPort; + } else { + return MemObject::getMasterPort(if_name, idx); + } +} + +BaseSlavePort& +MemCheckerMonitor::getSlavePort(const std::string& if_name, PortID idx) +{ + if (if_name == "slave" || if_name == "cpu_side") { + return slavePort; + } else { + return MemObject::getSlavePort(if_name, idx); + } +} + +void +MemCheckerMonitor::recvFunctional(PacketPtr pkt) +{ + Addr addr = pkt->getAddr(); + unsigned size = pkt->getSize(); + + // Conservatively reset this address-range. Alternatively we could try to + // update the values seen by the memchecker, however, there may be other + // reads/writes to these location from other devices we do not see. + memchecker->reset(addr, size); + + masterPort.sendFunctional(pkt); + + DPRINTF(MemCheckerMonitor, + "Forwarded functional access: addr = %#llx, size = %d\n", + addr, size); +} + +void +MemCheckerMonitor::recvFunctionalSnoop(PacketPtr pkt) +{ + Addr addr = pkt->getAddr(); + unsigned size = pkt->getSize(); + + // See above. + memchecker->reset(addr, size); + + slavePort.sendFunctionalSnoop(pkt); + + DPRINTF(MemCheckerMonitor, + "Received functional snoop: addr = %#llx, size = %d\n", + addr, size); +} + +Tick +MemCheckerMonitor::recvAtomic(PacketPtr pkt) +{ + assert(false && "Atomic not supported"); + return masterPort.sendAtomic(pkt); +} + +Tick +MemCheckerMonitor::recvAtomicSnoop(PacketPtr pkt) +{ + assert(false && "Atomic not supported"); + return slavePort.sendAtomicSnoop(pkt); +} + +bool +MemCheckerMonitor::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. + // + // For reads we are only interested in real reads, and not prefetches, as + // it is not guaranteed that the prefetch returns any useful data. + bool is_read = pkt->isRead() && !pkt->req->isPrefetch(); + bool is_write = pkt->isWrite(); + unsigned size = pkt->getSize(); + Addr addr = pkt->getAddr(); + bool expects_response = pkt->needsResponse() && !pkt->memInhibitAsserted(); + std::unique_ptr pkt_data; + MemCheckerMonitorSenderState* state = NULL; + + if (expects_response && is_write) { + // On receipt of a request, only need to allocate pkt_data if this is a + // write. For reads, we have no data yet, so it doesn't make sense to + // allocate. + pkt_data.reset(new uint8_t[size]); + memcpy(pkt_data.get(), pkt->getConstPtr(), size); + } + + // 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 (expects_response && (is_read || is_write)) { + state = new MemCheckerMonitorSenderState(0); + pkt->pushSenderState(state); + } + + // Attempt to send the packet (always succeeds for inhibited + // packets) + bool successful = masterPort.sendTimingReq(pkt); + + // If not successful, restore the sender state + if (!successful && expects_response && (is_read || is_write)) { + delete pkt->popSenderState(); + } + + if (successful && expects_response) { + if (is_read) { + MemChecker::Serial serial = memchecker->startRead(curTick(), + addr, + size); + + // At the time where we push the sender-state, we do not yet know + // the serial the MemChecker class will assign to this request. We + // cannot call startRead at the time we push the sender-state, as + // the masterPort may not be successful in executing sendTimingReq, + // and in case of a failure, we must not modify the state of the + // MemChecker. + // + // Once we know that sendTimingReq was successful, we can set the + // serial of the newly constructed sender-state. This is legal, as + // we know that nobody else will touch nor is responsible for + // deletion of our sender-state. + state->serial = serial; + + DPRINTF(MemCheckerMonitor, + "Forwarded read request: serial = %d, addr = %#llx, " + "size = %d\n", + serial, addr, size); + } else if (is_write) { + MemChecker::Serial serial = memchecker->startWrite(curTick(), + addr, + size, + pkt_data.get()); + + state->serial = serial; + + DPRINTF(MemCheckerMonitor, + "Forwarded write request: serial = %d, addr = %#llx, " + "size = %d\n", + serial, addr, size); + } else { + DPRINTF(MemCheckerMonitor, + "Forwarded non read/write request: addr = %#llx\n", addr); + } + } else if (successful) { + DPRINTF(MemCheckerMonitor, + "Forwarded inhibited request: addr = %#llx\n", addr); + } + + return successful; +} + +bool +MemCheckerMonitor::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 is_read = pkt->isRead() && !pkt->req->isPrefetch(); + bool is_write = pkt->isWrite(); + bool is_failed_LLSC = pkt->isLLSC() && pkt->req->getExtraData() == 0; + unsigned size = pkt->getSize(); + Addr addr = pkt->getAddr(); + std::unique_ptr pkt_data; + MemCheckerMonitorSenderState* received_state = NULL; + + if (is_read) { + // On receipt of a response, only need to allocate pkt_data if this is + // a read. For writes, we have already given the MemChecker the data on + // the request, so it doesn't make sense to allocate on write. + pkt_data.reset(new uint8_t[size]); + memcpy(pkt_data.get(), pkt->getConstPtr(), size); + } + + if (is_read || is_write) { + received_state = + dynamic_cast(pkt->senderState); + + // Restore initial sender state + panic_if(received_state == NULL, + "Monitor got a response without monitor sender state\n"); + + // Restore the state + pkt->senderState = received_state->predecessor; + } + + // Attempt to send the packet + bool successful = slavePort.sendTimingResp(pkt); + + // If packet successfully send, complete transaction in MemChecker + // instance, and delete sender state, otherwise restore state. + if (successful) { + if (is_read) { + DPRINTF(MemCheckerMonitor, + "Received read response: serial = %d, addr = %#llx, " + "size = %d\n", + received_state->serial, addr, size); + + bool result = memchecker->completeRead(received_state->serial, + curTick(), + addr, + size, + pkt_data.get()); + + if (!result) { + warn("%s: read of %#llx @ cycle %d failed:\n%s\n", + name(), + addr, curTick(), + memchecker->getErrorMessage().c_str()); + + panic_if(!warnOnly, "MemChecker violation!"); + } + + delete received_state; + } else if (is_write) { + DPRINTF(MemCheckerMonitor, + "Received write response: serial = %d, addr = %#llx, " + "size = %d\n", + received_state->serial, addr, size); + + if (is_failed_LLSC) { + // The write was not successful, let MemChecker know. + memchecker->abortWrite(received_state->serial, + addr, + size); + } else { + memchecker->completeWrite(received_state->serial, + curTick(), + addr, + size); + } + + delete received_state; + } else { + DPRINTF(MemCheckerMonitor, + "Received non read/write response: addr = %#llx\n", addr); + } + } else if (is_read || is_write) { + // Don't delete anything and let the packet look like we + // did not touch it + pkt->senderState = received_state; + } + + return successful; +} + +void +MemCheckerMonitor::recvTimingSnoopReq(PacketPtr pkt) +{ + slavePort.sendTimingSnoopReq(pkt); +} + +bool +MemCheckerMonitor::recvTimingSnoopResp(PacketPtr pkt) +{ + return masterPort.sendTimingSnoopResp(pkt); +} + +bool +MemCheckerMonitor::isSnooping() const +{ + // check if the connected master port is snooping + return slavePort.isSnooping(); +} + +AddrRangeList +MemCheckerMonitor::getAddrRanges() const +{ + // get the address ranges of the connected slave port + return masterPort.getAddrRanges(); +} + +void +MemCheckerMonitor::recvRetryMaster() +{ + slavePort.sendRetry(); +} + +void +MemCheckerMonitor::recvRetrySlave() +{ + masterPort.sendRetry(); +} + +void +MemCheckerMonitor::recvRangeChange() +{ + slavePort.sendRangeChange(); +} diff --git a/src/mem/mem_checker_monitor.hh b/src/mem/mem_checker_monitor.hh new file mode 100644 index 000000000..15e5b9665 --- /dev/null +++ b/src/mem/mem_checker_monitor.hh @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2012-2014 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 + * Marco Elver + */ + +#ifndef __MEM_MEM_CHECKER_MONITOR_HH__ +#define __MEM_MEM_CHECKER_MONITOR_HH__ + +#include "base/statistics.hh" +#include "mem/mem_checker.hh" +#include "mem/mem_object.hh" +#include "params/MemCheckerMonitor.hh" +#include "sim/system.hh" + +/** + * Implements a MemChecker monitor, to be inserted between two ports. + */ +class MemCheckerMonitor : public MemObject +{ + public: + + /** Parameters of memchecker monitor */ + typedef MemCheckerMonitorParams Params; + const Params* params() const + { return reinterpret_cast(_params); } + + /** + * Constructor based on the Python params + * + * @param params Python parameters + */ + MemCheckerMonitor(Params* params); + + /** Destructor */ + ~MemCheckerMonitor(); + + virtual BaseMasterPort& getMasterPort(const std::string& if_name, + PortID idx = InvalidPortID); + + virtual BaseSlavePort& getSlavePort(const std::string& if_name, + PortID idx = InvalidPortID); + + virtual void init(); + + private: + + struct MemCheckerMonitorSenderState : public Packet::SenderState + { + MemCheckerMonitorSenderState(MemChecker::Serial _serial) + : serial(_serial) + {} + + MemChecker::Serial serial; + }; + + /** + * This is the master port of the communication monitor. All recv + * functions call a function in MemCheckerMonitor, 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, MemCheckerMonitor& _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(); + } + + void recvRetry() + { + mon.recvRetryMaster(); + } + + private: + + MemCheckerMonitor& 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 MemCheckerMonitor, 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, MemCheckerMonitor& _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); + } + + AddrRangeList getAddrRanges() const + { + return mon.getAddrRanges(); + } + + void recvRetry() + { + mon.recvRetrySlave(); + } + + private: + + MemCheckerMonitor& 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); + + AddrRangeList getAddrRanges() const; + + bool isSnooping() const; + + void recvRetryMaster(); + + void recvRetrySlave(); + + void recvRangeChange(); + + bool warnOnly; + + MemChecker *memchecker; +}; + +#endif //__MEM_MEM_CHECKER_MONITOR_HH__ -- 2.30.2