mem-cache: Avoid hiding a virtual method in the dictionary compressor.
[gem5.git] / src / mem / packet.cc
index 55fe13f3c6f0c8ea9deb25d49e3a352959aa7211..551ccd8be69f9f74e7a49b503ce4c5a48215fbb6 100644 (file)
@@ -1,5 +1,18 @@
 /*
+ * Copyright (c) 2011-2019 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 <algorithm>
 #include <cstring>
-#include "base/misc.hh"
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "base/cprintf.hh"
+#include "base/logging.hh"
 #include "base/trace.hh"
-#include "mem/packet.hh"
+#include "mem/packet_access.hh"
 
 // 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" },
+    /* SoftPFExReq */
+    { SET6(IsRead, NeedsWritable, IsInvalidate, IsRequest,
+           IsSWPrefetch, NeedsResponse), SoftPFResp, "SoftPFExReq" },
     /* HardPFReq */
-    { SET4(IsRead, IsRequest, IsHWPrefetch, NeedsResponse),
+    { SET5(IsRead, IsRequest, IsHWPrefetch, NeedsResponse, FromCache),
             HardPFResp, "HardPFReq" },
     /* SoftPFResp */
     { SET4(IsRead, IsResponse, IsSWPrefetch, HasData),
@@ -78,193 +117,366 @@ MemCmd::commandInfo[] =
     /* 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),
+    { SET4(IsRead, IsWrite, IsResponse, HasData),
             InvalidCmd, "SwapResp" },
-    /* NetworkNackError  -- nacked at network layer (not by protocol) */
-    { SET2(IsRequest, IsError), InvalidCmd, "NetworkNackError" },
+    { 0, InvalidCmd, "Deprecated_MessageReq" },
+    { 0, InvalidCmd, "Deprecated_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(IsRequest, IsError), InvalidCmd, "InvalidDestError" },
+    { SET2(IsResponse, IsError), InvalidCmd, "InvalidDestError" },
     /* BadAddressError   -- memory address invalid */
-    { SET2(IsRequest, IsError), InvalidCmd, "BadAddressError" }
+    { 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" }
 };
 
+AddrRange
+Packet::getAddrRange() const
+{
+    return RangeSize(getAddr(), getSize());
+}
 
-/** delete the data pointed to in the data pointer. Ok to call to matter how
- * data was allocted. */
-void
-Packet::deleteData()
+bool
+Packet::trySatisfyFunctional(Printable *obj, Addr addr, bool is_secure, int size,
+                        uint8_t *_data)
 {
-    assert(staticData || dynamicData);
-    if (staticData)
-        return;
-
-    if (arrayData)
-        delete [] data;
-    else
-        delete 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;
 }
 
-/** If there isn't data in the packet, allocate some. */
 void
-Packet::allocate()
+Packet::copyResponderFlags(const PacketPtr pkt)
 {
-    if (data)
-        return;
-    assert(!staticData);
-    dynamicData = true;
-    arrayData = true;
-    data = new uint8_t[getSize()];
+    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);
 }
 
-/** Do the packet modify the same addresses. */
-bool
-Packet::intersect(PacketPtr p)
+void
+Packet::pushSenderState(Packet::SenderState *sender_state)
 {
-    Addr s1 = getAddr();
-    Addr e1 = getAddr() + getSize() - 1;
-    Addr s2 = p->getAddr();
-    Addr e2 = p->getAddr() + p->getSize() - 1;
+    assert(sender_state != NULL);
+    sender_state->predecessor = senderState;
+    senderState = sender_state;
+}
 
-    return !(s1 > e2 || e1 < s2);
+Packet::SenderState *
+Packet::popSenderState()
+{
+    assert(senderState != NULL);
+    SenderState *sender_state = senderState;
+    senderState = sender_state->predecessor;
+    sender_state->predecessor = NULL;
+    return sender_state;
 }
 
-bool
-fixDelayedResponsePacket(PacketPtr func, PacketPtr timing)
+uint64_t
+Packet::getUintX(ByteOrder endian) const
 {
-    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;
+    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());
     }
-    else {
-        //Don't toggle if it isn't a read/write response
-        result = fixPacket(func, timing);
+}
+
+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());
     }
 
-    return result;
+}
+
+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::checkFunctional(Addr addr, int size, uint8_t *data)
+Packet::matchBlockAddr(const Addr addr, const bool is_secure,
+                       const int blk_size) const
 {
-    Addr func_start = getAddr();
-    Addr func_end   = getAddr() + getSize() - 1;
-    Addr val_start  = addr;
-    Addr val_end    = val_start + size - 1;
+    return (getBlockAddr(blk_size) == addr) && (isSecure() == is_secure);
+}
 
-    if (func_start > val_end || val_start > func_end) {
-        // no intersection
-        return false;
-    }
+bool
+Packet::matchBlockAddr(const PacketPtr pkt, const int blk_size) const
+{
+    return matchBlockAddr(pkt->getBlockAddr(blk_size), pkt->isSecure(),
+                          blk_size);
+}
 
-    // offset of functional request into supplied value (could be
-    // negative if partial overlap)
-    int offset = func_start - val_start;
+bool
+Packet::matchAddr(const Addr addr, const bool is_secure) const
+{
+    return (getAddr() == addr) && (isSecure() == is_secure);
+}
 
-    if (isRead()) {
-        if (func_start >= val_start && func_end <= val_end) {
-            allocate();
-            std::memcpy(getPtr<uint8_t>(), data + offset, getSize());
-            makeResponse();
-            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?");
-        }
-    } 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);
-        }
-        // we always want to keep going with a write
-        return false;
-    } else
-        panic("Don't know how to handle command %s\n", cmdString());
+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;
+}
 
-std::ostream &
-operator<<(std::ostream &o, const Packet &p)
+Packet::PrintReqState::
+LabelStackEntry::LabelStackEntry(const std::string &_label,
+                                 std::string *_prefix)
+    : label(_label), prefix(_prefix), labelPrinted(false)
 {
+}
 
-    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.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;
+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());
+}