using namespace ArmISA;
TableWalker::TableWalker(const Params *p)
- : MemObject(p), port(this, params()->sys), drainEvent(NULL),
+ : MemObject(p), port(this, params()->sys), drainManager(NULL),
tlb(NULL), currState(NULL), pending(false),
masterId(p->sys->getMasterId(name())),
numSquashable(p->num_squash_per_cycle),
void
TableWalker::completeDrain()
{
- if (drainEvent && stateQueueL1.empty() && stateQueueL2.empty() &&
+ if (drainManager && stateQueueL1.empty() && stateQueueL2.empty() &&
pendingQueue.empty()) {
- changeState(Drained);
+ setDrainState(Drainable::Drained);
DPRINTF(Drain, "TableWalker done draining, processing drain event\n");
- drainEvent->process();
- drainEvent = NULL;
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
}
unsigned int
-TableWalker::drain(Event *de)
+TableWalker::drain(DrainManager *dm)
{
- unsigned int count = port.drain(de);
+ unsigned int count = port.drain(dm);
if (stateQueueL1.empty() && stateQueueL2.empty() &&
pendingQueue.empty()) {
- changeState(Drained);
+ setDrainState(Drainable::Drained);
DPRINTF(Drain, "TableWalker free, no need to drain\n");
// table walker is drained, but its ports may still need to be drained
return count;
} else {
- drainEvent = de;
- changeState(Draining);
+ drainManager = dm;
+ setDrainState(Drainable::Draining);
DPRINTF(Drain, "TableWalker not drained\n");
// return port drain count plus the table walker itself needs to drain
}
void
-TableWalker::resume()
+TableWalker::drainResume()
{
- MemObject::resume();
+ Drainable::drainResume();
if ((params()->sys->getMemoryMode() == Enums::timing) && currState) {
delete currState;
currState = NULL;
SnoopingDmaPort port;
/** If we're draining keep the drain event around until we're drained */
- Event *drainEvent;
+ DrainManager *drainManager;
/** TLB that is initiating these table walks */
TLB *tlb;
/** Checks if all state is cleared and if so, completes drain */
void completeDrain();
- virtual unsigned int drain(Event *de);
- virtual void resume();
+ unsigned int drain(DrainManager *dm);
+ void drainResume();
virtual BaseMasterPort& getMasterPort(const std::string &if_name,
PortID idx = InvalidPortID);
if (!tickEvent.scheduled()) {
if (_status == SwitchedOut ||
- getState() == SimObject::Drained) {
+ getDrainState() == Drainable::Drained) {
DPRINTF(O3CPU, "Switched out!\n");
// increment stat
lastRunningCycle = curCycle();
void
FullO3CPU<Impl>::serialize(std::ostream &os)
{
- SimObject::State so_state = SimObject::getState();
+ Drainable::State so_state(getDrainState());
SERIALIZE_ENUM(so_state);
BaseCPU::serialize(os);
nameOut(os, csprintf("%s.tickEvent", name()));
void
FullO3CPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion)
{
- SimObject::State so_state;
+ Drainable::State so_state;
UNSERIALIZE_ENUM(so_state);
BaseCPU::unserialize(cp, section);
tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
template <class Impl>
unsigned int
-FullO3CPU<Impl>::drain(Event *drain_event)
+FullO3CPU<Impl>::drain(DrainManager *drain_manager)
{
DPRINTF(O3CPU, "Switching out\n");
// Wake the CPU and record activity so everything can drain out if
// the CPU was not able to immediately drain.
- if (getState() != SimObject::Drained) {
- // A bit of a hack...set the drainEvent after all the drain()
+ if (getDrainState() != Drainable::Drained) {
+ // A bit of a hack...set the drainManager after all the drain()
// calls have been made, that way if all of the stages drain
// immediately, the signalDrained() function knows not to call
// process on the drain event.
- drainEvent = drain_event;
+ drainManager = drain_manager;
wakeCPU();
activityRec.activity();
template <class Impl>
void
-FullO3CPU<Impl>::resume()
+FullO3CPU<Impl>::drainResume()
{
fetch.resume();
decode.resume();
iew.resume();
commit.resume();
- changeState(SimObject::Running);
+ setDrainState(Drainable::Running);
if (_status == SwitchedOut)
return;
if (tickEvent.scheduled())
tickEvent.squash();
- changeState(SimObject::Drained);
+ setDrainState(Drainable::Drained);
BaseCPU::switchOut();
- if (drainEvent) {
+ if (drainManager) {
DPRINTF(Drain, "CPU done draining, processing drain event\n");
- drainEvent->process();
- drainEvent = NULL;
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
}
assert(drainCount <= 5);
/** Starts draining the CPU's pipeline of all instructions in
* order to stop all memory accesses. */
- virtual unsigned int drain(Event *drain_event);
+ unsigned int drain(DrainManager *drain_manager);
/** Resumes execution after a drain. */
- virtual void resume();
+ void drainResume();
/** Signals to this CPU that a stage has completed switching out. */
void signalDrained();
/** Pointer to the system. */
System *system;
- /** Event to call process() on once draining has completed. */
- Event *drainEvent;
+ /** DrainManager to notify when draining has completed. */
+ DrainManager *drainManager;
/** Counter of how many stages have completed draining. */
int drainCount;
void
AtomicSimpleCPU::serialize(ostream &os)
{
- SimObject::State so_state = SimObject::getState();
+ Drainable::State so_state(getDrainState());
SERIALIZE_ENUM(so_state);
SERIALIZE_SCALAR(locked);
BaseSimpleCPU::serialize(os);
void
AtomicSimpleCPU::unserialize(Checkpoint *cp, const string §ion)
{
- SimObject::State so_state;
+ Drainable::State so_state;
UNSERIALIZE_ENUM(so_state);
UNSERIALIZE_SCALAR(locked);
BaseSimpleCPU::unserialize(cp, section);
tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
}
+unsigned int
+AtomicSimpleCPU::drain(DrainManager *drain_manager)
+{
+ setDrainState(Drainable::Drained);
+ return 0;
+}
+
void
-AtomicSimpleCPU::resume()
+AtomicSimpleCPU::drainResume()
{
if (_status == Idle || _status == SwitchedOut)
return;
DPRINTF(SimpleCPU, "Resume\n");
assert(system->getMemoryMode() == Enums::atomic);
- changeState(SimObject::Running);
+ setDrainState(Drainable::Running);
if (thread->status() == ThreadContext::Active) {
if (!tickEvent.scheduled())
schedule(tickEvent, nextCycle());
void
AtomicSimpleCPU::switchOut()
{
- assert(_status == Running || _status == Idle);
+ assert(_status == BaseSimpleCPU::Running || _status == Idle);
_status = SwitchedOut;
tickEvent.squash();
ThreadID size = threadContexts.size();
for (ThreadID i = 0; i < size; ++i) {
ThreadContext *tc = threadContexts[i];
- if (tc->status() == ThreadContext::Active && _status != Running) {
- _status = Running;
+ if (tc->status() == ThreadContext::Active &&
+ _status != BaseSimpleCPU::Running) {
+ _status = BaseSimpleCPU::Running;
schedule(tickEvent, nextCycle());
break;
}
}
- if (_status != Running) {
+ if (_status != BaseSimpleCPU::Running) {
_status = Idle;
}
assert(threadContexts.size() == 1);
//Make sure ticks are still on multiples of cycles
schedule(tickEvent, clockEdge(delay));
- _status = Running;
+ _status = BaseSimpleCPU::Running;
}
if (_status == Idle)
return;
- assert(_status == Running);
+ assert(_status == BaseSimpleCPU::Running);
// tick event may not be scheduled if this gets called from inside
// an instruction's execution, e.g. "quiesce"
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string §ion);
- virtual void resume();
+
+ unsigned int drain(DrainManager *drain_manager);
+ void drainResume();
void switchOut();
void takeOverFrom(BaseCPU *oldCPU);
{
_status = Idle;
- changeState(SimObject::Running);
+ setDrainState(Drainable::Running);
system->totalNumInsts = 0;
}
void
TimingSimpleCPU::serialize(ostream &os)
{
- SimObject::State so_state = SimObject::getState();
+ Drainable::State so_state(getDrainState());
SERIALIZE_ENUM(so_state);
BaseSimpleCPU::serialize(os);
}
void
TimingSimpleCPU::unserialize(Checkpoint *cp, const string §ion)
{
- SimObject::State so_state;
+ Drainable::State so_state;
UNSERIALIZE_ENUM(so_state);
BaseSimpleCPU::unserialize(cp, section);
}
unsigned int
-TimingSimpleCPU::drain(Event *drain_event)
+TimingSimpleCPU::drain(DrainManager *drain_manager)
{
// TimingSimpleCPU is ready to drain if it's not waiting for
// an access to complete.
- if (_status == Idle || _status == Running || _status == SwitchedOut) {
- changeState(SimObject::Drained);
+ if (_status == Idle ||
+ _status == BaseSimpleCPU::Running ||
+ _status == SwitchedOut) {
+ setDrainState(Drainable::Drained);
return 0;
} else {
- changeState(SimObject::Draining);
- drainEvent = drain_event;
+ setDrainState(Drainable::Draining);
+ drainManager = drain_manager;
DPRINTF(Drain, "CPU not drained\n");
return 1;
}
}
void
-TimingSimpleCPU::resume()
+TimingSimpleCPU::drainResume()
{
DPRINTF(SimpleCPU, "Resume\n");
if (_status != SwitchedOut && _status != Idle) {
schedule(fetchEvent, nextCycle());
}
- changeState(SimObject::Running);
+ setDrainState(Drainable::Running);
}
void
TimingSimpleCPU::switchOut()
{
- assert(_status == Running || _status == Idle);
+ assert(_status == BaseSimpleCPU::Running || _status == Idle);
_status = SwitchedOut;
numCycles += curCycle() - previousCycle;
// running and schedule its tick event.
for (int i = 0; i < threadContexts.size(); ++i) {
ThreadContext *tc = threadContexts[i];
- if (tc->status() == ThreadContext::Active && _status != Running) {
- _status = Running;
+ if (tc->status() == ThreadContext::Active &&
+ _status != BaseSimpleCPU::Running) {
+ _status = BaseSimpleCPU::Running;
break;
}
}
- if (_status != Running) {
+ if (_status != BaseSimpleCPU::Running) {
_status = Idle;
}
assert(threadContexts.size() == 1);
assert(_status == Idle);
notIdleFraction++;
- _status = Running;
+ _status = BaseSimpleCPU::Running;
// kick things off by initiating the fetch of the next instruction
schedule(fetchEvent, clockEdge(delay));
if (_status == Idle)
return;
- assert(_status == Running);
+ assert(_status == BaseSimpleCPU::Running);
// just change status to Idle... if status != Running,
// completeInst() will not initiate fetch of next instruction.
postExecute();
- if (getState() == SimObject::Draining) {
+ if (getDrainState() == Drainable::Draining) {
advancePC(fault);
completeDrain();
} else {
void
TimingSimpleCPU::finishTranslation(WholeTranslationState *state)
{
- _status = Running;
+ _status = BaseSimpleCPU::Running;
if (state->getFault() != NoFault) {
if (state->isPrefetch()) {
bool needToFetch = !isRomMicroPC(pcState.microPC()) && !curMacroStaticInst;
if (needToFetch) {
- _status = Running;
+ _status = BaseSimpleCPU::Running;
Request *ifetch_req = new Request();
ifetch_req->setThreadContext(_cpuId, /* thread ID */ 0);
setupFetchRequest(ifetch_req);
DPRINTF(SimpleCPU, "Translation of addr %#x faulted\n", req->getVaddr());
delete req;
// fetch fault: advance directly to next instruction (fault handler)
- _status = Running;
+ _status = BaseSimpleCPU::Running;
advanceInst(fault);
}
if (!stayAtPC)
advancePC(fault);
- if (_status == Running) {
+ if (_status == BaseSimpleCPU::Running) {
// kick off fetch of next instruction... callback from icache
// response will cause that instruction to be executed,
// keeping the CPU running.
assert(!pkt || !pkt->isError());
assert(_status == IcacheWaitResponse);
- _status = Running;
+ _status = BaseSimpleCPU::Running;
numCycles += curCycle() - previousCycle;
previousCycle = curCycle();
- if (getState() == SimObject::Draining) {
+ if (getDrainState() == Drainable::Draining) {
if (pkt) {
delete pkt->req;
delete pkt;
// If we're not running now the instruction will complete in a dcache
// response callback or the instruction faulted and has started an
// ifetch
- if (_status == Running) {
+ if (_status == BaseSimpleCPU::Running) {
if (fault != NoFault && traceData) {
// If there was a fault, we shouldn't trace this instruction.
delete traceData;
}
}
- _status = Running;
+ _status = BaseSimpleCPU::Running;
Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
postExecute();
- if (getState() == SimObject::Draining) {
+ if (getDrainState() == Drainable::Draining) {
advancePC(fault);
completeDrain();
TimingSimpleCPU::completeDrain()
{
DPRINTF(Drain, "CPU done draining, processing drain event\n");
- changeState(SimObject::Drained);
- drainEvent->process();
+ setDrainState(Drainable::Drained);
+ drainManager->signalDrainDone();
}
bool
virtual void init();
public:
- Event *drainEvent;
+ DrainManager *drainManager;
private:
void
markDelayed()
{
- assert(cpu->_status == Running);
+ assert(cpu->_status == BaseSimpleCPU::Running);
cpu->_status = ITBWaitResponse;
}
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string §ion);
- virtual unsigned int drain(Event *drain_event);
- virtual void resume();
+ unsigned int drain(DrainManager *drain_manager);
+ void drainResume();
void switchOut();
void takeOverFrom(BaseCPU *oldCPU);
}
unsigned int
-TrafficGen::drain(Event* drain_event)
+TrafficGen::drain(DrainManager *dm)
{
// @todo we should also stop putting new requests in the queue and
// either interrupt the current state or wait for a transition
- return port.drain(drain_event);
+ return port.drain(dm);
}
void
void initState();
- unsigned int drain(Event *drain_event);
+ unsigned int drain(DrainManager *dm);
void serialize(std::ostream &os);
ce(_ce), channelId(cid), busy(false), underReset(false),
refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin),
latAfterCompletion(ce->params()->latAfterCompletion),
- completionDataReg(0), nextState(Idle), drainEvent(NULL),
+ completionDataReg(0), nextState(Idle), drainManager(NULL),
fetchCompleteEvent(this), addrCompleteEvent(this),
readCompleteEvent(this), writeCompleteEvent(this),
statusCompleteEvent(this)
cr.status.dma_transfer_status(0);
nextState = DescriptorFetch;
fetchAddress = cr.descChainAddr;
- if (ce->getState() == SimObject::Running)
+ if (ce->getDrainState() == Drainable::Running)
fetchDescriptor(cr.descChainAddr);
} else if (cr.command.append_dma()) {
if (!busy) {
nextState = AddressFetch;
- if (ce->getState() == SimObject::Running)
+ if (ce->getDrainState() == Drainable::Running)
fetchNextAddr(lastDescriptorAddr);
} else
refreshNext = true;
bool
CopyEngine::CopyEngineChannel::inDrain()
{
- if (ce->getState() == SimObject::Draining) {
+ if (ce->getDrainState() == Drainable::Draining) {
DPRINTF(Drain, "CopyEngine done draining, processing drain event\n");
- assert(drainEvent);
- drainEvent->process();
- drainEvent = NULL;
+ assert(drainManager);
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
- return ce->getState() != SimObject::Running;
+ return ce->getDrainState() != Drainable::Running;
}
unsigned int
-CopyEngine::CopyEngineChannel::drain(Event *de)
+CopyEngine::CopyEngineChannel::drain(DrainManager *dm)
{
- if (nextState == Idle || ce->getState() != SimObject::Running)
+ if (nextState == Idle || ce->getDrainState() != Drainable::Running)
return 0;
unsigned int count = 1;
- count += cePort.drain(de);
+ count += cePort.drain(dm);
DPRINTF(Drain, "CopyEngineChannel not drained\n");
- drainEvent = de;
+ this->drainManager = dm;
return count;
}
unsigned int
-CopyEngine::drain(Event *de)
+CopyEngine::drain(DrainManager *dm)
{
unsigned int count;
- count = pioPort.drain(de) + dmaPort.drain(de) + configPort.drain(de);
+ count = pioPort.drain(dm) + dmaPort.drain(dm) + configPort.drain(dm);
for (int x = 0;x < chan.size(); x++)
- count += chan[x]->drain(de);
+ count += chan[x]->drain(dm);
if (count)
- changeState(Draining);
+ setDrainState(Draining);
else
- changeState(Drained);
+ setDrainState(Drained);
DPRINTF(Drain, "CopyEngine not drained\n");
return count;
}
void
-CopyEngine::resume()
+CopyEngine::drainResume()
{
- SimObject::resume();
+ Drainable::drainResume();
for (int x = 0;x < chan.size(); x++)
- chan[x]->resume();
+ chan[x]->drainResume();
}
void
-CopyEngine::CopyEngineChannel::resume()
+CopyEngine::CopyEngineChannel::drainResume()
{
DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState);
restartStateMachine();
#include "dev/copy_engine_defs.hh"
#include "dev/pcidev.hh"
#include "params/CopyEngine.hh"
+#include "sim/drain.hh"
#include "sim/eventq.hh"
class CopyEngine : public PciDev
{
- class CopyEngineChannel
+ class CopyEngineChannel : public Drainable
{
private:
DmaPort cePort;
ChannelState nextState;
- Event *drainEvent;
+ DrainManager *drainManager;
public:
CopyEngineChannel(CopyEngine *_ce, int cid);
virtual ~CopyEngineChannel();
void channelRead(PacketPtr pkt, Addr daddr, int size);
void channelWrite(PacketPtr pkt, Addr daddr, int size);
- unsigned int drain(Event *de);
- void resume();
+ unsigned int drain(DrainManager *drainManger);
+ void drainResume();
+
void serialize(std::ostream &os);
void unserialize(Checkpoint *cp, const std::string §ion);
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string §ion);
- virtual unsigned int drain(Event *de);
- virtual void resume();
+
+ unsigned int drain(DrainManager *drainManger);
+ void drainResume();
};
#endif //__DEV_COPY_ENGINE_HH__
DmaPort::DmaPort(MemObject *dev, System *s)
: MasterPort(dev->name() + ".dma", dev), device(dev), sendEvent(this),
sys(s), masterId(s->getMasterId(dev->name())),
- pendingCount(0), drainEvent(NULL),
+ pendingCount(0), drainManager(NULL),
inRetry(false)
{ }
delete pkt;
// we might be drained at this point, if so signal the drain event
- if (pendingCount == 0 && drainEvent) {
- drainEvent->process();
- drainEvent = NULL;
+ if (pendingCount == 0 && drainManager) {
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
}
}
unsigned int
-DmaDevice::drain(Event *de)
+DmaDevice::drain(DrainManager *dm)
{
- unsigned int count = pioPort.drain(de) + dmaPort.drain(de);
+ unsigned int count = pioPort.drain(dm) + dmaPort.drain(dm);
if (count)
- changeState(Draining);
+ setDrainState(Drainable::Draining);
else
- changeState(Drained);
+ setDrainState(Drainable::Drained);
return count;
}
unsigned int
-DmaPort::drain(Event *de)
+DmaPort::drain(DrainManager *dm)
{
if (pendingCount == 0)
return 0;
- drainEvent = de;
+ drainManager = dm;
DPRINTF(Drain, "DmaPort not drained\n");
return 1;
}
#include "dev/io_device.hh"
#include "params/DmaDevice.hh"
+#include "sim/drain.hh"
class DmaPort : public MasterPort
{
/** If we need to drain, keep the drain event around until we're done
* here.*/
- Event *drainEvent;
+ DrainManager *drainManager;
/** If the port is currently waiting for a retry before it can
* send whatever it is that it's sending. */
bool dmaPending() const { return pendingCount > 0; }
unsigned cacheBlockSize() const { return peerBlockSize(); }
- unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *drainManger);
};
class DmaDevice : public PioDevice
virtual void init();
- virtual unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *drainManger);
unsigned cacheBlockSize() const { return dmaPort.cacheBlockSize(); }
using namespace Net;
IGbE::IGbE(const Params *p)
- : EtherDevice(p), etherInt(NULL), drainEvent(NULL),
+ : EtherDevice(p), etherInt(NULL), drainManager(NULL),
useFlowControl(p->use_flow_control),
rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size), rxTick(false),
txTick(false), txFifoTick(false), rxDmaPacket(false), pktOffset(0),
case REG_RDT:
regs.rdt = val;
DPRINTF(EthernetSM, "RXS: RDT Updated.\n");
- if (getState() == SimObject::Running) {
+ if (getDrainState() == Drainable::Running) {
DPRINTF(EthernetSM, "RXS: RDT Fetching Descriptors!\n");
rxDescCache.fetchDescriptors();
} else {
case REG_TDT:
regs.tdt = val;
DPRINTF(EthernetSM, "TXS: TX Tail pointer updated\n");
- if (getState() == SimObject::Running) {
+ if (getDrainState() == Drainable::Running) {
DPRINTF(EthernetSM, "TXS: TDT Fetching Descriptors!\n");
txDescCache.fetchDescriptors();
} else {
IGbE::DescCache<T>::writeback1()
{
// If we're draining delay issuing this DMA
- if (igbe->getState() != SimObject::Running) {
+ if (igbe->getDrainState() != Drainable::Running) {
igbe->schedule(wbDelayEvent, curTick() + igbe->wbDelay);
return;
}
IGbE::DescCache<T>::fetchDescriptors1()
{
// If we're draining delay issuing this DMA
- if (igbe->getState() != SimObject::Running) {
+ if (igbe->getDrainState() != Drainable::Running) {
igbe->schedule(fetchDelayEvent, curTick() + igbe->fetchDelay);
return;
}
void
IGbE::RxDescCache::enableSm()
{
- if (!igbe->drainEvent) {
+ if (!igbe->drainManager) {
igbe->rxTick = true;
igbe->restartClock();
}
void
IGbE::TxDescCache::enableSm()
{
- if (!igbe->drainEvent) {
+ if (!igbe->drainManager) {
igbe->txTick = true;
igbe->restartClock();
}
IGbE::restartClock()
{
if (!tickEvent.scheduled() && (rxTick || txTick || txFifoTick) &&
- getState() == SimObject::Running)
+ getDrainState() == Drainable::Running)
schedule(tickEvent, clockEdge(Cycles(1)));
}
unsigned int
-IGbE::drain(Event *de)
+IGbE::drain(DrainManager *dm)
{
unsigned int count;
- count = pioPort.drain(de) + dmaPort.drain(de);
+ count = pioPort.drain(dm) + dmaPort.drain(dm);
if (rxDescCache.hasOutstandingEvents() ||
txDescCache.hasOutstandingEvents()) {
count++;
- drainEvent = de;
+ drainManager = dm;
}
txFifoTick = false;
if (count) {
DPRINTF(Drain, "IGbE not drained\n");
- changeState(Draining);
+ setDrainState(Drainable::Draining);
} else
- changeState(Drained);
+ setDrainState(Drainable::Drained);
return count;
}
void
-IGbE::resume()
+IGbE::drainResume()
{
- SimObject::resume();
+ Drainable::drainResume();
txFifoTick = true;
txTick = true;
void
IGbE::checkDrain()
{
- if (!drainEvent)
+ if (!drainManager)
return;
txFifoTick = false;
if (!rxDescCache.hasOutstandingEvents() &&
!txDescCache.hasOutstandingEvents()) {
DPRINTF(Drain, "IGbE done draining, processing drain event\n");
- drainEvent->process();
- drainEvent = NULL;
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
}
bool success =
#endif
txFifo.push(txPacket);
- txFifoTick = true && !drainEvent;
+ txFifoTick = true && !drainManager;
assert(success);
txPacket = NULL;
anBegin("TXS", "Desc Writeback");
}
// restart the state machines if they are stopped
- rxTick = true && !drainEvent;
+ rxTick = true && !drainManager;
if ((rxTick || txTick) && !tickEvent.scheduled()) {
DPRINTF(EthernetSM,
"RXS: received packet into fifo, starting ticking\n");
// restart the tx state machines if they are stopped
// fifo to send another packet
// tx sm to put more data into the fifo
- txFifoTick = true && !drainEvent;
- if (txDescCache.descLeft() != 0 && !drainEvent)
+ txFifoTick = true && !drainManager;
+ if (txDescCache.descLeft() != 0 && !drainManager)
txTick = true;
restartClock();
uint16_t flash[iGbReg::EEPROM_SIZE];
// The drain event if we have one
- Event *drainEvent;
+ DrainManager *drainManager;
// cached parameters from params struct
bool useFlowControl;
virtual void updateHead(long h) { igbe->regs.rdh(h); }
virtual void enableSm();
virtual void fetchAfterWb() {
- if (!igbe->rxTick && igbe->getState() == SimObject::Running)
+ if (!igbe->rxTick && igbe->getDrainState() == Drainable::Running)
fetchDescriptors();
}
virtual void enableSm();
virtual void actionAfterWb();
virtual void fetchAfterWb() {
- if (!igbe->txTick && igbe->getState() == SimObject::Running)
+ if (!igbe->txTick && igbe->getDrainState() == Drainable::Running)
fetchDescriptors();
}
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string §ion);
- virtual unsigned int drain(Event *de);
- virtual void resume();
+
+ unsigned int drain(DrainManager *dm);
+ void drainResume();
};
panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
dmaState, devState);
- if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) {
+ if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
return;
} else
curPrd.getByteCount(), TheISA::PageBytes);
}
- if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) {
+ if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
return;
} else if (!dmaReadCG->done()) {
dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
curPrd.getByteCount(), TheISA::PageBytes);
}
- if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) {
+ if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
return;
}
unsigned int
-PioDevice::drain(Event *de)
+PioDevice::drain(DrainManager *dm)
{
unsigned int count;
- count = pioPort.drain(de);
+ count = pioPort.drain(dm);
if (count)
- changeState(Draining);
+ setDrainState(Drainable::Draining);
else
- changeState(Drained);
+ setDrainState(Drainable::Drained);
return count;
}
virtual void init();
- virtual unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *drainManger);
virtual BaseSlavePort &getSlavePort(const std::string &if_name,
PortID idx = InvalidPortID);
assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting);
rxDmaState = dmaReading;
- if (dmaPending() || getState() != Running)
+ if (dmaPending() || getDrainState() != Drainable::Running)
rxDmaState = dmaReadWaiting;
else
dmaRead(rxDmaAddr, rxDmaLen, &rxDmaReadEvent, (uint8_t*)rxDmaData);
assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting);
rxDmaState = dmaWriting;
- if (dmaPending() || getState() != Running)
+ if (dmaPending() || getDrainState() != Running)
rxDmaState = dmaWriteWaiting;
else
dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaWriteEvent, (uint8_t*)rxDmaData);
assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting);
txDmaState = dmaReading;
- if (dmaPending() || getState() != Running)
+ if (dmaPending() || getDrainState() != Running)
txDmaState = dmaReadWaiting;
else
dmaRead(txDmaAddr, txDmaLen, &txDmaReadEvent, (uint8_t*)txDmaData);
assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting);
txDmaState = dmaWriting;
- if (dmaPending() || getState() != Running)
+ if (dmaPending() || getDrainState() != Running)
txDmaState = dmaWriteWaiting;
else
dmaWrite(txDmaAddr, txDmaLen, &txDmaWriteEvent, (uint8_t*)txDmaData);
void
-NSGigE::resume()
+NSGigE::drainResume()
{
- SimObject::resume();
+ Drainable::drainResume();
// During drain we could have left the state machines in a waiting state and
// they wouldn't get out until some other event occured to kick them.
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string §ion);
- virtual void resume();
+ void drainResume();
};
/*
}
unsigned int
-PciDev::drain(Event *de)
+PciDev::drain(DrainManager *dm)
{
unsigned int count;
- count = pioPort.drain(de) + dmaPort.drain(de) + configPort.drain(de);
+ count = pioPort.drain(dm) + dmaPort.drain(dm) + configPort.drain(dm);
if (count)
- changeState(Draining);
+ setDrainState(Drainable::Draining);
else
- changeState(Drained);
+ setDrainState(Drainable::Drained);
return count;
}
virtual void unserialize(Checkpoint *cp, const std::string §ion);
- virtual unsigned int drain(Event *de);
+ virtual unsigned int drain(DrainManager *dm);
virtual BaseSlavePort &getSlavePort(const std::string &if_name,
PortID idx = InvalidPortID)
break;
case rxBeginCopy:
- if (dmaPending() || getState() != Running)
+ if (dmaPending() || getDrainState() != Drainable::Running)
goto exit;
rxDmaAddr = params()->platform->pciToDma(
break;
case txBeginCopy:
- if (dmaPending() || getState() != Running)
+ if (dmaPending() || getDrainState() != Drainable::Running)
goto exit;
txDmaAddr = params()->platform->pciToDma(
}
void
-Device::resume()
+Device::drainResume()
{
- SimObject::resume();
+ Drainable::drainResume();
// During drain we could have left the state machines in a waiting state and
// they wouldn't get out until some other event occured to kick them.
public:
virtual Tick read(PacketPtr pkt);
virtual Tick write(PacketPtr pkt);
- virtual void resume();
+ virtual void drainResume();
void prepareIO(int cpu, int index);
void prepareRead(int cpu, int index);
template <typename PortClass>
BaseBus::Layer<PortClass>::Layer(BaseBus& _bus, const std::string& _name,
Tick _clock) :
- bus(_bus), _name(_name), state(IDLE), clock(_clock), drainEvent(NULL),
+ Drainable(),
+ bus(_bus), _name(_name), state(IDLE), clock(_clock), drainManager(NULL),
releaseEvent(this)
{
}
// busy, and in the latter case the bus may be released before
// we see a retry from the destination
retryWaiting();
- } else if (drainEvent) {
- DPRINTF(Drain, "Bus done draining, processing drain event\n");
+ } else if (drainManager) {
+ DPRINTF(Drain, "Bus done draining, signaling drain manager\n");
//If we weren't able to drain before, do it now.
- drainEvent->process();
+ drainManager->signalDrainDone();
// Clear the drain event once we're done with it.
- drainEvent = NULL;
+ drainManager = NULL;
}
}
template <typename PortClass>
unsigned int
-BaseBus::Layer<PortClass>::drain(Event * de)
+BaseBus::Layer<PortClass>::drain(DrainManager *dm)
{
//We should check that we're not "doing" anything, and that noone is
//waiting. We might be idle but have someone waiting if the device we
//contacted for a retry didn't actually retry.
if (!retryList.empty() || state != IDLE) {
DPRINTF(Drain, "Bus not drained\n");
- drainEvent = de;
+ drainManager = dm;
return 1;
}
return 0;
* whereas a response layer holds master ports.
*/
template <typename PortClass>
- class Layer
+ class Layer : public Drainable
{
public:
*
* @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
/** the clock speed for the bus layer */
Tick clock;
- /** event for signalling when drained */
- Event * drainEvent;
+ /** manager to signal when drained */
+ DrainManager *drainManager;
/**
* An array of ports that retry should be called
BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID);
- virtual unsigned int drain(Event *de) = 0;
+ virtual unsigned int drain(DrainManager *dm) = 0;
};
blocked(0),
noTargetMSHR(NULL),
missCount(p->max_miss_count),
- drainEvent(NULL),
+ drainManager(NULL),
addrRanges(p->addr_ranges.begin(), p->addr_ranges.end()),
system(p->system)
{
}
unsigned int
-BaseCache::drain(Event *de)
+BaseCache::drain(DrainManager *dm)
{
- int count = memSidePort->drain(de) + cpuSidePort->drain(de);
+ int count = memSidePort->drain(dm) + cpuSidePort->drain(dm);
// Set status
if (count != 0) {
- drainEvent = de;
+ drainManager = dm;
- changeState(SimObject::Draining);
+ setDrainState(Drainable::Draining);
DPRINTF(Drain, "Cache not drained\n");
return count;
}
- changeState(SimObject::Drained);
+ setDrainState(Drainable::Drained);
return 0;
}
Counter missCount;
/** The drain event. */
- Event *drainEvent;
+ DrainManager *drainManager;
/**
* The address range to which the cache responds on the CPU side.
// interesting again.
}
- virtual unsigned int drain(Event *de);
+ virtual unsigned int drain(DrainManager *dm);
virtual bool inCache(Addr addr) = 0;
}
unsigned int
-CoherentBus::drain(Event *de)
+CoherentBus::drain(DrainManager *dm)
{
// sum up the individual layers
- return reqLayer.drain(de) + respLayer.drain(de) + snoopRespLayer.drain(de);
+ return reqLayer.drain(dm) + respLayer.drain(dm) + snoopRespLayer.drain(dm);
}
CoherentBus *
CoherentBus(const CoherentBusParams *p);
- unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *dm);
};
#endif //__MEM_COHERENT_BUS_HH__
}
unsigned int
-NoncoherentBus::drain(Event *de)
+NoncoherentBus::drain(DrainManager *dm)
{
// sum up the individual layers
- return reqLayer.drain(de) + respLayer.drain(de);
+ return reqLayer.drain(dm) + respLayer.drain(dm);
}
NoncoherentBus*
NoncoherentBus(const NoncoherentBusParams *p);
- unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *dm);
};
using namespace std;
PacketQueue::PacketQueue(EventManager& _em, const std::string& _label)
- : em(_em), sendEvent(this), drainEvent(NULL), label(_label),
+ : em(_em), sendEvent(this), drainManager(NULL), label(_label),
waitingOnRetry(false)
{
}
em.schedule(&sendEvent, std::max(nextReady, curTick() + 1));
} else {
// no more to send, so if we're draining, we may be done
- if (drainEvent && transmitList.empty() && !sendEvent.scheduled()) {
+ if (drainManager && transmitList.empty() && !sendEvent.scheduled()) {
DPRINTF(Drain, "PacketQueue done draining,"
"processing drain event\n");
- drainEvent->process();
- drainEvent = NULL;
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
}
}
}
unsigned int
-PacketQueue::drain(Event *de)
+PacketQueue::drain(DrainManager *dm)
{
if (transmitList.empty() && !sendEvent.scheduled())
return 0;
DPRINTF(Drain, "PacketQueue not drained\n");
- drainEvent = de;
+ drainManager = dm;
return 1;
}
#include "mem/port.hh"
#include "sim/eventq.hh"
+#include "sim/drain.hh"
/**
* A packet queue is a class that holds deferred packets and later
* sends them using the associated slave port or master port.
*/
-class PacketQueue
+class PacketQueue : public Drainable
{
private:
/** A deferred packet, buffered to transmit later. */
**/
EventWrapper<PacketQueue, &PacketQueue::processSendEvent> sendEvent;
- /** If we need to drain, keep the drain event around until we're done
+ /** If we need to drain, keep the drain manager around until we're done
* here.*/
- Event *drainEvent;
+ DrainManager *drainManager;
protected:
*/
void retry();
- /**
- * Hook for draining the packet queue.
- *
- * @param de An event which is used to signal back to the caller
- * @return A number indicating how many times process will be called
- */
- unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *dm);
};
class MasterPacketQueue : public PacketQueue
* functional request. */
bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); }
- /**
- * Hook for draining the queued port.
- *
- * @param de an event which is used to signal back to the caller
- * @returns a number indicating how many times process will be called
- */
- unsigned int drain(Event *de) { return queue.drain(de); }
+ unsigned int drain(DrainManager *dm) { return queue.drain(dm); }
};
class QueuedMasterPort : public MasterPort
* functional request. */
bool checkFunctional(PacketPtr pkt) { return queue.checkFunctional(pkt); }
- /**
- * Hook for draining the queued port.
- *
- * @param de an event which is used to signal back to the caller
- * @returns a number indicating how many times process will be called
- */
- unsigned int drain(Event *de) { return queue.drain(de); }
+ unsigned int drain(DrainManager *dm) { return queue.drain(dm); }
};
#endif // __MEM_QPORT_HH__
~MemoryControl();
- unsigned int drain(Event *de) = 0;
-
virtual void wakeup() = 0;
virtual void setConsumer(Consumer* consumer_ptr) = 0;
}
unsigned int
-RubyMemoryControl::drain(Event *de)
+RubyMemoryControl::drain(DrainManager *dm)
{
DPRINTF(RubyMemory, "MemoryController drain\n");
if(m_event.scheduled()) {
~RubyMemoryControl();
- unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *dm);
void wakeup();
m_mandatory_q_ptr(NULL),
pio_port(csprintf("%s-pio-port", name()), this),
m_usingRubyTester(p->using_ruby_tester), m_request_cnt(0),
- drainEvent(NULL), ruby_system(p->ruby_system), system(p->system),
+ drainManager(NULL), ruby_system(p->ruby_system), system(p->system),
waitingOnSequencer(false), access_phys_mem(p->access_phys_mem)
{
assert(m_version != -1);
RubyPort::testDrainComplete()
{
//If we weren't able to drain before, we might be able to now.
- if (drainEvent != NULL) {
+ if (drainManager != NULL) {
unsigned int drainCount = outstandingCount();
DPRINTF(Drain, "Drain count: %u\n", drainCount);
if (drainCount == 0) {
- DPRINTF(Drain, "RubyPort done draining, processing drain event\n");
- drainEvent->process();
- // Clear the drain event once we're done with it.
- drainEvent = NULL;
+ DPRINTF(Drain, "RubyPort done draining, signaling drain done\n");
+ drainManager->signalDrainDone();
+ // Clear the drain manager once we're done with it.
+ drainManager = NULL;
}
}
}
unsigned int
-RubyPort::getChildDrainCount(Event *de)
+RubyPort::getChildDrainCount(DrainManager *dm)
{
int count = 0;
if (pio_port.isConnected()) {
- count += pio_port.drain(de);
+ count += pio_port.drain(dm);
DPRINTF(Config, "count after pio check %d\n", count);
}
for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) {
- count += (*p)->drain(de);
+ count += (*p)->drain(dm);
DPRINTF(Config, "count after slave port check %d\n", count);
}
for (std::vector<PioPort*>::iterator p = master_ports.begin();
p != master_ports.end(); ++p) {
- count += (*p)->drain(de);
+ count += (*p)->drain(dm);
DPRINTF(Config, "count after master port check %d\n", count);
}
}
unsigned int
-RubyPort::drain(Event *de)
+RubyPort::drain(DrainManager *dm)
{
if (isDeadlockEventScheduled()) {
descheduleDeadlockEvent();
//
// If the RubyPort is not empty, then it needs to clear all outstanding
- // requests before it should call drainEvent->process()
+ // requests before it should call drainManager->signalDrainDone()
//
DPRINTF(Config, "outstanding count %d\n", outstandingCount());
bool need_drain = outstandingCount() > 0;
//
// Also, get the number of child ports that will also need to clear
- // their buffered requests before they call drainEvent->process()
+ // their buffered requests before they call drainManager->signalDrainDone()
//
- unsigned int child_drain_count = getChildDrainCount(de);
+ unsigned int child_drain_count = getChildDrainCount(dm);
// Set status
if (need_drain) {
- drainEvent = de;
+ drainManager = dm;
DPRINTF(Drain, "RubyPort not drained\n");
- changeState(SimObject::Draining);
+ setDrainState(Drainable::Draining);
return child_drain_count + 1;
}
- drainEvent = NULL;
- changeState(SimObject::Drained);
+ drainManager = NULL;
+ setDrainState(Drainable::Drained);
return child_drain_count;
}
//
void setController(AbstractController* _cntrl) { m_controller = _cntrl; }
int getId() { return m_version; }
- unsigned int drain(Event *de);
+ unsigned int drain(DrainManager *dm);
protected:
const std::string m_name;
}
}
- unsigned int getChildDrainCount(Event *de);
+ unsigned int getChildDrainCount(DrainManager *dm);
uint16_t m_port_id;
uint64_t m_request_cnt;
std::vector<M5Port*> slave_ports;
std::vector<PioPort*> master_ports;
- Event *drainEvent;
+ DrainManager *drainManager;
RubySystem* ruby_system;
System* system;
void
Sequencer::wakeup()
{
- assert(getState() != SimObject::Draining);
+ assert(getDrainState() != Drainable::Draining);
// Check for deadlock of any of the requests
Time current_time = g_system_ptr->getTime();
(m_writeRequestTable.size() + m_readRequestTable.size()));
// See if we should schedule a deadlock check
- if (!deadlockCheckEvent.scheduled() && getState() != SimObject::Draining) {
+ if (!deadlockCheckEvent.scheduled() &&
+ getDrainState() != Drainable::Draining) {
schedule(deadlockCheckEvent,
g_system_ptr->clockPeriod() * m_deadlock_threshold + curTick());
}
retryRdReq(false), retryWrReq(false),
rowHitFlag(false), stopReads(false),
writeEvent(this), respondEvent(this),
- refreshEvent(this), nextReqEvent(this), drainEvent(NULL),
+ refreshEvent(this), nextReqEvent(this), drainManager(NULL),
bytesPerCacheLine(0),
linesPerRowBuffer(p->lines_per_rowbuffer),
ranksPerChannel(p->ranks_per_channel),
// if there is nothing left in any queue, signal a drain
if (dramWriteQueue.empty() && dramReadQueue.empty() &&
- dramRespQueue.empty () && drainEvent) {
- drainEvent->process();
- drainEvent = NULL;
+ dramRespQueue.empty () && drainManager) {
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
// Once you're done emptying the write queue, check if there's
} else {
// if there is nothing left in any queue, signal a drain
if (dramWriteQueue.empty() && dramReadQueue.empty() &&
- drainEvent) {
- drainEvent->process();
- drainEvent = NULL;
+ drainManager) {
+ drainManager->signalDrainDone();
+ drainManager = NULL;
}
}
}
}
unsigned int
-SimpleDRAM::drain(Event *de)
+SimpleDRAM::drain(DrainManager *dm)
{
- unsigned int count = port.drain(de);
+ unsigned int count = port.drain(dm);
// if there is anything in any of our internal queues, keep track
// of that as well
if (!(dramWriteQueue.empty() && dramReadQueue.empty() &&
dramRespQueue.empty())) {
++count;
- drainEvent = de;
+ drainManager = dm;
}
if (count)
- changeState(Draining);
+ setDrainState(Drainable::Draining);
else
- changeState(Drained);
+ setDrainState(Drainable::Drained);
return count;
}
*/
std::list<DRAMPacket*> dramRespQueue;
- /** If we need to drain, keep the drain event around until we're done
+ /** If we need to drain, keep the drain manager around until we're done
* here.
*/
- Event *drainEvent;
+ DrainManager *drainManager;
/**
* Multi-dimensional vector of banks, first dimension is ranks,
SimpleDRAM(const SimpleDRAMParams* p);
- unsigned int drain(Event* de);
+ unsigned int drain(DrainManager* dm);
virtual BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID);
}
unsigned int
-SimpleMemory::drain(Event *de)
+SimpleMemory::drain(DrainManager *dm)
{
- int count = port.drain(de);
+ int count = port.drain(dm);
if (count)
- changeState(Draining);
+ setDrainState(Drainable::Draining);
else
- changeState(Drained);
+ setDrainState(Drainable::Drained);
return count;
}
SimpleMemory(const SimpleMemoryParams *p);
virtual ~SimpleMemory() { }
- unsigned int drain(Event* de);
+ unsigned int drain(DrainManager *dm);
virtual BaseSlavePort& getSlavePort(const std::string& if_name,
PortID idx = InvalidPortID);
SwigSource('m5.internal', 'swig/core.i')
SwigSource('m5.internal', 'swig/debug.i')
+SwigSource('m5.internal', 'swig/drain.i')
SwigSource('m5.internal', 'swig/event.i')
SwigSource('m5.internal', 'swig/pyobject.i')
SwigSource('m5.internal', 'swig/range.i')
'cxx_class' : str,
'cxx_type' : str,
'cxx_header' : str,
- 'type' : str }
+ 'type' : str,
+ 'cxx_bases' : list }
# Attributes that can be set any time
keywords = { 'check' : FunctionType }
value_dict[key] = val
if 'abstract' not in value_dict:
value_dict['abstract'] = False
+ if 'cxx_bases' not in value_dict:
+ value_dict['cxx_bases'] = []
cls_dict['_value_dict'] = value_dict
cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict)
if 'type' in value_dict:
code('%module(package="m5.internal") param_$cls')
code()
code('%{')
+ code('#include "sim/sim_object.hh"')
code('#include "params/$cls.hh"')
for param in params:
param.cxx_predecls(code)
code('%nodefault $classname;')
code('class $classname')
if cls._base:
- code(' : public ${{cls._base.cxx_class}}')
+ bases = [ cls._base.cxx_class ] + cls.cxx_bases
+ else:
+ bases = cls.cxx_bases
+ base_first = True
+ for base in bases:
+ if base_first:
+ code(' : public ${{base}}')
+ base_first = False
+ else:
+ code(' , public ${{base}}')
+
code('{')
code(' public:')
cls.export_methods(code)
abstract = True
cxx_header = "sim/sim_object.hh"
+ cxx_bases = [ "Drainable" ]
+
@classmethod
def export_method_swig_predecls(cls, code):
code('''
%include <std_string.i>
+
+%import "python/swig/drain.i"
''')
@classmethod
def export_methods(cls, code):
code('''
- enum State {
- Running,
- Draining,
- Drained
- };
-
void init();
void loadState(Checkpoint *cp);
void initState();
void regStats();
void resetStats();
void startup();
-
- unsigned int drain(Event *drain_event);
- void resume();
''')
# Initialize new instance. For objects with SimObject-valued
from event import *
from main import main
from simulate import *
+
import event
import stats
import trace
+from drain import DrainManager, Drainable
# be drained.
def drain(root):
all_drained = False
- drain_event = internal.event.createCountedDrain()
- unready_objs = sum(obj.drain(drain_event) for obj in root.descendants())
+ dm = internal.drain.createDrainManager()
+ unready_objs = sum(obj.drain(dm) for obj in root.descendants())
# If we've got some objects that can't drain immediately, then simulate
if unready_objs > 0:
- drain_event.setCount(unready_objs)
+ dm.setCount(unready_objs)
simulate()
else:
all_drained = True
- internal.event.cleanupCountedDrain(drain_event)
+ internal.drain.cleanupDrainManager(dm)
return all_drained
def resume(root):
- for obj in root.descendants(): obj.resume()
+ for obj in root.descendants(): obj.drainResume()
def checkpoint(dir):
root = objects.Root.getInstance()
--- /dev/null
+/*
+ * 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;
+}
+
+%}
// This must follow eventq.hh
%include "python/swig/pyevent.hh"
-struct CountedDrainEvent : public Event
-{
- void setCount(int _count);
-};
-
// minimal definition of SimExitEvent interface to wrap
class SimLoopExitEvent : public Event
{
// reference count must be decremented.
Py_DECREF(object);
}
-
-CountedDrainEvent *
-createCountedDrain()
-{
- return new CountedDrainEvent();
-}
-
-void
-cleanupCountedDrain(Event *counted_drain)
-{
- CountedDrainEvent *event =
- dynamic_cast<CountedDrainEvent *>(counted_drain);
- if (event == NULL) {
- fatal("Called cleanupCountedDrain() on an event that was not "
- "a CountedDrainEvent.");
- }
- assert(event->getCount() == 0);
- delete event;
-}
virtual void process();
};
-CountedDrainEvent *createCountedDrain();
-void cleanupCountedDrain(Event *counted_drain);
-
#endif // __PYTHON_SWIG_PYEVENT_HH__
Source('main.cc', main=True, skip_lib=True)
Source('root.cc')
Source('serialize.cc')
+Source('drain.cc')
Source('sim_events.cc')
Source('sim_object.cc')
Source('simulate.cc')
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
fromSimObject(objptr, sptr); \
} while (0)
-/*
+/**
* Basic support for object serialization.
+ *
+ * @note Many objects that support serialization need to be put in a
+ * consistent state when serialization takes place. We refer to the
+ * action of forcing an object into a consistent state as
+ * 'draining'. Objects that need draining inherit from Drainable. See
+ * Drainable for more information.
*/
class Serializable
{
mainEventQueue.schedule(event, when);
}
-CountedDrainEvent::CountedDrainEvent()
- : count(0)
-{ }
-
-void
-CountedDrainEvent::process()
-{
- if (--count == 0)
- exitSimLoop("Finished drain", 0);
-}
-
//
// constructor: automatically schedules at specified time
//
#endif
simObjectList.push_back(this);
- state = Running;
}
void
#endif
unsigned int
-SimObject::drain(Event *drain_event)
+SimObject::drain(DrainManager *drain_manager)
{
- state = Drained;
+ setDrainState(Drained);
return 0;
}
-void
-SimObject::resume()
-{
- state = Running;
-}
SimObject *
SimObject::find(const char *name)
#include "enums/MemoryMode.hh"
#include "params/SimObject.hh"
+#include "sim/drain.hh"
#include "sim/eventq.hh"
#include "sim/serialize.hh"
* </ul>
* <li>SimObject::resetStats()
* <li>SimObject::startup()
- * <li>SimObject::resume() if resuming from a checkpoint.
- * </ol>
- *
- * An object's internal state needs to be drained when creating a
- * checkpoint, switching between CPU models, or switching between
- * timing models. Once the internal state has been drained from
- * <i>all</i> objects in the system, the objects are serialized to
- * disc or the configuration change takes place. The process works as
- * follows (see simulate.py for details):
- *
- * <ol>
- * <li>An instance of a CountedDrainEvent is created to keep track of
- * how many objects need to be drained. The object maintains an
- * internal counter that is decreased every time its
- * CountedDrainEvent::process() method is called. When the counter
- * reaches zero, the simulation is stopped.
- *
- * <li>Call SimObject::drain() for every object in the
- * system. Draining has completed if all of them return
- * zero. Otherwise, the sum of the return values is loaded into
- * the counter of the CountedDrainEvent. A pointer of the drain
- * event is passed as an argument to the drain() method.
- *
- * <li>Continue simulation. When an object has finished draining its
- * internal state, it calls CountedDrainEvent::process() on the
- * CountedDrainEvent. When counter in the CountedDrainEvent reaches
- * zero, the simulation stops.
- *
- * <li>Check if any object still needs draining, if so repeat the
- * process above.
- *
- * <li>Serialize objects, switch CPU model, or change timing model.
- *
- * <li>Call SimObject::resume() and continue the simulation.
+ * <li>Drainable::drainResume() if resuming from a checkpoint.
* </ol>
*
* @note Whenever a method is called on all objects in the simulator's
* SimObject.py). This has the effect of calling the method on the
* parent node <i>before</i> its children.
*/
-class SimObject : public EventManager, public Serializable
+class SimObject : public EventManager, public Serializable, public Drainable
{
- public:
- /**
- * Object drain/handover states
- *
- * An object starts out in the Running state. When the simulator
- * prepares to take a snapshot or prepares a CPU for handover, it
- * calls the drain() method to transfer the object into the
- * Draining or Drained state. If any object enters the Draining
- * state (drain() returning >0), simulation continues until it all
- * objects have entered the Drained.
- *
- * The before resuming simulation, the simulator calls resume() to
- * transfer the object to the Running state.
- *
- * \note Even though the state of an object (visible to the rest
- * of the world through getState()) could be used to determine if
- * all objects have entered the Drained state, the protocol is
- * actually a bit more elaborate. See drain() for details.
- */
- enum State {
- Running, /** Running normally */
- Draining, /** Draining buffers pending serialization/handover */
- Drained /** Buffers drained, ready for serialization/handover */
- };
-
- private:
- State state;
-
- protected:
- void changeState(State new_state) { state = new_state; }
-
- public:
- State getState() { return state; }
-
private:
typedef std::vector<SimObject *> SimObjectList;
virtual void startup();
/**
- * Serialize all SimObjects in the system.
+ * Provide a default implementation of the drain interface that
+ * simply returns 0 (draining completed) and sets the drain state
+ * to Drained.
*/
- static void serializeAll(std::ostream &os);
+ unsigned int drain(DrainManager *drainManger);
/**
- * 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.
+ * Serialize all SimObjects in the system.
*/
- virtual void resume();
+ static void serializeAll(std::ostream &os);
#ifdef DEBUG
public:
void
System::setMemoryMode(Enums::MemoryMode mode)
{
- assert(getState() == Drained);
+ assert(getDrainState() == Drainable::Drained);
memoryMode = mode;
}
return physmem.isMemAddr(addr);
}
+unsigned int
+System::drain(DrainManager *dm)
+{
+ setDrainState(Drainable::Drained);
+ return 0;
+}
+
void
-System::resume()
+System::drainResume()
{
- SimObject::resume();
+ Drainable::drainResume();
totalNumInsts = 0;
}
void serialize(std::ostream &os);
void unserialize(Checkpoint *cp, const std::string §ion);
- virtual void resume();
+
+ unsigned int drain(DrainManager *dm);
+ void drainResume();
public:
Counter totalNumInsts;