From 9a910d3d9735538234961d07c82e7c929a509773 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 27 Aug 2020 07:54:27 +0000 Subject: [PATCH] sim._pyclock: new type of process. The overhead of coroutine processes is fairly high. A clock driver implemented through a coroutine process is mostly overhead. This was partially addressed in commit 2398b792 by microoptimizing yielding. This commit eliminates the coroutine process overhead completely by introducing dedicated clock processes. It also simplifies the logic to a simple toggle. This change improves runtime by about 12% on Minerva SRAM SoC. --- nmigen/sim/_pyclock.py | 35 +++++++++++++++++++++++++++++++++++ nmigen/sim/pysim.py | 20 +++----------------- 2 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 nmigen/sim/_pyclock.py diff --git a/nmigen/sim/_pyclock.py b/nmigen/sim/_pyclock.py new file mode 100644 index 0000000..3e1e6ab --- /dev/null +++ b/nmigen/sim/_pyclock.py @@ -0,0 +1,35 @@ +import inspect + +from ._core import Process + + +__all__ = ["PyClockProcess"] + + +class PyClockProcess(Process): + def __init__(self, state, signal, *, phase, period): + assert len(signal) == 1 + + self.state = state + self.slot = self.state.get_signal(signal) + self.phase = phase + self.period = period + + self.reset() + + def reset(self): + self.runnable = True + self.passive = True + self.initial = True + + def run(self): + if self.initial: + self.initial = False + self.state.timeline.delay(self.phase, self) + + else: + clk_state = self.state.slots[self.slot] + clk_state.set(not clk_state.curr) + self.state.timeline.delay(self.period / 2, self) + + self.runnable = False diff --git a/nmigen/sim/pysim.py b/nmigen/sim/pysim.py index c50b742..5f30816 100644 --- a/nmigen/sim/pysim.py +++ b/nmigen/sim/pysim.py @@ -11,6 +11,7 @@ from ._cmds import * from ._core import * from ._pyrtl import _FragmentCompiler from ._pycoro import PyCoroProcess +from ._pyclock import PyClockProcess __all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"] @@ -299,27 +300,12 @@ class Simulator: raise ValueError("Domain {!r} already has a clock driving it" .format(domain.name)) - half_period = period / 2 if phase is None: # By default, delay the first edge by half period. This causes any synchronous activity # to happen at a non-zero time, distinguishing it from the reset values in the waveform # viewer. - phase = half_period - def clk_process(): - yield Passive() - yield Delay(phase) - # Behave correctly if the process is added after the clock signal is manipulated, or if - # its reset state is high. - initial = (yield domain.clk) - steps = ( - domain.clk.eq(~initial), - Delay(half_period), - domain.clk.eq(initial), - Delay(half_period), - ) - while True: - yield from iter(steps) - self._add_coroutine_process(clk_process, default_cmd=None) + phase = period / 2 + self._processes.add(PyClockProcess(self._state, domain.clk, phase=phase, period=period)) self._clocked.add(domain) def reset(self): -- 2.30.2