changeset ab47fe7f03f0 in /z/repo/gem5
details: http://repo.gem5.org/gem5?cmd=changeset;node=ab47fe7f03f0
description:
cpu: Rewrite O3 draining to avoid stopping in microcode
Previously, the O3 CPU could stop in the middle of a microcode
sequence. This patch makes sure that the pipeline stops when it has
committed a normal instruction or exited from a microcode
sequence. Additionally, it makes sure that the pipeline has no
instructions in flight when it is drained, which should make draining
more robust.
Draining is controlled in the commit stage, which checks if the next
PC after a committed instruction is in microcode. If this isn't the
case, it requests a squash of all instructions after that the
instruction that just committed and immediately signals a drain stall
to the fetch stage. The CPU then continues to execute until the
pipeline and all associated buffers are empty.
diffstat:
src/cpu/o3/bpred_unit.hh | 6 +-
src/cpu/o3/bpred_unit_impl.hh | 12 +-
src/cpu/o3/commit.hh | 14 +-
src/cpu/o3/commit_impl.hh | 69 +++++++---
src/cpu/o3/cpu.cc | 263 ++++++++++++++++++++++++---------------
src/cpu/o3/cpu.hh | 45 +++++-
src/cpu/o3/decode.hh | 31 +++-
src/cpu/o3/decode_impl.hh | 61 ++++----
src/cpu/o3/dep_graph.hh | 28 ++++-
src/cpu/o3/fetch.hh | 37 +++--
src/cpu/o3/fetch_impl.hh | 178 ++++++++++++++++----------
src/cpu/o3/fu_pool.cc | 26 ++-
src/cpu/o3/fu_pool.hh | 18 ++-
src/cpu/o3/iew.hh | 19 +--
src/cpu/o3/iew_impl.hh | 54 +++----
src/cpu/o3/inst_queue.hh | 12 +-
src/cpu/o3/inst_queue_impl.hh | 33 +---
src/cpu/o3/lsq.hh | 18 ++-
src/cpu/o3/lsq_impl.hh | 73 ++++++++++-
src/cpu/o3/lsq_unit.hh | 34 +++-
src/cpu/o3/lsq_unit_impl.hh | 61 ++++-----
src/cpu/o3/mem_dep_unit.hh | 16 ++-
src/cpu/o3/mem_dep_unit_impl.hh | 25 ++-
src/cpu/o3/rename.hh | 26 ++-
src/cpu/o3/rename_impl.hh | 115 ++++++-----------
src/cpu/o3/rob.hh | 23 ++-
src/cpu/o3/rob_impl.hh | 52 ++++---
27 files changed, 816 insertions(+), 533 deletions(-)
diffs (truncated from 2445 to 300 lines):
diff -r 0cb3209bc5c7 -r ab47fe7f03f0 src/cpu/o3/bpred_unit.hh
--- a/src/cpu/o3/bpred_unit.hh Mon Jan 07 13:05:46 2013 -0500
+++ b/src/cpu/o3/bpred_unit.hh Mon Jan 07 13:05:46 2013 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -88,8 +88,10 @@
*/
void regStats();
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
+ /** Take over execution from another CPU's thread. */
void takeOverFrom();
/**
diff -r 0cb3209bc5c7 -r ab47fe7f03f0 src/cpu/o3/bpred_unit_impl.hh
--- a/src/cpu/o3/bpred_unit_impl.hh Mon Jan 07 13:05:46 2013 -0500
+++ b/src/cpu/o3/bpred_unit_impl.hh Mon Jan 07 13:05:46 2013 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 ARM Limited
+ * Copyright (c) 2011-2012 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -131,12 +131,12 @@
template <class Impl>
void
-BPredUnit<Impl>::switchOut()
+BPredUnit<Impl>::drainSanityCheck() const
{
- // Clear any state upon switch out.
- for (int i = 0; i < Impl::MaxThreads; ++i) {
- squash(0, i);
- }
+ // We shouldn't have any outstanding requests when we resume from
+ // a drained system.
+ for (int i = 0; i < Impl::MaxThreads; ++i)
+ assert(predHist[i].empty());
}
template <class Impl>
diff -r 0cb3209bc5c7 -r ab47fe7f03f0 src/cpu/o3/commit.hh
--- a/src/cpu/o3/commit.hh Mon Jan 07 13:05:46 2013 -0500
+++ b/src/cpu/o3/commit.hh Mon Jan 07 13:05:46 2013 -0500
@@ -199,13 +199,16 @@
void startupStage();
/** Initializes the draining of commit. */
- bool drain();
+ void drain();
/** Resumes execution after draining. */
- void resume();
+ void drainResume();
- /** Completes the switch out of commit. */
- void switchOut();
+ /** Perform sanity checks after a drain. */
+ void drainSanityCheck() const;
+
+ /** Has the stage drained? */
+ bool isDrained() const;
/** Takes over from another CPU's thread. */
void takeOverFrom();
@@ -438,9 +441,6 @@
/** Is a drain pending. */
bool drainPending;
- /** Is commit switched out. */
- bool switchedOut;
-
/** The latency to handle a trap. Used when scheduling trap
* squash event.
*/
diff -r 0cb3209bc5c7 -r ab47fe7f03f0 src/cpu/o3/commit_impl.hh
--- a/src/cpu/o3/commit_impl.hh Mon Jan 07 13:05:46 2013 -0500
+++ b/src/cpu/o3/commit_impl.hh Mon Jan 07 13:05:46 2013 -0500
@@ -57,6 +57,7 @@
#include "debug/Activity.hh"
#include "debug/Commit.hh"
#include "debug/CommitRate.hh"
+#include "debug/Drain.hh"
#include "debug/ExecFaulting.hh"
#include "params/DerivO3CPU.hh"
#include "sim/faults.hh"
@@ -99,7 +100,6 @@
commitWidth(params->commitWidth),
numThreads(params->numThreads),
drainPending(false),
- switchedOut(false),
trapLatency(params->trapLatency),
canHandleInterrupts(true)
{
@@ -369,35 +369,59 @@
}
template <class Impl>
-bool
+void
DefaultCommit<Impl>::drain()
{
drainPending = true;
-
- return false;
}
template <class Impl>
void
-DefaultCommit<Impl>::switchOut()
-{
- switchedOut = true;
- drainPending = false;
- rob->switchOut();
-}
-
-template <class Impl>
-void
-DefaultCommit<Impl>::resume()
+DefaultCommit<Impl>::drainResume()
{
drainPending = false;
}
template <class Impl>
void
+DefaultCommit<Impl>::drainSanityCheck() const
+{
+ assert(isDrained());
+ rob->drainSanityCheck();
+}
+
+template <class Impl>
+bool
+DefaultCommit<Impl>::isDrained() const
+{
+ /* Make sure no one is executing microcode. There are two reasons
+ * for this:
+ * - Hardware virtualized CPUs can't switch into the middle of a
+ * microcode sequence.
+ * - The current fetch implementation will most likely get very
+ * confused if it tries to start fetching an instruction that
+ * is executing in the middle of a ucode sequence that changes
+ * address mappings. This can happen on for example x86.
+ */
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ if (pc[tid].microPC() != 0)
+ return false;
+ }
+
+ /* Make sure that all instructions have finished committing before
+ * declaring the system as drained. We want the pipeline to be
+ * completely empty when we declare the CPU to be drained. This
+ * makes debugging easier since CPU handover and restoring from a
+ * checkpoint with a different CPU should have the same timing.
+ */
+ return rob->isEmpty() &&
+ interrupt == NoFault;
+}
+
+template <class Impl>
+void
DefaultCommit<Impl>::takeOverFrom()
{
- switchedOut = false;
_status = Active;
_nextStatus = Inactive;
for (ThreadID tid = 0; tid < numThreads; tid++) {
@@ -624,13 +648,6 @@
wroteToTimeBuffer = false;
_nextStatus = Inactive;
- if (drainPending && cpu->instList.empty() && !iewStage->hasStoresToWB() &&
- interrupt == NoFault) {
- cpu->signalDrained();
- drainPending = false;
- return;
- }
-
if (activeThreads->empty())
return;
@@ -1018,6 +1035,14 @@
if (head_inst->isSquashAfter())
squashAfter(tid, head_inst);
+ if (drainPending) {
+ DPRINTF(Drain, "Draining: %i:%s\n", tid, pc[tid]);
+ if (pc[tid].microPC() == 0 && interrupt == NoFault) {
+ squashAfter(tid, head_inst);
+ cpu->commitDrained(tid);
+ }
+ }
+
int count = 0;
Addr oldpc;
// Debug statement. Checks to make sure we're not
diff -r 0cb3209bc5c7 -r ab47fe7f03f0 src/cpu/o3/cpu.cc
--- a/src/cpu/o3/cpu.cc Mon Jan 07 13:05:46 2013 -0500
+++ b/src/cpu/o3/cpu.cc Mon Jan 07 13:05:46 2013 -0500
@@ -257,7 +257,7 @@
globalSeqNum(1),
system(params->system),
- drainCount(0),
+ drainManager(NULL),
lastRunningCycle(curCycle())
{
if (!params->switched_out) {
@@ -584,6 +584,8 @@
FullO3CPU<Impl>::tick()
{
DPRINTF(O3CPU, "\n\nFullO3CPU: Ticking main, FullO3CPU.\n");
+ assert(!switchedOut());
+ assert(getDrainState() != Drainable::Drained);
++numCycles;
@@ -618,8 +620,7 @@
}
if (!tickEvent.scheduled()) {
- if (_status == SwitchedOut ||
- getDrainState() == Drainable::Drained) {
+ if (_status == SwitchedOut) {
DPRINTF(O3CPU, "Switched out!\n");
// increment stat
lastRunningCycle = curCycle();
@@ -635,6 +636,8 @@
if (!FullSystem)
updateThreadPriority();
+
+ tryDrain();
}
template <class Impl>
@@ -657,13 +660,6 @@
thread[tid]->initMemProxies(thread[tid]->getTC());
}
- // this CPU could still be unconnected if we are restoring from a
- // checkpoint and this CPU is to be switched in, thus we can only
- // do this here if the instruction port is actually connected, if
- // not we have to do it as part of takeOverFrom
- if (icachePort.isConnected())
- fetch.setIcache();
-
if (FullSystem && !params()->switched_out) {
for (ThreadID tid = 0; tid < numThreads; tid++) {
ThreadContext *src_tc = threadContexts[tid];
@@ -683,6 +679,7 @@
FullO3CPU<Impl>::startup()
{
fetch.startupStage();
+ decode.startupStage();
iew.startupStage();
rename.startupStage();
commit.startupStage();
@@ -696,6 +693,7 @@
std::find(activeThreads.begin(), activeThreads.end(), tid);
DPRINTF(O3CPU, "[tid:%i]: Calling activate thread.\n", tid);
+ assert(!switchedOut());
if (isActive == activeThreads.end()) {
DPRINTF(O3CPU, "[tid:%i]: Adding to active threads list\n",
@@ -714,6 +712,7 @@
std::find(activeThreads.begin(), activeThreads.end(), tid);
DPRINTF(O3CPU, "[tid:%i]: Calling deactivate thread.\n", tid);
+ assert(!switchedOut());
if (thread_it != activeThreads.end()) {
DPRINTF(O3CPU,"[tid:%i]: Removing from active threads list\n",
@@ -752,6 +751,8 @@
void
FullO3CPU<Impl>::activateContext(ThreadID tid, Cycles delay)
{
+ assert(!switchedOut());
+
// Needs to set each stage to running as well.
if (delay){
DPRINTF(O3CPU, "[tid:%i]: Scheduling thread context to activate "
@@ -761,6 +762,12 @@
activateThread(tid);
}
+ // We don't want to wake the CPU if it is drained. In that case,
+ // we just want to flag the thread as active and schedule the tick
+ // event from drainResume() instead.
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev