Compare commits
2
Commits
f0c10633bf
..
v0.0.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3193df726f | ||
|
|
d247115744 |
No files matched your search
+2
-2
@@ -20,11 +20,11 @@ sys.path.insert(0, os.path.abspath('../src'))
|
|||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'KyuPy'
|
project = 'KyuPy'
|
||||||
copyright = '2020-2026, Stefan Holst'
|
copyright = '2020-2025, Stefan Holst'
|
||||||
author = 'Stefan Holst'
|
author = 'Stefan Holst'
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = '0.0.7'
|
release = '0.0.6'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|||||||
+2
-4
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "kyupy"
|
name = "kyupy"
|
||||||
version = "0.0.7"
|
version = "0.0.6"
|
||||||
authors = [
|
authors = [
|
||||||
{ name="Stefan Holst", email="mail@s-holst.de" },
|
{ name="Stefan Holst", email="mail@s-holst.de" },
|
||||||
]
|
]
|
||||||
@@ -33,10 +33,8 @@ build-backend = "hatchling.build"
|
|||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"myst-parser>=4.0.1",
|
"myst-parser>=4.0.1",
|
||||||
|
"pytest>=9.0.1",
|
||||||
"sphinx>=8.1.3",
|
"sphinx>=8.1.3",
|
||||||
"sphinx-rtd-theme>=3.0.2",
|
"sphinx-rtd-theme>=3.0.2",
|
||||||
"pytest>=9.0.1",
|
|
||||||
"ipython>=9.14.1",
|
|
||||||
"jupyter>=1.1.1",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
+18
-30
@@ -12,8 +12,6 @@ import gzip
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
type NestedNumericList = list['int|NestedNumericList']
|
|
||||||
type NestedStrIntDict = dict[str,'int|NestedStrIntDict']
|
|
||||||
|
|
||||||
_pop_count_lut = np.asarray([bin(x).count('1') for x in range(256)])
|
_pop_count_lut = np.asarray([bin(x).count('1') for x in range(256)])
|
||||||
|
|
||||||
@@ -113,9 +111,9 @@ class Timer:
|
|||||||
|
|
||||||
|
|
||||||
class Timers:
|
class Timers:
|
||||||
def __init__(self, t={}): self.timers: dict[str, Timer] = defaultdict(Timer) | t
|
def __init__(self, t={}): self.timers = defaultdict(Timer) | t
|
||||||
def __getitem__(self, name): return self.timers[name]
|
def __getitem__(self, name): return self.timers[name]
|
||||||
def __repr__(self): return '{' + ', '.join([f'{k}: {v:.2f}' for k, v in self.dict().items()]) + '}'
|
def __repr__(self): return '{' + ', '.join([f'{k}: {v}' for k, v in self.timers.items()]) + '}'
|
||||||
def __add__(self, t):
|
def __add__(self, t):
|
||||||
tmr = Timers(self.timers)
|
tmr = Timers(self.timers)
|
||||||
for k, v in t.timers.items(): tmr.timers[k] += v
|
for k, v in t.timers.items(): tmr.timers[k] += v
|
||||||
@@ -123,7 +121,7 @@ class Timers:
|
|||||||
def sum(self):
|
def sum(self):
|
||||||
return sum([v.s for v in self.timers.values()])
|
return sum([v.s for v in self.timers.values()])
|
||||||
def dict(self):
|
def dict(self):
|
||||||
return dict(sorted([(k, v.s) for k, v in self.timers.items()], key=lambda item: -item[1]))
|
return dict([(k, v.s) for k, v in self.timers.items()])
|
||||||
|
|
||||||
|
|
||||||
class Log:
|
class Log:
|
||||||
@@ -200,35 +198,25 @@ class Log:
|
|||||||
"""Log an error message."""
|
"""Log an error message."""
|
||||||
self.log('E', message)
|
self.log('E', message)
|
||||||
|
|
||||||
def progress(self):
|
|
||||||
class Progress:
|
|
||||||
def __init__(self, l): self.l = l
|
|
||||||
def __enter__(self):
|
|
||||||
self.start_time = time.perf_counter()
|
|
||||||
self.lastlog_time = self.start_time
|
|
||||||
self.log_interval = 5
|
|
||||||
return self
|
|
||||||
def update(self, completion: float, msg: str = ''):
|
|
||||||
assert completion > 0 and completion <= 1
|
|
||||||
current_time = time.perf_counter()
|
|
||||||
if current_time > self.lastlog_time + self.log_interval:
|
|
||||||
elapsed_time = current_time - self.start_time
|
|
||||||
total_time = elapsed_time / completion
|
|
||||||
rem_time = total_time - elapsed_time
|
|
||||||
self.l.log(':', f'{msg} - {completion*100:.0f}% done {hr_time(elapsed_time)} elapsed {hr_time(rem_time)} remaining')
|
|
||||||
self.log_interval = min(600, int(self.log_interval*1.5))
|
|
||||||
self.lastlog_time = current_time
|
|
||||||
def __exit__(self, *args): pass
|
|
||||||
return Progress(self)
|
|
||||||
|
|
||||||
def range(self, *args):
|
def range(self, *args):
|
||||||
"""A generator that operates just like the ``range()`` built-in, and also occasionally logs the progress
|
"""A generator that operates just like the ``range()`` built-in, and also occasionally logs the progress
|
||||||
and compute time estimates."""
|
and compute time estimates."""
|
||||||
elems = len(range(*args))
|
elems = len(range(*args))
|
||||||
with self.progress() as p:
|
start_time = time.perf_counter()
|
||||||
for elem, i in enumerate(range(*args)):
|
lastlog_time = start_time
|
||||||
yield i
|
log_interval = 5
|
||||||
p.update((elem + 1) / elems)
|
for elem, i in enumerate(range(*args)):
|
||||||
|
yield i
|
||||||
|
current_time = time.perf_counter()
|
||||||
|
if current_time > lastlog_time + log_interval:
|
||||||
|
done = (elem + 1) / elems
|
||||||
|
elapsed_time = current_time - start_time
|
||||||
|
total_time = elapsed_time / done
|
||||||
|
rem_time = total_time - elapsed_time
|
||||||
|
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))
|
||||||
|
lastlog_time = current_time
|
||||||
|
|
||||||
|
|
||||||
log = Log()
|
log = Log()
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
"""A parser and dumper for Atalanta's plain-text test pattern format (*.test).
|
|
||||||
|
|
||||||
A *.test file contains one test pattern per line in the form ``<index>: <bits>``,
|
|
||||||
where ``<bits>`` is a string of ``'0'`` and ``'1'`` characters giving the values of
|
|
||||||
the primary inputs (in circuit order). Lines starting with ``'*'`` are comments.
|
|
||||||
|
|
||||||
This parser also accepts files without ``<index>:`` and patterns elements in 8-valued
|
|
||||||
logic such as ``'R'``, ``'F'``, ``'X'``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from . import readtext, logic
|
|
||||||
from .circuit import Circuit
|
|
||||||
|
|
||||||
_pattern_re = re.compile(r'^\s*(?:\d+:\s*)?([01HLRFX-]+)\s*$')
|
|
||||||
|
|
||||||
|
|
||||||
class TestFile:
|
|
||||||
"""Intermediate representation of a *.test pattern file.
|
|
||||||
|
|
||||||
:param text: The contents of a *.test file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, text: str):
|
|
||||||
patterns = []
|
|
||||||
for line in text.splitlines():
|
|
||||||
if (m := _pattern_re.match(line)) is not None:
|
|
||||||
patterns.append(logic.mvarray(m.group(1)))
|
|
||||||
|
|
||||||
self.patterns = np.stack(patterns, axis=-1) if patterns \
|
|
||||||
else np.zeros((0, 0), dtype=np.uint8)
|
|
||||||
"""The parsed patterns as a mvarray (see :py:mod:`~kyupy.logic`).
|
|
||||||
|
|
||||||
The second-to-last axis goes along primary inputs, the last axis goes along patterns.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def tests(self, circuit: Circuit):
|
|
||||||
"""Assembles and returns the test pattern set for the given circuit.
|
|
||||||
|
|
||||||
:param circuit: The circuit to assemble the patterns for. The patterns will follow the
|
|
||||||
:py:attr:`~kyupy.circuit.Circuit.io_nodes` ordering of the circuit.
|
|
||||||
:return: A logic array (see :py:mod:`~kyupy.logic`). The values for primary inputs are
|
|
||||||
filled, all other values are left unassigned.
|
|
||||||
"""
|
|
||||||
pi_locs = [i for i, n in enumerate(circuit.io_nodes) if len(n.ins) == 0]
|
|
||||||
tests = np.full((len(circuit.io_nodes), self.patterns.shape[-1]), logic.UNASSIGNED, dtype=np.uint8)
|
|
||||||
tests[pi_locs] = self.patterns
|
|
||||||
return tests
|
|
||||||
|
|
||||||
|
|
||||||
def parse(text) -> TestFile:
|
|
||||||
"""Parses the given ``text`` and returns a :class:`TestFile` object."""
|
|
||||||
return TestFile(text)
|
|
||||||
|
|
||||||
|
|
||||||
def load(file) -> TestFile:
|
|
||||||
"""Parses the contents of ``file`` and returns a :class:`TestFile` object.
|
|
||||||
|
|
||||||
Files with `.gz`-suffix are decompressed on-the-fly.
|
|
||||||
"""
|
|
||||||
return parse(readtext(file))
|
|
||||||
+4
-28
@@ -5,16 +5,12 @@ Historically it was first used in the
|
|||||||
`ISCAS89 benchmark set <https://people.engr.ncsu.edu/brglez/CBL/benchmarks/ISCAS89/>`_.
|
`ISCAS89 benchmark set <https://people.engr.ncsu.edu/brglez/CBL/benchmarks/ISCAS89/>`_.
|
||||||
Besides loading these benchmarks, this module is also useful for easily constructing simple circuits:
|
Besides loading these benchmarks, this module is also useful for easily constructing simple circuits:
|
||||||
``c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)')``.
|
``c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)')``.
|
||||||
|
|
||||||
This parser automatically translates gate to simulation primitives supported by kyupy.
|
|
||||||
In most cases this means just adding the number of inputs to the name: e.g. NAND -> NAND2, OR -> OR4, INV -> INV1.
|
|
||||||
Since simulators only support at most four-input-gates, gates with more than four inputs are implemented as tree.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from lark import Lark, Transformer
|
from lark import Lark, Transformer
|
||||||
|
|
||||||
from .circuit import Circuit, Node, Line
|
from .circuit import Circuit, Node, Line
|
||||||
from . import readtext, batchrange, log
|
from . import readtext, batchrange
|
||||||
|
|
||||||
def treeify(l, max_degree=4):
|
def treeify(l, max_degree=4):
|
||||||
if len(l) <= max_degree: return l
|
if len(l) <= max_degree: return l
|
||||||
@@ -26,32 +22,12 @@ class BenchTransformer(Transformer):
|
|||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.c = Circuit(name)
|
self.c = Circuit(name)
|
||||||
self.inputs = set()
|
|
||||||
self.outputs = set()
|
|
||||||
|
|
||||||
def start(self, _): return self.c
|
def start(self, _): return self.c
|
||||||
|
|
||||||
def parameters(self, args): return [self.c.get_or_add_fork(str(name)) for name in args if name is not None]
|
def parameters(self, args): return [self.c.get_or_add_fork(str(name)) for name in args if name is not None]
|
||||||
|
|
||||||
def input(self, args):
|
def interface(self, args): self.c.io_nodes.extend(args[0])
|
||||||
for input in args[0]:
|
|
||||||
if input in self.outputs:
|
|
||||||
new_input = self.c.get_or_add_fork(input.name + '~i')
|
|
||||||
Line(self.c, new_input, input)
|
|
||||||
log.warn(f'input-output passthrough, renaming input: {input.name} -> {new_input.name}')
|
|
||||||
input = new_input
|
|
||||||
self.inputs.add(input)
|
|
||||||
self.c.io_nodes.append(input)
|
|
||||||
|
|
||||||
def output(self, args):
|
|
||||||
for output in args[0]:
|
|
||||||
if output in self.inputs:
|
|
||||||
new_output = self.c.get_or_add_fork(output.name + '~o')
|
|
||||||
Line(self.c, output, new_output)
|
|
||||||
log.warn(f'input-output passthrough, renaming output: {output.name} -> {new_output.name}')
|
|
||||||
output = new_output
|
|
||||||
self.outputs.add(output)
|
|
||||||
self.c.io_nodes.append(output)
|
|
||||||
|
|
||||||
def _cell_tree_inner(self, name, kind, inner_kind, drivers):
|
def _cell_tree_inner(self, name, kind, inner_kind, drivers):
|
||||||
cell = Node(self.c, name, f'{kind}{len(drivers)}')
|
cell = Node(self.c, name, f'{kind}{len(drivers)}')
|
||||||
@@ -89,8 +65,8 @@ class BenchTransformer(Transformer):
|
|||||||
GRAMMAR = r"""
|
GRAMMAR = r"""
|
||||||
start: (statement)*
|
start: (statement)*
|
||||||
statement: input | output | assignment
|
statement: input | output | assignment
|
||||||
input: ("INPUT" | "input") parameters
|
input: ("INPUT" | "input") parameters -> interface
|
||||||
output: ("OUTPUT" | "output") parameters
|
output: ("OUTPUT" | "output") parameters -> interface
|
||||||
assignment: NAME "=" NAME parameters
|
assignment: NAME "=" NAME parameters
|
||||||
parameters: "(" [ NAME ( "," NAME )* ] ")"
|
parameters: "(" [ NAME ( "," NAME )* ] ")"
|
||||||
NAME: /[-_a-z0-9]+/i
|
NAME: /[-_a-z0-9]+/i
|
||||||
|
|||||||
+45
-46
@@ -14,10 +14,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections import deque, defaultdict
|
from collections import deque, defaultdict
|
||||||
import re
|
import re
|
||||||
|
from typing import Union, Any
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from . import NestedNumericList, NestedStrIntDict
|
|
||||||
|
|
||||||
class GrowingList[T](list[T]):
|
class GrowingList[T](list[T]):
|
||||||
def __setitem__(self, index, value):
|
def __setitem__(self, index, value):
|
||||||
@@ -280,19 +280,20 @@ class Circuit:
|
|||||||
Use the :class:`Node` constructor and :py:attr:`Node.remove()` to add and remove nodes.
|
Use the :class:`Node` constructor and :py:attr:`Node.remove()` to add and remove nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def s_nodes(self, tlib: 'TechLib') -> list: # type: ignore
|
@property
|
||||||
|
def s_nodes(self):
|
||||||
"""A list of all primary I/Os as well as all flip-flops and latches in the circuit (in that order).
|
"""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.
|
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.
|
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 tlib.is_dff(n.kind)] + [n for n in self.nodes if tlib.is_latch(n.kind)]
|
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()]
|
||||||
|
|
||||||
def io_locs(self, prefix):
|
def io_locs(self, prefix):
|
||||||
"""Returns a list of indices of primary I/Os that start with given name prefix.
|
"""Returns the indices of primary I/Os that start with given name prefix.
|
||||||
|
|
||||||
The returned values are used to index into the :py:attr:`io_nodes` array.
|
The returned values are used to index into the :py:attr:`io_nodes` array.
|
||||||
If only one I/O cell matches the given prefix, a list with a single integer is returned.
|
If only one I/O cell matches the given prefix, a single integer is returned.
|
||||||
If a bus matches the given prefix, a sorted list of indices is returned.
|
If a bus matches the given prefix, a sorted list of indices is returned.
|
||||||
Busses are identified by integers in the cell names following the given prefix.
|
Busses are identified by integers in the cell names following the given prefix.
|
||||||
Lists for bus indices are sorted from LSB (e.g. :code:`data[0]`) to MSB (e.g. :code:`data[31]`).
|
Lists for bus indices are sorted from LSB (e.g. :code:`data[0]`) to MSB (e.g. :code:`data[31]`).
|
||||||
@@ -302,16 +303,16 @@ class Circuit:
|
|||||||
"""
|
"""
|
||||||
return self._locs(prefix, list(self.io_nodes))
|
return self._locs(prefix, list(self.io_nodes))
|
||||||
|
|
||||||
def s_locs(self, prefix, tlib: 'TechLib'): # type: ignore
|
def s_locs(self, prefix):
|
||||||
"""Returns the indices of I/Os and sequential elements that start with given name prefix.
|
"""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.
|
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.
|
It works the same as :py:attr:`io_locs`. See there for more details.
|
||||||
"""
|
"""
|
||||||
return self._locs(prefix, self.s_nodes(tlib))
|
return self._locs(prefix, self.s_nodes)
|
||||||
|
|
||||||
def _locs(self, prefix, nodes:list[Node]) -> NestedNumericList: # can return list[list[...]]
|
def _locs(self, prefix, nodes:list[Node]) -> Node|list[Any]: # can return list[list[...]]
|
||||||
d_top: NestedStrIntDict = dict()
|
d_top = dict()
|
||||||
for i, n in enumerate(nodes):
|
for i, n in enumerate(nodes):
|
||||||
if m := re.match(fr'({re.escape(prefix)}.*?)((?:[\d_\[\]])*$)', n.name):
|
if m := re.match(fr'({re.escape(prefix)}.*?)((?:[\d_\[\]])*$)', n.name):
|
||||||
path = [m[1]] + [int(v) for v in re.split(r'[_\[\]]+', m[2]) if len(v) > 0]
|
path = [m[1]] + [int(v) for v in re.split(r'[_\[\]]+', m[2]) if len(v) > 0]
|
||||||
@@ -319,16 +320,16 @@ class Circuit:
|
|||||||
for j in path[:-1]:
|
for j in path[:-1]:
|
||||||
d[j] = d.get(j, dict())
|
d[j] = d.get(j, dict())
|
||||||
d = d[j]
|
d = d[j]
|
||||||
assert isinstance(d, dict)
|
|
||||||
d[path[-1]] = i
|
d[path[-1]] = i
|
||||||
|
|
||||||
# sort recursively for multi-dimensional lists.
|
# sort recursively for multi-dimensional lists.
|
||||||
def sorted_values(d) -> NestedNumericList: return [sorted_values(v) for k, v in sorted(d.items())] if isinstance(d, dict) else d
|
def sorted_values(d): return [sorted_values(v) for k, v in sorted(d.items())] if isinstance(d, dict) else d
|
||||||
l = sorted_values(d_top)
|
l = sorted_values(d_top)
|
||||||
while isinstance(l, list) and len(l) == 1 and isinstance(l[0], list): l = l[0]
|
while isinstance(l, list) and len(l) == 1: l = l[0]
|
||||||
return l
|
return l #None if isinstance(l, list) and len(l) == 0 else l
|
||||||
|
|
||||||
def stats(self, tlib: 'TechLib'): # type: ignore
|
@property
|
||||||
|
def stats(self):
|
||||||
"""A dictionary with the counts of all different elements in the circuit.
|
"""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
|
The dictionary contains the number of all different kinds of nodes, the number
|
||||||
@@ -346,9 +347,9 @@ class Circuit:
|
|||||||
stats['__line__'] = len(self.lines)
|
stats['__line__'] = len(self.lines)
|
||||||
for n in self.cells.values():
|
for n in self.cells.values():
|
||||||
stats[n.kind] += 1
|
stats[n.kind] += 1
|
||||||
if tlib.is_dff(n.kind): stats['__dff__'] += 1
|
if 'dff' in n.kind.lower(): stats['__dff__'] += 1
|
||||||
elif tlib.is_latch(n.kind): stats['__latch__'] += 1
|
elif 'latch' in n.kind.lower(): stats['__latch__'] += 1
|
||||||
elif 'put' not in n.kind.lower(): stats['__comb__'] += 1 # don't count input or output towards comb
|
elif 'put' not in n.kind.lower(): stats['__comb__'] += 1 # no input or output
|
||||||
stats['__seq__'] = stats['__dff__'] + stats['__latch__']
|
stats['__seq__'] = stats['__dff__'] + stats['__latch__']
|
||||||
return dict(stats)
|
return dict(stats)
|
||||||
|
|
||||||
@@ -447,7 +448,7 @@ class Circuit:
|
|||||||
node_map[n] = Node(self, f'{node.name}~{n.name}', n.kind)
|
node_map[n] = Node(self, f'{node.name}~{n.name}', n.kind)
|
||||||
elif len(n.outs) > 0 and len(n.ins) > 0: # output is also read by impl. circuit, need to add a fork.
|
elif len(n.outs) > 0 and len(n.ins) > 0: # output is also read by impl. circuit, need to add a fork.
|
||||||
node_map[n] = Node(self, f'{node.name}~{n.name}')
|
node_map[n] = Node(self, f'{node.name}~{n.name}')
|
||||||
elif len(n.ins) == 0 and len(n.outs) != 1: # input is read by multiple nodes (or no nodes), need to add fork.
|
elif len(n.ins) == 0 and len(n.outs) > 1: # input is read by multiple nodes, need to add fork.
|
||||||
node_map[n] = Node(self, f'{node.name}~{n.name}')
|
node_map[n] = Node(self, f'{node.name}~{n.name}')
|
||||||
for l in impl.lines: # add all internal lines to main circuit
|
for l in impl.lines: # add all internal lines to main circuit
|
||||||
if l.reader in node_map and l.driver in node_map:
|
if l.reader in node_map and l.driver in node_map:
|
||||||
@@ -580,23 +581,24 @@ class Circuit:
|
|||||||
if line is not None:
|
if line is not None:
|
||||||
yield line
|
yield line
|
||||||
|
|
||||||
def reversed_topological_order(self, tlib: 'TechLib'): # type: ignore
|
def reversed_topological_order(self):
|
||||||
"""Generator function to iterate over all nodes in reversed topological order.
|
"""Generator function to iterate over all nodes in reversed topological order.
|
||||||
|
|
||||||
Nodes without output lines and sequential nodes (flip-flops, latches) are yielded first.
|
Nodes without output lines and nodes whose :py:attr:`Node.kind` contains the
|
||||||
|
substrings 'dff' or 'latch' are yielded first.
|
||||||
"""
|
"""
|
||||||
visit_count = [0] * len(self.nodes)
|
visit_count = [0] * len(self.nodes)
|
||||||
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))
|
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())
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
n = queue.popleft()
|
n = queue.popleft()
|
||||||
for line in n.ins:
|
for line in n.ins:
|
||||||
pred = line.driver
|
pred = line.driver
|
||||||
visit_count[pred] += 1
|
visit_count[pred] += 1
|
||||||
if visit_count[pred] == len(pred.outs) and not tlib.is_dff(pred.kind) and not tlib.is_latch(pred.kind):
|
if visit_count[pred] == len(pred.outs) and 'dff' not in pred.kind.lower() and 'latch' not in pred.kind.lower():
|
||||||
queue.append(pred)
|
queue.append(pred)
|
||||||
yield n
|
yield n
|
||||||
|
|
||||||
def fanin(self, origin_nodes, tlib: 'TechLib'): # type: ignore
|
def fanin(self, origin_nodes):
|
||||||
"""Generator function to iterate over the fan-in cone of a given list of origin nodes.
|
"""Generator function to iterate over the fan-in cone of a given list of origin nodes.
|
||||||
|
|
||||||
Nodes are yielded in reversed topological order.
|
Nodes are yielded in reversed topological order.
|
||||||
@@ -604,34 +606,31 @@ class Circuit:
|
|||||||
marks = [False] * len(self.nodes)
|
marks = [False] * len(self.nodes)
|
||||||
for n in origin_nodes:
|
for n in origin_nodes:
|
||||||
marks[n] = True
|
marks[n] = True
|
||||||
for n in self.reversed_topological_order(tlib):
|
for n in self.reversed_topological_order():
|
||||||
if not marks[n]:
|
if not marks[n]:
|
||||||
for line in n.outs.without_nones():
|
for line in n.outs:
|
||||||
marks[n] |= marks[line.reader]
|
if line is not None:
|
||||||
|
marks[n] |= marks[line.reader]
|
||||||
if marks[n]:
|
if marks[n]:
|
||||||
yield n
|
yield n
|
||||||
|
|
||||||
def fanout(self, origin_nodes: list[Node], node_filter = lambda n: 'dff' not in n.name.lower()):
|
def fanout(self, origin_nodes):
|
||||||
"""Generator function to iterate over the fan-out cone of a given list of origin nodes.
|
"""Generator function to iterate over the fan-out cone of a given list of origin nodes.
|
||||||
|
|
||||||
origin_nodes are yielded first, followed by nodes driven by them in a breadth-first manner.
|
Nodes are yielded in topological order.
|
||||||
The search stops at nodes for which node_filter returns False.
|
|
||||||
Only origin_nodes and nodes for which node_filter returned True are yielded.
|
|
||||||
By default, search stops at flip-flops.
|
|
||||||
"""
|
"""
|
||||||
queue = deque(origin_nodes)
|
marks = [False] * len(self.nodes)
|
||||||
yielded = set()
|
for n in origin_nodes:
|
||||||
while len(queue) > 0:
|
marks[n] = True
|
||||||
n = queue.popleft()
|
for n in self.topological_order():
|
||||||
for line in n.outs.without_nones():
|
if not marks[n]:
|
||||||
succ = line.reader
|
for line in n.ins.without_nones():
|
||||||
if succ not in yielded and node_filter(succ):
|
marks[n] |= marks[line.driver]
|
||||||
yielded.add(succ)
|
if marks[n]:
|
||||||
queue.append(succ)
|
yield n
|
||||||
yield n
|
|
||||||
|
|
||||||
def fanout_free_regions(self, tlib: 'TechLib'): # type: ignore
|
def fanout_free_regions(self):
|
||||||
for stem in self.reversed_topological_order(tlib):
|
for stem in self.reversed_topological_order():
|
||||||
if len(stem.outs) == 1 and 'dff' not in stem.kind.lower(): continue
|
if len(stem.outs) == 1 and 'dff' not in stem.kind.lower(): continue
|
||||||
region = []
|
region = []
|
||||||
if 'dff' in stem.kind.lower():
|
if 'dff' in stem.kind.lower():
|
||||||
@@ -651,11 +650,11 @@ class Circuit:
|
|||||||
region.append(n)
|
region.append(n)
|
||||||
yield stem, region
|
yield stem, region
|
||||||
|
|
||||||
def dot(self, tlib: 'TechLib', format='svg', graph_attr={}, line_labels={}):
|
def dot(self, format='svg', graph_attr={}, line_labels={}):
|
||||||
from graphviz import Digraph
|
from graphviz import Digraph
|
||||||
dot = Digraph(format=format, graph_attr={'rankdir': 'LR', 'splines': 'true', 'size': '10', 'ranksep': '0.1'} | graph_attr)
|
dot = Digraph(format=format, graph_attr={'rankdir': 'LR', 'splines': 'true', 'size': '10', 'ranksep': '0.1'} | graph_attr)
|
||||||
|
|
||||||
s_dict = dict((n, i) for i, n in enumerate(self.s_nodes(tlib)))
|
s_dict = dict((n, i) for i, n in enumerate(self.s_nodes))
|
||||||
node_level = np.zeros(len(self.nodes), dtype=np.uint32)
|
node_level = np.zeros(len(self.nodes), dtype=np.uint32)
|
||||||
level_nodes = defaultdict(list)
|
level_nodes = defaultdict(list)
|
||||||
for n, lv in self.topological_order_with_level():
|
for n, lv in self.topological_order_with_level():
|
||||||
@@ -678,7 +677,7 @@ class Circuit:
|
|||||||
reader = f'{l.reader.index}:i{l.reader_pin}' if len(l.reader.ins)>1 else f'{l.reader.index}'
|
reader = f'{l.reader.index}:i{l.reader_pin}' if len(l.reader.ins)>1 else f'{l.reader.index}'
|
||||||
label = str(line_labels.get(l, l.index))
|
label = str(line_labels.get(l, l.index))
|
||||||
if node_level[l.driver] == node_level[l.reader]:
|
if node_level[l.driver] == node_level[l.reader]:
|
||||||
dot.node(f'_{l.index}_')
|
s.node(f'_{l.index}_')
|
||||||
dot.edge(driver, f'_{l.index}_', style='dotted', label=label)
|
dot.edge(driver, f'_{l.index}_', style='dotted', label=label)
|
||||||
dot.edge(f'_{l.index}_', reader, style='dotted', label=label)
|
dot.edge(f'_{l.index}_', reader, style='dotted', label=label)
|
||||||
elif node_level[l.driver] > node_level[l.reader]:
|
elif node_level[l.driver] > node_level[l.reader]:
|
||||||
|
|||||||
+17
-48
@@ -49,7 +49,7 @@ from collections.abc import Iterable
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import DTypeLike
|
from numpy.typing import DTypeLike
|
||||||
|
|
||||||
from . import numba, hr_bytes, NestedNumericList
|
from . import numba, hr_bytes
|
||||||
|
|
||||||
|
|
||||||
ZERO = 0b000
|
ZERO = 0b000
|
||||||
@@ -81,30 +81,7 @@ on a signal. ``'N'``, ``'n'``, and ``'v'`` are interpreted as ``NPULSE``.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# 0 X - 1 P R F N
|
def interpret(value):
|
||||||
_mv_xor = np.array([[0,1,1,3,4,5,6,7], # 0
|
|
||||||
[1,1,1,1,1,1,1,1], # X
|
|
||||||
[1,1,1,1,1,1,1,1], # -
|
|
||||||
[3,1,1,0,7,6,5,4], # 1
|
|
||||||
[4,1,1,7,4,5,6,7], # P
|
|
||||||
[5,1,1,6,5,4,7,6], # R
|
|
||||||
[6,1,1,5,6,7,4,5], # F
|
|
||||||
[7,1,1,4,7,6,5,4], # N
|
|
||||||
], dtype=np.uint8)
|
|
||||||
|
|
||||||
# 0 X - 1 P R F N
|
|
||||||
_mv_merge = np.array([[0,0,0,1,1,1,1,1], # 0
|
|
||||||
[0,1,1,3,4,5,6,7], # X
|
|
||||||
[0,1,2,3,4,5,6,7], # -
|
|
||||||
[1,3,3,3,1,1,1,1], # 1
|
|
||||||
[1,4,4,1,4,1,1,1], # P
|
|
||||||
[1,5,5,1,1,5,1,1], # R
|
|
||||||
[1,6,6,1,1,1,6,1], # F
|
|
||||||
[1,7,7,1,1,1,1,7], # N
|
|
||||||
], dtype=np.uint8)
|
|
||||||
|
|
||||||
|
|
||||||
def interpret(value) -> int|NestedNumericList:
|
|
||||||
"""Converts characters, strings, and lists of them to lists of logic constants defined above.
|
"""Converts characters, strings, and lists of them to lists of logic constants defined above.
|
||||||
|
|
||||||
:param value: A character (string of length 1), Boolean, Integer, None, or Iterable.
|
:param value: A character (string of length 1), Boolean, Integer, None, or Iterable.
|
||||||
@@ -123,12 +100,11 @@ def interpret(value) -> int|NestedNumericList:
|
|||||||
return UNKNOWN
|
return UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
def mvarray(*a) -> np.ndarray[tuple[int]|tuple[int,int],np.dtype[np.uint8]]:
|
def mvarray(*a):
|
||||||
"""Converts (lists of) Boolean values or strings into a multi-valued array.
|
"""Converts (lists of) Boolean values or strings into a multi-valued array.
|
||||||
|
|
||||||
Iterable over characters, Booleans, Integers (0,1), None are interpreted as a single pattern.
|
The given values are interpreted and the axes are arranged as per KyuPy's convention.
|
||||||
Multi-character strings are Iterables over characters and are interpreted as a single pattern.
|
Use this function to convert strings into multi-valued arrays.
|
||||||
Lists of said patterns form a pattern set as ndarray with the axes are arranged as per KyuPy's convention.
|
|
||||||
"""
|
"""
|
||||||
mva = np.array(interpret(a), dtype=np.uint8)
|
mva = np.array(interpret(a), dtype=np.uint8)
|
||||||
if mva.ndim < 2: return mva
|
if mva.ndim < 2: return mva
|
||||||
@@ -215,6 +191,17 @@ def mv_and(x1, x2, out=None):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _mv_xor(out, *ins):
|
||||||
|
any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED)
|
||||||
|
for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED)
|
||||||
|
|
||||||
|
out[...] = ZERO
|
||||||
|
for inp in ins:
|
||||||
|
np.bitwise_xor(out, inp & 0b011, out=out)
|
||||||
|
np.bitwise_or(out, inp & 0b100, out=out)
|
||||||
|
np.putmask(out, any_unknown, UNKNOWN)
|
||||||
|
|
||||||
|
|
||||||
def mv_xor(x1, x2, out=None):
|
def mv_xor(x1, x2, out=None):
|
||||||
"""A multi-valued XOR operator.
|
"""A multi-valued XOR operator.
|
||||||
|
|
||||||
@@ -224,25 +211,7 @@ def mv_xor(x1, x2, out=None):
|
|||||||
:return: A multi-valued array with the result.
|
:return: A multi-valued array with the result.
|
||||||
"""
|
"""
|
||||||
out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8)
|
out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8)
|
||||||
out[...] = _mv_xor[x1, x2]
|
_mv_xor(out, x1, x2)
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
def mv_merge(x1, x2, out=None):
|
|
||||||
"""A multi-valued merge operator.
|
|
||||||
|
|
||||||
Merging identical values ``a`` and ``a`` yields ``a``.
|
|
||||||
Merging ``X`` and ``-`` yields ``X``.
|
|
||||||
Merging any value ``a`` with ``X`` or ``-`` yields ``a``.
|
|
||||||
Merging any other values ``a != b`` yields ``X``.
|
|
||||||
|
|
||||||
:param x1: A multi-valued array.
|
|
||||||
:param x2: A multi-valued array.
|
|
||||||
:param out: An optional storage destination. If None, a new multi-valued array is returned.
|
|
||||||
:return: A multi-valued array with the result.
|
|
||||||
"""
|
|
||||||
out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8)
|
|
||||||
out[...] = _mv_merge[x1, x2]
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+13
-49
@@ -1,24 +1,12 @@
|
|||||||
"""High-throughput combinational logic simulators.
|
"""A high-throughput combinational logic simulator.
|
||||||
|
|
||||||
These simulators take batches of input assignments to a logic circuit and compute batches of output values.
|
The class :py:class:`~kyupy.logic_sim.LogicSim` performs parallel simulations of the combinational part of a circuit.
|
||||||
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.
|
The logic operations are performed bit-parallel on packed numpy arrays (see bit-parallel (bp) array description in :py:mod:`~kyupy.logic`).
|
||||||
All simulations within a batch are mutually independent.
|
Simple sequential circuits can be simulated by repeated assignments and propagations.
|
||||||
Inner simulation loops (that iterate over the topologically sorted circuit model) are just-in-time (JIT) compiled using numba.
|
However, this simulator ignores the clock network and simply assumes that all state-elements are clocked all the time.
|
||||||
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 math
|
||||||
import warnings
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
@@ -29,10 +17,6 @@ from .circuit import Circuit, Line
|
|||||||
class LogicSim(sim.SimOps):
|
class LogicSim(sim.SimOps):
|
||||||
"""A bit-parallel naïve combinational simulator for 2-, 4-, or 8-valued logic.
|
"""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 circuit: The circuit to simulate.
|
||||||
:param sims: The number of parallel logic simulations to perform.
|
:param sims: The number of parallel logic simulations to perform.
|
||||||
:param m: The arity of the logic, must be 2, 4, or 8.
|
:param m: The arity of the logic, must be 2, 4, or 8.
|
||||||
@@ -40,9 +24,6 @@ class LogicSim(sim.SimOps):
|
|||||||
:param strip_forks: If True, forks are not included in the simulation model to save memory and simulation time.
|
: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):
|
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]
|
assert m in [2, 4, 8]
|
||||||
super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks)
|
super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks)
|
||||||
self.m = m
|
self.m = m
|
||||||
@@ -51,7 +32,6 @@ class LogicSim(sim.SimOps):
|
|||||||
nbytes = cdiv(sims, 8)
|
nbytes = cdiv(sims, 8)
|
||||||
|
|
||||||
self.c = np.zeros((self.c_len, self.mdim, nbytes), dtype=np.uint8)
|
self.c = np.zeros((self.c_len, self.mdim, nbytes), dtype=np.uint8)
|
||||||
self.c_dirty = np.full(self.c_len, 1, dtype=np.uint8)
|
|
||||||
self.s = np.zeros((2, self.s_len, 3, nbytes), dtype=np.uint8)
|
self.s = np.zeros((2, self.s_len, 3, nbytes), dtype=np.uint8)
|
||||||
"""Logic values of the sequential elements (flip-flops) and ports.
|
"""Logic values of the sequential elements (flip-flops) and ports.
|
||||||
|
|
||||||
@@ -98,7 +78,7 @@ class LogicSim(sim.SimOps):
|
|||||||
t1 = self.c_locs[self.tmp2_idx]
|
t1 = self.c_locs[self.tmp2_idx]
|
||||||
if self.m == 2:
|
if self.m == 2:
|
||||||
if inject_cb is None:
|
if inject_cb is None:
|
||||||
c_prop_2v_cpu(self.ops, self.c_locs, self.c, self.c_dirty, int(fault_line), fault_mask, int(fault_model))
|
c_prop_2v_cpu(self.ops, self.c_locs, self.c, int(fault_line), fault_mask, int(fault_model))
|
||||||
else:
|
else:
|
||||||
for op, o0l, i0l, i1l, i2l, i3l in self.ops[:,:6]:
|
for op, o0l, i0l, i1l, i2l, i3l in self.ops[:,:6]:
|
||||||
o0, i0, i1, i2, i3 = [self.c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)]
|
o0, i0, i1, i2, i3 = [self.c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)]
|
||||||
@@ -357,19 +337,6 @@ class LogicSim2V(sim.SimOps):
|
|||||||
Storage locations are indirectly addressed.
|
Storage locations are indirectly addressed.
|
||||||
Data for line `l` is in `self.c[self.c_locs[l]]`.
|
Data for line `l` is in `self.c[self.c_locs[l]]`.
|
||||||
"""
|
"""
|
||||||
self.c_dirty = np.full(self.c_len, 1, dtype=np.uint8)
|
|
||||||
"""Marker for logic values that have changed recently for use in incremental simulation.
|
|
||||||
|
|
||||||
A node n will be evaluated by `c_prop()` if and only if at least one of its inputs is marked dirty (`c_dirty[c_locs[n.ins[i]]] == 1`).
|
|
||||||
If a node is evaluated, its output is marked dirty.
|
|
||||||
By default, this array is all-1, therefore every node is evaluated every time by `c_prop()`.
|
|
||||||
Enable incremental simulation by setting this array to all-0, change signal values in `c` and set `c_dirty` to 1 for all changed signals.
|
|
||||||
Next call to `c_prop()` will only evaluate nodes that read from the changed signals.
|
|
||||||
To restore the original simulation state, revert the signal value changes in `c`, and call `c_prop()` again.
|
|
||||||
Caveats:
|
|
||||||
- Injected changes that are located downstream of other changes will have no effect.
|
|
||||||
- Only works with `c_reuse=False` (default).
|
|
||||||
"""
|
|
||||||
self.s_assign = np.zeros((self.s_len, self.sims), dtype=np.uint8)
|
self.s_assign = np.zeros((self.s_len, self.sims), dtype=np.uint8)
|
||||||
"""Logic values assigned to the ports and flip-flops of the circuit.
|
"""Logic values assigned to the ports and flip-flops of the circuit.
|
||||||
|
|
||||||
@@ -404,7 +371,7 @@ class LogicSim2V(sim.SimOps):
|
|||||||
fault_mask2 = np.full(self.c.shape[-1], 0, dtype=np.uint8)
|
fault_mask2 = np.full(self.c.shape[-1], 0, dtype=np.uint8)
|
||||||
fault_mask2[:len(fault_mask)] = fault_mask
|
fault_mask2[:len(fault_mask)] = fault_mask
|
||||||
fault_mask = fault_mask2
|
fault_mask = fault_mask2
|
||||||
c_prop_2v_cpu(self.ops, self.c_locs, self.c, self.c_dirty, int(fault_line), fault_mask, int(fault_model))
|
c_prop_2v_cpu(self.ops, self.c_locs, self.c, int(fault_line), fault_mask, int(fault_model))
|
||||||
|
|
||||||
def c_to_s(self):
|
def c_to_s(self):
|
||||||
"""Captures the results of the combinational portion into ``self.s_result``.
|
"""Captures the results of the combinational portion into ``self.s_result``.
|
||||||
@@ -438,11 +405,9 @@ class LogicSim2V(sim.SimOps):
|
|||||||
|
|
||||||
|
|
||||||
@numba.njit
|
@numba.njit
|
||||||
def c_prop_2v_cpu(ops, c_locs, c, c_dirty, fault_line, fault_mask, fault_model):
|
def c_prop_2v_cpu(ops, c_locs, c, fault_line, fault_mask, fault_model):
|
||||||
for op, o0l, i0l, i1l, i2l, i3l in ops[:,:6]:
|
for op, o0l, i0l, i1l, i2l, i3l in ops[:,:6]:
|
||||||
o0, i0, i1, i2, i3 = [c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)]
|
o0, i0, i1, i2, i3 = [c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)]
|
||||||
if fault_line < 0 or o0l != fault_line: # fault injection forces node evaluation
|
|
||||||
if not (c_dirty[i0] | c_dirty[i1] | c_dirty[i2] | c_dirty[i3]): continue
|
|
||||||
if op == sim.BUF1: c[o0]=c[i0]
|
if op == sim.BUF1: c[o0]=c[i0]
|
||||||
elif op == sim.INV1: c[o0] = ~c[i0]
|
elif op == sim.INV1: c[o0] = ~c[i0]
|
||||||
elif op == sim.AND2: c[o0] = c[i0] & c[i1]
|
elif op == sim.AND2: c[o0] = c[i0] & c[i1]
|
||||||
@@ -482,9 +447,8 @@ def c_prop_2v_cpu(ops, c_locs, c, c_dirty, fault_line, fault_mask, fault_model):
|
|||||||
c[o0] = c[o0] & ~fault_mask
|
c[o0] = c[o0] & ~fault_mask
|
||||||
elif fault_model == 1:
|
elif fault_model == 1:
|
||||||
c[o0] = c[o0] | fault_mask
|
c[o0] = c[o0] | fault_mask
|
||||||
elif fault_model == 2:
|
else:
|
||||||
c[o0] = c[o0] ^ fault_mask
|
c[o0] = c[o0] ^ fault_mask
|
||||||
c_dirty[o0] = 1
|
|
||||||
|
|
||||||
|
|
||||||
class LogicSim4V(sim.SimOps):
|
class LogicSim4V(sim.SimOps):
|
||||||
@@ -509,7 +473,7 @@ class LogicSim4V(sim.SimOps):
|
|||||||
Storage locations are indirectly addressed.
|
Storage locations are indirectly addressed.
|
||||||
Data for line `l` is in `self.c[self.c_locs[l]]`.
|
Data for line `l` is in `self.c[self.c_locs[l]]`.
|
||||||
"""
|
"""
|
||||||
self.s_assign = np.full((self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
|
self.s_assign = np.zeros((self.s_len, self.sims), dtype=np.uint8)
|
||||||
"""Logic values assigned to the ports and flip-flops of the circuit.
|
"""Logic values assigned to the ports and flip-flops of the circuit.
|
||||||
The simulator reads (P)PI values from here.
|
The simulator reads (P)PI values from here.
|
||||||
Values assigned to PO positions are ignored.
|
Values assigned to PO positions are ignored.
|
||||||
@@ -517,7 +481,7 @@ class LogicSim4V(sim.SimOps):
|
|||||||
First index is the port position (defined by `self.circuit.s_nodes`), second
|
First index is the port position (defined by `self.circuit.s_nodes`), second
|
||||||
index is the pattern index (0 ... `self.sims-1`).
|
index is the pattern index (0 ... `self.sims-1`).
|
||||||
"""
|
"""
|
||||||
self.s_result = np.full((self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
|
self.s_result = np.zeros((self.s_len, self.sims), dtype=np.uint8)
|
||||||
"""Logic values at the ports and flip-flops of the circuit after simulation.
|
"""Logic values at the ports and flip-flops of the circuit after simulation.
|
||||||
The simulator writes (P)PO values here.
|
The simulator writes (P)PO values here.
|
||||||
Values assigned to PI positions are ignored.
|
Values assigned to PI positions are ignored.
|
||||||
@@ -528,7 +492,7 @@ class LogicSim4V(sim.SimOps):
|
|||||||
self._full_mask = np.full(self.c.shape[-1], 255, dtype=np.uint8)
|
self._full_mask = np.full(self.c.shape[-1], 255, dtype=np.uint8)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'{{name: "{self.circuit.name}", sims: {self.sims}, s_len: {self.s_len}, c_bytes: {eng(self.c.nbytes)}}}'
|
return f'{{name: "{self.circuit.name}", sims: {self.sims}, c_bytes: {eng(self.c.nbytes)}}}'
|
||||||
|
|
||||||
def s_to_c(self):
|
def s_to_c(self):
|
||||||
"""Assigns the values from ``self.s_assign`` to the inputs of the combinational portion.
|
"""Assigns the values from ``self.s_assign`` to the inputs of the combinational portion.
|
||||||
@@ -775,7 +739,7 @@ class LogicSim6V(sim.SimOps):
|
|||||||
nbytes = cdiv(sims, 8)
|
nbytes = cdiv(sims, 8)
|
||||||
|
|
||||||
self.c = np.zeros((self.c_len, 3, nbytes), dtype=np.uint8)
|
self.c = np.zeros((self.c_len, 3, nbytes), dtype=np.uint8)
|
||||||
self.s = np.full((2, self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
|
self.s = np.zeros((2, self.s_len, self.sims), dtype=np.uint8)
|
||||||
"""Logic values of the sequential elements (flip-flops) and ports.
|
"""Logic values of the sequential elements (flip-flops) and ports.
|
||||||
|
|
||||||
It is a pair of arrays in mv storage format:
|
It is a pair of arrays in mv storage format:
|
||||||
|
|||||||
+6
-11
@@ -59,7 +59,7 @@ class DelayFile:
|
|||||||
if name not in circuit.cells: name = name.replace('[', '_').replace(']', '_')
|
if name not in circuit.cells: name = name.replace('[', '_').replace(']', '_')
|
||||||
return circuit.cells.get(name, None)
|
return circuit.cells.get(name, None)
|
||||||
|
|
||||||
delays = np.zeros((len(circuit.lines), 2, 2, 3), dtype=np.float32) # dataset last during construction.
|
delays = np.zeros((len(circuit.lines), 2, 2, 3)) # dataset last during construction.
|
||||||
|
|
||||||
with log.limit(50):
|
with log.limit(50):
|
||||||
for name, iopaths in self.cells.items():
|
for name, iopaths in self.cells.items():
|
||||||
@@ -101,19 +101,14 @@ class DelayFile:
|
|||||||
* Axis 3: polarity of the transition, 0='rising/posedge', 1='falling/negedge'
|
* Axis 3: polarity of the transition, 0='rising/posedge', 1='falling/negedge'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
delays = np.zeros((len(circuit.lines), 2, 2, 3), dtype=np.float32) # dataset last during construction.
|
delays = np.zeros((len(circuit.lines), 2, 2, 3)) # dataset last during construction.
|
||||||
|
|
||||||
def split_node_port(n_str):
|
|
||||||
if (split := n_str.rfind('.')) > 0 or (split := n_str.rfind('/')) > 0:
|
|
||||||
return n_str[:split], n_str[split+1:]
|
|
||||||
return (n_str, None)
|
|
||||||
|
|
||||||
nonfork_annotations = 0
|
nonfork_annotations = 0
|
||||||
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: continue
|
if max(max(delvals)) == 0: continue
|
||||||
cn1, pn1 = split_node_port(n1)
|
cn1, pn1 = (n1, None) if (slash := n1.rfind('/')) < 0 else (n1[:slash], n1[slash+1:])
|
||||||
cn2, pn2 = split_node_port(n2)
|
cn2, pn2 = (n2, None) if (slash := n2.rfind('/')) < 0 else (n2[:slash], n2[slash+1:])
|
||||||
cn1 = cn1.replace('\\','')
|
cn1 = cn1.replace('\\','')
|
||||||
cn2 = cn2.replace('\\','')
|
cn2 = cn2.replace('\\','')
|
||||||
c1, c2 = circuit.cells[cn1], circuit.cells[cn2]
|
c1, c2 = circuit.cells[cn1], circuit.cells[cn2]
|
||||||
@@ -222,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) -> DelayFile:
|
def load(file):
|
||||||
"""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)) # type: ignore
|
return parse(readtext(file))
|
||||||
+14
-36
@@ -6,7 +6,6 @@ import numpy as np
|
|||||||
|
|
||||||
from . import log
|
from . import log
|
||||||
from .circuit import Circuit
|
from .circuit import Circuit
|
||||||
from .techlib import KYUPY
|
|
||||||
|
|
||||||
BUF1 = np.uint16(0b1010_1010_1010_1010)
|
BUF1 = np.uint16(0b1010_1010_1010_1010)
|
||||||
INV1 = ~BUF1
|
INV1 = ~BUF1
|
||||||
@@ -160,7 +159,7 @@ class SimOps:
|
|||||||
"""A static scheduler that translates a Circuit into a topologically sorted list of basic logic operations (self.ops) and
|
"""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.
|
a memory mapping (self.c_locs, self.c_caps) for use in simulators.
|
||||||
|
|
||||||
:param circuit: The circuit to create a schedule for. Must contain cells from KYUPY techlib only.
|
:param circuit: The circuit to create a schedule for.
|
||||||
:param strip_forks: If enabled, the scheduler will not include fork nodes to safe simulation time.
|
: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.
|
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
|
:param c_reuse: If enabled, memory of intermediate signal waveforms will be re-used. This greatly reduces
|
||||||
@@ -168,7 +167,7 @@ class SimOps:
|
|||||||
"""
|
"""
|
||||||
def __init__(self, circuit: Circuit, c_caps=1, c_caps_min=1, a_ctrl=None, c_reuse=False, strip_forks=False):
|
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.circuit = circuit
|
||||||
self.s_len = len(circuit.s_nodes(KYUPY))
|
self.s_len = len(circuit.s_nodes)
|
||||||
|
|
||||||
if isinstance(c_caps, int):
|
if isinstance(c_caps, int):
|
||||||
c_caps = [c_caps] * (len(circuit.lines)+3)
|
c_caps = [c_caps] * (len(circuit.lines)+3)
|
||||||
@@ -188,11 +187,11 @@ class SimOps:
|
|||||||
# ALAP-toposort the circuit into self.ops
|
# ALAP-toposort the circuit into self.ops
|
||||||
levels = []
|
levels = []
|
||||||
|
|
||||||
ppio2idx = dict((n, i) for i, n in enumerate(circuit.s_nodes(KYUPY)))
|
ppio2idx = dict((n, i) for i, n in enumerate(circuit.s_nodes))
|
||||||
root_nodes = set([n for n in circuit.nodes if len(n.outs) == 0 or KYUPY.is_dff(n.kind)]) # start from POs (that are unused by circuit itself), PPOs, and any dangling 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
|
||||||
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
|
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 sorted(root_nodes, key=lambda n: n.index) if len(n.ins) > 0 ]
|
level_lines = [n.ins[0] for n in root_nodes]
|
||||||
# FIXME: Should probably instanciate buffers for PPOs and attach DFF clocks
|
# FIXME: Should probably instanciate buffers for PPOs and attach DFF clocks
|
||||||
|
|
||||||
while len(level_lines) > 0: # traverse the circuit level-wise back towards (P)PIs
|
while len(level_lines) > 0: # traverse the circuit level-wise back towards (P)PIs
|
||||||
@@ -201,15 +200,11 @@ class SimOps:
|
|||||||
|
|
||||||
for l in level_lines:
|
for l in level_lines:
|
||||||
n = l.driver
|
n = l.driver
|
||||||
assert len(n.ins) <= 4, f'Too many input pins for node {n}. Map circuit to KYUPY techlib first using resolve_tlib_cells().'
|
if len(n.ins) > 4:
|
||||||
|
log.warn(f'too many input pins: {n}')
|
||||||
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]]
|
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:
|
if n in ppio2idx:
|
||||||
in_idxs[1] = self.zero_idx
|
in_idxs[0] = self.ppi_offset + ppio2idx[n]
|
||||||
if 'dff' in n.kind.lower() or in_idxs[0] == self.zero_idx:
|
|
||||||
in_idxs[0] = self.ppi_offset + ppio2idx[n]
|
|
||||||
else: # this is a PO that is also used within the circuit. Treat as normal fork with ref counting. TODO: check strip_forks=True case for this.
|
|
||||||
readers[n.ins[0]] -= 1
|
|
||||||
if readers[n.ins[0]] == 0: prev_level_lines.append(n.ins[0])
|
|
||||||
if l.driver_pin == 1 and 'dff' in n.kind.lower(): # second output of DFF is inverted
|
if l.driver_pin == 1 and 'dff' in n.kind.lower(): # second output of DFF is inverted
|
||||||
level_ops.append((INV1, l.index, *in_idxs, *a_ctrl[l]))
|
level_ops.append((INV1, l.index, *in_idxs, *a_ctrl[l]))
|
||||||
else:
|
else:
|
||||||
@@ -230,8 +225,10 @@ class SimOps:
|
|||||||
if in_idxs[2] == self.zero_idx:
|
if in_idxs[2] == self.zero_idx:
|
||||||
sp = prims[2]
|
sp = prims[2]
|
||||||
break
|
break
|
||||||
assert sp is not None, f'Unsupported node type {n}. Map circuit to KYUPY techlib first using resolce_tlib_cells().'
|
if sp is None:
|
||||||
level_ops.append((sp, l.index, *in_idxs, *a_ctrl[l]))
|
log.warn(f'ignored cell of unknown type: {n}')
|
||||||
|
else:
|
||||||
|
level_ops.append((sp, l.index, *in_idxs, *a_ctrl[l]))
|
||||||
|
|
||||||
if len(level_ops) > 0: levels.append(level_ops)
|
if len(level_ops) > 0: levels.append(level_ops)
|
||||||
level_lines = prev_level_lines
|
level_lines = prev_level_lines
|
||||||
@@ -274,7 +271,7 @@ class SimOps:
|
|||||||
ref_count[self.tmp2_idx] += 1
|
ref_count[self.tmp2_idx] += 1
|
||||||
|
|
||||||
# allocate and keep memory for PI/PPI, keep memory for PO/PPO (allocated later)
|
# allocate and keep memory for PI/PPI, keep memory for PO/PPO (allocated later)
|
||||||
for i, n in enumerate(circuit.s_nodes(KYUPY)):
|
for i, n in enumerate(circuit.s_nodes):
|
||||||
if 'dff' in n.kind.lower() or len(n.ins) == 0: # PPI or PI
|
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
|
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
|
ref_count[self.ppi_offset + i] += 1
|
||||||
@@ -313,15 +310,10 @@ class SimOps:
|
|||||||
self.c_locs[lidx], self.c_caps[lidx] = self.c_locs[stem], self.c_caps[stem]
|
self.c_locs[lidx], self.c_caps[lidx] = self.c_locs[stem], self.c_caps[stem]
|
||||||
|
|
||||||
# copy memory location to PO/PPO area
|
# copy memory location to PO/PPO area
|
||||||
for i, n in enumerate(circuit.s_nodes(KYUPY)):
|
for i, n in enumerate(circuit.s_nodes):
|
||||||
if len(n.ins) > 0:
|
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]]
|
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]]
|
||||||
|
|
||||||
# sanity check
|
|
||||||
for oidx, io in zip(*np.nonzero(self.c_locs[self.ops[:, 1:6]] < 0)):
|
|
||||||
log.error(f'unallocated {["o0","i0","i1","i2","i3"][io]} in op {self.ops[oidx]}')
|
|
||||||
assert np.min(self.c_locs[self.ops[:, 1:6]]) >= 0, "Unallocated signal lines."
|
|
||||||
|
|
||||||
# line use information
|
# line use information
|
||||||
self.line_use_start = np.full(self.c_locs_len, -1, dtype=np.int32)
|
self.line_use_start = np.full(self.c_locs_len, -1, dtype=np.int32)
|
||||||
self.line_use_stop = np.full(self.c_locs_len, len(self.levels), dtype=np.int32)
|
self.line_use_stop = np.full(self.c_locs_len, len(self.levels), dtype=np.int32)
|
||||||
@@ -338,16 +330,11 @@ class SimOps:
|
|||||||
self.prim_counts = dict(d)
|
self.prim_counts = dict(d)
|
||||||
|
|
||||||
self.pi_s_locs = np.flatnonzero(self.c_locs[self.ppi_offset+np.arange(len(self.circuit.io_nodes))] >= 0)
|
self.pi_s_locs = np.flatnonzero(self.c_locs[self.ppi_offset+np.arange(len(self.circuit.io_nodes))] >= 0)
|
||||||
"""Locations in pattern array (axis 0) of all primary inputs (excluding flip-flops)."""
|
|
||||||
self.po_s_locs = np.flatnonzero(self.c_locs[self.ppo_offset+np.arange(len(self.circuit.io_nodes))] >= 0)
|
self.po_s_locs = np.flatnonzero(self.c_locs[self.ppo_offset+np.arange(len(self.circuit.io_nodes))] >= 0)
|
||||||
"""Locations in pattern array (axis 0) of all primary outputs (excluding flip-flops)."""
|
|
||||||
self.ppio_s_locs = np.arange(len(self.circuit.io_nodes), self.s_len)
|
self.ppio_s_locs = np.arange(len(self.circuit.io_nodes), self.s_len)
|
||||||
"""Locations in pattern array (axis 0) of all flip-flops. Pseudo-primary inputs (PPI) and pseudo-primary outputs (PPO) share the same location."""
|
|
||||||
|
|
||||||
self.pippi_s_locs = np.concatenate([self.pi_s_locs, self.ppio_s_locs])
|
self.pippi_s_locs = np.concatenate([self.pi_s_locs, self.ppio_s_locs])
|
||||||
"""Locations in pattern array (axis 0) of all primary and pseudo-primary inputs (input ports and flip-flops)."""
|
|
||||||
self.poppo_s_locs = np.concatenate([self.po_s_locs, self.ppio_s_locs])
|
self.poppo_s_locs = np.concatenate([self.po_s_locs, self.ppio_s_locs])
|
||||||
"""Locations in pattern array (axis 0) of all primary and pseudo-primary outputs (output ports and flip-flops)."""
|
|
||||||
|
|
||||||
self.pi_c_locs = self.c_locs[self.ppi_offset+self.pi_s_locs]
|
self.pi_c_locs = self.c_locs[self.ppi_offset+self.pi_s_locs]
|
||||||
self.po_c_locs = self.c_locs[self.ppo_offset+self.po_s_locs]
|
self.po_c_locs = self.c_locs[self.ppo_offset+self.po_s_locs]
|
||||||
@@ -356,12 +343,3 @@ class SimOps:
|
|||||||
|
|
||||||
self.pippi_c_locs = np.concatenate([self.pi_c_locs, self.ppi_c_locs])
|
self.pippi_c_locs = np.concatenate([self.pi_c_locs, self.ppi_c_locs])
|
||||||
self.poppo_c_locs = np.concatenate([self.po_c_locs, self.ppo_c_locs])
|
self.poppo_c_locs = np.concatenate([self.po_c_locs, self.ppo_c_locs])
|
||||||
|
|
||||||
def cone_ops(self, origin_lines: set[int]):
|
|
||||||
active_lines = {l for l in origin_lines}
|
|
||||||
ops = []
|
|
||||||
for kind, out0, in0, in1, in2, in3, *params in self.ops:
|
|
||||||
if in0 in active_lines or in1 in active_lines or in2 in active_lines or in3 in active_lines or out0 in origin_lines:
|
|
||||||
ops.append((kind, out0, in0, in1, in2, in3, *params))
|
|
||||||
active_lines.add(out0)
|
|
||||||
return np.asarray(ops, dtype=np.int32)
|
|
||||||
+5
-7
@@ -106,7 +106,7 @@ class StilFile:
|
|||||||
tests[pi_map, i] = logic.mvarray(p.capture['_pi'][0])
|
tests[pi_map, i] = logic.mvarray(p.capture['_pi'][0])
|
||||||
return tests
|
return tests
|
||||||
|
|
||||||
def tests_loc(self, circuit, tlib, init_filter=None, launch_filter=None):
|
def tests_loc(self, circuit, init_filter=None, launch_filter=None):
|
||||||
"""Assembles and returns a LoC scan test pattern set for given circuit.
|
"""Assembles and returns a LoC scan test pattern set for given circuit.
|
||||||
|
|
||||||
This function assumes a launch-on-capture (LoC) delay test.
|
This function assumes a launch-on-capture (LoC) delay test.
|
||||||
@@ -139,9 +139,7 @@ class StilFile:
|
|||||||
init[scan_maps[si_port], i] = pattern
|
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])
|
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)
|
if init_filter: init = init_filter(init)
|
||||||
circuit_resolved = circuit.copy()
|
sim8v = LogicSim(circuit, init.shape[-1], m=8)
|
||||||
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[0] = logic.mv_to_bp(init)
|
||||||
sim8v.s_to_c()
|
sim8v.s_to_c()
|
||||||
sim8v.c_prop()
|
sim8v.c_prop()
|
||||||
@@ -265,12 +263,12 @@ GRAMMAR = r"""
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def parse(text) -> StilFile:
|
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) # type: ignore
|
return Lark(GRAMMAR, parser="lalr", transformer=StilTransformer()).parse(text)
|
||||||
|
|
||||||
|
|
||||||
def load(file) -> StilFile:
|
def load(file):
|
||||||
"""Parses the contents of ``file`` and returns a :class:`StilFile` object.
|
"""Parses the contents of ``file`` and returns a :class:`StilFile` object.
|
||||||
|
|
||||||
Files with `.gz`-suffix are decompressed on-the-fly.
|
Files with `.gz`-suffix are decompressed on-the-fly.
|
||||||
|
|||||||
+9
-224
@@ -10,6 +10,7 @@ from itertools import product
|
|||||||
|
|
||||||
from . import bench
|
from . import bench
|
||||||
|
|
||||||
|
|
||||||
class TechLib:
|
class TechLib:
|
||||||
"""Class for standard cell library definitions.
|
"""Class for standard cell library definitions.
|
||||||
|
|
||||||
@@ -38,11 +39,9 @@ class TechLib:
|
|||||||
else:
|
else:
|
||||||
pin_dict[n.name] = (o_idx, True)
|
pin_dict[n.name] = (o_idx, True)
|
||||||
o_idx += 1
|
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]
|
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)]:
|
for name in [''.join(item) for item in product(*parts)]:
|
||||||
self.cells[name] = (c, pin_dict, has_dff, has_latch)
|
self.cells[name] = (c, pin_dict)
|
||||||
|
|
||||||
def pin_index(self, kind, pin):
|
def pin_index(self, kind, pin):
|
||||||
"""Returns a pin list position for a given node kind and pin name."""
|
"""Returns a pin list position for a given node kind and pin name."""
|
||||||
@@ -50,13 +49,13 @@ class TechLib:
|
|||||||
assert pin in self.cells[kind][1], f'Unknown pin: {pin} for cell {kind}'
|
assert pin in self.cells[kind][1], f'Unknown pin: {pin} for cell {kind}'
|
||||||
return self.cells[kind][1][pin][0]
|
return self.cells[kind][1][pin][0]
|
||||||
|
|
||||||
def pin_name(self, kind: str, pos: int, output=False) -> str:
|
def pin_name(self, kind, pos, output=False):
|
||||||
"""Returns the pin name for a given node kind, list position, and direction."""
|
"""Returns the pin name for a given node kind, list position, and direction."""
|
||||||
assert kind in self.cells, f'Unknown cell: {kind}'
|
assert kind in self.cells, f'Unknown cell: {kind}'
|
||||||
for name, (ppos, isout) in self.cells[kind][1].items():
|
for name, (ppos, isout) in self.cells[kind][1].items():
|
||||||
if isout == output and ppos == pos:
|
if isout == output and ppos == pos:
|
||||||
return name
|
return name
|
||||||
raise ValueError(f'Invalid {"output" if output else "input"} pin position {pos} for cell: {kind}')
|
return None
|
||||||
|
|
||||||
def pin_is_output(self, kind, pin):
|
def pin_is_output(self, kind, pin):
|
||||||
"""Returns True, if given pin name of a node kind is an output."""
|
"""Returns True, if given pin name of a node kind is an output."""
|
||||||
@@ -64,26 +63,6 @@ class TechLib:
|
|||||||
assert pin in self.cells[kind][1], f'Unknown pin: {pin} for cell {kind}'
|
assert pin in self.cells[kind][1], f'Unknown pin: {pin} for cell {kind}'
|
||||||
return self.cells[kind][1][pin][1]
|
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"""
|
KYUPY = TechLib(r"""
|
||||||
BUF1 input(i0) output(o) o=BUF1(i0) ;
|
BUF1 input(i0) output(o) o=BUF1(i0) ;
|
||||||
@@ -120,7 +99,6 @@ 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) ;
|
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) ;
|
MUX21 input(i0,i1,i2) output(o) o=MUX21(i0,i1,i2) ;
|
||||||
DFF input(D,CLK) output(Q) Q=DFF(D,CLK) ;
|
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.
|
"""A synthetic library of all KyuPy simulation primitives.
|
||||||
"""
|
"""
|
||||||
@@ -150,7 +128,7 @@ OAI21X1 input(A0,A1,B0) output(Y) Y=OAI21(A0,A1,B0) ;
|
|||||||
OAI22X1 input(A0,A1,B0,B1) output(Y) Y=OAI22(A0,A1,B0,B1) ;
|
OAI22X1 input(A0,A1,B0,B1) output(Y) Y=OAI22(A0,A1,B0,B1) ;
|
||||||
OAI33X1 input(A0,A1,A2,B0,B1,B2) output(Y) AA=OR2(A0,A1) BB=OR2(B0,B1) Y=OAI22(AA,A2,BB,B2) ;
|
OAI33X1 input(A0,A1,A2,B0,B1,B2) output(Y) AA=OR2(A0,A1) BB=OR2(B0,B1) Y=OAI22(AA,A2,BB,B2) ;
|
||||||
ADDFX1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
|
ADDFX1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
|
||||||
ADDHX1 input(A,B) output(CO,S) CO=AND2(A,B) S=XOR2(A,B) ;
|
ADDHX1 input(A,B) output(CO,S) CO=XOR2(A,B) S=AND2(A,B) ;
|
||||||
|
|
||||||
DFFX1 input(CK,D) output(Q,QN) Q=DFF(D,CK) QN=INV1(Q) ;
|
DFFX1 input(CK,D) output(Q,QN) Q=DFF(D,CK) QN=INV1(Q) ;
|
||||||
DFFSRX1 input(CK,D,RN,SN) output(Q,QN) DR=AND2(D,RN) SET=INV1(SN) DRS=OR2(DR,SET) Q=DFF(DRS,CK) QN=INV1(Q) ;
|
DFFSRX1 input(CK,D,RN,SN) output(Q,QN) DR=AND2(D,RN) SET=INV1(SN) DRS=OR2(DR,SET) Q=DFF(DRS,CK) QN=INV1(Q) ;
|
||||||
@@ -194,7 +172,7 @@ XNOR2_X1 input(A1,A2) output(ZN) ZN=XNOR2(A1,A2) ;
|
|||||||
|
|
||||||
MUX2_X1 input(I0,I1,S) output(Z) Z=MUX21(I0,I1,S) ;
|
MUX2_X1 input(I0,I1,S) output(Z) Z=MUX21(I0,I1,S) ;
|
||||||
|
|
||||||
HA_X1 input(A,B) output(CO,S) CO=AND2(A,B) S=XOR2(A,B) ;
|
HA_X1 input(A,B) output(CO,S) CO=XOR2(A,B) S=AND2(A,B) ;
|
||||||
FA_X1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(CI,A,B) ;
|
FA_X1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(CI,A,B) ;
|
||||||
|
|
||||||
AOI21_X{1,2} input(A1,A2,B) output(ZN) ZN=AOI21(A1,A2,B) ;
|
AOI21_X{1,2} input(A1,A2,B) output(ZN) ZN=AOI21(A1,A2,B) ;
|
||||||
@@ -247,7 +225,7 @@ XNOR2_X{1,2} input(A,B) output(ZN) ZN=XNOR2(A,B) ;
|
|||||||
|
|
||||||
MUX2_X{1,2} input(A,B,S) output(Z) Z=MUX21(A,B,S) ;
|
MUX2_X{1,2} input(A,B,S) output(Z) Z=MUX21(A,B,S) ;
|
||||||
|
|
||||||
HA_X1 input(A,B) output(CO,S) CO=AND2(A,B) S=XOR2(A,B) ;
|
HA_X1 input(A,B) output(CO,S) CO=XOR2(A,B) S=AND2(A,B) ;
|
||||||
FA_X1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(CI,A,B) ;
|
FA_X1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(CI,A,B) ;
|
||||||
|
|
||||||
AOI21_X{1,2,4} input(A,B1,B2) output(ZN) ZN=AOI21(B1,B2,A) ;
|
AOI21_X{1,2,4} input(A,B1,B2) output(ZN) ZN=AOI21(B1,B2,A) ;
|
||||||
@@ -362,7 +340,7 @@ MUX41X{1,2}$ input(A1,A2,A3,A4,S0,S1) output(Y) A=MUX21(A1,A2,S0) B=MUX21(A3,A4,
|
|||||||
|
|
||||||
DEC24X{1,2}$ input(A0,A1) output(Y0,Y1,Y2,Y3) A0B=INV1(A0) A1B=INV1(A1) Y0=NOR2(A0,A1) Y1=AND(A0,A1B) Y2=AND(A0B,A1) Y3=AND(A0,A1) ;
|
DEC24X{1,2}$ input(A0,A1) output(Y0,Y1,Y2,Y3) A0B=INV1(A0) A1B=INV1(A1) Y0=NOR2(A0,A1) Y1=AND(A0,A1B) Y2=AND(A0B,A1) Y3=AND(A0,A1) ;
|
||||||
FADDX{1,2}$ input(A,B,CI) output(S,CO) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
|
FADDX{1,2}$ input(A,B,CI) output(S,CO) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
|
||||||
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=AND2(A0,B0) SO=XOR2(A0,B0) ;
|
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=XOR2(A0,B0) SO=AND2(A0,B0) ;
|
||||||
|
|
||||||
{,AO}DFFARX{1,2}$ input(D,CLK,RSTB) output(Q,QN) DR=AND2(D,RSTB) Q=DFF(DR,CLK) QN=INV1(Q) ;
|
{,AO}DFFARX{1,2}$ input(D,CLK,RSTB) output(Q,QN) DR=AND2(D,RSTB) Q=DFF(DR,CLK) QN=INV1(Q) ;
|
||||||
DFFASRX{1,2}$ input(D,CLK,RSTB,SETB) output(Q,QN) DR=AND2(D,RSTB) SET=INV1(SETB) DRS=OR2(DR,SET) Q=DFF(DRS,CLK) QN=INV1(Q) ;
|
DFFASRX{1,2}$ input(D,CLK,RSTB,SETB) output(Q,QN) DR=AND2(D,RSTB) SET=INV1(SETB) DRS=OR2(DR,SET) Q=DFF(DRS,CLK) QN=INV1(Q) ;
|
||||||
@@ -452,7 +430,7 @@ MUX41X{1,2}$ input(IN1,IN2,IN3,IN4,S0,S1) output(Q) A=MUX21(IN1,IN2,S0) B=MUX21(
|
|||||||
|
|
||||||
DEC24X{1,2}$ input(IN1,IN2) output(Q0,Q1,Q2,Q3) IN1B=INV1(IN1) IN2B=INV1(IN2) Q0=NOR2(IN1,IN2) Q1=AND(IN1,IN2B) Q2=AND(IN1B,IN2) Q3=AND(IN1,IN2) ;
|
DEC24X{1,2}$ input(IN1,IN2) output(Q0,Q1,Q2,Q3) IN1B=INV1(IN1) IN2B=INV1(IN2) Q0=NOR2(IN1,IN2) Q1=AND(IN1,IN2B) Q2=AND(IN1B,IN2) Q3=AND(IN1,IN2) ;
|
||||||
FADDX{1,2}$ input(A,B,CI) output(S,CO) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
|
FADDX{1,2}$ input(A,B,CI) output(S,CO) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
|
||||||
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=AND2(A0,B0) SO=XOR2(A0,B0) ;
|
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=XOR2(A0,B0) SO=AND2(A0,B0) ;
|
||||||
|
|
||||||
{,AO}DFFARX{1,2}$ input(D,CLK,RSTB) output(Q,QN) DR=AND2(D,RSTB) Q=DFF(DR,CLK) QN=INV1(Q) ;
|
{,AO}DFFARX{1,2}$ input(D,CLK,RSTB) output(Q,QN) DR=AND2(D,RSTB) Q=DFF(DR,CLK) QN=INV1(Q) ;
|
||||||
DFFASRX{1,2}$ input(D,CLK,RSTB,SETB) output(Q,QN) DR=AND2(D,RSTB) SET=INV1(SETB) DRS=OR2(DR,SET) Q=DFF(DRS,CLK) QN=INV1(Q) ;
|
DFFASRX{1,2}$ input(D,CLK,RSTB,SETB) output(Q,QN) DR=AND2(D,RSTB) SET=INV1(SETB) DRS=OR2(DR,SET) Q=DFF(DRS,CLK) QN=INV1(Q) ;
|
||||||
@@ -472,196 +450,3 @@ LATCHX{1,2}$ input(D,CLK) output(Q,QN) Q=LATCH(D,CLK) QN=INV1(Q) ;
|
|||||||
"""The SAED 90nm educational technology library.
|
"""The SAED 90nm educational technology library.
|
||||||
It defines all cells except: negative-edge flip-flops, tri-state, latches, clock gating, level shifters
|
It defines all cells except: negative-edge flip-flops, tri-state, latches, clock gating, level shifters
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
SKY130 = TechLib(r"""
|
|
||||||
$decap_{3,4,6,8,12} ;
|
|
||||||
$fill_{1,2,4,8} ;
|
|
||||||
$tap_{1,2} ;
|
|
||||||
$tapvgnd_1 ;
|
|
||||||
$tapvgnd2_1 ;
|
|
||||||
$tapvpwrvgnd_1 ;
|
|
||||||
$lpflow_decapkapwr_{3,4,6,8,12} ;
|
|
||||||
$lpflow_bleeder_1 input(SHORT) ;
|
|
||||||
|
|
||||||
$conb_1 output(HI,LO) HI=__const1__() LO=__const0__() ;
|
|
||||||
$macro_sparecell output(LO) LO=__const0__() ;
|
|
||||||
|
|
||||||
$diode_2 input(DIODE) ;
|
|
||||||
$probe_p_8 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$probec_p_8 input(A) output(X) X=BUF1(A) ;
|
|
||||||
|
|
||||||
$inv_{1,2,4,6,8,12,16} input(A) output(Y) Y=INV1(A) ;
|
|
||||||
$buf_{1,2,4,6,8,12,16} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$bufbuf_{8,16} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$bufinv_{8,16} input(A) output(Y) Y=INV1(A) ;
|
|
||||||
$clkbuf_{1,2,4,8,16} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$clkinv_{1,2,4,8,16} input(A) output(Y) Y=INV1(A) ;
|
|
||||||
$clkinvlp_{2,4} input(A) output(Y) Y=INV1(A) ;
|
|
||||||
$clkdlybuf4s15_{1,2} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$clkdlybuf4s18_{1,2} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$clkdlybuf4s25_{1,2} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$clkdlybuf4s50_{1,2} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$dlygate4sd1_1 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$dlygate4sd2_1 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$dlygate4sd3_1 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$dlymetal6s2s_1 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$dlymetal6s4s_1 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$dlymetal6s6s_1 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$lpflow_clkbufkapwr_{1,2,4,8,16} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$lpflow_clkinvkapwr_{1,2,4,8,16} input(A) output(Y) Y=INV1(A) ;
|
|
||||||
$lpflow_lsbuf_lh_hl_isowell_tap_{1,2,4} input(A) output(X) X=BUF1(A) ;
|
|
||||||
$lpflow_lsbuf_lh_isowell_4 input(A) output(X) X=BUF1(A) ;
|
|
||||||
$lpflow_lsbuf_lh_isowell_tap_{1,2,4} input(A) output(X) X=BUF1(A) ;
|
|
||||||
|
|
||||||
$and2_{0,1,2,4} input(A,B) output(X) X=AND2(A,B) ;
|
|
||||||
$and2b_{1,2,4} input(A_N,B) output(X) AN=INV1(A_N) X=AND2(AN,B) ;
|
|
||||||
$and3_{1,2,4} input(A,B,C) output(X) X=AND3(A,B,C) ;
|
|
||||||
$and3b_{1,2,4} input(A_N,B,C) output(X) AN=INV1(A_N) X=AND3(AN,B,C) ;
|
|
||||||
$and4_{1,2,4} input(A,B,C,D) output(X) X=AND4(A,B,C,D) ;
|
|
||||||
$and4b_{1,2,4} input(A_N,B,C,D) output(X) AN=INV1(A_N) X=AND4(AN,B,C,D) ;
|
|
||||||
$and4bb_{1,2,4} input(A_N,B_N,C,D) output(X) AN=INV1(A_N) BN=INV1(B_N) X=AND4(AN,BN,C,D) ;
|
|
||||||
|
|
||||||
$or2_{0,1,2,4} input(A,B) output(X) X=OR2(A,B) ;
|
|
||||||
$or2b_{1,2,4} input(A,B_N) output(X) BN=INV1(B_N) X=OR2(A,BN) ;
|
|
||||||
$or3_{1,2,4} input(A,B,C) output(X) X=OR3(A,B,C) ;
|
|
||||||
$or3b_{1,2,4} input(A,B,C_N) output(X) CN=INV1(C_N) X=OR3(A,B,CN) ;
|
|
||||||
$or4_{1,2,4} input(A,B,C,D) output(X) X=OR4(A,B,C,D) ;
|
|
||||||
$or4b_{1,2,4} input(A,B,C,D_N) output(X) DN=INV1(D_N) X=OR4(A,B,C,DN) ;
|
|
||||||
$or4bb_{1,2,4} input(A,B,C_N,D_N) output(X) CN=INV1(C_N) DN=INV1(D_N) X=OR4(A,B,CN,DN) ;
|
|
||||||
|
|
||||||
$nand2_{1,2,4,8} input(A,B) output(Y) Y=NAND2(A,B) ;
|
|
||||||
$nand2b_{1,2,4} input(A_N,B) output(Y) AN=INV1(A_N) Y=NAND2(AN,B) ;
|
|
||||||
$nand3_{1,2,4} input(A,B,C) output(Y) Y=NAND3(A,B,C) ;
|
|
||||||
$nand3b_{1,2,4} input(A_N,B,C) output(Y) AN=INV1(A_N) Y=NAND3(AN,B,C) ;
|
|
||||||
$nand4_{1,2,4} input(A,B,C,D) output(Y) Y=NAND4(A,B,C,D) ;
|
|
||||||
$nand4b_{1,2,4} input(A_N,B,C,D) output(Y) AN=INV1(A_N) Y=NAND4(AN,B,C,D) ;
|
|
||||||
$nand4bb_{1,2,4} input(A_N,B_N,C,D) output(Y) AN=INV1(A_N) BN=INV1(B_N) Y=NAND4(AN,BN,C,D) ;
|
|
||||||
|
|
||||||
$nor2_{1,2,4,8} input(A,B) output(Y) Y=NOR2(A,B) ;
|
|
||||||
$nor2b_{1,2,4} input(A,B_N) output(Y) BN=INV1(B_N) Y=NOR2(A,BN) ;
|
|
||||||
$nor3_{1,2,4} input(A,B,C) output(Y) Y=NOR3(A,B,C) ;
|
|
||||||
$nor3b_{1,2,4} input(A,B,C_N) output(Y) CN=INV1(C_N) Y=NOR3(A,B,CN) ;
|
|
||||||
$nor4_{1,2,4} input(A,B,C,D) output(Y) Y=NOR4(A,B,C,D) ;
|
|
||||||
$nor4b_{1,2,4} input(A,B,C,D_N) output(Y) DN=INV1(D_N) Y=NOR4(A,B,C,DN) ;
|
|
||||||
$nor4bb_{1,2,4} input(A,B,C_N,D_N) output(Y) CN=INV1(C_N) DN=INV1(D_N) Y=NOR4(A,B,CN,DN) ;
|
|
||||||
|
|
||||||
$xor2_{1,2,4} input(A,B) output(X) X=XOR2(A,B) ;
|
|
||||||
$xor3_{1,2,4} input(A,B,C) output(X) X=XOR3(A,B,C) ;
|
|
||||||
$xnor2_{1,2,4} input(A,B) output(Y) Y=XNOR2(A,B) ;
|
|
||||||
$xnor3_{1,2,4} input(A,B,C) output(X) X=XNOR3(A,B,C) ;
|
|
||||||
|
|
||||||
$maj3_{1,2,4} input(A,B,C) output(X) AB=AND2(A,B) BC=AND2(B,C) AC=AND2(A,C) X=OR3(AB,BC,AC) ;
|
|
||||||
|
|
||||||
$mux2_{1,2,4,8} input(A0,A1,S) output(X) X=MUX21(A0,A1,S) ;
|
|
||||||
$mux2i_{1,2,4} input(A0,A1,S) output(Y) M=MUX21(A0,A1,S) Y=INV1(M) ;
|
|
||||||
$mux4_{1,2,4} input(A0,A1,A2,A3,S0,S1) output(X) M0=MUX21(A0,A1,S0) M1=MUX21(A2,A3,S0) X=MUX21(M0,M1,S1) ;
|
|
||||||
|
|
||||||
$ha_{1,2,4} input(A,B) output(COUT,SUM) SUM=XOR2(A,B) COUT=AND2(A,B) ;
|
|
||||||
$fa_{1,2,4} input(A,B,CIN) output(COUT,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CIN) COUT=AO22(A,B,AB,CIN) ;
|
|
||||||
$fah_1 input(A,B,CI) output(COUT,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CI) COUT=AO22(A,B,AB,CI) ;
|
|
||||||
$fahcin_1 input(A,B,CIN) output(COUT,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CIN) COUT=AO22(A,B,AB,CIN) ;
|
|
||||||
$fahcon_1 input(A,B,CI) output(COUT_N,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CI) T=AO22(A,B,AB,CI) COUT_N=INV1(T) ;
|
|
||||||
|
|
||||||
$a21o_{1,2,4} input(A1,A2,B1) output(X) X=AO21(A1,A2,B1) ;
|
|
||||||
$a21bo_{1,2,4} input(A1,A2,B1_N) output(X) BN=INV1(B1_N) X=AO21(A1,A2,BN) ;
|
|
||||||
$a22o_{1,2,4} input(A1,A2,B1,B2) output(X) X=AO22(A1,A2,B1,B2) ;
|
|
||||||
$a2bb2o_{1,2,4} input(A1_N,A2_N,B1,B2) output(X) AN1=INV1(A1_N) AN2=INV1(A2_N) X=AO22(AN1,AN2,B1,B2) ;
|
|
||||||
$a211o_{1,2,4} input(A1,A2,B1,C1) output(X) X=AO211(A1,A2,B1,C1) ;
|
|
||||||
$a221o_{1,2,4} input(A1,A2,B1,B2,C1) output(X) T=AO22(A1,A2,B1,B2) X=OR2(T,C1) ;
|
|
||||||
$a31o_{1,2,4} input(A1,A2,A3,B1) output(X) AA=AND3(A1,A2,A3) X=OR2(AA,B1) ;
|
|
||||||
$a311o_{1,2,4} input(A1,A2,A3,B1,C1) output(X) T=AND2(A1,A2) X=AO211(T,A3,B1,C1) ;
|
|
||||||
$a32o_{1,2,4} input(A1,A2,A3,B1,B2) output(X) T=AND2(A1,A2) X=AO22(T,A3,B1,B2) ;
|
|
||||||
$a41o_{1,2,4} input(A1,A2,A3,A4,B1) output(X) T1=AND2(A1,A2) T2=AND2(T1,A3) X=AO21(T2,A4,B1) ;
|
|
||||||
$a2111o_{1,2,4} input(A1,A2,B1,C1,D1) output(X) T=AO211(A1,A2,B1,C1) X=OR2(T,D1) ;
|
|
||||||
|
|
||||||
$a21oi_{1,2,4} input(A1,A2,B1) output(Y) Y=AOI21(A1,A2,B1) ;
|
|
||||||
$a21boi_{0,1,2,4} input(A1,A2,B1_N) output(Y) BN=INV1(B1_N) Y=AOI21(A1,A2,BN) ;
|
|
||||||
$a22oi_{1,2,4} input(A1,A2,B1,B2) output(Y) Y=AOI22(A1,A2,B1,B2) ;
|
|
||||||
$a2bb2oi_{1,2,4} input(A1_N,A2_N,B1,B2) output(Y) AN1=INV1(A1_N) AN2=INV1(A2_N) Y=AOI22(AN1,AN2,B1,B2) ;
|
|
||||||
$a211oi_{1,2,4} input(A1,A2,B1,C1) output(Y) Y=AOI211(A1,A2,B1,C1) ;
|
|
||||||
$a221oi_{1,2,4} input(A1,A2,B1,B2,C1) output(Y) T=AO22(A1,A2,B1,B2) Y=NOR2(T,C1) ;
|
|
||||||
$a222oi_1 input(A1,A2,B1,B2,C1,C2) output(Y) AB=AO22(A1,A2,B1,B2) Y=AOI21(C1,C2,AB) ;
|
|
||||||
$a31oi_{1,2,4} input(A1,A2,A3,B1) output(Y) AA=AND3(A1,A2,A3) Y=NOR2(AA,B1) ;
|
|
||||||
$a311oi_{1,2,4} input(A1,A2,A3,B1,C1) output(Y) T=AND2(A1,A2) Y=AOI211(T,A3,B1,C1) ;
|
|
||||||
$a32oi_{1,2,4} input(A1,A2,A3,B1,B2) output(Y) T=AND2(A1,A2) Y=AOI22(T,A3,B1,B2) ;
|
|
||||||
$a41oi_{1,2,4} input(A1,A2,A3,A4,B1) output(Y) T1=AND2(A1,A2) T2=AND2(T1,A3) Y=AOI21(T2,A4,B1) ;
|
|
||||||
$a2111oi_{0,1,2,4} input(A1,A2,B1,C1,D1) output(Y) T=AO211(A1,A2,B1,C1) Y=NOR2(T,D1) ;
|
|
||||||
|
|
||||||
$o21a_{1,2,4} input(A1,A2,B1) output(X) X=OA21(A1,A2,B1) ;
|
|
||||||
$o21ba_{1,2,4} input(A1,A2,B1_N) output(X) BN=INV1(B1_N) X=OA21(A1,A2,BN) ;
|
|
||||||
$o22a_{1,2,4} input(A1,A2,B1,B2) output(X) X=OA22(A1,A2,B1,B2) ;
|
|
||||||
$o2bb2a_{1,2,4} input(A1_N,A2_N,B1,B2) output(X) T=NAND2(A1_N,A2_N) X=OA21(B1,B2,T) ;
|
|
||||||
$o211a_{1,2,4} input(A1,A2,B1,C1) output(X) X=OA211(A1,A2,B1,C1) ;
|
|
||||||
$o221a_{1,2,4} input(A1,A2,B1,B2,C1) output(X) T=OA22(A1,A2,B1,B2) X=AND2(T,C1) ;
|
|
||||||
$o31a_{1,2,4} input(A1,A2,A3,B1) output(X) T=OR3(A1,A2,A3) X=AND2(T,B1) ;
|
|
||||||
$o311a_{1,2,4} input(A1,A2,A3,B1,C1) output(X) T=OR2(A1,A2) X=OA211(T,A3,B1,C1) ;
|
|
||||||
$o32a_{1,2,4} input(A1,A2,A3,B1,B2) output(X) T=OR3(A1,A2,A3) X=OA21(B1,B2,T) ;
|
|
||||||
$o41a_{1,2,4} input(A1,A2,A3,A4,B1) output(X) T=OR4(A1,A2,A3,A4) X=AND2(T,B1) ;
|
|
||||||
$o2111a_{1,2,4} input(A1,A2,B1,C1,D1) output(X) T=OA211(A1,A2,B1,C1) X=AND2(T,D1) ;
|
|
||||||
|
|
||||||
$o21ai_{0,1,2,4} input(A1,A2,B1) output(Y) Y=OAI21(A1,A2,B1) ;
|
|
||||||
$o21bai_{1,2,4} input(A1,A2,B1_N) output(Y) BN=INV1(B1_N) Y=OAI21(A1,A2,BN) ;
|
|
||||||
$o22ai_{1,2,4} input(A1,A2,B1,B2) output(Y) Y=OAI22(A1,A2,B1,B2) ;
|
|
||||||
$o2bb2ai_{1,2,4} input(A1_N,A2_N,B1,B2) output(Y) T=NAND2(A1_N,A2_N) Y=OAI21(B1,B2,T) ;
|
|
||||||
$o211ai_{1,2,4} input(A1,A2,B1,C1) output(Y) Y=OAI211(A1,A2,B1,C1) ;
|
|
||||||
$o221ai_{1,2,4} input(A1,A2,B1,B2,C1) output(Y) T=OA22(A1,A2,B1,B2) Y=NAND2(T,C1) ;
|
|
||||||
$o31ai_{1,2,4} input(A1,A2,A3,B1) output(Y) T=OR3(A1,A2,A3) Y=NAND2(T,B1) ;
|
|
||||||
$o311ai_{0,1,2,4} input(A1,A2,A3,B1,C1) output(Y) T=OR2(A1,A2) Y=OAI211(T,A3,B1,C1) ;
|
|
||||||
$o32ai_{1,2,4} input(A1,A2,A3,B1,B2) output(Y) T=OR3(A1,A2,A3) Y=OAI21(B1,B2,T) ;
|
|
||||||
$o41ai_{1,2,4} input(A1,A2,A3,A4,B1) output(Y) T=OR4(A1,A2,A3,A4) Y=NAND2(T,B1) ;
|
|
||||||
$o2111ai_{1,2,4} input(A1,A2,B1,C1,D1) output(Y) T=OA211(A1,A2,B1,C1) Y=NAND2(T,D1) ;
|
|
||||||
|
|
||||||
$dfxtp_{1,2,4} input(CLK,D) output(Q) Q=DFF(D,CLK) ;
|
|
||||||
$dfxbp_{1,2} input(CLK,D) output(Q,Q_N) Q=DFF(D,CLK) Q_N=INV1(Q) ;
|
|
||||||
$dfrtp_{1,2,4} input(CLK,D,RESET_B) output(Q) DR=AND2(D,RESET_B) Q=DFF(DR,CLK) ;
|
|
||||||
$dfrbp_{1,2} input(CLK,D,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) Q=DFF(DR,CLK) Q_N=INV1(Q) ;
|
|
||||||
$dfstp_{1,2,4} input(CLK,D,SET_B) output(Q) S=INV1(SET_B) DS=OR2(D,S) Q=DFF(DS,CLK) ;
|
|
||||||
$dfsbp_{1,2} input(CLK,D,SET_B) output(Q,Q_N) S=INV1(SET_B) DS=OR2(D,S) Q=DFF(DS,CLK) Q_N=INV1(Q) ;
|
|
||||||
$dfbbp_1 input(CLK,D,SET_B,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) Q=DFF(DRS,CLK) Q_N=INV1(Q) ;
|
|
||||||
$dfrtn_1 input(CLK_N,D,RESET_B) output(Q) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) Q=DFF(DR,CLKN) ;
|
|
||||||
$dfbbn_{1,2} input(CLK_N,D,SET_B,RESET_B) output(Q,Q_N) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) Q=DFF(DRS,CLKN) Q_N=INV1(Q) ;
|
|
||||||
|
|
||||||
$sdfxtp_{1,2,4} input(CLK,D,SCD,SCE) output(Q) DI=MUX21(D,SCD,SCE) Q=DFF(DI,CLK) ;
|
|
||||||
$sdfxbp_{1,2} input(CLK,D,SCD,SCE) output(Q,Q_N) DI=MUX21(D,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
|
|
||||||
$sdfrtp_{1,2,4} input(CLK,D,SCD,SCE,RESET_B) output(Q) DR=AND2(D,RESET_B) DI=MUX21(DR,SCD,SCE) Q=DFF(DI,CLK) ;
|
|
||||||
$sdfrbp_{1,2} input(CLK,D,SCD,SCE,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) DI=MUX21(DR,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
|
|
||||||
$sdfstp_{1,2,4} input(CLK,D,SCD,SCE,SET_B) output(Q) S=INV1(SET_B) DS=OR2(D,S) DI=MUX21(DS,SCD,SCE) Q=DFF(DI,CLK) ;
|
|
||||||
$sdfsbp_{1,2} input(CLK,D,SCD,SCE,SET_B) output(Q,Q_N) S=INV1(SET_B) DS=OR2(D,S) DI=MUX21(DS,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
|
|
||||||
$sdfbbp_1 input(CLK,D,SCD,SCE,SET_B,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) DI=MUX21(DRS,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
|
|
||||||
$sdfrtn_1 input(CLK_N,D,SCD,SCE,RESET_B) output(Q) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) DI=MUX21(DR,SCD,SCE) Q=DFF(DI,CLKN) ;
|
|
||||||
$sdfbbn_{1,2} input(CLK_N,D,SCD,SCE,SET_B,RESET_B) output(Q,Q_N) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) DI=MUX21(DRS,SCD,SCE) Q=DFF(DI,CLKN) Q_N=INV1(Q) ;
|
|
||||||
|
|
||||||
$edfxtp_1 input(CLK,D,DE) output(Q) GCK=AND2(CLK,DE) Q=DFF(D,GCK) ;
|
|
||||||
$edfxbp_1 input(CLK,D,DE) output(Q,Q_N) GCK=AND2(CLK,DE) Q=DFF(D,GCK) Q_N=INV1(Q) ;
|
|
||||||
$sedfxtp_{1,2,4} input(CLK,D,DE,SCD,SCE) output(Q) DI=MUX21(D,SCD,SCE) GCK=AND2(CLK,DE) Q=DFF(DI,GCK) ;
|
|
||||||
$sedfxbp_{1,2} input(CLK,D,DE,SCD,SCE) output(Q,Q_N) DI=MUX21(D,SCD,SCE) GCK=AND2(CLK,DE) Q=DFF(DI,GCK) Q_N=INV1(Q) ;
|
|
||||||
|
|
||||||
$dlxtp_1 input(D,GATE) output(Q) Q=LATCH(D,GATE) ;
|
|
||||||
$dlxtn_{1,2,4} input(D,GATE_N) output(Q) GN=INV1(GATE_N) Q=LATCH(D,GN) ;
|
|
||||||
$dlxbp_1 input(D,GATE) output(Q,Q_N) Q=LATCH(D,GATE) Q_N=INV1(Q) ;
|
|
||||||
$dlxbn_{1,2} input(D,GATE_N) output(Q,Q_N) GN=INV1(GATE_N) Q=LATCH(D,GN) Q_N=INV1(Q) ;
|
|
||||||
$dlrtp_{1,2,4} input(D,GATE,RESET_B) output(Q) DR=AND2(D,RESET_B) Q=LATCH(DR,GATE) ;
|
|
||||||
$dlrtn_{1,2,4} input(D,GATE_N,RESET_B) output(Q) GN=INV1(GATE_N) DR=AND2(D,RESET_B) Q=LATCH(DR,GN) ;
|
|
||||||
$dlrbp_{1,2} input(D,GATE,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) Q=LATCH(DR,GATE) Q_N=INV1(Q) ;
|
|
||||||
$dlrbn_{1,2} input(D,GATE_N,RESET_B) output(Q,Q_N) GN=INV1(GATE_N) DR=AND2(D,RESET_B) Q=LATCH(DR,GN) Q_N=INV1(Q) ;
|
|
||||||
|
|
||||||
$dlclkp_{1,2,4} input(CLK,GATE) output(GCLK) GCLK=AND2(CLK,GATE) ;
|
|
||||||
$sdlclkp_{1,2,4} input(CLK,GATE,SCE) output(GCLK) G=OR2(GATE,SCE) GCLK=AND2(CLK,G) ;
|
|
||||||
|
|
||||||
$ebufn_{1,2,4,8} input(A,TE_B) output(Z) TE=INV1(TE_B) Z=AND2(A,TE) ;
|
|
||||||
$einvn_{0,1,2,4,8} input(A,TE_B) output(Z) TE=INV1(TE_B) ZI=INV1(A) Z=AND2(ZI,TE) ;
|
|
||||||
$einvp_{1,2,4,8} input(A,TE) output(Z) ZI=INV1(A) Z=AND2(ZI,TE) ;
|
|
||||||
|
|
||||||
$lpflow_inputiso0n_1 input(A,SLEEP_B) output(X) X=AND2(A,SLEEP_B) ;
|
|
||||||
$lpflow_inputiso0p_1 input(A,SLEEP) output(X) SB=INV1(SLEEP) X=AND2(A,SB) ;
|
|
||||||
$lpflow_inputiso1n_1 input(A,SLEEP_B) output(X) X=AND2(A,SLEEP_B) ;
|
|
||||||
$lpflow_inputiso1p_1 input(A,SLEEP) output(X) SB=INV1(SLEEP) X=AND2(A,SB) ;
|
|
||||||
$lpflow_isobufsrc_{1,2,4,8,16} input(A,SLEEP) output(X) AI=INV1(A) X=OR2(AI,SLEEP) ;
|
|
||||||
$lpflow_isobufsrckapwr_16 input(A,SLEEP) output(X) AI=INV1(A) X=OR2(AI,SLEEP) ;
|
|
||||||
$lpflow_inputisolatch_1 input(D,SLEEP_B) output(Q) Q=LATCH(D,SLEEP_B) ;
|
|
||||||
""".replace('$','sky130_fd_sc_hd__'))
|
|
||||||
"""SkyWater 130nm High Density Digital Standard Cells (skywater-pdk-libs-sky130_fd_sc_hd).
|
|
||||||
"""
|
|
||||||
|
|
||||||
techlib_by_name = {k: v for k, v in globals().items() if isinstance(v, TechLib)}
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
"""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))
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return self.path
|
|
||||||
|
|
||||||
|
|
||||||
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: VcdVarMap, steps, data):
|
|
||||||
self.var_map = var_map
|
|
||||||
self.steps = steps
|
|
||||||
self.data = data
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'{{variables: {len(self.var_map.var_list)}, bits: {self.data.shape[0]}, steps: {self.data.shape[1]}}}'
|
|
||||||
|
|
||||||
|
|
||||||
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.value.strip()
|
|
||||||
width = int(width)
|
|
||||||
self.header.add_var(type.value, width, idcode.value, 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, s_len = None) -> VcdData:
|
|
||||||
"""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.
|
|
||||||
:param s_len: length of the first axis of returned pattern data.
|
|
||||||
: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)
|
|
||||||
width = s_len if s_len is not None else var_map.total_width
|
|
||||||
|
|
||||||
chunk_size = 10240
|
|
||||||
chunks = []
|
|
||||||
chunk = np.full((chunk_size, 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, 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)
|
|
||||||
@@ -52,14 +52,13 @@ class VerilogTransformer(Transformer):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def instantiation(args):
|
def instantiation(args):
|
||||||
pinmap = {}
|
pinmap = {}
|
||||||
if args[2] is not None:
|
for idx, pin in enumerate(args[2:]):
|
||||||
for idx, pin in enumerate(args[2:]):
|
p = pin.children[0]
|
||||||
p = pin.children[0]
|
if isinstance(p, tuple): # named pin
|
||||||
if isinstance(p, tuple): # named pin
|
if p[1] is not None:
|
||||||
if p[1] is not None:
|
pinmap[p[0]] = p[1]
|
||||||
pinmap[p[0]] = p[1]
|
else: # unnamed pin
|
||||||
else: # unnamed pin
|
pinmap[idx] = p
|
||||||
pinmap[idx] = p
|
|
||||||
return Instantiation(args[0], args[1], pinmap)
|
return Instantiation(args[0], args[1], pinmap)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -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(int(x), self._block_dim[0]), cdiv(int(y), self._block_dim[1])
|
def _grid_dim(self, x, y): return cdiv(x, self._block_dim[0]), cdiv(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)
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
|
|
||||||
from kyupy import atalanta, logic
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse():
|
|
||||||
text = '\n'.join([
|
|
||||||
'* Test pattern file',
|
|
||||||
'1: 0011',
|
|
||||||
'2: 1100',
|
|
||||||
'1010',
|
|
||||||
])
|
|
||||||
tf = atalanta.parse(text)
|
|
||||||
|
|
||||||
# second-to-last axis = inputs, last axis = patterns
|
|
||||||
assert tf.patterns.shape == (4, 3)
|
|
||||||
assert tf.patterns.dtype == np.uint8
|
|
||||||
assert set(np.unique(tf.patterns)) <= {logic.ZERO, logic.ONE}
|
|
||||||
|
|
||||||
# patterns are stored along the last axis
|
|
||||||
assert list(tf.patterns[:, 0]) == [logic.ZERO, logic.ZERO, logic.ONE, logic.ONE]
|
|
||||||
assert list(tf.patterns[:, 1]) == [logic.ONE, logic.ONE, logic.ZERO, logic.ZERO]
|
|
||||||
assert list(tf.patterns[:, 2]) == [logic.ONE, logic.ZERO, logic.ONE, logic.ZERO]
|
|
||||||
|
|
||||||
|
|
||||||
def test_empty():
|
|
||||||
tf = atalanta.parse('* only comments\n* nothing else\n')
|
|
||||||
assert tf.patterns.shape == (0, 0)
|
|
||||||
assert tf.patterns.dtype == np.uint8
|
|
||||||
@@ -2,7 +2,7 @@ import pickle
|
|||||||
|
|
||||||
from kyupy.circuit import GrowingList, Circuit, Node, Line
|
from kyupy.circuit import GrowingList, Circuit, Node, Line
|
||||||
from kyupy import verilog, bench
|
from kyupy import verilog, bench
|
||||||
from kyupy.techlib import KYUPY, SAED32
|
from kyupy.techlib import SAED32
|
||||||
|
|
||||||
def test_growing_list():
|
def test_growing_list():
|
||||||
gl = GrowingList()
|
gl = GrowingList()
|
||||||
@@ -155,7 +155,7 @@ def test_substitute():
|
|||||||
|
|
||||||
def test_resolve(mydir):
|
def test_resolve(mydir):
|
||||||
c = verilog.load(mydir / 'b15_4ig.v.gz', tlib=SAED32)
|
c = verilog.load(mydir / 'b15_4ig.v.gz', tlib=SAED32)
|
||||||
s_names = [n.name for n in c.s_nodes(SAED32)]
|
s_names = [n.name for n in c.s_nodes]
|
||||||
c.resolve_tlib_cells(SAED32)
|
c.resolve_tlib_cells(SAED32)
|
||||||
s_names_prim = [n.name for n in c.s_nodes(KYUPY)]
|
s_names_prim = [n.name for n in c.s_nodes]
|
||||||
assert s_names == s_names_prim, 'resolve_tlib_cells does not preserve names or order of s_nodes'
|
assert s_names == s_names_prim, 'resolve_tlib_cells does not preserve names or order of s_nodes'
|
||||||
+54
-49
@@ -2,8 +2,8 @@ import numpy as np
|
|||||||
|
|
||||||
from kyupy.logic_sim import LogicSim, LogicSim2V, LogicSim4V, LogicSim6V
|
from kyupy.logic_sim import LogicSim, LogicSim2V, LogicSim4V, LogicSim6V
|
||||||
from kyupy import bench, logic, sim, verilog
|
from kyupy import bench, logic, sim, verilog
|
||||||
from kyupy.logic import mv_str, mvarray, bparray, bp_to_mv, mv_to_bp
|
from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp
|
||||||
from kyupy.techlib import SAED90, KYUPY
|
from kyupy.techlib import SAED90
|
||||||
|
|
||||||
def test_dangling():
|
def test_dangling():
|
||||||
c = verilog.parse('''
|
c = verilog.parse('''
|
||||||
@@ -20,18 +20,6 @@ def test_dangling():
|
|||||||
sim.simulate(p)
|
sim.simulate(p)
|
||||||
assert p[1,0] == logic.ONE
|
assert p[1,0] == logic.ONE
|
||||||
|
|
||||||
def test_used_output():
|
|
||||||
c = bench.parse('input(i0) output(o0) output(o1) o0=BUF1(i0) o1=BUF(o0)')
|
|
||||||
sim = LogicSim2V(c)
|
|
||||||
expect = mvarray('000', '111')
|
|
||||||
actual = sim.simulate(expect.copy())
|
|
||||||
np.testing.assert_array_equal(
|
|
||||||
expect[c.io_locs('o0')],
|
|
||||||
actual[c.io_locs('o0')])
|
|
||||||
np.testing.assert_array_equal(
|
|
||||||
expect[c.io_locs('o1')],
|
|
||||||
actual[c.io_locs('o1')])
|
|
||||||
|
|
||||||
def assert_equal_shape_and_contents(actual, desired):
|
def assert_equal_shape_and_contents(actual, desired):
|
||||||
desired = np.array(desired, dtype=np.uint8)
|
desired = np.array(desired, dtype=np.uint8)
|
||||||
assert actual.shape == desired.shape
|
assert actual.shape == desired.shape
|
||||||
@@ -117,7 +105,7 @@ def test_LogicSim6V_simprims(mydir):
|
|||||||
sim1.simulate(tests1)
|
sim1.simulate(tests1)
|
||||||
sim2.simulate(tests2)
|
sim2.simulate(tests2)
|
||||||
for loc1, loc2 in zip(c1.io_locs('o'), c2.io_locs('o')):
|
for loc1, loc2 in zip(c1.io_locs('o'), c2.io_locs('o')):
|
||||||
n = c1.s_nodes(KYUPY)[loc1]
|
n = c1.s_nodes[loc1]
|
||||||
if (tests1[loc1] != tests2[loc2]).any():
|
if (tests1[loc1] != tests2[loc2]).any():
|
||||||
print(f'Mismatch at output {n}')
|
print(f'Mismatch at output {n}')
|
||||||
np.testing.assert_array_equal(
|
np.testing.assert_array_equal(
|
||||||
@@ -162,12 +150,14 @@ def test_2v():
|
|||||||
o31=OAI211(i0,i1,i2,i3)
|
o31=OAI211(i0,i1,i2,i3)
|
||||||
o32=MUX21(i0,i1,i2)
|
o32=MUX21(i0,i1,i2)
|
||||||
''')
|
''')
|
||||||
s = LogicSim2V(c, 16)
|
s = LogicSim(c, 16, m=2)
|
||||||
s.s_assign[...] = logic.mvarray([f'{i:04b}'+('-'*(s.s_len-4)) for i in range(16)])
|
bpa = logic.bparray([f'{i:04b}'+('-'*(s.s_len-4)) for i in range(16)])
|
||||||
|
s.s[0] = bpa
|
||||||
s.s_to_c()
|
s.s_to_c()
|
||||||
s.c_prop()
|
s.c_prop()
|
||||||
s.c_to_s()
|
s.c_to_s()
|
||||||
for res, exp in zip(logic.packbits(s.s_result[4:], dtype=np.uint32), [
|
mva = logic.bp_to_mv(s.s[1])
|
||||||
|
for res, exp in zip(logic.packbits(mva[4:], dtype=np.uint32), [
|
||||||
sim.BUF1, sim.INV1,
|
sim.BUF1, sim.INV1,
|
||||||
sim.AND2, sim.AND3, sim.AND4,
|
sim.AND2, sim.AND3, sim.AND4,
|
||||||
sim.NAND2, sim.NAND3, sim.NAND4,
|
sim.NAND2, sim.NAND3, sim.NAND4,
|
||||||
@@ -188,17 +178,19 @@ def test_2v():
|
|||||||
|
|
||||||
def test_4v():
|
def test_4v():
|
||||||
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)')
|
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)')
|
||||||
s = LogicSim4V(c, 16)
|
s = LogicSim(c, 16, m=4)
|
||||||
assert s.s_len == 5
|
assert s.s_len == 5
|
||||||
s.s_assign[...] = mvarray(
|
bpa = bparray(
|
||||||
'00---', '01---', '0----', '0X---',
|
'00---', '01---', '0----', '0X---',
|
||||||
'10---', '11---', '1----', '1X---',
|
'10---', '11---', '1----', '1X---',
|
||||||
'-0---', '-1---', '-----', '-X---',
|
'-0---', '-1---', '-----', '-X---',
|
||||||
'X0---', 'X1---', 'X----', 'XX---')
|
'X0---', 'X1---', 'X----', 'XX---')
|
||||||
|
s.s[0] = bpa
|
||||||
s.s_to_c()
|
s.s_to_c()
|
||||||
s.c_prop()
|
s.c_prop()
|
||||||
s.c_to_s()
|
s.c_to_s()
|
||||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
mva = bp_to_mv(s.s[1])
|
||||||
|
assert_equal_shape_and_contents(mva, mvarray(
|
||||||
'--001', '--011', '--0X1', '--0X1',
|
'--001', '--011', '--0X1', '--0X1',
|
||||||
'--010', '--110', '--X10', '--X10',
|
'--010', '--110', '--X10', '--X10',
|
||||||
'--0XX', '--X1X', '--XXX', '--XXX',
|
'--0XX', '--X1X', '--XXX', '--XXX',
|
||||||
@@ -206,34 +198,38 @@ def test_4v():
|
|||||||
|
|
||||||
def test_4v_fault():
|
def test_4v_fault():
|
||||||
c = bench.parse('input(x, y) output(a) a=and(x,y)')
|
c = bench.parse('input(x, y) output(a) a=and(x,y)')
|
||||||
s = LogicSim4V(c, 16)
|
s = LogicSim(c, 16, m=4)
|
||||||
assert s.s_len == 3
|
assert s.s_len == 3
|
||||||
s.s_assign[...] = mvarray(
|
bpa = bparray(
|
||||||
'00-', '01-', '0--', '0X-',
|
'00-', '01-', '0--', '0X-',
|
||||||
'10-', '11-', '1--', '1X-',
|
'10-', '11-', '1--', '1X-',
|
||||||
'-0-', '-1-', '---', '-X-',
|
'-0-', '-1-', '---', '-X-',
|
||||||
'X0-', 'X1-', 'X--', 'XX-')
|
'X0-', 'X1-', 'X--', 'XX-')
|
||||||
|
s.s[0] = bpa
|
||||||
s.s_to_c()
|
s.s_to_c()
|
||||||
s.c_prop()
|
s.c_prop()
|
||||||
s.c_to_s()
|
s.c_to_s()
|
||||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
mva = bp_to_mv(s.s[1])
|
||||||
|
assert_equal_shape_and_contents(mva, mvarray(
|
||||||
'--0', '--0', '--0', '--0',
|
'--0', '--0', '--0', '--0',
|
||||||
'--0', '--1', '--X', '--X',
|
'--0', '--1', '--X', '--X',
|
||||||
'--0', '--X', '--X', '--X',
|
'--0', '--X', '--X', '--X',
|
||||||
'--0', '--X', '--X', '--X'))
|
'--0', '--X', '--X', '--X'))
|
||||||
fault_line = s.circuit.cells['a'].ins[0]
|
fault_line = s.circuit.cells['a'].ins[0]
|
||||||
s.s_to_c()
|
s.s_to_c()
|
||||||
s.c_prop(fault_line=fault_line.index, fault_model=1)
|
s.c_prop(fault_line=fault_line, fault_model=1)
|
||||||
s.c_to_s()
|
s.c_to_s()
|
||||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
mva = bp_to_mv(s.s[1])
|
||||||
|
assert_equal_shape_and_contents(mva, mvarray(
|
||||||
'--0', '--1', '--X', '--X',
|
'--0', '--1', '--X', '--X',
|
||||||
'--0', '--1', '--X', '--X',
|
'--0', '--1', '--X', '--X',
|
||||||
'--0', '--1', '--X', '--X',
|
'--0', '--1', '--X', '--X',
|
||||||
'--0', '--1', '--X', '--X'))
|
'--0', '--1', '--X', '--X'))
|
||||||
s.s_to_c()
|
s.s_to_c()
|
||||||
s.c_prop(fault_line=fault_line.index, fault_model=0)
|
s.c_prop(fault_line=fault_line, fault_model=0)
|
||||||
s.c_to_s()
|
s.c_to_s()
|
||||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
mva = bp_to_mv(s.s[1])
|
||||||
|
assert_equal_shape_and_contents(mva, mvarray(
|
||||||
'--0', '--0', '--0', '--0',
|
'--0', '--0', '--0', '--0',
|
||||||
'--0', '--0', '--0', '--0',
|
'--0', '--0', '--0', '--0',
|
||||||
'--0', '--0', '--0', '--0',
|
'--0', '--0', '--0', '--0',
|
||||||
@@ -259,11 +255,11 @@ def test_6v():
|
|||||||
resp = s.s[1].copy()
|
resp = s.s[1].copy()
|
||||||
|
|
||||||
exp_resp = np.copy(mva)
|
exp_resp = np.copy(mva)
|
||||||
exp_resp[:2] = logic.UNASSIGNED
|
exp_resp[:2] = logic.ZERO
|
||||||
np.testing.assert_allclose(resp, exp_resp)
|
np.testing.assert_allclose(resp, exp_resp)
|
||||||
|
|
||||||
|
|
||||||
def xtest_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)
|
||||||
assert s.s_len == 6
|
assert s.s_len == 6
|
||||||
@@ -290,8 +286,8 @@ def xtest_8v():
|
|||||||
np.testing.assert_allclose(resp, exp_resp)
|
np.testing.assert_allclose(resp, exp_resp)
|
||||||
|
|
||||||
|
|
||||||
def xtest_loop():
|
def test_loop():
|
||||||
c = bench.parse('q=DFF(d) d=NOT(q)')
|
c = bench.parse('q=dff(d) d=not(q)')
|
||||||
s = LogicSim(c, 4, m=8)
|
s = LogicSim(c, 4, m=8)
|
||||||
assert s.s_len == 1
|
assert s.s_len == 1
|
||||||
mva = mvarray([['0'], ['1'], ['R'], ['F']])
|
mva = mvarray([['0'], ['1'], ['R'], ['F']])
|
||||||
@@ -317,8 +313,8 @@ def xtest_loop():
|
|||||||
# assert resp[3] == 'F'
|
# assert resp[3] == 'F'
|
||||||
|
|
||||||
|
|
||||||
def xtest_latch():
|
def test_latch():
|
||||||
c = bench.parse('input(d, t) output(q) q=LATCH(d, t)')
|
c = bench.parse('input(d, t) output(q) q=latch(d, t)')
|
||||||
s = LogicSim(c, 8, m=8)
|
s = LogicSim(c, 8, m=8)
|
||||||
assert s.s_len == 4
|
assert s.s_len == 4
|
||||||
mva = mvarray('00-0', '00-1', '01-0', '01-1', '10-0', '10-1', '11-0', '11-1')
|
mva = mvarray('00-0', '00-1', '01-0', '01-1', '10-0', '10-1', '11-0', '11-1')
|
||||||
@@ -331,6 +327,19 @@ def xtest_latch():
|
|||||||
# assert resp[i] == exp[i]
|
# 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):
|
def sim_and_compare(c, test_resp, m=8):
|
||||||
tests, resp = test_resp
|
tests, resp = test_resp
|
||||||
lsim = LogicSim(c, m=m, sims=tests.shape[1])
|
lsim = LogicSim(c, m=m, sims=tests.shape[1])
|
||||||
@@ -347,14 +356,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])}')
|
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
|
assert len(idxs) == 0
|
||||||
|
|
||||||
def sim_and_compare_cls(c, test_resp, SimCls):
|
def sim_and_compare_6v(c, test_resp):
|
||||||
tests, resp = test_resp
|
tests, resp = test_resp
|
||||||
lsim = SimCls(c, sims=tests.shape[1])
|
lsim = LogicSim6V(c, sims=tests.shape[1])
|
||||||
lsim.s_assign[...] = tests
|
lsim.s[0] = tests
|
||||||
lsim.s_to_c()
|
lsim.s_to_c()
|
||||||
lsim.c_prop()
|
lsim.c_prop()
|
||||||
lsim.c_to_s()
|
lsim.c_to_s()
|
||||||
resp_sim = lsim.s_result
|
resp_sim = lsim.s[1]
|
||||||
idxs, pats = np.nonzero(((resp == logic.ONE) & (resp_sim != logic.ONE)) | ((resp == logic.ZERO) & (resp_sim != logic.ZERO)))
|
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)):
|
for i, (idx, pat) in enumerate(zip(idxs, pats)):
|
||||||
if i >= 10:
|
if i >= 10:
|
||||||
@@ -365,32 +374,28 @@ def sim_and_compare_cls(c, test_resp, SimCls):
|
|||||||
|
|
||||||
|
|
||||||
def test_b15_2ig_sa_2v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
def test_b15_2ig_sa_2v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
||||||
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim2V)
|
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=2)
|
||||||
|
|
||||||
|
|
||||||
def test_b15_2ig_sa_4v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
def test_b15_2ig_sa_4v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
||||||
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim4V)
|
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=4)
|
||||||
|
|
||||||
|
|
||||||
def test_b15_2ig_sa_6v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
def test_b15_2ig_sa_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)
|
sim_and_compare_6v(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):
|
def test_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)
|
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):
|
def test_b15_4ig_sa_2v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
||||||
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim2V)
|
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=2)
|
||||||
|
|
||||||
|
|
||||||
def test_b15_4ig_sa_4v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
def test_b15_4ig_sa_4v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
||||||
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim4V)
|
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=4)
|
||||||
|
|
||||||
|
|
||||||
def test_b15_4ig_sa_6v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
def test_b15_4ig_sa_8v(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 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)
|
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=8)
|
||||||
+1
-1
@@ -14,7 +14,7 @@ def test_b15(mydir):
|
|||||||
assert len(resp) > 0
|
assert len(resp) > 0
|
||||||
|
|
||||||
s2 = stil.load(mydir / 'b15_2ig.tf_nf.stil.gz')
|
s2 = stil.load(mydir / 'b15_2ig.tf_nf.stil.gz')
|
||||||
tests = s2.tests_loc(b15, SAED32)
|
tests = s2.tests_loc(b15)
|
||||||
resp = s2.responses(b15)
|
resp = s2.responses(b15)
|
||||||
assert len(tests) > 0
|
assert len(tests) > 0
|
||||||
assert len(resp) > 0
|
assert len(resp) > 0
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ def test_b01(mydir):
|
|||||||
|
|
||||||
assert len(c.nodes) == 139
|
assert len(c.nodes) == 139
|
||||||
assert len(c.lines) == 203
|
assert len(c.lines) == 203
|
||||||
stats = c.stats(SAED90)
|
stats = c.stats
|
||||||
assert stats['input'] == 6
|
assert stats['input'] == 6
|
||||||
assert stats['output'] == 3
|
assert stats['output'] == 3
|
||||||
assert stats['__seq__'] == 5
|
assert stats['__seq__'] == 5
|
||||||
@@ -19,7 +19,7 @@ def test_b15(mydir):
|
|||||||
c = verilog.load(mydir / 'b15_4ig.v.gz', tlib=SAED32)
|
c = verilog.load(mydir / 'b15_4ig.v.gz', tlib=SAED32)
|
||||||
assert len(c.nodes) == 12067
|
assert len(c.nodes) == 12067
|
||||||
assert len(c.lines) == 20731
|
assert len(c.lines) == 20731
|
||||||
stats = c.stats(SAED32)
|
stats = c.stats
|
||||||
assert stats['input'] == 40
|
assert stats['input'] == 40
|
||||||
assert stats['output'] == 71
|
assert stats['output'] == 71
|
||||||
assert stats['__seq__'] == 417
|
assert stats['__seq__'] == 417
|
||||||
@@ -29,7 +29,7 @@ def test_gates(mydir):
|
|||||||
c = verilog.load(mydir / 'gates.v', tlib=NANGATE45)
|
c = verilog.load(mydir / 'gates.v', tlib=NANGATE45)
|
||||||
assert len(c.nodes) == 18
|
assert len(c.nodes) == 18
|
||||||
assert len(c.lines) == 21
|
assert len(c.lines) == 21
|
||||||
stats = c.stats(NANGATE45)
|
stats = c.stats
|
||||||
assert stats['input'] == 3
|
assert stats['input'] == 3
|
||||||
assert stats['output'] == 4
|
assert stats['output'] == 4
|
||||||
assert stats['__seq__'] == 0
|
assert stats['__seq__'] == 0
|
||||||
@@ -39,7 +39,7 @@ def test_halton2(mydir):
|
|||||||
c = verilog.load(mydir / 'rng_haltonBase2.synth_yosys.v', tlib=SAED90)
|
c = verilog.load(mydir / 'rng_haltonBase2.synth_yosys.v', tlib=SAED90)
|
||||||
assert len(c.nodes) == 146
|
assert len(c.nodes) == 146
|
||||||
assert len(c.lines) == 210
|
assert len(c.lines) == 210
|
||||||
stats = c.stats(SAED90)
|
stats = c.stats
|
||||||
assert stats['input'] == 2
|
assert stats['input'] == 2
|
||||||
assert stats['output'] == 12
|
assert stats['output'] == 12
|
||||||
assert stats['__seq__'] == 12
|
assert stats['__seq__'] == 12
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from kyupy.wave_sim import WaveSim, WaveSimCuda, wave_eval_cpu, TMIN, TMAX
|
from kyupy.wave_sim import WaveSim, WaveSimCuda, wave_eval_cpu, TMIN, TMAX
|
||||||
from kyupy.logic_sim import LogicSim6V
|
from kyupy.logic_sim import LogicSim
|
||||||
from kyupy import logic, bench, sim
|
from kyupy import logic, bench, sim
|
||||||
from kyupy.logic import mvarray
|
from kyupy.logic import mvarray
|
||||||
|
|
||||||
@@ -175,12 +175,12 @@ def compare_to_logic_sim(wsim: WaveSim):
|
|||||||
resp |= ((resp ^ (resp >> 1)) & 1) << 2 # transitions
|
resp |= ((resp ^ (resp >> 1)) & 1) << 2 # transitions
|
||||||
resp[wsim.pi_s_locs] = logic.UNASSIGNED
|
resp[wsim.pi_s_locs] = logic.UNASSIGNED
|
||||||
|
|
||||||
lsim = LogicSim6V(wsim.circuit, tests.shape[-1])
|
lsim = LogicSim(wsim.circuit, tests.shape[-1])
|
||||||
lsim.s_assign[...] = tests
|
lsim.s[0] = logic.mv_to_bp(tests)
|
||||||
lsim.s_to_c()
|
lsim.s_to_c()
|
||||||
lsim.c_prop()
|
lsim.c_prop()
|
||||||
lsim.c_to_s()
|
lsim.c_to_s()
|
||||||
exp = lsim.s_result[:,:tests.shape[-1]]
|
exp = logic.bp_to_mv(lsim.s[1])[:,:tests.shape[-1]]
|
||||||
|
|
||||||
resp[resp == logic.PPULSE] = logic.ZERO
|
resp[resp == logic.PPULSE] = logic.ZERO
|
||||||
resp[resp == logic.NPULSE] = logic.ONE
|
resp[resp == logic.NPULSE] = logic.ONE
|
||||||
|
|||||||
Reference in new issue
Block a user