Browse Source

de-lint and repr improvements

devel
Stefan Holst 4 years ago
parent
commit
ff4de6d782
  1. 11
      src/kyupy/__init__.py
  2. 12
      src/kyupy/bench.py
  3. 14
      src/kyupy/circuit.py
  4. 6
      src/kyupy/logic.py
  5. 2
      src/kyupy/logic_sim.py
  6. 18
      src/kyupy/saed.py
  7. 24
      src/kyupy/sdf.py
  8. 34
      src/kyupy/stil.py
  9. 35
      src/kyupy/verilog.py
  10. 27
      src/kyupy/wave_sim.py
  11. 4
      tests/test_bench.py
  12. 4
      tests/test_logic_sim.py
  13. 7
      tests/test_stil.py
  14. 42
      tests/test_wave_sim.py

11
src/kyupy/__init__.py

@ -77,14 +77,15 @@ class Log:
yield i yield i
current_time = time.perf_counter() current_time = time.perf_counter()
if current_time > lastlog_time + log_interval: if current_time > lastlog_time + log_interval:
work_done = (elem + 1) / elems done = (elem + 1) / elems
elapsed_time = current_time - start_time elapsed_time = current_time - start_time
total_time = elapsed_time / work_done total_time = elapsed_time / done
remaining_time = total_time - elapsed_time rem_time = total_time - elapsed_time
self.log(':', f'{work_done*100:.0f}% done {hr_time(elapsed_time)} elapsed {hr_time(remaining_time)} remaining') self.log(':', f'{done*100:.0f}% done {hr_time(elapsed_time)} elapsed {hr_time(rem_time)} remaining')
log_interval = min(600, int(log_interval*1.5)) log_interval = min(600, int(log_interval*1.5))
lastlog_time = current_time lastlog_time = current_time
log = Log() log = Log()
@ -107,7 +108,7 @@ class MockCuda:
outer = self outer = self
def make_launcher(func): def make_launcher(func):
class Launcher(object): class Launcher:
def __init__(self, funcc): def __init__(self, funcc):
self.func = funcc self.func = funcc

12
src/kyupy/bench.py

@ -14,15 +14,15 @@ from . import readtext
class BenchTransformer(Transformer): class BenchTransformer(Transformer):
def __init__(self, name): def __init__(self, name):
super().__init__() super().__init__()
self.c = Circuit(name) self.c = Circuit(name)
def start(self, _): return self.c def start(self, _): return self.c
def parameters(self, args): return [self.c.get_or_add_fork(name) for name in args] def parameters(self, args): return [self.c.get_or_add_fork(name) for name in args]
def interface(self, args): self.c.interface.extend(args[0]) def interface(self, args): self.c.interface.extend(args[0])
def assignment(self, args): def assignment(self, args):
@ -32,7 +32,7 @@ class BenchTransformer(Transformer):
[Line(self.c, d, cell) for d in drivers] [Line(self.c, d, cell) for d in drivers]
grammar = r""" GRAMMAR = r"""
start: (statement)* start: (statement)*
statement: input | output | assignment statement: input | output | assignment
input: ("INPUT" | "input") parameters -> interface input: ("INPUT" | "input") parameters -> interface
@ -51,7 +51,7 @@ def parse(text, name=None):
:param name: The name of the circuit. Circuit names are not included in bench descriptions. :param name: The name of the circuit. Circuit names are not included in bench descriptions.
:return: A :class:`Circuit` object. :return: A :class:`Circuit` object.
""" """
return Lark(grammar, parser="lalr", transformer=BenchTransformer(name)).parse(text) return Lark(GRAMMAR, parser="lalr", transformer=BenchTransformer(name)).parse(text)
def load(file, name=None): def load(file, name=None):

14
src/kyupy/circuit.py

@ -53,7 +53,7 @@ class Node:
""" """
self.kind = kind self.kind = kind
"""A string describing the type of the node. """A string describing the type of the node.
Common types are the names from a standard cell library or general gate names like 'AND' or 'NOR'. Common types are the names from a standard cell library or general gate names like 'AND' or 'NOR'.
If :py:attr:`kind` is set to '__fork__', it receives special treatment. If :py:attr:`kind` is set to '__fork__', it receives special treatment.
A `fork` describes a named signal or a fan-out point in the circuit and not a physical `cell` like a gate. A `fork` describes a named signal or a fan-out point in the circuit and not a physical `cell` like a gate.
@ -130,7 +130,7 @@ class Line:
""" """
self.driver_pin = driver[1] self.driver_pin = driver[1]
"""The output pin position of the driver node this line is connected to. """The output pin position of the driver node this line is connected to.
This is the position in the outs-list of the driving node this line referenced from: This is the position in the outs-list of the driving node this line referenced from:
:code:`self.driver.outs[self.driver_pin] == self`. :code:`self.driver.outs[self.driver_pin] == self`.
""" """
@ -187,17 +187,17 @@ class Circuit:
""" """
self.nodes = IndexList() self.nodes = IndexList()
"""A list of all :class:`Node` objects contained in the circuit. """A list of all :class:`Node` objects contained in the circuit.
The position of a node in this list equals its index :code:`self.nodes[42].index == 42`. The position of a node in this list equals its index :code:`self.nodes[42].index == 42`.
""" """
self.lines = IndexList() self.lines = IndexList()
"""A list of all :class:`Line` objects contained in the circuit. """A list of all :class:`Line` objects contained in the circuit.
The position of a line in this list equals its index :code:`self.lines[42].index == 42`. The position of a line in this list equals its index :code:`self.lines[42].index == 42`.
""" """
self.interface = GrowingList() self.interface = GrowingList()
"""A list of nodes that are designated as primary input- or output-ports. """A list of nodes that are designated as primary input- or output-ports.
Port-nodes are contained in :py:attr:`nodes` as well as :py:attr:`interface`. Port-nodes are contained in :py:attr:`nodes` as well as :py:attr:`interface`.
The position of a node in the interface list corresponds to positions of logic values in test vectors. The position of a node in the interface list corresponds to positions of logic values in test vectors.
The port direction is not stored explicitly. The port direction is not stored explicitly.
@ -213,7 +213,7 @@ class Circuit:
def get_or_add_fork(self, name): def get_or_add_fork(self, name):
return self.forks[name] if name in self.forks else Node(self, name) return self.forks[name] if name in self.forks else Node(self, name)
def copy(self): def copy(self):
"""Returns a deep copy of the circuit. """Returns a deep copy of the circuit.
""" """
@ -231,7 +231,7 @@ class Circuit:
n = c.cells[node.name] n = c.cells[node.name]
c.interface.append(n) c.interface.append(n)
return c return c
def dump(self): def dump(self):
"""Returns a string representation of the circuit and all its nodes. """Returns a string representation of the circuit and all its nodes.
""" """

6
src/kyupy/logic.py

@ -25,7 +25,7 @@ from collections.abc import Iterable
import numpy as np import numpy as np
from . import numba from . import numba, hr_bytes
ZERO = 0b000 ZERO = 0b000
@ -247,7 +247,7 @@ class MVArray:
self.width = self.data.shape[-2] self.width = self.data.shape[-2]
def __repr__(self): def __repr__(self):
return f'<MVArray length={self.length} width={self.width} m={self.m} nbytes={self.data.nbytes}>' return f'<MVArray length={self.length} width={self.width} m={self.m} mem={hr_bytes(self.data.nbytes)}>'
def __str__(self): def __str__(self):
return str([self[idx] for idx in range(self.length)]) return str([self[idx] for idx in range(self.length)])
@ -396,7 +396,7 @@ class BPArray:
self.width = a.width self.width = a.width
def __repr__(self): def __repr__(self):
return f'<BPArray length={self.length} width={self.width} m={self.m} bytes={self.data.nbytes}>' return f'<BPArray length={self.length} width={self.width} m={self.m} mem={hr_bytes(self.data.nbytes)}>'
def __len__(self): def __len__(self):
return self.length return self.length

2
src/kyupy/logic_sim.py

@ -37,7 +37,7 @@ class LogicSim:
self.zero = np.zeros((mdim, nbytes), dtype='uint8') self.zero = np.zeros((mdim, nbytes), dtype='uint8')
self.epoch = 0 self.epoch = 0
known_fct = [(f[:-4], getattr(self, f)) for f in dir(self) if f.endswith(f'_fct')] known_fct = [(f[:-4], getattr(self, f)) for f in dir(self) if f.endswith('_fct')]
self.node_fct = [] self.node_fct = []
for n in circuit.nodes: for n in circuit.nodes:
t = n.kind.lower().replace('__fork__', 'fork') t = n.kind.lower().replace('__fork__', 'fork')

18
src/kyupy/saed.py

@ -10,18 +10,18 @@ def pin_index(cell_type, pin):
if cell_type.startswith('DFF') and pin == 'CLK': return 1 if cell_type.startswith('DFF') and pin == 'CLK': return 1
if cell_type.startswith('DFF') and pin == 'RSTB': return 2 if cell_type.startswith('DFF') and pin == 'RSTB': return 2
if cell_type.startswith('DFF') and pin == 'SETB': return 3 if cell_type.startswith('DFF') and pin == 'SETB': return 3
if pin in ['A2', 'IN2', 'SE', 'B', 'CO']: return 1 if pin in ('A2', 'IN2', 'SE', 'B', 'CO'): return 1
if pin in ['A3', 'IN3', 'SI', 'CI']: return 2 if pin in ('A3', 'IN3', 'SI', 'CI'): return 2
if pin == 'A4' or pin == 'IN4' or pin == 'CLK': return 3 # CLK for scan cells SDFF if pin in ('A4', 'IN4', 'CLK'): return 3 # CLK for scan cells SDFF
if pin == 'A5' or pin == 'IN5' or pin == 'RSTB': return 4 if pin in ('A5', 'IN5', 'RSTB'): return 4
if pin == 'A6' or pin == 'IN6' or pin == 'SETB': return 5 if pin in ('A6', 'IN6', 'SETB'): return 5
return 0 return 0
def pin_is_output(kind, pin): def pin_is_output(kind, pin):
if 'MUX' in kind and pin == 'S': if 'MUX' in kind and pin == 'S':
return False return False
return pin in ['Q', 'QN', 'Z', 'ZN', 'Y', 'CO', 'S', 'SO', 'C1'] return pin in ('Q', 'QN', 'Z', 'ZN', 'Y', 'CO', 'S', 'SO', 'C1')
def add_and_connect(circuit, name, kind, in1=None, in2=None, out=None): def add_and_connect(circuit, name, kind, in1=None, in2=None, out=None):
@ -103,7 +103,7 @@ def split_complex_gates(circuit):
n_or1 = add_and_connect(circuit, name+'~or1', 'OR2', None, ins[4], outs[0]) n_or1 = add_and_connect(circuit, name+'~or1', 'OR2', None, ins[4], outs[0])
Line(circuit, n_and0, n_or0) Line(circuit, n_and0, n_or0)
Line(circuit, n_and1, n_or0) Line(circuit, n_and1, n_or0)
Line(circuit, n_or0, n_or1) Line(circuit, n_or0, n_or1)
elif n.kind.startswith('AOI221X'): elif n.kind.startswith('AOI221X'):
n.remove() n.remove()
n_and0 = add_and_connect(circuit, name+'~and0', 'AND2', ins[0], ins[1], None) n_and0 = add_and_connect(circuit, name+'~and0', 'AND2', ins[0], ins[1], None)
@ -112,7 +112,7 @@ def split_complex_gates(circuit):
n_nor = add_and_connect(circuit, name+'~nor', 'NOR2', None, ins[4], outs[0]) n_nor = add_and_connect(circuit, name+'~nor', 'NOR2', None, ins[4], outs[0])
Line(circuit, n_and0, n_or) Line(circuit, n_and0, n_or)
Line(circuit, n_and1, n_or) Line(circuit, n_and1, n_or)
Line(circuit, n_or, n_nor) Line(circuit, n_or, n_nor)
elif n.kind.startswith('OA221X'): elif n.kind.startswith('OA221X'):
n.remove() n.remove()
n_or0 = add_and_connect(circuit, name+'~or0', 'OR2', ins[0], ins[1], None) n_or0 = add_and_connect(circuit, name+'~or0', 'OR2', ins[0], ins[1], None)
@ -121,7 +121,7 @@ def split_complex_gates(circuit):
n_and1 = add_and_connect(circuit, name+'~and1', 'AND2', None, ins[4], outs[0]) n_and1 = add_and_connect(circuit, name+'~and1', 'AND2', None, ins[4], outs[0])
Line(circuit, n_or0, n_and0) Line(circuit, n_or0, n_and0)
Line(circuit, n_or1, n_and0) Line(circuit, n_or1, n_and0)
Line(circuit, n_and0, n_and1) Line(circuit, n_and0, n_and1)
elif n.kind.startswith('OAI221X'): elif n.kind.startswith('OAI221X'):
n.remove() n.remove()
n_or0 = add_and_connect(circuit, name+'~or0', 'OR2', ins[0], ins[1], None) n_or0 = add_and_connect(circuit, name+'~or0', 'OR2', ins[0], ins[1], None)

24
src/kyupy/sdf.py

@ -58,14 +58,10 @@ class DelayFile:
E.g., timing[42,1,0] is the rejection limit of a negative pulse at the output of the reader of line 42. E.g., timing[42,1,0] is the rejection limit of a negative pulse at the output of the reader of line 42.
""" """
def select_del(_delvals, idx): def select_del(_delvals, idx):
if type(dataset) is tuple: if isinstance(dataset, tuple):
s = 0 return sum(_delvals[idx][d] for d in dataset) / len(dataset)
for d in dataset: return _delvals[idx][dataset]
s += _delvals[idx][d]
return s / len(dataset)
else:
return _delvals[idx][dataset]
def find_cell(name): def find_cell(name):
if name not in circuit.cells: if name not in circuit.cells:
name = name.replace('\\', '') name = name.replace('\\', '')
@ -74,7 +70,7 @@ class DelayFile:
if name not in circuit.cells: if name not in circuit.cells:
return None return None
return circuit.cells[name] return circuit.cells[name]
timing = np.zeros((len(circuit.lines), 2, 2)) timing = np.zeros((len(circuit.lines), 2, 2))
for cn, iopaths in self.cells.items(): for cn, iopaths in self.cells.items():
for ipn, opn, *delvals in iopaths: for ipn, opn, *delvals in iopaths:
@ -91,7 +87,7 @@ class DelayFile:
ipn2 = ipn.replace('(posedge A1)', 'A1').replace('(negedge A1)', 'A1')\ ipn2 = ipn.replace('(posedge A1)', 'A1').replace('(negedge A1)', 'A1')\
.replace('(posedge A2)', 'A2').replace('(negedge A2)', 'A2') .replace('(posedge A2)', 'A2').replace('(negedge A2)', 'A2')
def add_delays(_line): def add_delays(_line):
if _line is not None: if _line is not None:
timing[_line.index, :, 0] += select_del(delvals, 0) timing[_line.index, :, 0] += select_del(delvals, 0)
@ -111,10 +107,10 @@ class DelayFile:
add_delays(cell.ins[ipin]) add_delays(cell.ins[ipin])
if take_avg: if take_avg:
timing[cell.ins[ipin].index] /= 2 timing[cell.ins[ipin].index] /= 2
if not interconnect or self.interconnects is None: if not interconnect or self.interconnects is None:
return timing return timing
for n1, n2, *delvals in self.interconnects: for n1, n2, *delvals in self.interconnects:
delvals = [d if len(d) > 0 else [0, 0, 0] for d in delvals] delvals = [d if len(d) > 0 else [0, 0, 0] for d in delvals]
if max(max(delvals)) == 0: if max(max(delvals)) == 0:
@ -184,7 +180,7 @@ class SdfTransformer(Transformer):
return DelayFile(name, cells) return DelayFile(name, cells)
grammar = r""" GRAMMAR = r"""
start: "(DELAYFILE" ( "(SDFVERSION" _NOB ")" start: "(DELAYFILE" ( "(SDFVERSION" _NOB ")"
| "(DESIGN" "\"" NAME "\"" ")" | "(DESIGN" "\"" NAME "\"" ")"
| "(DATE" _NOB ")" | "(DATE" _NOB ")"
@ -218,7 +214,7 @@ grammar = r"""
def parse(text): def parse(text):
"""Parses the given ``text`` and returns a :class:`DelayFile` object.""" """Parses the given ``text`` and returns a :class:`DelayFile` object."""
return Lark(grammar, parser="lalr", transformer=SdfTransformer()).parse(text) return Lark(GRAMMAR, parser="lalr", transformer=SdfTransformer()).parse(text)
def load(file): def load(file):

34
src/kyupy/stil.py

@ -54,26 +54,26 @@ class StilFile:
launch = dict((k, v.replace('\n', '')) for k, v in call.parameters.items()) launch = dict((k, v.replace('\n', '')) for k, v in call.parameters.items())
else: else:
capture = dict((k, v.replace('\n', '')) for k, v in call.parameters.items()) capture = dict((k, v.replace('\n', '')) for k, v in call.parameters.items())
def _maps(self, c): def _maps(self, c):
interface = list(c.interface) + [n for n in c.nodes if 'DFF' in n.kind] interface = list(c.interface) + [n for n in c.nodes if 'DFF' in n.kind]
intf_pos = dict([(n.name, i) for i, n in enumerate(interface)]) intf_pos = dict((n.name, i) for i, n in enumerate(interface))
pi_map = [intf_pos[n] for n in self.signal_groups['_pi']] pi_map = [intf_pos[n] for n in self.signal_groups['_pi']]
po_map = [intf_pos[n] for n in self.signal_groups['_po']] po_map = [intf_pos[n] for n in self.signal_groups['_po']]
scan_maps = {} scan_maps = {}
scan_inversions = {} scan_inversions = {}
for chain_name, chain in self.scan_chains.items(): for chain in self.scan_chains.values():
scan_map = [] scan_map = []
scan_in_inversion = [] scan_in_inversion = []
scan_out_inversion = [] scan_out_inversion = []
inversion = False inversion = False
for n in chain[1:-1]: for n in chain[1:-1]:
if n == '!': if n == '!':
inversion = not inversion inversion = not inversion
else: else:
scan_in_inversion.append(inversion) scan_in_inversion.append(inversion)
scan_in_inversion = list(reversed(scan_in_inversion)) scan_in_inversion = list(reversed(scan_in_inversion))
inversion = False inversion = False
for n in reversed(chain[1:-1]): for n in reversed(chain[1:-1]):
if n == '!': if n == '!':
inversion = not inversion inversion = not inversion
@ -85,13 +85,13 @@ class StilFile:
scan_inversions[chain[0]] = scan_in_inversion scan_inversions[chain[0]] = scan_in_inversion
scan_inversions[chain[-1]] = scan_out_inversion scan_inversions[chain[-1]] = scan_out_inversion
return interface, pi_map, po_map, scan_maps, scan_inversions return interface, pi_map, po_map, scan_maps, scan_inversions
def tests(self, circuit): def tests(self, circuit):
"""Assembles and returns a scan test pattern set for given circuit. """Assembles and returns a scan test pattern set for given circuit.
This function assumes a static (stuck-at fault) test. This function assumes a static (stuck-at fault) test.
""" """
interface, pi_map, po_map, scan_maps, scan_inversions = self._maps(circuit) interface, pi_map, _, scan_maps, scan_inversions = self._maps(circuit)
tests = logic.MVArray((len(interface), len(self.patterns))) tests = logic.MVArray((len(interface), len(self.patterns)))
for i, p in enumerate(self.patterns): for i, p in enumerate(self.patterns):
for si_port in self.si_ports.keys(): for si_port in self.si_ports.keys():
@ -133,10 +133,10 @@ class StilFile:
launch.data[po_map, i] = logic.UNASSIGNED launch.data[po_map, i] = logic.UNASSIGNED
return logic.mv_transition(init, launch) return logic.mv_transition(init, launch)
def responses(self, circuit): def responses(self, circuit):
"""Assembles and returns a scan test response pattern set for given circuit.""" """Assembles and returns a scan test response pattern set for given circuit."""
interface, pi_map, po_map, scan_maps, scan_inversions = self._maps(circuit) interface, _, po_map, scan_maps, scan_inversions = self._maps(circuit)
resp = logic.MVArray((len(interface), len(self.patterns))) resp = logic.MVArray((len(interface), len(self.patterns)))
# resp = PackedVectors(len(self.patterns), len(interface), 2) # resp = PackedVectors(len(self.patterns), len(interface), 2)
for i, p in enumerate(self.patterns): for i, p in enumerate(self.patterns):
@ -150,27 +150,27 @@ class StilFile:
resp.data[scan_maps[so_port], i] = pattern.data[:, 0] resp.data[scan_maps[so_port], i] = pattern.data[:, 0]
# resp.set_values(i, p.unload[so_port], scan_maps[so_port], scan_inversions[so_port]) # resp.set_values(i, p.unload[so_port], scan_maps[so_port], scan_inversions[so_port])
return resp return resp
class StilTransformer(Transformer): class StilTransformer(Transformer):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._signal_groups = None self._signal_groups = None
self._calls = None self._calls = None
self._scan_chains = None self._scan_chains = None
@staticmethod @staticmethod
def quoted(args): return args[0][1:-1] def quoted(args): return args[0][1:-1]
@staticmethod @staticmethod
def call(args): return Call(args[0], dict(args[1:])) def call(args): return Call(args[0], dict(args[1:]))
@staticmethod @staticmethod
def call_parameter(args): return args[0], args[1].value def call_parameter(args): return args[0], args[1].value
@staticmethod @staticmethod
def signal_group(args): return args[0], args[1:] def signal_group(args): return args[0], args[1:]
@staticmethod @staticmethod
def scan_chain(args): def scan_chain(args):
scan_in = None scan_in = None
@ -187,7 +187,7 @@ class StilTransformer(Transformer):
return args[0], ([scan_in] + scan_cells + [scan_out]) return args[0], ([scan_in] + scan_cells + [scan_out])
def signal_groups(self, args): self._signal_groups = dict(args) def signal_groups(self, args): self._signal_groups = dict(args)
def pattern(self, args): self._calls = [c for c in args if isinstance(c, Call)] def pattern(self, args): self._calls = [c for c in args if isinstance(c, Call)]
def scan_structures(self, args): self._scan_chains = dict(args) def scan_structures(self, args): self._scan_chains = dict(args)
@ -196,7 +196,7 @@ class StilTransformer(Transformer):
return StilFile(float(args[0]), self._signal_groups, self._scan_chains, self._calls) return StilFile(float(args[0]), self._signal_groups, self._scan_chains, self._calls)
grammar = r""" GRAMMAR = r"""
start: "STIL" FLOAT _ignore _block* start: "STIL" FLOAT _ignore _block*
_block: signal_groups | scan_structures | pattern _block: signal_groups | scan_structures | pattern
| "Header" _ignore | "Header" _ignore
@ -240,7 +240,7 @@ grammar = r"""
def parse(text): def parse(text):
"""Parses the given ``text`` and returns a :class:`StilFile` object.""" """Parses the given ``text`` and returns a :class:`StilFile` object."""
return Lark(grammar, parser="lalr", transformer=StilTransformer()).parse(text) return Lark(GRAMMAR, parser="lalr", transformer=StilTransformer()).parse(text)
def load(file): def load(file):

35
src/kyupy/verilog.py

@ -16,7 +16,7 @@ Instantiation = namedtuple('Instantiation', ['type', 'name', 'pins'])
class SignalDeclaration: class SignalDeclaration:
def __init__(self, kind, tokens): def __init__(self, kind, tokens):
self.left = None self.left = None
self.right = None self.right = None
@ -27,16 +27,15 @@ class SignalDeclaration:
self.basename = tokens.children[2] self.basename = tokens.children[2]
self.left = int(tokens.children[0].value) self.left = int(tokens.children[0].value)
self.right = int(tokens.children[1].value) self.right = int(tokens.children[1].value)
@property @property
def names(self): def names(self):
if self.left is None: if self.left is None:
return [self.basename] return [self.basename]
if self.left <= self.right: if self.left <= self.right:
return [f'{self.basename}[{i}]' for i in range(self.left, self.right + 1)] return [f'{self.basename}[{i}]' for i in range(self.left, self.right + 1)]
else: return [f'{self.basename}[{i}]' for i in range(self.left, self.right - 1, -1)]
return [f'{self.basename}[{i}]' for i in range(self.left, self.right - 1, -1)]
def __repr__(self): def __repr__(self):
return f"{self.kind}:{self.basename}[{self.left}:{self.right}]" return f"{self.kind}:{self.basename}[{self.left}:{self.right}]"
@ -57,24 +56,24 @@ class VerilogTransformer(Transformer):
@staticmethod @staticmethod
def instantiation(args): def instantiation(args):
return Instantiation(args[0], args[1], return Instantiation(args[0], args[1],
dict([(pin.children[0], pin.children[1]) for pin in args[2:]])) dict((pin.children[0], pin.children[1]) for pin in args[2:]))
def input(self, args): def input(self, args):
for sd in [SignalDeclaration('input', signal) for signal in args]: for sd in [SignalDeclaration('input', signal) for signal in args]:
self._signal_declarations[sd.basename] = sd self._signal_declarations[sd.basename] = sd
def inout(self, args): def inout(self, args):
for sd in [SignalDeclaration('input', signal) for signal in args]: # just treat as input for sd in [SignalDeclaration('input', signal) for signal in args]: # just treat as input
self._signal_declarations[sd.basename] = sd self._signal_declarations[sd.basename] = sd
def output(self, args): def output(self, args):
for sd in [SignalDeclaration('output', signal) for signal in args]: for sd in [SignalDeclaration('output', signal) for signal in args]:
self._signal_declarations[sd.basename] = sd self._signal_declarations[sd.basename] = sd
def wire(self, args): def wire(self, args):
for sd in [SignalDeclaration('wire', signal) for signal in args]: for sd in [SignalDeclaration('wire', signal) for signal in args]:
self._signal_declarations[sd.basename] = sd self._signal_declarations[sd.basename] = sd
def module(self, args): def module(self, args):
c = Circuit(args[0]) c = Circuit(args[0])
positions = {} positions = {}
@ -85,7 +84,7 @@ class VerilogTransformer(Transformer):
pos += 1 pos += 1
assignments = [] assignments = []
for stmt in args[2:]: # pass 1: instantiate cells and driven signals for stmt in args[2:]: # pass 1: instantiate cells and driven signals
if type(stmt) is Instantiation: if isinstance(stmt, Instantiation):
n = Node(c, stmt.name, kind=stmt.type) n = Node(c, stmt.name, kind=stmt.type)
for p, s in stmt.pins.items(): for p, s in stmt.pins.items():
if pin_is_output(n.kind, p): if pin_is_output(n.kind, p):
@ -108,7 +107,7 @@ class VerilogTransformer(Transformer):
assert s1 not in c.forks, 'assignment between two driven signals' assert s1 not in c.forks, 'assignment between two driven signals'
Line(c, c.forks[s2], Node(c, s1)) Line(c, c.forks[s2], Node(c, s1))
for stmt in args[2:]: # pass 2: connect signals to readers for stmt in args[2:]: # pass 2: connect signals to readers
if type(stmt) is Instantiation: if isinstance(stmt, Instantiation):
for p, s in stmt.pins.items(): for p, s in stmt.pins.items():
n = c.cells[stmt.name] n = c.cells[stmt.name]
if pin_is_output(n.kind, p): continue if pin_is_output(n.kind, p): continue
@ -129,14 +128,10 @@ class VerilogTransformer(Transformer):
return c return c
@staticmethod @staticmethod
def start(args): def start(args): return args[0] if len(args) == 1 else args
if len(args) == 1:
return args[0]
else:
return args
grammar = """ GRAMMAR = """
start: (module)* start: (module)*
module: "module" name parameters ";" (_statement)* "endmodule" module: "module" name parameters ";" (_statement)* "endmodule"
parameters: "(" [ name ( "," name )* ] ")" parameters: "(" [ name ( "," name )* ] ")"
@ -167,7 +162,7 @@ def parse(text, *, branchforks=False):
(see :py:func:`kyupy.sdf.DelayFile.annotation`). (see :py:func:`kyupy.sdf.DelayFile.annotation`).
:return: A :class:`~kyupy.circuit.Circuit` object. :return: A :class:`~kyupy.circuit.Circuit` object.
""" """
return Lark(grammar, parser="lalr", transformer=VerilogTransformer(branchforks)).parse(text) return Lark(GRAMMAR, parser="lalr", transformer=VerilogTransformer(branchforks)).parse(text)
def load(file, *args, **kwargs): def load(file, *args, **kwargs):

27
src/kyupy/wave_sim.py

@ -37,7 +37,7 @@ class Heap:
if self.chunks[loc] == size: if self.chunks[loc] == size:
del self.released[idx] del self.released[idx]
return loc return loc
elif self.chunks[loc] > size: # split chunk if self.chunks[loc] > size: # split chunk
chunksize = self.chunks[loc] chunksize = self.chunks[loc]
self.chunks[loc] = size self.chunks[loc] = size
self.chunks[loc + size] = chunksize - size self.chunks[loc + size] = chunksize - size
@ -103,7 +103,7 @@ class WaveSim:
self.cdata = np.zeros((len(self.interface), sims, 7), dtype='float32') self.cdata = np.zeros((len(self.interface), sims, 7), dtype='float32')
if type(wavecaps) is int: if isinstance(wavecaps, int):
wavecaps = [wavecaps] * len(circuit.lines) wavecaps = [wavecaps] * len(circuit.lines)
intf_wavecap = 4 # sufficient for storing only 1 transition. intf_wavecap = 4 # sufficient for storing only 1 transition.
@ -117,7 +117,7 @@ class WaveSim:
# translate circuit structure into self.ops # translate circuit structure into self.ops
ops = [] ops = []
interface_dict = dict([(n, i) for i, n in enumerate(self.interface)]) interface_dict = dict((n, i) for i, n in enumerate(self.interface))
for n in circuit.topological_order(): for n in circuit.topological_order():
if n in interface_dict: if n in interface_dict:
inp_idx = self.ppi_offset + interface_dict[n] inp_idx = self.ppi_offset + interface_dict[n]
@ -255,8 +255,7 @@ class WaveSim:
def __repr__(self): def __repr__(self):
total_mem = self.state.nbytes + self.sat.nbytes + self.ops.nbytes + self.cdata.nbytes total_mem = self.state.nbytes + self.sat.nbytes + self.ops.nbytes + self.cdata.nbytes
return f'<WaveSim {self.circuit.name} sims={self.sims} ops={len(self.ops)} ' + \ return f'<WaveSim {self.circuit.name} sims={self.sims} ops={len(self.ops)} ' + \
f'levels={len(self.level_starts)} state_mem={hr_bytes(self.state.nbytes)} ' + \ f'levels={len(self.level_starts)} mem={hr_bytes(total_mem)}>'
f'total_mem={hr_bytes(total_mem)}>'
def get_line_delay(self, line, polarity): def get_line_delay(self, line, polarity):
return self.timing[line, 0, polarity] return self.timing[line, 0, polarity]
@ -266,7 +265,7 @@ class WaveSim:
def assign(self, vectors, time=0.0, offset=0): def assign(self, vectors, time=0.0, offset=0):
nvectors = min(len(vectors) - offset, self.sims) nvectors = min(len(vectors) - offset, self.sims)
for i, node in enumerate(self.interface): for i in range(len(self.interface)):
ppi_loc = self.sat[self.ppi_offset + i, 0] ppi_loc = self.sat[self.ppi_offset + i, 0]
if ppi_loc < 0: continue if ppi_loc < 0: continue
for p in range(nvectors): for p in range(nvectors):
@ -321,7 +320,7 @@ class WaveSim:
return self.cdata return self.cdata
def reassign(self, time=0.0): def reassign(self, time=0.0):
for i, node in enumerate(self.interface): for i in range(len(self.interface)):
ppi_loc = self.sat[self.ppi_offset + i, 0] ppi_loc = self.sat[self.ppi_offset + i, 0]
ppo_loc = self.sat[self.ppo_offset + i, 0] ppo_loc = self.sat[self.ppo_offset + i, 0]
if ppi_loc < 0 or ppo_loc < 0: continue if ppi_loc < 0 or ppo_loc < 0: continue
@ -386,8 +385,7 @@ class WaveSim:
accs[idx] += 1 accs[idx] += 1
if s_sqrt2 == 0: if s_sqrt2 == 0:
return values return values
else: return accs
return accs
def vals(self, line, vector, times, sd=0): def vals(self, line, vector, times, sd=0):
return self._vals(line, vector, times, sd) return self._vals(line, vector, times, sd)
@ -464,7 +462,7 @@ def rand_gauss(seed, sd):
return 1.0 return 1.0
while True: while True:
x = -6.0 x = -6.0
for i in range(12): for _ in range(12):
seed = int(0xDEECE66D) * seed + 0xB seed = int(0xDEECE66D) * seed + 0xB
x += float((seed >> 8) & 0xffffff) / float(1 << 24) x += float((seed >> 8) & 0xffffff) / float(1 << 24)
x *= sd x *= sd
@ -541,7 +539,7 @@ def wave_eval(op, state, sat, st_idx, line_times, sd=0.0, seed=0):
state[z_mem + z_cur, st_idx] = TMAX_OVL state[z_mem + z_cur, st_idx] = TMAX_OVL
else: else:
state[z_mem + z_cur, st_idx] = a if a > b else b # propagate overflow flags by storing biggest TMAX from input state[z_mem + z_cur, st_idx] = a if a > b else b # propagate overflow flags by storing biggest TMAX from input
return overflows return overflows
@ -565,8 +563,7 @@ class WaveSimCuda(WaveSim):
total_mem = self.state.nbytes + self.sat.nbytes + self.ops.nbytes + self.timing.nbytes + \ total_mem = self.state.nbytes + self.sat.nbytes + self.ops.nbytes + self.timing.nbytes + \
self.tdata.nbytes + self.cdata.nbytes self.tdata.nbytes + self.cdata.nbytes
return f'<WaveSimCuda {self.circuit.name} sims={self.sims} ops={len(self.ops)} ' + \ return f'<WaveSimCuda {self.circuit.name} sims={self.sims} ops={len(self.ops)} ' + \
f'levels={len(self.level_starts)} state_mem={hr_bytes(self.state.nbytes)} ' + \ f'levels={len(self.level_starts)} mem={hr_bytes(total_mem)}>'
f'total_mem={hr_bytes(total_mem)}>'
def get_line_delay(self, line, polarity): def get_line_delay(self, line, polarity):
return self.d_timing[line, 0, polarity] return self.d_timing[line, 0, polarity]
@ -661,7 +658,7 @@ def reassign_kernel(state, sat, ppi_offset, ppo_offset, cdata, ppi_time):
if vector >= state.shape[-1]: return if vector >= state.shape[-1]: return
if ppo_offset + y >= len(sat): return if ppo_offset + y >= len(sat): return
ppo, ppo_cap, _ = sat[ppo_offset + y] ppo, _, _ = sat[ppo_offset + y]
ppi, ppi_cap, _ = sat[ppi_offset + y] ppi, ppi_cap, _ = sat[ppi_offset + y]
if ppo < 0: return if ppo < 0: return
if ppi < 0: return if ppi < 0: return
@ -771,7 +768,7 @@ def rand_gauss_dev(seed, sd):
return 1.0 return 1.0
while True: while True:
x = -6.0 x = -6.0
for i in range(12): for _ in range(12):
seed = int(0xDEECE66D) * seed + 0xB seed = int(0xDEECE66D) * seed + 0xB
x += float((seed >> 8) & 0xffffff) / float(1 << 24) x += float((seed >> 8) & 0xffffff) / float(1 << 24)
x *= sd x *= sd

4
tests/test_bench.py

@ -4,9 +4,9 @@ from kyupy import bench
def test_b01(mydir): def test_b01(mydir):
with open(mydir / 'b01.bench', 'r') as f: with open(mydir / 'b01.bench', 'r') as f:
c = bench.parse(f.read()) c = bench.parse(f.read())
assert 92 == len(c.nodes) assert len(c.nodes) == 92
c = bench.load(mydir / 'b01.bench') c = bench.load(mydir / 'b01.bench')
assert 92 == len(c.nodes) assert len(c.nodes) == 92
def test_simple(): def test_simple():

4
tests/test_logic_sim.py

@ -49,7 +49,7 @@ def test_4v():
assert mva[14] == 'X-XXX' assert mva[14] == 'X-XXX'
assert mva[15] == 'XXXXX' assert mva[15] == 'XXXXX'
def test_8v(): def test_8v():
c = bench.parse('input(x, y) output(a, o, n, xo) a=and(x,y) o=or(x,y) n=not(x) xo=xor(x,y)') c = bench.parse('input(x, y) output(a, o, n, xo) a=and(x,y) o=or(x,y) n=not(x) xo=xor(x,y)')
s = LogicSim(c, 64, m=8) s = LogicSim(c, 64, m=8)
@ -71,7 +71,7 @@ def test_8v():
for i in range(64): for i in range(64):
assert resp[i] == mva[i] assert resp[i] == mva[i]
def test_b01(mydir): def test_b01(mydir):
c = bench.load(mydir / 'b01.bench') c = bench.load(mydir / 'b01.bench')

7
tests/test_stil.py

@ -3,7 +3,6 @@ from kyupy import stil
def test_b14(mydir): def test_b14(mydir):
s = stil.load(mydir / 'b14.stuck.stil.gz') s = stil.load(mydir / 'b14.stuck.stil.gz')
assert 10 == len(s.signal_groups) assert len(s.signal_groups) == 10
assert 1 == len(s.scan_chains) assert len(s.scan_chains) == 1
assert 2163 == len(s.calls) assert len(s.calls) == 2163

42
tests/test_wave_sim.py

@ -19,7 +19,7 @@ def test_wave_eval():
line_times[1, 0, 1] = 0.4 line_times[1, 0, 1] = 0.4
line_times[1, 1, 0] = 0.3 line_times[1, 1, 0] = 0.3
line_times[1, 1, 1] = 0.4 line_times[1, 1, 1] = 0.4
state = np.zeros((3*16, 1)) + TMAX # 3 waveforms of capacity 16 state = np.zeros((3*16, 1)) + TMAX # 3 waveforms of capacity 16
state[::16, 0] = 16 # first entry is capacity state[::16, 0] = 16 # first entry is capacity
a = state[0:16, 0] a = state[0:16, 0]
@ -31,29 +31,29 @@ def test_wave_eval():
sat[2] = 32, 16, 0 sat[2] = 32, 16, 0
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert TMIN == z[0] assert z[0] == TMIN
a[0] = TMIN a[0] = TMIN
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert TMIN == z[0] assert z[0] == TMIN
b[0] = TMIN b[0] = TMIN
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert TMAX == z[0] assert z[0] == TMAX
a[0] = 1 # A _/^^^ a[0] = 1 # A _/^^^
b[0] = 2 # B __/^^ b[0] = 2 # B __/^^
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert TMIN == z[0] # ^^^\___ B -> Z fall delay assert z[0] == TMIN # ^^^\___ B -> Z fall delay
assert 2.4 == z[1] assert z[1] == 2.4
assert TMAX == z[2] assert z[2] == TMAX
a[0] = TMIN # A ^^^^^^ a[0] = TMIN # A ^^^^^^
b[0] = TMIN # B ^^^\__ b[0] = TMIN # B ^^^\__
b[1] = 2 b[1] = 2
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert 2.3 == z[0] # ___/^^^ B -> Z rise delay assert z[0] == 2.3 # ___/^^^ B -> Z rise delay
assert TMAX == z[1] assert z[1] == TMAX
# pos pulse of 0.35 at B -> 0.45 after delays # pos pulse of 0.35 at B -> 0.45 after delays
a[0] = TMIN # A ^^^^^^^^ a[0] = TMIN # A ^^^^^^^^
@ -61,9 +61,9 @@ def test_wave_eval():
b[1] = 2 # B ^^\__/^^ b[1] = 2 # B ^^\__/^^
b[2] = 2.35 b[2] = 2.35
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert 2.3 == z[0] # __/^^\__ assert z[0] == 2.3 # __/^^\__
assert 2.75 == z[1] assert z[1] == 2.75
assert TMAX == z[2] assert z[2] == TMAX
# neg pulse of 0.45 at B -> 0.35 after delays # neg pulse of 0.45 at B -> 0.35 after delays
a[0] = TMIN # A ^^^^^^^^ a[0] = TMIN # A ^^^^^^^^
@ -71,10 +71,10 @@ def test_wave_eval():
b[1] = 2.45 b[1] = 2.45
b[2] = TMAX b[2] = TMAX
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert TMIN == z[0] # ^^\__/^^ assert z[0] == TMIN # ^^\__/^^
assert 2.4 == z[1] assert z[1] == 2.4
assert 2.75 == z[2] assert z[2] == 2.75
assert TMAX == z[3] assert z[3] == TMAX
# neg pulse of 0.35 at B -> 0.25 after delays (filtered) # neg pulse of 0.35 at B -> 0.25 after delays (filtered)
a[0] = TMIN # A ^^^^^^^^ a[0] = TMIN # A ^^^^^^^^
@ -82,8 +82,8 @@ def test_wave_eval():
b[1] = 2.35 b[1] = 2.35
b[2] = TMAX b[2] = TMAX
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert TMIN == z[0] # ^^^^^^ assert z[0] == TMIN # ^^^^^^
assert TMAX == z[1] assert z[1] == TMAX
# pos pulse of 0.25 at B -> 0.35 after delays (filtered) # pos pulse of 0.25 at B -> 0.35 after delays (filtered)
a[0] = TMIN # A ^^^^^^^^ a[0] = TMIN # A ^^^^^^^^
@ -91,7 +91,7 @@ def test_wave_eval():
b[1] = 2 # B ^^\__/^^ b[1] = 2 # B ^^\__/^^
b[2] = 2.25 b[2] = 2.25
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times) wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times)
assert TMAX == z[0] # ______ assert z[0] == TMAX # ______
def compare_to_logic_sim(wsim): def compare_to_logic_sim(wsim):
@ -118,7 +118,7 @@ def compare_to_logic_sim(wsim):
exp_bp = BPArray(tests_bp) exp_bp = BPArray(tests_bp)
lsim.capture(exp_bp) lsim.capture(exp_bp)
exp = MVArray(exp_bp) exp = MVArray(exp_bp)
for i in range(8): for i in range(8):
exp_str = exp[i].replace('R', '1').replace('F', '0').replace('P', '0').replace('N', '1') exp_str = exp[i].replace('R', '1').replace('F', '0').replace('P', '0').replace('N', '1')
res_str = resp[i].replace('R', '1').replace('F', '0').replace('P', '0').replace('N', '1') res_str = resp[i].replace('R', '1').replace('F', '0').replace('P', '0').replace('N', '1')

Loading…
Cancel
Save