Daniel Carvalho has uploaded this change for review. (
https://gem5-review.googlesource.com/c/public/gem5/+/13835
Change subject: mem-cache: Fix and use blk->whenReady for latency
calculation
......................................................................
mem-cache: Fix and use blk->whenReady for latency calculation
Add missing blk->whenReady sets by including them on the
access calculation function.
Use new block access latency calculation to determine
when the a block is ready to be accessed after a write
or read-modify-write. This includes the payload delay.
Change-Id: Idc2c9773d0b788c3b3190c6099694173d0be6f32
Signed-off-by: Daniel R. Carvalho <[email protected]>
---
M src/mem/cache/base.cc
M src/mem/cache/base.hh
M src/mem/cache/cache.cc
3 files changed, 99 insertions(+), 47 deletions(-)
diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc
index fdc6eb8..901ebea 100644
--- a/src/mem/cache/base.cc
+++ b/src/mem/cache/base.cc
@@ -360,13 +360,12 @@
doWritebacks(writebacks, forward_time);
}
- // Here we charge the headerDelay that takes into account the latencies
+ // The latency charged is just the value calculated in access(), which
+ // includes the headerDelay. The latter takes into account the
latencies
// of the bus, if the packet comes from it.
- // The latency charged it is just lat that is the value of
lookupLatency
- // modified by access() function, or if not just lookupLatency.
// In case of a hit we are neglecting response latency.
// In case of a miss we are neglecting forward latency.
- Tick request_time = clockEdge(lat) + pkt->headerDelay;
+ Tick request_time = clockEdge(lat);
// Here we reset the timing of the packet.
pkt->headerDelay = pkt->payloadDelay = 0;
// track time of availability of next prefetch, if any
@@ -759,7 +758,8 @@
}
// Calculate read or read-modify-write latency
- lat = calculateAccessLatency(blk, true, overwrite_mem);
+ lat = calculateAccessLatency(blk, true, overwrite_mem,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
}
QueueEntry*
@@ -878,7 +878,8 @@
blk->status |= BlkDirty;
// Calculate read-modify-write latency
- lat = calculateAccessLatency(blk, true, true);
+ lat = calculateAccessLatency(blk, true, true,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
} else {
cmpAndSwap(blk, pkt, lat);
}
@@ -893,7 +894,8 @@
pkt->writeDataToBlock(blk->data, blkSize);
// Calculate write latency
- lat = calculateAccessLatency(blk, false, true);
+ lat = calculateAccessLatency(blk, false, true,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
}
// Always mark the line as dirty (and thus transition to the
// Modified state) even if we are a failed StoreCond so we
@@ -911,7 +913,10 @@
pkt->setDataFromBlock(blk->data, blkSize);
// Calculate read latency
- lat = calculateAccessLatency(blk, true, false);
+ lat = calculateAccessLatency(blk, true, false,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
+
+
} else if (pkt->isUpgrade()) {
// sanity check
assert(!pkt->hasSharers());
@@ -939,41 +944,83 @@
//
/////////////////////////////////////////////////////
Cycles
-BaseCache::calculateAccessLatency(const CacheBlk* blk, const bool is_read,
- const bool is_write) const
+BaseCache::calculateAccessLatency(CacheBlk* blk, const bool is_read,
+ const bool is_write, const Tick tick,
+ const Tick payload_delay) const
{
- Cycles lat(lookupLatency);
+ Cycles lat(0);
if (blk != nullptr) {
- // Check if operation is a read
+ // Time to wait until block is available
+ Cycles ready_lat(0);
+
+ // Time to finish read access
+ Cycles read_lat(0);
+
+ // Time to finish write access
+ Cycles write_lat(0);
+
+ // Check if the block to be accessed is available on the access
tick.
+ // If not, apply the access latency on top of block->whenReady.
+ if (blk->whenReady > tick) {
+ ready_lat = ticksToCycles(blk->whenReady - tick);
+ }
+
+ // Calculate read latency
if (is_read) {
- // First access tags, then data
+ // Check if it is a sequential or parallel access
if (sequentialAccess) {
- lat += dataLatency;
- // Latency is dictated by the slowest of tag and data latencies
+ // In a sequential read, latency is the time to perform a
tag
+ // lookup and then a data entry access
+ read_lat = lookupLatency + dataLatency;
} else {
- lat = std::max(lookupLatency, dataLatency);
+ // In a parallel read, latency is dictated by the slowest
of
+ // tag and data latencies
+ read_lat = std::max(lookupLatency, dataLatency);
}
}
- // Takes into account writes and read-modify-writes
+ // Calculate write latency
if (is_write) {
- // @todo Write latency is not added to the latency because this
- // latency accounts just for the time to service a request. It
- // should, however, be used to update the block's whenReady
tick.
- // A write is always sequential
+ // A write is always sequential, and while the lookup is done
+ // in parallel with the payload arrival, the data can only be
+ // inserted after the payload is present
+ write_lat = std::max(lookupLatency,
ticksToCycles(payload_delay)) +
+ fillLatency;
}
- // Check if the block to be accessed is available. If not, apply
the
- // access latency on top of block->whenReady.
- if (blk->whenReady > curTick()) {
- lat += ticksToCycles(blk->whenReady - curTick());
+ // Calculate final latency, taking into account block availability
+ if (is_read) {
+ // Add the read latency to the final access latency. RMW is a
+ // special case that must wait until both read and write are
+ // done, so we include the write latency in the final latency
+ if (is_write) {
+ write_lat = std::max(read_lat, write_lat);
+ lat = ready_lat + write_lat;
+ } else {
+ lat = ready_lat + read_lat;
+ }
+ } else {
+ // For writes, the latency that delays the critical path is
just
+ // the lookup latency
+ lat = ready_lat + lookupLatency;
+ }
+
+ // If dealing with a write, the off-critical path must be taken
+ // into account for subsequent accesses to the block. The block
+ // will be ready when the write has finished. The write latency,
+ // which includes the payload delay, is added on top of the time
+ // the block is requested to be accessed
+ if (is_write) {
+ blk->whenReady = tick + cyclesToTicks(ready_lat + write_lat);
}
} else {
// In case of a miss, latency is the tag lookup latency. For
parallel
// accesses, however, a read implies a data lookup
if (!sequentialAccess && is_read) {
lat = std::max(lookupLatency, dataLatency);
+ } else {
+ lat = lookupLatency;
}
}
@@ -997,9 +1044,11 @@
// Calculate access latency. This is a dummy value set for early
exits, and
// that will be overridden when appropriate (i.e., when the data is
read or
- // written).
+ // written). The is_write bit should not be set, otherwise whenReady
would
+ // be updated twice.
// @todo Is this safe to be substituted by a zero-value initialization?
- lat = calculateAccessLatency(blk, pkt->isRead(), false);
+ lat = calculateAccessLatency(blk, pkt->isRead(), false,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
DPRINTF(Cache, "%s for %s %s\n", __func__, pkt->print(),
blk ? "hit " + blk->print() : "miss");
@@ -1096,13 +1145,11 @@
pkt->writeDataToBlock(blk->data, blkSize);
// Calculate write latency
- lat = calculateAccessLatency(blk, false, true);
+ lat = calculateAccessLatency(blk, false, true,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
incHitCount(pkt);
- // populate the time when the block will be ready to access.
- blk->whenReady = clockEdge(fillLatency) + pkt->headerDelay +
- pkt->payloadDelay;
return true;
} else if (pkt->cmd == MemCmd::CleanEvict) {
if (blk) {
@@ -1156,14 +1203,11 @@
pkt->writeDataToBlock(blk->data, blkSize);
// Calculate write latency
- lat = calculateAccessLatency(blk, false, true);
+ lat = calculateAccessLatency(blk, false, true,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
DPRINTF(Cache, "%s new state is %s\n", __func__, blk->print());
-
incHitCount(pkt);
- // populate the time when the block will be ready to access.
- blk->whenReady = clockEdge(fillLatency) + pkt->headerDelay +
- pkt->payloadDelay;
// if this a write-through packet it will be sent to cache
// below
return !pkt->writeThrough();
@@ -1183,7 +1227,8 @@
incMissCount(pkt);
// Get miss latency
- lat = calculateAccessLatency(nullptr, pkt->isRead(), false);
+ lat = calculateAccessLatency(nullptr, pkt->isRead(), false,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
if (!blk && pkt->isLLSC() && pkt->isWrite()) {
// complete miss on store conditional... just give up now
@@ -1298,12 +1343,11 @@
pkt->writeDataToBlock(blk->data, blkSize);
- // @todo Use write latency.
- Cycles M5_VAR_USED lat = calculateAccessLatency(blk, false, true);
+ // Calculate write latency and update ready time of block
+ // @todo Use write latency
+ const Cycles M5_VAR_USED lat = calculateAccessLatency(blk, false,
true,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
}
- // We pay for fillLatency here.
- blk->whenReady = clockEdge() + fillLatency * clockPeriod() +
- pkt->payloadDelay;
return blk;
}
diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh
index be479a6..134612b 100644
--- a/src/mem/cache/base.hh
+++ b/src/mem/cache/base.hh
@@ -420,17 +420,24 @@
* Calculate access latency in ticks based on the access type (read,
* write or read-modify-write), and whether using sequential or
parallel
* tag-data access. If both is_read and is_write are set, this is a
RMW op.
+ * When dealing with writes, this functions updates the when ready
time of
+ * the block according to the tick at which the write is finished.
+ * Read-Modify-Writes lock the block for the whole duration of the
+ * operation, therefore its latency includes writing to the block.
* @todo Take into account the LSQ, which would be able to provide the
* desired data faster than having to access it in the tags
- * @todo Take into account lock time for atomic operations
*
* @param blk The cache block that was accessed.
* @param is_read Whether this access is a read.
* @param is_write Whether this access is a write.
- * @return The number of ticks that pass due to a block access.
+ * @param tick The Tick at which the packet arrived (without payload).
+ * @param payload_delay The extra ticks until the payload arrives
+ * (relevant for writes).
+ * @return The number of cycles needed to complete the block access.
*/
- Cycles calculateAccessLatency(const CacheBlk* blk, const bool is_read,
- const bool is_write) const;
+ Cycles calculateAccessLatency(CacheBlk* blk, const bool is_read,
+ const bool is_write, const Tick tick,
+ const Tick payload_delay) const;
/**
* Does all the processing necessary to perform the provided request.
diff --git a/src/mem/cache/cache.cc b/src/mem/cache/cache.cc
index 323992f..1e13b10 100644
--- a/src/mem/cache/cache.cc
+++ b/src/mem/cache/cache.cc
@@ -183,7 +183,8 @@
blk = nullptr;
// An uncacheable block's access latency is aliased as a miss
latency
- lat = calculateAccessLatency(blk, pkt->isRead(), false);
+ lat = calculateAccessLatency(blk, pkt->isRead(), false,
+ clockEdge() + pkt->headerDelay, pkt->payloadDelay);
return false;
}
--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/13835
To unsubscribe, or for help writing mail filters, visit
https://gem5-review.googlesource.com/settings
Gerrit-Project: public/gem5
Gerrit-Branch: master
Gerrit-Change-Id: Idc2c9773d0b788c3b3190c6099694173d0be6f32
Gerrit-Change-Number: 13835
Gerrit-PatchSet: 1
Gerrit-Owner: Daniel Carvalho
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev