sim: Move the draining interface into a separate base class

This patch moves the draining interface from SimObject to a separate
class that can be used by any object needing draining. However,
objects not visible to the Python code (i.e., objects not deriving
from SimObject) still depend on their parents informing them when to
drain. This patch also gets rid of the CountedDrainEvent (which isn't
really an event) and replaces it with a DrainManager.
This commit is contained in:
Andreas Sandberg 2012-11-02 11:32:01 -05:00
parent eb703a4b4e
commit b81a977e6a
64 changed files with 691 additions and 444 deletions

View file

@ -51,7 +51,7 @@
using namespace ArmISA;
TableWalker::TableWalker(const Params *p)
: MemObject(p), port(this, params()->sys), drainEvent(NULL),
: MemObject(p), port(this, params()->sys), drainManager(NULL),
tlb(NULL), currState(NULL), pending(false),
masterId(p->sys->getMasterId(name())),
numSquashable(p->num_squash_per_cycle),
@ -68,30 +68,30 @@ TableWalker::~TableWalker()
void
TableWalker::completeDrain()
{
if (drainEvent && stateQueueL1.empty() && stateQueueL2.empty() &&
if (drainManager && stateQueueL1.empty() && stateQueueL2.empty() &&
pendingQueue.empty()) {
changeState(Drained);
setDrainState(Drainable::Drained);
DPRINTF(Drain, "TableWalker done draining, processing drain event\n");
drainEvent->process();
drainEvent = NULL;
drainManager->signalDrainDone();
drainManager = NULL;
}
}
unsigned int
TableWalker::drain(Event *de)
TableWalker::drain(DrainManager *dm)
{
unsigned int count = port.drain(de);
unsigned int count = port.drain(dm);
if (stateQueueL1.empty() && stateQueueL2.empty() &&
pendingQueue.empty()) {
changeState(Drained);
setDrainState(Drainable::Drained);
DPRINTF(Drain, "TableWalker free, no need to drain\n");
// table walker is drained, but its ports may still need to be drained
return count;
} else {
drainEvent = de;
changeState(Draining);
drainManager = dm;
setDrainState(Drainable::Draining);
DPRINTF(Drain, "TableWalker not drained\n");
// return port drain count plus the table walker itself needs to drain
@ -101,9 +101,9 @@ TableWalker::drain(Event *de)
}
void
TableWalker::resume()
TableWalker::drainResume()
{
MemObject::resume();
Drainable::drainResume();
if ((params()->sys->getMemoryMode() == Enums::timing) && currState) {
delete currState;
currState = NULL;

View file

@ -364,7 +364,7 @@ class TableWalker : public MemObject
SnoopingDmaPort port;
/** If we're draining keep the drain event around until we're drained */
Event *drainEvent;
DrainManager *drainManager;
/** TLB that is initiating these table walks */
TLB *tlb;
@ -397,8 +397,8 @@ class TableWalker : public MemObject
/** Checks if all state is cleared and if so, completes drain */
void completeDrain();
virtual unsigned int drain(Event *de);
virtual void resume();
unsigned int drain(DrainManager *dm);
void drainResume();
virtual BaseMasterPort& getMasterPort(const std::string &if_name,
PortID idx = InvalidPortID);

View file

@ -619,7 +619,7 @@ FullO3CPU<Impl>::tick()
if (!tickEvent.scheduled()) {
if (_status == SwitchedOut ||
getState() == SimObject::Drained) {
getDrainState() == Drainable::Drained) {
DPRINTF(O3CPU, "Switched out!\n");
// increment stat
lastRunningCycle = curCycle();
@ -1077,7 +1077,7 @@ template <class Impl>
void
FullO3CPU<Impl>::serialize(std::ostream &os)
{
SimObject::State so_state = SimObject::getState();
Drainable::State so_state(getDrainState());
SERIALIZE_ENUM(so_state);
BaseCPU::serialize(os);
nameOut(os, csprintf("%s.tickEvent", name()));
@ -1100,7 +1100,7 @@ template <class Impl>
void
FullO3CPU<Impl>::unserialize(Checkpoint *cp, const std::string &section)
{
SimObject::State so_state;
Drainable::State so_state;
UNSERIALIZE_ENUM(so_state);
BaseCPU::unserialize(cp, section);
tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
@ -1120,7 +1120,7 @@ FullO3CPU<Impl>::unserialize(Checkpoint *cp, const std::string &section)
template <class Impl>
unsigned int
FullO3CPU<Impl>::drain(Event *drain_event)
FullO3CPU<Impl>::drain(DrainManager *drain_manager)
{
DPRINTF(O3CPU, "Switching out\n");
@ -1137,12 +1137,12 @@ FullO3CPU<Impl>::drain(Event *drain_event)
// Wake the CPU and record activity so everything can drain out if
// the CPU was not able to immediately drain.
if (getState() != SimObject::Drained) {
// A bit of a hack...set the drainEvent after all the drain()
if (getDrainState() != Drainable::Drained) {
// A bit of a hack...set the drainManager after all the drain()
// calls have been made, that way if all of the stages drain
// immediately, the signalDrained() function knows not to call
// process on the drain event.
drainEvent = drain_event;
drainManager = drain_manager;
wakeCPU();
activityRec.activity();
@ -1157,7 +1157,7 @@ FullO3CPU<Impl>::drain(Event *drain_event)
template <class Impl>
void
FullO3CPU<Impl>::resume()
FullO3CPU<Impl>::drainResume()
{
fetch.resume();
decode.resume();
@ -1165,7 +1165,7 @@ FullO3CPU<Impl>::resume()
iew.resume();
commit.resume();
changeState(SimObject::Running);
setDrainState(Drainable::Running);
if (_status == SwitchedOut)
return;
@ -1185,14 +1185,14 @@ FullO3CPU<Impl>::signalDrained()
if (tickEvent.scheduled())
tickEvent.squash();
changeState(SimObject::Drained);
setDrainState(Drainable::Drained);
BaseCPU::switchOut();
if (drainEvent) {
if (drainManager) {
DPRINTF(Drain, "CPU done draining, processing drain event\n");
drainEvent->process();
drainEvent = NULL;
drainManager->signalDrainDone();
drainManager = NULL;
}
}
assert(drainCount <= 5);

View file

@ -431,10 +431,10 @@ class FullO3CPU : public BaseO3CPU
/** Starts draining the CPU's pipeline of all instructions in
* order to stop all memory accesses. */
virtual unsigned int drain(Event *drain_event);
unsigned int drain(DrainManager *drain_manager);
/** Resumes execution after a drain. */
virtual void resume();
void drainResume();
/** Signals to this CPU that a stage has completed switching out. */
void signalDrained();
@ -730,8 +730,8 @@ class FullO3CPU : public BaseO3CPU
/** Pointer to the system. */
System *system;
/** Event to call process() on once draining has completed. */
Event *drainEvent;
/** DrainManager to notify when draining has completed. */
DrainManager *drainManager;
/** Counter of how many stages have completed draining. */
int drainCount;

View file

@ -123,7 +123,7 @@ AtomicSimpleCPU::~AtomicSimpleCPU()
void
AtomicSimpleCPU::serialize(ostream &os)
{
SimObject::State so_state = SimObject::getState();
Drainable::State so_state(getDrainState());
SERIALIZE_ENUM(so_state);
SERIALIZE_SCALAR(locked);
BaseSimpleCPU::serialize(os);
@ -134,15 +134,22 @@ AtomicSimpleCPU::serialize(ostream &os)
void
AtomicSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
SimObject::State so_state;
Drainable::State so_state;
UNSERIALIZE_ENUM(so_state);
UNSERIALIZE_SCALAR(locked);
BaseSimpleCPU::unserialize(cp, section);
tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
}
unsigned int
AtomicSimpleCPU::drain(DrainManager *drain_manager)
{
setDrainState(Drainable::Drained);
return 0;
}
void
AtomicSimpleCPU::resume()
AtomicSimpleCPU::drainResume()
{
if (_status == Idle || _status == SwitchedOut)
return;
@ -150,7 +157,7 @@ AtomicSimpleCPU::resume()
DPRINTF(SimpleCPU, "Resume\n");
assert(system->getMemoryMode() == Enums::atomic);
changeState(SimObject::Running);
setDrainState(Drainable::Running);
if (thread->status() == ThreadContext::Active) {
if (!tickEvent.scheduled())
schedule(tickEvent, nextCycle());
@ -161,7 +168,7 @@ AtomicSimpleCPU::resume()
void
AtomicSimpleCPU::switchOut()
{
assert(_status == Running || _status == Idle);
assert(_status == BaseSimpleCPU::Running || _status == Idle);
_status = SwitchedOut;
tickEvent.squash();
@ -180,13 +187,14 @@ AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
ThreadID size = threadContexts.size();
for (ThreadID i = 0; i < size; ++i) {
ThreadContext *tc = threadContexts[i];
if (tc->status() == ThreadContext::Active && _status != Running) {
_status = Running;
if (tc->status() == ThreadContext::Active &&
_status != BaseSimpleCPU::Running) {
_status = BaseSimpleCPU::Running;
schedule(tickEvent, nextCycle());
break;
}
}
if (_status != Running) {
if (_status != BaseSimpleCPU::Running) {
_status = Idle;
}
assert(threadContexts.size() == 1);
@ -212,7 +220,7 @@ AtomicSimpleCPU::activateContext(ThreadID thread_num, Cycles delay)
//Make sure ticks are still on multiples of cycles
schedule(tickEvent, clockEdge(delay));
_status = Running;
_status = BaseSimpleCPU::Running;
}
@ -227,7 +235,7 @@ AtomicSimpleCPU::suspendContext(ThreadID thread_num)
if (_status == Idle)
return;
assert(_status == Running);
assert(_status == BaseSimpleCPU::Running);
// tick event may not be scheduled if this gets called from inside
// an instruction's execution, e.g. "quiesce"

View file

@ -122,7 +122,9 @@ class AtomicSimpleCPU : public BaseSimpleCPU
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section);
virtual void resume();
unsigned int drain(DrainManager *drain_manager);
void drainResume();
void switchOut();
void takeOverFrom(BaseCPU *oldCPU);

View file

@ -92,7 +92,7 @@ TimingSimpleCPU::TimingSimpleCPU(TimingSimpleCPUParams *p)
{
_status = Idle;
changeState(SimObject::Running);
setDrainState(Drainable::Running);
system->totalNumInsts = 0;
}
@ -104,7 +104,7 @@ TimingSimpleCPU::~TimingSimpleCPU()
void
TimingSimpleCPU::serialize(ostream &os)
{
SimObject::State so_state = SimObject::getState();
Drainable::State so_state(getDrainState());
SERIALIZE_ENUM(so_state);
BaseSimpleCPU::serialize(os);
}
@ -112,29 +112,31 @@ TimingSimpleCPU::serialize(ostream &os)
void
TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
SimObject::State so_state;
Drainable::State so_state;
UNSERIALIZE_ENUM(so_state);
BaseSimpleCPU::unserialize(cp, section);
}
unsigned int
TimingSimpleCPU::drain(Event *drain_event)
TimingSimpleCPU::drain(DrainManager *drain_manager)
{
// TimingSimpleCPU is ready to drain if it's not waiting for
// an access to complete.
if (_status == Idle || _status == Running || _status == SwitchedOut) {
changeState(SimObject::Drained);
if (_status == Idle ||
_status == BaseSimpleCPU::Running ||
_status == SwitchedOut) {
setDrainState(Drainable::Drained);
return 0;
} else {
changeState(SimObject::Draining);
drainEvent = drain_event;
setDrainState(Drainable::Draining);
drainManager = drain_manager;
DPRINTF(Drain, "CPU not drained\n");
return 1;
}
}
void
TimingSimpleCPU::resume()
TimingSimpleCPU::drainResume()
{
DPRINTF(SimpleCPU, "Resume\n");
if (_status != SwitchedOut && _status != Idle) {
@ -146,13 +148,13 @@ TimingSimpleCPU::resume()
schedule(fetchEvent, nextCycle());
}
changeState(SimObject::Running);
setDrainState(Drainable::Running);
}
void
TimingSimpleCPU::switchOut()
{
assert(_status == Running || _status == Idle);
assert(_status == BaseSimpleCPU::Running || _status == Idle);
_status = SwitchedOut;
numCycles += curCycle() - previousCycle;
@ -172,13 +174,14 @@ TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
// running and schedule its tick event.
for (int i = 0; i < threadContexts.size(); ++i) {
ThreadContext *tc = threadContexts[i];
if (tc->status() == ThreadContext::Active && _status != Running) {
_status = Running;
if (tc->status() == ThreadContext::Active &&
_status != BaseSimpleCPU::Running) {
_status = BaseSimpleCPU::Running;
break;
}
}
if (_status != Running) {
if (_status != BaseSimpleCPU::Running) {
_status = Idle;
}
assert(threadContexts.size() == 1);
@ -197,7 +200,7 @@ TimingSimpleCPU::activateContext(ThreadID thread_num, Cycles delay)
assert(_status == Idle);
notIdleFraction++;
_status = Running;
_status = BaseSimpleCPU::Running;
// kick things off by initiating the fetch of the next instruction
schedule(fetchEvent, clockEdge(delay));
@ -215,7 +218,7 @@ TimingSimpleCPU::suspendContext(ThreadID thread_num)
if (_status == Idle)
return;
assert(_status == Running);
assert(_status == BaseSimpleCPU::Running);
// just change status to Idle... if status != Running,
// completeInst() will not initiate fetch of next instruction.
@ -330,7 +333,7 @@ TimingSimpleCPU::translationFault(Fault fault)
postExecute();
if (getState() == SimObject::Draining) {
if (getDrainState() == Drainable::Draining) {
advancePC(fault);
completeDrain();
} else {
@ -511,7 +514,7 @@ TimingSimpleCPU::writeMem(uint8_t *data, unsigned size,
void
TimingSimpleCPU::finishTranslation(WholeTranslationState *state)
{
_status = Running;
_status = BaseSimpleCPU::Running;
if (state->getFault() != NoFault) {
if (state->isPrefetch()) {
@ -552,7 +555,7 @@ TimingSimpleCPU::fetch()
bool needToFetch = !isRomMicroPC(pcState.microPC()) && !curMacroStaticInst;
if (needToFetch) {
_status = Running;
_status = BaseSimpleCPU::Running;
Request *ifetch_req = new Request();
ifetch_req->setThreadContext(_cpuId, /* thread ID */ 0);
setupFetchRequest(ifetch_req);
@ -592,7 +595,7 @@ TimingSimpleCPU::sendFetch(Fault fault, RequestPtr req, ThreadContext *tc)
DPRINTF(SimpleCPU, "Translation of addr %#x faulted\n", req->getVaddr());
delete req;
// fetch fault: advance directly to next instruction (fault handler)
_status = Running;
_status = BaseSimpleCPU::Running;
advanceInst(fault);
}
@ -620,7 +623,7 @@ TimingSimpleCPU::advanceInst(Fault fault)
if (!stayAtPC)
advancePC(fault);
if (_status == Running) {
if (_status == BaseSimpleCPU::Running) {
// kick off fetch of next instruction... callback from icache
// response will cause that instruction to be executed,
// keeping the CPU running.
@ -641,12 +644,12 @@ TimingSimpleCPU::completeIfetch(PacketPtr pkt)
assert(!pkt || !pkt->isError());
assert(_status == IcacheWaitResponse);
_status = Running;
_status = BaseSimpleCPU::Running;
numCycles += curCycle() - previousCycle;
previousCycle = curCycle();
if (getState() == SimObject::Draining) {
if (getDrainState() == Drainable::Draining) {
if (pkt) {
delete pkt->req;
delete pkt;
@ -664,7 +667,7 @@ TimingSimpleCPU::completeIfetch(PacketPtr pkt)
// If we're not running now the instruction will complete in a dcache
// response callback or the instruction faulted and has started an
// ifetch
if (_status == Running) {
if (_status == BaseSimpleCPU::Running) {
if (fault != NoFault && traceData) {
// If there was a fault, we shouldn't trace this instruction.
delete traceData;
@ -778,7 +781,7 @@ TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
}
}
_status = Running;
_status = BaseSimpleCPU::Running;
Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
@ -802,7 +805,7 @@ TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
postExecute();
if (getState() == SimObject::Draining) {
if (getDrainState() == Drainable::Draining) {
advancePC(fault);
completeDrain();
@ -817,8 +820,8 @@ void
TimingSimpleCPU::completeDrain()
{
DPRINTF(Drain, "CPU done draining, processing drain event\n");
changeState(SimObject::Drained);
drainEvent->process();
setDrainState(Drainable::Drained);
drainManager->signalDrainDone();
}
bool

View file

@ -45,7 +45,7 @@ class TimingSimpleCPU : public BaseSimpleCPU
virtual void init();
public:
Event *drainEvent;
DrainManager *drainManager;
private:
@ -109,7 +109,7 @@ class TimingSimpleCPU : public BaseSimpleCPU
void
markDelayed()
{
assert(cpu->_status == Running);
assert(cpu->_status == BaseSimpleCPU::Running);
cpu->_status = ITBWaitResponse;
}
@ -249,8 +249,8 @@ class TimingSimpleCPU : public BaseSimpleCPU
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section);
virtual unsigned int drain(Event *drain_event);
virtual void resume();
unsigned int drain(DrainManager *drain_manager);
void drainResume();
void switchOut();
void takeOverFrom(BaseCPU *oldCPU);

View file

@ -110,11 +110,11 @@ TrafficGen::initState()
}
unsigned int
TrafficGen::drain(Event* drain_event)
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(drain_event);
return port.drain(dm);
}
void

View file

@ -604,7 +604,7 @@ class TrafficGen : public MemObject
void initState();
unsigned int drain(Event *drain_event);
unsigned int drain(DrainManager *dm);
void serialize(std::ostream &os);

View file

@ -82,7 +82,7 @@ CopyEngine::CopyEngineChannel::CopyEngineChannel(CopyEngine *_ce, int cid)
ce(_ce), channelId(cid), busy(false), underReset(false),
refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin),
latAfterCompletion(ce->params()->latAfterCompletion),
completionDataReg(0), nextState(Idle), drainEvent(NULL),
completionDataReg(0), nextState(Idle), drainManager(NULL),
fetchCompleteEvent(this), addrCompleteEvent(this),
readCompleteEvent(this), writeCompleteEvent(this),
statusCompleteEvent(this)
@ -140,12 +140,12 @@ CopyEngine::CopyEngineChannel::recvCommand()
cr.status.dma_transfer_status(0);
nextState = DescriptorFetch;
fetchAddress = cr.descChainAddr;
if (ce->getState() == SimObject::Running)
if (ce->getDrainState() == Drainable::Running)
fetchDescriptor(cr.descChainAddr);
} else if (cr.command.append_dma()) {
if (!busy) {
nextState = AddressFetch;
if (ce->getState() == SimObject::Running)
if (ce->getDrainState() == Drainable::Running)
fetchNextAddr(lastDescriptorAddr);
} else
refreshNext = true;
@ -637,41 +637,41 @@ CopyEngine::CopyEngineChannel::fetchAddrComplete()
bool
CopyEngine::CopyEngineChannel::inDrain()
{
if (ce->getState() == SimObject::Draining) {
if (ce->getDrainState() == Drainable::Draining) {
DPRINTF(Drain, "CopyEngine done draining, processing drain event\n");
assert(drainEvent);
drainEvent->process();
drainEvent = NULL;
assert(drainManager);
drainManager->signalDrainDone();
drainManager = NULL;
}
return ce->getState() != SimObject::Running;
return ce->getDrainState() != Drainable::Running;
}
unsigned int
CopyEngine::CopyEngineChannel::drain(Event *de)
CopyEngine::CopyEngineChannel::drain(DrainManager *dm)
{
if (nextState == Idle || ce->getState() != SimObject::Running)
if (nextState == Idle || ce->getDrainState() != Drainable::Running)
return 0;
unsigned int count = 1;
count += cePort.drain(de);
count += cePort.drain(dm);
DPRINTF(Drain, "CopyEngineChannel not drained\n");
drainEvent = de;
this->drainManager = dm;
return count;
}
unsigned int
CopyEngine::drain(Event *de)
CopyEngine::drain(DrainManager *dm)
{
unsigned int count;
count = pioPort.drain(de) + dmaPort.drain(de) + configPort.drain(de);
count = pioPort.drain(dm) + dmaPort.drain(dm) + configPort.drain(dm);
for (int x = 0;x < chan.size(); x++)
count += chan[x]->drain(de);
count += chan[x]->drain(dm);
if (count)
changeState(Draining);
setDrainState(Draining);
else
changeState(Drained);
setDrainState(Drained);
DPRINTF(Drain, "CopyEngine not drained\n");
return count;
@ -760,16 +760,16 @@ CopyEngine::CopyEngineChannel::restartStateMachine()
}
void
CopyEngine::resume()
CopyEngine::drainResume()
{
SimObject::resume();
Drainable::drainResume();
for (int x = 0;x < chan.size(); x++)
chan[x]->resume();
chan[x]->drainResume();
}
void
CopyEngine::CopyEngineChannel::resume()
CopyEngine::CopyEngineChannel::drainResume()
{
DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState);
restartStateMachine();

View file

@ -55,11 +55,12 @@
#include "dev/copy_engine_defs.hh"
#include "dev/pcidev.hh"
#include "params/CopyEngine.hh"
#include "sim/drain.hh"
#include "sim/eventq.hh"
class CopyEngine : public PciDev
{
class CopyEngineChannel
class CopyEngineChannel : public Drainable
{
private:
DmaPort cePort;
@ -91,7 +92,7 @@ class CopyEngine : public PciDev
ChannelState nextState;
Event *drainEvent;
DrainManager *drainManager;
public:
CopyEngineChannel(CopyEngine *_ce, int cid);
virtual ~CopyEngineChannel();
@ -106,8 +107,9 @@ class CopyEngine : public PciDev
void channelRead(PacketPtr pkt, Addr daddr, int size);
void channelWrite(PacketPtr pkt, Addr daddr, int size);
unsigned int drain(Event *de);
void resume();
unsigned int drain(DrainManager *drainManger);
void drainResume();
void serialize(std::ostream &os);
void unserialize(Checkpoint *cp, const std::string &section);
@ -205,8 +207,9 @@ class CopyEngine : public PciDev
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section);
virtual unsigned int drain(Event *de);
virtual void resume();
unsigned int drain(DrainManager *drainManger);
void drainResume();
};
#endif //__DEV_COPY_ENGINE_HH__

View file

@ -51,7 +51,7 @@
DmaPort::DmaPort(MemObject *dev, System *s)
: MasterPort(dev->name() + ".dma", dev), device(dev), sendEvent(this),
sys(s), masterId(s->getMasterId(dev->name())),
pendingCount(0), drainEvent(NULL),
pendingCount(0), drainManager(NULL),
inRetry(false)
{ }
@ -98,9 +98,9 @@ DmaPort::handleResp(PacketPtr pkt, Tick delay)
delete pkt;
// we might be drained at this point, if so signal the drain event
if (pendingCount == 0 && drainEvent) {
drainEvent->process();
drainEvent = NULL;
if (pendingCount == 0 && drainManager) {
drainManager->signalDrainDone();
drainManager = NULL;
}
}
@ -128,22 +128,22 @@ DmaDevice::init()
}
unsigned int
DmaDevice::drain(Event *de)
DmaDevice::drain(DrainManager *dm)
{
unsigned int count = pioPort.drain(de) + dmaPort.drain(de);
unsigned int count = pioPort.drain(dm) + dmaPort.drain(dm);
if (count)
changeState(Draining);
setDrainState(Drainable::Draining);
else
changeState(Drained);
setDrainState(Drainable::Drained);
return count;
}
unsigned int
DmaPort::drain(Event *de)
DmaPort::drain(DrainManager *dm)
{
if (pendingCount == 0)
return 0;
drainEvent = de;
drainManager = dm;
DPRINTF(Drain, "DmaPort not drained\n");
return 1;
}

View file

@ -48,6 +48,7 @@
#include "dev/io_device.hh"
#include "params/DmaDevice.hh"
#include "sim/drain.hh"
class DmaPort : public MasterPort
{
@ -123,7 +124,7 @@ class DmaPort : public MasterPort
/** If we need to drain, keep the drain event around until we're done
* here.*/
Event *drainEvent;
DrainManager *drainManager;
/** If the port is currently waiting for a retry before it can
* send whatever it is that it's sending. */
@ -146,7 +147,7 @@ class DmaPort : public MasterPort
bool dmaPending() const { return pendingCount > 0; }
unsigned cacheBlockSize() const { return peerBlockSize(); }
unsigned int drain(Event *de);
unsigned int drain(DrainManager *drainManger);
};
class DmaDevice : public PioDevice
@ -175,7 +176,7 @@ class DmaDevice : public PioDevice
virtual void init();
virtual unsigned int drain(Event *de);
unsigned int drain(DrainManager *drainManger);
unsigned cacheBlockSize() const { return dmaPort.cacheBlockSize(); }

View file

@ -57,7 +57,7 @@ using namespace iGbReg;
using namespace Net;
IGbE::IGbE(const Params *p)
: EtherDevice(p), etherInt(NULL), drainEvent(NULL),
: EtherDevice(p), etherInt(NULL), drainManager(NULL),
useFlowControl(p->use_flow_control),
rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size), rxTick(false),
txTick(false), txFifoTick(false), rxDmaPacket(false), pktOffset(0),
@ -588,7 +588,7 @@ IGbE::write(PacketPtr pkt)
case REG_RDT:
regs.rdt = val;
DPRINTF(EthernetSM, "RXS: RDT Updated.\n");
if (getState() == SimObject::Running) {
if (getDrainState() == Drainable::Running) {
DPRINTF(EthernetSM, "RXS: RDT Fetching Descriptors!\n");
rxDescCache.fetchDescriptors();
} else {
@ -628,7 +628,7 @@ IGbE::write(PacketPtr pkt)
case REG_TDT:
regs.tdt = val;
DPRINTF(EthernetSM, "TXS: TX Tail pointer updated\n");
if (getState() == SimObject::Running) {
if (getDrainState() == Drainable::Running) {
DPRINTF(EthernetSM, "TXS: TDT Fetching Descriptors!\n");
txDescCache.fetchDescriptors();
} else {
@ -906,7 +906,7 @@ void
IGbE::DescCache<T>::writeback1()
{
// If we're draining delay issuing this DMA
if (igbe->getState() != SimObject::Running) {
if (igbe->getDrainState() != Drainable::Running) {
igbe->schedule(wbDelayEvent, curTick() + igbe->wbDelay);
return;
}
@ -987,7 +987,7 @@ void
IGbE::DescCache<T>::fetchDescriptors1()
{
// If we're draining delay issuing this DMA
if (igbe->getState() != SimObject::Running) {
if (igbe->getDrainState() != Drainable::Running) {
igbe->schedule(fetchDelayEvent, curTick() + igbe->fetchDelay);
return;
}
@ -1493,7 +1493,7 @@ IGbE::RxDescCache::pktComplete()
void
IGbE::RxDescCache::enableSm()
{
if (!igbe->drainEvent) {
if (!igbe->drainManager) {
igbe->rxTick = true;
igbe->restartClock();
}
@ -2029,7 +2029,7 @@ IGbE::TxDescCache::packetAvailable()
void
IGbE::TxDescCache::enableSm()
{
if (!igbe->drainEvent) {
if (!igbe->drainManager) {
igbe->txTick = true;
igbe->restartClock();
}
@ -2049,19 +2049,19 @@ void
IGbE::restartClock()
{
if (!tickEvent.scheduled() && (rxTick || txTick || txFifoTick) &&
getState() == SimObject::Running)
getDrainState() == Drainable::Running)
schedule(tickEvent, clockEdge(Cycles(1)));
}
unsigned int
IGbE::drain(Event *de)
IGbE::drain(DrainManager *dm)
{
unsigned int count;
count = pioPort.drain(de) + dmaPort.drain(de);
count = pioPort.drain(dm) + dmaPort.drain(dm);
if (rxDescCache.hasOutstandingEvents() ||
txDescCache.hasOutstandingEvents()) {
count++;
drainEvent = de;
drainManager = dm;
}
txFifoTick = false;
@ -2073,17 +2073,17 @@ IGbE::drain(Event *de)
if (count) {
DPRINTF(Drain, "IGbE not drained\n");
changeState(Draining);
setDrainState(Drainable::Draining);
} else
changeState(Drained);
setDrainState(Drainable::Drained);
return count;
}
void
IGbE::resume()
IGbE::drainResume()
{
SimObject::resume();
Drainable::drainResume();
txFifoTick = true;
txTick = true;
@ -2096,7 +2096,7 @@ IGbE::resume()
void
IGbE::checkDrain()
{
if (!drainEvent)
if (!drainManager)
return;
txFifoTick = false;
@ -2105,8 +2105,8 @@ IGbE::checkDrain()
if (!rxDescCache.hasOutstandingEvents() &&
!txDescCache.hasOutstandingEvents()) {
DPRINTF(Drain, "IGbE done draining, processing drain event\n");
drainEvent->process();
drainEvent = NULL;
drainManager->signalDrainDone();
drainManager = NULL;
}
}
@ -2130,7 +2130,7 @@ IGbE::txStateMachine()
bool success =
#endif
txFifo.push(txPacket);
txFifoTick = true && !drainEvent;
txFifoTick = true && !drainManager;
assert(success);
txPacket = NULL;
anBegin("TXS", "Desc Writeback");
@ -2229,7 +2229,7 @@ IGbE::ethRxPkt(EthPacketPtr pkt)
}
// restart the state machines if they are stopped
rxTick = true && !drainEvent;
rxTick = true && !drainManager;
if ((rxTick || txTick) && !tickEvent.scheduled()) {
DPRINTF(EthernetSM,
"RXS: received packet into fifo, starting ticking\n");
@ -2442,8 +2442,8 @@ IGbE::ethTxDone()
// restart the tx state machines if they are stopped
// fifo to send another packet
// tx sm to put more data into the fifo
txFifoTick = true && !drainEvent;
if (txDescCache.descLeft() != 0 && !drainEvent)
txFifoTick = true && !drainManager;
if (txDescCache.descLeft() != 0 && !drainManager)
txTick = true;
restartClock();

View file

@ -68,7 +68,7 @@ class IGbE : public EtherDevice
uint16_t flash[iGbReg::EEPROM_SIZE];
// The drain event if we have one
Event *drainEvent;
DrainManager *drainManager;
// cached parameters from params struct
bool useFlowControl;
@ -347,7 +347,7 @@ class IGbE : public EtherDevice
virtual void updateHead(long h) { igbe->regs.rdh(h); }
virtual void enableSm();
virtual void fetchAfterWb() {
if (!igbe->rxTick && igbe->getState() == SimObject::Running)
if (!igbe->rxTick && igbe->getDrainState() == Drainable::Running)
fetchDescriptors();
}
@ -409,7 +409,7 @@ class IGbE : public EtherDevice
virtual void enableSm();
virtual void actionAfterWb();
virtual void fetchAfterWb() {
if (!igbe->txTick && igbe->getState() == SimObject::Running)
if (!igbe->txTick && igbe->getDrainState() == Drainable::Running)
fetchDescriptors();
}
@ -535,8 +535,9 @@ class IGbE : public EtherDevice
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section);
virtual unsigned int drain(Event *de);
virtual void resume();
unsigned int drain(DrainManager *dm);
void drainResume();
};

View file

@ -323,7 +323,7 @@ IdeDisk::doDmaTransfer()
panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
dmaState, devState);
if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) {
if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
return;
} else
@ -404,7 +404,7 @@ IdeDisk::doDmaRead()
curPrd.getByteCount(), TheISA::PageBytes);
}
if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) {
if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
return;
} else if (!dmaReadCG->done()) {
@ -481,7 +481,7 @@ IdeDisk::doDmaWrite()
dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
curPrd.getByteCount(), TheISA::PageBytes);
}
if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) {
if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
return;

View file

@ -89,14 +89,14 @@ PioDevice::getSlavePort(const std::string &if_name, PortID idx)
}
unsigned int
PioDevice::drain(Event *de)
PioDevice::drain(DrainManager *dm)
{
unsigned int count;
count = pioPort.drain(de);
count = pioPort.drain(dm);
if (count)
changeState(Draining);
setDrainState(Drainable::Draining);
else
changeState(Drained);
setDrainState(Drainable::Drained);
return count;
}

View file

@ -125,7 +125,7 @@ class PioDevice : public MemObject
virtual void init();
virtual unsigned int drain(Event *de);
unsigned int drain(DrainManager *drainManger);
virtual BaseSlavePort &getSlavePort(const std::string &if_name,
PortID idx = InvalidPortID);

View file

@ -1069,7 +1069,7 @@ NSGigE::doRxDmaRead()
assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting);
rxDmaState = dmaReading;
if (dmaPending() || getState() != Running)
if (dmaPending() || getDrainState() != Drainable::Running)
rxDmaState = dmaReadWaiting;
else
dmaRead(rxDmaAddr, rxDmaLen, &rxDmaReadEvent, (uint8_t*)rxDmaData);
@ -1100,7 +1100,7 @@ NSGigE::doRxDmaWrite()
assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting);
rxDmaState = dmaWriting;
if (dmaPending() || getState() != Running)
if (dmaPending() || getDrainState() != Running)
rxDmaState = dmaWriteWaiting;
else
dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaWriteEvent, (uint8_t*)rxDmaData);
@ -1518,7 +1518,7 @@ NSGigE::doTxDmaRead()
assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting);
txDmaState = dmaReading;
if (dmaPending() || getState() != Running)
if (dmaPending() || getDrainState() != Running)
txDmaState = dmaReadWaiting;
else
dmaRead(txDmaAddr, txDmaLen, &txDmaReadEvent, (uint8_t*)txDmaData);
@ -1549,7 +1549,7 @@ NSGigE::doTxDmaWrite()
assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting);
txDmaState = dmaWriting;
if (dmaPending() || getState() != Running)
if (dmaPending() || getDrainState() != Running)
txDmaState = dmaWriteWaiting;
else
dmaWrite(txDmaAddr, txDmaLen, &txDmaWriteEvent, (uint8_t*)txDmaData);
@ -2112,9 +2112,9 @@ NSGigE::recvPacket(EthPacketPtr packet)
void
NSGigE::resume()
NSGigE::drainResume()
{
SimObject::resume();
Drainable::drainResume();
// During drain we could have left the state machines in a waiting state and
// they wouldn't get out until some other event occured to kick them.

View file

@ -369,7 +369,7 @@ class NSGigE : public EtherDevBase
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section);
virtual void resume();
void drainResume();
};
/*

View file

@ -157,14 +157,14 @@ PciDev::init()
}
unsigned int
PciDev::drain(Event *de)
PciDev::drain(DrainManager *dm)
{
unsigned int count;
count = pioPort.drain(de) + dmaPort.drain(de) + configPort.drain(de);
count = pioPort.drain(dm) + dmaPort.drain(dm) + configPort.drain(dm);
if (count)
changeState(Draining);
setDrainState(Drainable::Draining);
else
changeState(Drained);
setDrainState(Drainable::Drained);
return count;
}

View file

@ -216,7 +216,7 @@ class PciDev : public DmaDevice
virtual void unserialize(Checkpoint *cp, const std::string &section);
virtual unsigned int drain(Event *de);
virtual unsigned int drain(DrainManager *dm);
virtual BaseSlavePort &getSlavePort(const std::string &if_name,
PortID idx = InvalidPortID)

View file

@ -870,7 +870,7 @@ Device::rxKick()
break;
case rxBeginCopy:
if (dmaPending() || getState() != Running)
if (dmaPending() || getDrainState() != Drainable::Running)
goto exit;
rxDmaAddr = params()->platform->pciToDma(
@ -1070,7 +1070,7 @@ Device::txKick()
break;
case txBeginCopy:
if (dmaPending() || getState() != Running)
if (dmaPending() || getDrainState() != Drainable::Running)
goto exit;
txDmaAddr = params()->platform->pciToDma(
@ -1246,9 +1246,9 @@ Device::recvPacket(EthPacketPtr packet)
}
void
Device::resume()
Device::drainResume()
{
SimObject::resume();
Drainable::drainResume();
// During drain we could have left the state machines in a waiting state and
// they wouldn't get out until some other event occured to kick them.

View file

@ -271,7 +271,7 @@ class Device : public Base
public:
virtual Tick read(PacketPtr pkt);
virtual Tick write(PacketPtr pkt);
virtual void resume();
virtual void drainResume();
void prepareIO(int cpu, int index);
void prepareRead(int cpu, int index);

View file

@ -161,7 +161,8 @@ BaseBus::calcPacketTiming(PacketPtr pkt)
template <typename PortClass>
BaseBus::Layer<PortClass>::Layer(BaseBus& _bus, const std::string& _name,
Tick _clock) :
bus(_bus), _name(_name), state(IDLE), clock(_clock), drainEvent(NULL),
Drainable(),
bus(_bus), _name(_name), state(IDLE), clock(_clock), drainManager(NULL),
releaseEvent(this)
{
}
@ -266,12 +267,12 @@ BaseBus::Layer<PortClass>::releaseLayer()
// busy, and in the latter case the bus may be released before
// we see a retry from the destination
retryWaiting();
} else if (drainEvent) {
DPRINTF(Drain, "Bus done draining, processing drain event\n");
} else if (drainManager) {
DPRINTF(Drain, "Bus done draining, signaling drain manager\n");
//If we weren't able to drain before, do it now.
drainEvent->process();
drainManager->signalDrainDone();
// Clear the drain event once we're done with it.
drainEvent = NULL;
drainManager = NULL;
}
}
@ -522,14 +523,14 @@ BaseBus::deviceBlockSize() const
template <typename PortClass>
unsigned int
BaseBus::Layer<PortClass>::drain(Event * de)
BaseBus::Layer<PortClass>::drain(DrainManager *dm)
{
//We should check that we're not "doing" anything, and that noone is
//waiting. We might be idle but have someone waiting if the device we
//contacted for a retry didn't actually retry.
if (!retryList.empty() || state != IDLE) {
DPRINTF(Drain, "Bus not drained\n");
drainEvent = de;
drainManager = dm;
return 1;
}
return 0;

View file

@ -94,7 +94,7 @@ class BaseBus : public MemObject
* whereas a response layer holds master ports.
*/
template <typename PortClass>
class Layer
class Layer : public Drainable
{
public:
@ -118,7 +118,7 @@ class BaseBus : public MemObject
*
* @return 1 if busy or waiting to retry, or 0 if idle
*/
unsigned int drain(Event *de);
unsigned int drain(DrainManager *dm);
/**
* Get the bus layer's name
@ -206,8 +206,8 @@ class BaseBus : public MemObject
/** the clock speed for the bus layer */
Tick clock;
/** event for signalling when drained */
Event * drainEvent;
/** manager to signal when drained */
DrainManager *drainManager;
/**
* An array of ports that retry should be called
@ -366,7 +366,7 @@ class BaseBus : public MemObject
BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID);
virtual unsigned int drain(Event *de) = 0;
virtual unsigned int drain(DrainManager *dm) = 0;
};

12
src/mem/cache/base.cc vendored
View file

@ -77,7 +77,7 @@ BaseCache::BaseCache(const Params *p)
blocked(0),
noTargetMSHR(NULL),
missCount(p->max_miss_count),
drainEvent(NULL),
drainManager(NULL),
addrRanges(p->addr_ranges.begin(), p->addr_ranges.end()),
system(p->system)
{
@ -749,19 +749,19 @@ BaseCache::regStats()
}
unsigned int
BaseCache::drain(Event *de)
BaseCache::drain(DrainManager *dm)
{
int count = memSidePort->drain(de) + cpuSidePort->drain(de);
int count = memSidePort->drain(dm) + cpuSidePort->drain(dm);
// Set status
if (count != 0) {
drainEvent = de;
drainManager = dm;
changeState(SimObject::Draining);
setDrainState(Drainable::Draining);
DPRINTF(Drain, "Cache not drained\n");
return count;
}
changeState(SimObject::Drained);
setDrainState(Drainable::Drained);
return 0;
}

View file

@ -269,7 +269,7 @@ class BaseCache : public MemObject
Counter missCount;
/** The drain event. */
Event *drainEvent;
DrainManager *drainManager;
/**
* The address range to which the cache responds on the CPU side.
@ -542,7 +542,7 @@ class BaseCache : public MemObject
// interesting again.
}
virtual unsigned int drain(Event *de);
virtual unsigned int drain(DrainManager *dm);
virtual bool inCache(Addr addr) = 0;

View file

@ -508,10 +508,10 @@ CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
}
unsigned int
CoherentBus::drain(Event *de)
CoherentBus::drain(DrainManager *dm)
{
// sum up the individual layers
return reqLayer.drain(de) + respLayer.drain(de) + snoopRespLayer.drain(de);
return reqLayer.drain(dm) + respLayer.drain(dm) + snoopRespLayer.drain(dm);
}
CoherentBus *

View file

@ -299,7 +299,7 @@ class CoherentBus : public BaseBus
CoherentBus(const CoherentBusParams *p);
unsigned int drain(Event *de);
unsigned int drain(DrainManager *dm);
};
#endif //__MEM_COHERENT_BUS_HH__

View file

@ -212,10 +212,10 @@ NoncoherentBus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
}
unsigned int
NoncoherentBus::drain(Event *de)
NoncoherentBus::drain(DrainManager *dm)
{
// sum up the individual layers
return reqLayer.drain(de) + respLayer.drain(de);
return reqLayer.drain(dm) + respLayer.drain(dm);
}
NoncoherentBus*

View file

@ -207,7 +207,7 @@ class NoncoherentBus : public BaseBus
NoncoherentBus(const NoncoherentBusParams *p);
unsigned int drain(Event *de);
unsigned int drain(DrainManager *dm);
};

View file

@ -48,7 +48,7 @@
using namespace std;
PacketQueue::PacketQueue(EventManager& _em, const std::string& _label)
: em(_em), sendEvent(this), drainEvent(NULL), label(_label),
: em(_em), sendEvent(this), drainManager(NULL), label(_label),
waitingOnRetry(false)
{
}
@ -173,11 +173,11 @@ PacketQueue::scheduleSend(Tick time)
em.schedule(&sendEvent, std::max(nextReady, curTick() + 1));
} else {
// no more to send, so if we're draining, we may be done
if (drainEvent && transmitList.empty() && !sendEvent.scheduled()) {
if (drainManager && transmitList.empty() && !sendEvent.scheduled()) {
DPRINTF(Drain, "PacketQueue done draining,"
"processing drain event\n");
drainEvent->process();
drainEvent = NULL;
drainManager->signalDrainDone();
drainManager = NULL;
}
}
}
@ -204,12 +204,12 @@ PacketQueue::processSendEvent()
}
unsigned int
PacketQueue::drain(Event *de)
PacketQueue::drain(DrainManager *dm)
{
if (transmitList.empty() && !sendEvent.scheduled())
return 0;
DPRINTF(Drain, "PacketQueue not drained\n");
drainEvent = de;
drainManager = dm;
return 1;
}

View file

@ -57,12 +57,13 @@
#include "mem/port.hh"
#include "sim/eventq.hh"
#include "sim/drain.hh"
/**
* A packet queue is a class that holds deferred packets and later
* sends them using the associated slave port or master port.
*/
class PacketQueue
class PacketQueue : public Drainable
{
private:
/** A deferred packet, buffered to transmit later. */
@ -95,9 +96,9 @@ class PacketQueue
**/
EventWrapper<PacketQueue, &PacketQueue::processSendEvent> sendEvent;
/** If we need to drain, keep the drain event around until we're done
/** If we need to drain, keep the drain manager around until we're done
* here.*/
Event *drainEvent;
DrainManager *drainManager;
protected:
@ -207,13 +208,7 @@ class PacketQueue
*/
void retry();
/**
* Hook for draining the packet queue.
*
* @param de An event which is used to signal back to the caller
* @return A number indicating how many times process will be called
*/
unsigned int drain(Event *de);
unsigned int drain(DrainManager *dm);
};
class MasterPacketQueue : public PacketQueue

View file

@ -97,13 +97,7 @@ class QueuedSlavePort : public SlavePort
* functional request. */
bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); }
/**
* Hook for draining the queued port.
*
* @param de an event which is used to signal back to the caller
* @returns a number indicating how many times process will be called
*/
unsigned int drain(Event *de) { return queue.drain(de); }
unsigned int drain(DrainManager *dm) { return queue.drain(dm); }
};
class QueuedMasterPort : public MasterPort
@ -156,13 +150,7 @@ class QueuedMasterPort : public MasterPort
* functional request. */
bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); }
/**
* Hook for draining the queued port.
*
* @param de an event which is used to signal back to the caller
* @returns a number indicating how many times process will be called
*/
unsigned int drain(Event *de) { return queue.drain(de); }
unsigned int drain(DrainManager *dm) { return queue.drain(dm); }
};
#endif // __MEM_QPORT_HH__

View file

@ -56,8 +56,6 @@ class MemoryControl : public ClockedObject, public Consumer
~MemoryControl();
unsigned int drain(Event *de) = 0;
virtual void wakeup() = 0;
virtual void setConsumer(Consumer* consumer_ptr) = 0;

View file

@ -684,7 +684,7 @@ RubyMemoryControl::executeCycle()
}
unsigned int
RubyMemoryControl::drain(Event *de)
RubyMemoryControl::drain(DrainManager *dm)
{
DPRINTF(RubyMemory, "MemoryController drain\n");
if(m_event.scheduled()) {

View file

@ -62,7 +62,7 @@ class RubyMemoryControl : public MemoryControl
~RubyMemoryControl();
unsigned int drain(Event *de);
unsigned int drain(DrainManager *dm);
void wakeup();

View file

@ -53,7 +53,7 @@ RubyPort::RubyPort(const Params *p)
m_mandatory_q_ptr(NULL),
pio_port(csprintf("%s-pio-port", name()), this),
m_usingRubyTester(p->using_ruby_tester), m_request_cnt(0),
drainEvent(NULL), ruby_system(p->ruby_system), system(p->system),
drainManager(NULL), ruby_system(p->ruby_system), system(p->system),
waitingOnSequencer(false), access_phys_mem(p->access_phys_mem)
{
assert(m_version != -1);
@ -343,36 +343,36 @@ void
RubyPort::testDrainComplete()
{
//If we weren't able to drain before, we might be able to now.
if (drainEvent != NULL) {
if (drainManager != NULL) {
unsigned int drainCount = outstandingCount();
DPRINTF(Drain, "Drain count: %u\n", drainCount);
if (drainCount == 0) {
DPRINTF(Drain, "RubyPort done draining, processing drain event\n");
drainEvent->process();
// Clear the drain event once we're done with it.
drainEvent = NULL;
DPRINTF(Drain, "RubyPort done draining, signaling drain done\n");
drainManager->signalDrainDone();
// Clear the drain manager once we're done with it.
drainManager = NULL;
}
}
}
unsigned int
RubyPort::getChildDrainCount(Event *de)
RubyPort::getChildDrainCount(DrainManager *dm)
{
int count = 0;
if (pio_port.isConnected()) {
count += pio_port.drain(de);
count += pio_port.drain(dm);
DPRINTF(Config, "count after pio check %d\n", count);
}
for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) {
count += (*p)->drain(de);
count += (*p)->drain(dm);
DPRINTF(Config, "count after slave port check %d\n", count);
}
for (std::vector<PioPort*>::iterator p = master_ports.begin();
p != master_ports.end(); ++p) {
count += (*p)->drain(de);
count += (*p)->drain(dm);
DPRINTF(Config, "count after master port check %d\n", count);
}
@ -382,7 +382,7 @@ RubyPort::getChildDrainCount(Event *de)
}
unsigned int
RubyPort::drain(Event *de)
RubyPort::drain(DrainManager *dm)
{
if (isDeadlockEventScheduled()) {
descheduleDeadlockEvent();
@ -390,28 +390,28 @@ RubyPort::drain(Event *de)
//
// If the RubyPort is not empty, then it needs to clear all outstanding
// requests before it should call drainEvent->process()
// requests before it should call drainManager->signalDrainDone()
//
DPRINTF(Config, "outstanding count %d\n", outstandingCount());
bool need_drain = outstandingCount() > 0;
//
// Also, get the number of child ports that will also need to clear
// their buffered requests before they call drainEvent->process()
// their buffered requests before they call drainManager->signalDrainDone()
//
unsigned int child_drain_count = getChildDrainCount(de);
unsigned int child_drain_count = getChildDrainCount(dm);
// Set status
if (need_drain) {
drainEvent = de;
drainManager = dm;
DPRINTF(Drain, "RubyPort not drained\n");
changeState(SimObject::Draining);
setDrainState(Drainable::Draining);
return child_drain_count + 1;
}
drainEvent = NULL;
changeState(SimObject::Drained);
drainManager = NULL;
setDrainState(Drainable::Drained);
return child_drain_count;
}

View file

@ -142,7 +142,7 @@ class RubyPort : public MemObject
//
void setController(AbstractController* _cntrl) { m_controller = _cntrl; }
int getId() { return m_version; }
unsigned int drain(Event *de);
unsigned int drain(DrainManager *dm);
protected:
const std::string m_name;
@ -166,7 +166,7 @@ class RubyPort : public MemObject
}
}
unsigned int getChildDrainCount(Event *de);
unsigned int getChildDrainCount(DrainManager *dm);
uint16_t m_port_id;
uint64_t m_request_cnt;
@ -176,7 +176,7 @@ class RubyPort : public MemObject
std::vector<M5Port*> slave_ports;
std::vector<PioPort*> master_ports;
Event *drainEvent;
DrainManager *drainManager;
RubySystem* ruby_system;
System* system;

View file

@ -85,7 +85,7 @@ Sequencer::~Sequencer()
void
Sequencer::wakeup()
{
assert(getState() != SimObject::Draining);
assert(getDrainState() != Drainable::Draining);
// Check for deadlock of any of the requests
Time current_time = g_system_ptr->getTime();
@ -209,7 +209,8 @@ Sequencer::insertRequest(PacketPtr pkt, RubyRequestType request_type)
(m_writeRequestTable.size() + m_readRequestTable.size()));
// See if we should schedule a deadlock check
if (!deadlockCheckEvent.scheduled() && getState() != SimObject::Draining) {
if (!deadlockCheckEvent.scheduled() &&
getDrainState() != Drainable::Draining) {
schedule(deadlockCheckEvent,
g_system_ptr->clockPeriod() * m_deadlock_threshold + curTick());
}

View file

@ -51,7 +51,7 @@ SimpleDRAM::SimpleDRAM(const SimpleDRAMParams* p) :
retryRdReq(false), retryWrReq(false),
rowHitFlag(false), stopReads(false),
writeEvent(this), respondEvent(this),
refreshEvent(this), nextReqEvent(this), drainEvent(NULL),
refreshEvent(this), nextReqEvent(this), drainManager(NULL),
bytesPerCacheLine(0),
linesPerRowBuffer(p->lines_per_rowbuffer),
ranksPerChannel(p->ranks_per_channel),
@ -346,9 +346,9 @@ SimpleDRAM::processWriteEvent()
// if there is nothing left in any queue, signal a drain
if (dramWriteQueue.empty() && dramReadQueue.empty() &&
dramRespQueue.empty () && drainEvent) {
drainEvent->process();
drainEvent = NULL;
dramRespQueue.empty () && drainManager) {
drainManager->signalDrainDone();
drainManager = NULL;
}
// Once you're done emptying the write queue, check if there's
@ -595,9 +595,9 @@ SimpleDRAM::processRespondEvent()
} else {
// if there is nothing left in any queue, signal a drain
if (dramWriteQueue.empty() && dramReadQueue.empty() &&
drainEvent) {
drainEvent->process();
drainEvent = NULL;
drainManager) {
drainManager->signalDrainDone();
drainManager = NULL;
}
}
}
@ -1197,22 +1197,22 @@ SimpleDRAM::getSlavePort(const string &if_name, PortID idx)
}
unsigned int
SimpleDRAM::drain(Event *de)
SimpleDRAM::drain(DrainManager *dm)
{
unsigned int count = port.drain(de);
unsigned int count = port.drain(dm);
// if there is anything in any of our internal queues, keep track
// of that as well
if (!(dramWriteQueue.empty() && dramReadQueue.empty() &&
dramRespQueue.empty())) {
++count;
drainEvent = de;
drainManager = dm;
}
if (count)
changeState(Draining);
setDrainState(Drainable::Draining);
else
changeState(Drained);
setDrainState(Drainable::Drained);
return count;
}

View file

@ -341,10 +341,10 @@ class SimpleDRAM : public AbstractMemory
*/
std::list<DRAMPacket*> dramRespQueue;
/** If we need to drain, keep the drain event around until we're done
/** If we need to drain, keep the drain manager around until we're done
* here.
*/
Event *drainEvent;
DrainManager *drainManager;
/**
* Multi-dimensional vector of banks, first dimension is ranks,
@ -459,7 +459,7 @@ class SimpleDRAM : public AbstractMemory
SimpleDRAM(const SimpleDRAMParams* p);
unsigned int drain(Event* de);
unsigned int drain(DrainManager* dm);
virtual BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID);

View file

@ -176,14 +176,14 @@ SimpleMemory::getSlavePort(const std::string &if_name, PortID idx)
}
unsigned int
SimpleMemory::drain(Event *de)
SimpleMemory::drain(DrainManager *dm)
{
int count = port.drain(de);
int count = port.drain(dm);
if (count)
changeState(Draining);
setDrainState(Drainable::Draining);
else
changeState(Drained);
setDrainState(Drainable::Drained);
return count;
}

View file

@ -123,7 +123,7 @@ class SimpleMemory : public AbstractMemory
SimpleMemory(const SimpleMemoryParams *p);
virtual ~SimpleMemory() { }
unsigned int drain(Event* de);
unsigned int drain(DrainManager *dm);
virtual BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID);

View file

@ -66,6 +66,7 @@ PySource('m5.util', 'm5/util/terminal.py')
SwigSource('m5.internal', 'swig/core.i')
SwigSource('m5.internal', 'swig/debug.i')
SwigSource('m5.internal', 'swig/drain.i')
SwigSource('m5.internal', 'swig/event.i')
SwigSource('m5.internal', 'swig/pyobject.i')
SwigSource('m5.internal', 'swig/range.i')

View file

@ -123,7 +123,8 @@ class MetaSimObject(type):
'cxx_class' : str,
'cxx_type' : str,
'cxx_header' : str,
'type' : str }
'type' : str,
'cxx_bases' : list }
# Attributes that can be set any time
keywords = { 'check' : FunctionType }
@ -148,6 +149,8 @@ class MetaSimObject(type):
value_dict[key] = val
if 'abstract' not in value_dict:
value_dict['abstract'] = False
if 'cxx_bases' not in value_dict:
value_dict['cxx_bases'] = []
cls_dict['_value_dict'] = value_dict
cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
if 'type' in value_dict:
@ -414,6 +417,7 @@ class MetaSimObject(type):
code('%module(package="m5.internal") param_$cls')
code()
code('%{')
code('#include "sim/sim_object.hh"')
code('#include "params/$cls.hh"')
for param in params:
param.cxx_predecls(code)
@ -458,7 +462,17 @@ using std::ptrdiff_t;
code('%nodefault $classname;')
code('class $classname')
if cls._base:
code(' : public ${{cls._base.cxx_class}}')
bases = [ cls._base.cxx_class ] + cls.cxx_bases
else:
bases = cls.cxx_bases
base_first = True
for base in bases:
if base_first:
code(' : public ${{base}}')
base_first = False
else:
code(' , public ${{base}}')
code('{')
code(' public:')
cls.export_methods(code)
@ -581,30 +595,25 @@ class SimObject(object):
abstract = True
cxx_header = "sim/sim_object.hh"
cxx_bases = [ "Drainable" ]
@classmethod
def export_method_swig_predecls(cls, code):
code('''
%include <std_string.i>
%import "python/swig/drain.i"
''')
@classmethod
def export_methods(cls, code):
code('''
enum State {
Running,
Draining,
Drained
};
void init();
void loadState(Checkpoint *cp);
void initState();
void regStats();
void resetStats();
void startup();
unsigned int drain(Event *drain_event);
void resume();
''')
# Initialize new instance. For objects with SimObject-valued

View file

@ -51,3 +51,4 @@ if internal:
from event import *
from main import main
from simulate import *

View file

@ -31,3 +31,4 @@ import debug
import event
import stats
import trace
from drain import DrainManager, Drainable

View file

@ -169,19 +169,19 @@ def doDrain(root):
# be drained.
def drain(root):
all_drained = False
drain_event = internal.event.createCountedDrain()
unready_objs = sum(obj.drain(drain_event) for obj in root.descendants())
dm = internal.drain.createDrainManager()
unready_objs = sum(obj.drain(dm) for obj in root.descendants())
# If we've got some objects that can't drain immediately, then simulate
if unready_objs > 0:
drain_event.setCount(unready_objs)
dm.setCount(unready_objs)
simulate()
else:
all_drained = True
internal.event.cleanupCountedDrain(drain_event)
internal.drain.cleanupDrainManager(dm)
return all_drained
def resume(root):
for obj in root.descendants(): obj.resume()
for obj in root.descendants(): obj.drainResume()
def checkpoint(dir):
root = objects.Root.getInstance()

66
src/python/swig/drain.i Normal file
View file

@ -0,0 +1,66 @@
/*
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Andreas Sandberg
*/
%module(package="m5.internal") drain
%{
#include "sim/drain.hh"
%}
%nodefaultctor Drainable;
%include "sim/drain.hh"
%inline %{
DrainManager *
createDrainManager()
{
return new DrainManager();
}
void
cleanupDrainManager(DrainManager *drain_manager)
{
assert(drain_manager);
assert(drain_manager->getCount() == 0);
delete drain_manager;
}
%}

View file

@ -81,11 +81,6 @@
// This must follow eventq.hh
%include "python/swig/pyevent.hh"
struct CountedDrainEvent : public Event
{
void setCount(int _count);
};
// minimal definition of SimExitEvent interface to wrap
class SimLoopExitEvent : public Event
{

View file

@ -65,22 +65,3 @@ PythonEvent::process()
// reference count must be decremented.
Py_DECREF(object);
}
CountedDrainEvent *
createCountedDrain()
{
return new CountedDrainEvent();
}
void
cleanupCountedDrain(Event *counted_drain)
{
CountedDrainEvent *event =
dynamic_cast<CountedDrainEvent *>(counted_drain);
if (event == NULL) {
fatal("Called cleanupCountedDrain() on an event that was not "
"a CountedDrainEvent.");
}
assert(event->getCount() == 0);
delete event;
}

View file

@ -49,7 +49,4 @@ class PythonEvent : public Event
virtual void process();
};
CountedDrainEvent *createCountedDrain();
void cleanupCountedDrain(Event *counted_drain);
#endif // __PYTHON_SWIG_PYEVENT_HH__

View file

@ -44,6 +44,7 @@ Source('init.cc')
Source('main.cc', main=True, skip_lib=True)
Source('root.cc')
Source('serialize.cc')
Source('drain.cc')
Source('sim_events.cc')
Source('sim_object.cc')
Source('simulate.cc')

73
src/sim/drain.cc Normal file
View file

@ -0,0 +1,73 @@
/*
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Andreas Sandberg
*/
#include "sim/drain.hh"
#include "sim/sim_exit.hh"
DrainManager::DrainManager()
: _count(0)
{
}
DrainManager::~DrainManager()
{
}
void
DrainManager::drainCycleDone()
{
exitSimLoop("Finished drain", 0);
}
Drainable::Drainable()
: _drainState(Running)
{
}
Drainable::~Drainable()
{
}
void
Drainable::drainResume()
{
_drainState = Running;
}

216
src/sim/drain.hh Normal file
View file

@ -0,0 +1,216 @@
/*
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Andreas Sandberg
*/
#ifndef __SIM_DRAIN_HH__
#define __SIM_DRAIN_HH__
#include <cassert>
#include <vector>
#include "base/flags.hh"
class Event;
/**
* This class coordinates draining of a System.
*
* When draining a System, we need to make sure that all SimObjects in
* that system have drained their state before declaring the operation
* to be successful. This class keeps track of how many objects are
* still in the process of draining their state. Once it determines
* that all objects have drained their state, it exits the simulation
* loop.
*
* @note A System might not be completely drained even though the
* DrainManager has caused the simulation loop to exit. Draining needs
* to be restarted until all Drainable objects declare that they don't
* need further simulation to be completely drained. See Drainable for
* more information.
*/
class DrainManager
{
public:
DrainManager();
virtual ~DrainManager();
/**
* Get the number of objects registered with this DrainManager
* that are currently draining their state.
*
* @return Number of objects currently draining.
*/
unsigned int getCount() const { return _count; }
void setCount(int count) { _count = count; }
/**
* Notify the DrainManager that a Drainable object has finished
* draining.
*/
void signalDrainDone() {
assert(_count > 0);
if (--_count == 0)
drainCycleDone();
}
protected:
/**
* Callback when all registered Drainable objects have completed a
* drain cycle.
*/
virtual void drainCycleDone();
/** Number of objects still draining. */
unsigned int _count;
};
/**
* Interface for objects that might require draining before
* checkpointing.
*
* An object's internal state needs to be drained when creating a
* checkpoint, switching between CPU models, or switching between
* timing models. Once the internal state has been drained from
* <i>all</i> objects in the system, the objects are serialized to
* disc or the configuration change takes place. The process works as
* follows (see simulate.py for details):
*
* <ol>
* <li>An instance of a DrainManager is created to keep track of how
* many objects need to be drained. The object maintains an
* internal counter that is decreased every time its
* CountedDrainEvent::signalDrainDone() method is called. When the
* counter reaches zero, the simulation is stopped.
*
* <li>Call Drainable::drain() for every object in the
* system. Draining has completed if all of them return
* zero. Otherwise, the sum of the return values is loaded into
* the counter of the DrainManager. A pointer to the drain
* manager is passed as an argument to the drain() method.
*
* <li>Continue simulation. When an object has finished draining its
* internal state, it calls CountedDrainEvent::signalDrainDone()
* on the manager. When the counter in the manager reaches zero,
* the simulation stops.
*
* <li>Check if any object still needs draining, if so repeat the
* process above.
*
* <li>Serialize objects, switch CPU model, or change timing model.
*
* <li>Call Drainable::drainResume() and continue the simulation.
* </ol>
*
*/
class Drainable
{
public:
/**
* Object drain/handover states
*
* An object starts out in the Running state. When the simulator
* prepares to take a snapshot or prepares a CPU for handover, it
* calls the drain() method to transfer the object into the
* Draining or Drained state. If any object enters the Draining
* state (drain() returning >0), simulation continues until it all
* objects have entered the Drained state.
*
* Before resuming simulation, the simulator calls resume() to
* transfer the object to the Running state.
*
* \note Even though the state of an object (visible to the rest
* of the world through getState()) could be used to determine if
* all objects have entered the Drained state, the protocol is
* actually a bit more elaborate. See drain() for details.
*/
enum State {
Running, /** Running normally */
Draining, /** Draining buffers pending serialization/handover */
Drained /** Buffers drained, ready for serialization/handover */
};
Drainable();
virtual ~Drainable();
/**
* Determine if an object needs draining and register a
* DrainManager.
*
* When draining the state of an object, the simulator calls drain
* with a pointer to a drain manager. If the object does not need
* further simulation to drain internal buffers, it switched to
* the Drained state and returns 0, otherwise it switches to the
* Draining state and returns the number of times that it will
* call Event::process() on the drain event. Most objects are
* expected to return either 0 or 1.
*
* @note An object that has entered the Drained state can be
* disturbed by other objects in the system and consequently be
* forced to enter the Draining state again. The simulator
* therefore repeats the draining process until all objects return
* 0 on the first call to drain().
*
* @param drainManager DrainManager to use to inform the simulator
* when draining has completed.
*
* @return 0 if the object is ready for serialization now, >0 if
* it needs further simulation.
*/
virtual unsigned int drain(DrainManager *drainManager) = 0;
/**
* Resume execution after a successful drain.
*
* @note This method is normally only called from the simulation
* scripts.
*/
virtual void drainResume();
State getDrainState() const { return _drainState; }
protected:
void setDrainState(State new_state) { _drainState = new_state; }
private:
State _drainState;
};
#endif

View file

@ -144,8 +144,14 @@ void fromSimObject(T &t, SimObject *s)
fromSimObject(objptr, sptr); \
} while (0)
/*
/**
* Basic support for object serialization.
*
* @note Many objects that support serialization need to be put in a
* consistent state when serialization takes place. We refer to the
* action of forcing an object into a consistent state as
* 'draining'. Objects that need draining inherit from Drainable. See
* Drainable for more information.
*/
class Serializable
{

View file

@ -83,17 +83,6 @@ exitSimLoop(const std::string &message, int exit_code, Tick when, Tick repeat)
mainEventQueue.schedule(event, when);
}
CountedDrainEvent::CountedDrainEvent()
: count(0)
{ }
void
CountedDrainEvent::process()
{
if (--count == 0)
exitSimLoop("Finished drain", 0);
}
//
// constructor: automatically schedules at specified time
//

View file

@ -67,7 +67,6 @@ SimObject::SimObject(const Params *p)
#endif
simObjectList.push_back(this);
state = Running;
}
void
@ -151,17 +150,12 @@ debugObjectBreak(const char *objs)
#endif
unsigned int
SimObject::drain(Event *drain_event)
SimObject::drain(DrainManager *drain_manager)
{
state = Drained;
setDrainState(Drained);
return 0;
}
void
SimObject::resume()
{
state = Running;
}
SimObject *
SimObject::find(const char *name)

View file

@ -45,6 +45,7 @@
#include "enums/MemoryMode.hh"
#include "params/SimObject.hh"
#include "sim/drain.hh"
#include "sim/eventq.hh"
#include "sim/serialize.hh"
@ -72,40 +73,7 @@ class Event;
* </ul>
* <li>SimObject::resetStats()
* <li>SimObject::startup()
* <li>SimObject::resume() if resuming from a checkpoint.
* </ol>
*
* An object's internal state needs to be drained when creating a
* checkpoint, switching between CPU models, or switching between
* timing models. Once the internal state has been drained from
* <i>all</i> objects in the system, the objects are serialized to
* disc or the configuration change takes place. The process works as
* follows (see simulate.py for details):
*
* <ol>
* <li>An instance of a CountedDrainEvent is created to keep track of
* how many objects need to be drained. The object maintains an
* internal counter that is decreased every time its
* CountedDrainEvent::process() method is called. When the counter
* reaches zero, the simulation is stopped.
*
* <li>Call SimObject::drain() for every object in the
* system. Draining has completed if all of them return
* zero. Otherwise, the sum of the return values is loaded into
* the counter of the CountedDrainEvent. A pointer of the drain
* event is passed as an argument to the drain() method.
*
* <li>Continue simulation. When an object has finished draining its
* internal state, it calls CountedDrainEvent::process() on the
* CountedDrainEvent. When counter in the CountedDrainEvent reaches
* zero, the simulation stops.
*
* <li>Check if any object still needs draining, if so repeat the
* process above.
*
* <li>Serialize objects, switch CPU model, or change timing model.
*
* <li>Call SimObject::resume() and continue the simulation.
* <li>Drainable::drainResume() if resuming from a checkpoint.
* </ol>
*
* @note Whenever a method is called on all objects in the simulator's
@ -114,42 +82,8 @@ class Event;
* SimObject.py). This has the effect of calling the method on the
* parent node <i>before</i> its children.
*/
class SimObject : public EventManager, public Serializable
class SimObject : public EventManager, public Serializable, public Drainable
{
public:
/**
* Object drain/handover states
*
* An object starts out in the Running state. When the simulator
* prepares to take a snapshot or prepares a CPU for handover, it
* calls the drain() method to transfer the object into the
* Draining or Drained state. If any object enters the Draining
* state (drain() returning >0), simulation continues until it all
* objects have entered the Drained.
*
* The before resuming simulation, the simulator calls resume() to
* transfer the object to the Running state.
*
* \note Even though the state of an object (visible to the rest
* of the world through getState()) could be used to determine if
* all objects have entered the Drained state, the protocol is
* actually a bit more elaborate. See drain() for details.
*/
enum State {
Running, /** Running normally */
Draining, /** Draining buffers pending serialization/handover */
Drained /** Buffers drained, ready for serialization/handover */
};
private:
State state;
protected:
void changeState(State new_state) { state = new_state; }
public:
State getState() { return state; }
private:
typedef std::vector<SimObject *> SimObjectList;
@ -216,45 +150,18 @@ class SimObject : public EventManager, public Serializable
*/
virtual void startup();
/**
* Provide a default implementation of the drain interface that
* simply returns 0 (draining completed) and sets the drain state
* to Drained.
*/
unsigned int drain(DrainManager *drainManger);
/**
* Serialize all SimObjects in the system.
*/
static void serializeAll(std::ostream &os);
/**
* Determine if an object needs draining and register a drain
* event.
*
* When draining the state of an object, the simulator calls drain
* with a pointer to a drain event. If the object does not need
* further simulation to drain internal buffers, it switched to
* the Drained state and returns 0, otherwise it switches to the
* Draining state and returns the number of times that it will
* call Event::process() on the drain event. Most objects are
* expected to return either 0 or 1.
*
* The default implementation simply switches to the Drained state
* and returns 0.
*
* @note An object that has entered the Drained state can be
* disturbed by other objects in the system and consequently be
* forced to enter the Draining state again. The simulator
* therefore repeats the draining process until all objects return
* 0 on the first call to drain().
*
* @param drain_event Event to use to inform the simulator when
* the draining has completed.
*
* @return 0 if the object is ready for serialization now, >0 if
* it needs further simulation.
*/
virtual unsigned int drain(Event *drain_event);
/**
* Switch an object in the Drained stated into the Running state.
*/
virtual void resume();
#ifdef DEBUG
public:
bool doDebugBreak;

View file

@ -181,7 +181,7 @@ System::getMasterPort(const std::string &if_name, PortID idx)
void
System::setMemoryMode(Enums::MemoryMode mode)
{
assert(getState() == Drained);
assert(getDrainState() == Drainable::Drained);
memoryMode = mode;
}
@ -328,10 +328,17 @@ System::isMemAddr(Addr addr) const
return physmem.isMemAddr(addr);
}
void
System::resume()
unsigned int
System::drain(DrainManager *dm)
{
SimObject::resume();
setDrainState(Drainable::Drained);
return 0;
}
void
System::drainResume()
{
Drainable::drainResume();
totalNumInsts = 0;
}

View file

@ -382,7 +382,9 @@ class System : public MemObject
void serialize(std::ostream &os);
void unserialize(Checkpoint *cp, const std::string &section);
virtual void resume();
unsigned int drain(DrainManager *dm);
void drainResume();
public:
Counter totalNumInsts;