Browse Source

del deprecated sdf code, explicit tlib use

devel
Stefan Holst 1 year ago
parent
commit
cf9a98b5ce
  1. 170
      src/kyupy/sdf.py
  2. 6
      tests/conftest.py
  3. 8
      tests/test_sdf.py

170
src/kyupy/sdf.py

@ -16,7 +16,6 @@ from lark import Lark, Transformer @@ -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: @@ -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: @@ -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: @@ -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``.
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.
* 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'.
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.
"""
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 interconnects(self, circuit, tlib):
"""Constructs an ndarray containing all INTERCONNECT delays.
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]
timing = np.zeros((len(circuit.lines), 2, 2))
for cn, iopaths in self.cells.items():
for ipn, opn, *delvals in iopaths:
delvals = [d if len(d) > 0 else [0, 0, 0] for d in delvals]
if max(max(delvals)) == 0:
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}')
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` or :py:func:`kyupy.verilog.load`.
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.
* 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'
"""
if not interconnect or self.interconnects is None:
return timing
delays = np.zeros((len(circuit.lines), 2, 2, 3)) # dataset last during construction.
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]
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}')
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
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):

6
tests/conftest.py

@ -10,9 +10,11 @@ def mydir(): @@ -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]

8
tests/test_sdf.py

@ -2,6 +2,7 @@ import numpy as np @@ -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): @@ -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(): @@ -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

Loading…
Cancel
Save