Skip to content

Commit 11f7b88

Browse files
committed
sim: write process commands to VCD file.
If delta cycles are expanded (i.e. if the `fs_per_delta` argument to `Simulator.write_vcd` is not zero), then create a string typed variable for each testbench in the simulation, which reflects the current command being executed by that testbench. To make all commands visible, insert a (visual) delta cycle after each executed command, and ensure that there is a change/crossing point in the waveform display each time a command is executed, even if several identical ones in a row. If delta cycles are not expanded, the behavior is unchanged.
1 parent 36fb903 commit 11f7b88

File tree

3 files changed

+57
-4
lines changed

3 files changed

+57
-4
lines changed

amaranth/sim/_pycoro.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212

1313

1414
class PyCoroProcess(BaseProcess):
15-
def __init__(self, state, domains, constructor, *, default_cmd=None, testbench=False):
15+
def __init__(self, state, domains, constructor, *, default_cmd=None, testbench=False,
16+
on_command=None):
1617
self.state = state
1718
self.domains = domains
1819
self.constructor = constructor
1920
self.default_cmd = default_cmd
2021
self.testbench = testbench
22+
self.on_command = on_command
2123

2224
self.reset()
2325

@@ -79,6 +81,9 @@ def run(self):
7981
response = None
8082
exception = None
8183

84+
if self.on_command is not None:
85+
self.on_command(self, command)
86+
8287
if isinstance(command, ValueCastable):
8388
command = Value.cast(command)
8489
if isinstance(command, Value):

amaranth/sim/pysim.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from contextlib import contextmanager
22
import itertools
33
import re
4+
import os.path
45

56
from ..hdl import *
67
from ..hdl._repr import *
@@ -36,7 +37,7 @@ def eval_field(field, signal, value):
3637
else:
3738
raise NotImplementedError # :nocov:
3839

39-
def __init__(self, design, *, vcd_file, gtkw_file=None, traces=(), fs_per_delta=0):
40+
def __init__(self, design, *, vcd_file, gtkw_file=None, traces=(), fs_per_delta=0, processes=()):
4041
self.fs_per_delta = fs_per_delta
4142

4243
# Although pyvcd is a mandatory dependency, be resilient and import it as needed, so that
@@ -165,6 +166,26 @@ def __init__(self, design, *, vcd_file, gtkw_file=None, traces=(), fs_per_delta=
165166
gtkw_names.append(gtkw_name)
166167

167168

169+
self.vcd_process_vars = {}
170+
if fs_per_delta == 0:
171+
return # Not useful without delta cycle expansion.
172+
for index, process in enumerate(processes):
173+
func_name = process.constructor.__name__
174+
func_file = os.path.basename(process.constructor.__code__.co_filename)
175+
func_line = process.constructor.__code__.co_firstlineno
176+
for name in (
177+
f"{process.constructor.__name__}",
178+
f"{process.constructor.__name__}!{func_file};{func_line}",
179+
f"{process.constructor.__name__}#{index}",
180+
):
181+
try:
182+
self.vcd_process_vars[process] = self.vcd_writer.register_var(
183+
scope=("debug", "proc"), name=name, var_type="string", size=None,
184+
init="(init)")
185+
break
186+
except KeyError:
187+
pass # try another name
188+
168189
def update_signal(self, timestamp, signal, value):
169190
for (vcd_var, repr) in self.vcd_signal_vars.get(signal, ()):
170191
var_value = self.eval_field(repr.value, signal, value)
@@ -176,6 +197,16 @@ def update_memory(self, timestamp, memory, addr, value):
176197
vcd_var = self.vcd_memory_vars[memory._identity][addr]
177198
self.vcd_writer.change(vcd_var, timestamp, value)
178199

200+
def update_process(self, timestamp, process, command):
201+
try:
202+
vcd_var = self.vcd_process_vars[process]
203+
except KeyError:
204+
return
205+
# Ensure that the waveform viewer displays a change point even if the previous command is
206+
# the same as the next one.
207+
self.vcd_writer.change(vcd_var, timestamp, "")
208+
self.vcd_writer.change(vcd_var, timestamp, repr(command))
209+
179210
def close(self, timestamp):
180211
if self.vcd_writer is not None:
181212
self.vcd_writer.close(timestamp)
@@ -425,7 +456,7 @@ def add_coroutine_process(self, process, *, default_cmd):
425456

426457
def add_testbench_process(self, process):
427458
self._testbenches.append(PyCoroProcess(self._state, self._design.fragment.domains, process,
428-
testbench=True))
459+
testbench=True, on_command=self._debug_process))
429460

430461
def reset(self):
431462
self._state.reset()
@@ -462,6 +493,13 @@ def _step_rtl(self):
462493

463494
self._delta_cycles += 1
464495

496+
def _debug_process(self, process, command):
497+
for vcd_writer in self._vcd_writers:
498+
now_plus_deltas = self._now_plus_deltas(vcd_writer)
499+
vcd_writer.update_process(now_plus_deltas, process, command)
500+
501+
self._delta_cycles += 1
502+
465503
def _step_tb(self):
466504
# Run processes waiting for an interval to expire (mainly `add_clock_process()``)
467505
self._step_rtl()
@@ -494,7 +532,8 @@ def _now_plus_deltas(self, vcd_writer):
494532
@contextmanager
495533
def write_vcd(self, *, vcd_file, gtkw_file, traces, fs_per_delta):
496534
vcd_writer = _VCDWriter(self._design,
497-
vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces, fs_per_delta=fs_per_delta)
535+
vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces, fs_per_delta=fs_per_delta,
536+
processes=self._testbenches)
498537
try:
499538
self._vcd_writers.append(vcd_writer)
500539
yield

tests/test_sim.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,15 @@ def testbench_2():
11911191
with sim.write_vcd("test.vcd", fs_per_delta=1):
11921192
sim.run()
11931193

1194+
def test_process_name_collision(self):
1195+
def testbench():
1196+
yield Passive()
1197+
sim = Simulator(Module())
1198+
sim.add_testbench(testbench)
1199+
sim.add_testbench(testbench)
1200+
with sim.write_vcd("test.vcd", fs_per_delta=1):
1201+
sim.run()
1202+
11941203

11951204
class SimulatorRegressionTestCase(FHDLTestCase):
11961205
def test_bug_325(self):

0 commit comments

Comments
 (0)