22 """Write Value Change Dump files.
24 This module provides :class:`VCDWriter` for writing VCD files.
27 from __future__
import print_function
28 from collections
import OrderedDict, Sequence
29 from numbers
import Number
33 from six.moves
import zip, zip_longest
37 """Indicating a :class:`VCDWriter` method was called in the wrong phase.
39 For example, calling :meth:`register_var()` after :meth:`close()` will
46 """Value Change Dump writer.
48 A VCD file captures time-ordered changes to the value of variables.
50 :param file file: A file-like object to write the VCD data.
52 Scale of the VCD timestamps. The timescale may either be a string or a
53 tuple containing an (int, str) pair.
54 :type timescale: str, tuple
55 :param str date: Optional `$date` string used in the VCD header.
56 :param str comment: Optional `$comment` string used in the VCD header.
57 :param str version: Optional `$version` string used in the VCD header.
58 :param str default_scope_type: Scope type for scopes where
59 :meth:`set_scope_type()` is not called explicitly.
60 :param str scope_sep: Separator for scopes specified as strings.
61 :param int init_timestamp: The initial timestamp. default=0
62 :raises ValueError: for invalid timescale values
67 SCOPE_TYPES = [
'begin',
'fork',
'function',
'module',
'task']
70 VAR_TYPES = [
'event',
'integer',
'parameter',
'real',
'realtime',
'reg',
71 'supply0',
'supply1',
'time',
'tri',
'triand',
'trior',
72 'trireg',
'tri0',
'tri1',
'wand',
'wire',
'wor']
75 TIMESCALE_NUMS = [1, 10, 100]
78 TIMESCALE_UNITS = [
's',
'ms',
'us',
'ns',
'ps',
'fs']
80 def __init__(self, file, timescale='1 us', date=None, comment='',
81 version='', default_scope_type='module', scope_sep='.',
82 check_values=True, init_timestamp=0):
83 """Initialization of VCDWriter"""
89 '$date': str(datetime.datetime.now())
if date
is None else date,
93 if default_scope_type
not in self.
SCOPE_TYPESSCOPE_TYPES:
94 raise ValueError(
'Invalid default scope type ({})'.format(
122 """Set the scope_type for a given scope.
124 The scope's type may be set to one of the valid :const:`SCOPE_TYPES`.
125 VCD viewer applications may display different scope types differently.
127 :param scope: The scope to set the type of.
128 :type scope: str or sequence of str
129 :param str scope_type: A valid scope type string.
130 :raises ValueError: for invalid `scope_type`
133 if scope_type
is not None and scope_type
not in self.
SCOPE_TYPESSCOPE_TYPES:
134 raise ValueError(
'Invalid scope_type "{}"'.format(scope_type))
140 """Register a VCD variable and return function to change value.
142 All VCD variables must be registered prior to any value changes.
146 The variable `name` differs from the variable's `ident`
147 (identifier). The `name` (also known as `ref`) is meant to refer to
148 the variable name in the code being traced and is visible in VCD
149 viewer applications. The `ident`, however, is only used within the
150 VCD file and can be auto-generated (by specifying ``ident=None``)
151 for most applications.
153 :param scope: The hierarchical scope that the variable belongs within.
154 :type scope: str or sequence of str
155 :param str name: Name of the variable.
156 :param str var_type: One of :const:`VAR_TYPES`.
158 Size, in bits, of the variable. The *size* may be expressed as an
159 int or, for vector variable types, a tuple of int. When the size is
160 expressed as a tuple, the *value* passed to :meth:`change()` must
161 also be a tuple of same arity as the *size* tuple. Some variable
162 types ('integer', 'real', 'realtime', and 'event') have a default
163 size and thus *size* may be ``None`` for those variable types.
164 :type size: int or tuple(int) or None
165 :param init: Optional initial value; defaults to 'x'.
166 :param str ident: Optional identifier for use in the VCD stream.
167 :raises VCDPhaseError: if any values have been changed
168 :raises ValueError: for invalid var_type value
169 :raises TypeError: for invalid parameter types
170 :raises KeyError: for duplicate var name
171 :returns: :class:`Variable` instance appropriate for use with
179 elif var_type
not in self.
VAR_TYPESVAR_TYPES:
180 raise ValueError(
'Invalid var_type "{}"'.format(var_type))
184 scope_names = self.
_scope_var_names_scope_var_names.setdefault(scope_tuple, [])
185 if name
in scope_names:
186 raise KeyError(
'Duplicate var {} in scope {}'.format(name, scope))
192 if var_type
in [
'integer',
'real',
'realtime']:
194 elif var_type ==
'event':
198 'Must supply size for {} var_type'.format(var_type))
200 if isinstance(size, Sequence):
206 var_str =
'$var {var_type} {size} {ident} {name} $end'.format(
207 var_type=var_type, size=var_size, ident=ident, name=name)
210 if var_type ==
'real':
212 elif isinstance(size, tuple):
213 init = tuple(
'x' * len(size))
219 elif var_type ==
'real':
224 if var_type !=
'event':
229 self.
_scope_var_strs_scope_var_strs.setdefault(scope_tuple, []).append(var_str)
230 scope_names.append(name)
235 """Suspend dumping to VCD file."""
242 """Stop dumping to VCD file."""
243 print(
'#' + str(int(timestamp)), file=self.
_ofile_ofile)
244 print(
'$dumpoff', file=self.
_ofile_ofile)
245 for ident, val_str
in six.iteritems(self.
_ident_values_ident_values):
246 if val_str[0] ==
'b':
247 print(
'bx', ident, file=self.
_ofile_ofile)
248 elif val_str[0] ==
'r':
251 print(
'x', ident, sep=
'', file=self.
_ofile_ofile)
252 print(
'$end', file=self.
_ofile_ofile)
255 """Resume dumping to VCD file."""
257 print(
'#' + str(int(timestamp)), file=self.
_ofile_ofile)
258 """set dump_values"""
263 """Dump values to VCD file."""
264 print(keyword, file=self.
_ofile_ofile)
267 sep=
'\n', file=self.
_ofile_ofile)
268 print(
'$end', file=self.
_ofile_ofile)
271 """Change variable's value in VCD stream.
273 This is the fundamental behavior of a :class:`VCDWriter` instance. Each
274 time a variable's value changes, this method should be called.
276 The *timestamp* must be in-order relative to timestamps from previous
277 calls to :meth:`change()`. It is okay to call :meth:`change()` multiple
278 times with the same *timestamp*, but never with a past *timestamp*.
282 :meth:`change()` may be called multiple times before the timestamp
283 progresses past 0. The last value change for each variable will go
284 into the $dumpvars section.
286 :param Variable var: :class:`Variable` instance (i.e. from
287 :meth:`register_var()`).
288 :param int timestamp: Current simulation time.
290 New value for *var*. For :class:`VectorVariable`, if the variable's
291 *size* is a tuple, then *value* must be a tuple of the same arity.
293 :raises ValueError: if the value is not valid for *var*.
294 :raises VCDPhaseError: if the timestamp is out of order or the
295 :class:`VCDWriter` instance is closed.
299 raise VCDPhaseError(
'Out of order value change ({})'.format(var))
303 val_str = var.format_value(value, self.
_check_values_check_values)
304 ts_int = int(timestamp)
310 print(
'#', ts_int, sep=
'', file=self.
_ofile_ofile)
314 print(val_str, file=self.
_ofile_ofile)
319 """get scope tuple function of the VCDWrite"""
320 if isinstance(scope, six.string_types):
321 return tuple(scope.split(self.
_scope_sep_scope_sep))
322 if isinstance(scope, (list, tuple)):
325 raise TypeError(
'Invalid scope {}'.format(scope))
329 """check time scale function of the VCDWrite"""
330 if isinstance(timescale, (list, tuple)):
331 if len(timescale) == 1:
334 elif len(timescale) == 2:
335 num, unit = timescale
337 raise ValueError(
'Invalid timescale num {}'.format(num))
340 raise ValueError(
'Invalid timescale {}'.format(timescale))
341 elif isinstance(timescale, six.string_types):
346 for num
in sorted(cls.
TIMESCALE_NUMSTIMESCALE_NUMS, reverse=
True):
348 if timescale.startswith(num_str):
349 unit = timescale[len(num_str):].lstrip(
' ')
353 'Invalid timescale num {}'.format(timescale))
355 raise TypeError(
'Invalid timescale type {}'
356 .format(type(timescale).__name__))
358 raise ValueError(
'Invalid timescale unit "{}"'.format(unit))
359 return ' '.join([num_str, unit])
362 """enter of VCDWriter"""
366 """exit of VCDWriter"""
372 Any buffered VCD data is flushed to the output file. After
373 :meth:`close()`, no variable registration or value changes will be
376 :param int timestamp: optional final timestamp to insert into VCD
381 The output file is not automatically closed. It is up to the user
382 to ensure the output file is closed after the :class:`VCDWriter`
387 self.
flushflush(timestamp)
391 """Flush any buffered VCD data to output file.
393 If the VCD header has not already been written, calling `flush()` will
394 force the header to be written thus disallowing any further variable
397 :param int timestamp: optional timestamp to insert into VCD stream.
404 if timestamp
is not None and timestamp > self.
_timestamp_timestamp:
405 print(
"#", int(timestamp), sep=
'', file=self.
_ofile_ofile)
409 """generate header for VCDWriter"""
410 for kwname, kwvalue
in sorted(six.iteritems(self.
_header_keywords_header_keywords)):
413 lines = kwvalue.split(
'\n')
415 yield '{} {} $end'.format(kwname, lines[0])
426 for i, (prev, this)
in enumerate(zip_longest(prev_scope, scope)):
428 for _
in prev_scope[i:]:
429 yield '$upscope $end'
431 for j, name
in enumerate(scope[i:]):
434 yield '$scope {} {} $end'.format(scope_type, name)
437 assert scope != prev_scope
439 for var_str
in var_strs:
445 yield '$upscope $end'
447 yield '$enddefinitions $end'
450 """finalize registration of VCDWriter"""
467 """VCD variable details needed to call :meth:`VCDWriter.change()`."""
470 __slots__ = (
'ident',
'type',
'size')
473 """Initialization of Variable function"""
482 """Format value change for use in VCD stream."""
483 raise NotImplementedError
487 """One-bit VCD scalar.
489 This is a 4-state variable and thus may have values of 0, 1, 'z', or 'x'.
497 """Format scalar value change for VCD stream.
499 :param value: 1-bit (4-state) scalar value.
500 :type value: str, bool, int, or None
501 :raises ValueError: for invalid *value*.
502 :returns: string representing value change for use in a VCD stream.
505 if isinstance(value, six.string_types):
506 if check
and (len(value) != 1
or value
not in '01xzXZ'):
507 raise ValueError(
'Invalid scalar value ({})'.format(value))
508 return value + self.
identident
510 return 'z' + self.
identident
512 return '1' + self.
identident
514 return '0' + self.
identident
518 """Real (IEEE-754 double-precision floating point) variable.
520 Values must be numeric and cannot be 'x' or 'z' states.
528 """Format real value change for VCD stream.
530 :param value: Numeric changed value.
531 :param type: float or int
532 :raises ValueError: for invalid real *value*.
533 :returns: string representing value change for use in a VCD stream.
536 if not check
or isinstance(value, Number):
537 return 'r{:.16g} {}'.format(value, self.
identident)
539 raise ValueError(
'Invalid real value ({})'.format(value))
543 """Bit vector variable type.
545 This is for the various non-scalar and non-real variable types including
546 integer, register, wire, etc.
554 """Format value change for VCD stream.
556 :param value: New value for the variable.
557 :types value: int, str, or None
558 :raises ValueError: for *some* invalid values.
560 A *value* of `None` is the same as `'z'`.
564 If *value* is of type :py:class:`str`, all characters must be one
565 of `'01xzXZ'`. For the sake of performance, checking **is not**
566 done to ensure value strings only contain conforming characters.
567 Thus it is possible to produce invalid VCD streams with invalid
571 if isinstance(self.
sizesize, tuple):
577 for i, (v, size)
in enumerate(zip(reversed(value),
578 reversed(self.
sizesize))):
581 vstr_list.insert(0, vstr)
582 vstr_len += len(vstr)
584 leftc = vstr_list[0][0]
586 if len(vstr) > 1
or ((rightc != leftc
or leftc ==
'1')
and
587 (rightc !=
'0' or leftc !=
'1')):
588 extendc =
'0' if leftc ==
'1' else leftc
589 extend_size = size_sum - vstr_len
590 vstr_list.insert(0, extendc * extend_size)
591 vstr_list.insert(0, vstr)
592 vstr_len += extend_size + len(vstr)
594 value_str =
''.join(vstr_list)
597 return 'b{} {}'.format(value_str, self.
identident)
600 """format value function of VCDWriter"""
601 if isinstance(value, six.integer_types):
603 if check
and (-value > (max_val >> 1)
or value >= max_val):
604 raise ValueError(
'Value ({}) not representable in {} bits'
605 .format(value, size))
608 return format(value,
'b')
612 if check
and (
not isinstance(value, six.string_types)
or
614 any(c
not in '01xzXZ-' for c
in value)):
615 raise ValueError(
'Invalid vector value ({})'.format(value))
def format_value(self, value, check=True)
def format_value(self, value, check=True)
_registering
set registering
def __exit__(self, exc_type, exc_val, exc_tb)
def _check_timescale(cls, timescale)
def _dump_values(self, keyword)
def dump_off(self, timestamp)
_next_var_id
set next_var_id
def _finalize_registration(self)
list TIMESCALE_NUMS
Valid timescale numbers.
def dump_on(self, timestamp)
_scope_types
set scopr_types
def change(self, var, timestamp, value)
def __init__(self, file, timescale='1 us', date=None, comment='', version='', default_scope_type='module', scope_sep='.', check_values=True, init_timestamp=0)
list TIMESCALE_UNITS
Valid timescale units.
list VAR_TYPES
Valid VCD variable types.
_check_values
set check_values
def register_var(self, scope, name, var_type, size=None, init=None, ident=None)
def close(self, timestamp=None)
def flush(self, timestamp=None)
_scope_var_names
set scope_var_names
_default_scope_type
set default_scope_type
def _dump_off(self, timestamp)
def set_scope_type(self, scope, scope_type)
list SCOPE_TYPES
Valid VCD scope types.
def _get_scope_tuple(self, scope)
_scope_var_strs
set scope_var_strs
_ident_values
set ident_values
_header_keywords
header keywords
ident
Identifier used in VCD output stream.
type
VCD variable type; one of :const:VCDWriter.VAR_TYPES.
size
Size, in bits, of variable.
def __init__(self, ident, type, size)
def format_value(self, value, check=True)
def _format_value(self, value, size, check)
def format_value(self, value, check=True)