MEM: Separate snoops and normal memory requests/responses

This patch introduces port access methods that separates snoop
request/responses from normal memory request/responses. The
differentiation is made for functional, atomic and timing accesses and
builds on the introduction of master and slave ports.

Before the introduction of this patch, the packets belonging to the
different phases of the protocol (request -> [forwarded snoop request
-> snoop response]* -> response) all use the same port access
functions, even though the snoop packets flow in the opposite
direction to the normal packet. That is, a coherent master sends
normal request and receives responses, but receives snoop requests and
sends snoop responses (vice versa for the slave). These two distinct
phases now use different access functions, as described below.

Starting with the functional access, a master sends a request to a
slave through sendFunctional, and the request packet is turned into a
response before the call returns. In a system without cache coherence,
this is all that is needed from the functional interface. For the
cache-coherent scenario, a slave also sends snoop requests to coherent
masters through sendFunctionalSnoop, with responses returned within
the same packet pointer. This is currently used by the bus and caches,
and the LSQ of the O3 CPU. The send/recvFunctional and
send/recvFunctionalSnoop are moved from the Port super class to the
appropriate subclass.

Atomic accesses follow the same flow as functional accesses, with
request being sent from master to slave through sendAtomic. In the
case of cache-coherent ports, a slave can send snoop requests to a
master through sendAtomicSnoop. Just as for the functional access
methods, the atomic send and receive member functions are moved to the
appropriate subclasses.

The timing access methods are different from the functional and atomic
in that requests and responses are separated in time and
send/recvTiming are used for both directions. Hence, a master uses
sendTiming to send a request to a slave, and a slave uses sendTiming
to send a response back to a master, at a later point in time. Snoop
requests and responses travel in the opposite direction, similar to
what happens in functional and atomic accesses. With the introduction
of this patch, it is possible to determine the direction of packets in
the bus, and no longer necessary to look for both a master and a slave
port with the requested port id.

In contrast to the normal recvFunctional, recvAtomic and recvTiming
that are pure virtual functions, the recvFunctionalSnoop,
recvAtomicSnoop and recvTimingSnoop have a default implementation that
calls panic. This is to allow non-coherent master and slave ports to
not implement these functions.
This commit is contained in:
Andreas Hansson 2012-04-14 05:45:07 -04:00
parent b9bc530ad2
commit dccca0d3a9
39 changed files with 781 additions and 514 deletions

View file

@ -1,4 +1,16 @@
/*
* Copyright (c) 2012 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) 2007 The Hewlett-Packard Development Company
* All rights reserved.
*
@ -110,49 +122,34 @@ Walker::WalkerPort::recvTiming(PacketPtr pkt)
bool
Walker::recvTiming(PacketPtr pkt)
{
if (pkt->isResponse() || pkt->wasNacked()) {
WalkerSenderState * senderState =
dynamic_cast<WalkerSenderState *>(pkt->senderState);
pkt->senderState = senderState->saved;
WalkerState * senderWalk = senderState->senderWalk;
bool walkComplete = senderWalk->recvPacket(pkt);
delete senderState;
if (walkComplete) {
std::list<WalkerState *>::iterator iter;
for (iter = currStates.begin(); iter != currStates.end(); iter++) {
WalkerState * walkerState = *(iter);
if (walkerState == senderWalk) {
iter = currStates.erase(iter);
break;
}
}
delete senderWalk;
// Since we block requests when another is outstanding, we
// need to check if there is a waiting request to be serviced
if (currStates.size()) {
WalkerState * newState = currStates.front();
if (!newState->wasStarted())
newState->startWalk();
assert(pkt->isResponse());
WalkerSenderState * senderState =
dynamic_cast<WalkerSenderState *>(pkt->senderState);
pkt->senderState = senderState->saved;
WalkerState * senderWalk = senderState->senderWalk;
bool walkComplete = senderWalk->recvPacket(pkt);
delete senderState;
if (walkComplete) {
std::list<WalkerState *>::iterator iter;
for (iter = currStates.begin(); iter != currStates.end(); iter++) {
WalkerState * walkerState = *(iter);
if (walkerState == senderWalk) {
iter = currStates.erase(iter);
break;
}
}
} else {
DPRINTF(PageTableWalker, "Received strange packet\n");
delete senderWalk;
// Since we block requests when another is outstanding, we
// need to check if there is a waiting request to be serviced
if (currStates.size()) {
WalkerState * newState = currStates.front();
if (!newState->wasStarted())
newState->startWalk();
}
}
return true;
}
Tick
Walker::WalkerPort::recvAtomic(PacketPtr pkt)
{
return 0;
}
void
Walker::WalkerPort::recvFunctional(PacketPtr pkt)
{
return;
}
void
Walker::WalkerPort::recvRetry()
{
@ -572,7 +569,8 @@ Walker::WalkerState::setupWalk(Addr vaddr)
bool
Walker::WalkerState::recvPacket(PacketPtr pkt)
{
if (pkt->isResponse() && !pkt->wasNacked()) {
assert(pkt->isResponse());
if (!pkt->wasNacked()) {
assert(inflight);
assert(state == Waiting);
assert(!read);
@ -615,7 +613,7 @@ Walker::WalkerState::recvPacket(PacketPtr pkt)
}
return true;
}
} else if (pkt->wasNacked()) {
} else {
DPRINTF(PageTableWalker, "Request was nacked. Entering retry state\n");
pkt->reinitNacked();
if (!walker->sendTiming(this, pkt)) {

View file

@ -71,8 +71,13 @@ namespace X86ISA
Walker *walker;
bool recvTiming(PacketPtr pkt);
Tick recvAtomic(PacketPtr pkt);
void recvFunctional(PacketPtr pkt);
/**
* Snooping a coherence request, do nothing.
*/
bool recvTimingSnoop(PacketPtr pkt) { return true; }
Tick recvAtomicSnoop(PacketPtr pkt) { return 0; }
void recvFunctionalSnoop(PacketPtr pkt) { }
void recvRetry();
bool isSnooping() const { return true; }
};

View file

@ -534,27 +534,20 @@ BaseCPU::traceFunctionsInternal(Addr pc)
bool
BaseCPU::CpuPort::recvTiming(PacketPtr pkt)
{
panic("BaseCPU doesn't expect recvTiming callback!");
panic("BaseCPU doesn't expect recvTiming!\n");
return true;
}
void
BaseCPU::CpuPort::recvRetry()
{
panic("BaseCPU doesn't expect recvRetry callback!");
}
Tick
BaseCPU::CpuPort::recvAtomic(PacketPtr pkt)
{
panic("BaseCPU doesn't expect recvAtomic callback!");
return curTick();
panic("BaseCPU doesn't expect recvRetry!\n");
}
void
BaseCPU::CpuPort::recvFunctional(PacketPtr pkt)
BaseCPU::CpuPort::recvFunctionalSnoop(PacketPtr pkt)
{
// No internal storage to update (in the general case). In the
// long term this should never be called, but that assumed a split
// into master/slave and request/response.
// No internal storage to update (in the general case). A CPU with
// internal storage, e.g. an LSQ that should be part of the
// coherent memory has to check against stored data.
}

View file

@ -135,11 +135,9 @@ class BaseCPU : public MemObject
virtual bool recvTiming(PacketPtr pkt);
virtual Tick recvAtomic(PacketPtr pkt);
virtual void recvRetry();
void recvFunctional(PacketPtr pkt);
virtual void recvFunctionalSnoop(PacketPtr pkt);
};

View file

@ -90,16 +90,13 @@ InOrderCPU::CachePort::CachePort(CacheUnit *_cacheUnit) :
bool
InOrderCPU::CachePort::recvTiming(Packet *pkt)
{
assert(pkt->isResponse());
if (pkt->isError())
DPRINTF(InOrderCachePort, "Got error packet back for address: %x\n",
pkt->getAddr());
else if (pkt->isResponse())
else
cacheUnit->processCacheCompletion(pkt);
else {
//@note: depending on consistency model, update here
DPRINTF(InOrderCachePort, "Received snoop pkt %x,Ignoring\n",
pkt->getAddr());
}
return true;
}

View file

@ -174,6 +174,9 @@ class InOrderCPU : public BaseCPU
/** Handles doing a retry of a failed timing request. */
void recvRetry();
/** Ignoring snoops for now. */
bool recvTimingSnoop(PacketPtr pkt) { return true; }
};
/** Define TickEvent for the CPU */

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 ARM Limited
* Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -89,14 +89,12 @@ template<class Impl>
bool
FullO3CPU<Impl>::IcachePort::recvTiming(PacketPtr pkt)
{
assert(pkt->isResponse());
DPRINTF(O3CPU, "Fetch unit received timing\n");
if (pkt->isResponse()) {
// We shouldn't ever get a block in ownership state
assert(!(pkt->memInhibitAsserted() && !pkt->sharedAsserted()));
// We shouldn't ever get a block in ownership state
assert(!(pkt->memInhibitAsserted() && !pkt->sharedAsserted()));
fetch->processCacheCompletion(pkt);
fetch->processCacheCompletion(pkt);
}
//else Snooped a coherence request, just return
return true;
}
@ -111,9 +109,18 @@ template <class Impl>
bool
FullO3CPU<Impl>::DcachePort::recvTiming(PacketPtr pkt)
{
assert(pkt->isResponse());
return lsq->recvTiming(pkt);
}
template <class Impl>
bool
FullO3CPU<Impl>::DcachePort::recvTimingSnoop(PacketPtr pkt)
{
assert(pkt->isRequest());
return lsq->recvTimingSnoop(pkt);
}
template <class Impl>
void
FullO3CPU<Impl>::DcachePort::recvRetry()

View file

@ -149,6 +149,7 @@ class FullO3CPU : public BaseO3CPU
/** Timing version of receive. Handles setting fetch to the
* proper status to start fetching. */
virtual bool recvTiming(PacketPtr pkt);
virtual bool recvTimingSnoop(PacketPtr pkt) { return true; }
/** Handles doing a retry of a failed fetch. */
virtual void recvRetry();
@ -176,6 +177,7 @@ class FullO3CPU : public BaseO3CPU
* completing the load or store that has returned from
* memory. */
virtual bool recvTiming(PacketPtr pkt);
virtual bool recvTimingSnoop(PacketPtr pkt);
/** Handles doing a retry of the previous send. */
virtual void recvRetry();

View file

@ -299,6 +299,8 @@ class LSQ {
*/
bool recvTiming(PacketPtr pkt);
bool recvTimingSnoop(PacketPtr pkt);
/** The CPU pointer. */
O3CPU *cpu;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 ARM Limited
* Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -321,25 +321,32 @@ template <class Impl>
bool
LSQ<Impl>::recvTiming(PacketPtr pkt)
{
assert(pkt->isResponse());
if (pkt->isError())
DPRINTF(LSQ, "Got error packet back for address: %#X\n",
pkt->getAddr());
if (pkt->isResponse()) {
thread[pkt->req->threadId()].completeDataAccess(pkt);
} else {
DPRINTF(LSQ, "received pkt for addr:%#x %s\n", pkt->getAddr(),
pkt->cmdString());
thread[pkt->req->threadId()].completeDataAccess(pkt);
return true;
}
// must be a snoop
if (pkt->isInvalidate()) {
DPRINTF(LSQ, "received invalidation for addr:%#x\n",
pkt->getAddr());
for (ThreadID tid = 0; tid < numThreads; tid++) {
thread[tid].checkSnoop(pkt);
}
template <class Impl>
bool
LSQ<Impl>::recvTimingSnoop(PacketPtr pkt)
{
assert(pkt->isRequest());
DPRINTF(LSQ, "received pkt for addr:%#x %s\n", pkt->getAddr(),
pkt->cmdString());
// must be a snoop
if (pkt->isInvalidate()) {
DPRINTF(LSQ, "received invalidation for addr:%#x\n",
pkt->getAddr());
for (ThreadID tid = 0; tid < numThreads; tid++) {
thread[tid].checkSnoop(pkt);
}
// to provide stronger consistency model
}
// to provide stronger consistency model
return true;
}

View file

@ -91,7 +91,7 @@ class AtomicSimpleCPU : public BaseSimpleCPU
protected:
virtual Tick recvAtomic(PacketPtr pkt)
virtual Tick recvAtomicSnoop(PacketPtr pkt)
{
// Snooping a coherence request, just return
return 0;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010 ARM Limited
* Copyright (c) 2010-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -718,7 +718,8 @@ TimingSimpleCPU::IcachePort::ITickEvent::process()
bool
TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
{
if (pkt->isResponse() && !pkt->wasNacked()) {
assert(pkt->isResponse());
if (!pkt->wasNacked()) {
DPRINTF(SimpleCPU, "Received timing response %#x\n", pkt->getAddr());
// delay processing of returned data until next CPU clock edge
Tick next_tick = cpu->nextCycle(curTick());
@ -729,7 +730,7 @@ TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
tickEvent.schedule(pkt, next_tick);
return true;
} else if (pkt->wasNacked()) {
} else {
assert(cpu->_status == IcacheWaitResponse);
pkt->reinitNacked();
if (!sendTiming(pkt)) {
@ -737,7 +738,7 @@ TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
cpu->ifetch_pkt = pkt;
}
}
//Snooping a Coherence Request, do nothing
return true;
}
@ -838,7 +839,8 @@ TimingSimpleCPU::completeDrain()
bool
TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
{
if (pkt->isResponse() && !pkt->wasNacked()) {
assert(pkt->isResponse());
if (!pkt->wasNacked()) {
// delay processing of returned data until next CPU clock edge
Tick next_tick = cpu->nextCycle(curTick());
@ -858,8 +860,7 @@ TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
}
return true;
}
else if (pkt->wasNacked()) {
} else {
assert(cpu->_status == DcacheWaitResponse);
pkt->reinitNacked();
if (!sendTiming(pkt)) {
@ -867,7 +868,7 @@ TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
cpu->dcache_pkt = pkt;
}
}
//Snooping a Coherence Request, do nothing
return true;
}

View file

@ -153,6 +153,11 @@ class TimingSimpleCPU : public BaseSimpleCPU
protected:
/**
* Snooping a coherence request, do nothing.
*/
virtual bool recvTimingSnoop(PacketPtr pkt) { return true; }
TimingSimpleCPU* cpu;
struct TickEvent : public Event

View file

@ -90,13 +90,6 @@ RubyDirectedTester::getMasterPort(const std::string &if_name, int idx)
}
}
Tick
RubyDirectedTester::CpuPort::recvAtomic(PacketPtr pkt)
{
panic("RubyDirectedTester::CpuPort::recvAtomic() not implemented!\n");
return 0;
}
bool
RubyDirectedTester::CpuPort::recvTiming(PacketPtr pkt)
{

View file

@ -64,8 +64,6 @@ class RubyDirectedTester : public MemObject
virtual bool recvTiming(PacketPtr pkt);
virtual void recvRetry()
{ panic("%s does not expect a retry\n", name()); }
virtual Tick recvAtomic(PacketPtr pkt);
virtual void recvFunctional(PacketPtr pkt) { }
};
typedef RubyDirectedTesterParams Params;

View file

@ -55,35 +55,11 @@ int TESTER_ALLOCATOR=0;
bool
MemTest::CpuPort::recvTiming(PacketPtr pkt)
{
if (pkt->isResponse()) {
memtest->completeRequest(pkt);
} else {
// must be snoop upcall
assert(pkt->isRequest());
assert(pkt->getDest() == Packet::Broadcast);
}
assert(pkt->isResponse());
memtest->completeRequest(pkt);
return true;
}
Tick
MemTest::CpuPort::recvAtomic(PacketPtr pkt)
{
// must be snoop upcall
assert(pkt->isRequest());
assert(pkt->getDest() == Packet::Broadcast);
return curTick();
}
void
MemTest::CpuPort::recvFunctional(PacketPtr pkt)
{
//Do nothing if we see one come through
// if (curTick() != 0)//Supress warning durring initialization
// warn("Functional Writes not implemented in MemTester\n");
//Need to find any response values that intersect and update
return;
}
void
MemTest::CpuPort::recvRetry()
{

View file

@ -99,9 +99,11 @@ class MemTest : public MemObject
virtual bool recvTiming(PacketPtr pkt);
virtual Tick recvAtomic(PacketPtr pkt);
virtual bool recvTimingSnoop(PacketPtr pkt) { return true; }
virtual void recvFunctional(PacketPtr pkt);
virtual Tick recvAtomicSnoop(PacketPtr pkt) { return 0; }
virtual void recvFunctionalSnoop(PacketPtr pkt) { }
virtual void recvRetry();
};

View file

@ -53,34 +53,11 @@ int TESTER_NETWORK=0;
bool
NetworkTest::CpuPort::recvTiming(PacketPtr pkt)
{
if (pkt->isResponse()) {
networktest->completeRequest(pkt);
} else {
// must be snoop upcall
assert(pkt->isRequest());
assert(pkt->getDest() == Packet::Broadcast);
}
assert(pkt->isResponse());
networktest->completeRequest(pkt);
return true;
}
Tick
NetworkTest::CpuPort::recvAtomic(PacketPtr pkt)
{
panic("NetworkTest doesn't expect recvAtomic call!");
// Will not be used
assert(pkt->isRequest());
assert(pkt->getDest() == Packet::Broadcast);
return curTick();
}
void
NetworkTest::CpuPort::recvFunctional(PacketPtr pkt)
{
panic("NetworkTest doesn't expect recvFunctional call!");
// Will not be used
return;
}
void
NetworkTest::CpuPort::recvRetry()
{

View file

@ -94,10 +94,6 @@ class NetworkTest : public MemObject
virtual bool recvTiming(PacketPtr pkt);
virtual Tick recvAtomic(PacketPtr pkt);
virtual void recvFunctional(PacketPtr pkt);
virtual void recvRetry();
};

View file

@ -146,13 +146,6 @@ RubyTester::getMasterPort(const std::string &if_name, int idx)
}
}
Tick
RubyTester::CpuPort::recvAtomic(PacketPtr pkt)
{
panic("RubyTester::CpuPort::recvAtomic() not implemented!\n");
return 0;
}
bool
RubyTester::CpuPort::recvTiming(PacketPtr pkt)
{

View file

@ -78,8 +78,6 @@ class RubyTester : public MemObject
virtual bool recvTiming(PacketPtr pkt);
virtual void recvRetry()
{ panic("%s does not expect a retry\n", name()); }
virtual Tick recvAtomic(PacketPtr pkt);
virtual void recvFunctional(PacketPtr pkt) { }
};
struct SenderState : public Packet::SenderState

View file

@ -1,4 +1,16 @@
/*
* Copyright (c) 2012 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
* All rights reserved.
*
@ -121,6 +133,7 @@ DmaPort::DmaPort(MemObject *dev, System *s, Tick min_backoff, Tick max_backoff,
bool
DmaPort::recvTiming(PacketPtr pkt)
{
assert(pkt->isResponse());
if (pkt->wasNacked()) {
DPRINTF(DMA, "Received nacked %s addr %#x\n",
pkt->cmdString(), pkt->getAddr());
@ -136,8 +149,6 @@ DmaPort::recvTiming(PacketPtr pkt)
pkt->reinitNacked();
queueDma(pkt, true);
} else if (pkt->isRequest() && recvSnoops) {
return true;
} else if (pkt->senderState) {
DmaReqState *state;
backoffTime >>= 2;

View file

@ -1,4 +1,16 @@
/*
* Copyright (c) 2012 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) 2004-2005 The Regents of The University of Michigan
* All rights reserved.
*
@ -135,17 +147,25 @@ class DmaPort : public MasterPort
bool recvSnoops;
virtual bool recvTiming(PacketPtr pkt);
virtual Tick recvAtomic(PacketPtr pkt)
{
if (recvSnoops) return 0;
panic("dma port shouldn't be used for pio access."); M5_DUMMY_RETURN
virtual bool recvTimingSnoop(PacketPtr pkt)
{
if (!recvSnoops)
panic("%s was not expecting a snoop\n", name());
return true;
}
virtual void recvFunctional(PacketPtr pkt)
{
if (recvSnoops) return;
panic("dma port shouldn't be used for pio access.");
virtual Tick recvAtomicSnoop(PacketPtr pkt)
{
if (!recvSnoops)
panic("%s was not expecting a snoop\n", name());
return 0;
}
virtual void recvFunctionalSnoop(PacketPtr pkt)
{
if (!recvSnoops)
panic("%s was not expecting a snoop\n", name());
}
virtual void recvRetry() ;

View file

@ -411,31 +411,12 @@ Bridge::BridgeSlavePort::recvRetry()
bridge->schedule(sendEvent, nextReady);
}
Tick
Bridge::BridgeMasterPort::recvAtomic(PacketPtr pkt)
{
// master port should never receive any atomic access (panic only
// works once the other side, i.e. the busses, respects this)
//
//panic("Master port on %s got a recvAtomic\n", bridge->name());
return 0;
}
Tick
Bridge::BridgeSlavePort::recvAtomic(PacketPtr pkt)
{
return delay + masterPort.sendAtomic(pkt);
}
void
Bridge::BridgeMasterPort::recvFunctional(PacketPtr pkt)
{
// master port should never receive any functional access (panic
// only works once the other side, i.e. the busses, respect this)
// panic("Master port on %s got a recvFunctional\n", bridge->name());
}
void
Bridge::BridgeSlavePort::recvFunctional(PacketPtr pkt)
{

View file

@ -358,14 +358,6 @@ class Bridge : public MemObject
/** When receiving a retry request from the peer port,
pass it to the bridge. */
virtual void recvRetry();
/** When receiving a Atomic requestfrom the peer port,
pass it to the bridge. */
virtual Tick recvAtomic(PacketPtr pkt);
/** When receiving a Functional request from the peer port,
pass it to the bridge. */
virtual void recvFunctional(PacketPtr pkt);
};
/** Slave port of the bridge. */

View file

@ -57,7 +57,6 @@ Bus::Bus(const BusParams *p)
: MemObject(p), clock(p->clock),
headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
drainEvent(NULL), busIdleEvent(this), inRetry(false),
nbrMasterPorts(p->port_master_connection_count),
defaultPortId(INVALID_PORT_ID), useDefaultRange(p->use_default_range),
defaultBlockSize(p->block_size),
cachedBlockSize(0), cachedBlockSizeValid(false)
@ -71,40 +70,28 @@ Bus::Bus(const BusParams *p)
fatal("Number of header cycles must be positive\n");
// create the ports based on the size of the master and slave
// vector ports, and the presence of the default master
// id used to index into master and slave ports, that currently
// has holes to be able to use the id to index into either
int id = 0;
// vector ports, and the presence of the default port, the ports
// are enumerated starting from zero
for (int i = 0; i < p->port_master_connection_count; ++i) {
std::string portName = csprintf("%s-p%d", name(), id);
BusMasterPort* bp = new BusMasterPort(portName, this, id);
std::string portName = csprintf("%s-p%d", name(), i);
BusMasterPort* bp = new BusMasterPort(portName, this, i);
masterPorts.push_back(bp);
slavePorts.push_back(NULL);
++id;
}
// see if we have a default master connected and if so add the
// port
// see if we have a default slave device connected and if so add
// our corresponding master port
if (p->port_default_connection_count) {
defaultPortId = id;
defaultPortId = masterPorts.size();
std::string portName = csprintf("%s-default", name());
BusMasterPort* bp = new BusMasterPort(portName, this, id);
BusMasterPort* bp = new BusMasterPort(portName, this, defaultPortId);
masterPorts.push_back(bp);
slavePorts.push_back(NULL);
++id;
// this is an additional master port
++nbrMasterPorts;
}
// note that the first slave port is now stored on index
// nbrMasterPorts in the vector
// create the slave ports, once again starting at zero
for (int i = 0; i < p->port_slave_connection_count; ++i) {
std::string portName = csprintf("%s-p%d", name(), id);
BusSlavePort* bp = new BusSlavePort(portName, this, id);
masterPorts.push_back(NULL);
std::string portName = csprintf("%s-p%d", name(), i);
BusSlavePort* bp = new BusSlavePort(portName, this, i);
slavePorts.push_back(bp);
++id;
}
clearPortCache();
@ -113,9 +100,8 @@ Bus::Bus(const BusParams *p)
MasterPort &
Bus::getMasterPort(const std::string &if_name, int idx)
{
if (if_name == "master") {
// the master index translates directly to the interfaces
// vector as they are stored first
if (if_name == "master" && idx < masterPorts.size()) {
// the master port index translates directly to the vector position
return *masterPorts[idx];
} else if (if_name == "default") {
return *masterPorts[defaultPortId];
@ -127,8 +113,9 @@ Bus::getMasterPort(const std::string &if_name, int idx)
SlavePort &
Bus::getSlavePort(const std::string &if_name, int idx)
{
if (if_name == "slave") {
return *slavePorts[nbrMasterPorts + idx];
if (if_name == "slave" && idx < slavePorts.size()) {
// the slave port index translates directly to the vector position
return *slavePorts[idx];
} else {
return MemObject::getSlavePort(if_name, idx);
}
@ -137,19 +124,15 @@ Bus::getSlavePort(const std::string &if_name, int idx)
void
Bus::init()
{
std::vector<BusSlavePort*>::iterator intIter;
std::vector<BusSlavePort*>::iterator p;
// iterate over our interfaces and determine which of our neighbours
// are snooping and add them as snoopers
for (intIter = slavePorts.begin(); intIter != slavePorts.end();
intIter++) {
// since there are holes in the vector, check for NULL
if (*intIter != NULL) {
if ((*intIter)->getMasterPort().isSnooping()) {
DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n",
(*intIter)->getMasterPort().name());
snoopPorts.push_back(*intIter);
}
// iterate over our slave ports and determine which of our
// neighbouring master ports are snooping and add them as snoopers
for (p = slavePorts.begin(); p != slavePorts.end(); ++p) {
if ((*p)->getMasterPort().isSnooping()) {
DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n",
(*p)->getMasterPort().name());
snoopPorts.push_back(*p);
}
}
}
@ -200,27 +183,36 @@ void Bus::occupyBus(Tick until)
curTick(), tickNextIdle);
}
/** Function called by the port when the bus is receiving a Timing
* transaction.*/
bool
Bus::isOccupied(PacketPtr pkt, Port* port)
{
// first we see if the next idle tick is in the future, next the
// bus is considered occupied if there are ports on the retry list
// and we are not in a retry with the current port
if (tickNextIdle > curTick() ||
(!retryList.empty() && !(inRetry && port == retryList.front()))) {
addToRetryList(port);
return true;
}
return false;
}
bool
Bus::recvTiming(PacketPtr pkt)
{
// called for both requests and responses
// get the source id and port
// get the source id
Packet::NodeID src_id = pkt->getSrc();
// determine the source port based on the id
Port *src_port = slavePorts[src_id] ?
(Port*) slavePorts[src_id] : (Port*) masterPorts[src_id];
// determine the source port based on the id and direction
Port *src_port = NULL;
if (pkt->isRequest())
src_port = slavePorts[src_id];
else
src_port = masterPorts[src_id];
// If the bus is busy, or other devices are in line ahead of the current
// one, put this device on the retry list.
if (!pkt->isExpressSnoop() &&
(tickNextIdle > curTick() ||
(!retryList.empty() && (!inRetry || src_port != retryList.front()))))
{
addToRetryList(src_port);
// test if the bus should be considered occupied for the current
// packet, and exclude express snoops from the check
if (!pkt->isExpressSnoop() && isOccupied(pkt, src_port)) {
DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x BUSY\n",
src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
return false;
@ -232,89 +224,196 @@ Bus::recvTiming(PacketPtr pkt)
Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
Packet::NodeID dest = pkt->getDest();
int dest_id;
Port *dest_port;
// decide what to do based on the direction
if (pkt->isRequest()) {
// the packet is a memory-mapped request and should be broadcasted to
// our snoopers
assert(dest == Packet::Broadcast);
assert(pkt->getDest() == Packet::Broadcast);
SnoopIter s_end = snoopPorts.end();
for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
BusSlavePort *p = *s_iter;
// we got this request from a snooping master
// (corresponding to our own slave port that is also in
// snoopPorts) and should not send it back to where it
// came from
if (p->getId() != src_id) {
// cache is not allowed to refuse snoop
bool success M5_VAR_USED = p->sendTiming(pkt);
assert(success);
}
// forward to all snoopers but the source
forwardTiming(pkt, src_id);
// remember if we add an outstanding req so we can undo it if
// necessary, if the packet needs a response, we should add it
// as outstanding and express snoops never fail so there is
// not need to worry about them
bool add_outstanding = !pkt->isExpressSnoop() && pkt->needsResponse();
// keep track that we have an outstanding request packet
// matching this request, this is used by the coherency
// mechanism in determining what to do with snoop responses
// (in recvTimingSnoop)
if (add_outstanding) {
// we should never have an exsiting request outstanding
assert(outstandingReq.find(pkt->req) == outstandingReq.end());
outstandingReq.insert(pkt->req);
}
// since it is a request, similar to functional and atomic,
// determine the destination based on the address and forward
// through the corresponding master port
dest_id = findPort(pkt->getAddr());
dest_port = masterPorts[dest_id];
} else {
// the packet is a response, and it should always go back to
// the port determined by the destination field
dest_id = dest;
assert(dest_id != src_id); // catch infinite loops
dest_port = slavePorts[dest_id] ?
(Port*) slavePorts[dest_id] : (Port*) masterPorts[dest_id];
// since it is a normal request, determine the destination
// based on the address and attempt to send the packet
bool success = masterPorts[findPort(pkt->getAddr())]->sendTiming(pkt);
// a normal response from the memory system (i.e. from a
// connected slave) should always go back to the master
// that issued it through one of our slave ports, however
// if this is a snoop response it could go either way, for
// example, it could be coming from a slave port
// connecting an L1 with a coherent master and another L1
// coherent master (one of our slave ports), or coming
// from the L1 and going to the L2 slave port (through one
// of our master ports)
}
assert(dest_port != NULL);
// if this is a snoop from a slave (corresponding to our own
// master), i.e. the memory side of the bus, then do not send it
// back to where it came from
if (dest_id != src_id) {
// send to actual target
if (!dest_port->sendTiming(pkt)) {
// Packet not successfully sent. Leave or put it on the retry list.
// illegal to block responses... can lead to deadlock
assert(!pkt->isResponse());
// It's also illegal to force a transaction to retry after
// someone else has committed to respond.
if (!success) {
// inhibited packets should never be forced to retry
assert(!pkt->memInhibitAsserted());
DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x TGT RETRY\n",
// if it was added as outstanding and the send failed, then
// erase it again
if (add_outstanding)
outstandingReq.erase(pkt->req);
DPRINTF(Bus, "recvTiming: src %d dst %d %s 0x%x RETRY\n",
src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
addToRetryList(src_port);
occupyBus(headerFinishTime);
return false;
}
// send OK, fall through... pkt may have been deleted by
// target at this point, so it should *not* be referenced
// again. We'll set it to NULL here just to be safe.
pkt = NULL;
} else {
// the packet is a normal response to a request that we should
// have seen passing through the bus
assert(outstandingReq.find(pkt->req) != outstandingReq.end());
// remove it as outstanding
outstandingReq.erase(pkt->req);
// send the packet to the destination through one of our slave
// ports, as determined by the destination field
bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTiming(pkt);
// currently it is illegal to block responses... can lead to
// deadlock
assert(success);
}
occupyBus(packetFinishTime);
succeededTiming(packetFinishTime);
// Packet was successfully sent.
// Also take care of retries
return true;
}
bool
Bus::recvTimingSnoop(PacketPtr pkt)
{
// get the source id
Packet::NodeID src_id = pkt->getSrc();
if (pkt->isRequest()) {
DPRINTF(Bus, "recvTimingSnoop: src %d dst %d %s 0x%x\n",
src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
// the packet is an express snoop request and should be
// broadcasted to our snoopers
assert(pkt->getDest() == Packet::Broadcast);
assert(pkt->isExpressSnoop());
// forward to all snoopers
forwardTiming(pkt, INVALID_PORT_ID);
// a snoop request came from a connected slave device (one of
// our master ports), and if it is not coming from the slave
// device responsible for the address range something is
// wrong, hence there is nothing further to do as the packet
// would be going back to where it came from
assert(src_id == findPort(pkt->getAddr()));
// this is an express snoop and is never forced to retry
assert(!inRetry);
return true;
} else {
// determine the source port based on the id
SlavePort* src_port = slavePorts[src_id];
if (isOccupied(pkt, src_port)) {
DPRINTF(Bus, "recvTimingSnoop: src %d dst %d %s 0x%x BUSY\n",
src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
return false;
}
DPRINTF(Bus, "recvTimingSnoop: src %d dst %d %s 0x%x\n",
src_id, pkt->getDest(), pkt->cmdString(), pkt->getAddr());
// get the destination from the packet
Packet::NodeID dest = pkt->getDest();
// responses are never express snoops
assert(!pkt->isExpressSnoop());
calcPacketTiming(pkt);
Tick packetFinishTime = pkt->finishTime;
// determine if the response is from a snoop request we
// created as the result of a normal request (in which case it
// should be in the outstandingReq), or if we merely forwarded
// someone else's snoop request
if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
// this is a snoop response to a snoop request we
// forwarded, e.g. coming from the L1 and going to the L2
// this should be forwarded as a snoop response
bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoop(pkt);
assert(success);
} else {
// we got a snoop response on one of our slave ports,
// i.e. from a coherent master connected to the bus, and
// since we created the snoop request as part of
// recvTiming, this should now be a normal response again
outstandingReq.erase(pkt->req);
// this is a snoop response from a coherent master, with a
// destination field set on its way through the bus as
// request, hence it should never go back to where the
// snoop response came from, but instead to where the
// original request came from
assert(src_id != dest);
// as a normal response, it should go back to a master
// through one of our slave ports
bool success M5_VAR_USED = slavePorts[dest]->sendTiming(pkt);
// currently it is illegal to block responses... can lead
// to deadlock
assert(success);
}
succeededTiming(packetFinishTime);
return true;
}
}
void
Bus::succeededTiming(Tick busy_time)
{
// occupy the bus accordingly
occupyBus(busy_time);
// if a retrying port succeeded, also take it off the retry list
if (inRetry) {
DPRINTF(Bus, "Remove retry from list %d\n", src_id);
DPRINTF(Bus, "Remove retry from list %s\n",
retryList.front()->name());
retryList.pop_front();
inRetry = false;
}
return true;
}
void
Bus::forwardTiming(PacketPtr pkt, int exclude_slave_port_id)
{
SnoopIter s_end = snoopPorts.end();
for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
BusSlavePort *p = *s_iter;
// we could have gotten this request from a snooping master
// (corresponding to our own slave port that is also in
// snoopPorts) and should not send it back to where it came
// from
if (exclude_slave_port_id == INVALID_PORT_ID ||
p->getId() != exclude_slave_port_id) {
// cache is not allowed to refuse snoop
bool success M5_VAR_USED = p->sendTimingSnoop(pkt);
assert(success);
}
}
}
void
@ -430,9 +529,6 @@ Bus::findPort(Addr addr)
name());
}
/** Function called by the port when the bus is receiving a Atomic
* transaction.*/
Tick
Bus::recvAtomic(PacketPtr pkt)
{
@ -443,12 +539,59 @@ Bus::recvAtomic(PacketPtr pkt)
assert(pkt->getDest() == Packet::Broadcast);
assert(pkt->isRequest());
// the packet may be changed by another bus on snoops, record the
// source id here
Packet::NodeID src_id = pkt->getSrc();
// forward to all snoopers but the source
std::pair<MemCmd, Tick> snoop_result = forwardAtomic(pkt, pkt->getSrc());
MemCmd snoop_response_cmd = snoop_result.first;
Tick snoop_response_latency = snoop_result.second;
// record the original command to enable us to restore it between
// snoops so that additional snoops can take place properly
// even if we had a snoop response, we must continue and also
// perform the actual request at the destination
int dest_id = findPort(pkt->getAddr());
// forward the request to the appropriate destination
Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
// if we got a response from a snooper, restore it here
if (snoop_response_cmd != MemCmd::InvalidCmd) {
// no one else should have responded
assert(!pkt->isResponse());
pkt->cmd = snoop_response_cmd;
response_latency = snoop_response_latency;
}
pkt->finishTime = curTick() + response_latency;
return response_latency;
}
Tick
Bus::recvAtomicSnoop(PacketPtr pkt)
{
DPRINTF(Bus, "recvAtomicSnoop: packet src %d dest %d addr 0x%x cmd %s\n",
pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
// we should always see a request routed based on the address
assert(pkt->getDest() == Packet::Broadcast);
assert(pkt->isRequest());
// forward to all snoopers
std::pair<MemCmd, Tick> snoop_result = forwardAtomic(pkt, INVALID_PORT_ID);
MemCmd snoop_response_cmd = snoop_result.first;
Tick snoop_response_latency = snoop_result.second;
if (snoop_response_cmd != MemCmd::InvalidCmd)
pkt->cmd = snoop_response_cmd;
pkt->finishTime = curTick() + snoop_response_latency;
return snoop_response_latency;
}
std::pair<MemCmd, Tick>
Bus::forwardAtomic(PacketPtr pkt, int exclude_slave_port_id)
{
// the packet may be changed on snoops, record the original source
// and command to enable us to restore it between snoops so that
// additional snoops can take place properly
Packet::NodeID orig_src_id = pkt->getSrc();
MemCmd orig_cmd = pkt->cmd;
MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
Tick snoop_response_latency = 0;
@ -460,8 +603,9 @@ Bus::recvAtomic(PacketPtr pkt)
// (corresponding to our own slave port that is also in
// snoopPorts) and should not send it back to where it came
// from
if (p->getId() != src_id) {
Tick latency = p->sendAtomic(pkt);
if (exclude_slave_port_id == INVALID_PORT_ID ||
p->getId() != exclude_slave_port_id) {
Tick latency = p->sendAtomicSnoop(pkt);
// in contrast to a functional access, we have to keep on
// going as all snoopers must be updated even if we get a
// response
@ -476,41 +620,17 @@ Bus::recvAtomic(PacketPtr pkt)
snoop_response_latency = latency;
// restore original packet state for remaining snoopers
pkt->cmd = orig_cmd;
pkt->setSrc(src_id);
pkt->setSrc(orig_src_id);
pkt->setDest(Packet::Broadcast);
}
}
}
// even if we had a snoop response, we must continue and also
// perform the actual request at the destination
int dest_id = findPort(pkt->getAddr());
Tick response_latency = 0;
// if this is a snoop from a slave (corresponding to our own
// master), i.e. the memory side of the bus, then do not send it
// back to where it came from
if (dest_id != src_id) {
response_latency = masterPorts[dest_id]->sendAtomic(pkt);
}
// if we got a response from a snooper, restore it here
if (snoop_response_cmd != MemCmd::InvalidCmd) {
// no one else should have responded
assert(!pkt->isResponse());
assert(pkt->cmd == orig_cmd);
pkt->cmd = snoop_response_cmd;
response_latency = snoop_response_latency;
}
// why do we have this packet field and the return value both???
pkt->finishTime = curTick() + response_latency;
return response_latency;
// the packet is restored as part of the loop and any potential
// snoop response is part of the returned pair
return std::make_pair(snoop_response_cmd, snoop_response_latency);
}
/** Function called by the port when the bus is receiving a Functional
* transaction.*/
void
Bus::recvFunctional(PacketPtr pkt)
{
@ -526,10 +646,40 @@ Bus::recvFunctional(PacketPtr pkt)
assert(pkt->getDest() == Packet::Broadcast);
assert(pkt->isRequest());
// the packet may be changed by another bus on snoops, record the
// source id here
Packet::NodeID src_id = pkt->getSrc();
// forward to all snoopers but the source
forwardFunctional(pkt, pkt->getSrc());
// there is no need to continue if the snooping has found what we
// were looking for and the packet is already a response
if (!pkt->isResponse()) {
int dest_id = findPort(pkt->getAddr());
masterPorts[dest_id]->sendFunctional(pkt);
}
}
void
Bus::recvFunctionalSnoop(PacketPtr pkt)
{
if (!pkt->isPrint()) {
// don't do DPRINTFs on PrintReq as it clutters up the output
DPRINTF(Bus,
"recvFunctionalSnoop: packet src %d dest %d addr 0x%x cmd %s\n",
pkt->getSrc(), pkt->getDest(), pkt->getAddr(),
pkt->cmdString());
}
// we should always see a request routed based on the address
assert(pkt->getDest() == Packet::Broadcast);
assert(pkt->isRequest());
// forward to all snoopers
forwardFunctional(pkt, INVALID_PORT_ID);
}
void
Bus::forwardFunctional(PacketPtr pkt, int exclude_slave_port_id)
{
SnoopIter s_end = snoopPorts.end();
for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) {
BusSlavePort *p = *s_iter;
@ -537,26 +687,13 @@ Bus::recvFunctional(PacketPtr pkt)
// (corresponding to our own slave port that is also in
// snoopPorts) and should not send it back to where it came
// from
if (p->getId() != src_id) {
p->sendFunctional(pkt);
if (exclude_slave_port_id == INVALID_PORT_ID ||
p->getId() != exclude_slave_port_id)
p->sendFunctionalSnoop(pkt);
// if we get a response we are done
if (pkt->isResponse()) {
break;
}
}
}
// there is no need to continue if the snooping has found what we
// were looking for and the packet is already a response
if (!pkt->isResponse()) {
int dest_id = findPort(pkt->getAddr());
// if this is a snoop from a slave (corresponding to our own
// master), i.e. the memory side of the bus, then do not send
// it back to where it came from,
if (dest_id != src_id) {
masterPorts[dest_id]->sendFunctional(pkt);
// if we get a response we are done
if (pkt->isResponse()) {
break;
}
}
}
@ -621,8 +758,7 @@ Bus::recvRangeChange(int id)
std::vector<BusSlavePort*>::const_iterator intIter;
for (intIter = slavePorts.begin(); intIter != slavePorts.end(); intIter++)
if (*intIter != NULL && (*intIter)->getId() != id)
(*intIter)->sendRangeChange();
(*intIter)->sendRangeChange();
inRecvRangeChange.erase(id);
}

View file

@ -91,25 +91,35 @@ class Bus : public MemObject
protected:
/** When reciving a timing request from the peer port (at id),
pass it to the bus. */
/**
* When receiving a timing request, pass it to the bus.
*/
virtual bool recvTiming(PacketPtr pkt)
{ pkt->setSrc(id); return bus->recvTiming(pkt); }
/** When reciving a Atomic requestfrom the peer port (at id),
pass it to the bus. */
/**
* When receiving a timing snoop response, pass it to the bus.
*/
virtual bool recvTimingSnoop(PacketPtr pkt)
{ pkt->setSrc(id); return bus->recvTimingSnoop(pkt); }
/**
* When receiving an atomic request, pass it to the bus.
*/
virtual Tick recvAtomic(PacketPtr pkt)
{ pkt->setSrc(id); return bus->recvAtomic(pkt); }
/** When reciving a Functional requestfrom the peer port (at id),
pass it to the bus. */
/**
* When receiving a functional request, pass it to the bus.
*/
virtual void recvFunctional(PacketPtr pkt)
{ pkt->setSrc(id); bus->recvFunctional(pkt); }
/** When reciving a retry from the peer port (at id),
pass it to the bus. */
/**
* When receiving a retry, pass it to the bus.
*/
virtual void recvRetry()
{ bus->recvRetry(id); }
{ panic("Bus slave ports always succeed and should never retry.\n"); }
// This should return all the 'owned' addresses that are
// downstream from this bus, yes? That is, the union of all
@ -160,20 +170,29 @@ class Bus : public MemObject
protected:
/** When reciving a timing request from the peer port (at id),
pass it to the bus. */
/**
* When receiving a timing response, pass it to the bus.
*/
virtual bool recvTiming(PacketPtr pkt)
{ pkt->setSrc(id); return bus->recvTiming(pkt); }
/** When reciving a Atomic requestfrom the peer port (at id),
pass it to the bus. */
virtual Tick recvAtomic(PacketPtr pkt)
{ pkt->setSrc(id); return bus->recvAtomic(pkt); }
/**
* When receiving a timing snoop request, pass it to the bus.
*/
virtual bool recvTimingSnoop(PacketPtr pkt)
{ pkt->setSrc(id); return bus->recvTimingSnoop(pkt); }
/** When reciving a Functional requestfrom the peer port (at id),
pass it to the bus. */
virtual void recvFunctional(PacketPtr pkt)
{ pkt->setSrc(id); bus->recvFunctional(pkt); }
/**
* When receiving an atomic snoop request, pass it to the bus.
*/
virtual Tick recvAtomicSnoop(PacketPtr pkt)
{ pkt->setSrc(id); return bus->recvAtomicSnoop(pkt); }
/**
* When receiving a functional snoop request, pass it to the bus.
*/
virtual void recvFunctionalSnoop(PacketPtr pkt)
{ pkt->setSrc(id); bus->recvFunctionalSnoop(pkt); }
/** When reciving a range change from the peer port (at id),
pass it to the bus. */
@ -212,18 +231,91 @@ class Bus : public MemObject
typedef std::vector<BusSlavePort*>::iterator SnoopIter;
std::vector<BusSlavePort*> snoopPorts;
/**
* Store the outstanding requests so we can determine which ones
* we generated and which ones were merely forwarded. This is used
* in the coherent bus when coherency responses come back.
*/
std::set<RequestPtr> outstandingReq;
/** Function called by the port when the bus is recieving a Timing
transaction.*/
bool recvTiming(PacketPtr pkt);
/** Function called by the port when the bus is recieving a timing
snoop transaction.*/
bool recvTimingSnoop(PacketPtr pkt);
/**
* Forward a timing packet to our snoopers, potentially excluding
* one of the connected coherent masters to avoid sending a packet
* back to where it came from.
*
* @param pkt Packet to forward
* @param exclude_slave_port_id Id of slave port to exclude
*/
void forwardTiming(PacketPtr pkt, int exclude_slave_port_id);
/**
* Determine if the bus is to be considered occupied when being
* presented with a packet from a specific port. If so, the port
* in question is also added to the retry list.
*
* @param pkt Incoming packet
* @param port Source port on the bus presenting the packet
*
* @return True if the bus is to be considered occupied
*/
bool isOccupied(PacketPtr pkt, Port* port);
/**
* Deal with a destination port accepting a packet by potentially
* removing the source port from the retry list (if retrying) and
* occupying the bus accordingly.
*
* @param busy_time Time to spend as a result of a successful send
*/
void succeededTiming(Tick busy_time);
/** Function called by the port when the bus is recieving a Atomic
transaction.*/
Tick recvAtomic(PacketPtr pkt);
/** Function called by the port when the bus is recieving an
atomic snoop transaction.*/
Tick recvAtomicSnoop(PacketPtr pkt);
/**
* Forward an atomic packet to our snoopers, potentially excluding
* one of the connected coherent masters to avoid sending a packet
* back to where it came from.
*
* @param pkt Packet to forward
* @param exclude_slave_port_id Id of slave port to exclude
*
* @return a pair containing the snoop response and snoop latency
*/
std::pair<MemCmd, Tick> forwardAtomic(PacketPtr pkt,
int exclude_slave_port_id);
/** Function called by the port when the bus is recieving a Functional
transaction.*/
void recvFunctional(PacketPtr pkt);
/** Function called by the port when the bus is recieving a functional
snoop transaction.*/
void recvFunctionalSnoop(PacketPtr pkt);
/**
* Forward a functional packet to our snoopers, potentially
* excluding one of the connected coherent masters to avoid
* sending a packet back to where it came from.
*
* @param pkt Packet to forward
* @param exclude_slave_port_id Id of slave port to exclude
*/
void forwardFunctional(PacketPtr pkt, int exclude_slave_port_id);
/** Timing function called by port when it is once again able to process
* requests. */
void recvRetry(int id);
@ -345,11 +437,6 @@ class Bus : public MemObject
bool inRetry;
std::set<int> inRecvRangeChange;
// keep track of the number of master ports (not counting the
// default master) since we need this as an offset into the
// interfaces vector
unsigned int nbrMasterPorts;
/** The master and slave ports of the bus */
std::vector<BusSlavePort*> slavePorts;
std::vector<BusMasterPort*> masterPorts;

View file

@ -142,7 +142,7 @@ class BaseCache : public MemObject
* @param when time to send the response
*/
void respond(PacketPtr pkt, Tick time) {
queue.schedSendTiming(pkt, time);
queue.schedSendTiming(pkt, time, true);
}
protected:

View file

@ -90,6 +90,8 @@ class Cache : public BaseCache
protected:
virtual bool recvTimingSnoop(PacketPtr pkt);
virtual bool recvTiming(PacketPtr pkt);
virtual Tick recvAtomic(PacketPtr pkt);
@ -152,11 +154,13 @@ class Cache : public BaseCache
protected:
virtual bool recvTimingSnoop(PacketPtr pkt);
virtual bool recvTiming(PacketPtr pkt);
virtual Tick recvAtomic(PacketPtr pkt);
virtual Tick recvAtomicSnoop(PacketPtr pkt);
virtual void recvFunctional(PacketPtr pkt);
virtual void recvFunctionalSnoop(PacketPtr pkt);
virtual unsigned deviceBlockSize() const
{ return cache->getBlockSize(); }

View file

@ -785,7 +785,7 @@ Cache<TagStore>::functionalAccess(PacketPtr pkt, bool fromCpuSide)
} else if (forwardSnoops && cpuSidePort->getMasterPort().isSnooping()) {
// if it came from the memory side, it must be a snoop request
// and we should only forward it if we are forwarding snoops
cpuSidePort->sendFunctional(pkt);
cpuSidePort->sendFunctionalSnoop(pkt);
}
}
}
@ -1178,24 +1178,23 @@ Cache<TagStore>::handleSnoop(PacketPtr pkt, BlkType *blk,
// rewritten to be relative to cpu-side bus (if any)
bool alreadyResponded = pkt->memInhibitAsserted();
if (is_timing) {
Packet *snoopPkt = new Packet(pkt, true); // clear flags
snoopPkt->setExpressSnoop();
snoopPkt->senderState = new ForwardResponseRecord(pkt, this);
cpuSidePort->sendTiming(snoopPkt);
if (snoopPkt->memInhibitAsserted()) {
Packet snoopPkt(pkt, true); // clear flags
snoopPkt.setExpressSnoop();
snoopPkt.senderState = new ForwardResponseRecord(pkt, this);
cpuSidePort->sendTimingSnoop(&snoopPkt);
if (snoopPkt.memInhibitAsserted()) {
// cache-to-cache response from some upper cache
assert(!alreadyResponded);
pkt->assertMemInhibit();
} else {
delete snoopPkt->senderState;
delete snoopPkt.senderState;
}
if (snoopPkt->sharedAsserted()) {
if (snoopPkt.sharedAsserted()) {
pkt->assertShared();
}
delete snoopPkt;
} else {
int origSrc = pkt->getSrc();
cpuSidePort->sendAtomic(pkt);
Packet::NodeID origSrc = pkt->getSrc();
cpuSidePort->sendAtomicSnoop(pkt);
if (!alreadyResponded && pkt->memInhibitAsserted()) {
// cache-to-cache response from some upper cache:
// forward response to original requester
@ -1335,6 +1334,16 @@ Cache<TagStore>::snoopTiming(PacketPtr pkt)
handleSnoop(pkt, blk, true, false, false);
}
template<class TagStore>
bool
Cache<TagStore>::CpuSidePort::recvTimingSnoop(PacketPtr pkt)
{
// Express snoop responses from master to slave, e.g., from L1 to L2
assert(pkt->isResponse());
cache->timingAccess(pkt);
return true;
}
template<class TagStore>
Tick
@ -1483,7 +1492,7 @@ Cache<TagStore>::getTimingPacket()
PacketPtr snoop_pkt = new Packet(tgt_pkt, true);
snoop_pkt->setExpressSnoop();
snoop_pkt->senderState = mshr;
cpuSidePort->sendTiming(snoop_pkt);
cpuSidePort->sendTimingSnoop(snoop_pkt);
if (snoop_pkt->memInhibitAsserted()) {
markInService(mshr, snoop_pkt);
@ -1550,8 +1559,9 @@ template<class TagStore>
bool
Cache<TagStore>::CpuSidePort::recvTiming(PacketPtr pkt)
{
// illegal to block responses... can lead to deadlock
if (pkt->isRequest() && !pkt->memInhibitAsserted() && blocked) {
assert(pkt->isRequest());
// always let inhibited requests through even if blocked
if (!pkt->memInhibitAsserted() && blocked) {
DPRINTF(Cache,"Scheduling a retry while blocked\n");
mustSendRetry = true;
return false;
@ -1603,17 +1613,25 @@ Cache<TagStore>::MemSidePort::recvTiming(PacketPtr pkt)
if (pkt->wasNacked())
panic("Need to implement cache resending nacked packets!\n");
if (pkt->isResponse()) {
cache->handleResponse(pkt);
} else {
cache->snoopTiming(pkt);
}
assert(pkt->isResponse());
cache->handleResponse(pkt);
return true;
}
// Express snooping requests to memside port
template<class TagStore>
bool
Cache<TagStore>::MemSidePort::recvTimingSnoop(PacketPtr pkt)
{
// handle snooping requests
assert(pkt->isRequest());
cache->snoopTiming(pkt);
return true;
}
template<class TagStore>
Tick
Cache<TagStore>::MemSidePort::recvAtomic(PacketPtr pkt)
Cache<TagStore>::MemSidePort::recvAtomicSnoop(PacketPtr pkt)
{
assert(pkt->isRequest());
// atomic snoop
@ -1622,7 +1640,7 @@ Cache<TagStore>::MemSidePort::recvAtomic(PacketPtr pkt)
template<class TagStore>
void
Cache<TagStore>::MemSidePort::recvFunctional(PacketPtr pkt)
Cache<TagStore>::MemSidePort::recvFunctionalSnoop(PacketPtr pkt)
{
assert(pkt->isRequest());
// functional snoop (note that in contrast to atomic we don't have

View file

@ -52,17 +52,3 @@ MessageSlavePort::recvAtomic(PacketPtr pkt)
name(), pkt->cmd.toString(), getMasterPort().name());
}
}
Tick
MessageMasterPort::recvAtomic(PacketPtr pkt)
{
if (pkt->cmd == MemCmd::MessageResp) {
// normally we would never see responses in recvAtomic, but
// since the timing port uses recvAtomic to implement
// recvTiming we have to deal with them here
return recvResponse(pkt);
} else {
panic("%s received unexpected atomic command %s from %s.\n",
name(), pkt->cmd.toString(), getSlavePort().name());
}
}

View file

@ -82,11 +82,7 @@ class MessageMasterPort : public QueuedMasterPort
virtual ~MessageMasterPort()
{}
void recvFunctional(PacketPtr pkt) { assert(false); }
Tick recvAtomic(PacketPtr pkt);
bool recvTiming(PacketPtr pkt) { recvAtomic(pkt); return true; }
bool recvTiming(PacketPtr pkt) { recvResponse(pkt); return true; }
protected:

View file

@ -104,7 +104,7 @@ PacketQueue::schedSendEvent(Tick when)
}
void
PacketQueue::schedSendTiming(PacketPtr pkt, Tick when)
PacketQueue::schedSendTiming(PacketPtr pkt, Tick when, bool send_as_snoop)
{
assert(when > curTick());
@ -114,14 +114,14 @@ PacketQueue::schedSendTiming(PacketPtr pkt, Tick when)
// note that currently we ignore a potentially outstanding retry
// and could in theory put a new packet at the head of the
// transmit list before retrying the existing packet
transmitList.push_front(DeferredPacket(when, pkt));
transmitList.push_front(DeferredPacket(when, pkt, send_as_snoop));
schedSendEvent(when);
return;
}
// list is non-empty and this belongs at the end
if (when >= transmitList.back().tick) {
transmitList.push_back(DeferredPacket(when, pkt));
transmitList.push_back(DeferredPacket(when, pkt, send_as_snoop));
return;
}
@ -130,7 +130,7 @@ PacketQueue::schedSendTiming(PacketPtr pkt, Tick when)
++i; // already checked for insertion at front
while (i != transmitList.end() && when >= i->tick)
++i;
transmitList.insert(i, DeferredPacket(when, pkt));
transmitList.insert(i, DeferredPacket(when, pkt, send_as_snoop));
}
void PacketQueue::trySendTiming()
@ -143,7 +143,10 @@ void PacketQueue::trySendTiming()
transmitList.pop_front();
// attempt to send the packet and remember the outcome
waitingOnRetry = !port.sendTiming(dp.pkt);
if (!dp.sendAsSnoop)
waitingOnRetry = !port.sendTiming(dp.pkt);
else
waitingOnRetry = !port.sendTimingSnoop(dp.pkt);
if (waitingOnRetry) {
// put the packet back at the front of the list (packet should

View file

@ -70,8 +70,9 @@ class PacketQueue
public:
Tick tick; ///< The tick when the packet is ready to transmit
PacketPtr pkt; ///< Pointer to the packet to transmit
DeferredPacket(Tick t, PacketPtr p)
: tick(t), pkt(p)
bool sendAsSnoop; ///< Should it be sent as a snoop or not
DeferredPacket(Tick t, PacketPtr p, bool send_as_snoop)
: tick(t), pkt(p), sendAsSnoop(send_as_snoop)
{}
};
@ -196,8 +197,9 @@ class PacketQueue
*
* @param pkt Packet to send
* @param when Absolute time (in ticks) to send packet
* @param send_as_snoop Send the packet as a snoop or not
*/
void schedSendTiming(PacketPtr pkt, Tick when);
void schedSendTiming(PacketPtr pkt, Tick when, bool send_as_snoop = false);
/**
* Used by a port to notify the queue that a retry was received
@ -215,4 +217,4 @@ class PacketQueue
unsigned int drain(Event *de);
};
#endif // __MEM_TPORT_HH__
#endif // __MEM_PACKET_QUEUE_HH__

View file

@ -104,7 +104,19 @@ MasterPort::peerBlockSize() const
return _slavePort->deviceBlockSize();
}
void
Tick
MasterPort::sendAtomic(PacketPtr pkt)
{
return _slavePort->recvAtomic(pkt);
}
void
MasterPort::sendFunctional(PacketPtr pkt)
{
return _slavePort->recvFunctional(pkt);
}
void
MasterPort::printAddr(Addr a)
{
Request req(a, 1, 0, Request::funcMasterId);
@ -155,3 +167,15 @@ SlavePort::isConnected() const
{
return _masterPort != NULL;
}
Tick
SlavePort::sendAtomicSnoop(PacketPtr pkt)
{
return _masterPort->recvAtomicSnoop(pkt);
}
void
SlavePort::sendFunctionalSnoop(PacketPtr pkt)
{
return _masterPort->recvFunctionalSnoop(pkt);
}

View file

@ -115,28 +115,35 @@ class Port
/** These functions are protected because they should only be
* called by a peer port, never directly by any outside object. */
/** Called to recive a timing call from the peer port. */
/**
* Receive a timing request or response packet from the peer port.
*/
virtual bool recvTiming(PacketPtr pkt) = 0;
/** Called to recive a atomic call from the peer port. */
virtual Tick recvAtomic(PacketPtr pkt) = 0;
/** Called to recive a functional call from the peer port. */
virtual void recvFunctional(PacketPtr pkt) = 0;
/**
* Receive a timing snoop request or snoop response packet from
* the peer port.
*/
virtual bool recvTimingSnoop(PacketPtr pkt)
{
panic("%s was not expecting a timing snoop\n", name());
return false;
}
/**
* Called by a peer port if sendTiming was unsuccesful, and had to
* wait.
* Called by a peer port if sendTiming or sendTimingSnoop was
* unsuccesful, and had to wait.
*/
virtual void recvRetry() = 0;
public:
/**
* Attempt to send a timing packet to the peer port by calling its
* receive function. If the send does not succeed, as indicated by
* the return value, then the sender must wait for a recvRetry at
* which point it can re-issue a sendTiming.
* Attempt to send a timing request or response packet to the peer
* port by calling its receive function. If the send does not
* succeed, as indicated by the return value, then the sender must
* wait for a recvRetry at which point it can re-issue a
* sendTiming.
*
* @param pkt Packet to send.
*
@ -145,31 +152,24 @@ class Port
bool sendTiming(PacketPtr pkt) { return peer->recvTiming(pkt); }
/**
* Send a retry to a peer port that previously attempted a sendTiming
* which was unsuccessful.
* Attempt to send a timing snoop request or snoop response packet
* to the peer port by calling its receive function. If the send
* does not succeed, as indicated by the return value, then the
* sender must wait for a recvRetry at which point it can re-issue
* a sendTimingSnoop.
*
* @param pkt Packet to send.
*
* @return If the send was succesful or not.
*/
bool sendTimingSnoop(PacketPtr pkt) { return peer->recvTimingSnoop(pkt); }
/**
* Send a retry to a peer port that previously attempted a
* sendTiming or sendTimingSnoop which was unsuccessful.
*/
void sendRetry() { return peer->recvRetry(); }
/**
* Send an atomic packet, where the data is moved and the state
* is updated in zero time, without interleaving with other
* memory accesses.
*
* @param pkt Packet to send.
*
* @return Estimated latency of access.
*/
Tick sendAtomic(PacketPtr pkt) { return peer->recvAtomic(pkt); }
/**
* Send a functional packet, where the data is instantly updated
* everywhere in the memory system, without affecting the current
* state of any block or moving the block.
*
* @param pkt Packet to send.
*/
void sendFunctional(PacketPtr pkt) { return peer->recvFunctional(pkt); }
};
/** Forward declaration */
@ -197,6 +197,43 @@ class MasterPort : public Port
SlavePort& getSlavePort() const;
bool isConnected() const;
/**
* Send an atomic request packet, where the data is moved and the
* state is updated in zero time, without interleaving with other
* memory accesses.
*
* @param pkt Packet to send.
*
* @return Estimated latency of access.
*/
Tick sendAtomic(PacketPtr pkt);
/**
* Send a functional request packet, where the data is instantly
* updated everywhere in the memory system, without affecting the
* current state of any block or moving the block.
*
* @param pkt Packet to send.
*/
void sendFunctional(PacketPtr pkt);
/**
* Receive an atomic snoop request packet from the slave port.
*/
virtual Tick recvAtomicSnoop(PacketPtr pkt)
{
panic("%s was not expecting an atomic snoop\n", name());
return 0;
}
/**
* Receive a functional snoop request packet from the slave port.
*/
virtual void recvFunctionalSnoop(PacketPtr pkt)
{
panic("%s was not expecting a functional snoop\n", name());
}
/**
* Called to receive an address range change from the peer slave
* port. the default implementation ignored the change and does
@ -256,6 +293,36 @@ class SlavePort : public Port
MasterPort& getMasterPort() const;
bool isConnected() const;
/**
* Send an atomic snoop request packet, where the data is moved
* and the state is updated in zero time, without interleaving
* with other memory accesses.
*
* @param pkt Snoop packet to send.
*
* @return Estimated latency of access.
*/
Tick sendAtomicSnoop(PacketPtr pkt);
/**
* Send a functional snoop request packet, where the data is
* instantly updated everywhere in the memory system, without
* affecting the current state of any block or moving the block.
*
* @param pkt Snoop packet to send.
*/
void sendFunctionalSnoop(PacketPtr pkt);
/**
* Receive an atomic request packet from the master port.
*/
virtual Tick recvAtomic(PacketPtr pkt) = 0;
/**
* Receive a functional request packet from the master port.
*/
virtual void recvFunctional(PacketPtr pkt) = 0;
/**
* Called by a peer port in order to determine the block size of
* the owner of this port.

View file

@ -132,13 +132,6 @@ RubyPort::M5Port::M5Port(const std::string &_name, RubyPort *_port,
DPRINTF(RubyPort, "creating slave port on ruby sequencer %s\n", _name);
}
Tick
RubyPort::PioPort::recvAtomic(PacketPtr pkt)
{
panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
return 0;
}
Tick
RubyPort::M5Port::recvAtomic(PacketPtr pkt)
{
@ -662,10 +655,11 @@ RubyPort::M5Port::hitCallback(PacketPtr pkt)
}
bool
RubyPort::M5Port::sendNextCycle(PacketPtr pkt)
RubyPort::M5Port::sendNextCycle(PacketPtr pkt, bool send_as_snoop)
{
//minimum latency, must be > 0
queue.schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
queue.schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()),
send_as_snoop);
return true;
}
@ -706,7 +700,8 @@ RubyPort::ruby_eviction_callback(const Address& address)
for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) {
if ((*p)->getMasterPort().isSnooping()) {
Packet *pkt = new Packet(&req, MemCmd::InvalidationReq, -1);
(*p)->sendNextCycle(pkt);
// send as a snoop request
(*p)->sendNextCycle(pkt, true);
}
}
}

View file

@ -71,7 +71,7 @@ class RubyPort : public MemObject
public:
M5Port(const std::string &_name, RubyPort *_port,
RubySystem*_system, bool _access_phys_mem);
bool sendNextCycle(PacketPtr pkt);
bool sendNextCycle(PacketPtr pkt, bool send_as_snoop = false);
void hitCallback(PacketPtr pkt);
void evictionCallback(const Address& address);
unsigned deviceBlockSize() const;
@ -110,8 +110,6 @@ class RubyPort : public MemObject
protected:
virtual bool recvTiming(PacketPtr pkt);
virtual Tick recvAtomic(PacketPtr pkt);
virtual void recvFunctional(PacketPtr pkt) { }
};
friend class PioPort;

View file

@ -92,10 +92,6 @@ class System : public MemObject
{ panic("SystemPort does not receive timing!\n"); return false; }
void recvRetry()
{ panic("SystemPort does not expect retry!\n"); }
Tick recvAtomic(PacketPtr pkt)
{ panic("SystemPort does not receive atomic!\n"); return 0; }
void recvFunctional(PacketPtr pkt)
{ panic("SystemPort does not receive functional!\n"); }
};
SystemPort _systemPort;