back.pysim: only extract signal names if VCD is requested.
authorwhitequark <whitequark@whitequark.org>
Wed, 8 Jul 2020 08:29:20 +0000 (08:29 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 31 Dec 2021 14:48:38 +0000 (14:48 +0000)
This commit also fixes an issue introduced in 2606ee33 that regressed
simulator startup time and bloated VCD files. (It's actually about
10% faster now than *before* the regression was introduced.)

nmigen/back/pysim.py

index 155a649a43893dbfc0ad0d0084455e6c2e771ea1..7983b94a476300f7e05ea82eea9bb3a25bdf9a82 100644 (file)
@@ -18,6 +18,38 @@ from ..sim._cmds import *
 __all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"]
 
 
+class _NameExtractor:
+    def __init__(self):
+        self.names = SignalDict()
+
+    def __call__(self, fragment, *, hierarchy=("top",)):
+        def add_signal_name(signal):
+            hierarchical_signal_name = (*hierarchy, signal.name)
+            if signal not in self.names:
+                self.names[signal] = {hierarchical_signal_name}
+            else:
+                self.names[signal].add(hierarchical_signal_name)
+
+        for domain_name, domain_signals in fragment.drivers.items():
+            if domain_name is not None:
+                domain = fragment.domains[domain_name]
+                add_signal_name(domain.clk)
+                if domain.rst is not None:
+                    add_signal_name(domain.rst)
+
+        for statement in fragment.statements:
+            for signal in statement._lhs_signals() | statement._rhs_signals():
+                if not isinstance(signal, (ClockSignal, ResetSignal)):
+                    add_signal_name(signal)
+
+        for subfragment_index, (subfragment, subfragment_name) in enumerate(fragment.subfragments):
+            if subfragment_name is None:
+                subfragment_name = "U${}".format(subfragment_index)
+            self(subfragment, hierarchy=(*hierarchy, subfragment_name))
+
+        return self.names
+
+
 class _WaveformWriter:
     def update(self, timestamp, signal, value):
         raise NotImplementedError # :nocov:
@@ -35,7 +67,7 @@ class _VCDWaveformWriter(_WaveformWriter):
     def decode_to_vcd(signal, value):
         return signal.decoder(value).expandtabs().replace(" ", "_")
 
-    def __init__(self, signal_names, *, vcd_file, gtkw_file=None, traces=()):
+    def __init__(self, fragment, *, vcd_file, gtkw_file=None, traces=()):
         if isinstance(vcd_file, str):
             vcd_file = open(vcd_file, "wt")
         if isinstance(gtkw_file, str):
@@ -52,6 +84,8 @@ class _VCDWaveformWriter(_WaveformWriter):
 
         self.traces = []
 
+        signal_names = _NameExtractor()(fragment)
+
         trace_names = SignalDict()
         for trace in traces:
             if trace not in signal_names:
@@ -630,20 +664,12 @@ class _CompiledProcess(_Process):
 
 
 class _FragmentCompiler:
-    def __init__(self, state, signal_names):
+    def __init__(self, state):
         self.state = state
-        self.signal_names = signal_names
 
     def __call__(self, fragment, *, hierarchy=("top",)):
         processes = set()
 
-        def add_signal_name(signal):
-            hierarchical_signal_name = (*hierarchy, signal.name)
-            if signal not in self.signal_names:
-                self.signal_names[signal] = {hierarchical_signal_name}
-            else:
-                self.signal_names[signal].add(hierarchical_signal_name)
-
         for domain_name, domain_signals in fragment.drivers.items():
             domain_stmts = LHSGroupFilter(domain_signals)(fragment.statements)
             domain_process = _CompiledProcess(self.state, comb=domain_name is None)
@@ -665,10 +691,6 @@ class _FragmentCompiler:
 
             else:
                 domain = fragment.domains[domain_name]
-                add_signal_name(domain.clk)
-                if domain.rst is not None:
-                    add_signal_name(domain.rst)
-
                 clk_trigger = 1 if domain.clk_edge == "pos" else 0
                 self.state.add_trigger(domain_process, domain.clk, trigger=clk_trigger)
                 if domain.rst is not None and domain.async_reset:
@@ -710,9 +732,6 @@ class _FragmentCompiler:
 
             processes.add(domain_process)
 
-            for used_signal in domain_process.state.signals:
-                add_signal_name(used_signal)
-
         for subfragment_index, (subfragment, subfragment_name) in enumerate(fragment.subfragments):
             if subfragment_name is None:
                 subfragment_name = "U${}".format(subfragment_index)
@@ -830,7 +849,7 @@ class Simulator:
         self._state = _SimulatorState()
         self._signal_names = SignalDict()
         self._fragment = Fragment.get(fragment, platform=None).prepare()
-        self._processes = _FragmentCompiler(self._state, self._signal_names)(self._fragment)
+        self._processes = _FragmentCompiler(self._state)(self._fragment)
         self._clocked = set()
         self._waveform_writers = []
 
@@ -1013,7 +1032,7 @@ class Simulator:
         """
         if self._state.timeline.now != 0.0:
             raise ValueError("Cannot start writing waveforms after advancing simulation time")
-        waveform_writer = _VCDWaveformWriter(self._signal_names,
+        waveform_writer = _VCDWaveformWriter(self._fragment,
             vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces)
         self._waveform_writers.append(waveform_writer)
         yield