1
1
from contextlib import contextmanager
2
2
import itertools
3
3
import re
4
+ import os .path
4
5
5
6
from ..hdl import *
6
7
from ..hdl ._repr import *
@@ -36,7 +37,7 @@ def eval_field(field, signal, value):
36
37
else :
37
38
raise NotImplementedError # :nocov:
38
39
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 = () ):
40
41
self .fs_per_delta = fs_per_delta
41
42
42
43
# 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=
165
166
gtkw_names .append (gtkw_name )
166
167
167
168
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
+
168
189
def update_signal (self , timestamp , signal , value ):
169
190
for (vcd_var , repr ) in self .vcd_signal_vars .get (signal , ()):
170
191
var_value = self .eval_field (repr .value , signal , value )
@@ -176,6 +197,16 @@ def update_memory(self, timestamp, memory, addr, value):
176
197
vcd_var = self .vcd_memory_vars [memory ._identity ][addr ]
177
198
self .vcd_writer .change (vcd_var , timestamp , value )
178
199
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
+
179
210
def close (self , timestamp ):
180
211
if self .vcd_writer is not None :
181
212
self .vcd_writer .close (timestamp )
@@ -425,7 +456,7 @@ def add_coroutine_process(self, process, *, default_cmd):
425
456
426
457
def add_testbench_process (self , process ):
427
458
self ._testbenches .append (PyCoroProcess (self ._state , self ._design .fragment .domains , process ,
428
- testbench = True ))
459
+ testbench = True , on_command = self . _debug_process ))
429
460
430
461
def reset (self ):
431
462
self ._state .reset ()
@@ -462,6 +493,13 @@ def _step_rtl(self):
462
493
463
494
self ._delta_cycles += 1
464
495
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
+
465
503
def _step_tb (self ):
466
504
# Run processes waiting for an interval to expire (mainly `add_clock_process()``)
467
505
self ._step_rtl ()
@@ -494,7 +532,8 @@ def _now_plus_deltas(self, vcd_writer):
494
532
@contextmanager
495
533
def write_vcd (self , * , vcd_file , gtkw_file , traces , fs_per_delta ):
496
534
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 )
498
537
try :
499
538
self ._vcd_writers .append (vcd_writer )
500
539
yield
0 commit comments