From 4931414ca79b97ee64a958c4dd4ed1cafc44b4bd Mon Sep 17 00:00:00 2001 From: Andreas Hansson Date: Thu, 30 May 2013 12:54:05 -0400 Subject: cpu: Block traffic generator when requests have to retry This patch changes the queued port for a conventional master port and stalls the traffic generator when requests are not immediately accepted. This is a first step to allowing elasticity in the injection of requests. The patch also adds stats for the sent packets and retries, and slightly changes how the nextPacketTick and getNextPacket interact. The advancing of the trace is now moved to getNextPacket and nextPacketTick is only responsible for answering the question when the next packet should be sent. --- src/cpu/testers/traffic_gen/generators.cc | 79 +++++++++--------- src/cpu/testers/traffic_gen/generators.hh | 15 ++-- src/cpu/testers/traffic_gen/traffic_gen.cc | 128 +++++++++++++++++++++++------ src/cpu/testers/traffic_gen/traffic_gen.hh | 54 ++++++++---- 4 files changed, 188 insertions(+), 88 deletions(-) (limited to 'src/cpu') diff --git a/src/cpu/testers/traffic_gen/generators.cc b/src/cpu/testers/traffic_gen/generators.cc index 4d1812cd4..f9556b2b3 100644 --- a/src/cpu/testers/traffic_gen/generators.cc +++ b/src/cpu/testers/traffic_gen/generators.cc @@ -100,20 +100,20 @@ LinearGen::getNextPacket() // increment the address nextAddr += blocksize; - return pkt; -} - -Tick -LinearGen::nextPacketTick() -{ // If we have reached the end of the address space, reset the // address to the start of the range - if (nextAddr + blocksize > endAddr) { + if (nextAddr > endAddr) { DPRINTF(TrafficGen, "Wrapping address to the start of " "the range\n"); nextAddr = startAddr; } + return pkt; +} + +Tick +LinearGen::nextPacketTick() const +{ // Check to see if we have reached the data limit. If dataLimit is // zero we do not have a data limit and therefore we will keep // generating requests for the entire residency in this state. @@ -162,7 +162,7 @@ RandomGen::getNextPacket() } Tick -RandomGen::nextPacketTick() +RandomGen::nextPacketTick() const { // Check to see if we have reached the data limit. If dataLimit is // zero we do not have a data limit and therefore we will keep @@ -217,38 +217,19 @@ TraceGen::InputStream::read(TraceElement& element) } Tick -TraceGen::nextPacketTick() { - if (traceComplete) +TraceGen::nextPacketTick() const +{ + if (traceComplete) { + DPRINTF(TrafficGen, "No next tick as trace is finished\n"); // We are at the end of the file, thus we have no more data in // the trace Return MaxTick to signal that there will be no // more transactions in this active period for the state. return MaxTick; - - - //Reset the nextElement to the default values - currElement = nextElement; - nextElement.clear(); - - // We need to look at the next line to calculate the next time an - // event occurs, or potentially return MaxTick to signal that - // nothing has to be done. - if (!trace.read(nextElement)) { - traceComplete = true; - return MaxTick; } - DPRINTF(TrafficGen, "currElement: %c addr %d size %d tick %d (%d)\n", - currElement.cmd.isRead() ? 'r' : 'w', - currElement.addr, - currElement.blocksize, - currElement.tick + tickOffset, - currElement.tick); - - DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n", - nextElement.cmd.isRead() ? 'r' : 'w', - nextElement.addr, - nextElement.blocksize, - nextElement.tick + tickOffset, + assert(nextElement.isValid()); + + DPRINTF(TrafficGen, "Next packet tick is %d\n", tickOffset + nextElement.tick); return tickOffset + nextElement.tick; @@ -261,17 +242,24 @@ TraceGen::enter() tickOffset = curTick(); // clear everything - nextElement.clear(); currElement.clear(); - traceComplete = false; + // read the first element in the file and set the complete flag + traceComplete = !trace.read(nextElement); } PacketPtr TraceGen::getNextPacket() { - // it is the responsibility of nextPacketTick to prevent the - // state graph from executing the state if it should not + // shift things one step forward + currElement = nextElement; + nextElement.clear(); + + // read the next element and set the complete flag + traceComplete = !trace.read(nextElement); + + // it is the responsibility of the traceComplete flag to ensure we + // always have a valid element here assert(currElement.isValid()); DPRINTF(TrafficGen, "TraceGen::getNextPacket: %c %d %d %d 0x%x\n", @@ -281,8 +269,19 @@ TraceGen::getNextPacket() currElement.tick, currElement.flags); - return getPacket(currElement.addr + addrOffset, currElement.blocksize, - currElement.cmd, currElement.flags); + PacketPtr pkt = getPacket(currElement.addr + addrOffset, + currElement.blocksize, + currElement.cmd, currElement.flags); + + if (!traceComplete) + DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n", + nextElement.cmd.isRead() ? 'r' : 'w', + nextElement.addr, + nextElement.blocksize, + nextElement.tick + tickOffset, + nextElement.tick); + + return pkt; } void diff --git a/src/cpu/testers/traffic_gen/generators.hh b/src/cpu/testers/traffic_gen/generators.hh index 5bcfc8e80..2b86afa22 100644 --- a/src/cpu/testers/traffic_gen/generators.hh +++ b/src/cpu/testers/traffic_gen/generators.hh @@ -126,7 +126,7 @@ class BaseGen * * @return next tick when a packet is available */ - virtual Tick nextPacketTick() = 0; + virtual Tick nextPacketTick() const = 0; }; @@ -146,7 +146,7 @@ class IdleGen : public BaseGen PacketPtr getNextPacket() { return NULL; } - Tick nextPacketTick() { return MaxTick; } + Tick nextPacketTick() const { return MaxTick; } }; /** @@ -192,7 +192,7 @@ class LinearGen : public BaseGen PacketPtr getNextPacket(); - Tick nextPacketTick(); + Tick nextPacketTick() const; private: @@ -269,7 +269,7 @@ class RandomGen : public BaseGen PacketPtr getNextPacket(); - Tick nextPacketTick(); + Tick nextPacketTick() const; private: @@ -415,12 +415,11 @@ class TraceGen : public BaseGen void exit(); /** - * Read a line of the trace file. Returns the raw tick - * when the next request should be generated. If the end - * of the file has been reached, it returns MaxTick to + * Returns the tick when the next request should be generated. If + * the end of the file has been reached, it returns MaxTick to * indicate that there will be no more requests. */ - Tick nextPacketTick(); + Tick nextPacketTick() const; private: diff --git a/src/cpu/testers/traffic_gen/traffic_gen.cc b/src/cpu/testers/traffic_gen/traffic_gen.cc index ea1a74e96..8916dcb8d 100644 --- a/src/cpu/testers/traffic_gen/traffic_gen.cc +++ b/src/cpu/testers/traffic_gen/traffic_gen.cc @@ -56,8 +56,12 @@ TrafficGen::TrafficGen(const TrafficGenParams* p) masterID(system->getMasterId(name())), configFile(p->config_file), nextTransitionTick(0), + nextPacketTick(0), port(name() + ".port", *this), - updateEvent(this) + retryPkt(NULL), + retryPktTick(0), + updateEvent(this), + drainManager(NULL) { } @@ -102,7 +106,9 @@ TrafficGen::initState() { // when not restoring from a checkpoint, make sure we kick things off if (system->isTimingMode()) { - schedule(updateEvent, nextEventTick()); + // call nextPacketTick on the state to advance it + nextPacketTick = states[currState]->nextPacketTick(); + schedule(updateEvent, std::min(nextPacketTick, nextTransitionTick)); } else { DPRINTF(TrafficGen, "Traffic generator is only active in timing mode\n"); @@ -112,9 +118,16 @@ TrafficGen::initState() unsigned int TrafficGen::drain(DrainManager *dm) { - // @todo we should also stop putting new requests in the queue and - // either interrupt the current state or wait for a transition - return port.drain(dm); + if (retryPkt == NULL) { + // shut things down + nextPacketTick = MaxTick; + nextTransitionTick = MaxTick; + deschedule(updateEvent); + return 0; + } else { + drainManager = dm; + return 1; + } } void @@ -123,18 +136,17 @@ TrafficGen::serialize(ostream &os) DPRINTF(Checkpoint, "Serializing TrafficGen\n"); // save ticks of the graph event if it is scheduled - Tick nextEvent = updateEvent.scheduled() ? - updateEvent.when() : 0; + Tick nextEvent = updateEvent.scheduled() ? updateEvent.when() : 0; - DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", - nextEvent); + DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", nextEvent); SERIALIZE_SCALAR(nextEvent); SERIALIZE_SCALAR(nextTransitionTick); - // @todo: also serialise the current state, figure out the best - // way to drain and restore + SERIALIZE_SCALAR(nextPacketTick); + + SERIALIZE_SCALAR(currState); } void @@ -148,28 +160,43 @@ TrafficGen::unserialize(Checkpoint* cp, const string& section) } UNSERIALIZE_SCALAR(nextTransitionTick); + + UNSERIALIZE_SCALAR(nextPacketTick); + + // @todo In the case of a stateful generator state such as the + // trace player we would also have to restore the position in the + // trace playback + UNSERIALIZE_SCALAR(currState); } void TrafficGen::update() { - // schedule next update event based on either the next execute - // tick or the next transition, which ever comes first - Tick nextEvent = nextEventTick(); - DPRINTF(TrafficGen, "Updating state graph, next event at %lld\n", - nextEvent); - schedule(updateEvent, nextEvent); - - // perform the update associated with the current update event - // if we have reached the time for the next state transition, then // perform the transition if (curTick() >= nextTransitionTick) { transition(); } else { - // we are still in the current state and should execute it + assert(curTick() >= nextPacketTick); + // get the next packet and try to send it PacketPtr pkt = states[currState]->getNextPacket(); - port.schedTimingReq(pkt, curTick()); + numPackets++; + if (!port.sendTimingReq(pkt)) { + retryPkt = pkt; + retryPktTick = curTick(); + } + } + + // if we are waiting for a retry, do not schedule any further + // events, in the case of a transition or a successful send, go + // ahead and determine when the next update should take place + if (retryPkt == NULL) { + // schedule next update event based on either the next execute + // tick or the next transition, which ever comes first + nextPacketTick = states[currState]->nextPacketTick(); + Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick); + DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick); + schedule(updateEvent, nextEventTick); } } @@ -340,10 +367,65 @@ TrafficGen::enterState(uint32_t newState) DPRINTF(TrafficGen, "Transition to state %d\n", newState); currState = newState; - nextTransitionTick += states[currState]->duration; + // we could have been delayed and not transitioned on the exact + // tick when we were supposed to (due to back pressure when + // sending a packet) + nextTransitionTick = curTick() + states[currState]->duration; states[currState]->enter(); } +void +TrafficGen::recvRetry() +{ + assert(retryPkt != NULL); + + DPRINTF(TrafficGen, "Received retry\n"); + numRetries++; + // attempt to send the packet, and if we are successful start up + // the machinery again + if (port.sendTimingReq(retryPkt)) { + retryPkt = NULL; + // remember how much delay was incurred due to back-pressure + // when sending the request + Tick delay = curTick() - retryPktTick; + retryPktTick = 0; + retryTicks += delay; + + if (drainManager == NULL) { + // packet is sent, so find out when the next one is due + nextPacketTick = states[currState]->nextPacketTick(); + Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick); + schedule(updateEvent, std::max(curTick(), nextEventTick)); + } else { + // shut things down + nextPacketTick = MaxTick; + nextTransitionTick = MaxTick; + drainManager->signalDrainDone(); + // Clear the drain event once we're done with it. + drainManager = NULL; + } + } +} + +void +TrafficGen::regStats() +{ + // Initialise all the stats + using namespace Stats; + + numPackets + .name(name() + ".numPackets") + .desc("Number of packets generated"); + + numRetries + .name(name() + ".numRetries") + .desc("Number of retries"); + + retryTicks + .name(name() + ".retryTicks") + .desc("Time spent waiting due to back-pressure (ticks)"); +} + bool TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt) { diff --git a/src/cpu/testers/traffic_gen/traffic_gen.hh b/src/cpu/testers/traffic_gen/traffic_gen.hh index c013109b7..0adcf781e 100644 --- a/src/cpu/testers/traffic_gen/traffic_gen.hh +++ b/src/cpu/testers/traffic_gen/traffic_gen.hh @@ -42,6 +42,7 @@ #define __CPU_TRAFFIC_GEN_TRAFFIC_GEN_HH__ #include "base/hashmap.hh" +#include "base/statistics.hh" #include "cpu/testers/traffic_gen/generators.hh" #include "mem/mem_object.hh" #include "mem/qport.hh" @@ -73,18 +74,6 @@ class TrafficGen : public MemObject */ void enterState(uint32_t newState); - /** - * Get the tick of the next event, either a new packet or a - * transition. - * - * @return tick of the next update event - */ - Tick nextEventTick() - { - return std::min(states[currState]->nextPacketTick(), - nextTransitionTick); - } - /** * Parse the config file and build the state map and * transition matrix. @@ -98,6 +87,12 @@ class TrafficGen : public MemObject */ void update(); + /** + * Receive a retry from the neighbouring port and attempt to + * resend the waiting packet. + */ + void recvRetry(); + /** Struct to represent a probabilistic transition during parsing. */ struct Transition { uint32_t from; @@ -124,6 +119,9 @@ class TrafficGen : public MemObject /** Time of next transition */ Tick nextTransitionTick; + /** Time of the next packet. */ + Tick nextPacketTick; + /** State transition matrix */ std::vector > transitionMatrix; @@ -133,31 +131,50 @@ class TrafficGen : public MemObject /** Map of generator states */ m5::hash_map states; - /** Queued master port */ - class TrafficGenPort : public QueuedMasterPort + /** Master port specialisation for the traffic generator */ + class TrafficGenPort : public MasterPort { public: - TrafficGenPort(const std::string& name, TrafficGen& _owner) - : QueuedMasterPort(name, &_owner, queue), queue(_owner, *this) + TrafficGenPort(const std::string& name, TrafficGen& traffic_gen) + : MasterPort(name, &traffic_gen), trafficGen(traffic_gen) { } protected: + void recvRetry() { trafficGen.recvRetry(); } + bool recvTimingResp(PacketPtr pkt); private: - MasterPacketQueue queue; + TrafficGen& trafficGen; }; /** The instance of master port used by the traffic generator. */ TrafficGenPort port; + /** Packet waiting to be sent. */ + PacketPtr retryPkt; + + /** Tick when the stalled packet was meant to be sent. */ + Tick retryPktTick; + /** Event for scheduling updates */ EventWrapper updateEvent; + /** Manager to signal when drained */ + DrainManager* drainManager; + + /** Count the number of generated packets. */ + Stats::Scalar numPackets; + + /** Count the number of retries. */ + Stats::Scalar numRetries; + + /** Count the time incurred from back-pressure. */ + Stats::Scalar retryTicks; public: @@ -178,6 +195,9 @@ class TrafficGen : public MemObject void unserialize(Checkpoint* cp, const std::string& section); + /** Register statistics */ + void regStats(); + }; #endif //__CPU_TRAFFIC_GEN_TRAFFIC_GEN_HH__ -- cgit v1.2.3