back.pysim: extract timeline handling to class _Timeline. NFC.
authorwhitequark <whitequark@whitequark.org>
Wed, 8 Jul 2020 06:04:50 +0000 (06:04 +0000)
committerwhitequark <whitequark@whitequark.org>
Wed, 8 Jul 2020 06:31:04 +0000 (06:31 +0000)
nmigen/back/pysim.py

index f3c1feccc1aafabacb55328ae67706b256646795..57480b2792d9dabc86b2ab273427f5024e36117e 100644 (file)
@@ -138,6 +138,54 @@ class _Process:
         raise NotImplementedError # :nocov:
 
 
+class _Timeline:
+    def __init__(self):
+        self.now = 0.0
+        self.deadlines = dict()
+
+    def reset(self):
+        self.now = 0.0
+        self.deadlines.clear()
+
+    def at(self, run_at, process):
+        assert process not in self.deadlines
+        self.deadlines[process] = run_at
+
+    def delay(self, delay_by, process):
+        if delay_by is None:
+            run_at = self.now
+        else:
+            run_at = self.now + delay_by
+        self.at(run_at, process)
+
+    def advance(self):
+        nearest_processes = set()
+        nearest_deadline = None
+        for process, deadline in self.deadlines.items():
+            if deadline is None:
+                if nearest_deadline is not None:
+                    nearest_processes.clear()
+                nearest_processes.add(process)
+                nearest_deadline = self.now
+                break
+            elif nearest_deadline is None or deadline <= nearest_deadline:
+                assert deadline >= self.now
+                if nearest_deadline is not None and deadline < nearest_deadline:
+                    nearest_processes.clear()
+                nearest_processes.add(process)
+                nearest_deadline = deadline
+
+        if not nearest_processes:
+            return False
+
+        for process in nearest_processes:
+            process.runnable = True
+            del self.deadlines[process]
+        self.now = nearest_deadline
+
+        return True
+
+
 class _SignalState:
     __slots__ = ("signal", "curr", "next", "waiters", "pending")
 
@@ -167,21 +215,16 @@ class _SignalState:
 
 class _SimulatorState:
     def __init__(self):
-        self.signals = SignalDict()
-        self.slots   = []
-        self.pending = set()
-
-        self.timestamp = 0.0
-        self.deadlines = dict()
+        self.timeline = _Timeline()
+        self.signals  = SignalDict()
+        self.slots    = []
+        self.pending  = set()
 
     def reset(self):
         for signal, index in self.signals.items():
             self.slots[index].curr = self.slots[index].next = signal.reset
         self.pending.clear()
 
-        self.timestamp = 0.0
-        self.deadlines.clear()
-
     def get_signal(self, signal):
         try:
             return self.signals[signal]
@@ -210,33 +253,6 @@ class _SimulatorState:
         self.pending.clear()
         return converged
 
-    def advance(self):
-        nearest_processes = set()
-        nearest_deadline = None
-        for process, deadline in self.deadlines.items():
-            if deadline is None:
-                if nearest_deadline is not None:
-                    nearest_processes.clear()
-                nearest_processes.add(process)
-                nearest_deadline = self.timestamp
-                break
-            elif nearest_deadline is None or deadline <= nearest_deadline:
-                assert deadline >= self.timestamp
-                if nearest_deadline is not None and deadline < nearest_deadline:
-                    nearest_processes.clear()
-                nearest_processes.add(process)
-                nearest_deadline = deadline
-
-        if not nearest_processes:
-            return False
-
-        for process in nearest_processes:
-            process.runnable = True
-            del self.deadlines[process]
-        self.timestamp = nearest_deadline
-
-        return True
-
 
 class _Emitter:
     def __init__(self):
@@ -783,14 +799,11 @@ class _CoroutineProcess(_Process):
                     return
 
                 elif type(command) is Settle:
-                    self.state.deadlines[self] = None
+                    self.state.timeline.delay(None, self)
                     return
 
                 elif type(command) is Delay:
-                    if command.interval is None:
-                        self.state.deadlines[self] = None
-                    else:
-                        self.state.deadlines[self] = self.state.timestamp + command.interval
+                    self.state.timeline.delay(command.interval, self)
                     return
 
                 elif type(command) is Passive:
@@ -937,7 +950,7 @@ class Simulator:
 
             for waveform_writer in self._waveform_writers:
                 for signal_state in self._state.pending:
-                    waveform_writer.update(self._state.timestamp,
+                    waveform_writer.update(self._state.timeline.now,
                         signal_state.signal, signal_state.curr)
 
             # 2. commit: apply every queued signal change, waking up any waiting processes
@@ -958,7 +971,7 @@ class Simulator:
         Returns ``True`` if there are any active processes, ``False`` otherwise.
         """
         self._real_step()
-        self._state.advance()
+        self._state.timeline.advance()
         return any(not process.passive for process in self._processes)
 
     def run(self):
@@ -980,8 +993,8 @@ class Simulator:
 
         If the simulation stops advancing, this function will never return.
         """
-        assert self._state.timestamp <= deadline
-        while (self.advance() or run_passive) and self._state.timestamp < deadline:
+        assert self._state.timeline.now <= deadline
+        while (self.advance() or run_passive) and self._state.timeline.now < deadline:
             pass
 
     @contextmanager
@@ -1004,11 +1017,11 @@ class Simulator:
         traces : iterable of Signal
             Signals to display traces for.
         """
-        if self._state.timestamp != 0.0:
+        if self._state.timeline.now != 0.0:
             raise ValueError("Cannot start writing waveforms after advancing simulation time")
         waveform_writer = _VCDWaveformWriter(self._signal_names,
             vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces)
         self._waveform_writers.append(waveform_writer)
         yield
-        waveform_writer.close(self._state.timestamp)
+        waveform_writer.close(self._state.timeline.now)
         self._waveform_writers.remove(waveform_writer)