blob: 31dc330cab83fbf0d6aa51de77780fa06f5d3949 [file] [log] [blame] [edit]
/*
* Copyright (c) 2011-2019, 2021 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.
*
* Copyright (c) 2006 The Regents of The University of Michigan
* Copyright (c) 2010,2015 Advanced Micro Devices, Inc.
* All rights reserved.
*
* 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.
*/
/**
* @file
* Definition of the Packet Class, a packet is a transaction occuring
* between a single level of the memory heirarchy (ie L1->L2).
*/
#include "mem/packet.hh"
#include <algorithm>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include "base/cprintf.hh"
#include "base/logging.hh"
#include "base/trace.hh"
#include "mem/packet_access.hh"
#include "sim/bufval.hh"
namespace gem5
{
const MemCmd::CommandInfo
MemCmd::commandInfo[] =
{
/* InvalidCmd */
{ {}, InvalidCmd, "InvalidCmd" },
/* ReadReq - Read issued by a non-caching agent such as a CPU or
* device, with no restrictions on alignment. */
{ {IsRead, IsRequest, NeedsResponse}, ReadResp, "ReadReq" },
/* ReadResp */
{ {IsRead, IsResponse, HasData}, InvalidCmd, "ReadResp" },
/* ReadRespWithInvalidate */
{ {IsRead, IsResponse, HasData, IsInvalidate},
InvalidCmd, "ReadRespWithInvalidate" },
/* WriteReq */
{ {IsWrite, NeedsWritable, IsRequest, NeedsResponse, HasData},
WriteResp, "WriteReq" },
/* WriteResp */
{ {IsWrite, IsResponse}, InvalidCmd, "WriteResp" },
/* WriteCompleteResp - The WriteCompleteResp command is needed
* because in the GPU memory model we use a WriteResp to indicate
* that a write has reached the cache controller so we can free
* resources at the coalescer. Later, when the write succesfully
* completes we send a WriteCompleteResp to the CU so its wait
* counters can be updated. Wait counters in the CU is how memory
* dependences are handled in the GPU ISA. */
{ {IsWrite, IsResponse}, InvalidCmd, "WriteCompleteResp" },
/* WritebackDirty */
{ {IsWrite, IsRequest, IsEviction, HasData, FromCache},
InvalidCmd, "WritebackDirty" },
/* WritebackClean - This allows the upstream cache to writeback a
* line to the downstream cache without it being considered
* dirty. */
{ {IsWrite, IsRequest, IsEviction, HasData, FromCache},
InvalidCmd, "WritebackClean" },
/* WriteClean - This allows a cache to write a dirty block to a memory
below without evicting its copy. */
{ {IsWrite, IsRequest, HasData, FromCache}, InvalidCmd, "WriteClean" },
/* CleanEvict */
{ {IsRequest, IsEviction, FromCache}, InvalidCmd, "CleanEvict" },
/* SoftPFReq */
{ {IsRead, IsRequest, IsSWPrefetch, NeedsResponse},
SoftPFResp, "SoftPFReq" },
/* SoftPFExReq */
{ {IsRead, NeedsWritable, IsInvalidate, IsRequest,
IsSWPrefetch, NeedsResponse}, SoftPFResp, "SoftPFExReq" },
/* HardPFReq */
{ {IsRead, IsRequest, IsHWPrefetch, NeedsResponse, FromCache},
HardPFResp, "HardPFReq" },
/* SoftPFResp */
{ {IsRead, IsResponse, IsSWPrefetch, HasData}, InvalidCmd, "SoftPFResp" },
/* HardPFResp */
{ {IsRead, IsResponse, IsHWPrefetch, HasData}, InvalidCmd, "HardPFResp" },
/* WriteLineReq */
{ {IsWrite, NeedsWritable, IsRequest, NeedsResponse, HasData},
WriteResp, "WriteLineReq" },
/* UpgradeReq */
{ {IsInvalidate, NeedsWritable, IsUpgrade, IsRequest, NeedsResponse,
FromCache}, UpgradeResp, "UpgradeReq" },
/* SCUpgradeReq: response could be UpgradeResp or UpgradeFailResp */
{ {IsInvalidate, NeedsWritable, IsUpgrade, IsLlsc,
IsRequest, NeedsResponse, FromCache},
UpgradeResp, "SCUpgradeReq" },
/* UpgradeResp */
{ {IsUpgrade, IsResponse}, InvalidCmd, "UpgradeResp" },
/* SCUpgradeFailReq: generates UpgradeFailResp but still gets the data */
{ {IsRead, NeedsWritable, IsInvalidate,
IsLlsc, IsRequest, NeedsResponse, FromCache},
UpgradeFailResp, "SCUpgradeFailReq" },
/* UpgradeFailResp - Behaves like a ReadExReq, but notifies an SC
* that it has failed, acquires line as Dirty*/
{ {IsRead, IsResponse, HasData}, InvalidCmd, "UpgradeFailResp" },
/* ReadExReq - Read issues by a cache, always cache-line aligned,
* and the response is guaranteed to be writeable (exclusive or
* even modified} */
{ {IsRead, NeedsWritable, IsInvalidate, IsRequest, NeedsResponse,
FromCache}, ReadExResp, "ReadExReq" },
/* ReadExResp - Response matching a read exclusive, as we check
* the need for exclusive also on responses */
{ {IsRead, IsResponse, HasData}, InvalidCmd, "ReadExResp" },
/* ReadCleanReq - Read issued by a cache, always cache-line
* aligned, and the response is guaranteed to not contain dirty data
* (exclusive or shared}.*/
{ {IsRead, IsRequest, NeedsResponse, FromCache},
ReadResp, "ReadCleanReq" },
/* ReadSharedReq - Read issued by a cache, always cache-line
* aligned, response is shared, possibly exclusive, owned or even
* modified. */
{ {IsRead, IsRequest, NeedsResponse, FromCache},
ReadResp, "ReadSharedReq" },
/* LoadLockedReq: note that we use plain ReadResp as response, so that
* we can also use ReadRespWithInvalidate when needed */
{ {IsRead, IsLlsc, IsRequest, NeedsResponse},
ReadResp, "LoadLockedReq" },
/* StoreCondReq */
{ {IsWrite, NeedsWritable, IsLlsc,
IsRequest, NeedsResponse, HasData},
StoreCondResp, "StoreCondReq" },
/* StoreCondFailReq: generates failing StoreCondResp */
{ {IsWrite, NeedsWritable, IsLlsc, IsRequest, NeedsResponse, HasData},
StoreCondResp, "StoreCondFailReq" },
/* StoreCondResp */
{ {IsWrite, IsLlsc, IsResponse},
InvalidCmd, "StoreCondResp" },
/* LockedRMWReadReq */
{ {IsRead, IsLockedRMW, NeedsWritable, IsRequest, NeedsResponse},
LockedRMWReadResp, "LockedRMWReadReq" },
/* LockedRMWReadResp */
{ {IsRead, IsLockedRMW, NeedsWritable, IsResponse, HasData},
InvalidCmd, "LockedRMWReadResp" },
/* LockedRMWWriteReq */
{ {IsWrite, IsLockedRMW, NeedsWritable, IsRequest, NeedsResponse,
HasData}, LockedRMWWriteResp, "LockedRMWWriteReq" },
/* LockedRMWWriteResp */
{ {IsWrite, IsLockedRMW, NeedsWritable, IsResponse},
InvalidCmd, "LockedRMWWriteResp" },
/* SwapReq -- for Swap ldstub type operations */
{ {IsRead, IsWrite, NeedsWritable, IsRequest, HasData, NeedsResponse},
SwapResp, "SwapReq" },
/* SwapResp -- for Swap ldstub type operations */
{ {IsRead, IsWrite, IsResponse, HasData}, InvalidCmd, "SwapResp" },
{ {}, InvalidCmd, "Deprecated_MessageReq" },
{ {}, InvalidCmd, "Deprecated_MessageResp" },
/* MemFenceReq -- for synchronization requests */
{{IsRequest, NeedsResponse}, MemFenceResp, "MemFenceReq"},
/* MemSyncReq */
{{IsRequest, NeedsResponse}, MemSyncResp, "MemSyncReq"},
/* MemSyncResp */
{{IsResponse}, InvalidCmd, "MemSyncResp"},
/* MemFenceResp -- for synchronization responses */
{{IsResponse}, InvalidCmd, "MemFenceResp"},
/* Cache Clean Request -- Update with the latest data all existing
copies of the block down to the point indicated by the
request */
{ {IsRequest, IsClean, NeedsResponse, FromCache},
CleanSharedResp, "CleanSharedReq" },
/* Cache Clean Response - Indicates that all caches up to the
specified point of reference have a up-to-date copy of the
cache block or no copy at all */
{ {IsResponse, IsClean}, InvalidCmd, "CleanSharedResp" },
/* Cache Clean and Invalidate Request -- Invalidate all existing
copies down to the point indicated by the request */
{ {IsRequest, IsInvalidate, IsClean, NeedsResponse, FromCache},
CleanInvalidResp, "CleanInvalidReq" },
/* Cache Clean and Invalidate Respose -- Indicates that no cache
above the specified point holds the block and that the block
was written to a memory below the specified point. */
{ {IsResponse, IsInvalidate, IsClean},
InvalidCmd, "CleanInvalidResp" },
/* InvalidDestError -- packet dest field invalid */
{ {IsResponse, IsError}, InvalidCmd, "InvalidDestError" },
/* BadAddressError -- memory address invalid */
{ {IsResponse, IsError}, InvalidCmd, "BadAddressError" },
/* ReadError -- packet dest unable to fulfill read command */
{ {IsRead, IsResponse, IsError}, InvalidCmd, "ReadError" },
/* WriteError -- packet dest unable to fulfill write command */
{ {IsWrite, IsResponse, IsError}, InvalidCmd, "WriteError" },
/* FunctionalReadError */
{ {IsRead, IsResponse, IsError}, InvalidCmd, "FunctionalReadError" },
/* FunctionalWriteError */
{ {IsWrite, IsResponse, IsError}, InvalidCmd, "FunctionalWriteError" },
/* PrintReq */
{ {IsRequest, IsPrint}, InvalidCmd, "PrintReq" },
/* Flush Request */
{ {IsRequest, IsFlush, NeedsWritable}, InvalidCmd, "FlushReq" },
/* Invalidation Request */
{ {IsInvalidate, IsRequest, NeedsWritable, NeedsResponse, FromCache},
InvalidateResp, "InvalidateReq" },
/* Invalidation Response */
{ {IsInvalidate, IsResponse},
InvalidCmd, "InvalidateResp" },
// hardware transactional memory
{ {IsRead, IsRequest, NeedsResponse}, HTMReqResp, "HTMReq" },
{ {IsRead, IsResponse}, InvalidCmd, "HTMReqResp" },
{ {IsRead, IsRequest}, InvalidCmd, "HTMAbort" },
{ {IsRequest}, InvalidCmd, "TlbiExtSync" },
};
AddrRange
Packet::getAddrRange() const
{
return RangeSize(getAddr(), getSize());
}
bool
Packet::trySatisfyFunctional(Printable *obj, Addr addr, bool is_secure, int size,
uint8_t *_data)
{
const Addr func_start = getAddr();
const Addr func_end = getAddr() + getSize() - 1;
const Addr val_start = addr;
const Addr val_end = val_start + size - 1;
if (is_secure != _isSecure || func_start > val_end ||
val_start > func_end) {
// no intersection
return false;
}
// check print first since it doesn't require data
if (isPrint()) {
assert(!_data);
safe_cast<PrintReqState*>(senderState)->printObj(obj);
return false;
}
// we allow the caller to pass NULL to signify the other packet
// has no data
if (!_data) {
return false;
}
const Addr val_offset = func_start > val_start ?
func_start - val_start : 0;
const Addr func_offset = func_start < val_start ?
val_start - func_start : 0;
const Addr overlap_size = std::min(val_end, func_end)+1 -
std::max(val_start, func_start);
if (isRead()) {
std::memcpy(getPtr<uint8_t>() + func_offset,
_data + val_offset,
overlap_size);
// initialise the tracking of valid bytes if we have not
// used it already
if (bytesValid.empty())
bytesValid.resize(getSize(), false);
// track if we are done filling the functional access
bool all_bytes_valid = true;
int i = 0;
// check up to func_offset
for (; all_bytes_valid && i < func_offset; ++i)
all_bytes_valid &= bytesValid[i];
// update the valid bytes
for (i = func_offset; i < func_offset + overlap_size; ++i)
bytesValid[i] = true;
// check the bit after the update we just made
for (; all_bytes_valid && i < getSize(); ++i)
all_bytes_valid &= bytesValid[i];
return all_bytes_valid;
} else if (isWrite()) {
std::memcpy(_data + val_offset,
getConstPtr<uint8_t>() + func_offset,
overlap_size);
} else {
panic("Don't know how to handle command %s\n", cmdString());
}
// keep going with request by default
return false;
}
void
Packet::copyResponderFlags(const PacketPtr pkt)
{
assert(isRequest());
// If we have already found a responder, no other cache should
// commit to responding
assert(!pkt->cacheResponding() || !cacheResponding());
flags.set(pkt->flags & RESPONDER_FLAGS);
}
void
Packet::pushSenderState(Packet::SenderState *sender_state)
{
assert(sender_state != NULL);
sender_state->predecessor = senderState;
senderState = sender_state;
}
Packet::SenderState *
Packet::popSenderState()
{
assert(senderState != NULL);
SenderState *sender_state = senderState;
senderState = sender_state->predecessor;
sender_state->predecessor = NULL;
return sender_state;
}
uint64_t
Packet::getUintX(ByteOrder endian) const
{
auto [val, success] =
gem5::getUintX(getConstPtr<void>(), getSize(), endian);
panic_if(!success, "%i isn't a supported word size.\n", getSize());
return val;
}
void
Packet::setUintX(uint64_t w, ByteOrder endian)
{
bool success = gem5::setUintX(w, getPtr<void>(), getSize(), endian);
panic_if(!success, "%i isn't a supported word size.\n", getSize());
}
void
Packet::print(std::ostream &o, const int verbosity,
const std::string &prefix) const
{
ccprintf(o, "%s%s [%x:%x]%s%s%s%s%s%s", prefix, cmdString(),
getAddr(), getAddr() + getSize() - 1,
req->isSecure() ? " (s)" : "",
req->isInstFetch() ? " IF" : "",
req->isUncacheable() ? " UC" : "",
isExpressSnoop() ? " ES" : "",
req->isToPOC() ? " PoC" : "",
req->isToPOU() ? " PoU" : "");
}
std::string
Packet::print() const {
std::ostringstream str;
print(str);
return str.str();
}
bool
Packet::matchBlockAddr(const Addr addr, const bool is_secure,
const int blk_size) const
{
return (getBlockAddr(blk_size) == addr) && (isSecure() == is_secure);
}
bool
Packet::matchBlockAddr(const PacketPtr pkt, const int blk_size) const
{
return matchBlockAddr(pkt->getBlockAddr(blk_size), pkt->isSecure(),
blk_size);
}
bool
Packet::matchAddr(const Addr addr, const bool is_secure) const
{
return (getAddr() == addr) && (isSecure() == is_secure);
}
bool
Packet::matchAddr(const PacketPtr pkt) const
{
return matchAddr(pkt->getAddr(), pkt->isSecure());
}
Packet::PrintReqState::PrintReqState(std::ostream &_os, int _verbosity)
: curPrefixPtr(new std::string("")), os(_os), verbosity(_verbosity)
{
labelStack.push_back(LabelStackEntry("", curPrefixPtr));
}
Packet::PrintReqState::~PrintReqState()
{
labelStack.pop_back();
assert(labelStack.empty());
delete curPrefixPtr;
}
Packet::PrintReqState::
LabelStackEntry::LabelStackEntry(const std::string &_label,
std::string *_prefix)
: label(_label), prefix(_prefix), labelPrinted(false)
{
}
void
Packet::PrintReqState::pushLabel(const std::string &lbl,
const std::string &prefix)
{
labelStack.push_back(LabelStackEntry(lbl, curPrefixPtr));
curPrefixPtr = new std::string(*curPrefixPtr);
*curPrefixPtr += prefix;
}
void
Packet::PrintReqState::popLabel()
{
delete curPrefixPtr;
curPrefixPtr = labelStack.back().prefix;
labelStack.pop_back();
assert(!labelStack.empty());
}
void
Packet::PrintReqState::printLabels()
{
if (!labelStack.back().labelPrinted) {
LabelStack::iterator i = labelStack.begin();
LabelStack::iterator end = labelStack.end();
while (i != end) {
if (!i->labelPrinted) {
ccprintf(os, "%s%s\n", *(i->prefix), i->label);
i->labelPrinted = true;
}
i++;
}
}
}
void
Packet::PrintReqState::printObj(Printable *obj)
{
printLabels();
obj->print(os, verbosity, curPrefix());
}
void
Packet::makeHtmTransactionalReqResponse(
const HtmCacheFailure htm_return_code)
{
assert(needsResponse());
assert(isRequest());
cmd = cmd.responseCommand();
setHtmTransactionFailedInCache(htm_return_code);
// responses are never express, even if the snoop that
// triggered them was
flags.clear(EXPRESS_SNOOP);
}
void
Packet::setHtmTransactionFailedInCache(
const HtmCacheFailure htm_return_code)
{
if (htm_return_code != HtmCacheFailure::NO_FAIL)
flags.set(FAILS_TRANSACTION);
htmReturnReason = htm_return_code;
}
bool
Packet::htmTransactionFailedInCache() const
{
return flags.isSet(FAILS_TRANSACTION);
}
HtmCacheFailure
Packet::getHtmTransactionFailedInCacheRC() const
{
assert(htmTransactionFailedInCache());
return htmReturnReason;
}
void
Packet::setHtmTransactional(uint64_t htm_uid)
{
flags.set(FROM_TRANSACTION);
htmTransactionUid = htm_uid;
}
bool
Packet::isHtmTransactional() const
{
return flags.isSet(FROM_TRANSACTION);
}
uint64_t
Packet::getHtmTransactionUid() const
{
assert(flags.isSet(FROM_TRANSACTION));
return htmTransactionUid;
}
} // namespace gem5