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.
This commit is contained in:
parent
c00219d9f3
commit
9bc42cb8c5
35
nmigen/sim/_pyclock.py
Normal file
35
nmigen/sim/_pyclock.py
Normal file
|
@ -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
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue