techlib decides on cell type comb/dff/latch
This commit is contained in:
1 parent
ca25942b27
commit
3a2cfb7788
7 files changed
+50
-27
No files matched your search
@@ -14,7 +14,6 @@ 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
|
||||||
|
|
||||||
@@ -282,14 +281,13 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
def s_nodes(self, tlib: 'TechLib') -> list: # type: ignore
|
||||||
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 'dff' in n.kind.lower()] + [n for n in self.nodes if 'latch' in n.kind.lower()]
|
return list(self.io_nodes) + [n for n in self.nodes if tlib.is_dff(n.kind)] + [n for n in self.nodes if tlib.is_latch(n.kind)]
|
||||||
|
|
||||||
def io_locs(self, prefix):
|
def io_locs(self, prefix):
|
||||||
"""Returns a list of indices of primary I/Os that start with given name prefix.
|
"""Returns a list of indices of primary I/Os that start with given name prefix.
|
||||||
@@ -305,13 +303,13 @@ class Circuit:
|
|||||||
"""
|
"""
|
||||||
return self._locs(prefix, list(self.io_nodes))
|
return self._locs(prefix, list(self.io_nodes))
|
||||||
|
|
||||||
def s_locs(self, prefix):
|
def s_locs(self, prefix, tlib: 'TechLib'): # type: ignore
|
||||||
"""Returns the indices of I/Os and sequential elements that start with given name prefix.
|
"""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)
|
return self._locs(prefix, self.s_nodes(tlib))
|
||||||
|
|
||||||
def _locs(self, prefix, nodes:list[Node]) -> NestedNumericList: # can return list[list[...]]
|
def _locs(self, prefix, nodes:list[Node]) -> NestedNumericList: # can return list[list[...]]
|
||||||
d_top: NestedStrIntDict = dict()
|
d_top: NestedStrIntDict = dict()
|
||||||
@@ -331,8 +329,7 @@ class Circuit:
|
|||||||
while isinstance(l, list) and len(l) == 1 and isinstance(l[0], list): l = l[0]
|
while isinstance(l, list) and len(l) == 1 and isinstance(l[0], list): l = l[0]
|
||||||
return l
|
return l
|
||||||
|
|
||||||
@property
|
def stats(self, tlib: 'TechLib'): # type: ignore
|
||||||
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
|
||||||
|
|||||||
+6
-5
@@ -6,6 +6,7 @@ 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
|
||||||
@@ -167,7 +168,7 @@ class SimOps:
|
|||||||
"""
|
"""
|
||||||
def __init__(self, circuit: Circuit, c_caps=1, c_caps_min=1, a_ctrl=None, c_reuse=False, strip_forks=False):
|
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)
|
self.s_len = len(circuit.s_nodes(KYUPY))
|
||||||
|
|
||||||
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)
|
||||||
@@ -187,8 +188,8 @@ 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))
|
ppio2idx = dict((n, i) for i, n in enumerate(circuit.s_nodes(KYUPY)))
|
||||||
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
|
root_nodes = set([n for n in circuit.s_nodes(KYUPY) if len(n.ins) > 0] + [n for n in circuit.nodes if len(n.outs) == 0]) # start from POs, PPOs, and any dangling nodes
|
||||||
readers = np.array([1 if l.reader in root_nodes else len(l.reader.outs) for l in circuit.lines], dtype=np.int32) # for ref-counting forks
|
readers = np.array([1 if l.reader in root_nodes else len(l.reader.outs) for l in circuit.lines], dtype=np.int32) # for ref-counting forks
|
||||||
|
|
||||||
level_lines = [n.ins[0] for n in root_nodes if len(n.ins) > 0 ]
|
level_lines = [n.ins[0] for n in root_nodes if len(n.ins) > 0 ]
|
||||||
@@ -271,7 +272,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):
|
for i, n in enumerate(circuit.s_nodes(KYUPY)):
|
||||||
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
|
||||||
@@ -310,7 +311,7 @@ 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):
|
for i, n in enumerate(circuit.s_nodes(KYUPY)):
|
||||||
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]]
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -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, init_filter=None, launch_filter=None):
|
def tests_loc(self, circuit, tlib, init_filter=None, launch_filter=None):
|
||||||
"""Assembles and returns a LoC scan test pattern set for given circuit.
|
"""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,7 +139,9 @@ 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)
|
||||||
sim8v = LogicSim(circuit, init.shape[-1], m=8)
|
circuit_resolved = circuit.copy()
|
||||||
|
circuit_resolved.resolve_tlib_cells(tlib)
|
||||||
|
sim8v = LogicSim(circuit_resolved, init.shape[-1], m=8)
|
||||||
sim8v.s[0] = logic.mv_to_bp(init)
|
sim8v.s[0] = logic.mv_to_bp(init)
|
||||||
sim8v.s_to_c()
|
sim8v.s_to_c()
|
||||||
sim8v.c_prop()
|
sim8v.c_prop()
|
||||||
@@ -263,12 +265,12 @@ GRAMMAR = r"""
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def parse(text):
|
def parse(text) -> StilFile:
|
||||||
"""Parses the given ``text`` and returns a :class:`StilFile` object."""
|
"""Parses the given ``text`` and returns a :class:`StilFile` object."""
|
||||||
return Lark(GRAMMAR, parser="lalr", transformer=StilTransformer()).parse(text)
|
return Lark(GRAMMAR, parser="lalr", transformer=StilTransformer()).parse(text) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def load(file):
|
def load(file) -> StilFile:
|
||||||
"""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.
|
||||||
|
|||||||
+24
-1
@@ -38,9 +38,11 @@ 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)
|
self.cells[name] = (c, pin_dict, has_dff, has_latch)
|
||||||
|
|
||||||
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."""
|
||||||
@@ -61,6 +63,26 @@ class TechLib:
|
|||||||
assert kind in self.cells, f'Unknown cell: {kind}'
|
assert kind in self.cells, f'Unknown cell: {kind}'
|
||||||
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"""
|
||||||
@@ -98,6 +120,7 @@ AOI211 input(i0,i1,i2,i3) output(o) o=AOI211(i0,i1,i2,i3) ;
|
|||||||
OAI211 input(i0,i1,i2,i3) output(o) o=OAI211(i0,i1,i2,i3) ;
|
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.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -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 SAED32
|
from kyupy.techlib import KYUPY, 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]
|
s_names = [n.name for n in c.s_nodes(SAED32)]
|
||||||
c.resolve_tlib_cells(SAED32)
|
c.resolve_tlib_cells(SAED32)
|
||||||
s_names_prim = [n.name for n in c.s_nodes]
|
s_names_prim = [n.name for n in c.s_nodes(KYUPY)]
|
||||||
assert s_names == s_names_prim, 'resolve_tlib_cells does not preserve names or order of s_nodes'
|
assert s_names == s_names_prim, 'resolve_tlib_cells does not preserve names or order of s_nodes'
|
||||||
@@ -3,7 +3,7 @@ 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 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
|
from kyupy.techlib import SAED90, KYUPY
|
||||||
|
|
||||||
def test_dangling():
|
def test_dangling():
|
||||||
c = verilog.parse('''
|
c = verilog.parse('''
|
||||||
@@ -105,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[loc1]
|
n = c1.s_nodes(KYUPY)[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(
|
||||||
@@ -287,7 +287,7 @@ def test_8v():
|
|||||||
|
|
||||||
|
|
||||||
def test_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']])
|
||||||
@@ -314,7 +314,7 @@ def test_loop():
|
|||||||
|
|
||||||
|
|
||||||
def test_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')
|
||||||
|
|||||||
+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)
|
tests = s2.tests_loc(b15, SAED32)
|
||||||
resp = s2.responses(b15)
|
resp = s2.responses(b15)
|
||||||
assert len(tests) > 0
|
assert len(tests) > 0
|
||||||
assert len(resp) > 0
|
assert len(resp) > 0
|
||||||
|
|||||||
Reference in new issue
Block a user