/*
+ * Copyright (c) 2011-2018 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
* between a single level of the memory heirarchy (ie L1->L2).
*/
-#include <iostream>
+#include "mem/packet.hh"
+
#include <cstring>
-#include "base/misc.hh"
+#include <iostream>
+
+#include "base/cprintf.hh"
+#include "base/logging.hh"
#include "base/trace.hh"
-#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+
+using namespace std;
// The one downside to bitsets is that static initializers can get ugly.
#define SET1(a1) (1 << (a1))
#define SET4(a1, a2, a3, a4) (SET3(a1, a2, a3) | SET1(a4))
#define SET5(a1, a2, a3, a4, a5) (SET4(a1, a2, a3, a4) | SET1(a5))
#define SET6(a1, a2, a3, a4, a5, a6) (SET5(a1, a2, a3, a4, a5) | SET1(a6))
+#define SET7(a1, a2, a3, a4, a5, a6, a7) (SET6(a1, a2, a3, a4, a5, a6) | \
+ SET1(a7))
const MemCmd::CommandInfo
MemCmd::commandInfo[] =
{
/* InvalidCmd */
{ 0, InvalidCmd, "InvalidCmd" },
- /* ReadReq */
+ /* ReadReq - Read issued by a non-caching agent such as a CPU or
+ * device, with no restrictions on alignment. */
{ SET3(IsRead, IsRequest, NeedsResponse), ReadResp, "ReadReq" },
/* ReadResp */
{ SET3(IsRead, IsResponse, HasData), InvalidCmd, "ReadResp" },
+ /* ReadRespWithInvalidate */
+ { SET4(IsRead, IsResponse, HasData, IsInvalidate),
+ InvalidCmd, "ReadRespWithInvalidate" },
/* WriteReq */
- { SET5(IsWrite, NeedsExclusive, IsRequest, NeedsResponse, HasData),
+ { SET5(IsWrite, NeedsWritable, IsRequest, NeedsResponse, HasData),
WriteResp, "WriteReq" },
/* WriteResp */
- { SET3(IsWrite, NeedsExclusive, IsResponse), InvalidCmd, "WriteResp" },
- /* Writeback */
- { SET4(IsWrite, NeedsExclusive, IsRequest, HasData),
- InvalidCmd, "Writeback" },
+ { SET2(IsWrite, IsResponse), InvalidCmd, "WriteResp" },
+ /* WritebackDirty */
+ { SET5(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. */
+ { SET5(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. */
+ { SET4(IsWrite, IsRequest, HasData, FromCache), InvalidCmd, "WriteClean" },
+ /* CleanEvict */
+ { SET3(IsRequest, IsEviction, FromCache), InvalidCmd, "CleanEvict" },
/* SoftPFReq */
{ SET4(IsRead, IsRequest, IsSWPrefetch, NeedsResponse),
SoftPFResp, "SoftPFReq" },
/* HardPFReq */
- { SET4(IsRead, IsRequest, IsHWPrefetch, NeedsResponse),
+ { SET5(IsRead, IsRequest, IsHWPrefetch, NeedsResponse, FromCache),
HardPFResp, "HardPFReq" },
/* SoftPFResp */
{ SET4(IsRead, IsResponse, IsSWPrefetch, HasData),
/* HardPFResp */
{ SET4(IsRead, IsResponse, IsHWPrefetch, HasData),
InvalidCmd, "HardPFResp" },
- /* WriteInvalidateReq */
- { SET6(IsWrite, NeedsExclusive, IsInvalidate,
- IsRequest, HasData, NeedsResponse),
- WriteInvalidateResp, "WriteInvalidateReq" },
- /* WriteInvalidateResp */
- { SET4(IsWrite, NeedsExclusive, IsInvalidate, IsResponse),
- InvalidCmd, "WriteInvalidateResp" },
+ /* WriteLineReq */
+ { SET5(IsWrite, NeedsWritable, IsRequest, NeedsResponse, HasData),
+ WriteResp, "WriteLineReq" },
/* UpgradeReq */
- { SET4(IsInvalidate, NeedsExclusive, IsRequest, NeedsResponse),
+ { SET6(IsInvalidate, NeedsWritable, IsUpgrade, IsRequest, NeedsResponse,
+ FromCache),
UpgradeResp, "UpgradeReq" },
+ /* SCUpgradeReq: response could be UpgradeResp or UpgradeFailResp */
+ { SET7(IsInvalidate, NeedsWritable, IsUpgrade, IsLlsc,
+ IsRequest, NeedsResponse, FromCache),
+ UpgradeResp, "SCUpgradeReq" },
/* UpgradeResp */
- { SET3(IsInvalidate, NeedsExclusive, IsResponse),
+ { SET2(IsUpgrade, IsResponse),
InvalidCmd, "UpgradeResp" },
- /* ReadExReq */
- { SET5(IsRead, NeedsExclusive, IsInvalidate, IsRequest, NeedsResponse),
+ /* SCUpgradeFailReq: generates UpgradeFailResp but still gets the data */
+ { SET7(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*/
+ { SET3(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) */
+ { SET6(IsRead, NeedsWritable, IsInvalidate, IsRequest, NeedsResponse,
+ FromCache),
ReadExResp, "ReadExReq" },
- /* ReadExResp */
- { SET5(IsRead, NeedsExclusive, IsInvalidate, IsResponse, HasData),
+ /* ReadExResp - Response matching a read exclusive, as we check
+ * the need for exclusive also on responses */
+ { SET3(IsRead, IsResponse, HasData),
InvalidCmd, "ReadExResp" },
- /* LoadLockedReq */
- { SET4(IsRead, IsLocked, IsRequest, NeedsResponse),
+ /* ReadCleanReq - Read issued by a cache, always cache-line
+ * aligned, and the response is guaranteed to not contain dirty data
+ * (exclusive or shared).*/
+ { SET4(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. */
+ { SET4(IsRead, IsRequest, NeedsResponse, FromCache),
+ ReadResp, "ReadSharedReq" },
+ /* LoadLockedReq: note that we use plain ReadResp as response, so that
+ * we can also use ReadRespWithInvalidate when needed */
+ { SET4(IsRead, IsLlsc, IsRequest, NeedsResponse),
ReadResp, "LoadLockedReq" },
- /* LoadLockedResp */
- { SET4(IsRead, IsLocked, IsResponse, HasData),
- InvalidCmd, "LoadLockedResp" },
/* StoreCondReq */
- { SET6(IsWrite, NeedsExclusive, IsLocked,
+ { SET6(IsWrite, NeedsWritable, IsLlsc,
IsRequest, NeedsResponse, HasData),
StoreCondResp, "StoreCondReq" },
+ /* StoreCondFailReq: generates failing StoreCondResp */
+ { SET6(IsWrite, NeedsWritable, IsLlsc,
+ IsRequest, NeedsResponse, HasData),
+ StoreCondResp, "StoreCondFailReq" },
/* StoreCondResp */
- { SET4(IsWrite, NeedsExclusive, IsLocked, IsResponse),
+ { SET3(IsWrite, IsLlsc, IsResponse),
InvalidCmd, "StoreCondResp" },
/* SwapReq -- for Swap ldstub type operations */
- { SET6(IsRead, IsWrite, NeedsExclusive, IsRequest, HasData, NeedsResponse),
+ { SET6(IsRead, IsWrite, NeedsWritable, IsRequest, HasData, NeedsResponse),
SwapResp, "SwapReq" },
/* SwapResp -- for Swap ldstub type operations */
- { SET5(IsRead, IsWrite, NeedsExclusive, IsResponse, HasData),
- InvalidCmd, "SwapResp" }
+ { SET4(IsRead, IsWrite, IsResponse, HasData),
+ InvalidCmd, "SwapResp" },
+ /* IntReq -- for interrupts */
+ { SET4(IsWrite, IsRequest, NeedsResponse, HasData),
+ MessageResp, "MessageReq" },
+ /* IntResp -- for interrupts */
+ { SET2(IsWrite, IsResponse), InvalidCmd, "MessageResp" },
+ /* MemFenceReq -- for synchronization requests */
+ {SET2(IsRequest, NeedsResponse), MemFenceResp, "MemFenceReq"},
+ /* MemFenceResp -- for synchronization responses */
+ {SET1(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 */
+ { SET4(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 */
+ { SET2(IsResponse, IsClean), InvalidCmd, "CleanSharedResp" },
+ /* Cache Clean and Invalidate Request -- Invalidate all existing
+ copies down to the point indicated by the request */
+ { SET5(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. */
+ { SET3(IsResponse, IsInvalidate, IsClean),
+ InvalidCmd, "CleanInvalidResp" },
+ /* InvalidDestError -- packet dest field invalid */
+ { SET2(IsResponse, IsError), InvalidCmd, "InvalidDestError" },
+ /* BadAddressError -- memory address invalid */
+ { SET2(IsResponse, IsError), InvalidCmd, "BadAddressError" },
+ /* FunctionalReadError */
+ { SET3(IsRead, IsResponse, IsError), InvalidCmd, "FunctionalReadError" },
+ /* FunctionalWriteError */
+ { SET3(IsWrite, IsResponse, IsError), InvalidCmd, "FunctionalWriteError" },
+ /* PrintReq */
+ { SET2(IsRequest, IsPrint), InvalidCmd, "PrintReq" },
+ /* Flush Request */
+ { SET3(IsRequest, IsFlush, NeedsWritable), InvalidCmd, "FlushReq" },
+ /* Invalidation Request */
+ { SET5(IsInvalidate, IsRequest, NeedsWritable, NeedsResponse, FromCache),
+ InvalidateResp, "InvalidateReq" },
+ /* Invalidation Response */
+ { SET2(IsInvalidate, IsResponse),
+ InvalidCmd, "InvalidateResp" }
};
-
-/** delete the data pointed to in the data pointer. Ok to call to matter how
- * data was allocted. */
-void
-Packet::deleteData()
-{
- assert(staticData || dynamicData);
- if (staticData)
- return;
-
- if (arrayData)
- delete [] data;
- else
- delete data;
-}
-
-/** If there isn't data in the packet, allocate some. */
-void
-Packet::allocate()
-{
- if (data)
- return;
- assert(!staticData);
- dynamicData = true;
- arrayData = true;
- data = new uint8_t[getSize()];
-}
-
-/** Do the packet modify the same addresses. */
-bool
-Packet::intersect(PacketPtr p)
-{
- Addr s1 = getAddr();
- Addr e1 = getAddr() + getSize() - 1;
- Addr s2 = p->getAddr();
- Addr e2 = p->getAddr() + p->getSize() - 1;
-
- return !(s1 > e2 || e1 < s2);
-}
-
bool
-fixDelayedResponsePacket(PacketPtr func, PacketPtr timing)
-{
- bool result;
-
- if (timing->isRead() || timing->isWrite()) {
- // Ugly hack to deal with the fact that we queue the requests
- // and don't convert them to responses until we issue them on
- // the bus. I tried to avoid this by converting packets to
- // responses right away, but this breaks during snoops where a
- // responder may do the conversion before other caches have
- // done the snoop. Would work if we copied the packet instead
- // of just hanging on to a pointer.
- MemCmd oldCmd = timing->cmd;
- timing->cmd = timing->cmd.responseCommand();
- result = fixPacket(func, timing);
- timing->cmd = oldCmd;
- }
- else {
- //Don't toggle if it isn't a read/write response
- result = fixPacket(func, timing);
- }
-
- return result;
-}
-
-bool
-Packet::checkFunctional(Addr addr, int size, uint8_t *data)
+Packet::checkFunctional(Printable *obj, Addr addr, bool is_secure, int size,
+ uint8_t *_data)
{
Addr func_start = getAddr();
Addr func_end = getAddr() + getSize() - 1;
Addr val_start = addr;
Addr val_end = val_start + size - 1;
- if (func_start > val_end || val_start > func_end) {
+ 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;
+ }
+
// offset of functional request into supplied value (could be
// negative if partial overlap)
int offset = func_start - val_start;
if (isRead()) {
if (func_start >= val_start && func_end <= val_end) {
- allocate();
- std::memcpy(getPtr<uint8_t>(), data + offset, getSize());
- result = Packet::Success;
+ memcpy(getPtr<uint8_t>(), _data + offset, getSize());
+ if (bytesValid.empty())
+ bytesValid.resize(getSize(), true);
+ // complete overlap, and as the current packet is a read
+ // we are done
return true;
} else {
- // In this case the timing packet only partially satisfies
- // the request, so we would need more information to make
- // this work. Like bytes valid in the packet or
- // something, so the request could continue and get this
- // bit of possibly newer data along with the older data
- // not written to yet.
- panic("Memory value only partially satisfies the functional "
- "request. Now what?");
+ // Offsets and sizes to copy in case of partial overlap
+ int func_offset;
+ int val_offset;
+ int overlap_size;
+
+ // calculate offsets and copy sizes for the two byte arrays
+ if (val_start < func_start && val_end <= func_end) {
+ // the one we are checking against starts before and
+ // ends before or the same
+ val_offset = func_start - val_start;
+ func_offset = 0;
+ overlap_size = val_end - func_start;
+ } else if (val_start >= func_start && val_end > func_end) {
+ // the one we are checking against starts after or the
+ // same, and ends after
+ val_offset = 0;
+ func_offset = val_start - func_start;
+ overlap_size = func_end - val_start;
+ } else if (val_start >= func_start && val_end <= func_end) {
+ // the one we are checking against is completely
+ // subsumed in the current packet, possibly starting
+ // and ending at the same address
+ val_offset = 0;
+ func_offset = val_start - func_start;
+ overlap_size = size;
+ } else if (val_start < func_start && val_end > func_end) {
+ // the current packet is completely subsumed in the
+ // one we are checking against
+ val_offset = func_start - val_start;
+ func_offset = 0;
+ overlap_size = func_end - func_start;
+ } else {
+ panic("Missed a case for checkFunctional with "
+ " %s 0x%x size %d, against 0x%x size %d\n",
+ cmdString(), getAddr(), getSize(), addr, size);
+ }
+
+ // copy partial data into the packet's data array
+ uint8_t *dest = getPtr<uint8_t>() + func_offset;
+ uint8_t *src = _data + val_offset;
+ memcpy(dest, src, 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()) {
if (offset >= 0) {
- std::memcpy(data + offset, getPtr<uint8_t>(),
- (std::min(func_end, val_end) - func_start) + 1);
- } else { // val_start > func_start
- std::memcpy(data, getPtr<uint8_t>() - offset,
- (std::min(func_end, val_end) - val_start) + 1);
+ memcpy(_data + offset, getConstPtr<uint8_t>(),
+ (min(func_end, val_end) - func_start) + 1);
+ } else {
+ // val_start > func_start
+ memcpy(_data, getConstPtr<uint8_t>() - offset,
+ (min(func_end, val_end) - val_start) + 1);
}
- // we always want to keep going with a write
- return false;
- } else
+ } else {
panic("Don't know how to handle command %s\n", cmdString());
+ }
+
+ // keep going with request by default
+ return false;
+}
+
+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
+{
+ switch(getSize()) {
+ case 1:
+ return (uint64_t)get<uint8_t>(endian);
+ case 2:
+ return (uint64_t)get<uint16_t>(endian);
+ case 4:
+ return (uint64_t)get<uint32_t>(endian);
+ case 8:
+ return (uint64_t)get<uint64_t>(endian);
+ default:
+ panic("%i isn't a supported word size.\n", getSize());
+ }
}
+void
+Packet::setUintX(uint64_t w, ByteOrder endian)
+{
+ switch(getSize()) {
+ case 1:
+ set<uint8_t>((uint8_t)w, endian);
+ break;
+ case 2:
+ set<uint16_t>((uint16_t)w, endian);
+ break;
+ case 4:
+ set<uint32_t>((uint32_t)w, endian);
+ break;
+ case 8:
+ set<uint64_t>((uint64_t)w, endian);
+ break;
+ default:
+ panic("%i isn't a supported word size.\n", getSize());
+ }
+
+}
-std::ostream &
-operator<<(std::ostream &o, const Packet &p)
+void
+Packet::print(ostream &o, const int verbosity, const 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 {
+ ostringstream str;
+ print(str);
+ return str.str();
+}
- o << "[0x";
- o.setf(std::ios_base::hex, std::ios_base::showbase);
- o << p.getAddr();
- o.unsetf(std::ios_base::hex| std::ios_base::showbase);
- o << ":";
- o.setf(std::ios_base::hex, std::ios_base::showbase);
- o << p.getAddr() + p.getSize() - 1 << "] ";
- o.unsetf(std::ios_base::hex| std::ios_base::showbase);
-
- if (p.result == Packet::Success)
- o << "Successful ";
- if (p.result == Packet::BadAddress)
- o << "BadAddress ";
- if (p.result == Packet::Nacked)
- o << "Nacked ";
- if (p.result == Packet::Unknown)
- o << "Inflight ";
-
- if (p.isRead())
- o << "Read ";
- if (p.isWrite())
- o << "Write ";
- if (p.isInvalidate())
- o << "Invalidate ";
- if (p.isRequest())
- o << "Request ";
- if (p.isResponse())
- o << "Response ";
- if (p.hasData())
- o << "w/Data ";
-
- o << std::endl;
- return o;
+Packet::PrintReqState::PrintReqState(ostream &_os, int _verbosity)
+ : curPrefixPtr(new 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 string &_label, string *_prefix)
+ : label(_label), prefix(_prefix), labelPrinted(false)
+{
+}
+
+void
+Packet::PrintReqState::pushLabel(const string &lbl, const string &prefix)
+{
+ labelStack.push_back(LabelStackEntry(lbl, curPrefixPtr));
+ curPrefixPtr = new 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());
+}