Skip to content

Commit 9a923aa

Browse files
committed
Add ability to specify how values render in trace; improve symbol_len
calculation.
1 parent 6a86dc4 commit 9a923aa

File tree

3 files changed

+84
-13
lines changed

3 files changed

+84
-13
lines changed

pyrtl/simulation.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -876,21 +876,23 @@ def tick_segment(self, n, symbol_len, segment_size):
876876
num_tick = self._tick + str(n)
877877
return num_tick.ljust(symbol_len * segment_size)
878878

879-
def render_val(self, w, n, current_val, symbol_len):
879+
def render_val(self, w, n, current_val, symbol_len, repr_func):
880880
if w is not self.prev_wire:
881881
self.prev_wire = w
882882
self.prior_val = current_val
883-
out = self._render_val_with_prev(w, n, current_val, symbol_len)
883+
out = self._render_val_with_prev(w, n, current_val, symbol_len, repr_func)
884884
self.prior_val = current_val
885885
return out
886886

887-
def _render_val_with_prev(self, w, n, current_val, symbol_len):
887+
def _render_val_with_prev(self, w, n, current_val, symbol_len, repr_func):
888888
"""Return a string encoding the given value in a waveform.
889889
890890
:param w: The WireVector we are rendering to a waveform
891891
:param n: An integer from 0 to segment_len-1
892892
:param current_val: the value to be rendered
893893
:param symbol_len: and integer for how big to draw the current value
894+
:param repr_func: function to use for representing the current_val;
895+
examples are 'hex', 'oct', 'bin', and 'str' (for decimal). Defaults to 'hex'.
894896
895897
Returns a string of printed length symbol_len that will draw the
896898
representation of current_val. The input prior_val is used to
@@ -900,9 +902,9 @@ def _render_val_with_prev(self, w, n, current_val, symbol_len):
900902
if len(w) > 1:
901903
out = self._revstart
902904
if current_val != self.prior_val:
903-
out += self._x + hex(current_val).rstrip('L').ljust(sl)[:sl]
905+
out += self._x + repr_func(current_val).rstrip('L').ljust(sl)[:sl]
904906
elif n == 0:
905-
out += hex(current_val).rstrip('L').ljust(symbol_len)[:symbol_len]
907+
out += repr_func(current_val).rstrip('L').ljust(symbol_len)[:symbol_len]
906908
else:
907909
out += ' ' * symbol_len
908910
out += self._revstop
@@ -1130,14 +1132,18 @@ def print_trace_strs(time):
11301132

11311133
def render_trace(
11321134
self, trace_list=None, file=sys.stdout, render_cls=default_renderer(),
1133-
symbol_len=5, segment_size=5, segment_delim=' ', extra_line=True):
1135+
symbol_len=5, repr_func=hex, segment_size=5, segment_delim=' ', extra_line=True):
11341136

11351137
""" Render the trace to a file using unicode and ASCII escape sequences.
11361138
11371139
:param trace_list: A list of signal names to be output in the specified order.
11381140
:param file: The place to write output, default to stdout.
11391141
:param render_cls: A class that translates traces into output bytes.
11401142
:param symbol_len: The "length" of each rendered cycle in characters.
1143+
If None, the length will be automatically set such that the largest
1144+
represented value fits.
1145+
:param repr_func: Function to use for representing each value in the trace;
1146+
examples are 'hex', 'oct', 'bin', and 'str' (for decimal). Defaults to 'hex'.
11411147
:param segment_size: Traces are broken in the segments of this number of cycles.
11421148
:param segment_delim: The character to be output between segments.
11431149
:param extra_line: A Boolean to determine if we should print a blank line between signals.
@@ -1166,12 +1172,12 @@ def render_trace(
11661172
else:
11671173
self.render_trace_to_text(
11681174
trace_list=trace_list, file=file, render_cls=render_cls,
1169-
symbol_len=symbol_len, segment_size=segment_size,
1175+
symbol_len=symbol_len, repr_func=repr_func, segment_size=segment_size,
11701176
segment_delim=segment_delim, extra_line=extra_line)
11711177

11721178
def render_trace_to_text(
11731179
self, trace_list, file, render_cls,
1174-
symbol_len, segment_size, segment_delim, extra_line):
1180+
symbol_len, repr_func, segment_size, segment_delim, extra_line):
11751181

11761182
renderer = render_cls()
11771183

@@ -1185,7 +1191,8 @@ def formatted_trace_line(wire, trace):
11851191
self._wires[wire],
11861192
i % segment_size,
11871193
trace[i],
1188-
symbol_len)
1194+
symbol_len,
1195+
repr_func)
11891196
return heading + trace_line
11901197

11911198
# default to printing all signals in sorted order
@@ -1204,6 +1211,12 @@ def formatted_trace_line(wire, trace):
12041211
"untraceable wires were removed prior to simulation, "
12051212
"if a CompiledSimulation was used.")
12061213

1214+
if symbol_len is None:
1215+
maxvallen = 0
1216+
for trace in self.trace.values():
1217+
maxvallen = max(maxvallen, max(len(repr_func(v)) for v in trace))
1218+
symbol_len = maxvallen + 1
1219+
12071220
# print the 'ruler' which is just a list of 'ticks'
12081221
# mapped by the pretty map
12091222

pyrtl/visualization.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ def block_to_svg(block=None, split_state=True, maintain_arg_order=False):
459459
# |__| | |\/| |
460460
# | | | | | |___
461461

462-
def trace_to_html(simtrace, trace_list=None, sortkey=None):
462+
def trace_to_html(simtrace, trace_list=None, sortkey=None, repr_func=hex):
463463
""" Return a HTML block showing the trace.
464464
465465
:param simtrace: A SimulationTrace object
@@ -484,12 +484,15 @@ def trace_to_html(simtrace, trace_list=None, sortkey=None):
484484
<script type="WaveDrom">
485485
{ signal : [
486486
%s
487-
]}
487+
],
488+
config: {hscale: %d }}
488489
</script>
489490
490491
"""
491492
)
492493

494+
vallens = [] # For determining longest value length
495+
493496
def extract(w):
494497
wavelist = []
495498
datalist = []
@@ -506,7 +509,8 @@ def extract(w):
506509
last = value
507510

508511
wavestring = ''.join(wavelist)
509-
datastring = ', '.join(['"%d"' % data for data in datalist])
512+
datastring = ', '.join(['"%s"' % repr_func(data) for data in datalist])
513+
vallens.extend([len(repr_func(data)) for data in datalist])
510514
if len(w) == 1:
511515
return bool_signal_template % (w, wavestring)
512516
else:
@@ -516,6 +520,8 @@ def extract(w):
516520
int_signal_template = '{ name: "%s", wave: "%s", data: [%s] },'
517521
signals = [extract(w) for w in trace_list]
518522
all_signals = '\n'.join(signals)
519-
wave = wave_template % all_signals
523+
maxvallen = max(vallens)
524+
scale = (maxvallen // 5) + 1
525+
wave = wave_template % (all_signals, scale)
520526
# print(wave)
521527
return wave

tests/test_simulation.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import unittest
22
import six
3+
import io
34

45
import pyrtl
56
from pyrtl.corecircuits import _basic_add
@@ -128,6 +129,57 @@ def test_reg_to_reg_simulation(self):
128129
self.check_trace(' r 00224466\nr2 02244660\n')
129130

130131

132+
class RenderTraceBase(unittest.TestCase):
133+
def setUp(self):
134+
pyrtl.reset_working_block()
135+
a, b = pyrtl.input_list('a/8 b/8')
136+
o = pyrtl.Output()
137+
o <<= a + b
138+
139+
def check_rendered_trace(self, expected, **kwargs):
140+
sim = pyrtl.Simulation()
141+
sim.step_multiple({
142+
'a': [1, 4, 9, 11, 12],
143+
'b': [2, 23, 43, 120, 0],
144+
})
145+
buff = io.StringIO()
146+
sim.tracer.render_trace(file=buff, render_cls=pyrtl.simulation.AsciiWaveRenderer,
147+
extra_line=False, **kwargs)
148+
self.assertEqual(buff.getvalue(), expected)
149+
150+
def test_hex_trace(self):
151+
expected = (
152+
" -0 \n"
153+
"a 0x1 x0x4 x0x9 x0xb x0xc \n"
154+
"b 0x2 x0x17 x0x2b x0x78 x0x0 \n"
155+
)
156+
self.check_rendered_trace(expected)
157+
158+
def test_hex_trace(self):
159+
expected = (
160+
" -0 \n"
161+
"a 0o1 x0o4 x0o11 x0o13 x0o14 \n"
162+
"b 0o2 x0o27 x0o53 x0o170 x0o0 \n"
163+
)
164+
self.check_rendered_trace(expected, repr_func=oct, symbol_len=None)
165+
166+
def test_bin_trace(self):
167+
expected = (
168+
" -0 \n"
169+
"a 0b1 x0b100 x0b1001 x0b1011 x0b1100 \n"
170+
"b 0b10 x0b10111 x0b101011 x0b1111000 x0b0 \n"
171+
)
172+
self.check_rendered_trace(expected, repr_func=bin, symbol_len=None)
173+
174+
def test_decimal_trace(self):
175+
expected = (
176+
" -0 \n"
177+
"a 1 x4 x9 x11 x12 \n"
178+
"b 2 x23 x43 x120 x0 \n"
179+
)
180+
self.check_rendered_trace(expected, repr_func=str, symbol_len=None)
181+
182+
131183
class PrintTraceBase(unittest.TestCase):
132184
# note: doesn't include tests for compact=True because all the tests test that
133185
def setUp(self):

0 commit comments

Comments
 (0)