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:
parent
b9bc530ad2
commit
dccca0d3a9
|
@ -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)) {
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -299,6 +299,8 @@ class LSQ {
|
|||
*/
|
||||
bool recvTiming(PacketPtr pkt);
|
||||
|
||||
bool recvTimingSnoop(PacketPtr pkt);
|
||||
|
||||
/** The CPU pointer. */
|
||||
O3CPU *cpu;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() ;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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. */
|
||||
|
|
498
src/mem/bus.cc
498
src/mem/bus.cc
|
@ -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);
|
||||
}
|
||||
|
|
135
src/mem/bus.hh
135
src/mem/bus.hh
|
@ -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;
|
||||
|
|
2
src/mem/cache/base.hh
vendored
2
src/mem/cache/base.hh
vendored
|
@ -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:
|
||||
|
|
8
src/mem/cache/cache.hh
vendored
8
src/mem/cache/cache.hh
vendored
|
@ -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(); }
|
||||
|
|
60
src/mem/cache/cache_impl.hh
vendored
60
src/mem/cache/cache_impl.hh
vendored
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
135
src/mem/port.hh
135
src/mem/port.hh
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue