119 def set_scope_type(self, scope, scope_type):
120 """Set the scope_type for a given scope.
122 The scope's type may be set to one of the valid :const:`SCOPE_TYPES`.
123 VCD viewer applications may display different scope types differently.
125 :param scope: The scope to set the type of.
126 :type scope: str or sequence of str
127 :param str scope_type: A valid scope type string.
128 :raises ValueError: for invalid `scope_type`
131 if scope_type is not None and scope_type not in self.SCOPE_TYPES:
132 raise ValueError(f'Invalid scope_type "{scope_type}"')
133 scope_tuple = self._get_scope_tuple(scope)
134 self._scope_types[scope_tuple] = scope_type
136 def register_var(self, scope, name, var_type, size=None, init=None,
138 """Register a VCD variable and return function to change value.
140 All VCD variables must be registered prior to any value changes.
144 The variable `name` differs from the variable's `ident`
145 (identifier). The `name` (also known as `ref`) is meant to refer to
146 the variable name in the code being traced and is visible in VCD
147 viewer applications. The `ident`, however, is only used within the
148 VCD file and can be auto-generated (by specifying ``ident=None``)
149 for most applications.
151 :param scope: The hierarchical scope that the variable belongs within.
152 :type scope: str or sequence of str
153 :param str name: Name of the variable.
154 :param str var_type: One of :const:`VAR_TYPES`.
156 Size, in bits, of the variable. The *size* may be expressed as an
157 int or, for vector variable types, a tuple of int. When the size is
158 expressed as a tuple, the *value* passed to :meth:`change()` must
159 also be a tuple of same arity as the *size* tuple. Some variable
160 types ('integer', 'real', 'realtime', and 'event') have a default
161 size and thus *size* may be ``None`` for those variable types.
162 :type size: int or tuple(int) or None
163 :param init: Optional initial value; defaults to 'x'.
164 :param str ident: Optional identifier for use in the VCD stream.
165 :raises VCDPhaseError: if any values have been changed
166 :raises ValueError: for invalid var_type value
167 :raises TypeError: for invalid parameter types
168 :raises KeyError: for duplicate var name
169 :returns: :class:`Variable` instance appropriate for use with
174 raise VCDPhaseError('Cannot register after close().')
175 elif not self._registering:
176 raise VCDPhaseError('Cannot register after time 0.')
177 elif var_type not in self.VAR_TYPES:
178 raise ValueError(f'Invalid var_type "{var_type}"')
180 scope_tuple = self._get_scope_tuple(scope)
182 scope_names = self._scope_var_names.setdefault(scope_tuple, [])
183 if name in scope_names:
184 raise KeyError(f'Duplicate var {name} in scope {scope}')
187 ident = format(self._next_var_id, 'x')
190 if var_type in ['integer', 'real', 'realtime']:
192 elif var_type == 'event':
196 f'Must supply size for {var_type} var_type')
198 if isinstance(size, Sequence):
204 var_str = f'$var {var_type} {var_size} {ident} {name} $end'
207 if var_type == 'real':
209 elif isinstance(size, tuple):
210 init = tuple('x' * len(size))
215 var = ScalarVariable(ident, var_type, size)
216 elif var_type == 'real':
217 var = RealVariable(ident, var_type, size)
219 var = VectorVariable(ident, var_type, size)
221 if var_type != 'event':
222 self.change(var, self._timestamp, init)
224 # Only alter state after change_func() succeeds
225 self._next_var_id += 1
226 self._scope_var_strs.setdefault(scope_tuple, []).append(var_str)
227 scope_names.append(name)
238 def _dump_off(self, timestamp):
239 """Stop dumping to VCD file."""
240 print('#' + str(int(timestamp)), file=self._ofile)
241 print('$dumpoff', file=self._ofile)
242 for ident, val_str in self._ident_values.items():
243 if val_str[0] == 'b':
244 print('bx', ident, file=self._ofile)
245 elif val_str[0] == 'r':
246 pass # real variables cannot have 'z' or 'x' state
248 print('x', ident, sep='', file=self._ofile)
249 print('$end', file=self._ofile)
267 def change(self, var, timestamp, value):
268 """Change variable's value in VCD stream.
270 This is the fundamental behavior of a :class:`VCDWriter` instance. Each
271 time a variable's value changes, this method should be called.
273 The *timestamp* must be in-order relative to timestamps from previous
274 calls to :meth:`change()`. It is okay to call :meth:`change()` multiple
275 times with the same *timestamp*, but never with a past *timestamp*.
279 :meth:`change()` may be called multiple times before the timestamp
280 progresses past 0. The last value change for each variable will go
281 into the $dumpvars section.
283 :param Variable var: :class:`Variable` instance (i.e. from
284 :meth:`register_var()`).
285 :param int timestamp: Current simulation time.
287 New value for *var*. For :class:`VectorVariable`, if the variable's
288 *size* is a tuple, then *value* must be a tuple of the same arity.
290 :raises ValueError: if the value is not valid for *var*.
291 :raises VCDPhaseError: if the timestamp is out of order or the
292 :class:`VCDWriter` instance is closed.
295 if timestamp < self._timestamp:
296 raise VCDPhaseError(f'Out of order value change ({var})')
298 raise VCDPhaseError('Cannot change value after close()')
300 val_str = var.format_value(value, self._check_values)
301 ts_int = int(timestamp)
303 if ts_int > self._timestamp:
304 if self._registering:
305 self._finalize_registration()
307 print('#', ts_int, sep='', file=self._ofile)
308 self._timestamp = ts_int
310 if self._dumping and not self._registering:
311 print(val_str, file=self._ofile)
313 self._ident_values[var.ident] = val_str
325 def _check_timescale(cls, timescale):
326 """check time scale function of the VCDWrite"""
327 if isinstance(timescale, (list, tuple)):
328 if len(timescale) == 1:
331 elif len(timescale) == 2:
332 num, unit = timescale
333 if num not in cls.TIMESCALE_NUMS:
334 raise ValueError(f'Invalid timescale num {num}')
337 raise ValueError(f'Invalid timescale {timescale}')
338 elif isinstance(timescale, str):
339 if timescale in cls.TIMESCALE_UNITS:
343 for num in sorted(cls.TIMESCALE_NUMS, reverse=True):
345 if timescale.startswith(num_str):
346 unit = timescale[len(num_str):].lstrip(' ')
350 f'Invalid timescale num {timescale}')
352 raise TypeError(f'Invalid timescale type {type(timescale).__name__}')
353 if unit not in cls.TIMESCALE_UNITS:
354 raise ValueError(f'Invalid timescale unit "{unit}"')
355 return ' '.join([num_str, unit])
386 def flush(self, timestamp=None):
387 """Flush any buffered VCD data to output file.
389 If the VCD header has not already been written, calling `flush()` will
390 force the header to be written thus disallowing any further variable
393 :param int timestamp: optional timestamp to insert into VCD stream.
397 raise VCDPhaseError('Cannot flush() after close()')
398 if self._registering:
399 self._finalize_registration()
400 if timestamp is not None and timestamp > self._timestamp:
401 print("#", int(timestamp), sep='', file=self._ofile)