summaryrefslogtreecommitdiff
path: root/src/cpu/simple/timing.cc
diff options
context:
space:
mode:
authorAndreas Sandberg <Andreas.Sandberg@ARM.com>2013-01-07 13:05:46 -0500
committerAndreas Sandberg <Andreas.Sandberg@ARM.com>2013-01-07 13:05:46 -0500
commitf9bcf46371f27de8d22a1ecde4800b10eb5ef797 (patch)
treec9269b17b4ba5ac8d0a9ab3fa9931becd0a1ef44 /src/cpu/simple/timing.cc
parent52ff37caa3dc434baa0468f13ac609430f078982 (diff)
downloadgem5-f9bcf46371f27de8d22a1ecde4800b10eb5ef797.tar.xz
cpu: Make sure that a drained timing CPU isn't executing ucode
Currently, the timing CPU can be in the middle of a microcode sequence or multicycle (stayAtPC is true) instruction when it is drained. This leads to two problems: * When switching to a hardware virtualized CPU, we obviously can't execute gem5 microcode. * If stayAtPC is true we might execute half of an instruction twice when restoring a checkpoint or switching CPUs, which leads to an incorrect execution. After applying this patch, the CPU will be on a proper instruction boundary, which means that it is safe to switch to any CPU model (including hardware virtualized ones). This changeset also fixes a bug where the timing CPU sometimes switches out with while stayAtPC is true, which corrupts the target state after a CPU switch or checkpoint. Note: This changeset removes the so_state variable from checkpoints since the drain state isn't used anymore.
Diffstat (limited to 'src/cpu/simple/timing.cc')
-rw-r--r--src/cpu/simple/timing.cc106
1 files changed, 40 insertions, 66 deletions
diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc
index 621f99f29..78603be4f 100644
--- a/src/cpu/simple/timing.cc
+++ b/src/cpu/simple/timing.cc
@@ -94,11 +94,10 @@ TimingSimpleCPU::TimingCPUPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
TimingSimpleCPU::TimingSimpleCPU(TimingSimpleCPUParams *p)
: BaseSimpleCPU(p), fetchTranslation(this), icachePort(this),
dcachePort(this), ifetch_pkt(NULL), dcache_pkt(NULL), previousCycle(0),
- fetchEvent(this)
+ fetchEvent(this), drainManager(NULL)
{
_status = Idle;
- setDrainState(Drainable::Running);
system->totalNumInsts = 0;
}
@@ -107,36 +106,27 @@ TimingSimpleCPU::~TimingSimpleCPU()
{
}
-void
-TimingSimpleCPU::serialize(ostream &os)
-{
- Drainable::State so_state(getDrainState());
- SERIALIZE_ENUM(so_state);
- BaseSimpleCPU::serialize(os);
-}
-
-void
-TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
-{
- Drainable::State so_state;
- UNSERIALIZE_ENUM(so_state);
- BaseSimpleCPU::unserialize(cp, section);
-}
-
unsigned int
TimingSimpleCPU::drain(DrainManager *drain_manager)
{
- // TimingSimpleCPU is ready to drain if it's not waiting for
- // an access to complete.
if (_status == Idle ||
- _status == BaseSimpleCPU::Running ||
+ (_status == BaseSimpleCPU::Running && isDrained()) ||
_status == SwitchedOut) {
- setDrainState(Drainable::Drained);
+ assert(!fetchEvent.scheduled());
+ DPRINTF(Drain, "No need to drain.\n");
return 0;
} else {
- setDrainState(Drainable::Draining);
drainManager = drain_manager;
- DPRINTF(Drain, "CPU not drained\n");
+ DPRINTF(Drain, "Requesting drain: %s\n", pcState());
+
+ // The fetch event can become descheduled if a drain didn't
+ // succeed on the first attempt. We need to reschedule it if
+ // the CPU is waiting for a microcode routine to complete.
+ if (_status == BaseSimpleCPU::Running && !isDrained() &&
+ !fetchEvent.scheduled()) {
+ schedule(fetchEvent, nextCycle());
+ }
+
return 1;
}
}
@@ -144,6 +134,8 @@ TimingSimpleCPU::drain(DrainManager *drain_manager)
void
TimingSimpleCPU::drainResume()
{
+ assert(!fetchEvent.scheduled());
+
DPRINTF(SimpleCPU, "Resume\n");
if (_status != SwitchedOut && _status != Idle) {
if (system->getMemoryMode() != Enums::timing) {
@@ -151,13 +143,25 @@ TimingSimpleCPU::drainResume()
"'timing' mode.\n");
}
- if (fetchEvent.scheduled())
- deschedule(fetchEvent);
-
schedule(fetchEvent, nextCycle());
}
+}
+
+bool
+TimingSimpleCPU::tryCompleteDrain()
+{
+ if (!drainManager)
+ return false;
+
+ DPRINTF(Drain, "tryCompleteDrain: %s\n", pcState());
+ if (!isDrained())
+ return false;
+
+ DPRINTF(Drain, "CPU done draining, processing drain event\n");
+ drainManager->signalDrainDone();
+ drainManager = NULL;
- setDrainState(Drainable::Running);
+ return true;
}
void
@@ -165,14 +169,13 @@ TimingSimpleCPU::switchOut()
{
BaseSimpleCPU::switchOut();
+ assert(!fetchEvent.scheduled());
assert(_status == BaseSimpleCPU::Running || _status == Idle);
+ assert(!stayAtPC);
+ assert(microPC() == 0);
+
_status = SwitchedOut;
numCycles += curCycle() - previousCycle;
-
- // If we've been scheduled to resume but are then told to switch out,
- // we'll need to cancel it.
- if (fetchEvent.scheduled())
- deschedule(fetchEvent);
}
@@ -344,12 +347,7 @@ TimingSimpleCPU::translationFault(Fault fault)
postExecute();
- if (getDrainState() == Drainable::Draining) {
- advancePC(fault);
- completeDrain();
- } else {
- advanceInst(fault);
- }
+ advanceInst(fault);
}
void
@@ -618,7 +616,6 @@ TimingSimpleCPU::sendFetch(Fault fault, RequestPtr req, ThreadContext *tc)
void
TimingSimpleCPU::advanceInst(Fault fault)
{
-
if (_status == Faulting)
return;
@@ -634,6 +631,9 @@ TimingSimpleCPU::advanceInst(Fault fault)
if (!stayAtPC)
advancePC(fault);
+ if (tryCompleteDrain())
+ return;
+
if (_status == BaseSimpleCPU::Running) {
// kick off fetch of next instruction... callback from icache
// response will cause that instruction to be executed,
@@ -660,16 +660,6 @@ TimingSimpleCPU::completeIfetch(PacketPtr pkt)
numCycles += curCycle() - previousCycle;
previousCycle = curCycle();
- if (getDrainState() == Drainable::Draining) {
- if (pkt) {
- delete pkt->req;
- delete pkt;
- }
-
- completeDrain();
- return;
- }
-
preExecute();
if (curStaticInst && curStaticInst->isMemRef()) {
// load or store: just send to dcache
@@ -816,25 +806,9 @@ TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
postExecute();
- if (getDrainState() == Drainable::Draining) {
- advancePC(fault);
- completeDrain();
-
- return;
- }
-
advanceInst(fault);
}
-
-void
-TimingSimpleCPU::completeDrain()
-{
- DPRINTF(Drain, "CPU done draining, processing drain event\n");
- setDrainState(Drainable::Drained);
- drainManager->signalDrainDone();
-}
-
bool
TimingSimpleCPU::DcachePort::recvTimingResp(PacketPtr pkt)
{