from nmigen import *
-from nmigen.back import rtlil, verilog, pysim
+from nmigen.sim import *
+from nmigen.back import rtlil, verilog
class Counter(Elaboratable):
print(verilog.convert(ctr, ports=[ctr.o, ctr.en]))
-sim = pysim.Simulator(ctr)
+sim = Simulator(ctr)
sim.add_clock(1e-6)
def ce_proc():
yield; yield; yield
import warnings
-from ..sim.pysim import *
+from ..sim import *
__all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"]
# TODO(nmigen-0.4): remove
-warnings.warn("instead of back.pysim, use sim.pysim",
+warnings.warn("instead of nmigen.back.pysim.*, use nmigen.sim.*",
DeprecationWarning, stacklevel=2)
import inspect
from collections.abc import Iterable
from ...hdl.cd import ClockDomain
-from ...back.pysim import *
from ...hdl.ir import Fragment
+from ...sim import *
__all__ = ["run_simulation", "passive"]
+from .core import *
+
+
+__all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"]
--- /dev/null
+__all__ = ["BaseProcess", "BaseSignalState", "BaseSimulation", "BaseEngine"]
+
+
+class BaseProcess:
+ __slots__ = ()
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.runnable = False
+ self.passive = True
+
+ def run(self):
+ raise NotImplementedError
+
+
+class BaseSignalState:
+ __slots__ = ()
+
+ signal = NotImplemented
+
+ curr = NotImplemented
+ next = NotImplemented
+
+ def set(self, value):
+ raise NotImplementedError
+
+
+class BaseSimulation:
+ def reset(self):
+ raise NotImplementedError
+
+ def get_signal(self, signal):
+ raise NotImplementedError
+
+ slots = NotImplemented
+
+ def add_trigger(self, process, signal, *, trigger=None):
+ raise NotImplementedError
+
+ def remove_trigger(self, process, signal):
+ raise NotImplementedError
+
+ def wait_interval(self, process, interval):
+ raise NotImplementedError
+
+
+class BaseEngine:
+ def add_coroutine_process(self, process, *, default_cmd):
+ raise NotImplementedError
+
+ def add_clock_process(self, clock, *, phase, period):
+ raise NotImplementedError
+
+ def reset(self):
+ raise NotImplementedError
+
+ @property
+ def now(self):
+ raise NotImplementedError
+
+ def advance(self):
+ raise NotImplementedError
+
+ def write_vcd(self, *, vcd_file, gtkw_file, traces):
+ raise NotImplementedError
+++ /dev/null
-from ..hdl.cd import *
-
-
-__all__ = ["Settle", "Delay", "Tick", "Passive", "Active"]
-
-
-class Command:
- pass
-
-
-class Settle(Command):
- def __repr__(self):
- return "(settle)"
-
-
-class Delay(Command):
- def __init__(self, interval=None):
- self.interval = None if interval is None else float(interval)
-
- def __repr__(self):
- if self.interval is None:
- return "(delay ε)"
- else:
- return "(delay {:.3}us)".format(self.interval * 1e6)
-
-
-class Tick(Command):
- def __init__(self, domain="sync"):
- if not isinstance(domain, (str, ClockDomain)):
- raise TypeError("Domain must be a string or a ClockDomain instance, not {!r}"
- .format(domain))
- assert domain != "comb"
- self.domain = domain
-
- def __repr__(self):
- return "(tick {})".format(self.domain)
-
-
-class Passive(Command):
- def __repr__(self):
- return "(passive)"
-
-
-class Active(Command):
- def __repr__(self):
- return "(active)"
+++ /dev/null
-__all__ = ["Process", "Timeline"]
-
-
-class Process:
- def __init__(self, *, is_comb):
- self.is_comb = is_comb
-
- self.reset()
-
- def reset(self):
- self.runnable = self.is_comb
- self.passive = True
-
- def run(self):
- raise NotImplementedError
-
-
-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
import inspect
-from ._core import Process
+from ._base import BaseProcess
__all__ = ["PyClockProcess"]
-class PyClockProcess(Process):
+class PyClockProcess(BaseProcess):
def __init__(self, state, signal, *, phase, period):
assert len(signal) == 1
def reset(self):
self.runnable = True
self.passive = True
+
self.initial = True
def run(self):
+ self.runnable = False
+
if self.initial:
self.initial = False
- self.state.timeline.delay(self.phase, self)
+ self.state.wait_interval(self, self.phase)
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
+ self.state.wait_interval(self, self.period / 2)
from ..hdl import *
from ..hdl.ast import Statement, SignalSet
-from ._cmds import *
-from ._core import Process
+from .core import Tick, Settle, Delay, Passive, Active
+from ._base import BaseProcess
from ._pyrtl import _ValueCompiler, _RHSValueCompiler, _StatementCompiler
__all__ = ["PyCoroProcess"]
-class PyCoroProcess(Process):
+class PyCoroProcess(BaseProcess):
def __init__(self, state, domains, constructor, *, default_cmd=None):
self.state = state
self.domains = domains
def reset(self):
self.runnable = True
self.passive = False
+
self.coroutine = self.constructor()
self.exec_locals = {
"slots": self.state.slots,
return
elif type(command) is Settle:
- self.state.timeline.delay(None, self)
+ self.state.wait_interval(self, None)
return
elif type(command) is Delay:
- self.state.timeline.delay(command.interval, self)
+ self.state.wait_interval(self, command.interval)
return
elif type(command) is Passive:
from ..hdl import *
from ..hdl.ast import SignalSet
from ..hdl.xfrm import ValueVisitor, StatementVisitor, LHSGroupFilter
-from ._core import *
+from ._base import BaseProcess
__all__ = ["PyRTLProcess"]
-class PyRTLProcess(Process):
- pass
+class PyRTLProcess(BaseProcess):
+ __slots__ = ("is_comb", "runnable", "passive", "run")
+
+ def __init__(self, *, is_comb):
+ self.is_comb = is_comb
+
+ self.reset()
+
+ def reset(self):
+ self.runnable = self.is_comb
+ self.passive = True
class _PythonEmitter:
--- /dev/null
+import inspect
+
+from .._utils import deprecated
+from ..hdl.cd import *
+from ..hdl.ir import *
+from ._base import BaseEngine
+
+
+__all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"]
+
+
+class Command:
+ pass
+
+
+class Settle(Command):
+ def __repr__(self):
+ return "(settle)"
+
+
+class Delay(Command):
+ def __init__(self, interval=None):
+ self.interval = None if interval is None else float(interval)
+
+ def __repr__(self):
+ if self.interval is None:
+ return "(delay ε)"
+ else:
+ return "(delay {:.3}us)".format(self.interval * 1e6)
+
+
+class Tick(Command):
+ def __init__(self, domain="sync"):
+ if not isinstance(domain, (str, ClockDomain)):
+ raise TypeError("Domain must be a string or a ClockDomain instance, not {!r}"
+ .format(domain))
+ assert domain != "comb"
+ self.domain = domain
+
+ def __repr__(self):
+ return "(tick {})".format(self.domain)
+
+
+class Passive(Command):
+ def __repr__(self):
+ return "(passive)"
+
+
+class Active(Command):
+ def __repr__(self):
+ return "(active)"
+
+
+class Simulator:
+ def __init__(self, fragment, *, engine="pysim"):
+ if isinstance(engine, type) and issubclass(engine, BaseEngine):
+ pass
+ elif engine == "pysim":
+ from .pysim import PySimEngine
+ engine = PySimEngine
+ else:
+ raise TypeError("Value '{!r}' is not a simulation engine class or "
+ "a simulation engine name"
+ .format(engine))
+
+ self._fragment = Fragment.get(fragment, platform=None).prepare()
+ self._engine = engine(self._fragment)
+ self._clocked = set()
+
+ def _check_process(self, process):
+ if not (inspect.isgeneratorfunction(process) or inspect.iscoroutinefunction(process)):
+ raise TypeError("Cannot add a process {!r} because it is not a generator function"
+ .format(process))
+ return process
+
+ def add_process(self, process):
+ process = self._check_process(process)
+ def wrapper():
+ # Only start a bench process after comb settling, so that the reset values are correct.
+ yield Settle()
+ yield from process()
+ self._engine.add_coroutine_process(wrapper, default_cmd=None)
+
+ def add_sync_process(self, process, *, domain="sync"):
+ process = self._check_process(process)
+ def wrapper():
+ # Only start a sync process after the first clock edge (or reset edge, if the domain
+ # uses an asynchronous reset). This matches the behavior of synchronous FFs.
+ yield Tick(domain)
+ yield from process()
+ self._engine.add_coroutine_process(wrapper, default_cmd=Tick(domain))
+
+ def add_clock(self, period, *, phase=None, domain="sync", if_exists=False):
+ """Add a clock process.
+
+ Adds a process that drives the clock signal of ``domain`` at a 50% duty cycle.
+
+ Arguments
+ ---------
+ period : float
+ Clock period. The process will toggle the ``domain`` clock signal every ``period / 2``
+ seconds.
+ phase : None or float
+ Clock phase. The process will wait ``phase`` seconds before the first clock transition.
+ If not specified, defaults to ``period / 2``.
+ domain : str or ClockDomain
+ Driven clock domain. If specified as a string, the domain with that name is looked up
+ in the root fragment of the simulation.
+ if_exists : bool
+ If ``False`` (the default), raise an error if the driven domain is specified as
+ a string and the root fragment does not have such a domain. If ``True``, do nothing
+ in this case.
+ """
+ if isinstance(domain, ClockDomain):
+ pass
+ elif domain in self._fragment.domains:
+ domain = self._fragment.domains[domain]
+ elif if_exists:
+ return
+ else:
+ raise ValueError("Domain {!r} is not present in simulation"
+ .format(domain))
+ if domain in self._clocked:
+ raise ValueError("Domain {!r} already has a clock driving it"
+ .format(domain.name))
+
+ 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 = period / 2
+ self._engine.add_clock_process(domain.clk, phase=phase, period=period)
+ self._clocked.add(domain)
+
+ def reset(self):
+ """Reset the simulation.
+
+ Assign the reset value to every signal in the simulation, and restart every user process.
+ """
+ self._engine.reset()
+
+ # TODO(nmigen-0.4): replace with _real_step
+ @deprecated("instead of `sim.step()`, use `sim.advance()`")
+ def step(self):
+ return self.advance()
+
+ def advance(self):
+ """Advance the simulation.
+
+ Run every process and commit changes until a fixed point is reached, then advance time
+ to the closest deadline (if any). If there is an unstable combinatorial loop,
+ this function will never return.
+
+ Returns ``True`` if there are any active processes, ``False`` otherwise.
+ """
+ return self._engine.advance()
+
+ def run(self):
+ """Run the simulation while any processes are active.
+
+ Processes added with :meth:`add_process` and :meth:`add_sync_process` are initially active,
+ and may change their status using the ``yield Passive()`` and ``yield Active()`` commands.
+ Processes compiled from HDL and added with :meth:`add_clock` are always passive.
+ """
+ while self.advance():
+ pass
+
+ def run_until(self, deadline, *, run_passive=False):
+ """Run the simulation until it advances to ``deadline``.
+
+ If ``run_passive`` is ``False``, the simulation also stops when there are no active
+ processes, similar to :meth:`run`. Otherwise, the simulation will stop only after it
+ advances to or past ``deadline``.
+
+ If the simulation stops advancing, this function will never return.
+ """
+ assert self._engine.now <= deadline
+ while (self.advance() or run_passive) and self._engine.now < deadline:
+ pass
+
+ def write_vcd(self, vcd_file, gtkw_file=None, *, traces=()):
+ """Write waveforms to a Value Change Dump file, optionally populating a GTKWave save file.
+
+ This method returns a context manager. It can be used as: ::
+
+ sim = Simulator(frag)
+ sim.add_clock(1e-6)
+ with sim.write_vcd("dump.vcd", "dump.gtkw"):
+ sim.run_until(1e-3)
+
+ Arguments
+ ---------
+ vcd_file : str or file-like object
+ Verilog Value Change Dump file or filename.
+ gtkw_file : str or file-like object
+ GTKWave save file or filename.
+ traces : iterable of Signal
+ Signals to display traces for.
+ """
+ if self._engine.now != 0.0:
+ for file in (vcd_file, gtkw_file):
+ if hasattr(file, "close"):
+ file.close()
+ raise ValueError("Cannot start writing waveforms after advancing simulation time")
+
+ return self._engine.write_vcd(vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces)
from contextlib import contextmanager
import itertools
-import inspect
from vcd import VCDWriter
from vcd.gtkw import GTKWSave
-from .._utils import deprecated
from ..hdl import *
from ..hdl.ast import SignalDict
-from ._cmds import *
-from ._core import *
+from ._base import *
from ._pyrtl import _FragmentCompiler
from ._pycoro import PyCoroProcess
from ._pyclock import PyClockProcess
-__all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"]
+__all__ = ["PySimEngine"]
class _NameExtractor:
return self.names
-class _WaveformWriter:
- def update(self, timestamp, signal, value):
- raise NotImplementedError # :nocov:
-
- def close(self, timestamp):
- raise NotImplementedError # :nocov:
-
-
-class _VCDWaveformWriter(_WaveformWriter):
+class _VCDWriter:
@staticmethod
def timestamp_to_vcd(timestamp):
return timestamp * (10 ** 10) # 1/(100 ps)
self.gtkw_file.close()
-class _SignalState:
+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 _PySignalState(BaseSignalState):
__slots__ = ("signal", "curr", "next", "waiters", "pending")
def __init__(self, signal, pending):
return awoken_any
-class _SimulatorState:
+class _PySimulation(BaseSimulation):
def __init__(self):
- self.timeline = Timeline()
+ self.timeline = _Timeline()
self.signals = SignalDict()
self.slots = []
self.pending = set()
return self.signals[signal]
except KeyError:
index = len(self.slots)
- self.slots.append(_SignalState(signal, self.pending))
+ self.slots.append(_PySignalState(signal, self.pending))
self.signals[signal] = index
return index
assert process in self.slots[index].waiters
del self.slots[index].waiters[process]
+ def wait_interval(self, process, interval):
+ self.timeline.delay(interval, process)
+
def commit(self):
converged = True
for signal_state in self.pending:
return converged
-class Simulator:
+class PySimEngine(BaseEngine):
def __init__(self, fragment):
- self._state = _SimulatorState()
- self._fragment = Fragment.get(fragment, platform=None).prepare()
- self._processes = _FragmentCompiler(self._state)(self._fragment)
- self._clocked = set()
- self._waveform_writers = []
+ self._state = _PySimulation()
+ self._timeline = self._state.timeline
- def _check_process(self, process):
- if not (inspect.isgeneratorfunction(process) or inspect.iscoroutinefunction(process)):
- raise TypeError("Cannot add a process {!r} because it is not a generator function"
- .format(process))
- return process
+ self._fragment = fragment
+ self._processes = _FragmentCompiler(self._state)(self._fragment)
+ self._vcd_writers = []
- def _add_coroutine_process(self, process, *, default_cmd):
+ def add_coroutine_process(self, process, *, default_cmd):
self._processes.add(PyCoroProcess(self._state, self._fragment.domains, process,
default_cmd=default_cmd))
- def add_process(self, process):
- process = self._check_process(process)
- def wrapper():
- # Only start a bench process after comb settling, so that the reset values are correct.
- yield Settle()
- yield from process()
- self._add_coroutine_process(wrapper, default_cmd=None)
-
- def add_sync_process(self, process, *, domain="sync"):
- process = self._check_process(process)
- def wrapper():
- # Only start a sync process after the first clock edge (or reset edge, if the domain
- # uses an asynchronous reset). This matches the behavior of synchronous FFs.
- yield Tick(domain)
- yield from process()
- return self._add_coroutine_process(wrapper, default_cmd=Tick(domain))
-
- def add_clock(self, period, *, phase=None, domain="sync", if_exists=False):
- """Add a clock process.
-
- Adds a process that drives the clock signal of ``domain`` at a 50% duty cycle.
-
- Arguments
- ---------
- period : float
- Clock period. The process will toggle the ``domain`` clock signal every ``period / 2``
- seconds.
- phase : None or float
- Clock phase. The process will wait ``phase`` seconds before the first clock transition.
- If not specified, defaults to ``period / 2``.
- domain : str or ClockDomain
- Driven clock domain. If specified as a string, the domain with that name is looked up
- in the root fragment of the simulation.
- if_exists : bool
- If ``False`` (the default), raise an error if the driven domain is specified as
- a string and the root fragment does not have such a domain. If ``True``, do nothing
- in this case.
- """
- if isinstance(domain, ClockDomain):
- pass
- elif domain in self._fragment.domains:
- domain = self._fragment.domains[domain]
- elif if_exists:
- return
- else:
- raise ValueError("Domain {!r} is not present in simulation"
- .format(domain))
- if domain in self._clocked:
- raise ValueError("Domain {!r} already has a clock driving it"
- .format(domain.name))
-
- 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 = period / 2
- self._processes.add(PyClockProcess(self._state, domain.clk, phase=phase, period=period))
- self._clocked.add(domain)
+ def add_clock_process(self, clock, *, phase, period):
+ self._processes.add(PyClockProcess(self._state, clock,
+ phase=phase, period=period))
def reset(self):
- """Reset the simulation.
-
- Assign the reset value to every signal in the simulation, and restart every user process.
- """
self._state.reset()
for process in self._processes:
process.reset()
- def _real_step(self):
- """Step the simulation.
-
- Run every process and commit changes until a fixed point is reached. If there is
- an unstable combinatorial loop, this function will never return.
- """
+ def _step(self):
# Performs the two phases of a delta cycle in a loop:
converged = False
while not converged:
process.runnable = False
process.run()
- for waveform_writer in self._waveform_writers:
+ for vcd_writer in self._vcd_writers:
for signal_state in self._state.pending:
- waveform_writer.update(self._state.timeline.now,
+ vcd_writer.update(self._timeline.now,
signal_state.signal, signal_state.next)
# 2. commit: apply every queued signal change, waking up any waiting processes
converged = self._state.commit()
- # TODO(nmigen-0.4): replace with _real_step
- @deprecated("instead of `sim.step()`, use `sim.advance()`")
- def step(self):
- return self.advance()
-
def advance(self):
- """Advance the simulation.
-
- Run every process and commit changes until a fixed point is reached, then advance time
- to the closest deadline (if any). If there is an unstable combinatorial loop,
- this function will never return.
-
- Returns ``True`` if there are any active processes, ``False`` otherwise.
- """
- self._real_step()
- self._state.timeline.advance()
+ self._step()
+ self._timeline.advance()
return any(not process.passive for process in self._processes)
- def run(self):
- """Run the simulation while any processes are active.
-
- Processes added with :meth:`add_process` and :meth:`add_sync_process` are initially active,
- and may change their status using the ``yield Passive()`` and ``yield Active()`` commands.
- Processes compiled from HDL and added with :meth:`add_clock` are always passive.
- """
- while self.advance():
- pass
-
- def run_until(self, deadline, *, run_passive=False):
- """Run the simulation until it advances to ``deadline``.
-
- If ``run_passive`` is ``False``, the simulation also stops when there are no active
- processes, similar to :meth:`run`. Otherwise, the simulation will stop only after it
- advances to or past ``deadline``.
-
- If the simulation stops advancing, this function will never return.
- """
- assert self._state.timeline.now <= deadline
- while (self.advance() or run_passive) and self._state.timeline.now < deadline:
- pass
+ @property
+ def now(self):
+ return self._timeline.now
@contextmanager
- def write_vcd(self, vcd_file, gtkw_file=None, *, traces=()):
- """Write waveforms to a Value Change Dump file, optionally populating a GTKWave save file.
-
- This method returns a context manager. It can be used as: ::
-
- sim = Simulator(frag)
- sim.add_clock(1e-6)
- with sim.write_vcd("dump.vcd", "dump.gtkw"):
- sim.run_until(1e-3)
-
- Arguments
- ---------
- vcd_file : str or file-like object
- Verilog Value Change Dump file or filename.
- gtkw_file : str or file-like object
- GTKWave save file or filename.
- traces : iterable of Signal
- Signals to display traces for.
- """
- if self._state.timeline.now != 0.0:
- for file in (vcd_file, gtkw_file):
- if hasattr(file, "close"):
- file.close()
- raise ValueError("Cannot start writing waveforms after advancing simulation time")
-
- waveform_writer = _VCDWaveformWriter(self._fragment,
+ def write_vcd(self, *, vcd_file, gtkw_file, traces):
+ vcd_writer = _VCDWriter(self._fragment,
vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces)
try:
- self._waveform_writers.append(waveform_writer)
+ self._vcd_writers.append(vcd_writer)
yield
finally:
- waveform_writer.close(self._state.timeline.now)
- self._waveform_writers.remove(waveform_writer)
+ vcd_writer.close(self._timeline.now)
+ self._vcd_writers.remove(vcd_writer)
# TODO(nmigen-0.4): remove
-warnings.warn("instead of vendor.lattice_machxo2, use vendor.lattice_machxo_2_3l",
+warnings.warn("instead of nmigen.vendor.lattice_machxo2, use nmigen.vendor.lattice_machxo_2_3l",
DeprecationWarning, stacklevel=2)
# nmigen: UnusedElaboratable=no
from nmigen.hdl import *
-from nmigen.back.pysim import *
+from nmigen.sim import *
from nmigen.lib.cdc import *
from .utils import *
from nmigen.hdl import *
from nmigen.asserts import *
-from nmigen.back.pysim import *
+from nmigen.sim import *
from nmigen.lib.coding import *
from .utils import *
from nmigen.hdl import *
from nmigen.asserts import *
-from nmigen.back.pysim import *
+from nmigen.sim import *
from nmigen.lib.fifo import *
from .utils import *
from nmigen.hdl import *
from nmigen.hdl.rec import *
-from nmigen.back.pysim import *
+from nmigen.sim import *
from nmigen.lib.io import *
from .utils import *
from nmigen.hdl import *
from nmigen.asserts import *
-from nmigen.sim.pysim import *
+from nmigen.sim import *
from nmigen.lib.scheduler import *
from .utils import *
from nmigen.hdl.rec import *
from nmigen.hdl.dsl import *
from nmigen.hdl.ir import *
-from nmigen.back.pysim import *
+from nmigen.sim import *
from .utils import *