/*
- * Copyright (c) 2012-2017 ARM Limited
+ * Copyright (c) 2012-2019 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* 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: Ron Dreslinski
- * Steve Reinhardt
- * Ali Saidi
- * Andreas Hansson
- * Nikos Nikoleris
*/
/**
#include <cassert>
#include <list>
+#include "base/addr_range.hh"
#include "base/cast.hh"
#include "base/compiler.hh"
#include "base/flags.hh"
#include "base/logging.hh"
#include "base/printable.hh"
#include "base/types.hh"
+#include "mem/htm.hh"
#include "mem/request.hh"
#include "sim/core.hh"
typedef Packet *PacketPtr;
typedef uint8_t* PacketDataPtr;
typedef std::list<PacketPtr> PacketList;
+typedef uint64_t PacketId;
class MemCmd
{
ReadRespWithInvalidate,
WriteReq,
WriteResp,
+ WriteCompleteResp,
WritebackDirty,
WritebackClean,
WriteClean, // writes dirty data below without evicting
CleanEvict,
SoftPFReq,
+ SoftPFExReq,
HardPFReq,
SoftPFResp,
HardPFResp,
StoreCondResp,
SwapReq,
SwapResp,
- MessageReq,
- MessageResp,
- MemFenceReq,
+ // MessageReq and MessageResp are deprecated.
+ MemFenceReq = SwapResp + 3,
+ MemSyncReq, // memory synchronization request (e.g., cache invalidate)
+ MemSyncResp, // memory synchronization response
MemFenceResp,
CleanSharedReq,
CleanSharedResp,
FlushReq, //request for a cache flush
InvalidateReq, // request for address to be invalidated
InvalidateResp,
+ // hardware transactional memory
+ HTMReq,
+ HTMReqResp,
+ HTMAbort,
NUM_MEM_CMDS
};
/**
* A Packet is used to encapsulate a transfer between two objects in
* the memory system (e.g., the L1 and L2 cache). (In contrast, a
- * single Request travels all the way from the requester to the
+ * single Request travels all the way from the requestor to the
* ultimate destination and back, possibly being conveyed by several
* different Packets along the way.)
*/
enum : FlagsType {
// Flags to transfer across when copying a packet
- COPY_FLAGS = 0x0000003F,
+ COPY_FLAGS = 0x000000FF,
+
+ // Flags that are used to create reponse packets
+ RESPONDER_FLAGS = 0x00000009,
// Does this packet have sharers (which means it should not be
// considered writable) or not. See setHasSharers below.
// operations
SATISFIED = 0x00000020,
+ // hardware transactional memory
+
+ // Indicates that this packet/request has returned from the
+ // cache hierarchy in a failed transaction. The core is
+ // notified like this.
+ FAILS_TRANSACTION = 0x00000040,
+
+ // Indicates that this packet/request originates in the CPU executing
+ // in transactional mode, i.e. in a transaction.
+ FROM_TRANSACTION = 0x00000080,
+
/// Are the 'addr' and 'size' fields valid?
VALID_ADDR = 0x00000100,
VALID_SIZE = 0x00000200,
/// The command field of the packet.
MemCmd cmd;
+ const PacketId id;
+
/// A pointer to the original request.
- const RequestPtr req;
+ RequestPtr req;
private:
/**
- * A pointer to the data being transfered. It can be differnt
- * sizes at each level of the heirarchy so it belongs in the
+ * A pointer to the data being transferred. It can be different
+ * sizes at each level of the hierarchy so it belongs to the
* packet, not request. This may or may not be populated when a
- * responder recieves the packet. If not populated it memory should
+ * responder receives the packet. If not populated memory should
* be allocated.
*/
PacketDataPtr data;
*/
std::vector<bool> bytesValid;
+ // Quality of Service priority value
+ uint8_t _qosValue;
+
+ // hardware transactional memory
+
+ /**
+ * Holds the return status of the transaction.
+ * The default case will be NO_FAIL, otherwise this will specify the
+ * reason for the transaction's failure in the memory subsystem.
+ */
+ HtmCacheFailure htmReturnReason;
+
+ /**
+ * A global unique identifier of the transaction.
+ * This is used for correctness/debugging only.
+ */
+ uint64_t htmTransactionUid;
+
public:
/**
/**
* A virtual base opaque structure used to hold state associated
- * with the packet (e.g., an MSHR), specific to a MemObject that
+ * with the packet (e.g., an MSHR), specific to a SimObject that
* sees the packet. A pointer to this state is returned in the
- * packet's response so that the MemObject in question can quickly
+ * packet's response so that the SimObject in question can quickly
* look up the state needed to process it. A specific subclass
* would be derived from this to carry state specific to a
* particular sending device.
*
- * As multiple MemObjects may add their SenderState throughout the
+ * As multiple SimObjects may add their SenderState throughout the
* memory system, the SenderStates create a stack, where a
- * MemObject can add a new Senderstate, as long as the
+ * SimObject can add a new Senderstate, as long as the
* predecessing SenderState is restored when the response comes
* back. For this reason, the predecessor should always be
* populated with the current SenderState of a packet before
bool isPrint() const { return cmd.isPrint(); }
bool isFlush() const { return cmd.isFlush(); }
+ bool isWholeLineWrite(unsigned blk_size)
+ {
+ return (cmd == MemCmd::WriteReq || cmd == MemCmd::WriteLineReq) &&
+ getOffset(blk_size) == 0 && getSize() == blk_size;
+ }
+
//@{
/// Snoop flags
/**
bool responderHadWritable() const
{ return flags.isSet(RESPONDER_HAD_WRITABLE); }
+ /**
+ * Copy the reponse flags from an input packet to this packet. The
+ * reponse flags determine whether a responder has been found and
+ * the state at which the block will be at the destination.
+ *
+ * @pkt The packet that we will copy flags from
+ */
+ void copyResponderFlags(const PacketPtr pkt);
+
/**
* A writeback/writeclean cmd gets propagated further downstream
* by the receiver when the flag is set.
bool isBlockCached() const { return flags.isSet(BLOCK_CACHED); }
void clearBlockCached() { flags.clear(BLOCK_CACHED); }
+ /**
+ * QoS Value getter
+ * Returns 0 if QoS value was never set (constructor default).
+ *
+ * @return QoS priority value of the packet
+ */
+ inline uint8_t qosValue() const { return _qosValue; }
+
+ /**
+ * QoS Value setter
+ * Interface for setting QoS priority value of the packet.
+ *
+ * @param qos_value QoS priority value
+ */
+ inline void qosValue(const uint8_t qos_value)
+ { _qosValue = qos_value; }
+
+ inline RequestorID requestorId() const { return req->requestorId(); }
+
// Network error conditions... encapsulate them as methods since
// their encoding keeps changing (from result field to command
// field, etc.)
unsigned getSize() const { assert(flags.isSet(VALID_SIZE)); return size; }
+ /**
+ * Get address range to which this packet belongs.
+ *
+ * @return Address range of this packet.
+ */
+ AddrRange getAddrRange() const;
+
Addr getOffset(unsigned int blk_size) const
{
return getAddr() & Addr(blk_size - 1);
* first, but the Requests's physical address and size fields need
* not be valid. The command must be supplied.
*/
- Packet(const RequestPtr _req, MemCmd _cmd)
- : cmd(_cmd), req(_req), data(nullptr), addr(0), _isSecure(false),
- size(0), headerDelay(0), snoopDelay(0), payloadDelay(0),
- senderState(NULL)
+ Packet(const RequestPtr &_req, MemCmd _cmd)
+ : cmd(_cmd), id((PacketId)_req.get()), req(_req),
+ data(nullptr), addr(0), _isSecure(false), size(0),
+ _qosValue(0),
+ htmReturnReason(HtmCacheFailure::NO_FAIL),
+ htmTransactionUid(0),
+ headerDelay(0), snoopDelay(0),
+ payloadDelay(0), senderState(NULL)
{
+ flags.clear();
if (req->hasPaddr()) {
addr = req->getPaddr();
flags.set(VALID_ADDR);
_isSecure = req->isSecure();
}
+
+ /**
+ * hardware transactional memory
+ *
+ * This is a bit of a hack!
+ * Technically the address of a HTM command is set to zero
+ * but is not valid. The reason that we pretend it's valid is
+ * to void the getAddr() function from failing. It would be
+ * cumbersome to add control flow in many places to check if the
+ * packet represents a HTM command before calling getAddr().
+ */
+ if (req->isHTMCmd()) {
+ flags.set(VALID_ADDR);
+ assert(addr == 0x0);
+ }
if (req->hasSize()) {
size = req->getSize();
flags.set(VALID_SIZE);
* a request that is for a whole block, not the address from the
* req. this allows for overriding the size/addr of the req.
*/
- Packet(const RequestPtr _req, MemCmd _cmd, int _blkSize)
- : cmd(_cmd), req(_req), data(nullptr), addr(0), _isSecure(false),
- headerDelay(0), snoopDelay(0), payloadDelay(0),
- senderState(NULL)
+ Packet(const RequestPtr &_req, MemCmd _cmd, int _blkSize, PacketId _id = 0)
+ : cmd(_cmd), id(_id ? _id : (PacketId)_req.get()), req(_req),
+ data(nullptr), addr(0), _isSecure(false),
+ _qosValue(0),
+ htmReturnReason(HtmCacheFailure::NO_FAIL),
+ htmTransactionUid(0),
+ headerDelay(0),
+ snoopDelay(0), payloadDelay(0), senderState(NULL)
{
+ flags.clear();
if (req->hasPaddr()) {
addr = req->getPaddr() & ~(_blkSize - 1);
flags.set(VALID_ADDR);
* packet should allocate its own data.
*/
Packet(const PacketPtr pkt, bool clear_flags, bool alloc_data)
- : cmd(pkt->cmd), req(pkt->req),
+ : cmd(pkt->cmd), id(pkt->id), req(pkt->req),
data(nullptr),
addr(pkt->addr), _isSecure(pkt->_isSecure), size(pkt->size),
bytesValid(pkt->bytesValid),
+ _qosValue(pkt->qosValue()),
+ htmReturnReason(HtmCacheFailure::NO_FAIL),
+ htmTransactionUid(0),
headerDelay(pkt->headerDelay),
snoopDelay(0),
payloadDelay(pkt->payloadDelay),
flags.set(pkt->flags & (VALID_ADDR|VALID_SIZE));
+ if (pkt->isHtmTransactional())
+ setHtmTransactional(pkt->getHtmTransactionUid());
+
+ if (pkt->htmTransactionFailedInCache()) {
+ setHtmTransactionFailedInCache(
+ pkt->getHtmTransactionFailedInCacheRC()
+ );
+ }
+
// should we allocate space for data, or not, the express
// snoops do not need to carry any data as they only serve to
// co-ordinate state changes
* Generate the appropriate read MemCmd based on the Request flags.
*/
static MemCmd
- makeReadCmd(const RequestPtr req)
+ makeReadCmd(const RequestPtr &req)
{
- if (req->isLLSC())
+ if (req->isHTMCmd()) {
+ if (req->isHTMAbort())
+ return MemCmd::HTMAbort;
+ else
+ return MemCmd::HTMReq;
+ } else if (req->isLLSC())
return MemCmd::LoadLockedReq;
+ else if (req->isPrefetchEx())
+ return MemCmd::SoftPFExReq;
else if (req->isPrefetch())
return MemCmd::SoftPFReq;
else
* Generate the appropriate write MemCmd based on the Request flags.
*/
static MemCmd
- makeWriteCmd(const RequestPtr req)
+ makeWriteCmd(const RequestPtr &req)
{
if (req->isLLSC())
return MemCmd::StoreCondReq;
- else if (req->isSwap())
+ else if (req->isSwap() || req->isAtomic())
return MemCmd::SwapReq;
else if (req->isCacheInvalidate()) {
return req->isCacheClean() ? MemCmd::CleanInvalidReq :
* Fine-tune the MemCmd type if it's not a vanilla read or write.
*/
static PacketPtr
- createRead(const RequestPtr req)
+ createRead(const RequestPtr &req)
{
return new Packet(req, makeReadCmd(req));
}
static PacketPtr
- createWrite(const RequestPtr req)
+ createWrite(const RequestPtr &req)
{
return new Packet(req, makeWriteCmd(req));
}
*/
~Packet()
{
- // Delete the request object if this is a request packet which
- // does not need a response, because the requester will not get
- // a chance. If the request packet needs a response then the
- // request will be deleted on receipt of the response
- // packet. We also make sure to never delete the request for
- // express snoops, even for cases when responses are not
- // needed (CleanEvict and Writeback), since the snoop packet
- // re-uses the same request.
- if (req && isRequest() && !needsResponse() &&
- !isExpressSnoop()) {
- delete req;
- }
deleteData();
}
flags.set(VALID_SIZE);
}
+ /**
+ * Check if packet corresponds to a given block-aligned address and
+ * address space.
+ *
+ * @param addr The address to compare against.
+ * @param is_secure Whether addr belongs to the secure address space.
+ * @param blk_size Block size in bytes.
+ * @return Whether packet matches description.
+ */
+ bool matchBlockAddr(const Addr addr, const bool is_secure,
+ const int blk_size) const;
+
+ /**
+ * Check if this packet refers to the same block-aligned address and
+ * address space as another packet.
+ *
+ * @param pkt The packet to compare against.
+ * @param blk_size Block size in bytes.
+ * @return Whether packet matches description.
+ */
+ bool matchBlockAddr(const PacketPtr pkt, const int blk_size) const;
+
+ /**
+ * Check if packet corresponds to a given address and address space.
+ *
+ * @param addr The address to compare against.
+ * @param is_secure Whether addr belongs to the secure address space.
+ * @return Whether packet matches description.
+ */
+ bool matchAddr(const Addr addr, const bool is_secure) const;
+
+ /**
+ * Check if this packet refers to the same address and address space as
+ * another packet.
+ *
+ * @param pkt The packet to compare against.
+ * @return Whether packet matches description.
+ */
+ bool matchAddr(const PacketPtr pkt) const;
public:
/**
getPtr()
{
assert(flags.isSet(STATIC_DATA|DYNAMIC_DATA));
+ assert(!isMaskedWrite());
return (T*)data;
}
template <typename T>
T get(ByteOrder endian) const;
- /**
- * Get the data in the packet byte swapped from guest to host
- * endian.
- */
- template <typename T>
- T get() const;
-
/** Set the value in the data pointer to v as big endian. */
template <typename T>
void setBE(T v);
template <typename T>
void set(T v, ByteOrder endian);
- /** Set the value in the data pointer to v as guest endian. */
- template <typename T>
- void set(T v);
+ /**
+ * Get the data in the packet byte swapped from the specified
+ * endianness and zero-extended to 64 bits.
+ */
+ uint64_t getUintX(ByteOrder endian) const;
+
+ /**
+ * Set the value in the word w after truncating it to the length
+ * of the packet and then byteswapping it to the desired
+ * endianness.
+ */
+ void setUintX(uint64_t w, ByteOrder endian);
/**
* Copy data into the packet from the provided pointer.
// same pointer from source to destination and back
assert(p != getPtr<uint8_t>() || flags.isSet(STATIC_DATA));
- if (p != getPtr<uint8_t>())
+ if (p != getPtr<uint8_t>()) {
// for packet with allocated dynamic data, we copy data from
// one to the other, e.g. a forwarded response to a response
std::memcpy(getPtr<uint8_t>(), p, getSize());
+ }
}
/**
}
/**
- * Copy data from the packet to the provided block pointer, which
- * is aligned to the given block size.
+ * Copy data from the packet to the memory at the provided pointer.
+ * @param p Pointer to which data will be copied.
*/
void
writeData(uint8_t *p) const
{
- std::memcpy(p, getConstPtr<uint8_t>(), getSize());
+ if (!isMaskedWrite()) {
+ std::memcpy(p, getConstPtr<uint8_t>(), getSize());
+ } else {
+ assert(req->getByteEnable().size() == getSize());
+ // Write only the enabled bytes
+ const uint8_t *base = getConstPtr<uint8_t>();
+ for (int i = 0; i < getSize(); i++) {
+ if (req->getByteEnable()[i]) {
+ p[i] = *(base + i);
+ }
+ // Disabled bytes stay untouched
+ }
+ }
}
/**
- * Copy data from the packet to the memory at the provided pointer.
+ * Copy data from the packet to the provided block pointer, which
+ * is aligned to the given block size.
+ * @param blk_data Pointer to block to which data will be copied.
+ * @param blkSize Block size in bytes.
*/
void
writeDataToBlock(uint8_t *blk_data, int blkSize) const
/** @} */
- private: // Private data accessor methods
/** Get the data in the packet without byte swapping. */
template <typename T>
T getRaw() const;
* accordingly.
*/
bool
- checkFunctional(PacketPtr other)
+ trySatisfyFunctional(PacketPtr other)
{
+ if (other->isMaskedWrite()) {
+ // Do not forward data if overlapping with a masked write
+ if (_isSecure == other->isSecure() &&
+ getAddr() <= (other->getAddr() + other->getSize() - 1) &&
+ other->getAddr() <= (getAddr() + getSize() - 1)) {
+ warn("Trying to check against a masked write, skipping."
+ " (addr: 0x%x, other addr: 0x%x)", getAddr(),
+ other->getAddr());
+ }
+ return false;
+ }
// all packets that are carrying a payload should have a valid
// data pointer
- return checkFunctional(other, other->getAddr(), other->isSecure(),
- other->getSize(),
- other->hasData() ?
- other->getPtr<uint8_t>() : NULL);
+ return trySatisfyFunctional(other, other->getAddr(), other->isSecure(),
+ other->getSize(),
+ other->hasData() ?
+ other->getPtr<uint8_t>() : NULL);
}
/**
return cmd == MemCmd::CleanEvict || cmd == MemCmd::WritebackClean;
}
+ bool
+ isMaskedWrite() const
+ {
+ return (cmd == MemCmd::WriteReq && req->isMasked());
+ }
+
/**
* Check a functional request against a memory value represented
* by a base/size pair and an associated data array. If the
* memory value.
*/
bool
- checkFunctional(Printable *obj, Addr base, bool is_secure, int size,
- uint8_t *_data);
+ trySatisfyFunctional(Printable *obj, Addr base, bool is_secure, int size,
+ uint8_t *_data);
/**
* Push label for PrintReq (safe to call unconditionally).
* @return string with the request's type and start<->end addresses
*/
std::string print() const;
+
+ // hardware transactional memory
+
+ /**
+ * Communicates to the core that a packet was processed by the memory
+ * subsystem while running in transactional mode.
+ * It may happen that the transaction has failed at the memory subsystem
+ * and this needs to be communicated to the core somehow.
+ * This function decorates the response packet with flags to indicate
+ * such a situation has occurred.
+ */
+ void makeHtmTransactionalReqResponse(const HtmCacheFailure ret_code);
+
+ /**
+ * Stipulates that this packet/request originates in the CPU executing
+ * in transactional mode, i.e. within a transaction.
+ */
+ void setHtmTransactional(uint64_t val);
+
+ /**
+ * Returns whether or not this packet/request originates in the CPU
+ * executing in transactional mode, i.e. within a transaction.
+ */
+ bool isHtmTransactional() const;
+
+ /**
+ * If a packet/request originates in a CPU executing in transactional
+ * mode, i.e. within a transaction, this function returns the unique ID
+ * of the transaction. This is used for verifying correctness
+ * and debugging.
+ */
+ uint64_t getHtmTransactionUid() const;
+
+ /**
+ * Stipulates that this packet/request has returned from the
+ * cache hierarchy in a failed transaction. The core is
+ * notified like this.
+ */
+ void setHtmTransactionFailedInCache(const HtmCacheFailure ret_code);
+
+ /**
+ * Returns whether or not this packet/request has returned from the
+ * cache hierarchy in a failed transaction. The core is
+ * notified liked this.
+ */
+ bool htmTransactionFailedInCache() const;
+
+ /**
+ * If a packet/request has returned from the cache hierarchy in a
+ * failed transaction, this function returns the failure reason.
+ */
+ HtmCacheFailure getHtmTransactionFailedInCacheRC() const;
};
#endif //__MEM_PACKET_HH