blob: a8a53362cd603100a3a5406ff0ad98975feb2395 [file] [log] [blame]
/*
* Copyright (c) 2013 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.
*
* 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.
*
* Authors: Stephan Diestelhorst <stephan.diestelhorst@arm.com>
*/
/**
* @file
* Definition of a snoop filter.
*/
#include "base/misc.hh"
#include "base/trace.hh"
#include "debug/SnoopFilter.hh"
#include "mem/snoop_filter.hh"
#include "sim/system.hh"
std::pair<SnoopFilter::SnoopList, Cycles>
SnoopFilter::lookupRequest(const Packet* cpkt, const SlavePort& slave_port)
{
DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
__func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
SnoopMask req_port = portToMask(slave_port);
auto sf_it = cachedLocations.find(line_addr);
bool is_hit = (sf_it != cachedLocations.end());
// Create a new element through operator[] and modify in-place
SnoopItem& sf_item = is_hit ? sf_it->second : cachedLocations[line_addr];
SnoopMask interested = sf_item.holder | sf_item.requested;
totRequests++;
if (is_hit) {
// Single bit set -> value is a power of two
if (isPow2(interested))
hitSingleRequests++;
else
hitMultiRequests++;
}
DPRINTF(SnoopFilter, "%s: SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
if (cpkt->needsResponse()) {
if (!cpkt->memInhibitAsserted()) {
// Max one request per address per port
panic_if(sf_item.requested & req_port, "double request :( "\
"SF value %x.%x\n", sf_item.requested, sf_item.holder);
// Mark in-flight requests to distinguish later on
sf_item.requested |= req_port;
} else {
// NOTE: The memInhibit might have been asserted by a cache closer
// to the CPU, already -> the response will not be seen by this
// filter -> we do not need to keep the in-flight request, but make
// sure that we know that that cluster has a copy
panic_if(!(sf_item.holder & req_port), "Need to hold the value!");
DPRINTF(SnoopFilter, "%s: not marking request. SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
}
DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
}
return snoopSelected(maskToPortList(interested & ~req_port), lookupLatency);
}
void
SnoopFilter::updateRequest(const Packet* cpkt, const SlavePort& slave_port,
bool will_retry)
{
DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
__func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
SnoopMask req_port = portToMask(slave_port);
SnoopItem& sf_item = cachedLocations[line_addr];
DPRINTF(SnoopFilter, "%s: old SF value %x.%x retry: %i\n",
__func__, sf_item.requested, sf_item.holder, will_retry);
if (will_retry) {
// Unmark a request that will come again.
sf_item.requested &= ~req_port;
return;
}
// will_retry == false
if (!cpkt->needsResponse()) {
// Packets that will not evoke a response but still need updates of the
// snoop filter; WRITEBACKs for now only
if (cpkt->cmd == MemCmd::Writeback) {
// make sure that the sender actually had the line
panic_if(sf_item.requested & req_port, "double request :( "\
"SF value %x.%x\n", sf_item.requested, sf_item.holder);
panic_if(!(sf_item.holder & req_port), "requester %x is not a "\
"holder :( SF value %x.%x\n", req_port,
sf_item.requested, sf_item.holder);
// Writebacks -> the sender does not have the line anymore
sf_item.holder &= ~req_port;
} else {
assert(0 == "Handle non-writeback, here");
}
DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
}
}
std::pair<SnoopFilter::SnoopList, Cycles>
SnoopFilter::lookupSnoop(const Packet* cpkt)
{
DPRINTF(SnoopFilter, "%s: packet addr 0x%x cmd %s\n",
__func__, cpkt->getAddr(), cpkt->cmdString());
assert(cpkt->isRequest());
// Broadcast / filter upward snoops
const bool filter_upward = true; // @todo: Make configurable
if (!filter_upward)
return snoopAll(lookupLatency);
Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
auto sf_it = cachedLocations.find(line_addr);
bool is_hit = (sf_it != cachedLocations.end());
// Create a new element through operator[] and modify in-place
SnoopItem& sf_item = is_hit ? sf_it->second : cachedLocations[line_addr];
DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
SnoopMask interested = (sf_item.holder | sf_item.requested);
totSnoops++;
if (is_hit) {
// Single bit set -> value is a power of two
if (isPow2(interested))
hitSingleSnoops++;
else
hitMultiSnoops++;
}
assert(cpkt->isInvalidate() == cpkt->needsExclusive());
if (cpkt->isInvalidate() && !sf_item.requested) {
// Early clear of the holder, if no other request is currently going on
// @todo: This should possibly be updated even though we do not filter
// upward snoops
sf_item.holder = 0;
}
DPRINTF(SnoopFilter, "%s: new SF value %x.%x interest: %x \n",
__func__, sf_item.requested, sf_item.holder, interested);
return snoopSelected(maskToPortList(interested), lookupLatency);
}
void
SnoopFilter::updateSnoopResponse(const Packet* cpkt,
const SlavePort& rsp_port,
const SlavePort& req_port)
{
DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
__func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
cpkt->cmdString());
Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
SnoopMask rsp_mask = portToMask(rsp_port);
SnoopMask req_mask = portToMask(req_port);
SnoopItem& sf_item = cachedLocations[line_addr];
assert(cpkt->isResponse());
assert(cpkt->memInhibitAsserted());
DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
// The source should have the line
panic_if(!(sf_item.holder & rsp_mask), "SF value %x.%x does not have "\
"the line\n", sf_item.requested, sf_item.holder);
// The destination should have had a request in
panic_if(!(sf_item.requested & req_mask), "SF value %x.%x missing "\
"the original request\n", sf_item.requested, sf_item.holder);
// Update the residency of the cache line.
if (cpkt->needsExclusive() || !cpkt->sharedAsserted()) {
DPRINTF(SnoopFilter, "%s: dropping %x because needs: %i shared: %i "\
"SF val: %x.%x\n", __func__, rsp_mask,
cpkt->needsExclusive(), cpkt->sharedAsserted(),
sf_item.requested, sf_item.holder);
sf_item.holder &= ~rsp_mask;
// The snoop filter does not see any ACKs from non-responding sharers
// that have been invalidated :( So below assert would be nice, but..
//assert(sf_item.holder == 0);
sf_item.holder = 0;
}
assert(cpkt->cmd != MemCmd::Writeback);
sf_item.holder |= req_mask;
sf_item.requested &= ~req_mask;
DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
}
void
SnoopFilter::updateSnoopForward(const Packet* cpkt,
const SlavePort& rsp_port, const MasterPort& req_port)
{
DPRINTF(SnoopFilter, "%s: packet rsp %s req %s addr 0x%x cmd %s\n",
__func__, rsp_port.name(), req_port.name(), cpkt->getAddr(),
cpkt->cmdString());
Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
SnoopItem& sf_item = cachedLocations[line_addr];
SnoopMask rsp_mask M5_VAR_USED = portToMask(rsp_port);
assert(cpkt->isResponse());
assert(cpkt->memInhibitAsserted());
DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
// Remote (to this snoop filter) snoops update the filter already when they
// arrive from below, because we may not see any response.
if (cpkt->needsExclusive()) {
// If the request to this snoop response hit an in-flight transaction,
// the holder was not reset -> no assertion & do that here, now!
//assert(sf_item.holder == 0);
sf_item.holder = 0;
}
DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
}
void
SnoopFilter::updateResponse(const Packet* cpkt, const SlavePort& slave_port)
{
DPRINTF(SnoopFilter, "%s: packet src %s addr 0x%x cmd %s\n",
__func__, slave_port.name(), cpkt->getAddr(), cpkt->cmdString());
Addr line_addr = cpkt->getAddr() & ~(linesize - 1);
SnoopMask slave_mask = portToMask(slave_port);
SnoopItem& sf_item = cachedLocations[line_addr];
assert(cpkt->isResponse());
DPRINTF(SnoopFilter, "%s: old SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
// Make sure we have seen the actual request, too
panic_if(!(sf_item.requested & slave_mask), "SF value %x.%x missing "\
"request bit\n", sf_item.requested, sf_item.holder);
// Update the residency of the cache line.
if (cpkt->needsExclusive() || !cpkt->sharedAsserted())
sf_item.holder = 0;
sf_item.holder |= slave_mask;
sf_item.requested &= ~slave_mask;
DPRINTF(SnoopFilter, "%s: new SF value %x.%x\n",
__func__, sf_item.requested, sf_item.holder);
}
void
SnoopFilter::regStats()
{
totRequests
.name(name() + ".tot_requests")
.desc("Total number of requests made to the snoop filter.");
hitSingleRequests
.name(name() + ".hit_single_requests")
.desc("Number of requests hitting in the snoop filter with a single "\
"holder of the requested data.");
hitMultiRequests
.name(name() + ".hit_multi_requests")
.desc("Number of requests hitting in the snoop filter with multiple "\
"(>1) holders of the requested data.");
totSnoops
.name(name() + ".tot_snoops")
.desc("Total number of snoops made to the snoop filter.");
hitSingleSnoops
.name(name() + ".hit_single_snoops")
.desc("Number of snoops hitting in the snoop filter with a single "\
"holder of the requested data.");
hitMultiSnoops
.name(name() + ".hit_multi_snoops")
.desc("Number of snoops hitting in the snoop filter with multiple "\
"(>1) holders of the requested data.");
}
SnoopFilter *
SnoopFilterParams::create()
{
return new SnoopFilter(this);
}