back.pysim: only extract signal names if VCD is requested.

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.)
This commit is contained in:
whitequark 2020-07-08 08:29:20 +00:00
parent 3c3cfd48fb
commit 6d417568ad

View file

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