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.
This commit is contained in:
Andreas Hansson 2013-05-30 12:54:05 -04:00
parent c9c35da934
commit 4931414ca7
4 changed files with 187 additions and 87 deletions

View file

@ -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);
assert(nextElement.isValid());
DPRINTF(TrafficGen, "nextElement: %c addr %d size %d tick %d (%d)\n",
nextElement.cmd.isRead() ? 'r' : 'w',
nextElement.addr,
nextElement.blocksize,
nextElement.tick + tickOffset,
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

View file

@ -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:

View file

@ -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)
{

View file

@ -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<std::vector<double> > transitionMatrix;
@ -133,31 +131,50 @@ class TrafficGen : public MemObject
/** Map of generator states */
m5::hash_map<uint32_t, BaseGen*> 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<TrafficGen, &TrafficGen::update> 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__