CheckerCPU: Re-factor CheckerCPU to be compatible with current gem5
[gem5.git] / src / cpu / o3 / lsq_unit_impl.hh
index 1a4e686a393c6d3f4ebe509414873f9dd91cd776..facd88597af3170954c6f569f7a76727ed18f341 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 ARM Limited
+ * Copyright (c) 2010-2011 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
  *          Korey Sewell
  */
 
+#include "arch/generic/debugfaults.hh"
 #include "arch/locked_mem.hh"
+#include "base/str.hh"
 #include "config/the_isa.hh"
-#include "config/use_checker.hh"
 #include "cpu/o3/lsq.hh"
 #include "cpu/o3/lsq_unit.hh"
-#include "base/str.hh"
+#include "debug/Activity.hh"
+#include "debug/IEW.hh"
+#include "debug/LSQUnit.hh"
 #include "mem/packet.hh"
 #include "mem/request.hh"
 
@@ -57,9 +60,9 @@
 template<class Impl>
 LSQUnit<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt,
                                               LSQUnit *lsq_ptr)
-    : inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr)
+    : Event(Default_Pri, AutoDelete),
+      inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr)
 {
-    this->setFlags(Event::AutoDelete);
 }
 
 template<class Impl>
@@ -132,9 +135,9 @@ LSQUnit<Impl>::completeDataAccess(PacketPtr pkt)
 
 template <class Impl>
 LSQUnit<Impl>::LSQUnit()
-    : loads(0), stores(0), storesToWB(0), stalled(false),
+    : loads(0), stores(0), storesToWB(0), cacheBlockMask(0), stalled(false),
       isStoreBlocked(false), isLoadBlocked(false),
-      loadBlockedHandled(false), hasPendingPkt(false)
+      loadBlockedHandled(false), storeInFlight(false), hasPendingPkt(false)
 {
 }
 
@@ -151,6 +154,8 @@ LSQUnit<Impl>::init(O3CPU *cpu_ptr, IEW *iew_ptr, DerivO3CPUParams *params,
 
     switchedOut = false;
 
+    cacheBlockMask = 0;
+
     lsq = lsq_ptr;
 
     lsqID = id;
@@ -162,6 +167,9 @@ LSQUnit<Impl>::init(O3CPU *cpu_ptr, IEW *iew_ptr, DerivO3CPUParams *params,
     loadQueue.resize(LQEntries);
     storeQueue.resize(SQEntries);
 
+    depCheckShift = params->LSQDepCheckShift;
+    checkLoads = params->LSQCheckLoads;
+
     loadHead = loadTail = 0;
 
     storeHead = storeWBIdx = storeTail = 0;
@@ -173,6 +181,7 @@ LSQUnit<Impl>::init(O3CPU *cpu_ptr, IEW *iew_ptr, DerivO3CPUParams *params,
     memDepViolator = NULL;
 
     blockedLoadSeqNum = 0;
+    needsTSO = params->needsTSO;
 }
 
 template<class Impl>
@@ -182,7 +191,7 @@ LSQUnit<Impl>::name() const
     if (Impl::MaxThreads == 1) {
         return iewStage->name() + ".lsq";
     } else {
-        return iewStage->name() + ".lsq.thread." + to_string(lsqID);
+        return iewStage->name() + ".lsq.thread" + to_string(lsqID);
     }
 }
 
@@ -236,12 +245,6 @@ void
 LSQUnit<Impl>::setDcachePort(Port *dcache_port)
 {
     dcachePort = dcache_port;
-
-#if USE_CHECKER
-    if (cpu->checker) {
-        cpu->checker->setDcachePort(dcachePort);
-    }
-#endif
 }
 
 template<class Impl>
@@ -291,6 +294,9 @@ LSQUnit<Impl>::takeOverFrom()
     stalled = false;
     isLoadBlocked = false;
     loadBlockedHandled = false;
+
+    // Just incase the memory system changed out from under us
+    cacheBlockMask = 0;
 }
 
 template<class Impl>
@@ -436,6 +442,139 @@ LSQUnit<Impl>::numLoadsReady()
     return retval;
 }
 
+template <class Impl>
+void
+LSQUnit<Impl>::checkSnoop(PacketPtr pkt)
+{
+    int load_idx = loadHead;
+
+    if (!cacheBlockMask) {
+        assert(dcachePort);
+        Addr bs = dcachePort->peerBlockSize();
+
+        // Make sure we actually got a size
+        assert(bs != 0);
+
+        cacheBlockMask = ~(bs - 1);
+    }
+
+    // If this is the only load in the LSQ we don't care
+    if (load_idx == loadTail)
+        return;
+    incrLdIdx(load_idx);
+
+    DPRINTF(LSQUnit, "Got snoop for address %#x\n", pkt->getAddr());
+    Addr invalidate_addr = pkt->getAddr() & cacheBlockMask;
+    while (load_idx != loadTail) {
+        DynInstPtr ld_inst = loadQueue[load_idx];
+
+        if (!ld_inst->effAddrValid || ld_inst->uncacheable()) {
+            incrLdIdx(load_idx);
+            continue;
+        }
+
+        Addr load_addr = ld_inst->physEffAddr & cacheBlockMask;
+        DPRINTF(LSQUnit, "-- inst [sn:%lli] load_addr: %#x to pktAddr:%#x\n",
+                    ld_inst->seqNum, load_addr, invalidate_addr);
+
+        if (load_addr == invalidate_addr) {
+            if (ld_inst->possibleLoadViolation) {
+                DPRINTF(LSQUnit, "Conflicting load at addr %#x [sn:%lli]\n",
+                        ld_inst->physEffAddr, pkt->getAddr(), ld_inst->seqNum);
+
+                // Mark the load for re-execution
+                ld_inst->fault = new ReExec;
+            } else {
+                // If a older load checks this and it's true
+                // then we might have missed the snoop
+                // in which case we need to invalidate to be sure
+                ld_inst->hitExternalSnoop = true;
+            }
+        }
+        incrLdIdx(load_idx);
+    }
+    return;
+}
+
+template <class Impl>
+Fault
+LSQUnit<Impl>::checkViolations(int load_idx, DynInstPtr &inst)
+{
+    Addr inst_eff_addr1 = inst->effAddr >> depCheckShift;
+    Addr inst_eff_addr2 = (inst->effAddr + inst->effSize - 1) >> depCheckShift;
+
+    /** @todo in theory you only need to check an instruction that has executed
+     * however, there isn't a good way in the pipeline at the moment to check
+     * all instructions that will execute before the store writes back. Thus,
+     * like the implementation that came before it, we're overly conservative.
+     */
+    while (load_idx != loadTail) {
+        DynInstPtr ld_inst = loadQueue[load_idx];
+        if (!ld_inst->effAddrValid || ld_inst->uncacheable()) {
+            incrLdIdx(load_idx);
+            continue;
+        }
+
+        Addr ld_eff_addr1 = ld_inst->effAddr >> depCheckShift;
+        Addr ld_eff_addr2 =
+            (ld_inst->effAddr + ld_inst->effSize - 1) >> depCheckShift;
+
+        if (inst_eff_addr2 >= ld_eff_addr1 && inst_eff_addr1 <= ld_eff_addr2) {
+            if (inst->isLoad()) {
+                // If this load is to the same block as an external snoop
+                // invalidate that we've observed then the load needs to be
+                // squashed as it could have newer data
+                if (ld_inst->hitExternalSnoop) {
+                    if (!memDepViolator ||
+                            ld_inst->seqNum < memDepViolator->seqNum) {
+                        DPRINTF(LSQUnit, "Detected fault with inst [sn:%lli] "
+                                "and [sn:%lli] at address %#x\n",
+                                inst->seqNum, ld_inst->seqNum, ld_eff_addr1);
+                        memDepViolator = ld_inst;
+
+                        ++lsqMemOrderViolation;
+
+                        return new GenericISA::M5PanicFault(
+                                "Detected fault with inst [sn:%lli] and "
+                                "[sn:%lli] at address %#x\n",
+                                inst->seqNum, ld_inst->seqNum, ld_eff_addr1);
+                    }
+                }
+
+                // Otherwise, mark the load has a possible load violation
+                // and if we see a snoop before it's commited, we need to squash
+                ld_inst->possibleLoadViolation = true;
+                DPRINTF(LSQUnit, "Found possible load violaiton at addr: %#x"
+                        " between instructions [sn:%lli] and [sn:%lli]\n",
+                        inst_eff_addr1, inst->seqNum, ld_inst->seqNum);
+            } else {
+                // A load/store incorrectly passed this store.
+                // Check if we already have a violator, or if it's newer
+                // squash and refetch.
+                if (memDepViolator && ld_inst->seqNum > memDepViolator->seqNum)
+                    break;
+
+                DPRINTF(LSQUnit, "Detected fault with inst [sn:%lli] and "
+                        "[sn:%lli] at address %#x\n",
+                        inst->seqNum, ld_inst->seqNum, ld_eff_addr1);
+                memDepViolator = ld_inst;
+
+                ++lsqMemOrderViolation;
+
+                return new GenericISA::M5PanicFault("Detected fault with "
+                        "inst [sn:%lli] and [sn:%lli] at address %#x\n",
+                        inst->seqNum, ld_inst->seqNum, ld_eff_addr1);
+            }
+        }
+
+        incrLdIdx(load_idx);
+    }
+    return NoFault;
+}
+
+
+
+
 template <class Impl>
 Fault
 LSQUnit<Impl>::executeLoad(DynInstPtr &inst)
@@ -477,39 +616,9 @@ LSQUnit<Impl>::executeLoad(DynInstPtr &inst)
         assert(inst->effAddrValid);
         int load_idx = inst->lqIdx;
         incrLdIdx(load_idx);
-        while (load_idx != loadTail) {
-            // Really only need to check loads that have actually executed
-
-            // @todo: For now this is extra conservative, detecting a
-            // violation if the addresses match assuming all accesses
-            // are quad word accesses.
-
-            // @todo: Fix this, magic number being used here
-
-            // @todo: Uncachable load is not executed until it reaches
-            // the head of the ROB. Once this if checks only the executed
-            // loads(as noted above), this check can be removed
-            if (loadQueue[load_idx]->effAddrValid &&
-                ((loadQueue[load_idx]->effAddr >> 8)
-                 == (inst->effAddr >> 8)) &&
-                !loadQueue[load_idx]->uncacheable()) {
-                // A load incorrectly passed this load.  Squash and refetch.
-                // For now return a fault to show that it was unsuccessful.
-                DynInstPtr violator = loadQueue[load_idx];
-                if (!memDepViolator ||
-                    (violator->seqNum < memDepViolator->seqNum)) {
-                    memDepViolator = violator;
-                } else {
-                    break;
-                }
 
-                ++lsqMemOrderViolation;
-
-                return genMachineCheckFault();
-            }
-
-            incrLdIdx(load_idx);
-        }
+        if (checkLoads)
+            return checkViolations(load_idx, inst);
     }
 
     return load_fault;
@@ -564,44 +673,8 @@ LSQUnit<Impl>::executeStore(DynInstPtr &store_inst)
         ++storesToWB;
     }
 
-    assert(store_inst->effAddrValid);
-    while (load_idx != loadTail) {
-        // Really only need to check loads that have actually executed
-        // It's safe to check all loads because effAddr is set to
-        // InvalAddr when the dyn inst is created.
-
-        // @todo: For now this is extra conservative, detecting a
-        // violation if the addresses match assuming all accesses
-        // are quad word accesses.
-
-        // @todo: Fix this, magic number being used here
-
-        // @todo: Uncachable load is not executed until it reaches
-        // the head of the ROB. Once this if checks only the executed
-        // loads(as noted above), this check can be removed
-        if (loadQueue[load_idx]->effAddrValid &&
-            ((loadQueue[load_idx]->effAddr >> 8)
-             == (store_inst->effAddr >> 8)) &&
-            !loadQueue[load_idx]->uncacheable()) {
-            // A load incorrectly passed this store.  Squash and refetch.
-            // For now return a fault to show that it was unsuccessful.
-            DynInstPtr violator = loadQueue[load_idx];
-            if (!memDepViolator ||
-                (violator->seqNum < memDepViolator->seqNum)) {
-                memDepViolator = violator;
-            } else {
-                break;
-            }
-
-            ++lsqMemOrderViolation;
-
-            return genMachineCheckFault();
-        }
-
-        incrLdIdx(load_idx);
-    }
+    return checkViolations(load_idx, store_inst);
 
-    return store_fault;
 }
 
 template <class Impl>
@@ -691,6 +764,7 @@ LSQUnit<Impl>::writebackStores()
            storeWBIdx != storeTail &&
            storeQueue[storeWBIdx].inst &&
            storeQueue[storeWBIdx].canWB &&
+           ((!needsTSO) || (!storeInFlight)) &&
            usedPorts < cachePorts) {
 
         if (isStoreBlocked || lsq->cacheBlocked()) {
@@ -728,6 +802,9 @@ LSQUnit<Impl>::writebackStores()
         DynInstPtr inst = storeQueue[storeWBIdx].inst;
 
         Request *req = storeQueue[storeWBIdx].req;
+        RequestPtr sreqLow = storeQueue[storeWBIdx].sreqLow;
+        RequestPtr sreqHigh = storeQueue[storeWBIdx].sreqHigh;
+
         storeQueue[storeWBIdx].committed = true;
 
         assert(!inst->memData);
@@ -753,9 +830,6 @@ LSQUnit<Impl>::writebackStores()
             data_pkt->dataStatic(inst->memData);
             data_pkt->senderState = state;
         } else {
-            RequestPtr sreqLow = storeQueue[storeWBIdx].sreqLow;
-            RequestPtr sreqHigh = storeQueue[storeWBIdx].sreqHigh;
-
             // Create two packets if the store is split in two.
             data_pkt = new Packet(sreqLow, command, Packet::Broadcast);
             snd_data_pkt = new Packet(sreqHigh, command, Packet::Broadcast);
@@ -797,6 +871,11 @@ LSQUnit<Impl>::writebackStores()
                         inst->seqNum);
                 WritebackEvent *wb = new WritebackEvent(inst, data_pkt, this);
                 cpu->schedule(wb, curTick() + 1);
+#if USE_CHECKER
+                // Make sure to set the LLSC data for verification
+                inst->reqToVerify->setExtraData(0);
+                inst->completeAcc(data_pkt);
+#endif
                 completeStore(storeWBIdx);
                 incrStIdx(storeWBIdx);
                 continue;
@@ -806,20 +885,40 @@ LSQUnit<Impl>::writebackStores()
             state->noWB = true;
         }
 
-        if (!sendStore(data_pkt)) {
+        bool split =
+            TheISA::HasUnalignedMemAcc && storeQueue[storeWBIdx].isSplit;
+
+        ThreadContext *thread = cpu->tcBase(lsqID);
+
+        if (req->isMmappedIpr()) {
+            assert(!inst->isStoreConditional());
+            TheISA::handleIprWrite(thread, data_pkt);
+            delete data_pkt;
+            if (split) {
+                assert(snd_data_pkt->req->isMmappedIpr());
+                TheISA::handleIprWrite(thread, snd_data_pkt);
+                delete snd_data_pkt;
+                delete sreqLow;
+                delete sreqHigh;
+            }
+            delete state;
+            delete req;
+            completeStore(storeWBIdx);
+            incrStIdx(storeWBIdx);
+        } else if (!sendStore(data_pkt)) {
             DPRINTF(IEW, "D-Cache became blocked when writing [sn:%lli], will"
                     "retry later\n",
                     inst->seqNum);
 
             // Need to store the second packet, if split.
-            if (TheISA::HasUnalignedMemAcc && storeQueue[storeWBIdx].isSplit) {
+            if (split) {
                 state->pktToSend = true;
                 state->pendingPacket = snd_data_pkt;
             }
         } else {
 
             // If split, try to send the second packet too
-            if (TheISA::HasUnalignedMemAcc && storeQueue[storeWBIdx].isSplit) {
+            if (split) {
                 assert(snd_data_pkt);
 
                 // Ensure there are enough ports to use.
@@ -991,6 +1090,10 @@ LSQUnit<Impl>::storePostSend(PacketPtr pkt)
 #endif
     }
 
+    if (needsTSO) {
+        storeInFlight = true;
+    }
+
     incrStIdx(storeWBIdx);
 }
 
@@ -1064,6 +1167,10 @@ LSQUnit<Impl>::completeStore(int store_idx)
 
     storeQueue[store_idx].inst->setCompleted();
 
+    if (needsTSO) {
+        storeInFlight = false;
+    }
+
     // Tell the checker we've completed this instruction.  Some stores
     // may get reported twice to the checker, but the checker can
     // handle that case.
@@ -1098,10 +1205,10 @@ LSQUnit<Impl>::recvRetry()
         DPRINTF(LSQUnit, "Receiving retry: store blocked\n");
         assert(retryPkt != NULL);
 
-        if (dcachePort->sendTiming(retryPkt)) {
-            LSQSenderState *state =
-                dynamic_cast<LSQSenderState *>(retryPkt->senderState);
+        LSQSenderState *state =
+            dynamic_cast<LSQSenderState *>(retryPkt->senderState);
 
+        if (dcachePort->sendTiming(retryPkt)) {
             // Don't finish the store unless this is the last packet.
             if (!TheISA::HasUnalignedMemAcc || !state->pktToSend ||
                     state->pendingPacket == retryPkt) {