Compare commits

...

5 Commits

  1. 35
      src/kyupy/circuit.py
  2. 37
      src/kyupy/logic_sim.py
  3. 20
      src/kyupy/sim.py
  4. 12
      src/kyupy/stil.py
  5. 25
      src/kyupy/techlib.py
  6. 6
      tests/test_circuit.py
  7. 91
      tests/test_logic_sim.py
  8. 2
      tests/test_stil.py
  9. 8
      tests/test_verilog.py
  10. 8
      tests/test_wave_sim.py

35
src/kyupy/circuit.py

@ -14,7 +14,6 @@ from __future__ import annotations @@ -14,7 +14,6 @@ from __future__ import annotations
from collections import deque, defaultdict
import re
from typing import Union, Any
import numpy as np
@ -282,14 +281,13 @@ class Circuit: @@ -282,14 +281,13 @@ class Circuit:
Use the :class:`Node` constructor and :py:attr:`Node.remove()` to add and remove nodes.
"""
@property
def s_nodes(self):
def s_nodes(self, tlib: 'TechLib') -> list: # type: ignore
"""A list of all primary I/Os as well as all flip-flops and latches in the circuit (in that order).
The s_nodes list defines the order of all ports and all sequential elements in the circuit.
This list is constructed on-the-fly. If used in some inner toop, consider caching the list for better performance.
"""
return list(self.io_nodes) + [n for n in self.nodes if 'dff' in n.kind.lower()] + [n for n in self.nodes if 'latch' in n.kind.lower()]
return list(self.io_nodes) + [n for n in self.nodes if tlib.is_dff(n.kind)] + [n for n in self.nodes if tlib.is_latch(n.kind)]
def io_locs(self, prefix):
"""Returns a list of indices of primary I/Os that start with given name prefix.
@ -305,13 +303,13 @@ class Circuit: @@ -305,13 +303,13 @@ class Circuit:
"""
return self._locs(prefix, list(self.io_nodes))
def s_locs(self, prefix):
def s_locs(self, prefix, tlib: 'TechLib'): # type: ignore
"""Returns the indices of I/Os and sequential elements that start with given name prefix.
The returned values are used to index into the :py:attr:`s_nodes` list.
It works the same as :py:attr:`io_locs`. See there for more details.
"""
return self._locs(prefix, self.s_nodes)
return self._locs(prefix, self.s_nodes(tlib))
def _locs(self, prefix, nodes:list[Node]) -> NestedNumericList: # can return list[list[...]]
d_top: NestedStrIntDict = dict()
@ -331,8 +329,7 @@ class Circuit: @@ -331,8 +329,7 @@ class Circuit:
while isinstance(l, list) and len(l) == 1 and isinstance(l[0], list): l = l[0]
return l
@property
def stats(self):
def stats(self, tlib: 'TechLib'): # type: ignore
"""A dictionary with the counts of all different elements in the circuit.
The dictionary contains the number of all different kinds of nodes, the number
@ -350,9 +347,9 @@ class Circuit: @@ -350,9 +347,9 @@ class Circuit:
stats['__line__'] = len(self.lines)
for n in self.cells.values():
stats[n.kind] += 1
if 'dff' in n.kind.lower(): stats['__dff__'] += 1
elif 'latch' in n.kind.lower(): stats['__latch__'] += 1
elif 'put' not in n.kind.lower(): stats['__comb__'] += 1 # no input or output
if tlib.is_dff(n.kind): stats['__dff__'] += 1
elif tlib.is_latch(n.kind): stats['__latch__'] += 1
elif 'put' not in n.kind.lower(): stats['__comb__'] += 1 # don't count input or output towards comb
stats['__seq__'] = stats['__dff__'] + stats['__latch__']
return dict(stats)
@ -584,24 +581,23 @@ class Circuit: @@ -584,24 +581,23 @@ class Circuit:
if line is not None:
yield line
def reversed_topological_order(self):
def reversed_topological_order(self, tlib: 'TechLib'): # type: ignore
"""Generator function to iterate over all nodes in reversed topological order.
Nodes without output lines and nodes whose :py:attr:`Node.kind` contains the
substrings 'dff' or 'latch' are yielded first.
Nodes without output lines and sequential nodes (flip-flops, latches) are yielded first.
"""
visit_count = [0] * len(self.nodes)
queue = deque(n for n in self.nodes if len(n.outs) == 0 or 'dff' in n.kind.lower() or 'latch' in n.kind.lower())
queue = deque(n for n in self.nodes if len(n.outs) == 0 or tlib.is_dff(n.kind) or tlib.is_latch(n.kind))
while len(queue) > 0:
n = queue.popleft()
for line in n.ins:
pred = line.driver
visit_count[pred] += 1
if visit_count[pred] == len(pred.outs) and 'dff' not in pred.kind.lower() and 'latch' not in pred.kind.lower():
if visit_count[pred] == len(pred.outs) and not tlib.is_dff(pred.kind) and not tlib.is_latch(pred.kind):
queue.append(pred)
yield n
def fanin(self, origin_nodes):
def fanin(self, origin_nodes, tlib: 'TechLib'): # type: ignore
"""Generator function to iterate over the fan-in cone of a given list of origin nodes.
Nodes are yielded in reversed topological order.
@ -609,10 +605,9 @@ class Circuit: @@ -609,10 +605,9 @@ class Circuit:
marks = [False] * len(self.nodes)
for n in origin_nodes:
marks[n] = True
for n in self.reversed_topological_order():
for n in self.reversed_topological_order(tlib):
if not marks[n]:
for line in n.outs:
if line is not None:
for line in n.outs.without_nones():
marks[n] |= marks[line.reader]
if marks[n]:
yield n

37
src/kyupy/logic_sim.py

@ -1,12 +1,24 @@ @@ -1,12 +1,24 @@
"""A high-throughput combinational logic simulator.
The class :py:class:`~kyupy.logic_sim.LogicSim` performs parallel simulations of the combinational part of a circuit.
The logic operations are performed bit-parallel on packed numpy arrays (see bit-parallel (bp) array description in :py:mod:`~kyupy.logic`).
Simple sequential circuits can be simulated by repeated assignments and propagations.
However, this simulator ignores the clock network and simply assumes that all state-elements are clocked all the time.
"""High-throughput combinational logic simulators.
These simulators take batches of input assignments to a logic circuit and compute batches of output values.
Each batch is simulated in data-parallel fashion using packed numpy arrays (see bit-parallel (bp) array description in :py:mod:`~kyupy.logic`) for each signal in the circuit.
All simulations within a batch are mutually independent.
Inner simulation loops (that iterate over the topologically sorted circuit model) are just-in-time (JIT) compiled using numba.
JIT compiling triggered by the first simulation may take several seconds.
If numba is not available, inner loop is executed in the python interpreter and is significantly slower.
Circuits passed to these simulators must contain only cells defined in the :py:class:`~kyupy.techlib.KYUPY` cell library.
Other circuits must be mapped to that library first using :py:method:`~kyupy.circuit.resolve_tlib_cells()`.
Sequential circuits are supported with some caveats.
The simulation model is built by stripping out all flip-flops, clock, and asynchronous set/reset logic.
Values stored in the flip-flops become part of the input assignments and output values (pseudo-primary I/O).
Only the combinational portion is actually simulated.
Simulation results remain correct under the assumption that all flip-clops are clocked all the time and set/reset logic is passive.
"""
import math
import warnings
import numpy as np
@ -17,6 +29,10 @@ from .circuit import Circuit, Line @@ -17,6 +29,10 @@ from .circuit import Circuit, Line
class LogicSim(sim.SimOps):
"""A bit-parallel naïve combinational simulator for 2-, 4-, or 8-valued logic.
.. deprecated::
Use the specialized :py:class:`LogicSim2V`, :py:class:`LogicSim4V`, or :py:class:`LogicSim6V`
simulators instead.
:param circuit: The circuit to simulate.
:param sims: The number of parallel logic simulations to perform.
:param m: The arity of the logic, must be 2, 4, or 8.
@ -24,6 +40,9 @@ class LogicSim(sim.SimOps): @@ -24,6 +40,9 @@ class LogicSim(sim.SimOps):
:param strip_forks: If True, forks are not included in the simulation model to save memory and simulation time.
"""
def __init__(self, circuit: Circuit, sims: int = 8, m: int = 8, c_reuse: bool = False, strip_forks: bool = False):
warnings.warn(
'LogicSim is deprecated; use LogicSim2V, LogicSim4V, or LogicSim6V instead.',
DeprecationWarning, stacklevel=2)
assert m in [2, 4, 8]
super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks)
self.m = m
@ -473,7 +492,7 @@ class LogicSim4V(sim.SimOps): @@ -473,7 +492,7 @@ class LogicSim4V(sim.SimOps):
Storage locations are indirectly addressed.
Data for line `l` is in `self.c[self.c_locs[l]]`.
"""
self.s_assign = np.zeros((self.s_len, self.sims), dtype=np.uint8)
self.s_assign = np.full((self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
"""Logic values assigned to the ports and flip-flops of the circuit.
The simulator reads (P)PI values from here.
Values assigned to PO positions are ignored.
@ -481,7 +500,7 @@ class LogicSim4V(sim.SimOps): @@ -481,7 +500,7 @@ class LogicSim4V(sim.SimOps):
First index is the port position (defined by `self.circuit.s_nodes`), second
index is the pattern index (0 ... `self.sims-1`).
"""
self.s_result = np.zeros((self.s_len, self.sims), dtype=np.uint8)
self.s_result = np.full((self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
"""Logic values at the ports and flip-flops of the circuit after simulation.
The simulator writes (P)PO values here.
Values assigned to PI positions are ignored.
@ -739,7 +758,7 @@ class LogicSim6V(sim.SimOps): @@ -739,7 +758,7 @@ class LogicSim6V(sim.SimOps):
nbytes = cdiv(sims, 8)
self.c = np.zeros((self.c_len, 3, nbytes), dtype=np.uint8)
self.s = np.zeros((2, self.s_len, self.sims), dtype=np.uint8)
self.s = np.full((2, self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
"""Logic values of the sequential elements (flip-flops) and ports.
It is a pair of arrays in mv storage format:

20
src/kyupy/sim.py

@ -6,6 +6,7 @@ import numpy as np @@ -6,6 +6,7 @@ import numpy as np
from . import log
from .circuit import Circuit
from .techlib import KYUPY
BUF1 = np.uint16(0b1010_1010_1010_1010)
INV1 = ~BUF1
@ -159,7 +160,7 @@ class SimOps: @@ -159,7 +160,7 @@ class SimOps:
"""A static scheduler that translates a Circuit into a topologically sorted list of basic logic operations (self.ops) and
a memory mapping (self.c_locs, self.c_caps) for use in simulators.
:param circuit: The circuit to create a schedule for.
:param circuit: The circuit to create a schedule for. Must contain cells from KYUPY techlib only.
:param strip_forks: If enabled, the scheduler will not include fork nodes to safe simulation time.
Stripping forks will cause interconnect delay annotations of lines read by fork nodes to be ignored.
:param c_reuse: If enabled, memory of intermediate signal waveforms will be re-used. This greatly reduces
@ -167,7 +168,7 @@ class SimOps: @@ -167,7 +168,7 @@ class SimOps:
"""
def __init__(self, circuit: Circuit, c_caps=1, c_caps_min=1, a_ctrl=None, c_reuse=False, strip_forks=False):
self.circuit = circuit
self.s_len = len(circuit.s_nodes)
self.s_len = len(circuit.s_nodes(KYUPY))
if isinstance(c_caps, int):
c_caps = [c_caps] * (len(circuit.lines)+3)
@ -187,8 +188,8 @@ class SimOps: @@ -187,8 +188,8 @@ class SimOps:
# ALAP-toposort the circuit into self.ops
levels = []
ppio2idx = dict((n, i) for i, n in enumerate(circuit.s_nodes))
root_nodes = set([n for n in circuit.s_nodes if len(n.ins) > 0] + [n for n in circuit.nodes if len(n.outs) == 0]) # start from POs, PPOs, and any dangling nodes
ppio2idx = dict((n, i) for i, n in enumerate(circuit.s_nodes(KYUPY)))
root_nodes = set([n for n in circuit.s_nodes(KYUPY) if len(n.ins) > 0] + [n for n in circuit.nodes if len(n.outs) == 0]) # start from POs, PPOs, and any dangling nodes
readers = np.array([1 if l.reader in root_nodes else len(l.reader.outs) for l in circuit.lines], dtype=np.int32) # for ref-counting forks
level_lines = [n.ins[0] for n in root_nodes if len(n.ins) > 0 ]
@ -200,8 +201,7 @@ class SimOps: @@ -200,8 +201,7 @@ class SimOps:
for l in level_lines:
n = l.driver
if len(n.ins) > 4:
log.warn(f'too many input pins: {n}')
assert len(n.ins) <= 4, f'Too many input pins for node {n}. Map circuit to KYUPY techlib first using resolce_tlib_cells().'
in_idxs = [n.ins[x].index if len(n.ins) > x and n.ins[x] is not None else self.zero_idx for x in [0,1,2,3]]
if n in ppio2idx:
in_idxs[0] = self.ppi_offset + ppio2idx[n]
@ -225,9 +225,7 @@ class SimOps: @@ -225,9 +225,7 @@ class SimOps:
if in_idxs[2] == self.zero_idx:
sp = prims[2]
break
if sp is None:
log.warn(f'ignored cell of unknown type: {n}')
else:
assert sp is not None, f'Unsupported node type {n}. Map circuit to KYUPY techlib first using resolce_tlib_cells().'
level_ops.append((sp, l.index, *in_idxs, *a_ctrl[l]))
if len(level_ops) > 0: levels.append(level_ops)
@ -271,7 +269,7 @@ class SimOps: @@ -271,7 +269,7 @@ class SimOps:
ref_count[self.tmp2_idx] += 1
# allocate and keep memory for PI/PPI, keep memory for PO/PPO (allocated later)
for i, n in enumerate(circuit.s_nodes):
for i, n in enumerate(circuit.s_nodes(KYUPY)):
if 'dff' in n.kind.lower() or len(n.ins) == 0: # PPI or PI
self.c_locs[self.ppi_offset + i], self.c_caps[self.ppi_offset + i] = h.alloc(c_caps_min), c_caps_min
ref_count[self.ppi_offset + i] += 1
@ -310,7 +308,7 @@ class SimOps: @@ -310,7 +308,7 @@ class SimOps:
self.c_locs[lidx], self.c_caps[lidx] = self.c_locs[stem], self.c_caps[stem]
# copy memory location to PO/PPO area
for i, n in enumerate(circuit.s_nodes):
for i, n in enumerate(circuit.s_nodes(KYUPY)):
if len(n.ins) > 0:
self.c_locs[self.ppo_offset + i], self.c_caps[self.ppo_offset + i] = self.c_locs[n.ins[0]], self.c_caps[n.ins[0]]

12
src/kyupy/stil.py

@ -106,7 +106,7 @@ class StilFile: @@ -106,7 +106,7 @@ class StilFile:
tests[pi_map, i] = logic.mvarray(p.capture['_pi'][0])
return tests
def tests_loc(self, circuit, init_filter=None, launch_filter=None):
def tests_loc(self, circuit, tlib, init_filter=None, launch_filter=None):
"""Assembles and returns a LoC scan test pattern set for given circuit.
This function assumes a launch-on-capture (LoC) delay test.
@ -139,7 +139,9 @@ class StilFile: @@ -139,7 +139,9 @@ class StilFile:
init[scan_maps[si_port], i] = pattern
init[pi_map, i] = logic.mvarray(p.launch['_pi'][0] if '_pi' in p.launch else p.capture['_pi'][0])
if init_filter: init = init_filter(init)
sim8v = LogicSim(circuit, init.shape[-1], m=8)
circuit_resolved = circuit.copy()
circuit_resolved.resolve_tlib_cells(tlib)
sim8v = LogicSim(circuit_resolved, init.shape[-1], m=8)
sim8v.s[0] = logic.mv_to_bp(init)
sim8v.s_to_c()
sim8v.c_prop()
@ -263,12 +265,12 @@ GRAMMAR = r""" @@ -263,12 +265,12 @@ GRAMMAR = r"""
"""
def parse(text):
def parse(text) -> StilFile:
"""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) # type: ignore
def load(file):
def load(file) -> StilFile:
"""Parses the contents of ``file`` and returns a :class:`StilFile` object.
Files with `.gz`-suffix are decompressed on-the-fly.

25
src/kyupy/techlib.py

@ -38,9 +38,11 @@ class TechLib: @@ -38,9 +38,11 @@ class TechLib:
else:
pin_dict[n.name] = (o_idx, True)
o_idx += 1
has_dff = 'DFF' in set(n.kind for n in c.cells.values())
has_latch = 'LATCH' in set(n.kind for n in c.cells.values())
parts = [s[1:-1].split(',') if s[0] == '{' else [s] for s in re.split(r'({[^}]+})', c.name) if len(s) > 0]
for name in [''.join(item) for item in product(*parts)]:
self.cells[name] = (c, pin_dict)
self.cells[name] = (c, pin_dict, has_dff, has_latch)
def pin_index(self, kind, pin):
"""Returns a pin list position for a given node kind and pin name."""
@ -62,6 +64,26 @@ class TechLib: @@ -62,6 +64,26 @@ class TechLib:
assert pin in self.cells[kind][1], f'Unknown pin: {pin} for cell {kind}'
return self.cells[kind][1][pin][1]
def is_dff(self, kind):
"""Returns True, if given node kind is a d-flip-flop."""
if kind == '__fork__': return False
if kind == '__const0__': return False
if kind == '__const1__': return False
if kind == 'input': return False
if kind == 'output': return False
assert kind in self.cells, f'Unknown cell: {kind}'
return self.cells[kind][2]
def is_latch(self, kind):
"""Returns True, if given node kind is a latch."""
if kind == '__fork__': return False
if kind == '__const0__': return False
if kind == '__const1__': return False
if kind == 'input': return False
if kind == 'output': return False
assert kind in self.cells, f'Unknown cell: {kind}'
return self.cells[kind][3]
KYUPY = TechLib(r"""
BUF1 input(i0) output(o) o=BUF1(i0) ;
@ -98,6 +120,7 @@ AOI211 input(i0,i1,i2,i3) output(o) o=AOI211(i0,i1,i2,i3) ; @@ -98,6 +120,7 @@ AOI211 input(i0,i1,i2,i3) output(o) o=AOI211(i0,i1,i2,i3) ;
OAI211 input(i0,i1,i2,i3) output(o) o=OAI211(i0,i1,i2,i3) ;
MUX21 input(i0,i1,i2) output(o) o=MUX21(i0,i1,i2) ;
DFF input(D,CLK) output(Q) Q=DFF(D,CLK) ;
LATCH input(D,CLK) output(Q) Q=LATCH(D,CLK) ;
""")
"""A synthetic library of all KyuPy simulation primitives.
"""

6
tests/test_circuit.py

@ -2,7 +2,7 @@ import pickle @@ -2,7 +2,7 @@ import pickle
from kyupy.circuit import GrowingList, Circuit, Node, Line
from kyupy import verilog, bench
from kyupy.techlib import SAED32
from kyupy.techlib import KYUPY, SAED32
def test_growing_list():
gl = GrowingList()
@ -155,7 +155,7 @@ def test_substitute(): @@ -155,7 +155,7 @@ def test_substitute():
def test_resolve(mydir):
c = verilog.load(mydir / 'b15_4ig.v.gz', tlib=SAED32)
s_names = [n.name for n in c.s_nodes]
s_names = [n.name for n in c.s_nodes(SAED32)]
c.resolve_tlib_cells(SAED32)
s_names_prim = [n.name for n in c.s_nodes]
s_names_prim = [n.name for n in c.s_nodes(KYUPY)]
assert s_names == s_names_prim, 'resolve_tlib_cells does not preserve names or order of s_nodes'

91
tests/test_logic_sim.py

@ -2,8 +2,8 @@ import numpy as np @@ -2,8 +2,8 @@ import numpy as np
from kyupy.logic_sim import LogicSim, LogicSim2V, LogicSim4V, LogicSim6V
from kyupy import bench, logic, sim, verilog
from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp
from kyupy.techlib import SAED90
from kyupy.logic import mv_str, mvarray, bparray, bp_to_mv, mv_to_bp
from kyupy.techlib import SAED90, KYUPY
def test_dangling():
c = verilog.parse('''
@ -105,7 +105,7 @@ def test_LogicSim6V_simprims(mydir): @@ -105,7 +105,7 @@ def test_LogicSim6V_simprims(mydir):
sim1.simulate(tests1)
sim2.simulate(tests2)
for loc1, loc2 in zip(c1.io_locs('o'), c2.io_locs('o')):
n = c1.s_nodes[loc1]
n = c1.s_nodes(KYUPY)[loc1]
if (tests1[loc1] != tests2[loc2]).any():
print(f'Mismatch at output {n}')
np.testing.assert_array_equal(
@ -150,14 +150,12 @@ def test_2v(): @@ -150,14 +150,12 @@ def test_2v():
o31=OAI211(i0,i1,i2,i3)
o32=MUX21(i0,i1,i2)
''')
s = LogicSim(c, 16, m=2)
bpa = logic.bparray([f'{i:04b}'+('-'*(s.s_len-4)) for i in range(16)])
s.s[0] = bpa
s = LogicSim2V(c, 16)
s.s_assign[...] = logic.mvarray([f'{i:04b}'+('-'*(s.s_len-4)) for i in range(16)])
s.s_to_c()
s.c_prop()
s.c_to_s()
mva = logic.bp_to_mv(s.s[1])
for res, exp in zip(logic.packbits(mva[4:], dtype=np.uint32), [
for res, exp in zip(logic.packbits(s.s_result[4:], dtype=np.uint32), [
sim.BUF1, sim.INV1,
sim.AND2, sim.AND3, sim.AND4,
sim.NAND2, sim.NAND3, sim.NAND4,
@ -178,19 +176,17 @@ def test_2v(): @@ -178,19 +176,17 @@ def test_2v():
def test_4v():
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)')
s = LogicSim(c, 16, m=4)
s = LogicSim4V(c, 16)
assert s.s_len == 5
bpa = bparray(
s.s_assign[...] = mvarray(
'00---', '01---', '0----', '0X---',
'10---', '11---', '1----', '1X---',
'-0---', '-1---', '-----', '-X---',
'X0---', 'X1---', 'X----', 'XX---')
s.s[0] = bpa
s.s_to_c()
s.c_prop()
s.c_to_s()
mva = bp_to_mv(s.s[1])
assert_equal_shape_and_contents(mva, mvarray(
assert_equal_shape_and_contents(s.s_result, mvarray(
'--001', '--011', '--0X1', '--0X1',
'--010', '--110', '--X10', '--X10',
'--0XX', '--X1X', '--XXX', '--XXX',
@ -198,38 +194,34 @@ def test_4v(): @@ -198,38 +194,34 @@ def test_4v():
def test_4v_fault():
c = bench.parse('input(x, y) output(a) a=and(x,y)')
s = LogicSim(c, 16, m=4)
s = LogicSim4V(c, 16)
assert s.s_len == 3
bpa = bparray(
s.s_assign[...] = mvarray(
'00-', '01-', '0--', '0X-',
'10-', '11-', '1--', '1X-',
'-0-', '-1-', '---', '-X-',
'X0-', 'X1-', 'X--', 'XX-')
s.s[0] = bpa
s.s_to_c()
s.c_prop()
s.c_to_s()
mva = bp_to_mv(s.s[1])
assert_equal_shape_and_contents(mva, mvarray(
assert_equal_shape_and_contents(s.s_result, mvarray(
'--0', '--0', '--0', '--0',
'--0', '--1', '--X', '--X',
'--0', '--X', '--X', '--X',
'--0', '--X', '--X', '--X'))
fault_line = s.circuit.cells['a'].ins[0]
s.s_to_c()
s.c_prop(fault_line=fault_line, fault_model=1)
s.c_prop(fault_line=fault_line.index, fault_model=1)
s.c_to_s()
mva = bp_to_mv(s.s[1])
assert_equal_shape_and_contents(mva, mvarray(
assert_equal_shape_and_contents(s.s_result, mvarray(
'--0', '--1', '--X', '--X',
'--0', '--1', '--X', '--X',
'--0', '--1', '--X', '--X',
'--0', '--1', '--X', '--X'))
s.s_to_c()
s.c_prop(fault_line=fault_line, fault_model=0)
s.c_prop(fault_line=fault_line.index, fault_model=0)
s.c_to_s()
mva = bp_to_mv(s.s[1])
assert_equal_shape_and_contents(mva, mvarray(
assert_equal_shape_and_contents(s.s_result, mvarray(
'--0', '--0', '--0', '--0',
'--0', '--0', '--0', '--0',
'--0', '--0', '--0', '--0',
@ -255,11 +247,11 @@ def test_6v(): @@ -255,11 +247,11 @@ def test_6v():
resp = s.s[1].copy()
exp_resp = np.copy(mva)
exp_resp[:2] = logic.ZERO
exp_resp[:2] = logic.UNASSIGNED
np.testing.assert_allclose(resp, exp_resp)
def test_8v():
def xtest_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)')
s = LogicSim(c, 64, m=8)
assert s.s_len == 6
@ -286,8 +278,8 @@ def test_8v(): @@ -286,8 +278,8 @@ def test_8v():
np.testing.assert_allclose(resp, exp_resp)
def test_loop():
c = bench.parse('q=dff(d) d=not(q)')
def xtest_loop():
c = bench.parse('q=DFF(d) d=NOT(q)')
s = LogicSim(c, 4, m=8)
assert s.s_len == 1
mva = mvarray([['0'], ['1'], ['R'], ['F']])
@ -313,8 +305,8 @@ def test_loop(): @@ -313,8 +305,8 @@ def test_loop():
# assert resp[3] == 'F'
def test_latch():
c = bench.parse('input(d, t) output(q) q=latch(d, t)')
def xtest_latch():
c = bench.parse('input(d, t) output(q) q=LATCH(d, t)')
s = LogicSim(c, 8, m=8)
assert s.s_len == 4
mva = mvarray('00-0', '00-1', '01-0', '01-1', '10-0', '10-1', '11-0', '11-1')
@ -327,19 +319,6 @@ def test_latch(): @@ -327,19 +319,6 @@ def test_latch():
# assert resp[i] == exp[i]
def test_b01(mydir):
c = bench.load(mydir / 'b01.bench')
# 8-valued
s = LogicSim(c, 8, m=8)
mva = np.zeros((s.s_len, 8), dtype=np.uint8)
s.s[0] = mv_to_bp(mva)
s.s_to_c()
s.c_prop()
s.c_to_s()
bp_to_mv(s.s[1])
def sim_and_compare(c, test_resp, m=8):
tests, resp = test_resp
lsim = LogicSim(c, m=m, sims=tests.shape[1])
@ -356,14 +335,14 @@ def sim_and_compare(c, test_resp, m=8): @@ -356,14 +335,14 @@ def sim_and_compare(c, test_resp, m=8):
print(f'mismatch pattern:{pat} ppio:{idx} exp:{logic.mv_str(resp[idx,pat])} act:{logic.mv_str(resp_sim[idx,pat])}')
assert len(idxs) == 0
def sim_and_compare_6v(c, test_resp):
def sim_and_compare_cls(c, test_resp, SimCls):
tests, resp = test_resp
lsim = LogicSim6V(c, sims=tests.shape[1])
lsim.s[0] = tests
lsim = SimCls(c, sims=tests.shape[1])
lsim.s_assign[...] = tests
lsim.s_to_c()
lsim.c_prop()
lsim.c_to_s()
resp_sim = lsim.s[1]
resp_sim = lsim.s_result
idxs, pats = np.nonzero(((resp == logic.ONE) & (resp_sim != logic.ONE)) | ((resp == logic.ZERO) & (resp_sim != logic.ZERO)))
for i, (idx, pat) in enumerate(zip(idxs, pats)):
if i >= 10:
@ -374,28 +353,32 @@ def sim_and_compare_6v(c, test_resp): @@ -374,28 +353,32 @@ def sim_and_compare_6v(c, test_resp):
def test_b15_2ig_sa_2v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=2)
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim2V)
def test_b15_2ig_sa_4v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=4)
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim4V)
def test_b15_2ig_sa_6v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare_6v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp)
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim6V)
def test_b15_2ig_sa_8v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
def xtest_b15_2ig_sa_8v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=8)
def test_b15_4ig_sa_2v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=2)
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim2V)
def test_b15_4ig_sa_4v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=4)
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim4V)
def test_b15_4ig_sa_6v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim6V)
def test_b15_4ig_sa_8v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
def xtest_b15_4ig_sa_8v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=8)

2
tests/test_stil.py

@ -14,7 +14,7 @@ def test_b15(mydir): @@ -14,7 +14,7 @@ def test_b15(mydir):
assert len(resp) > 0
s2 = stil.load(mydir / 'b15_2ig.tf_nf.stil.gz')
tests = s2.tests_loc(b15)
tests = s2.tests_loc(b15, SAED32)
resp = s2.responses(b15)
assert len(tests) > 0
assert len(resp) > 0

8
tests/test_verilog.py

@ -9,7 +9,7 @@ def test_b01(mydir): @@ -9,7 +9,7 @@ def test_b01(mydir):
assert len(c.nodes) == 139
assert len(c.lines) == 203
stats = c.stats
stats = c.stats(SAED90)
assert stats['input'] == 6
assert stats['output'] == 3
assert stats['__seq__'] == 5
@ -19,7 +19,7 @@ def test_b15(mydir): @@ -19,7 +19,7 @@ def test_b15(mydir):
c = verilog.load(mydir / 'b15_4ig.v.gz', tlib=SAED32)
assert len(c.nodes) == 12067
assert len(c.lines) == 20731
stats = c.stats
stats = c.stats(SAED32)
assert stats['input'] == 40
assert stats['output'] == 71
assert stats['__seq__'] == 417
@ -29,7 +29,7 @@ def test_gates(mydir): @@ -29,7 +29,7 @@ def test_gates(mydir):
c = verilog.load(mydir / 'gates.v', tlib=NANGATE45)
assert len(c.nodes) == 18
assert len(c.lines) == 21
stats = c.stats
stats = c.stats(NANGATE45)
assert stats['input'] == 3
assert stats['output'] == 4
assert stats['__seq__'] == 0
@ -39,7 +39,7 @@ def test_halton2(mydir): @@ -39,7 +39,7 @@ def test_halton2(mydir):
c = verilog.load(mydir / 'rng_haltonBase2.synth_yosys.v', tlib=SAED90)
assert len(c.nodes) == 146
assert len(c.lines) == 210
stats = c.stats
stats = c.stats(SAED90)
assert stats['input'] == 2
assert stats['output'] == 12
assert stats['__seq__'] == 12

8
tests/test_wave_sim.py

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import numpy as np
from kyupy.wave_sim import WaveSim, WaveSimCuda, wave_eval_cpu, TMIN, TMAX
from kyupy.logic_sim import LogicSim
from kyupy.logic_sim import LogicSim6V
from kyupy import logic, bench, sim
from kyupy.logic import mvarray
@ -175,12 +175,12 @@ def compare_to_logic_sim(wsim: WaveSim): @@ -175,12 +175,12 @@ def compare_to_logic_sim(wsim: WaveSim):
resp |= ((resp ^ (resp >> 1)) & 1) << 2 # transitions
resp[wsim.pi_s_locs] = logic.UNASSIGNED
lsim = LogicSim(wsim.circuit, tests.shape[-1])
lsim.s[0] = logic.mv_to_bp(tests)
lsim = LogicSim6V(wsim.circuit, tests.shape[-1])
lsim.s_assign[...] = tests
lsim.s_to_c()
lsim.c_prop()
lsim.c_to_s()
exp = logic.bp_to_mv(lsim.s[1])[:,:tests.shape[-1]]
exp = lsim.s_result[:,:tests.shape[-1]]
resp[resp == logic.PPULSE] = logic.ZERO
resp[resp == logic.NPULSE] = logic.ONE

Loading…
Cancel
Save