From: whitequark Date: Thu, 27 Aug 2020 07:54:27 +0000 (+0000) Subject: sim._pyclock: new type of process. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9a910d3d9735538234961d07c82e7c929a509773;p=nmigen.git 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. --- 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):