pwr: Adds logic to enter power gating for the cpu model
[gem5.git] / src / cpu / simple / atomic.cc
index 6690c1da62316ef69e27a9815bb000ec0ef7aea6..9039e6137b1d3164e7f3b524c13a17d1fe4483e3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014 Google, Inc.
- * Copyright (c) 2012-2013,2015 ARM Limited
+ * Copyright (c) 2012-2013,2015,2017 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
  * Authors: Steve Reinhardt
  */
 
+#include "cpu/simple/atomic.hh"
+
 #include "arch/locked_mem.hh"
 #include "arch/mmapped_ipr.hh"
 #include "arch/utility.hh"
 #include "base/bigint.hh"
 #include "base/output.hh"
 #include "config/the_isa.hh"
-#include "cpu/simple/atomic.hh"
 #include "cpu/exetrace.hh"
 #include "debug/Drain.hh"
 #include "debug/ExecFaulting.hh"
 #include "mem/physical.hh"
 #include "params/AtomicSimpleCPU.hh"
 #include "sim/faults.hh"
-#include "sim/system.hh"
 #include "sim/full_system.hh"
+#include "sim/system.hh"
 
 using namespace std;
 using namespace TheISA;
 
-AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c)
-    : Event(CPU_Tick_Pri), cpu(c)
-{
-}
-
-
-void
-AtomicSimpleCPU::TickEvent::process()
-{
-    cpu->tick();
-}
-
-const char *
-AtomicSimpleCPU::TickEvent::description() const
-{
-    return "AtomicSimpleCPU tick";
-}
-
 void
 AtomicSimpleCPU::init()
 {
     BaseSimpleCPU::init();
 
-    ifetch_req.setThreadContext(_cpuId, 0);
-    data_read_req.setThreadContext(_cpuId, 0);
-    data_write_req.setThreadContext(_cpuId, 0);
+    int cid = threadContexts[0]->contextId();
+    ifetch_req.setContext(cid);
+    data_read_req.setContext(cid);
+    data_write_req.setContext(cid);
 }
 
 AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p)
-    : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false),
+    : BaseSimpleCPU(p),
+      tickEvent([this]{ tick(); }, "AtomicSimpleCPU tick",
+                false, Event::CPU_Tick_Pri),
+      width(p->width), locked(false),
       simulate_data_stalls(p->simulate_data_stalls),
       simulate_inst_stalls(p->simulate_inst_stalls),
       icachePort(name() + ".icache_port", this),
@@ -114,6 +101,9 @@ AtomicSimpleCPU::~AtomicSimpleCPU()
 DrainState
 AtomicSimpleCPU::drain()
 {
+    // Deschedule any power gating event (if any)
+    deschedulePowerGatingEvent();
+
     if (switchedOut())
         return DrainState::Drained;
 
@@ -130,6 +120,24 @@ AtomicSimpleCPU::drain()
     }
 }
 
+void
+AtomicSimpleCPU::threadSnoop(PacketPtr pkt, ThreadID sender)
+{
+    DPRINTF(SimpleCPU, "received snoop pkt for addr:%#x %s\n", pkt->getAddr(),
+            pkt->cmdString());
+
+    for (ThreadID tid = 0; tid < numThreads; tid++) {
+        if (tid != sender) {
+            if (getCpuAddrMonitor(tid)->doMonitor(pkt)) {
+                wakeup(tid);
+            }
+
+            TheISA::handleLockedSnoop(threadInfo[tid]->thread,
+                                      pkt, dcachePort.cacheBlockMask);
+        }
+    }
+}
+
 void
 AtomicSimpleCPU::drainResume()
 {
@@ -158,6 +166,9 @@ AtomicSimpleCPU::drainResume()
             threadInfo[tid]->notIdleFraction = 0;
         }
     }
+
+    // Reschedule any power gating event (if any)
+    schedulePowerGatingEvent();
 }
 
 bool
@@ -228,6 +239,8 @@ AtomicSimpleCPU::activateContext(ThreadID thread_num)
         == activeThreads.end()) {
         activeThreads.push_back(thread_num);
     }
+
+    BaseCPU::activateContext(thread_num);
 }
 
 
@@ -254,6 +267,7 @@ AtomicSimpleCPU::suspendContext(ThreadID thread_num)
         }
     }
 
+    BaseCPU::suspendContext(thread_num);
 }
 
 
@@ -265,12 +279,18 @@ AtomicSimpleCPU::AtomicCPUDPort::recvAtomicSnoop(PacketPtr pkt)
 
     // X86 ISA: Snooping an invalidation for monitor/mwait
     AtomicSimpleCPU *cpu = (AtomicSimpleCPU *)(&owner);
-    if(cpu->getCpuAddrMonitor()->doMonitor(pkt)) {
-        cpu->wakeup();
+
+    for (ThreadID tid = 0; tid < cpu->numThreads; tid++) {
+        if (cpu->getCpuAddrMonitor(tid)->doMonitor(pkt)) {
+            cpu->wakeup(tid);
+        }
     }
 
     // if snoop invalidates, release any associated locks
-    if (pkt->isInvalidate()) {
+    // When run without caches, Invalidation packets will not be received
+    // hence we must check if the incoming packets are writes and wakeup
+    // the processor accordingly
+    if (pkt->isInvalidate() || pkt->isWrite()) {
         DPRINTF(SimpleCPU, "received invalidation for addr:%#x\n",
                 pkt->getAddr());
         for (auto &t_info : cpu->threadInfo) {
@@ -289,8 +309,10 @@ AtomicSimpleCPU::AtomicCPUDPort::recvFunctionalSnoop(PacketPtr pkt)
 
     // X86 ISA: Snooping an invalidation for monitor/mwait
     AtomicSimpleCPU *cpu = (AtomicSimpleCPU *)(&owner);
-    if(cpu->getCpuAddrMonitor()->doMonitor(pkt)) {
-        cpu->wakeup();
+    for (ThreadID tid = 0; tid < cpu->numThreads; tid++) {
+        if (cpu->getCpuAddrMonitor(tid)->doMonitor(pkt)) {
+            cpu->wakeup(tid);
+        }
     }
 
     // if snoop invalidates, release any associated locks
@@ -304,8 +326,8 @@ AtomicSimpleCPU::AtomicCPUDPort::recvFunctionalSnoop(PacketPtr pkt)
 }
 
 Fault
-AtomicSimpleCPU::readMem(Addr addr, uint8_t * data,
-                         unsigned size, unsigned flags)
+AtomicSimpleCPU::readMem(Addr addr, uint8_t * data, unsigned size,
+                         Request::Flags flags)
 {
     SimpleExecContext& t_info = *threadInfo[curThread];
     SimpleThread* thread = t_info.thread;
@@ -391,10 +413,17 @@ AtomicSimpleCPU::readMem(Addr addr, uint8_t * data,
     }
 }
 
+Fault
+AtomicSimpleCPU::initiateMemRead(Addr addr, unsigned size,
+                                 Request::Flags flags)
+{
+    panic("initiateMemRead() is for timing accesses, and should "
+          "never be called on AtomicSimpleCPU.\n");
+}
 
 Fault
-AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size,
-                          Addr addr, unsigned flags, uint64_t *res)
+AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size, Addr addr,
+                          Request::Flags flags, uint64_t *res)
 {
     SimpleExecContext& t_info = *threadInfo[curThread];
     SimpleThread* thread = t_info.thread;
@@ -420,13 +449,13 @@ AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size,
     //across a cache line boundary.
     Addr secondAddr = roundDown(addr + size - 1, cacheLineSize());
 
-    if(secondAddr > addr)
+    if (secondAddr > addr)
         size = secondAddr - addr;
 
     dcache_latency = 0;
 
     req->taskId(taskId());
-    while(1) {
+    while (1) {
         req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr());
 
         // translate to physical address
@@ -460,6 +489,9 @@ AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size,
                         system->getPhysMem().access(&pkt);
                     else
                         dcache_latency += dcachePort.sendAtomic(&pkt);
+
+                    // Notify other threads on this CPU of write
+                    threadSnoop(&pkt, curThread);
                 }
                 dcache_access = true;
                 assert(!pkt.isError());
@@ -516,9 +548,11 @@ AtomicSimpleCPU::tick()
 
     // Set memroy request ids to current thread
     if (numThreads > 1) {
-        ifetch_req.setThreadContext(_cpuId, curThread);
-        data_read_req.setThreadContext(_cpuId, curThread);
-        data_write_req.setThreadContext(_cpuId, curThread);
+        ContextID cid = threadContexts[curThread]->contextId();
+
+        ifetch_req.setContext(cid);
+        data_read_req.setContext(cid);
+        data_write_req.setContext(cid);
     }
 
     SimpleExecContext& t_info = *threadInfo[curThread];
@@ -565,7 +599,7 @@ AtomicSimpleCPU::tick()
                 // like the I cache. It should be flushed, and when that works
                 // this code should be uncommented.
                 //Fetch more instruction memory if necessary
-                //if(decoder.needMoreBytes())
+                //if (decoder.needMoreBytes())
                 //{
                     icache_access = true;
                     Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq);
@@ -585,6 +619,7 @@ AtomicSimpleCPU::tick()
 
             preExecute();
 
+            Tick stall_ticks = 0;
             if (curStaticInst) {
                 fault = curStaticInst->execute(&t_info, traceData);
 
@@ -598,6 +633,13 @@ AtomicSimpleCPU::tick()
                     traceData = NULL;
                 }
 
+                if (dynamic_pointer_cast<SyscallRetryFault>(fault)) {
+                    // Retry execution of system calls after a delay.
+                    // Prevents immediate re-execution since conditions which
+                    // caused the retry are unlikely to change every tick.
+                    stall_ticks += clockEdge(syscallRetryLatency) - curTick();
+                }
+
                 postExecute();
             }
 
@@ -606,7 +648,6 @@ AtomicSimpleCPU::tick()
                         curStaticInst->isFirstMicroop()))
                 instCnt++;
 
-            Tick stall_ticks = 0;
             if (simulate_inst_stalls && icache_access)
                 stall_ticks += icache_latency;
 
@@ -622,7 +663,7 @@ AtomicSimpleCPU::tick()
             }
 
         }
-        if(fault != NoFault || !t_info.stayAtPC)
+        if (fault != NoFault || !t_info.stayAtPC)
             advancePC(fault);
     }