// internally, and have a sufficiently weak memory
// model, this is probably unnecessary, but at some
// point it must have seemed like we needed it...
- assert((pkt->needsWritable() && !blk->isWritable()) ||
- pkt->req->isCacheMaintenance());
- blk->status &= ~BlkReadable;
+ assert((pkt->needsWritable() &&
+ !blk->isSet(CacheBlk::WritableBit)) ||
+ pkt->req->isCacheMaintenance());
+ blk->clearCoherenceBits(CacheBlk::ReadableBit);
}
// Here we are using forward_time, modelling the latency of
// a miss (outbound) just as forwardLatency, neglecting the
if (blk && blk->isValid() && pkt->isClean() && !pkt->isInvalidate()) {
// The block was marked not readable while there was a pending
// cache maintenance operation, restore its flag.
- blk->status |= BlkReadable;
+ blk->setCoherenceBits(CacheBlk::ReadableBit);
// This was a cache clean operation (without invalidate)
// and we have a copy of the block already. Since there
mshr->promoteReadable();
}
- if (blk && blk->isWritable() && !pkt->req->isCacheInvalidate()) {
+ if (blk && blk->isSet(CacheBlk::WritableBit) &&
+ !pkt->req->isCacheInvalidate()) {
// If at this point the referenced block is writable and the
// response is not a cache invalidate, we promote targets that
// were deferred as we couldn't guarrantee a writable copy
// avoid later read getting stale data while write miss is
// outstanding.. see comment in timingAccess()
if (blk) {
- blk->status &= ~BlkReadable;
+ blk->clearCoherenceBits(CacheBlk::ReadableBit);
}
mshrQueue.markPending(mshr);
schedMemSideSendEvent(clockEdge() + pkt->payloadDelay);
PacketList writebacks;
bool satisfied = access(pkt, blk, lat, writebacks);
- if (pkt->isClean() && blk && blk->isDirty()) {
+ if (pkt->isClean() && blk && blk->isSet(CacheBlk::DirtyBit)) {
// A cache clean opearation is looking for a dirty
// block. If a dirty block is encountered a WriteClean
// will update any copies to the path to the memory
// data we have is dirty if marked as such or if we have an
// in-service MSHR that is pending a modified line
bool have_dirty =
- have_data && (blk->isDirty() ||
+ have_data && (blk->isSet(CacheBlk::DirtyBit) ||
(mshr && mshr->inService && mshr->isPendingModified()));
bool done = have_dirty ||
if (overwrite_mem) {
std::memcpy(blk_data, &overwrite_val, pkt->getSize());
- blk->status |= BlkDirty;
+ blk->setCoherenceBits(CacheBlk::DirtyBit);
}
}
if (mshr) {
// Must be an outstanding upgrade or clean request on a block
// we're about to replace
- assert((!blk->isWritable() && mshr->needsWritable()) ||
- mshr->isCleaning());
+ assert((!blk->isSet(CacheBlk::WritableBit) &&
+ mshr->needsWritable()) || mshr->isCleaning());
return false;
}
}
// can satisfy a following ReadEx anyway since we can rely on the
// Read requestor(s) to have buffered the ReadEx snoop and to
// invalidate their blocks after receiving them.
- // assert(!pkt->needsWritable() || blk->isWritable());
+ // assert(!pkt->needsWritable() || blk->isSet(CacheBlk::WritableBit));
assert(pkt->getOffset(blkSize) + pkt->getSize() <= blkSize);
// Check RMW operations first since both isRead() and
(*(pkt->getAtomicOp()))(blk_data);
// set block status to dirty
- blk->status |= BlkDirty;
+ blk->setCoherenceBits(CacheBlk::DirtyBit);
} else {
cmpAndSwap(blk, pkt);
}
// note that the line may be also be considered writable in
// downstream caches along the path to memory, but always
// Exclusive, and never Modified
- assert(blk->isWritable());
+ assert(blk->isSet(CacheBlk::WritableBit));
// Write or WriteLine at the first cache with block in writable state
if (blk->checkWrite(pkt)) {
pkt->writeDataToBlock(blk->data, blkSize);
// Modified state) even if we are a failed StoreCond so we
// supply data to any snoops that have appended themselves to
// this cache before knowing the store will fail.
- blk->status |= BlkDirty;
+ blk->setCoherenceBits(CacheBlk::DirtyBit);
DPRINTF(CacheVerbose, "%s for %s (write)\n", __func__, pkt->print());
} else if (pkt->isRead()) {
if (pkt->isLLSC()) {
// sanity check
assert(!pkt->hasSharers());
- if (blk->isDirty()) {
+ if (blk->isSet(CacheBlk::DirtyBit)) {
// we were in the Owned state, and a cache above us that
// has the line in Shared state needs to be made aware
// that the data it already has is in fact dirty
pkt->setCacheResponding();
- blk->status &= ~BlkDirty;
+ blk->clearCoherenceBits(CacheBlk::DirtyBit);
}
} else if (pkt->isClean()) {
- blk->status &= ~BlkDirty;
+ blk->clearCoherenceBits(CacheBlk::DirtyBit);
} else {
assert(pkt->isInvalidate());
invalidateBlock(blk);
return false;
}
- blk->status |= BlkReadable;
+ blk->setCoherenceBits(CacheBlk::ReadableBit);
} else if (compressor) {
// This is an overwrite to an existing block, therefore we need
// to check for data expansion (i.e., block was compressed with
// only mark the block dirty if we got a writeback command,
// and leave it as is for a clean writeback
if (pkt->cmd == MemCmd::WritebackDirty) {
- // TODO: the coherent cache can assert(!blk->isDirty());
- blk->status |= BlkDirty;
+ // TODO: the coherent cache can assert that the dirty bit is set
+ blk->setCoherenceBits(CacheBlk::DirtyBit);
}
// if the packet does not have sharers, it is passing
// writable, and we got the writeback in Modified or Exclusive
// state, if not we are in the Owned or Shared state
if (!pkt->hasSharers()) {
- blk->status |= BlkWritable;
+ blk->setCoherenceBits(CacheBlk::WritableBit);
}
// nothing else to do; writeback doesn't expect response
assert(!pkt->needsResponse());
return false;
}
- blk->status |= BlkReadable;
+ blk->setCoherenceBits(CacheBlk::ReadableBit);
}
} else if (compressor) {
// This is an overwrite to an existing block, therefore we need
// write clean operation and the block is already in this
// cache, we need to update the data and the block flags
assert(blk);
- // TODO: the coherent cache can assert(!blk->isDirty());
+ // TODO: the coherent cache can assert that the dirty bit is set
if (!pkt->writeThrough()) {
- blk->status |= BlkDirty;
+ blk->setCoherenceBits(CacheBlk::DirtyBit);
}
// nothing else to do; writeback doesn't expect response
assert(!pkt->needsResponse());
// If this a write-through packet it will be sent to cache below
return !pkt->writeThrough();
- } else if (blk && (pkt->needsWritable() ? blk->isWritable() :
- blk->isReadable())) {
+ } else if (blk && (pkt->needsWritable() ?
+ blk->isSet(CacheBlk::WritableBit) :
+ blk->isSet(CacheBlk::ReadableBit))) {
// OK to satisfy access
incHitCount(pkt);
void
BaseCache::maintainClusivity(bool from_cache, CacheBlk *blk)
{
- if (from_cache && blk && blk->isValid() && !blk->isDirty() &&
- clusivity == Enums::mostly_excl) {
+ if (from_cache && blk && blk->isValid() &&
+ !blk->isSet(CacheBlk::DirtyBit) && clusivity == Enums::mostly_excl) {
// if we have responded to a cache, and our block is still
// valid, but not dirty, and this cache is mostly exclusive
// with respect to the cache above, drop the block
assert(blk->isSecure() == is_secure);
assert(regenerateBlkAddr(blk) == addr);
- blk->status |= BlkReadable;
+ blk->setCoherenceBits(CacheBlk::ReadableBit);
// sanity check for whole-line writes, which should always be
// marked as writable as part of the fill, and then later marked
// we could get a writable line from memory (rather than a
// cache) even in a read-only cache, note that we set this bit
// even for a read-only cache, possibly revisit this decision
- blk->status |= BlkWritable;
+ blk->setCoherenceBits(CacheBlk::WritableBit);
// check if we got this via cache-to-cache transfer (i.e., from a
// cache that had the block in Modified or Owned state)
if (pkt->cacheResponding()) {
// we got the block in Modified state, and invalidated the
// owners copy
- blk->status |= BlkDirty;
+ blk->setCoherenceBits(CacheBlk::DirtyBit);
chatty_assert(!isReadOnly, "Should never see dirty snoop response "
"in read-only cache %s\n", name());
{
chatty_assert(!isReadOnly || writebackClean,
"Writeback from read-only cache");
- assert(blk && blk->isValid() && (blk->isDirty() || writebackClean));
+ assert(blk && blk->isValid() &&
+ (blk->isSet(CacheBlk::DirtyBit) || writebackClean));
stats.writebacks[Request::wbRequestorId]++;
req->taskId(blk->getTaskId());
PacketPtr pkt =
- new Packet(req, blk->isDirty() ?
+ new Packet(req, blk->isSet(CacheBlk::DirtyBit) ?
MemCmd::WritebackDirty : MemCmd::WritebackClean);
DPRINTF(Cache, "Create Writeback %s writable: %d, dirty: %d\n",
- pkt->print(), blk->isWritable(), blk->isDirty());
+ pkt->print(), blk->isSet(CacheBlk::WritableBit),
+ blk->isSet(CacheBlk::DirtyBit));
- if (blk->isWritable()) {
+ if (blk->isSet(CacheBlk::WritableBit)) {
// not asserting shared means we pass the block in modified
// state, mark our own block non-writeable
- blk->status &= ~BlkWritable;
+ blk->clearCoherenceBits(CacheBlk::WritableBit);
} else {
// we are in the Owned state, tell the receiver
pkt->setHasSharers();
}
// make sure the block is not marked dirty
- blk->status &= ~BlkDirty;
+ blk->clearCoherenceBits(CacheBlk::DirtyBit);
pkt->allocate();
pkt->setDataFromBlock(blk->data, blkSize);
}
DPRINTF(Cache, "Create %s writable: %d, dirty: %d\n", pkt->print(),
- blk->isWritable(), blk->isDirty());
+ blk->isSet(CacheBlk::WritableBit), blk->isSet(CacheBlk::DirtyBit));
- if (blk->isWritable()) {
+ if (blk->isSet(CacheBlk::WritableBit)) {
// not asserting shared means we pass the block in modified
// state, mark our own block non-writeable
- blk->status &= ~BlkWritable;
+ blk->clearCoherenceBits(CacheBlk::WritableBit);
} else {
// we are in the Owned state, tell the receiver
pkt->setHasSharers();
}
// make sure the block is not marked dirty
- blk->status &= ~BlkDirty;
+ blk->clearCoherenceBits(CacheBlk::DirtyBit);
pkt->allocate();
pkt->setDataFromBlock(blk->data, blkSize);
bool
BaseCache::isDirty() const
{
- return tags->anyBlk([](CacheBlk &blk) { return blk.isDirty(); });
+ return tags->anyBlk([](CacheBlk &blk) {
+ return blk.isSet(CacheBlk::DirtyBit); });
}
bool
void
BaseCache::writebackVisitor(CacheBlk &blk)
{
- if (blk.isDirty()) {
+ if (blk.isSet(CacheBlk::DirtyBit)) {
assert(blk.isValid());
RequestPtr request = std::make_shared<Request>(
memSidePort.sendFunctional(&packet);
- blk.status &= ~BlkDirty;
+ blk.clearCoherenceBits(CacheBlk::DirtyBit);
}
}
void
BaseCache::invalidateVisitor(CacheBlk &blk)
{
- if (blk.isDirty())
+ if (blk.isSet(CacheBlk::DirtyBit))
warn_once("Invalidating dirty cache lines. " \
"Expect things to break.\n");
if (blk.isValid()) {
- assert(!blk.isDirty());
+ assert(!blk.isSet(CacheBlk::DirtyBit));
invalidateBlock(&blk);
}
}
// as forwarded packets may already have existing state
pkt->pushSenderState(mshr);
- if (pkt->isClean() && blk && blk->isDirty()) {
+ if (pkt->isClean() && blk && blk->isSet(CacheBlk::DirtyBit)) {
// A cache clean opearation is looking for a dirty block. Mark
// the packet so that the destination xbar can determine that
// there will be a follow-up write packet as well.
pkt->cacheResponding();
markInService(mshr, pending_modified_resp);
- if (pkt->isClean() && blk && blk->isDirty()) {
+ if (pkt->isClean() && blk && blk->isSet(CacheBlk::DirtyBit)) {
// A cache clean opearation is looking for a dirty
// block. If a dirty block is encountered a WriteClean
// will update any copies to the path to the memory
overallAvgMshrUncacheableLatency =
overallMshrUncacheableLatency / overallMshrUncacheable;
for (int i = 0; i < max_requestors; i++) {
- overallAvgMshrUncacheableLatency.subname(i, system->getRequestorName(i));
+ overallAvgMshrUncacheableLatency.subname(i,
+ system->getRequestorName(i));
}
dataExpansions.flags(nozero | nonan);
// if we have a dirty copy, make sure the recipient
// keeps it marked dirty (in the modified state)
- if (blk->isDirty()) {
+ if (blk->isSet(CacheBlk::DirtyBit)) {
pkt->setCacheResponding();
- blk->status &= ~BlkDirty;
+ blk->clearCoherenceBits(CacheBlk::DirtyBit);
}
- } else if (blk->isWritable() && !pending_downgrade &&
- !pkt->hasSharers() &&
- pkt->cmd != MemCmd::ReadCleanReq) {
+ } else if (blk->isSet(CacheBlk::WritableBit) &&
+ !pending_downgrade && !pkt->hasSharers() &&
+ pkt->cmd != MemCmd::ReadCleanReq) {
// we can give the requestor a writable copy on a read
// request if:
// - we have a writable copy at this level (& below)
// snooping the packet)
// - the read has explicitly asked for a clean
// copy of the line
- if (blk->isDirty()) {
+ if (blk->isSet(CacheBlk::DirtyBit)) {
// special considerations if we're owner:
if (!deferred_response) {
// respond with the line in Modified state
// the cache hierarchy through a cache,
// and first snoop upwards in all other
// branches
- blk->status &= ~BlkDirty;
+ blk->clearCoherenceBits(CacheBlk::DirtyBit);
} else {
// if we're responding after our own miss,
// there's a window where the recipient didn't
const bool useUpgrades = true;
assert(cpu_pkt->cmd != MemCmd::WriteLineReq || is_whole_line_write);
if (is_whole_line_write) {
- assert(!blkValid || !blk->isWritable());
+ assert(!blkValid || !blk->isSet(CacheBlk::WritableBit));
// forward as invalidate to all other caches, this gives us
// the line in Exclusive state, and invalidates all other
// copies
// only reason to be here is that blk is read only and we need
// it to be writable
assert(needsWritable);
- assert(!blk->isWritable());
+ assert(!blk->isSet(CacheBlk::WritableBit));
cmd = cpu_pkt->isLLSC() ? MemCmd::SCUpgradeReq : MemCmd::UpgradeReq;
} else if (cpu_pkt->cmd == MemCmd::SCUpgradeFailReq ||
cpu_pkt->cmd == MemCmd::StoreCondFailReq) {
// between the PrefetchExReq and the expected WriteReq, we
// proactively mark the block as Dirty.
assert(blk);
- blk->status |= BlkDirty;
+ blk->setCoherenceBits(CacheBlk::DirtyBit);
panic_if(isReadOnly, "Prefetch exclusive requests from "
"read-only cache %s\n", name());
if (tgt_pkt->cmd == MemCmd::WriteLineReq) {
assert(!is_error);
assert(blk);
- assert(blk->isWritable());
+ assert(blk->isSet(CacheBlk::WritableBit));
}
// Here we decide whether we will satisfy the target using
if (is_invalidate || mshr->hasPostInvalidate()) {
invalidateBlock(blk);
} else if (mshr->hasPostDowngrade()) {
- blk->status &= ~BlkWritable;
+ blk->clearCoherenceBits(CacheBlk::WritableBit);
}
}
}
PacketPtr
Cache::evictBlock(CacheBlk *blk)
{
- PacketPtr pkt = (blk->isDirty() || writebackClean) ?
+ PacketPtr pkt = (blk->isSet(CacheBlk::DirtyBit) || writebackClean) ?
writebackBlk(blk) : cleanEvictBlk(blk);
invalidateBlock(blk);
Cache::cleanEvictBlk(CacheBlk *blk)
{
assert(!writebackClean);
- assert(blk && blk->isValid() && !blk->isDirty());
+ assert(blk && blk->isValid() && !blk->isSet(CacheBlk::DirtyBit));
// Creating a zero sized write, a message to the snoop filter
RequestPtr req = std::make_shared<Request>(
bool respond = false;
bool blk_valid = blk && blk->isValid();
if (pkt->isClean()) {
- if (blk_valid && blk->isDirty()) {
+ if (blk_valid && blk->isSet(CacheBlk::DirtyBit)) {
DPRINTF(CacheVerbose, "%s: packet (snoop) %s found block: %s\n",
__func__, pkt->print(), blk->print());
- PacketPtr wb_pkt = writecleanBlk(blk, pkt->req->getDest(), pkt->id);
+ PacketPtr wb_pkt =
+ writecleanBlk(blk, pkt->req->getDest(), pkt->id);
PacketList writebacks;
writebacks.push_back(wb_pkt);
// invalidation itself is taken care of below. We don't respond to
// cache maintenance operations as this is done by the destination
// xbar.
- respond = blk->isDirty() && pkt->needsResponse();
+ respond = blk->isSet(CacheBlk::DirtyBit) && pkt->needsResponse();
- chatty_assert(!(isReadOnly && blk->isDirty()), "Should never have "
- "a dirty block in a read-only cache %s\n", name());
+ chatty_assert(!(isReadOnly && blk->isSet(CacheBlk::DirtyBit)),
+ "Should never have a dirty block in a read-only cache %s\n",
+ name());
}
// Invalidate any prefetch's from below that would strip write permissions
// which means we go from Modified to Owned (and will respond
// below), remain in Owned (and will respond below), from
// Exclusive to Shared, or remain in Shared
- if (!pkt->req->isUncacheable())
- blk->status &= ~BlkWritable;
+ if (!pkt->req->isUncacheable()) {
+ blk->clearCoherenceBits(CacheBlk::WritableBit);
+ }
DPRINTF(Cache, "new state is %s\n", blk->print());
}
// memory, and also prevent any memory from even seeing the
// request
pkt->setCacheResponding();
- if (!pkt->isClean() && blk->isWritable()) {
+ if (!pkt->isClean() && blk->isSet(CacheBlk::WritableBit)) {
// inform the cache hierarchy that this cache had the line
// in the Modified state so that we avoid unnecessary
// invalidations (see Packet::setResponderHadWritable)
#include "mem/request.hh"
#include "sim/core.hh"
-/**
- * Cache block status bit assignments
- */
-enum CacheBlkStatusBits : unsigned {
- /** write permission */
- BlkWritable = 0x02,
- /** read permission (yes, block can be valid but not readable) */
- BlkReadable = 0x04,
- /** dirty (modified) */
- BlkDirty = 0x08,
-};
-
/**
* A Basic Cache block.
* Contains information regarding its coherence, prefetching status, as
class CacheBlk : public TaggedEntry
{
public:
+ /**
+ * Cache block's enum listing the supported coherence bits. The valid
+ * bit is not defined here because it is part of a TaggedEntry.
+ */
+ enum CoherenceBits : unsigned
+ {
+ /** write permission */
+ WritableBit = 0x02,
+ /**
+ * Read permission. Note that a block can be valid but not readable
+ * if there is an outstanding write upgrade miss.
+ */
+ ReadableBit = 0x04,
+ /** dirty (modified) */
+ DirtyBit = 0x08,
+
+ /**
+ * Helper enum value that includes all other bits. Whenever a new
+ * bits is added, this should be updated.
+ */
+ AllBits = 0x0E,
+ };
+
/**
* Contains a copy of the data in this block for easy access. This is used
* for efficient execution when the data could be actually stored in
*/
uint8_t *data;
- /** block state: OR of CacheBlkStatusBit */
- typedef unsigned State;
-
- /** The current status of this block. @sa CacheBlockStatusBits */
- State status;
-
/**
* Which curTick() will this block be accessible. Its value is only
* meaningful if the block is valid.
CacheBlk& operator=(const CacheBlk&) = delete;
virtual ~CacheBlk() {};
- /**
- * Checks the write permissions of this block.
- * @return True if the block is writable.
- */
- bool isWritable() const { return isValid() && (status & BlkWritable); }
-
- /**
- * Checks the read permissions of this block. Note that a block
- * can be valid but not readable if there is an outstanding write
- * upgrade miss.
- * @return True if the block is readable.
- */
- bool isReadable() const { return isValid() && (status & BlkReadable); }
-
/**
* Invalidate the block and clear all state.
*/
virtual void invalidate()
{
TaggedEntry::invalidate();
+
clearPrefetched();
+ clearCoherenceBits(AllBits);
+
setTaskId(ContextSwitchTaskId::Unknown);
- status = 0;
whenReady = MaxTick;
setRefCount(0);
setSrcRequestorId(Request::invldRequestorId);
}
/**
- * Check to see if a block has been written.
- * @return True if the block is dirty.
+ * Sets the corresponding coherence bits.
+ *
+ * @param bits The coherence bits to be set.
*/
- bool isDirty() const
+ void
+ setCoherenceBits(unsigned bits)
{
- return (status & BlkDirty) != 0;
+ assert(isValid());
+ coherence |= bits;
+ }
+
+ /**
+ * Clear the corresponding coherence bits.
+ *
+ * @param bits The coherence bits to be cleared.
+ */
+ void clearCoherenceBits(unsigned bits) { coherence &= ~bits; }
+
+ /**
+ * Checks the given coherence bits are set.
+ *
+ * @return True if the block is readable.
+ */
+ bool
+ isSet(unsigned bits) const
+ {
+ return isValid() && (coherence & bits);
}
/**
*
* Note that only one cache ever has a block in Modified or
* Owned state, i.e., only one cache owns the block, or
- * equivalently has the BlkDirty bit set. However, multiple
+ * equivalently has the DirtyBit bit set. However, multiple
* caches on the same path to memory can have a block in the
* Exclusive state (despite the name). Exclusive means this
* cache has the only copy at this level of the hierarchy,
* this branch of the hierarchy, and no caches at or above
* this level on any other branch have copies either.
**/
- unsigned state = isWritable() << 2 | isDirty() << 1 | isValid();
+ unsigned state =
+ isSet(WritableBit) << 2 | isSet(DirtyBit) << 1 | isValid();
char s = '?';
switch (state) {
case 0b111: s = 'M'; break;
default: s = 'T'; break; // @TODO add other types
}
return csprintf("state: %x (%c) writable: %d readable: %d "
- "dirty: %d | %s", status, s, isWritable(), isReadable(),
- isDirty(), TaggedEntry::print());
+ "dirty: %d | %s", coherence, s, isSet(WritableBit),
+ isSet(ReadableBit), isSet(DirtyBit), TaggedEntry::print());
}
/**
}
protected:
+ /** The current coherence status of this block. @sa CoherenceBits */
+ unsigned coherence;
+
+ // The following setters have been marked as protected because their
+ // respective variables should only be modified at 2 moments:
+ // invalidation and insertion. Because of that, they shall only be
+ // called by the functions that perform those actions.
+
/** Set the task id value. */
void setTaskId(const uint32_t task_id) { _taskId = task_id; }