cpu: Fix o3 drain bug
authorMitch Hayenga <mitch.hayenga@arm.com>
Wed, 3 Sep 2014 11:42:45 +0000 (07:42 -0400)
committerMitch Hayenga <mitch.hayenga@arm.com>
Wed, 3 Sep 2014 11:42:45 +0000 (07:42 -0400)
For X86, the o3 CPU would get stuck with the commit stage not being
drained if an interrupt arrived while drain was pending. isDrained()
makes sure that pcState.microPC() == 0, thus ensuring that we are at
an instruction boundary. However, when we take an interrupt we
execute:

    pcState.upc(romMicroPC(entry));
    pcState.nupc(romMicroPC(entry) + 1);
    tc->pcState(pcState);

As a result, the MicroPC is no longer zero. This patch ensures the drain is
delayed until no interrupts are present.  Once draining, non-synchronous
interrupts are deffered until after the switch.

src/cpu/o3/commit.hh
src/cpu/o3/commit_impl.hh

index 4c9ccf1eba8d5f4eb0ebefcff886015aef9b6b31..93c145d5b8b4f090c5e9d53b2fd54ea4d368fb66 100644 (file)
@@ -438,9 +438,18 @@ class DefaultCommit
     /** Number of Active Threads */
     ThreadID numThreads;
 
-    /** Is a drain pending. */
+    /** Is a drain pending? Commit is looking for an instruction boundary while
+     * there are no pending interrupts
+     */
     bool drainPending;
 
+    /** Is a drain imminent? Commit has found an instruction boundary while no
+     * interrupts were present or in flight.  This was the last architecturally
+     * committed instruction.  Interrupts disabled and pipeline flushed.
+     * Waiting for structures to finish draining.
+     */
+    bool drainImminent;
+
     /** The latency to handle a trap.  Used when scheduling trap
      * squash event.
      */
index 347b23359f519081fe925e8e4094c4c369cf6c7b..f0566233c856e03285a3dd2684244b2de472ab01 100644 (file)
@@ -104,6 +104,7 @@ DefaultCommit<Impl>::DefaultCommit(O3CPU *_cpu, DerivO3CPUParams *params)
       commitWidth(params->commitWidth),
       numThreads(params->numThreads),
       drainPending(false),
+      drainImminent(false),
       trapLatency(params->trapLatency),
       canHandleInterrupts(true),
       avoidQuiesceLiveLock(false)
@@ -406,6 +407,7 @@ void
 DefaultCommit<Impl>::drainResume()
 {
     drainPending = false;
+    drainImminent = false;
 }
 
 template <class Impl>
@@ -816,8 +818,10 @@ template <class Impl>
 void
 DefaultCommit<Impl>::propagateInterrupt()
 {
+    // Don't propagate intterupts if we are currently handling a trap or
+    // in draining and the last observable instruction has been committed.
     if (commitStatus[0] == TrapPending || interrupt || trapSquash[0] ||
-            tcSquash[0])
+            tcSquash[0] || drainImminent)
         return;
 
     // Process interrupts if interrupts are enabled, not in PAL
@@ -1089,10 +1093,15 @@ DefaultCommit<Impl>::commitInsts()
                     squashAfter(tid, head_inst);
 
                 if (drainPending) {
-                    DPRINTF(Drain, "Draining: %i:%s\n", tid, pc[tid]);
-                    if (pc[tid].microPC() == 0 && interrupt == NoFault) {
+                    if (pc[tid].microPC() == 0 && interrupt == NoFault &&
+                        !thread[tid]->trapPending) {
+                        // Last architectually committed instruction.
+                        // Squash the pipeline, stall fetch, and use
+                        // drainImminent to disable interrupts
+                        DPRINTF(Drain, "Draining: %i:%s\n", tid, pc[tid]);
                         squashAfter(tid, head_inst);
                         cpu->commitDrained(tid);
+                        drainImminent = true;
                     }
                 }