From dccca0d3a9c985972d3d603190e62899d03825e8 Mon Sep 17 00:00:00 2001 From: Andreas Hansson Date: Sat, 14 Apr 2012 05:45:07 -0400 Subject: [PATCH] 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. --- src/arch/x86/pagetable_walker.cc | 76 ++- src/arch/x86/pagetable_walker.hh | 9 +- src/cpu/base.cc | 19 +- src/cpu/base.hh | 4 +- src/cpu/inorder/cpu.cc | 9 +- src/cpu/inorder/cpu.hh | 3 + src/cpu/o3/cpu.cc | 21 +- src/cpu/o3/cpu.hh | 2 + src/cpu/o3/lsq.hh | 2 + src/cpu/o3/lsq_impl.hh | 37 +- src/cpu/simple/atomic.hh | 2 +- src/cpu/simple/timing.cc | 17 +- src/cpu/simple/timing.hh | 5 + .../directedtest/RubyDirectedTester.cc | 7 - .../directedtest/RubyDirectedTester.hh | 2 - src/cpu/testers/memtest/memtest.cc | 28 +- src/cpu/testers/memtest/memtest.hh | 6 +- src/cpu/testers/networktest/networktest.cc | 27 +- src/cpu/testers/networktest/networktest.hh | 4 - src/cpu/testers/rubytest/RubyTester.cc | 7 - src/cpu/testers/rubytest/RubyTester.hh | 2 - src/dev/io_device.cc | 15 +- src/dev/io_device.hh | 34 +- src/mem/bridge.cc | 19 - src/mem/bridge.hh | 8 - src/mem/bus.cc | 502 +++++++++++------- src/mem/bus.hh | 135 ++++- src/mem/cache/base.hh | 2 +- src/mem/cache/cache.hh | 8 +- src/mem/cache/cache_impl.hh | 60 ++- src/mem/mport.cc | 14 - src/mem/mport.hh | 6 +- src/mem/packet_queue.cc | 13 +- src/mem/packet_queue.hh | 10 +- src/mem/port.cc | 26 +- src/mem/port.hh | 127 +++-- src/mem/ruby/system/RubyPort.cc | 15 +- src/mem/ruby/system/RubyPort.hh | 4 +- src/sim/system.hh | 4 - 39 files changed, 779 insertions(+), 512 deletions(-) diff --git a/src/arch/x86/pagetable_walker.cc b/src/arch/x86/pagetable_walker.cc index 87505f4df..c07af0c83 100644 --- a/src/arch/x86/pagetable_walker.cc +++ b/src/arch/x86/pagetable_walker.cc @@ -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(pkt->senderState); - pkt->senderState = senderState->saved; - WalkerState * senderWalk = senderState->senderWalk; - bool walkComplete = senderWalk->recvPacket(pkt); - delete senderState; - if (walkComplete) { - std::list::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(pkt->senderState); + pkt->senderState = senderState->saved; + WalkerState * senderWalk = senderState->senderWalk; + bool walkComplete = senderWalk->recvPacket(pkt); + delete senderState; + if (walkComplete) { + std::list::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)) { diff --git a/src/arch/x86/pagetable_walker.hh b/src/arch/x86/pagetable_walker.hh index bf7a9e615..3cc20b6cd 100644 --- a/src/arch/x86/pagetable_walker.hh +++ b/src/arch/x86/pagetable_walker.hh @@ -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; } }; diff --git a/src/cpu/base.cc b/src/cpu/base.cc index d01dcbef3..edbec8c80 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -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. } diff --git a/src/cpu/base.hh b/src/cpu/base.hh index 3fb0f648b..f94c5e0a4 100644 --- a/src/cpu/base.hh +++ b/src/cpu/base.hh @@ -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); }; diff --git a/src/cpu/inorder/cpu.cc b/src/cpu/inorder/cpu.cc index 8dab82d71..04176c54f 100644 --- a/src/cpu/inorder/cpu.cc +++ b/src/cpu/inorder/cpu.cc @@ -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; } diff --git a/src/cpu/inorder/cpu.hh b/src/cpu/inorder/cpu.hh index d8fe5c057..06d733d85 100644 --- a/src/cpu/inorder/cpu.hh +++ b/src/cpu/inorder/cpu.hh @@ -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 */ diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index bfc9438d3..fe70c3fcf 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -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 bool FullO3CPU::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 bool FullO3CPU::DcachePort::recvTiming(PacketPtr pkt) { + assert(pkt->isResponse()); return lsq->recvTiming(pkt); } +template +bool +FullO3CPU::DcachePort::recvTimingSnoop(PacketPtr pkt) +{ + assert(pkt->isRequest()); + return lsq->recvTimingSnoop(pkt); +} + template void FullO3CPU::DcachePort::recvRetry() diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh index 493730458..be51f415f 100644 --- a/src/cpu/o3/cpu.hh +++ b/src/cpu/o3/cpu.hh @@ -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(); diff --git a/src/cpu/o3/lsq.hh b/src/cpu/o3/lsq.hh index b821dd3f9..dac5fab18 100644 --- a/src/cpu/o3/lsq.hh +++ b/src/cpu/o3/lsq.hh @@ -299,6 +299,8 @@ class LSQ { */ bool recvTiming(PacketPtr pkt); + bool recvTimingSnoop(PacketPtr pkt); + /** The CPU pointer. */ O3CPU *cpu; diff --git a/src/cpu/o3/lsq_impl.hh b/src/cpu/o3/lsq_impl.hh index 02758f212..c2f410e37 100644 --- a/src/cpu/o3/lsq_impl.hh +++ b/src/cpu/o3/lsq_impl.hh @@ -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 bool LSQ::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()); - - // 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); - } + thread[pkt->req->threadId()].completeDataAccess(pkt); + return true; +} + +template +bool +LSQ::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; } diff --git a/src/cpu/simple/atomic.hh b/src/cpu/simple/atomic.hh index 3e6238f7d..e88c93cce 100644 --- a/src/cpu/simple/atomic.hh +++ b/src/cpu/simple/atomic.hh @@ -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; diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc index f661756da..d52003f19 100644 --- a/src/cpu/simple/timing.cc +++ b/src/cpu/simple/timing.cc @@ -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; } diff --git a/src/cpu/simple/timing.hh b/src/cpu/simple/timing.hh index e0c5c89f7..4c23391d9 100644 --- a/src/cpu/simple/timing.hh +++ b/src/cpu/simple/timing.hh @@ -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 diff --git a/src/cpu/testers/directedtest/RubyDirectedTester.cc b/src/cpu/testers/directedtest/RubyDirectedTester.cc index bfdd28e08..a6dc257d5 100644 --- a/src/cpu/testers/directedtest/RubyDirectedTester.cc +++ b/src/cpu/testers/directedtest/RubyDirectedTester.cc @@ -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) { diff --git a/src/cpu/testers/directedtest/RubyDirectedTester.hh b/src/cpu/testers/directedtest/RubyDirectedTester.hh index cb207ff80..0965fb786 100644 --- a/src/cpu/testers/directedtest/RubyDirectedTester.hh +++ b/src/cpu/testers/directedtest/RubyDirectedTester.hh @@ -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; diff --git a/src/cpu/testers/memtest/memtest.cc b/src/cpu/testers/memtest/memtest.cc index 07cdf73a6..7e34c2833 100644 --- a/src/cpu/testers/memtest/memtest.cc +++ b/src/cpu/testers/memtest/memtest.cc @@ -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() { diff --git a/src/cpu/testers/memtest/memtest.hh b/src/cpu/testers/memtest/memtest.hh index d179fa530..8dccfdc80 100644 --- a/src/cpu/testers/memtest/memtest.hh +++ b/src/cpu/testers/memtest/memtest.hh @@ -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(); }; diff --git a/src/cpu/testers/networktest/networktest.cc b/src/cpu/testers/networktest/networktest.cc index c4d44b1ab..45a414539 100644 --- a/src/cpu/testers/networktest/networktest.cc +++ b/src/cpu/testers/networktest/networktest.cc @@ -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() { diff --git a/src/cpu/testers/networktest/networktest.hh b/src/cpu/testers/networktest/networktest.hh index 21984f45d..36d311aa8 100644 --- a/src/cpu/testers/networktest/networktest.hh +++ b/src/cpu/testers/networktest/networktest.hh @@ -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(); }; diff --git a/src/cpu/testers/rubytest/RubyTester.cc b/src/cpu/testers/rubytest/RubyTester.cc index 657167394..67f4c372a 100644 --- a/src/cpu/testers/rubytest/RubyTester.cc +++ b/src/cpu/testers/rubytest/RubyTester.cc @@ -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) { diff --git a/src/cpu/testers/rubytest/RubyTester.hh b/src/cpu/testers/rubytest/RubyTester.hh index 82698f201..aaf609e1e 100644 --- a/src/cpu/testers/rubytest/RubyTester.hh +++ b/src/cpu/testers/rubytest/RubyTester.hh @@ -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 diff --git a/src/dev/io_device.cc b/src/dev/io_device.cc index 2937c66b1..e1fc28949 100644 --- a/src/dev/io_device.cc +++ b/src/dev/io_device.cc @@ -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; diff --git a/src/dev/io_device.hh b/src/dev/io_device.hh index 9bb2c3d87..b113f4379 100644 --- a/src/dev/io_device.hh +++ b/src/dev/io_device.hh @@ -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) + + virtual Tick recvAtomicSnoop(PacketPtr pkt) { - if (recvSnoops) return; + if (!recvSnoops) + panic("%s was not expecting a snoop\n", name()); + return 0; + } - panic("dma port shouldn't be used for pio access."); + virtual void recvFunctionalSnoop(PacketPtr pkt) + { + if (!recvSnoops) + panic("%s was not expecting a snoop\n", name()); } virtual void recvRetry() ; diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc index ebb37e792..4dcb2a537 100644 --- a/src/mem/bridge.cc +++ b/src/mem/bridge.cc @@ -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) { diff --git a/src/mem/bridge.hh b/src/mem/bridge.hh index e7dbc0a28..87b327ca3 100644 --- a/src/mem/bridge.hh +++ b/src/mem/bridge.hh @@ -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. */ diff --git a/src/mem/bus.cc b/src/mem/bus.cc index c89455f02..eb26e268b 100644 --- a/src/mem/bus.cc +++ b/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::iterator intIter; - - // 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); - } + std::vector::iterator p; + + // 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::recvTiming(PacketPtr pkt) +Bus::isOccupied(PacketPtr pkt, Port* port) { - // called for both requests and responses + // 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; +} - // get the source id and port +bool +Bus::recvTiming(PacketPtr pkt) +{ + // 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]; - - // 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); + // 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]; + + // 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); - - 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); - } + assert(pkt->getDest() == Packet::Broadcast); + + // 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]; - - // 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. + // 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); + + 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); + + 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]; - // Packet was successfully sent. - // Also take care of retries + 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 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 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 +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,48 +620,51 @@ 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; + // 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); +} - // 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); +void +Bus::recvFunctional(PacketPtr pkt) +{ + if (!pkt->isPrint()) { + // don't do DPRINTFs on PrintReq as it clutters up the output + DPRINTF(Bus, + "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", + pkt->getSrc(), pkt->getDest(), pkt->getAddr(), + pkt->cmdString()); } - // 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; - } + // we should always see a request routed based on the address + assert(pkt->getDest() == Packet::Broadcast); + assert(pkt->isRequest()); - // why do we have this packet field and the return value both??? - pkt->finishTime = curTick() + response_latency; - return response_latency; + // 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); + } } -/** Function called by the port when the bus is receiving a Functional - * transaction.*/ void -Bus::recvFunctional(PacketPtr pkt) +Bus::recvFunctionalSnoop(PacketPtr pkt) { if (!pkt->isPrint()) { // don't do DPRINTFs on PrintReq as it clutters up the output DPRINTF(Bus, - "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", + "recvFunctionalSnoop: packet src %d dest %d addr 0x%x cmd %s\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); } @@ -526,10 +673,13 @@ 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 + 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 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 (exclude_slave_port_id == INVALID_PORT_ID || + p->getId() != exclude_slave_port_id) + p->sendFunctionalSnoop(pkt); - // 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::const_iterator intIter; for (intIter = slavePorts.begin(); intIter != slavePorts.end(); intIter++) - if (*intIter != NULL && (*intIter)->getId() != id) - (*intIter)->sendRangeChange(); + (*intIter)->sendRangeChange(); inRecvRangeChange.erase(id); } diff --git a/src/mem/bus.hh b/src/mem/bus.hh index e79e9df9e..8a0676353 100644 --- a/src/mem/bus.hh +++ b/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::iterator SnoopIter; std::vector 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 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 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 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 slavePorts; std::vector masterPorts; diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh index 47cbaf7a0..b24e595b7 100644 --- a/src/mem/cache/base.hh +++ b/src/mem/cache/base.hh @@ -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: diff --git a/src/mem/cache/cache.hh b/src/mem/cache/cache.hh index e745529a7..a774a356c 100644 --- a/src/mem/cache/cache.hh +++ b/src/mem/cache/cache.hh @@ -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(); } diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh index 3525e0777..fa6f6c860 100644 --- a/src/mem/cache/cache_impl.hh +++ b/src/mem/cache/cache_impl.hh @@ -785,7 +785,7 @@ Cache::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::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::snoopTiming(PacketPtr pkt) handleSnoop(pkt, blk, true, false, false); } +template +bool +Cache::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 Tick @@ -1483,7 +1492,7 @@ Cache::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 bool Cache::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::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 +bool +Cache::MemSidePort::recvTimingSnoop(PacketPtr pkt) +{ + // handle snooping requests + assert(pkt->isRequest()); + cache->snoopTiming(pkt); return true; } template Tick -Cache::MemSidePort::recvAtomic(PacketPtr pkt) +Cache::MemSidePort::recvAtomicSnoop(PacketPtr pkt) { assert(pkt->isRequest()); // atomic snoop @@ -1622,7 +1640,7 @@ Cache::MemSidePort::recvAtomic(PacketPtr pkt) template void -Cache::MemSidePort::recvFunctional(PacketPtr pkt) +Cache::MemSidePort::recvFunctionalSnoop(PacketPtr pkt) { assert(pkt->isRequest()); // functional snoop (note that in contrast to atomic we don't have diff --git a/src/mem/mport.cc b/src/mem/mport.cc index 9af394d27..5678f87d7 100644 --- a/src/mem/mport.cc +++ b/src/mem/mport.cc @@ -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()); - } -} diff --git a/src/mem/mport.hh b/src/mem/mport.hh index 664acc559..7f9e8f4e0 100644 --- a/src/mem/mport.hh +++ b/src/mem/mport.hh @@ -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: diff --git a/src/mem/packet_queue.cc b/src/mem/packet_queue.cc index 29914bea2..bffae5674 100644 --- a/src/mem/packet_queue.cc +++ b/src/mem/packet_queue.cc @@ -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 diff --git a/src/mem/packet_queue.hh b/src/mem/packet_queue.hh index ac868802b..fbcd7d449 100644 --- a/src/mem/packet_queue.hh +++ b/src/mem/packet_queue.hh @@ -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__ diff --git a/src/mem/port.cc b/src/mem/port.cc index 3305541c7..92c6aaab8 100644 --- a/src/mem/port.cc +++ b/src/mem/port.cc @@ -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); +} diff --git a/src/mem/port.hh b/src/mem/port.hh index 80bb3b085..61c92d8e4 100644 --- a/src/mem/port.hh +++ b/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,30 +152,23 @@ 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. - */ - 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. + * 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 Estimated latency of access. - */ - Tick sendAtomic(PacketPtr pkt) { return peer->recvAtomic(pkt); } + * @return If the send was succesful or not. + */ + bool sendTimingSnoop(PacketPtr pkt) { return peer->recvTimingSnoop(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. + * Send a retry to a peer port that previously attempted a + * sendTiming or sendTimingSnoop which was unsuccessful. */ - void sendFunctional(PacketPtr pkt) { return peer->recvFunctional(pkt); } + void sendRetry() { return peer->recvRetry(); } }; @@ -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. diff --git a/src/mem/ruby/system/RubyPort.cc b/src/mem/ruby/system/RubyPort.cc index 0cdb919b1..74a60f863 100644 --- a/src/mem/ruby/system/RubyPort.cc +++ b/src/mem/ruby/system/RubyPort.cc @@ -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); } } } diff --git a/src/mem/ruby/system/RubyPort.hh b/src/mem/ruby/system/RubyPort.hh index 553614021..f41c98f55 100644 --- a/src/mem/ruby/system/RubyPort.hh +++ b/src/mem/ruby/system/RubyPort.hh @@ -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; diff --git a/src/sim/system.hh b/src/sim/system.hh index 7b5ba998e..f7831d09c 100644 --- a/src/sim/system.hh +++ b/src/sim/system.hh @@ -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; -- 2.30.2