systemc: Match how Accellera schedules processes even more closely.
authorGabe Black <gabeblack@google.com>
Thu, 6 Sep 2018 00:21:11 +0000 (17:21 -0700)
committerGabe Black <gabeblack@google.com>
Tue, 9 Oct 2018 21:36:50 +0000 (21:36 +0000)
The Accellera implementation runs processes in a cycle where it first
runs all the methods it has, then all the threads, and then starts
again in case any new methods have been scheduled. This keeps methods
and processes in the order they were marked ready (what a prior change
made this scheduler do), but also keeps the methods together and the
threads together (something it used to do, but that change made it
stop doing). This change should make the gem5 scheduler match in both
respects.

Note that its correct to run the processes in whatever order we want,
it's just that if we're going to compare against the "golden" output
from the Accellera tests, we need to match the order to get sensible
results.

Change-Id: I0b1e4ed24c56f97921148b74e90c2dca5fd3fbc4
Reviewed-on: https://gem5-review.googlesource.com/c/12595
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>

src/systemc/core/scheduler.cc
src/systemc/core/scheduler.hh

index cf34fe8b7c38789fcf87c0a786f72050814a7f06..78b47cd4d88338778dcd706368a9eb9ac13f61b4 100644 (file)
@@ -47,7 +47,7 @@ Scheduler::Scheduler() :
     _started(false), _paused(false), _stopped(false), _stopNow(false),
     maxTickEvent(this, false, MaxTickPriority),
     _numCycles(0), _changeStamp(0), _current(nullptr), initDone(false),
-    runOnce(false)
+    runOnce(false), readyList(nullptr)
 {}
 
 Scheduler::~Scheduler()
@@ -90,7 +90,9 @@ Scheduler::clear()
         p->popListNode();
     while ((p = initList.getNext()))
         p->popListNode();
-    while ((p = readyList.getNext()))
+    while ((p = readyListMethods.getNext()))
+        p->popListNode();
+    while ((p = readyListThreads.getNext()))
         p->popListNode();
 
     Channel *c;
@@ -162,7 +164,8 @@ Scheduler::dontInitialize(Process *p)
 void
 Scheduler::yield()
 {
-    _current = readyList.getNext();
+    // Pull a process from the active list.
+    _current = readyList->getNext();
     if (!_current) {
         // There are no more processes, so return control to evaluate.
         Fiber::primaryFiber()->run();
@@ -192,13 +195,10 @@ Scheduler::ready(Process *p)
     if (_stopNow)
         return;
 
-    // Clump methods together to minimize context switching.
-    static bool cluster_methods = false;
-
-    if (cluster_methods && p->procKind() == ::sc_core::SC_METHOD_PROC_)
-        readyList.pushFirst(p);
+    if (p->procKind() == ::sc_core::SC_METHOD_PROC_)
+        readyListMethods.pushLast(p);
     else
-        readyList.pushLast(p);
+        readyListThreads.pushLast(p);
 
     scheduleReadyEvent();
 }
@@ -212,28 +212,32 @@ Scheduler::resume(Process *p)
         initList.pushLast(p);
 }
 
+bool
+listContains(ListNode *list, ListNode *target)
+{
+    ListNode *n = list->nextListNode;
+    while (n != list)
+        if (n == target)
+            return true;
+    return false;
+}
+
 bool
 Scheduler::suspend(Process *p)
 {
+    bool was_ready;
     if (initDone) {
         // After initialization, the only list we can be on is the ready list.
-        bool was_ready = (p->nextListNode != nullptr);
+        was_ready = (p->nextListNode != nullptr);
         p->popListNode();
-        return was_ready;
     } else {
-        bool was_ready = false;
-        // Check the ready list to see if we find this process.
-        ListNode *n = readyList.nextListNode;
-        while (n != &readyList) {
-            if (n == p) {
-                was_ready = true;
-                break;
-            }
-        }
+        // Check the ready lists to see if we find this process.
+        was_ready = listContains(&readyListMethods, p) ||
+            listContains(&readyListThreads, p);
         if (was_ready)
             toFinalize.pushLast(p);
-        return was_ready;
     }
+    return was_ready;
 }
 
 void
@@ -267,13 +271,24 @@ Scheduler::scheduleStarvationEvent()
 void
 Scheduler::runReady()
 {
-    bool empty = readyList.empty();
+    bool empty = readyListMethods.empty() && readyListThreads.empty();
     lastReadyTick = getCurTick();
 
     // The evaluation phase.
     do {
-        yield();
-    } while (!readyList.empty());
+        // We run methods and threads in two seperate passes to emulate how
+        // Accellera orders things, but without having to scan through a
+        // unified list to find the next process of the correct type.
+        readyList = &readyListMethods;
+        while (!readyListMethods.empty())
+            yield();
+
+        readyList = &readyListThreads;
+        while (!readyListThreads.empty())
+            yield();
+
+        // We already know that readyListThreads is empty at this point.
+    } while (!readyListMethods.empty());
 
     if (!empty) {
         _numCycles++;
index f0cbac43cd6d81e79edd74741df8727cf34a6d7b..29501bc24efc2ed7b1f7b667d9a164bd6da0e0c7 100644 (file)
@@ -197,11 +197,16 @@ class Scheduler
     void
     runNow(Process *p)
     {
+        // This function may put a process on the wrong list, ie a method on
+        // the process list or vice versa. That's fine since that's just a
+        // performance optimization, and the important thing here is how the
+        // processes are ordered.
+
         // If a process is running, schedule it/us to run again.
         if (_current)
-            readyList.pushFirst(_current);
+            readyList->pushFirst(_current);
         // Schedule p to run first.
-        readyList.pushFirst(p);
+        readyList->pushFirst(p);
         yield();
     }
 
@@ -290,7 +295,8 @@ class Scheduler
     bool
     pendingCurr()
     {
-        return !readyList.empty() || !updateList.empty() || !deltas.empty();
+        return !readyListMethods.empty() || !readyListThreads.empty() ||
+            !updateList.empty() || !deltas.empty();
     }
 
     // Return whether there are pending timed notifications or timeouts.
@@ -376,7 +382,8 @@ class Scheduler
     bool
     starved()
     {
-        return (readyList.empty() && updateList.empty() && deltas.empty() &&
+        return (readyListMethods.empty() && readyListThreads.empty() &&
+                updateList.empty() && deltas.empty() &&
                 (timeSlots.empty() || timeSlots.begin()->first > maxTick) &&
                 initList.empty());
     }
@@ -410,7 +417,10 @@ class Scheduler
 
     ProcessList initList;
     ProcessList toFinalize;
-    ProcessList readyList;
+
+    ProcessList *readyList;
+    ProcessList readyListMethods;
+    ProcessList readyListThreads;
 
     ChannelList updateList;