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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -110,11 +110,11 @@ TrafficGen::initState()
} }
unsigned int unsigned int
TrafficGen::drain(Event* drain_event) TrafficGen::drain(DrainManager *dm)
{ {
// @todo we should also stop putting new requests in the queue and // @todo we should also stop putting new requests in the queue and
// either interrupt the current state or wait for a transition // either interrupt the current state or wait for a transition
return port.drain(drain_event); return port.drain(dm);
} }
void void

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -68,7 +68,7 @@ class IGbE : public EtherDevice
uint16_t flash[iGbReg::EEPROM_SIZE]; uint16_t flash[iGbReg::EEPROM_SIZE];
// The drain event if we have one // The drain event if we have one
Event *drainEvent; DrainManager *drainManager;
// cached parameters from params struct // cached parameters from params struct
bool useFlowControl; bool useFlowControl;
@ -347,7 +347,7 @@ class IGbE : public EtherDevice
virtual void updateHead(long h) { igbe->regs.rdh(h); } virtual void updateHead(long h) { igbe->regs.rdh(h); }
virtual void enableSm(); virtual void enableSm();
virtual void fetchAfterWb() { virtual void fetchAfterWb() {
if (!igbe->rxTick && igbe->getState() == SimObject::Running) if (!igbe->rxTick && igbe->getDrainState() == Drainable::Running)
fetchDescriptors(); fetchDescriptors();
} }
@ -409,7 +409,7 @@ class IGbE : public EtherDevice
virtual void enableSm(); virtual void enableSm();
virtual void actionAfterWb(); virtual void actionAfterWb();
virtual void fetchAfterWb() { virtual void fetchAfterWb() {
if (!igbe->txTick && igbe->getState() == SimObject::Running) if (!igbe->txTick && igbe->getDrainState() == Drainable::Running)
fetchDescriptors(); fetchDescriptors();
} }
@ -535,8 +535,9 @@ class IGbE : public EtherDevice
virtual void serialize(std::ostream &os); virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section); 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", panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
dmaState, devState); dmaState, devState);
if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD); schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
return; return;
} else } else
@ -404,7 +404,7 @@ IdeDisk::doDmaRead()
curPrd.getByteCount(), TheISA::PageBytes); curPrd.getByteCount(), TheISA::PageBytes);
} }
if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD); schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
return; return;
} else if (!dmaReadCG->done()) { } else if (!dmaReadCG->done()) {
@ -481,7 +481,7 @@ IdeDisk::doDmaWrite()
dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(), dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
curPrd.getByteCount(), TheISA::PageBytes); curPrd.getByteCount(), TheISA::PageBytes);
} }
if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD); schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n"); DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
return; return;

View file

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

View file

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

View file

@ -1069,7 +1069,7 @@ NSGigE::doRxDmaRead()
assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting); assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting);
rxDmaState = dmaReading; rxDmaState = dmaReading;
if (dmaPending() || getState() != Running) if (dmaPending() || getDrainState() != Drainable::Running)
rxDmaState = dmaReadWaiting; rxDmaState = dmaReadWaiting;
else else
dmaRead(rxDmaAddr, rxDmaLen, &rxDmaReadEvent, (uint8_t*)rxDmaData); dmaRead(rxDmaAddr, rxDmaLen, &rxDmaReadEvent, (uint8_t*)rxDmaData);
@ -1100,7 +1100,7 @@ NSGigE::doRxDmaWrite()
assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting); assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting);
rxDmaState = dmaWriting; rxDmaState = dmaWriting;
if (dmaPending() || getState() != Running) if (dmaPending() || getDrainState() != Running)
rxDmaState = dmaWriteWaiting; rxDmaState = dmaWriteWaiting;
else else
dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaWriteEvent, (uint8_t*)rxDmaData); dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaWriteEvent, (uint8_t*)rxDmaData);
@ -1518,7 +1518,7 @@ NSGigE::doTxDmaRead()
assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting); assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting);
txDmaState = dmaReading; txDmaState = dmaReading;
if (dmaPending() || getState() != Running) if (dmaPending() || getDrainState() != Running)
txDmaState = dmaReadWaiting; txDmaState = dmaReadWaiting;
else else
dmaRead(txDmaAddr, txDmaLen, &txDmaReadEvent, (uint8_t*)txDmaData); dmaRead(txDmaAddr, txDmaLen, &txDmaReadEvent, (uint8_t*)txDmaData);
@ -1549,7 +1549,7 @@ NSGigE::doTxDmaWrite()
assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting); assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting);
txDmaState = dmaWriting; txDmaState = dmaWriting;
if (dmaPending() || getState() != Running) if (dmaPending() || getDrainState() != Running)
txDmaState = dmaWriteWaiting; txDmaState = dmaWriteWaiting;
else else
dmaWrite(txDmaAddr, txDmaLen, &txDmaWriteEvent, (uint8_t*)txDmaData); dmaWrite(txDmaAddr, txDmaLen, &txDmaWriteEvent, (uint8_t*)txDmaData);
@ -2112,9 +2112,9 @@ NSGigE::recvPacket(EthPacketPtr packet)
void void
NSGigE::resume() NSGigE::drainResume()
{ {
SimObject::resume(); Drainable::drainResume();
// During drain we could have left the state machines in a waiting state and // 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. // 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 serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section); virtual void unserialize(Checkpoint *cp, const std::string &section);
virtual void resume(); void drainResume();
}; };
/* /*

View file

@ -157,14 +157,14 @@ PciDev::init()
} }
unsigned int unsigned int
PciDev::drain(Event *de) PciDev::drain(DrainManager *dm)
{ {
unsigned int count; 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) if (count)
changeState(Draining); setDrainState(Drainable::Draining);
else else
changeState(Drained); setDrainState(Drainable::Drained);
return count; return count;
} }

View file

@ -216,7 +216,7 @@ class PciDev : public DmaDevice
virtual void unserialize(Checkpoint *cp, const std::string &section); 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, virtual BaseSlavePort &getSlavePort(const std::string &if_name,
PortID idx = InvalidPortID) PortID idx = InvalidPortID)

View file

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

View file

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

View file

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

View file

@ -94,7 +94,7 @@ class BaseBus : public MemObject
* whereas a response layer holds master ports. * whereas a response layer holds master ports.
*/ */
template <typename PortClass> template <typename PortClass>
class Layer class Layer : public Drainable
{ {
public: public:
@ -118,7 +118,7 @@ class BaseBus : public MemObject
* *
* @return 1 if busy or waiting to retry, or 0 if idle * @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 * Get the bus layer's name
@ -206,8 +206,8 @@ class BaseBus : public MemObject
/** the clock speed for the bus layer */ /** the clock speed for the bus layer */
Tick clock; Tick clock;
/** event for signalling when drained */ /** manager to signal when drained */
Event * drainEvent; DrainManager *drainManager;
/** /**
* An array of ports that retry should be called * An array of ports that retry should be called
@ -366,7 +366,7 @@ class BaseBus : public MemObject
BaseSlavePort& getSlavePort(const std::string& if_name, BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID); 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), blocked(0),
noTargetMSHR(NULL), noTargetMSHR(NULL),
missCount(p->max_miss_count), missCount(p->max_miss_count),
drainEvent(NULL), drainManager(NULL),
addrRanges(p->addr_ranges.begin(), p->addr_ranges.end()), addrRanges(p->addr_ranges.begin(), p->addr_ranges.end()),
system(p->system) system(p->system)
{ {
@ -749,19 +749,19 @@ BaseCache::regStats()
} }
unsigned int 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 // Set status
if (count != 0) { if (count != 0) {
drainEvent = de; drainManager = dm;
changeState(SimObject::Draining); setDrainState(Drainable::Draining);
DPRINTF(Drain, "Cache not drained\n"); DPRINTF(Drain, "Cache not drained\n");
return count; return count;
} }
changeState(SimObject::Drained); setDrainState(Drainable::Drained);
return 0; return 0;
} }

View file

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

View file

@ -508,10 +508,10 @@ CoherentBus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
} }
unsigned int unsigned int
CoherentBus::drain(Event *de) CoherentBus::drain(DrainManager *dm)
{ {
// sum up the individual layers // 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 * CoherentBus *

View file

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

View file

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

View file

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

View file

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

View file

@ -57,12 +57,13 @@
#include "mem/port.hh" #include "mem/port.hh"
#include "sim/eventq.hh" #include "sim/eventq.hh"
#include "sim/drain.hh"
/** /**
* A packet queue is a class that holds deferred packets and later * A packet queue is a class that holds deferred packets and later
* sends them using the associated slave port or master port. * sends them using the associated slave port or master port.
*/ */
class PacketQueue class PacketQueue : public Drainable
{ {
private: private:
/** A deferred packet, buffered to transmit later. */ /** A deferred packet, buffered to transmit later. */
@ -95,9 +96,9 @@ class PacketQueue
**/ **/
EventWrapper<PacketQueue, &PacketQueue::processSendEvent> sendEvent; 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.*/ * here.*/
Event *drainEvent; DrainManager *drainManager;
protected: protected:
@ -207,13 +208,7 @@ class PacketQueue
*/ */
void retry(); void retry();
/** unsigned int drain(DrainManager *dm);
* 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);
}; };
class MasterPacketQueue : public PacketQueue class MasterPacketQueue : public PacketQueue

View file

@ -97,13 +97,7 @@ class QueuedSlavePort : public SlavePort
* functional request. */ * functional request. */
bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); } bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); }
/** unsigned int drain(DrainManager *dm) { return queue.drain(dm); }
* 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); }
}; };
class QueuedMasterPort : public MasterPort class QueuedMasterPort : public MasterPort
@ -156,13 +150,7 @@ class QueuedMasterPort : public MasterPort
* functional request. */ * functional request. */
bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); } bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); }
/** unsigned int drain(DrainManager *dm) { return queue.drain(dm); }
* 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); }
}; };
#endif // __MEM_QPORT_HH__ #endif // __MEM_QPORT_HH__

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -341,10 +341,10 @@ class SimpleDRAM : public AbstractMemory
*/ */
std::list<DRAMPacket*> dramRespQueue; 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. * here.
*/ */
Event *drainEvent; DrainManager *drainManager;
/** /**
* Multi-dimensional vector of banks, first dimension is ranks, * Multi-dimensional vector of banks, first dimension is ranks,
@ -459,7 +459,7 @@ class SimpleDRAM : public AbstractMemory
SimpleDRAM(const SimpleDRAMParams* p); SimpleDRAM(const SimpleDRAMParams* p);
unsigned int drain(Event* de); unsigned int drain(DrainManager* dm);
virtual BaseSlavePort& getSlavePort(const std::string& if_name, virtual BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID); PortID idx = InvalidPortID);

View file

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

View file

@ -123,7 +123,7 @@ class SimpleMemory : public AbstractMemory
SimpleMemory(const SimpleMemoryParams *p); SimpleMemory(const SimpleMemoryParams *p);
virtual ~SimpleMemory() { } virtual ~SimpleMemory() { }
unsigned int drain(Event* de); unsigned int drain(DrainManager *dm);
virtual BaseSlavePort& getSlavePort(const std::string& if_name, virtual BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID); 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/core.i')
SwigSource('m5.internal', 'swig/debug.i') SwigSource('m5.internal', 'swig/debug.i')
SwigSource('m5.internal', 'swig/drain.i')
SwigSource('m5.internal', 'swig/event.i') SwigSource('m5.internal', 'swig/event.i')
SwigSource('m5.internal', 'swig/pyobject.i') SwigSource('m5.internal', 'swig/pyobject.i')
SwigSource('m5.internal', 'swig/range.i') SwigSource('m5.internal', 'swig/range.i')

View file

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

View file

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

View file

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

View file

@ -169,19 +169,19 @@ def doDrain(root):
# be drained. # be drained.
def drain(root): def drain(root):
all_drained = False all_drained = False
drain_event = internal.event.createCountedDrain() dm = internal.drain.createDrainManager()
unready_objs = sum(obj.drain(drain_event) for obj in root.descendants()) 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 we've got some objects that can't drain immediately, then simulate
if unready_objs > 0: if unready_objs > 0:
drain_event.setCount(unready_objs) dm.setCount(unready_objs)
simulate() simulate()
else: else:
all_drained = True all_drained = True
internal.event.cleanupCountedDrain(drain_event) internal.drain.cleanupDrainManager(dm)
return all_drained return all_drained
def resume(root): def resume(root):
for obj in root.descendants(): obj.resume() for obj in root.descendants(): obj.drainResume()
def checkpoint(dir): def checkpoint(dir):
root = objects.Root.getInstance() 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 // This must follow eventq.hh
%include "python/swig/pyevent.hh" %include "python/swig/pyevent.hh"
struct CountedDrainEvent : public Event
{
void setCount(int _count);
};
// minimal definition of SimExitEvent interface to wrap // minimal definition of SimExitEvent interface to wrap
class SimLoopExitEvent : public Event class SimLoopExitEvent : public Event
{ {

View file

@ -65,22 +65,3 @@ PythonEvent::process()
// reference count must be decremented. // reference count must be decremented.
Py_DECREF(object); 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(); virtual void process();
}; };
CountedDrainEvent *createCountedDrain();
void cleanupCountedDrain(Event *counted_drain);
#endif // __PYTHON_SWIG_PYEVENT_HH__ #endif // __PYTHON_SWIG_PYEVENT_HH__

View file

@ -44,6 +44,7 @@ Source('init.cc')
Source('main.cc', main=True, skip_lib=True) Source('main.cc', main=True, skip_lib=True)
Source('root.cc') Source('root.cc')
Source('serialize.cc') Source('serialize.cc')
Source('drain.cc')
Source('sim_events.cc') Source('sim_events.cc')
Source('sim_object.cc') Source('sim_object.cc')
Source('simulate.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); \ fromSimObject(objptr, sptr); \
} while (0) } while (0)
/* /**
* Basic support for object serialization. * 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 class Serializable
{ {

View file

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

View file

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

View file

@ -45,6 +45,7 @@
#include "enums/MemoryMode.hh" #include "enums/MemoryMode.hh"
#include "params/SimObject.hh" #include "params/SimObject.hh"
#include "sim/drain.hh"
#include "sim/eventq.hh" #include "sim/eventq.hh"
#include "sim/serialize.hh" #include "sim/serialize.hh"
@ -72,40 +73,7 @@ class Event;
* </ul> * </ul>
* <li>SimObject::resetStats() * <li>SimObject::resetStats()
* <li>SimObject::startup() * <li>SimObject::startup()
* <li>SimObject::resume() if resuming from a checkpoint. * <li>Drainable::drainResume() 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.
* </ol> * </ol>
* *
* @note Whenever a method is called on all objects in the simulator's * @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 * SimObject.py). This has the effect of calling the method on the
* parent node <i>before</i> its children. * 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: private:
typedef std::vector<SimObject *> SimObjectList; typedef std::vector<SimObject *> SimObjectList;
@ -216,45 +150,18 @@ class SimObject : public EventManager, public Serializable
*/ */
virtual void startup(); 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. * Serialize all SimObjects in the system.
*/ */
static void serializeAll(std::ostream &os); 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 #ifdef DEBUG
public: public:
bool doDebugBreak; bool doDebugBreak;

View file

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

View file

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