Most create() methods are no longer necessary. This change deletes them, and occasionally moves some code from them into the constructors they call. Change-Id: Icbab29ba280144b892f9b12fac9e29a0839477e5 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/36536 Reviewed-by: Gabe Black <gabe.black@gmail.com> Maintainer: Gabe Black <gabe.black@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
355 lines
12 KiB
C++
355 lines
12 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "mem/mem_checker_monitor.hh"
|
|
|
|
#include <memory>
|
|
|
|
#include "base/logging.hh"
|
|
#include "base/output.hh"
|
|
#include "base/trace.hh"
|
|
#include "debug/MemCheckerMonitor.hh"
|
|
|
|
MemCheckerMonitor::MemCheckerMonitor(const Params ¶ms)
|
|
: SimObject(params),
|
|
memSidePort(name() + "-memSidePort", *this),
|
|
cpuSidePort(name() + "-cpuSidePort", *this),
|
|
warnOnly(params.warn_only),
|
|
memchecker(params.memchecker)
|
|
{}
|
|
|
|
MemCheckerMonitor::~MemCheckerMonitor()
|
|
{}
|
|
|
|
void
|
|
MemCheckerMonitor::init()
|
|
{
|
|
// make sure both sides of the monitor are connected
|
|
if (!cpuSidePort.isConnected() || !memSidePort.isConnected())
|
|
fatal("Communication monitor is not connected on both sides.\n");
|
|
}
|
|
|
|
Port &
|
|
MemCheckerMonitor::getPort(const std::string &if_name, PortID idx)
|
|
{
|
|
if (if_name == "request" || if_name == "mem_side_port") {
|
|
return memSidePort;
|
|
} else if (if_name == "response" || if_name == "cpu_side_port") {
|
|
return cpuSidePort;
|
|
} else {
|
|
return SimObject::getPort(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);
|
|
|
|
memSidePort.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);
|
|
|
|
cpuSidePort.sendFunctionalSnoop(pkt);
|
|
|
|
DPRINTF(MemCheckerMonitor,
|
|
"Received functional snoop: addr = %#llx, size = %d\n",
|
|
addr, size);
|
|
}
|
|
|
|
Tick
|
|
MemCheckerMonitor::recvAtomic(PacketPtr pkt)
|
|
{
|
|
panic("Atomic not supported");
|
|
}
|
|
|
|
Tick
|
|
MemCheckerMonitor::recvAtomicSnoop(PacketPtr pkt)
|
|
{
|
|
panic("Atomic not supported");
|
|
}
|
|
|
|
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->cacheResponding();
|
|
std::unique_ptr<uint8_t[]> 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]);
|
|
pkt->writeData(pkt_data.get());
|
|
}
|
|
|
|
// 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 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
|
|
bool successful = memSidePort.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 memSidePort 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 request marked for cache response: 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<uint8_t[]> 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]);
|
|
pkt->writeData(pkt_data.get());
|
|
}
|
|
|
|
if (is_read || is_write) {
|
|
received_state =
|
|
dynamic_cast<MemCheckerMonitorSenderState*>(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 = cpuSidePort.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)
|
|
{
|
|
cpuSidePort.sendTimingSnoopReq(pkt);
|
|
}
|
|
|
|
bool
|
|
MemCheckerMonitor::recvTimingSnoopResp(PacketPtr pkt)
|
|
{
|
|
return memSidePort.sendTimingSnoopResp(pkt);
|
|
}
|
|
|
|
bool
|
|
MemCheckerMonitor::isSnooping() const
|
|
{
|
|
// check if the connected memSidePort is snooping
|
|
return cpuSidePort.isSnooping();
|
|
}
|
|
|
|
AddrRangeList
|
|
MemCheckerMonitor::getAddrRanges() const
|
|
{
|
|
// get the address ranges of the connected cpuSidePort
|
|
return memSidePort.getAddrRanges();
|
|
}
|
|
|
|
void
|
|
MemCheckerMonitor::recvReqRetry()
|
|
{
|
|
cpuSidePort.sendRetryReq();
|
|
}
|
|
|
|
void
|
|
MemCheckerMonitor::recvRespRetry()
|
|
{
|
|
memSidePort.sendRetryResp();
|
|
}
|
|
|
|
void
|
|
MemCheckerMonitor::recvRangeChange()
|
|
{
|
|
cpuSidePort.sendRangeChange();
|
|
}
|