Compare commits
6
Commits
c4aecbd6c9
...
c5bdeb4a68
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5bdeb4a68 | ||
|
|
67db64611b | ||
|
|
7a9ed1e536 | ||
|
|
fcd9b78c24 | ||
|
|
f975765bbb | ||
|
|
3cb1b2bc86 |
No files matched your search
+2
-2
@@ -217,9 +217,9 @@ def parse(text):
|
|||||||
return Lark(GRAMMAR, parser="lalr", transformer=SdfTransformer()).parse(text)
|
return Lark(GRAMMAR, parser="lalr", transformer=SdfTransformer()).parse(text)
|
||||||
|
|
||||||
|
|
||||||
def load(file):
|
def load(file) -> DelayFile:
|
||||||
"""Parses the contents of ``file`` and returns a :class:`DelayFile` object.
|
"""Parses the contents of ``file`` and returns a :class:`DelayFile` object.
|
||||||
|
|
||||||
Files with `.gz`-suffix are decompressed on-the-fly.
|
Files with `.gz`-suffix are decompressed on-the-fly.
|
||||||
"""
|
"""
|
||||||
return parse(readtext(file))
|
return parse(readtext(file)) # type: ignore
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
"""A simple parser for Verilog Change Dump (VCD) files.
|
||||||
|
|
||||||
|
This parser loads the changes into a ndarray of logic values.
|
||||||
|
Axis 0 is the time step, axis 1 is the variable data.
|
||||||
|
All variable values are flattened. Offsets are stored in each variable metadata.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
from lark import Lark, Transformer
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from . import readtext, logic
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Var:
|
||||||
|
type: str
|
||||||
|
width: int
|
||||||
|
idcode: str
|
||||||
|
reference: str
|
||||||
|
scope: 'Scope'
|
||||||
|
locs: list[int] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
class Scope:
|
||||||
|
def __init__(self, parent, name='', type='module') -> None:
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
self.parent_scope = parent
|
||||||
|
self.sub_scopes : list[Scope] = []
|
||||||
|
self.vars : list = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self) -> str:
|
||||||
|
parts = []
|
||||||
|
s = self
|
||||||
|
while s is not None and s.name:
|
||||||
|
parts.append(s.name)
|
||||||
|
s = s.parent_scope
|
||||||
|
return '/'.join(reversed(parts))
|
||||||
|
|
||||||
|
|
||||||
|
class VcdHeader:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.comment: str = ''
|
||||||
|
self.date: str = ''
|
||||||
|
self.timescale: str = ''
|
||||||
|
self.version: str = ''
|
||||||
|
self.root_scope = Scope(None)
|
||||||
|
self.current_scope = self.root_scope
|
||||||
|
|
||||||
|
def scope(self, name, type):
|
||||||
|
new_scope = Scope(self.current_scope, name, type)
|
||||||
|
self.current_scope.sub_scopes.append(new_scope)
|
||||||
|
self.current_scope = new_scope
|
||||||
|
|
||||||
|
def upscope(self):
|
||||||
|
assert self.current_scope.parent_scope is not None, "root scope has no parent"
|
||||||
|
self.current_scope = self.current_scope.parent_scope
|
||||||
|
|
||||||
|
def add_var(self, type: str, width: int, idcode: str, reference: str):
|
||||||
|
self.current_scope.vars.append(Var(type, width, idcode, reference, self.current_scope))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
lines = [
|
||||||
|
'VcdHeader(',
|
||||||
|
f' comment = {self.comment!r}',
|
||||||
|
f' date = {self.date!r}',
|
||||||
|
f' timescale = {self.timescale!r}',
|
||||||
|
f' version = {self.version!r}',
|
||||||
|
' scopes:',
|
||||||
|
]
|
||||||
|
def fmt_scope(scope, indent=4):
|
||||||
|
lines.append(f'{" " * (indent // 2)}{scope.type}:{scope.name} ({len(scope.vars)} vars)')
|
||||||
|
for sub in scope.sub_scopes:
|
||||||
|
fmt_scope(sub, indent + 2)
|
||||||
|
for sub in self.root_scope.sub_scopes:
|
||||||
|
fmt_scope(sub)
|
||||||
|
lines.append(')')
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
class VcdVarMap:
|
||||||
|
def __init__(self, header: VcdHeader, var_locs = lambda _: []) -> None:
|
||||||
|
self.var_list: list[Var] = []
|
||||||
|
def collect(scope):
|
||||||
|
for v in scope.vars:
|
||||||
|
v.locs = var_locs(v)
|
||||||
|
if v.locs is not None and len(v.locs) > 0:
|
||||||
|
assert len(v.locs) == v.width, f'{v.reference}: len(locs)={len(v.locs)} != width={v.width}'
|
||||||
|
self.var_list.append(v)
|
||||||
|
for sub in scope.sub_scopes:
|
||||||
|
collect(sub)
|
||||||
|
collect(header.root_scope)
|
||||||
|
self.total_width = max((max(v.locs) for v in self.var_list), default=-1) + 1
|
||||||
|
self.idcode2var = {var.idcode: var for var in self.var_list}
|
||||||
|
|
||||||
|
|
||||||
|
class VcdData:
|
||||||
|
def __init__(self, var_map, steps, data):
|
||||||
|
self.var_map = var_map
|
||||||
|
self.steps = steps
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
|
||||||
|
class VcdHeaderTransformer(Transformer):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.header = VcdHeader()
|
||||||
|
|
||||||
|
def comment(self, args): self.header.comment = args[0].value.strip()
|
||||||
|
def date(self, args): self.header.date = args[0].value.strip()
|
||||||
|
def timescale(self, args): self.header.timescale = args[0].value.strip()
|
||||||
|
def version(self, args): self.header.version = args[0].value.strip()
|
||||||
|
|
||||||
|
def scope(self, args):
|
||||||
|
type, name = args
|
||||||
|
self.header.scope(name.value, type.value)
|
||||||
|
|
||||||
|
def upscope(self, args):
|
||||||
|
self.header.upscope()
|
||||||
|
|
||||||
|
def var(self, args):
|
||||||
|
type, width, idcode, reference = args
|
||||||
|
reference = reference.strip()
|
||||||
|
width = int(width)
|
||||||
|
self.header.add_var(type, width, idcode, reference)
|
||||||
|
|
||||||
|
def start(self, args):
|
||||||
|
return self.header
|
||||||
|
#return tuple(args) if len(args) > 1 else (args[0], None)
|
||||||
|
|
||||||
|
|
||||||
|
GRAMMAR = r"""
|
||||||
|
start: (_declaration_command)*
|
||||||
|
_declaration_command: ( comment | date | scope | timescale | upscope | var | version ) "$end"
|
||||||
|
comment: "$comment" TEXT?
|
||||||
|
date: "$date" TEXT
|
||||||
|
scope: "$scope" /[^\s]+/ /[^\s]+/
|
||||||
|
timescale: "$timescale" TEXT
|
||||||
|
upscope: "$upscope"
|
||||||
|
var: "$var" /[^\s]+/ /[^\s]+/ /[^\s]+/ TEXT
|
||||||
|
version: "$version" TEXT
|
||||||
|
TEXT: /(?:(?!\$end)[\s\S])+/
|
||||||
|
%ignore ( /\r?\n/ )+
|
||||||
|
%ignore /[\t \f]+/
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def load(file, var_locs = lambda _: [], step_filter = lambda *_: True):
|
||||||
|
"""Parses the contents of ``file`` as Verilog Change Dump (VCD).
|
||||||
|
|
||||||
|
:param file: A file name or a file handle. Files with `.gz`-suffix are decompressed on-the-fly.
|
||||||
|
:param var_locs: A callback ``(var) -> list[int]`` mapping each variable to ndarray column indices. Empty list drops the variable.
|
||||||
|
:param step_filter: A callback ``(time, values, var_map) -> bool`` to select which timesteps to include.
|
||||||
|
:return: A VcdData object with metadata and an ndarray with all values.
|
||||||
|
"""
|
||||||
|
vcd = readtext(file)
|
||||||
|
header_size = vcd.find('$enddefinitions')
|
||||||
|
assert header_size > 0, "invalid VCD file: end of header not found"
|
||||||
|
vcd_header_str = vcd[:header_size]
|
||||||
|
vcd_header : VcdHeader = Lark(GRAMMAR, parser="lalr", lexer='contextual', transformer=VcdHeaderTransformer()).parse(vcd_header_str) # type: ignore
|
||||||
|
vcd_data = vcd[header_size:].splitlines()
|
||||||
|
var_map = VcdVarMap(vcd_header, var_locs)
|
||||||
|
|
||||||
|
chunk_size = 10240
|
||||||
|
chunks = []
|
||||||
|
chunk = np.full((chunk_size, var_map.total_width), logic.UNASSIGNED, dtype=np.uint8)
|
||||||
|
|
||||||
|
_val_map = {'0': logic.ZERO, '1': logic.ONE,
|
||||||
|
'x': logic.UNKNOWN, 'X': logic.UNKNOWN,
|
||||||
|
'z': logic.UNASSIGNED, 'Z': logic.UNASSIGNED}
|
||||||
|
_pad_char = {'0': '0', '1': '0', 'x': 'x', 'X': 'x', 'z': 'z', 'Z': 'z'}
|
||||||
|
|
||||||
|
current_time = None
|
||||||
|
steps = []
|
||||||
|
step_idx = 0
|
||||||
|
for line in vcd_data:
|
||||||
|
if not line or line[0] == '$':
|
||||||
|
continue
|
||||||
|
if line[0] == '#':
|
||||||
|
if step_filter(current_time, chunk[step_idx], var_map):
|
||||||
|
step_idx += 1
|
||||||
|
steps.append(current_time)
|
||||||
|
if step_idx >= chunk_size:
|
||||||
|
chunks.append(chunk)
|
||||||
|
chunk = np.empty((chunk_size, var_map.total_width), dtype=np.uint8)
|
||||||
|
chunk[0] = chunks[-1][-1]
|
||||||
|
step_idx = 0
|
||||||
|
else:
|
||||||
|
chunk[step_idx] = chunk[step_idx - 1]
|
||||||
|
current_time = int(line[1:])
|
||||||
|
continue
|
||||||
|
if line[0] in ('b', 'B'):
|
||||||
|
value_str, idcode = line[1:].split(None, 1)
|
||||||
|
else:
|
||||||
|
value_str, idcode = line[0], line[1:]
|
||||||
|
var = var_map.idcode2var.get(idcode)
|
||||||
|
if var is None:
|
||||||
|
continue
|
||||||
|
if len(value_str) < var.width:
|
||||||
|
value_str = _pad_char.get(value_str[0], '0') * (var.width - len(value_str)) + value_str
|
||||||
|
for i, c in enumerate(value_str):
|
||||||
|
if var.locs[i] >= 0:
|
||||||
|
chunk[step_idx, var.locs[i]] = _val_map.get(c, logic.UNKNOWN)
|
||||||
|
|
||||||
|
chunks.append(chunk[:step_idx])
|
||||||
|
data = np.concatenate(chunks, axis=0).T
|
||||||
|
return VcdData(var_map, steps, data)
|
||||||
@@ -434,7 +434,7 @@ class WaveSimCuda(WaveSim):
|
|||||||
grid_dim = self._grid_dim(self.sims, self.s_len)
|
grid_dim = self._grid_dim(self.sims, self.s_len)
|
||||||
wave_assign_gpu[grid_dim, self._block_dim](self.c, self.s, self.c_locs, self.ppi_offset)
|
wave_assign_gpu[grid_dim, self._block_dim](self.c, self.s, self.c_locs, self.ppi_offset)
|
||||||
|
|
||||||
def _grid_dim(self, x, y): return cdiv(x, self._block_dim[0]), cdiv(y, self._block_dim[1])
|
def _grid_dim(self, x, y): return cdiv(int(x), self._block_dim[0]), cdiv(int(y), self._block_dim[1])
|
||||||
|
|
||||||
def c_prop(self, sims=None, seed=1, op_from=0, op_to=None, delta=0):
|
def c_prop(self, sims=None, seed=1, op_from=0, op_to=None, delta=0):
|
||||||
sims = min(sims or self.sims, self.sims)
|
sims = min(sims or self.sims, self.sims)
|
||||||
|
|||||||
Reference in new issue
Block a user