O3: Fixes fetch deadlock when the interrupt clears before CPU handles it.
authorMin Kyu Jeong <minkyu.jeong@arm.com>
Tue, 18 Jan 2011 22:30:01 +0000 (16:30 -0600)
committerMin Kyu Jeong <minkyu.jeong@arm.com>
Tue, 18 Jan 2011 22:30:01 +0000 (16:30 -0600)
When this condition occurs the cpu should restart the fetch stage to fetch from
the original execution path. Fault handling in the commit stage is cleaned up a
little bit so the control flow is simplier. Finally, if an instruction is being
used to carry a fault it isn't executed, so the fault propagates appropriately.

src/arch/arm/interrupts.hh
src/cpu/o3/commit.hh
src/cpu/o3/commit_impl.hh
src/cpu/o3/fetch.hh
src/cpu/o3/fetch_impl.hh
src/cpu/o3/iew_impl.hh

index 5870034c3689499186ed3637744fcbedc150cb0c..a94408a4f0791d84761623f3c8b50ad0fb59e798 100644 (file)
@@ -102,7 +102,7 @@ class Interrupts : public SimObject
     void
     clear(int int_num, int index)
     {
-        DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index);
+        DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index);
 
         if (int_num < 0 || int_num >= NumInterruptTypes)
             panic("int_num out of bounds\n");
@@ -112,7 +112,6 @@ class Interrupts : public SimObject
 
         interrupts[int_num] = false;
         intStatus &= ~(ULL(1) << int_num);
-
     }
 
     void
index a38d6a96f2981099f9a7620336dbb4924bb2bb40..7183889c6e52b6fbb3a292f0ca8fb0e7d241a741 100644 (file)
@@ -255,6 +255,9 @@ class DefaultCommit
 #if FULL_SYSTEM
     /** Handles processing an interrupt. */
     void handleInterrupt();
+
+    /** Get fetch redirecting so we can handle an interrupt */
+    void propagateInterrupt();
 #endif // FULL_SYSTEM
 
     /** Commits as many instructions as possible. */
index 2912cbb037ca54a533cabfa5337c1fdbb1a761f0..7f37b5f09e9928cfdabcbdc43b598d1a6a1d18f8 100644 (file)
@@ -674,54 +674,67 @@ template <class Impl>
 void
 DefaultCommit<Impl>::handleInterrupt()
 {
-    if (interrupt != NoFault) {
-        // Wait until the ROB is empty and all stores have drained in
-        // order to enter the interrupt.
-        if (rob->isEmpty() && !iewStage->hasStoresToWB()) {
-            // Squash or record that I need to squash this cycle if
-            // an interrupt needed to be handled.
-            DPRINTF(Commit, "Interrupt detected.\n");
+    // Verify that we still have an interrupt to handle
+    if (!cpu->checkInterrupts(cpu->tcBase(0))) {
+        DPRINTF(Commit, "Pending interrupt is cleared by master before "
+                "it got handled. Restart fetching from the orig path.\n");
+        toIEW->commitInfo[0].clearInterrupt = true;
+        interrupt = NoFault;
+        return;
+    }
 
-            // Clear the interrupt now that it's going to be handled
-            toIEW->commitInfo[0].clearInterrupt = true;
+    // Wait until the ROB is empty and all stores have drained in
+    // order to enter the interrupt.
+    if (rob->isEmpty() && !iewStage->hasStoresToWB()) {
+        // Squash or record that I need to squash this cycle if
+        // an interrupt needed to be handled.
+        DPRINTF(Commit, "Interrupt detected.\n");
 
-            assert(!thread[0]->inSyscall);
-            thread[0]->inSyscall = true;
+        // Clear the interrupt now that it's going to be handled
+        toIEW->commitInfo[0].clearInterrupt = true;
 
-            // CPU will handle interrupt.
-            cpu->processInterrupts(interrupt);
+        assert(!thread[0]->inSyscall);
+        thread[0]->inSyscall = true;
 
-            thread[0]->inSyscall = false;
+        // CPU will handle interrupt.
+        cpu->processInterrupts(interrupt);
 
-            commitStatus[0] = TrapPending;
+        thread[0]->inSyscall = false;
 
-            // Generate trap squash event.
-            generateTrapEvent(0);
+        commitStatus[0] = TrapPending;
 
-            interrupt = NoFault;
-        } else {
-            DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n");
-        }
-    } else if (commitStatus[0] != TrapPending &&
-               cpu->checkInterrupts(cpu->tcBase(0)) &&
-               !trapSquash[0] &&
-               !tcSquash[0]) {
-        // Process interrupts if interrupts are enabled, not in PAL
-        // mode, and no other traps or external squashes are currently
-        // pending.
-        // @todo: Allow other threads to handle interrupts.
-
-        // Get any interrupt that happened
-        interrupt = cpu->getInterrupts();
-
-        if (interrupt != NoFault) {
-            // Tell fetch that there is an interrupt pending.  This
-            // will make fetch wait until it sees a non PAL-mode PC,
-            // at which point it stops fetching instructions.
-            toIEW->commitInfo[0].interruptPending = true;
-        }
+        // Generate trap squash event.
+        generateTrapEvent(0);
+
+        interrupt = NoFault;
+    } else {
+        DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n");
     }
 }
+
+template <class Impl>
+void
+DefaultCommit<Impl>::propagateInterrupt()
+{
+    if (commitStatus[0] == TrapPending || interrupt || trapSquash[0] ||
+            tcSquash[0])
+        return;
+
+    // Process interrupts if interrupts are enabled, not in PAL
+    // mode, and no other traps or external squashes are currently
+    // pending.
+    // @todo: Allow other threads to handle interrupts.
+
+    // Get any interrupt that happened
+    interrupt = cpu->getInterrupts();
+
+    // Tell fetch that there is an interrupt pending.  This
+    // will make fetch wait until it sees a non PAL-mode PC,
+    // at which point it stops fetching instructions.
+    if (interrupt != NoFault)
+        toIEW->commitInfo[0].interruptPending = true;
+}
+
 #endif // FULL_SYSTEM
 
 template <class Impl>
@@ -730,12 +743,13 @@ DefaultCommit<Impl>::commit()
 {
 
 #if FULL_SYSTEM
-    // Check for any interrupt, and start processing it.  Or if we
-    // have an outstanding interrupt and are at a point when it is
-    // valid to take an interrupt, process it.
-    if (cpu->checkInterrupts(cpu->tcBase(0))) {
+    // Check for any interrupt that we've already squashed for and start processing it.
+    if (interrupt != NoFault)
         handleInterrupt();
-    }
+
+    // Check if we have a interrupt and get read to handle it
+    if (cpu->checkInterrupts(cpu->tcBase(0)))
+        propagateInterrupt();
 #endif // FULL_SYSTEM
 
     ////////////////////////////////////
index 87dde1da85571a6f19c75ae6a54844ad2f84712b..b86ccf81ee4f5dd15856c4880d5318d2dcc8e011 100644 (file)
@@ -244,6 +244,15 @@ class DefaultFetch
      */
     bool fetchCacheLine(Addr vaddr, Fault &ret_fault, ThreadID tid, Addr pc);
 
+
+    /** Check if an interrupt is pending and that we need to handle
+     */
+    bool
+    checkInterrupt(Addr pc)
+    {
+        return (interruptPending && (THE_ISA != ALPHA_ISA || !(pc & 0x3)));
+    }
+
     /** Squashes a specific thread and resets the PC. */
     inline void doSquash(const TheISA::PCState &newPC, ThreadID tid);
 
index 28ef423c444ee3dea59510054063837583d11a7b..1875d9c50e5dbeeac5a625bfc0d34fa88a76cfe2 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2010 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.
+ *
  * Copyright (c) 2004-2006 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -550,7 +562,7 @@ DefaultFetch<Impl>::fetchCacheLine(Addr vaddr, Fault &ret_fault, ThreadID tid,
         DPRINTF(Fetch, "[tid:%i] Can't fetch cache line, switched out\n",
                 tid);
         return false;
-    } else if (interruptPending && !(pc & 0x3)) {
+    } else if (checkInterrupt(pc)) {
         // Hold off fetch from getting new instructions when:
         // Cache is blocked, or
         // while an interrupt is pending and we're not in PAL mode, or
@@ -1250,8 +1262,8 @@ DefaultFetch<Impl>::fetch(bool &status_change)
         fetchStatus[tid] = TrapPending;
         status_change = true;
 
-        DPRINTF(Fetch, "[tid:%i]: fault (%s) detected @ PC %s",
-                tid, fault->name(), thisPC);
+        DPRINTF(Fetch, "[tid:%i]: fault (%s) detected @ PC %s, sending nop "
+                       "[sn:%lli]\n", tid, fault->name(), thisPC, inst_seq);
     }
 }
 
index c809b93abf3d395ce39729284153afccfebb872b..2acb941e0a37c733cd1d08ee2bc3f9bd2d8daaa8 100644 (file)
@@ -1255,7 +1255,13 @@ DefaultIEW<Impl>::executeInsts()
             }
 
         } else {
-            inst->execute();
+            // If the instruction has already faulted, then skip executing it.
+            // Such case can happen when it faulted during ITLB translation.
+            // If we execute the instruction (even if it's a nop) the fault
+            // will be replaced and we will lose it.
+            if (inst->getFault() == NoFault) {
+                inst->execute();
+            }
 
             inst->setExecuted();