Fix bugs due to interaction between SEV instructions and O3 pipeline
authorGeoffrey Blake <geoffrey.blake@arm.com>
Fri, 19 Aug 2011 20:08:07 +0000 (15:08 -0500)
committerGeoffrey Blake <geoffrey.blake@arm.com>
Fri, 19 Aug 2011 20:08:07 +0000 (15:08 -0500)
SEV instructions were originally implemented to cause asynchronous squashes
via the generateTCSquash() function in the O3 pipeline when updating the
SEV_MAILBOX miscReg. This caused race conditions between CPUs in an MP system
that would lead to a pipeline either going inactive indefinitely or not being
able to commit squashed instructions. Fixed SEV instructions to behave like
interrupts and cause synchronous sqaushes inside the pipeline, eliminating
the race conditions. Also fixed up the semantics of the WFE instruction to
behave as documented in the ARMv7 ISA description to not sleep if SEV_MAILBOX=1
or unmasked interrupts are pending.

src/arch/arm/faults.cc
src/arch/arm/faults.hh
src/arch/arm/interrupts.hh
src/arch/arm/isa/insts/misc.isa
src/arch/arm/isa/templates/pred.isa
src/arch/arm/isa_traits.hh
src/cpu/o3/commit_impl.hh
src/cpu/o3/cpu.hh
src/cpu/o3/thread_context_impl.hh

index 3bd9f070c3275d8401bd61e3442b3b7f594c404f..3c361404e72a6ec522e4f09099b90a665ae30fe9 100644 (file)
@@ -78,6 +78,8 @@ template<> ArmFault::FaultVals ArmFaultVals<FlushPipe>::vals =
 template<> ArmFault::FaultVals ArmFaultVals<ReExec>::vals =
     {"ReExec Flush", 0x00, MODE_SVC, 0, 0, true, true}; // some dummy values
 
+template<> ArmFault::FaultVals ArmFaultVals<ArmSev>::vals =
+    {"ArmSev Flush", 0x00, MODE_SVC, 0, 0, true, true}; // some dummy values
 Addr 
 ArmFault::getVector(ThreadContext *tc)
 {
@@ -127,6 +129,8 @@ ArmFault::invoke(ThreadContext *tc, StaticInstPtr inst)
     cpsr.i = 1;
     cpsr.e = sctlr.ee;
     tc->setMiscReg(MISCREG_CPSR, cpsr);
+    // Make sure mailbox sets to one always
+    tc->setMiscReg(MISCREG_SEV_MAILBOX, 1);
     tc->setIntReg(INTREG_LR, curPc +
             (saved_cpsr.t ? thumbPcOffset() : armPcOffset()));
 
@@ -252,6 +256,18 @@ template void AbortFault<PrefetchAbort>::invoke(ThreadContext *tc,
 template void AbortFault<DataAbort>::invoke(ThreadContext *tc,
                                             StaticInstPtr inst);
 
+void
+ArmSev::invoke(ThreadContext *tc, StaticInstPtr inst) {
+    DPRINTF(Faults, "Invoking ArmSev Fault\n");
+#if FULL_SYSTEM
+    // Set sev_mailbox to 1, clear the pending interrupt from remote
+    // SEV execution and let pipeline continue as pcState is still
+    // valid.
+    tc->setMiscReg(MISCREG_SEV_MAILBOX, 1);
+    tc->getCpuPtr()->clearInterrupt(INT_SEV, 0);
+#endif
+}
+
 // return via SUBS pc, lr, xxx; rfe, movs, ldm
 
 } // namespace ArmISA
index 234d8cfec61e6d151367535fc6b046c09df0ae22..54edb336b1011657c0ee85075c6c876d72fdf7fd 100644 (file)
@@ -257,6 +257,15 @@ static inline Fault genMachineCheckFault()
     return new Reset();
 }
 
+// A fault that flushes the pipe, excluding the faulting instructions
+class ArmSev : public ArmFaultVals<ArmSev>
+{
+  public:
+    ArmSev () {}
+    void invoke(ThreadContext *tc,
+            StaticInstPtr inst = StaticInst::nullStaticInstPtr);
+};
+
 } // namespace ArmISA
 
 #endif // __ARM_FAULTS_HH__
index 16a5a1f3d65605cb23d3c712178b3d84c6a62d8e..82c0bb713cdceb690d32d3921ed395582a0597d4 100644 (file)
@@ -134,7 +134,8 @@ class Interrupts : public SimObject
         return ((interrupts[INT_IRQ] && !cpsr.i) ||
                 (interrupts[INT_FIQ] && !cpsr.f) ||
                 (interrupts[INT_ABT] && !cpsr.a) ||
-                (interrupts[INT_RST]));
+                (interrupts[INT_RST]) ||
+                (interrupts[INT_SEV]));
     }
 
     /**
@@ -167,6 +168,8 @@ class Interrupts : public SimObject
                     ArmFault::AsynchronousExternalAbort);
         if (interrupts[INT_RST])
            return new Reset;
+        if (interrupts[INT_SEV])
+           return new ArmSev;
 
         panic("intStatus and interrupts not in sync\n");
     }
index 4a4f1e31447454344fe3b9dedb881577de590987..4d8ea66a201444699f22eaef7cb5b3686919691f 100644 (file)
@@ -502,20 +502,32 @@ let {{
 
     wfeCode = '''
 #if FULL_SYSTEM
+    // WFE Sleeps if SevMailbox==0 and no unmasked interrupts are pending
     if (SevMailbox == 1) {
         SevMailbox = 0;
         PseudoInst::quiesceSkip(xc->tcBase());
+    } else if (xc->tcBase()->getCpuPtr()->getInterruptController()->checkInterrupts(xc->tcBase())) {
+        PseudoInst::quiesceSkip(xc->tcBase());
     } else {
         PseudoInst::quiesce(xc->tcBase());
     }
+#endif
+    '''
+    wfePredFixUpCode = '''
+#if FULL_SYSTEM
+    // WFE is predicated false, reset SevMailbox to reduce spurious sleeps
+    // and SEV interrupts
+    SevMailbox = 1;
 #endif
     '''
     wfeIop = InstObjParams("wfe", "WfeInst", "PredOp", \
-            { "code" : wfeCode, "predicate_test" : predicateTest },
+            { "code" : wfeCode,
+              "pred_fixup" : wfePredFixUpCode,
+              "predicate_test" : predicateTest },
             ["IsNonSpeculative", "IsQuiesce", "IsSerializeAfter"])
     header_output += BasicDeclare.subst(wfeIop)
     decoder_output += BasicConstructor.subst(wfeIop)
-    exec_output += QuiescePredOpExecute.subst(wfeIop)
+    exec_output += QuiescePredOpExecuteWithFixup.subst(wfeIop)
 
     wfiCode = '''
 #if FULL_SYSTEM
@@ -535,19 +547,20 @@ let {{
     exec_output += QuiescePredOpExecute.subst(wfiIop)
 
     sevCode = '''
-    // Need a way for O3 to not scoreboard these accesses as pipe flushes.
+#if FULL_SYSTEM
     SevMailbox = 1;
     System *sys = xc->tcBase()->getSystemPtr();
     for (int x = 0; x < sys->numContexts(); x++) {
         ThreadContext *oc = sys->getThreadContext(x);
         if (oc == xc->tcBase())
             continue;
-        // Only wake if they were sleeping
+        // Wake CPU with interrupt if they were sleeping
         if (oc->readMiscReg(MISCREG_SEV_MAILBOX) == 0) {
-            oc->setMiscReg(MISCREG_SEV_MAILBOX, 1);
-            PseudoInst::wakeCPU(xc->tcBase(), x);
+            // Post Interrupt and wake cpu if needed
+            oc->getCpuPtr()->postInterrupt(INT_SEV, 0);
         }
     }
+#endif
     '''
     sevIop = InstObjParams("sev", "SevInst", "PredOp", \
             { "code" : sevCode, "predicate_test" : predicateTest },
index 04f253ca94bfc6e113d90d83470d7d975faee7e1..8b7ff69e735b836e731c6eca72c90db5cbcd78d0 100644 (file)
@@ -205,6 +205,34 @@ def template QuiescePredOpExecute {{
     }
 }};
 
+def template QuiescePredOpExecuteWithFixup {{
+    Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const
+    {
+        Fault fault = NoFault;
+        uint64_t resTemp = 0;
+        resTemp = resTemp;
+        %(op_decl)s;
+        %(op_rd)s;
+
+        if (%(predicate_test)s)
+        {
+            %(code)s;
+            if (fault == NoFault)
+            {
+                %(op_wb)s;
+            }
+        } else {
+            xc->setPredicate(false);
+            %(pred_fixup)s;
+#if FULL_SYSTEM
+            PseudoInst::quiesceSkip(xc->tcBase());
+#endif
+        }
+
+        return fault;
+    }
+}};
+
 def template DataDecode {{
     if (machInst.opcode4 == 0) {
         if (machInst.sField == 0)
index c89c9abce573c12bea7f623f953083e89ae46a41..40371e065a6223780a759a84a4ba4fd077fbbd8d 100644 (file)
@@ -121,6 +121,7 @@ namespace ArmISA
         INT_ABT,
         INT_IRQ,
         INT_FIQ,
+        INT_SEV, // Special interrupt for recieving SEV's
         NumInterruptTypes
     };
 } // namespace ArmISA
index 0d9952df49afc42fcd0c90ac08218595cd21cffe..f218cc76a65655f33397ba1ad3ad40d5e2fb67e8 100644 (file)
@@ -505,6 +505,7 @@ DefaultCommit<Impl>::generateTrapEvent(ThreadID tid)
 
     cpu->schedule(trap, curTick() + trapLatency);
     trapInFlight[tid] = true;
+    thread[tid]->trapPending = true;
 }
 
 template <class Impl>
index 7095e52eceb0c56ddb80254ff954f31a2b3609a7..dd9f5d40fe2cf962ed55e2e72c824199741c9175 100644 (file)
@@ -184,9 +184,19 @@ class FullO3CPU : public BaseO3CPU
         if (activateThreadEvent[tid].squashed())
             reschedule(activateThreadEvent[tid],
                 nextCycle(curTick() + ticks(delay)));
-        else if (!activateThreadEvent[tid].scheduled())
-            schedule(activateThreadEvent[tid],
-                nextCycle(curTick() + ticks(delay)));
+        else if (!activateThreadEvent[tid].scheduled()) {
+            Tick when = nextCycle(curTick() + ticks(delay));
+
+            // Check if the deallocateEvent is also scheduled, and make
+            // sure they do not happen at same time causing a sleep that
+            // is never woken from.
+            if (deallocateContextEvent[tid].scheduled() &&
+                deallocateContextEvent[tid].when() == when) {
+                when++;
+            }
+
+            schedule(activateThreadEvent[tid], when);
+        }
     }
 
     /** Unschedule actiavte thread event, regardless of its current state. */
index c3b7d2248a980c6214adc4e19eb144dbd88ea7c3..4888cf92e4d94a224849bb3c2a8a6be49089c955 100755 (executable)
@@ -351,8 +351,7 @@ O3ThreadContext<Impl>::setMiscRegNoEffect(int misc_reg, const MiscReg &val)
 
 template <class Impl>
 void
-O3ThreadContext<Impl>::setMiscReg(int misc_reg,
-                                                const MiscReg &val)
+O3ThreadContext<Impl>::setMiscReg(int misc_reg, const MiscReg &val)
 {
     cpu->setMiscReg(misc_reg, val, thread->threadId());