del deprecated sdf code, explicit tlib use
This commit is contained in:
1 parent
d8f605a47a
commit
cf9a98b5ce
3 files changed
+50
-124
No files matched your search
+41
-119
@@ -16,7 +16,6 @@ from lark import Lark, Transformer
|
||||
|
||||
from . import log, readtext
|
||||
from .circuit import Circuit
|
||||
from .techlib import TechLib
|
||||
|
||||
|
||||
Interconnect = namedtuple('Interconnect', ['orig', 'dest', 'r', 'f'])
|
||||
@@ -28,14 +27,14 @@ class DelayFile:
|
||||
"""
|
||||
def __init__(self, name, cells):
|
||||
self.name = name
|
||||
self.interconnects = cells.get(None, None)
|
||||
self._interconnects = cells.get(None, None)
|
||||
self.cells = dict((n, l) for n, l in cells.items() if n)
|
||||
|
||||
def __repr__(self):
|
||||
return '\n'.join(f'{n}: {l}' for n, l in self.cells.items()) + '\n' + \
|
||||
'\n'.join(str(i) for i in self.interconnects)
|
||||
'\n'.join(str(i) for i in self._interconnects)
|
||||
|
||||
def iopaths(self, circuit:Circuit, tlib=TechLib()):
|
||||
def iopaths(self, circuit:Circuit, tlib):
|
||||
"""Constructs an ndarray containing all IOPATH delays.
|
||||
|
||||
All IOPATH delays for a node ``n`` are annotated to the line connected to the input pin specified in the IOPATH.
|
||||
@@ -60,7 +59,8 @@ class DelayFile:
|
||||
delays = np.zeros((len(circuit.lines), 2, 2, 3)) # dataset last during construction.
|
||||
|
||||
for name, iopaths in self.cells.items():
|
||||
if cell := find_cell(name):
|
||||
name = name.replace('\\', '')
|
||||
if cell := circuit.cells.get(name, None):
|
||||
for i_pin_spec, o_pin_spec, *dels in iopaths:
|
||||
if i_pin_spec.startswith('(posedge '): i_pol_idxs = [0]
|
||||
elif i_pin_spec.startswith('(negedge '): i_pol_idxs = [1]
|
||||
@@ -75,135 +75,57 @@ class DelayFile:
|
||||
|
||||
return np.moveaxis(delays, -1, 0)
|
||||
|
||||
def annotation(self, circuit:Circuit, tlib=TechLib(), dataset=1, interconnect=True, ffdelays=True):
|
||||
"""Constructs an 3-dimensional ndarray with timing data for each line in ``circuit``.
|
||||
def interconnects(self, circuit, tlib):
|
||||
"""Constructs an ndarray containing all INTERCONNECT delays.
|
||||
|
||||
DEPRECATED
|
||||
|
||||
An IOPATH delay for a node is annotated to the line connected to the input pin specified in the IOPATH.
|
||||
|
||||
Currently, only ABSOLUTE IOPATH and INTERCONNECT delays are supported.
|
||||
Pulse rejection limits are derived from absolute delays, explicit declarations (PATHPULSE etc.) are ignored.
|
||||
|
||||
:param circuit: The circuit to annotate. Names from the STIL file are matched to the node names.
|
||||
:type circuit: :class:`~kyupy.circuit.Circuit`
|
||||
:param tlib: A technology library object that provides pin name mappings.
|
||||
:type tlib: :py:class:`~kyupy.techlib.TechLib`
|
||||
:param dataset: SDFs store multiple values for each delay (e.g. minimum, typical, maximum).
|
||||
An integer selects the dataset to use (default is 1 for 'typical').
|
||||
If a tuple is given, the annotator will calculate the average of multiple datasets.
|
||||
:type dataset: ``int`` or ``tuple``
|
||||
:param interconnect: Whether or not to include the delays of interconnects in the annotation.
|
||||
To properly annotate interconnect delays, the circuit model has to include a '__fork__' node on
|
||||
every signal and every fanout-branch. The Verilog parser aids in this by setting the parameter
|
||||
`branchforks=True` in :py:func:`kyupy.verilog.parse`.
|
||||
:type interconnect: ``bool``
|
||||
:param ffdelays: Whether or not to include the delays of flip-flops in the annotation.
|
||||
:type ffdelays: ``bool``
|
||||
:return: A 3-dimensional ndarray with timing data.
|
||||
`branchforks=True` in :py:func:`kyupy.verilog.parse` or :py:func:`kyupy.verilog.load`.
|
||||
|
||||
* Axis 0: line index.
|
||||
* Axis 1: type of timing data: 0='delay', 1='pulse rejection limit'.
|
||||
* Axis 2: The polarity of the output transition of the reading node: 0='rising', 1='falling'.
|
||||
Limited support of SDF spec:
|
||||
* ABSOLUTE delay values only
|
||||
* two delvals per delval_list. First is rising/posedge, second is falling/negedge transition.
|
||||
* PATHPULSE declarations are ignored.
|
||||
|
||||
The polarity for pulse rejection is determined by the latter transition of the pulse.
|
||||
E.g., ``timing[42, 1, 0]`` is the rejection limit of a negative pulse at the output
|
||||
of the reader of line 42.
|
||||
* Axis 0: dataset (usually 3 datasets per SDF-file)
|
||||
* Axis 1: line index. usually input line of a __fork__
|
||||
* Axis 2: (axis of size 2 for compatability to IOPATH results. Values are broadcast along this axis.)
|
||||
* Axis 3: polarity of the transition, 0='rising/posedge', 1='falling/negedge'
|
||||
"""
|
||||
def select_del(_delvals, idx):
|
||||
if isinstance(dataset, tuple):
|
||||
return sum(_delvals[idx][d] for d in dataset) / len(dataset)
|
||||
return _delvals[idx][dataset]
|
||||
|
||||
def find_cell(name:str):
|
||||
if name not in circuit.cells: name = name.replace('\\', '')
|
||||
if name not in circuit.cells: name = name.replace('[', '_').replace(']', '_')
|
||||
if name not in circuit.cells:
|
||||
return None
|
||||
return circuit.cells[name]
|
||||
delays = np.zeros((len(circuit.lines), 2, 2, 3)) # dataset last during construction.
|
||||
|
||||
timing = np.zeros((len(circuit.lines), 2, 2))
|
||||
for cn, iopaths in self.cells.items():
|
||||
for ipn, opn, *delvals in iopaths:
|
||||
for n1, n2, *delvals in self._interconnects:
|
||||
delvals = [d if len(d) > 0 else [0, 0, 0] for d in delvals]
|
||||
if max(max(delvals)) == 0:
|
||||
if max(max(delvals)) == 0: continue
|
||||
cn1, pn1 = n1.split('/') if '/' in n1 else (n1, None)
|
||||
cn2, pn2 = n2.split('/') if '/' in n2 else (n2, None)
|
||||
cn1 = cn1.replace('\\','')
|
||||
cn2 = cn2.replace('\\','')
|
||||
c1, c2 = circuit.cells[cn1], circuit.cells[cn2]
|
||||
p1 = tlib.pin_index(c1.kind, pn1) if pn1 is not None else 0
|
||||
p2 = tlib.pin_index(c2.kind, pn2) if pn2 is not None else 0
|
||||
if len(c1.outs) <= p1 or c1.outs[p1] is None:
|
||||
log.warn(f'No line to annotate pin {pn1} of {c1}')
|
||||
continue
|
||||
cell = find_cell(cn)
|
||||
if cell is None:
|
||||
#log.warn(f'Cell from SDF not found in circuit: {cn}')
|
||||
continue
|
||||
ipn = re.sub(r'\((neg|pos)edge ([^)]+)\)', r'\2', ipn)
|
||||
ipin = tlib.pin_index(cell.kind, ipn)
|
||||
opin = tlib.pin_index(cell.kind, opn)
|
||||
kind = cell.kind.lower()
|
||||
|
||||
def add_delays(_line):
|
||||
if _line is not None:
|
||||
timing[_line, :, 0] += select_del(delvals, 0)
|
||||
timing[_line, :, 1] += select_del(delvals, 1)
|
||||
|
||||
take_avg = False
|
||||
if kind.startswith('sdff'):
|
||||
if not ipn.startswith('CLK'):
|
||||
continue
|
||||
if ffdelays and (len(cell.outs) > opin):
|
||||
add_delays(cell.outs[opin])
|
||||
else:
|
||||
if ipin < len(cell.ins):
|
||||
if kind.startswith(('xor', 'xnor')):
|
||||
# print(ipn, ipin, times[cell.i_lines[ipin], 0, 0])
|
||||
take_avg = timing[cell.ins[ipin]].sum() > 0
|
||||
add_delays(cell.ins[ipin])
|
||||
if take_avg:
|
||||
timing[cell.ins[ipin]] /= 2
|
||||
else:
|
||||
log.warn(f'No line to annotate pin {ipn} of {cell}')
|
||||
|
||||
if not interconnect or self.interconnects is None:
|
||||
return timing
|
||||
|
||||
for n1, n2, *delvals in self.interconnects:
|
||||
delvals = [d if len(d) > 0 else [0, 0, 0] for d in delvals]
|
||||
if max(max(delvals)) == 0:
|
||||
continue
|
||||
if '/' in n1:
|
||||
i = n1.rfind('/')
|
||||
cn1 = n1[0:i]
|
||||
pn1 = n1[i+1:]
|
||||
else:
|
||||
cn1, pn1 = (n1, 'Z')
|
||||
if '/' in n2:
|
||||
i = n2.rfind('/')
|
||||
cn2 = n2[0:i]
|
||||
pn2 = n2[i+1:]
|
||||
else:
|
||||
cn2, pn2 = (n2, 'IN')
|
||||
c1 = find_cell(cn1)
|
||||
if c1 is None:
|
||||
#log.warn(f'Cell from SDF not found in circuit: {cn1}')
|
||||
continue
|
||||
c2 = find_cell(cn2)
|
||||
if c2 is None:
|
||||
#log.warn(f'Cell from SDF not found in circuit: {cn2}')
|
||||
continue
|
||||
p1, p2 = tlib.pin_index(c1.kind, pn1), tlib.pin_index(c2.kind, pn2)
|
||||
line = None
|
||||
if len(c2.ins) <= p2:
|
||||
if len(c2.ins) <= p2 or c2.ins[p2] is None:
|
||||
log.warn(f'No line to annotate pin {pn2} of {c2}')
|
||||
continue
|
||||
f1, f2 = c1.outs[p1].reader, c2.ins[p2].driver
|
||||
if f1 != f2: # possible branchfork
|
||||
assert len(f2.ins) == 1
|
||||
f1, f2 = c1.outs[p1].reader, c2.ins[p2].driver # find the forks between cells.
|
||||
assert f1.kind == '__fork__'
|
||||
assert f2.kind == '__fork__'
|
||||
if f1 != f2: # at least two forks, make sure f2 is a branchfork connected to f1
|
||||
assert len(f2.outs) == 1
|
||||
assert f1.outs[f2.ins[0].driver_pin] == f2.ins[0]
|
||||
line = f2.ins[0]
|
||||
assert f1.outs[f2.ins[0].driver_pin] == line
|
||||
elif len(f2.outs) == 1: # no fanout?
|
||||
elif len(f2.outs) == 1: # f1==f2, only OK when there is no fanout.
|
||||
line = f2.ins[0]
|
||||
if line is not None:
|
||||
timing[line, :, 0] += select_del(delvals, 0)
|
||||
timing[line, :, 1] += select_del(delvals, 1)
|
||||
else:
|
||||
log.warn(f'No branchfork for annotating interconnect delay {c1.name}/{p1}->{c2.name}/{p2}')
|
||||
return timing
|
||||
log.warn(f'No branchfork to annotate interconnect delay {c1.name}/{p1}->{c2.name}/{p2}')
|
||||
continue
|
||||
delays[line, :] = delvals
|
||||
|
||||
return np.moveaxis(delays, -1, 0)
|
||||
|
||||
|
||||
def sanitize(args):
|
||||
|
||||
+4
-2
@@ -10,9 +10,11 @@ def mydir():
|
||||
@pytest.fixture(scope='session')
|
||||
def b14_circuit(mydir):
|
||||
from kyupy import verilog
|
||||
return verilog.load(mydir / 'b14.v.gz', branchforks=True)
|
||||
from kyupy.techlib import SAED32
|
||||
return verilog.load(mydir / 'b14.v.gz', branchforks=True, tlib=SAED32)
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def b14_delays(mydir, b14_circuit):
|
||||
from kyupy import sdf
|
||||
return sdf.load(mydir / 'b14.sdf.gz').iopaths(b14_circuit)[1:2]
|
||||
from kyupy.techlib import SAED32
|
||||
return sdf.load(mydir / 'b14.sdf.gz').iopaths(b14_circuit, tlib=SAED32)[1:2]
|
||||
+5
-3
@@ -2,6 +2,7 @@ import numpy as np
|
||||
|
||||
from kyupy import sdf, verilog, bench
|
||||
from kyupy.wave_sim import WaveSim, TMAX, TMIN
|
||||
from kyupy.techlib import SAED32, SAED90
|
||||
|
||||
def test_parse():
|
||||
test = '''
|
||||
@@ -80,9 +81,9 @@ def test_b14(mydir):
|
||||
|
||||
|
||||
def test_gates(mydir):
|
||||
c = verilog.load(mydir / 'gates.v')
|
||||
c = verilog.load(mydir / 'gates.v', tlib=SAED90)
|
||||
df = sdf.load(mydir / 'gates.sdf')
|
||||
lt = df.iopaths(c)[1]
|
||||
lt = df.iopaths(c, tlib=SAED90)[1]
|
||||
nand_a = c.cells['nandgate'].ins[0]
|
||||
nand_b = c.cells['nandgate'].ins[1]
|
||||
and_a = c.cells['andgate'].ins[0]
|
||||
@@ -134,7 +135,8 @@ def test_nand_xor():
|
||||
)
|
||||
)
|
||||
""")
|
||||
d = df.iopaths(c)[1]
|
||||
d = df.iopaths(c, tlib=SAED32)[1]
|
||||
c.resolve_tlib_cells(SAED32)
|
||||
sim = WaveSim(c, delays=d, sims=16)
|
||||
|
||||
# input A1
|
||||
|
||||
Reference in new issue
Block a user