blob: 6f0769f34387a57d86f078315b4c5de710b93df9 [file] [log] [blame]
/*
* 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();
/**
* Write back dirty buffers to memory using functional writes.
*
* After returning, an object implementing this method should have
* written all its dirty data back to memory. This method is
* typically used to prepare a system with caches for
* checkpointing.
*/
virtual void memWriteback() {};
/**
* Invalidate the contents of memory buffers.
*
* When the switching to hardware virtualized CPU models, we need
* to make sure that we don't have any cached state in the system
* that might become stale when we return. This method is used to
* flush all such state back to main memory.
*
* @warn This does <i>not</i> cause any dirty state to be written
* back to memory.
*/
virtual void memInvalidate() {};
State getDrainState() const { return _drainState; }
protected:
void setDrainState(State new_state) { _drainState = new_state; }
private:
State _drainState;
};
DrainManager *createDrainManager();
void cleanupDrainManager(DrainManager *drain_manager);
#endif