Previously the prefetching bit was among the status bits; yet, it has no correlation with the other bits. It has been isolated as a single boolean, with a respective getter and setter. Change-Id: Ibe76e1196ca17a7c9ab9bda2216186707427cb64 Signed-off-by: Daniel R. Carvalho <odanrc@yahoo.com.br> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/35699 Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com> Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com> Tested-by: kokoro <noreply+kokoro@google.com>
359 lines
12 KiB
C++
359 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2010-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) 2002-2005 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
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Cache definitions.
|
|
*/
|
|
|
|
#include "mem/cache/noncoherent_cache.hh"
|
|
|
|
#include <cassert>
|
|
|
|
#include "base/logging.hh"
|
|
#include "base/trace.hh"
|
|
#include "base/types.hh"
|
|
#include "debug/Cache.hh"
|
|
#include "mem/cache/cache_blk.hh"
|
|
#include "mem/cache/mshr.hh"
|
|
#include "params/NoncoherentCache.hh"
|
|
|
|
NoncoherentCache::NoncoherentCache(const NoncoherentCacheParams *p)
|
|
: BaseCache(p, p->system->cacheLineSize())
|
|
{
|
|
}
|
|
|
|
void
|
|
NoncoherentCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool)
|
|
{
|
|
// As this a non-coherent cache located below the point of
|
|
// coherency, we do not expect requests that are typically used to
|
|
// keep caches coherent (e.g., InvalidateReq or UpdateReq).
|
|
assert(pkt->isRead() || pkt->isWrite());
|
|
BaseCache::satisfyRequest(pkt, blk);
|
|
}
|
|
|
|
bool
|
|
NoncoherentCache::access(PacketPtr pkt, CacheBlk *&blk, Cycles &lat,
|
|
PacketList &writebacks)
|
|
{
|
|
bool success = BaseCache::access(pkt, blk, lat, writebacks);
|
|
|
|
if (pkt->isWriteback() || pkt->cmd == MemCmd::WriteClean) {
|
|
assert(blk && blk->isValid());
|
|
// Writeback and WriteClean can allocate and fill even if the
|
|
// referenced block was not present or it was invalid. If that
|
|
// is the case, make sure that the new block is marked as
|
|
// writable
|
|
blk->status |= BlkWritable;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void
|
|
NoncoherentCache::doWritebacks(PacketList& writebacks, Tick forward_time)
|
|
{
|
|
while (!writebacks.empty()) {
|
|
PacketPtr wb_pkt = writebacks.front();
|
|
allocateWriteBuffer(wb_pkt, forward_time);
|
|
writebacks.pop_front();
|
|
}
|
|
}
|
|
|
|
void
|
|
NoncoherentCache::doWritebacksAtomic(PacketList& writebacks)
|
|
{
|
|
while (!writebacks.empty()) {
|
|
PacketPtr wb_pkt = writebacks.front();
|
|
memSidePort.sendAtomic(wb_pkt);
|
|
writebacks.pop_front();
|
|
delete wb_pkt;
|
|
}
|
|
}
|
|
|
|
void
|
|
NoncoherentCache::handleTimingReqMiss(PacketPtr pkt, CacheBlk *blk,
|
|
Tick forward_time, Tick request_time)
|
|
{
|
|
// miss
|
|
Addr blk_addr = pkt->getBlockAddr(blkSize);
|
|
MSHR *mshr = mshrQueue.findMatch(blk_addr, pkt->isSecure(), false);
|
|
|
|
// We can always write to a non coherent cache if the block is
|
|
// present and therefore if we have reached this point then the
|
|
// block should not be in the cache.
|
|
assert(mshr || !blk || !blk->isValid());
|
|
|
|
BaseCache::handleTimingReqMiss(pkt, mshr, blk, forward_time, request_time);
|
|
}
|
|
|
|
void
|
|
NoncoherentCache::recvTimingReq(PacketPtr pkt)
|
|
{
|
|
panic_if(pkt->cacheResponding(), "Should not see packets where cache "
|
|
"is responding");
|
|
|
|
panic_if(!(pkt->isRead() || pkt->isWrite()),
|
|
"Should only see read and writes at non-coherent cache\n");
|
|
|
|
BaseCache::recvTimingReq(pkt);
|
|
}
|
|
|
|
PacketPtr
|
|
NoncoherentCache::createMissPacket(PacketPtr cpu_pkt, CacheBlk *blk,
|
|
bool needs_writable,
|
|
bool is_whole_line_write) const
|
|
{
|
|
// We also fill for writebacks from the coherent caches above us,
|
|
// and they do not need responses
|
|
assert(cpu_pkt->needsResponse());
|
|
|
|
// A miss can happen only due to missing block
|
|
assert(!blk || !blk->isValid());
|
|
|
|
PacketPtr pkt = new Packet(cpu_pkt->req, MemCmd::ReadReq, blkSize);
|
|
|
|
// the packet should be block aligned
|
|
assert(pkt->getAddr() == pkt->getBlockAddr(blkSize));
|
|
|
|
pkt->allocate();
|
|
DPRINTF(Cache, "%s created %s from %s\n", __func__, pkt->print(),
|
|
cpu_pkt->print());
|
|
return pkt;
|
|
}
|
|
|
|
|
|
Cycles
|
|
NoncoherentCache::handleAtomicReqMiss(PacketPtr pkt, CacheBlk *&blk,
|
|
PacketList &writebacks)
|
|
{
|
|
PacketPtr bus_pkt = createMissPacket(pkt, blk, true,
|
|
pkt->isWholeLineWrite(blkSize));
|
|
DPRINTF(Cache, "Sending an atomic %s\n", bus_pkt->print());
|
|
|
|
Cycles latency = ticksToCycles(memSidePort.sendAtomic(bus_pkt));
|
|
|
|
assert(bus_pkt->isResponse());
|
|
// At the moment the only supported downstream requests we issue
|
|
// are ReadReq and therefore here we should only see the
|
|
// corresponding responses
|
|
assert(bus_pkt->isRead());
|
|
assert(pkt->cmd != MemCmd::UpgradeResp);
|
|
assert(!bus_pkt->isInvalidate());
|
|
assert(!bus_pkt->hasSharers());
|
|
|
|
// We are now dealing with the response handling
|
|
DPRINTF(Cache, "Receive response: %s\n", bus_pkt->print());
|
|
|
|
if (!bus_pkt->isError()) {
|
|
// Any reponse that does not have an error should be filling,
|
|
// afterall it is a read response
|
|
DPRINTF(Cache, "Block for addr %#llx being updated in Cache\n",
|
|
bus_pkt->getAddr());
|
|
blk = handleFill(bus_pkt, blk, writebacks, allocOnFill(bus_pkt->cmd));
|
|
assert(blk);
|
|
}
|
|
satisfyRequest(pkt, blk);
|
|
|
|
maintainClusivity(true, blk);
|
|
|
|
// Use the separate bus_pkt to generate response to pkt and
|
|
// then delete it.
|
|
if (!pkt->isWriteback() && pkt->cmd != MemCmd::WriteClean) {
|
|
assert(pkt->needsResponse());
|
|
pkt->makeAtomicResponse();
|
|
if (bus_pkt->isError()) {
|
|
pkt->copyError(bus_pkt);
|
|
}
|
|
}
|
|
|
|
delete bus_pkt;
|
|
|
|
return latency;
|
|
}
|
|
|
|
Tick
|
|
NoncoherentCache::recvAtomic(PacketPtr pkt)
|
|
{
|
|
panic_if(pkt->cacheResponding(), "Should not see packets where cache "
|
|
"is responding");
|
|
|
|
panic_if(!(pkt->isRead() || pkt->isWrite()),
|
|
"Should only see read and writes at non-coherent cache\n");
|
|
|
|
return BaseCache::recvAtomic(pkt);
|
|
}
|
|
|
|
|
|
void
|
|
NoncoherentCache::functionalAccess(PacketPtr pkt, bool from_cpu_side)
|
|
{
|
|
panic_if(!from_cpu_side, "Non-coherent cache received functional snoop"
|
|
" request\n");
|
|
|
|
BaseCache::functionalAccess(pkt, from_cpu_side);
|
|
}
|
|
|
|
void
|
|
NoncoherentCache::serviceMSHRTargets(MSHR *mshr, const PacketPtr pkt,
|
|
CacheBlk *blk)
|
|
{
|
|
// First offset for critical word first calculations
|
|
const int initial_offset = mshr->getTarget()->pkt->getOffset(blkSize);
|
|
|
|
MSHR::TargetList targets = mshr->extractServiceableTargets(pkt);
|
|
for (auto &target: targets) {
|
|
Packet *tgt_pkt = target.pkt;
|
|
|
|
switch (target.source) {
|
|
case MSHR::Target::FromCPU:
|
|
// handle deferred requests comming from a cache or core
|
|
// above
|
|
|
|
Tick completion_time;
|
|
// Here we charge on completion_time the delay of the xbar if the
|
|
// packet comes from it, charged on headerDelay.
|
|
completion_time = pkt->headerDelay;
|
|
|
|
satisfyRequest(tgt_pkt, blk);
|
|
|
|
// How many bytes past the first request is this one
|
|
int transfer_offset;
|
|
transfer_offset = tgt_pkt->getOffset(blkSize) - initial_offset;
|
|
if (transfer_offset < 0) {
|
|
transfer_offset += blkSize;
|
|
}
|
|
// If not critical word (offset) return payloadDelay.
|
|
// responseLatency is the latency of the return path
|
|
// from lower level caches/memory to an upper level cache or
|
|
// the core.
|
|
completion_time += clockEdge(responseLatency) +
|
|
(transfer_offset ? pkt->payloadDelay : 0);
|
|
|
|
assert(tgt_pkt->req->requestorId() < system->maxRequestors());
|
|
stats.cmdStats(tgt_pkt).missLatency[tgt_pkt->req->requestorId()] +=
|
|
completion_time - target.recvTime;
|
|
|
|
tgt_pkt->makeTimingResponse();
|
|
if (pkt->isError())
|
|
tgt_pkt->copyError(pkt);
|
|
|
|
// Reset the bus additional time as it is now accounted for
|
|
tgt_pkt->headerDelay = tgt_pkt->payloadDelay = 0;
|
|
cpuSidePort.schedTimingResp(tgt_pkt, completion_time);
|
|
break;
|
|
|
|
case MSHR::Target::FromPrefetcher:
|
|
// handle deferred requests comming from a prefetcher
|
|
// attached to this cache
|
|
assert(tgt_pkt->cmd == MemCmd::HardPFReq);
|
|
|
|
if (blk)
|
|
blk->setPrefetched();
|
|
|
|
// We have filled the block and the prefetcher does not
|
|
// require responses.
|
|
delete tgt_pkt;
|
|
break;
|
|
|
|
default:
|
|
// we should never see FromSnoop Targets as this is a
|
|
// non-coherent cache
|
|
panic("Illegal target->source enum %d\n", target.source);
|
|
}
|
|
}
|
|
|
|
// Reponses are filling and bring in writable blocks, therefore
|
|
// there should be no deferred targets and all the non-deferred
|
|
// targets are now serviced.
|
|
assert(mshr->getNumTargets() == 0);
|
|
}
|
|
|
|
void
|
|
NoncoherentCache::recvTimingResp(PacketPtr pkt)
|
|
{
|
|
assert(pkt->isResponse());
|
|
// At the moment the only supported downstream requests we issue
|
|
// are ReadReq and therefore here we should only see the
|
|
// corresponding responses
|
|
assert(pkt->isRead());
|
|
assert(pkt->cmd != MemCmd::UpgradeResp);
|
|
assert(!pkt->isInvalidate());
|
|
// This cache is non-coherent and any memories below are
|
|
// non-coherent too (non-coherent caches or the main memory),
|
|
// therefore the fetched block can be marked as writable.
|
|
assert(!pkt->hasSharers());
|
|
|
|
BaseCache::recvTimingResp(pkt);
|
|
}
|
|
|
|
PacketPtr
|
|
NoncoherentCache::evictBlock(CacheBlk *blk)
|
|
{
|
|
// A dirty block is always written back.
|
|
|
|
// A clean block can we written back, if we turned on writebacks
|
|
// for clean blocks. This could be useful if there is a cache
|
|
// below and we want to make sure the block is cached but if the
|
|
// memory below is the main memory WritebackCleans are
|
|
// unnecessary.
|
|
|
|
// If we clean writebacks are not enabled, we do not take any
|
|
// further action for evictions of clean blocks (i.e., CleanEvicts
|
|
// are unnecessary).
|
|
PacketPtr pkt = (blk->isDirty() || writebackClean) ?
|
|
writebackBlk(blk) : nullptr;
|
|
|
|
invalidateBlock(blk);
|
|
|
|
return pkt;
|
|
}
|
|
|
|
NoncoherentCache*
|
|
NoncoherentCacheParams::create()
|
|
{
|
|
assert(tags);
|
|
assert(replacement_policy);
|
|
|
|
return new NoncoherentCache(this);
|
|
}
|