prjxray/experiments/timfuz/timfuz_csv.py

273 lines
8.3 KiB
Python

#!/usr/bin/env python3
from timfuz import Benchmark, row_di2ds, A_di2ds
import numpy as np
import glob
import math
import json
import sympy
import sys
from collections import OrderedDict
# Speed index: some sort of special value
SI_NONE = 0xFFFF
PREFIX_W = 'WIRE_'
PREFIX_P = 'PIP_'
#PREFIX_W = ''
#PREFIX_P = ''
def parse_pip(s):
# Entries like
# CLK_BUFG_REBUF_X60Y117/CLK_BUFG_REBUF.CLK_BUFG_REBUF_R_CK_GCLK0_BOT<<->>CLK_BUFG_REBUF_R_CK_GCLK0_TOP
# Convert to (site, type, pip_junction, pip)
pipstr, speed_index = s.split(':')
speed_index = int(speed_index)
site, instance = pipstr.split('/')
#type, pip_junction, pip = others.split('.')
#return (site, type, pip_junction, pip)
return site, instance, int(speed_index)
def parse_node(s):
node, nwires = s.split(':')
return node, int(nwires)
def parse_wire(s):
# CLBLM_R_X3Y80/CLBLM_M_D6:952
wirestr, speed_index = s.split(':')
site, instance = wirestr.split('/')
return site, instance, int(speed_index)
# FIXME: these actually have a delay element
# Probably need to put these back in
def remove_virtual_pips(pips):
return pips
return filter(lambda pip: not re.match(r'CLBL[LM]_[LR]_', pip[0]), pips)
def load_timing3(f, name='file'):
# src_bel dst_bel ico fast_max fast_min slow_max slow_min pips
f.readline()
ret = []
bads = 0
for l in f:
# FIXME: hack
if 0 and 'CLK' in l:
continue
l = l.strip()
if not l:
continue
parts = l.split(' ')
# FIXME: deal with these nodes
if len(parts) != 11:
bads += 1
continue
net, src_bel, dst_bel, ico, fast_max, fast_min, slow_max, slow_min, pips, nodes, wires = parts
pips = pips.split('|')
nodes = nodes.split('|')
wires = wires.split('|')
ret.append({
'net': net,
'src_bel': src_bel,
'dst_bel': dst_bel,
'ico': int(ico),
# ps
'fast_max': int(fast_max),
'fast_min': int(fast_min),
'slow_max': int(slow_max),
'slow_min': int(slow_min),
'pips': remove_virtual_pips([parse_pip(pip) for pip in pips]),
'nodes': [parse_node(node) for node in nodes],
'wires': [parse_wire(wire) for wire in wires],
'line': l,
})
print(' load %s: %d bad, %d good' % (name, bads, len(ret)))
#assert 0
return ret
def load_speed_json(f):
j = json.load(f)
# Index speed indexes to names
speed_i2s = {}
for k, v in j['speed_model'].items():
i = v['speed_index']
if i != SI_NONE:
speed_i2s[i] = k
return j, speed_i2s
# Verify the nodes and wires really do line up
def vals2Adi_check(vals, names):
print('Checking')
for val in vals:
node_wires = 0
for _node, wiresn in val['nodes']:
node_wires += wiresn
assert node_wires == len(val['wires'])
print('Done')
assert 0
def vals2Adi(vals, speed_i2s,
name_tr={}, name_drop=[],
verbose=False):
def pip2speed(pip):
_site, _name, speed_index = pip
return PREFIX_P + speed_i2s[speed_index]
def wire2speed(wire):
_site, _name, speed_index = wire
return PREFIX_W + speed_i2s[speed_index]
# Want this ordered
names = OrderedDict()
print('Creating matrix w/ tr: %d, drop: %d' % (len(name_tr), len(name_drop)))
# Take sites out entirely using handy "interconnect only" option
#vals = filter(lambda x: str(x).find('SLICE') >= 0, vals)
# Highest count while still getting valid result
# First index all of the given pip types
# Start out as set then convert to list to keep matrix order consistent
sys.stdout.write('Indexing delay elements ')
sys.stdout.flush()
progress = max(1, len(vals) / 100)
for vali, val in enumerate(vals):
if vali % progress == 0:
sys.stdout.write('.')
sys.stdout.flush()
odl = [(pip2speed(pip), None) for pip in val['pips']]
names.update(OrderedDict(odl))
odl = [(wire2speed(wire), None) for wire in val['wires']]
names.update(OrderedDict(odl))
print(' done')
# Apply transform
orig_names = len(names)
for k in (list(name_drop) + list(name_tr.keys())):
if k in names:
del names[k]
else:
print('WARNING: failed to remove %s' % k)
names.update(OrderedDict([(name, None) for name in name_tr.values()]))
print('Names tr %d => %d' % (orig_names, len(names)))
# Make unique list
names = list(names.keys())
name_s2i = {}
for namei, name in enumerate(names):
name_s2i[name] = namei
if verbose:
for name in names:
print('NAME: ', name)
for name in name_drop:
print('DROP: ', name)
for l, r in name_tr.items():
print('TR: %s => %s' % (l, r))
# Now create a matrix with all of these delays
# Each row needs len(names) elements
# -2 means 2 elements present, 0 means absent
# (could hit same pip twice)
print('Creating delay element matrix w/ %d names' % len(names))
Adi = [None for _i in range(len(vals))]
for vali, val in enumerate(vals):
def add_name(name):
if name in name_drop:
return
name = name_tr.get(name, name)
namei = name_s2i[name]
row_di[namei] = row_di.get(namei, 0) + 1
# Start with 0 occurances
#row = [0 for _i in range(len(names))]
row_di = {}
#print('pips: ', val['pips']
for pip in val['pips']:
add_name(pip2speed(pip))
for wire in val['wires']:
add_name(wire2speed(wire))
#A_ub.append(row)
Adi[vali] = row_di
return Adi, names
# TODO: load directly as Ads
# remove names_tr, names_drop
def vals2Ads(vals, speed_i2s, verbose=False):
Adi, names = vals2Adi(vals, speed_i2s, verbose=False)
return A_di2ds(Adi, names)
def load_Ads(speed_json_f, f_ins):
print('Loading data')
_speedj, speed_i2s = load_speed_json(speed_json_f)
vals = []
for avals in [load_timing3(f_in, name) for f_in, name in f_ins]:
vals.extend(avals)
Ads = vals2Ads(vals, speed_i2s)
def mkb(val):
return (val['fast_max'], val['fast_min'], val['slow_max'], val['slow_min'])
b = [mkb(val) for val in vals]
ico = [val['ico'] for val in vals]
return Ads, b, ico
def run(speed_json_f, fout, f_ins, verbose=0, corner=None):
Ads, bs, ico = load_Ads(speed_json_f, f_ins)
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
for row_bs, row_ds, row_ico in zip(bs, Ads, ico):
# like: 123 456 120 450, 1 a, 2 b
# first column has delay corners, followed by delay element count
items = [str(row_ico), ' '.join([str(x) for x in row_bs])]
for k, v in sorted(row_ds.items()):
items.append('%u %s' % (v, k))
fout.write(','.join(items) + '\n')
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Convert obscure timing3.txt into more readable but roughly equivilent timing3.csv'
)
parser.add_argument('--verbose', type=int, help='')
parser.add_argument('--auto-name', action='store_true', help='timing3.txt => timing3.csv')
parser.add_argument('--speed-json', default='build_speed/speed.json',
help='Provides speed index to name translation')
parser.add_argument('--out', default=None, help='Output csv')
parser.add_argument(
'fns_in',
nargs='*',
help='timing3.txt input files')
args = parser.parse_args()
bench = Benchmark()
fnout = args.out
if fnout is None:
if args.auto_name:
assert len(args.fns_in) == 1
fnin = args.fns_in[0]
fnout = fnin.replace('.txt', '.csv')
assert fnout != fnin, 'Expect .txt in'
else:
fnout = '/dev/stdout'
print("Writing to %s" % fnout)
fout = open(fnout, 'w')
fns_in = args.fns_in
if not fns_in:
fns_in = glob.glob('specimen_*/timing3.txt')
run(speed_json_f=open(args.speed_json, 'r'), fout=fout,
f_ins=[(open(fn_in, 'r'), fn_in) for fn_in in fns_in], verbose=args.verbose)
if __name__ == '__main__':
main()