mirror of https://github.com/openXC7/prjxray.git
commit
7595a8d0a8
|
|
@ -0,0 +1,2 @@
|
||||||
|
timing3.csv
|
||||||
|
timing3.txt
|
||||||
|
|
@ -6,51 +6,57 @@ from timfuz import Benchmark, loadc_Ads_bs, load_sub, Ads2bounds, corners2csv, c
|
||||||
def gen_flat(fns_in, sub_json, corner=None):
|
def gen_flat(fns_in, sub_json, corner=None):
|
||||||
Ads, bs = loadc_Ads_bs(fns_in, ico=True)
|
Ads, bs = loadc_Ads_bs(fns_in, ico=True)
|
||||||
bounds = Ads2bounds(Ads, bs)
|
bounds = Ads2bounds(Ads, bs)
|
||||||
zeros = set()
|
# Elements with zero delay assigned due to sub group
|
||||||
|
group_zeros = set()
|
||||||
|
# Elements with a concrete delay
|
||||||
nonzeros = set()
|
nonzeros = set()
|
||||||
|
|
||||||
|
if corner:
|
||||||
|
zero_row = [None, None, None, None]
|
||||||
|
zero_row[corner_s2i[corner]] = 0
|
||||||
|
else:
|
||||||
|
zero_row = None
|
||||||
|
|
||||||
for bound_name, bound_bs in bounds.items():
|
for bound_name, bound_bs in bounds.items():
|
||||||
sub = sub_json['subs'].get(bound_name, None)
|
sub = sub_json['subs'].get(bound_name, None)
|
||||||
if sub:
|
if bound_name in sub_json['zero_names']:
|
||||||
|
if zero_row:
|
||||||
|
yield bound_name, 0
|
||||||
|
elif sub:
|
||||||
|
print('sub', sub)
|
||||||
# put entire delay into pivot
|
# put entire delay into pivot
|
||||||
pivot = sub_json['pivots'][bound_name]
|
pivot = sub_json['pivots'][bound_name]
|
||||||
assert pivot not in zeros
|
assert pivot not in group_zeros
|
||||||
nonzeros.add(pivot)
|
nonzeros.add(pivot)
|
||||||
non_pivot = set(sub.keys() - set([pivot]))
|
non_pivot = set(sub.keys() - set([pivot]))
|
||||||
#for name in non_pivot:
|
#for name in non_pivot:
|
||||||
# assert name not in nonzeros, (pivot, name, nonzeros)
|
# assert name not in nonzeros, (pivot, name, nonzeros)
|
||||||
zeros.update(non_pivot)
|
group_zeros.update(non_pivot)
|
||||||
yield pivot, bound_bs
|
yield pivot, bound_bs
|
||||||
else:
|
else:
|
||||||
nonzeros.add(bound_name)
|
nonzeros.add(bound_name)
|
||||||
yield bound_name, bound_bs
|
yield bound_name, bound_bs
|
||||||
# non-pivots can appear multiple times, but they should always be zero
|
# non-pivots can appear multiple times, but they should always be zero
|
||||||
# however, due to substitution limitations, just warn
|
# however, due to substitution limitations, just warn
|
||||||
violations = zeros.intersection(nonzeros)
|
violations = group_zeros.intersection(nonzeros)
|
||||||
if len(violations):
|
if len(violations):
|
||||||
print('WARNING: %s non-0 non-pivot' % (len(violations)))
|
print('WARNING: %s non-0 non-pivot' % (len(violations)))
|
||||||
|
|
||||||
# XXX: how to best handle these?
|
# XXX: how to best handle these?
|
||||||
# should they be fixed 0?
|
# should they be fixed 0?
|
||||||
if corner:
|
if zero_row:
|
||||||
zero_row = [None, None, None, None]
|
print('zero_row', len(group_zeros), len(violations))
|
||||||
zero_row[corner_s2i[corner]] = 0
|
for zero in sub_json['zero_names']:
|
||||||
for zero in zeros - violations:
|
yield zero, zero_row
|
||||||
|
for zero in group_zeros - violations:
|
||||||
yield zero, zero_row
|
yield zero, zero_row
|
||||||
|
|
||||||
|
|
||||||
def run(fns_in, fnout, sub_json, corner=None, sort=False, verbose=False):
|
def run(fns_in, fnout, sub_json, corner=None, verbose=False):
|
||||||
'''
|
|
||||||
if sort:
|
|
||||||
sortf = sorted
|
|
||||||
else:
|
|
||||||
sortf = lambda x: x
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open(fnout, 'w') as fout:
|
with open(fnout, 'w') as fout:
|
||||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
||||||
#for name, corners in sortf(gen_flat(fnin, sub_json)):
|
for name, corners in sorted(list(gen_flat(fns_in, sub_json,
|
||||||
for name, corners in gen_flat(fns_in, sub_json, corner=corner):
|
corner=corner))):
|
||||||
row_ico = 1
|
row_ico = 1
|
||||||
items = [str(row_ico), corners2csv(corners)]
|
items = [str(row_ico), corners2csv(corners)]
|
||||||
items.append('%u %s' % (1, name))
|
items.append('%u %s' % (1, name))
|
||||||
|
|
@ -64,7 +70,6 @@ def main():
|
||||||
description='Substitute .csv to ungroup correlated variables')
|
description='Substitute .csv to ungroup correlated variables')
|
||||||
|
|
||||||
parser.add_argument('--verbose', action='store_true', help='')
|
parser.add_argument('--verbose', action='store_true', help='')
|
||||||
#parser.add_argument('--sort', action='store_true', help='')
|
|
||||||
parser.add_argument('--sub-csv', help='')
|
parser.add_argument('--sub-csv', help='')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--sub-json',
|
'--sub-json',
|
||||||
|
|
@ -87,7 +92,6 @@ def main():
|
||||||
args.fns_in,
|
args.fns_in,
|
||||||
args.out,
|
args.out,
|
||||||
sub_json=sub_json,
|
sub_json=sub_json,
|
||||||
#sort=args.sort,
|
|
||||||
verbose=args.verbose,
|
verbose=args.verbose,
|
||||||
corner=args.corner)
|
corner=args.corner)
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ build/$(CORNER)/leastsq.csv: build/sub.json build/grouped.csv build/checksub bui
|
||||||
|
|
||||||
build/$(CORNER)/linprog.csv: build/$(CORNER)/leastsq.csv build/grouped.csv
|
build/$(CORNER)/linprog.csv: build/$(CORNER)/leastsq.csv build/grouped.csv
|
||||||
# Tweak rough timing model, making sure all constraints are satisfied
|
# Tweak rough timing model, making sure all constraints are satisfied
|
||||||
ALLOW_ZERO_EQN=$(ALLOW_ZERO_EQN) python3 $(TIMFUZ_DIR)/solve_linprog.py --sub-json build/sub.json --sub-csv build/$(CORNER)/leastsq.csv --massage build/grouped.csv --corner $(CORNER) --out build/$(CORNER)/linprog.csv.tmp
|
ALLOW_ZERO_EQN=$(ALLOW_ZERO_EQN) python3 $(TIMFUZ_DIR)/solve_linprog.py --sub-json build/sub.json --bounds-csv build/$(CORNER)/leastsq.csv --massage build/grouped.csv --corner $(CORNER) --out build/$(CORNER)/linprog.csv.tmp
|
||||||
mv build/$(CORNER)/linprog.csv.tmp build/$(CORNER)/linprog.csv
|
mv build/$(CORNER)/linprog.csv.tmp build/$(CORNER)/linprog.csv
|
||||||
|
|
||||||
build/$(CORNER)/flat.csv: build/$(CORNER)/linprog.csv
|
build/$(CORNER)/flat.csv: build/$(CORNER)/linprog.csv
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from timfuz import Benchmark, Ar_di2np, loadc_Ads_b, index_names, A_ds2np, simplify_rows
|
from timfuz import Benchmark, Ar_di2np, loadc_Ads_b, index_names, A_ds2np, simplify_rows, OrderedSet
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import glob
|
import glob
|
||||||
import math
|
import math
|
||||||
|
|
@ -10,6 +10,22 @@ from collections import OrderedDict
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
|
||||||
|
|
||||||
|
def rm_zero_cols(Ads, verbose=True):
|
||||||
|
removed = OrderedSet()
|
||||||
|
|
||||||
|
print('Removing ZERO elements')
|
||||||
|
for row_ds in Ads:
|
||||||
|
for k in set(row_ds.keys()):
|
||||||
|
if k in removed:
|
||||||
|
del row_ds[k]
|
||||||
|
elif k.find('ZERO') >= 0:
|
||||||
|
del row_ds[k]
|
||||||
|
removed.add(k)
|
||||||
|
if verbose:
|
||||||
|
print(' Removing %s' % k)
|
||||||
|
return removed
|
||||||
|
|
||||||
|
|
||||||
def fracr_quick(r):
|
def fracr_quick(r):
|
||||||
return [Fraction(numerator=int(x), denominator=1) for x in r]
|
return [Fraction(numerator=int(x), denominator=1) for x in r]
|
||||||
|
|
||||||
|
|
@ -22,19 +38,20 @@ def fracm_quick(m):
|
||||||
|
|
||||||
|
|
||||||
class State(object):
|
class State(object):
|
||||||
def __init__(self, Ads, drop_names=[]):
|
def __init__(self, Ads, zero_names=[]):
|
||||||
self.Ads = Ads
|
self.Ads = Ads
|
||||||
self.names = index_names(self.Ads)
|
self.names = index_names(self.Ads)
|
||||||
|
|
||||||
# known zero delay elements
|
# known zero delay elements
|
||||||
self.drop_names = set(drop_names)
|
self.zero_names = OrderedSet(zero_names)
|
||||||
# active names in rows
|
# active names in rows
|
||||||
# includes sub variables, excludes variables that have been substituted out
|
# includes sub variables, excludes variables that have been substituted out
|
||||||
self.base_names = set(self.names)
|
self.base_names = OrderedSet(self.names)
|
||||||
|
#self.names = OrderedSet(self.base_names)
|
||||||
self.names = set(self.base_names)
|
self.names = set(self.base_names)
|
||||||
# List of variable substitutions
|
# List of variable substitutions
|
||||||
# k => dict of v:n entries that it came from
|
# k => dict of v:n entries that it came from
|
||||||
self.subs = {}
|
self.subs = OrderedDict()
|
||||||
self.verbose = True
|
self.verbose = True
|
||||||
|
|
||||||
def print_stats(self):
|
def print_stats(self):
|
||||||
|
|
@ -45,51 +62,44 @@ class State(object):
|
||||||
" Largest: %u" % max([len(x) for x in self.subs.values()]))
|
" Largest: %u" % max([len(x) for x in self.subs.values()]))
|
||||||
print(" Rows: %u" % len(self.Ads))
|
print(" Rows: %u" % len(self.Ads))
|
||||||
print(
|
print(
|
||||||
" Cols (in): %u" % (len(self.base_names) + len(self.drop_names)))
|
" Cols (in): %u" % (len(self.base_names) + len(self.zero_names)))
|
||||||
print(" Cols (preprocessed): %u" % len(self.base_names))
|
print(" Cols (preprocessed): %u" % len(self.base_names))
|
||||||
print(" Drop names: %u" % len(self.drop_names))
|
print(" ZERO names: %u" % len(self.zero_names))
|
||||||
print(" Cols (out): %u" % len(self.names))
|
print(" Cols (out): %u" % len(self.names))
|
||||||
print(" Solvable vars: %u" % len(self.names & self.base_names))
|
print(" Solvable vars: %u" % len(self.names & self.base_names))
|
||||||
assert len(self.names) >= len(self.subs)
|
assert len(self.names) >= len(self.subs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(fn_ins, simplify=False, corner=None):
|
def load(fn_ins, simplify=False, corner=None, rm_zero=False):
|
||||||
|
zero_names = OrderedSet()
|
||||||
|
|
||||||
Ads, b = loadc_Ads_b(fn_ins, corner=corner, ico=True)
|
Ads, b = loadc_Ads_b(fn_ins, corner=corner, ico=True)
|
||||||
|
if rm_zero:
|
||||||
|
zero_names = rm_zero_cols(Ads)
|
||||||
if simplify:
|
if simplify:
|
||||||
print('Simplifying corner %s' % (corner, ))
|
print('Simplifying corner %s' % (corner, ))
|
||||||
Ads, b = simplify_rows(Ads, b, remove_zd=False, corner=corner)
|
Ads, b = simplify_rows(Ads, b, remove_zd=False, corner=corner)
|
||||||
return State(Ads)
|
return State(Ads, zero_names=zero_names)
|
||||||
|
|
||||||
|
|
||||||
def write_state(state, fout):
|
def write_state(state, fout):
|
||||||
j = {
|
j = {
|
||||||
'names': dict([(x, None) for x in state.names]),
|
'names':
|
||||||
'drop_names': list(state.drop_names),
|
OrderedDict([(x, None) for x in state.names]),
|
||||||
'base_names': list(state.base_names),
|
'zero_names':
|
||||||
'subs': dict([(name, values) for name, values in state.subs.items()]),
|
sorted(list(state.zero_names)),
|
||||||
'pivots': state.pivots,
|
'base_names':
|
||||||
|
sorted(list(state.base_names)),
|
||||||
|
'subs':
|
||||||
|
OrderedDict([(name, values) for name, values in state.subs.items()]),
|
||||||
|
'pivots':
|
||||||
|
state.pivots,
|
||||||
}
|
}
|
||||||
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
|
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
|
||||||
|
|
||||||
|
|
||||||
def Anp2matrix(Anp):
|
|
||||||
'''
|
|
||||||
Original idea was to make into a square matrix
|
|
||||||
but this loses too much information
|
|
||||||
so now this actually isn't doing anything and should probably be eliminated
|
|
||||||
'''
|
|
||||||
|
|
||||||
ncols = len(Anp[0])
|
|
||||||
A_ub2 = [np.zeros(ncols) for _i in range(ncols)]
|
|
||||||
dst_rowi = 0
|
|
||||||
for rownp in Anp:
|
|
||||||
A_ub2[dst_rowi] = np.add(A_ub2[dst_rowi], rownp)
|
|
||||||
dst_rowi = (dst_rowi + 1) % ncols
|
|
||||||
return A_ub2
|
|
||||||
|
|
||||||
|
|
||||||
def row_np2ds(rownp, names):
|
def row_np2ds(rownp, names):
|
||||||
ret = {}
|
ret = OrderedDict()
|
||||||
assert len(rownp) == len(names), (len(rownp), len(names))
|
assert len(rownp) == len(names), (len(rownp), len(names))
|
||||||
for namei, name in enumerate(names):
|
for namei, name in enumerate(names):
|
||||||
v = rownp[namei]
|
v = rownp[namei]
|
||||||
|
|
@ -102,7 +112,7 @@ def row_sym2dsf(rowsym, names):
|
||||||
'''Convert a sympy row into a dictionary of keys to (numerator, denominator) tuples'''
|
'''Convert a sympy row into a dictionary of keys to (numerator, denominator) tuples'''
|
||||||
from sympy import fraction
|
from sympy import fraction
|
||||||
|
|
||||||
ret = {}
|
ret = OrderedDict()
|
||||||
assert len(rowsym) == len(names), (len(rowsym), len(names))
|
assert len(rowsym) == len(names), (len(rowsym), len(names))
|
||||||
for namei, name in enumerate(names):
|
for namei, name in enumerate(names):
|
||||||
v = rowsym[namei]
|
v = rowsym[namei]
|
||||||
|
|
@ -117,11 +127,7 @@ def state_rref(state, verbose=False):
|
||||||
names, Anp = A_ds2np(state.Ads)
|
names, Anp = A_ds2np(state.Ads)
|
||||||
|
|
||||||
print('np: %u rows x %u cols' % (len(Anp), len(Anp[0])))
|
print('np: %u rows x %u cols' % (len(Anp), len(Anp[0])))
|
||||||
if 0:
|
mnp = Anp
|
||||||
print('Combining rows into matrix')
|
|
||||||
mnp = Anp2matrix(Anp)
|
|
||||||
else:
|
|
||||||
mnp = Anp
|
|
||||||
print('Matrix: %u rows x %u cols' % (len(mnp), len(mnp[0])))
|
print('Matrix: %u rows x %u cols' % (len(mnp), len(mnp[0])))
|
||||||
print('Converting np to sympy matrix')
|
print('Converting np to sympy matrix')
|
||||||
mfrac = fracm_quick(mnp)
|
mfrac = fracm_quick(mnp)
|
||||||
|
|
@ -145,7 +151,7 @@ def state_rref(state, verbose=False):
|
||||||
print('rref')
|
print('rref')
|
||||||
sympy.pprint(rref)
|
sympy.pprint(rref)
|
||||||
|
|
||||||
state.pivots = {}
|
state.pivots = OrderedDict()
|
||||||
|
|
||||||
def row_solved(rowsym, row_pivot):
|
def row_solved(rowsym, row_pivot):
|
||||||
for ci, c in enumerate(rowsym):
|
for ci, c in enumerate(rowsym):
|
||||||
|
|
@ -177,7 +183,7 @@ def state_rref(state, verbose=False):
|
||||||
state.names.add(group_name)
|
state.names.add(group_name)
|
||||||
# Remove substituted variables
|
# Remove substituted variables
|
||||||
# Note: variables may appear multiple times
|
# Note: variables may appear multiple times
|
||||||
state.names.difference_update(set(rowdsf.keys()))
|
state.names.difference_update(OrderedSet(rowdsf.keys()))
|
||||||
pivot_name = names[row_pivot]
|
pivot_name = names[row_pivot]
|
||||||
state.pivots[group_name] = pivot_name
|
state.pivots[group_name] = pivot_name
|
||||||
if verbose:
|
if verbose:
|
||||||
|
|
@ -186,11 +192,12 @@ def state_rref(state, verbose=False):
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
def run(fnout, fn_ins, simplify=False, corner=None, verbose=0):
|
def run(fnout, fn_ins, simplify=False, corner=None, rm_zero=False, verbose=0):
|
||||||
print('Loading data')
|
print('Loading data')
|
||||||
|
|
||||||
assert len(fn_ins) > 0
|
assert len(fn_ins) > 0
|
||||||
state = State.load(fn_ins, simplify=simplify, corner=corner)
|
state = State.load(
|
||||||
|
fn_ins, simplify=simplify, corner=corner, rm_zero=rm_zero)
|
||||||
state_rref(state, verbose=verbose)
|
state_rref(state, verbose=verbose)
|
||||||
state.print_stats()
|
state.print_stats()
|
||||||
if fnout:
|
if fnout:
|
||||||
|
|
@ -209,6 +216,8 @@ def main():
|
||||||
parser.add_argument('--verbose', action='store_true', help='')
|
parser.add_argument('--verbose', action='store_true', help='')
|
||||||
parser.add_argument('--simplify', action='store_true', help='')
|
parser.add_argument('--simplify', action='store_true', help='')
|
||||||
parser.add_argument('--corner', default="slow_max", help='')
|
parser.add_argument('--corner', default="slow_max", help='')
|
||||||
|
parser.add_argument(
|
||||||
|
'--rm-zero', action='store_true', help='Remove ZERO elements')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--speed-json',
|
'--speed-json',
|
||||||
default='build_speed/speed.json',
|
default='build_speed/speed.json',
|
||||||
|
|
@ -228,6 +237,7 @@ def main():
|
||||||
fn_ins=fns_in,
|
fn_ins=fns_in,
|
||||||
simplify=args.simplify,
|
simplify=args.simplify,
|
||||||
corner=args.corner,
|
corner=args.corner,
|
||||||
|
rm_zero=args.rm_zero,
|
||||||
verbose=args.verbose)
|
verbose=args.verbose)
|
||||||
finally:
|
finally:
|
||||||
print('Exiting after %s' % bench)
|
print('Exiting after %s' % bench)
|
||||||
|
|
|
||||||
|
|
@ -30,47 +30,6 @@ def mkestimate(Anp, b):
|
||||||
return x0
|
return x0
|
||||||
|
|
||||||
|
|
||||||
def save(outfn, xvals, names, corner):
|
|
||||||
# ballpark minimum actual observed delay is around 7 (carry chain)
|
|
||||||
# anything less than one is probably a solver artifact
|
|
||||||
delta = 0.5
|
|
||||||
corneri = corner_s2i[corner]
|
|
||||||
|
|
||||||
# Round conservatively
|
|
||||||
roundf = {
|
|
||||||
'fast_max': math.ceil,
|
|
||||||
'fast_min': math.floor,
|
|
||||||
'slow_max': math.ceil,
|
|
||||||
'slow_min': math.floor,
|
|
||||||
}[corner]
|
|
||||||
|
|
||||||
print('Writing results')
|
|
||||||
skips = 0
|
|
||||||
keeps = 0
|
|
||||||
with open(outfn, 'w') as fout:
|
|
||||||
# write as one variable per line
|
|
||||||
# this natively forms a bound if fed into linprog solver
|
|
||||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
|
||||||
for xval, name in zip(xvals, names):
|
|
||||||
row_ico = 1
|
|
||||||
|
|
||||||
# also review ceil vs floor choice for min vs max
|
|
||||||
# lets be more conservative for now
|
|
||||||
if xval < delta:
|
|
||||||
#print('Skipping %s: %0.6f' % (name, xval))
|
|
||||||
skips += 1
|
|
||||||
continue
|
|
||||||
keeps += 1
|
|
||||||
#xvali = round(xval)
|
|
||||||
|
|
||||||
items = [str(row_ico), acorner2csv(roundf(xval), corneri)]
|
|
||||||
items.append('%u %s' % (1, name))
|
|
||||||
fout.write(','.join(items) + '\n')
|
|
||||||
print(
|
|
||||||
'Wrote: skip %u => %u / %u valid delays' % (skips, keeps, len(names)))
|
|
||||||
assert keeps, 'Failed to estimate delay'
|
|
||||||
|
|
||||||
|
|
||||||
def run_corner(
|
def run_corner(
|
||||||
Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
|
Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
|
||||||
# Given timing scores for above delays (-ps)
|
# Given timing scores for above delays (-ps)
|
||||||
|
|
@ -140,7 +99,7 @@ def run_corner(
|
||||||
print('Done')
|
print('Done')
|
||||||
|
|
||||||
if outfn:
|
if outfn:
|
||||||
save(outfn, res.x, names, corner)
|
timfuz_solve.solve_save(outfn, res.x, names, corner, verbose=verbose)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
||||||
|
|
@ -12,50 +12,12 @@ import time
|
||||||
import timfuz_solve
|
import timfuz_solve
|
||||||
|
|
||||||
|
|
||||||
def save(outfn, xvals, names, corner):
|
|
||||||
# ballpark minimum actual observed delay is around 7 (carry chain)
|
|
||||||
# anything less than one is probably a solver artifact
|
|
||||||
delta = 0.5
|
|
||||||
corneri = corner_s2i[corner]
|
|
||||||
|
|
||||||
roundf = {
|
|
||||||
'fast_max': math.ceil,
|
|
||||||
'fast_min': math.floor,
|
|
||||||
'slow_max': math.ceil,
|
|
||||||
'slow_min': math.floor,
|
|
||||||
}[corner]
|
|
||||||
|
|
||||||
print('Writing results')
|
|
||||||
zeros = 0
|
|
||||||
with open(outfn, 'w') as fout:
|
|
||||||
# write as one variable per line
|
|
||||||
# this natively forms a bound if fed into linprog solver
|
|
||||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
|
||||||
for xval, name in zip(xvals, names):
|
|
||||||
row_ico = 1
|
|
||||||
|
|
||||||
# FIXME: only report for the given corner?
|
|
||||||
# also review ceil vs floor choice for min vs max
|
|
||||||
# lets be more conservative for now
|
|
||||||
if xval < delta:
|
|
||||||
print('WARNING: near 0 delay on %s: %0.6f' % (name, xval))
|
|
||||||
zeros += 1
|
|
||||||
#continue
|
|
||||||
items = [str(row_ico), acorner2csv(roundf(xval), corneri)]
|
|
||||||
items.append('%u %s' % (1, name))
|
|
||||||
fout.write(','.join(items) + '\n')
|
|
||||||
nonzeros = len(names) - zeros
|
|
||||||
print(
|
|
||||||
'Wrote: %u / %u constrained delays, %u zeros' %
|
|
||||||
(nonzeros, len(names), zeros))
|
|
||||||
|
|
||||||
|
|
||||||
def run_corner(
|
def run_corner(
|
||||||
Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
|
Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
|
||||||
if len(Anp) == 0:
|
if len(Anp) == 0:
|
||||||
print('WARNING: zero equations')
|
print('WARNING: zero equations')
|
||||||
if outfn:
|
if outfn:
|
||||||
save(outfn, [], [], corner)
|
timfuz_solve.solve_save(outfn, [], [], corner)
|
||||||
return
|
return
|
||||||
maxcorner = {
|
maxcorner = {
|
||||||
'slow_max': True,
|
'slow_max': True,
|
||||||
|
|
@ -180,7 +142,8 @@ def run_corner(
|
||||||
print('Delay on %d / %d' % (nonzeros, len(res.x)))
|
print('Delay on %d / %d' % (nonzeros, len(res.x)))
|
||||||
|
|
||||||
if outfn:
|
if outfn:
|
||||||
save(outfn, res.x, names, corner)
|
timfuz_solve.solve_save(
|
||||||
|
outfn, res.x, names, corner, verbose=verbose)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
@ -192,7 +155,8 @@ def main():
|
||||||
|
|
||||||
parser.add_argument('--verbose', action='store_true', help='')
|
parser.add_argument('--verbose', action='store_true', help='')
|
||||||
parser.add_argument('--massage', action='store_true', help='')
|
parser.add_argument('--massage', action='store_true', help='')
|
||||||
parser.add_argument('--sub-csv', help='')
|
parser.add_argument(
|
||||||
|
'--bounds-csv', help='Previous solve result starting point')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--sub-json', help='Group substitutions to make fully ranked')
|
'--sub-json', help='Group substitutions to make fully ranked')
|
||||||
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
||||||
|
|
@ -215,7 +179,7 @@ def main():
|
||||||
timfuz_solve.run(
|
timfuz_solve.run(
|
||||||
run_corner=run_corner,
|
run_corner=run_corner,
|
||||||
sub_json=sub_json,
|
sub_json=sub_json,
|
||||||
sub_csv=args.sub_csv,
|
bounds_csv=args.bounds_csv,
|
||||||
fns_in=fns_in,
|
fns_in=fns_in,
|
||||||
corner=args.corner,
|
corner=args.corner,
|
||||||
massage=args.massage,
|
massage=args.massage,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from timfuz import Benchmark, load_sub, load_bounds, loadc_Ads_b
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def run(fns_in, corner, bounds_csv, verbose=False):
|
||||||
|
print('Loading data')
|
||||||
|
Ads, borig = loadc_Ads_b(fns_in, corner, ico=True)
|
||||||
|
|
||||||
|
bounds = load_bounds(bounds_csv, corner)
|
||||||
|
# verify is flattened
|
||||||
|
for k in bounds.keys():
|
||||||
|
assert 'GROUP_' not in k, 'Must operate on flattened bounds'
|
||||||
|
|
||||||
|
# compute our timing model delay at the given corner
|
||||||
|
bgots = []
|
||||||
|
for row_ds in Ads:
|
||||||
|
delays = [n * bounds[x] for x, n in row_ds.items()]
|
||||||
|
bgots.append(sum(delays))
|
||||||
|
|
||||||
|
ses = (np.asarray(bgots) - np.asarray(borig))**2
|
||||||
|
mse = (ses).mean(axis=None)
|
||||||
|
print('MSE aggregate: %0.1f' % mse)
|
||||||
|
print('Min SE: %0.1f' % min(ses))
|
||||||
|
print('Max SE: %0.1f' % max(ses))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Report a timing fit score')
|
||||||
|
|
||||||
|
parser.add_argument('--verbose', action='store_true', help='')
|
||||||
|
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
||||||
|
parser.add_argument(
|
||||||
|
'--bounds-csv',
|
||||||
|
required=True,
|
||||||
|
help='Previous solve result starting point')
|
||||||
|
parser.add_argument('fns_in', nargs='+', help='timing3.csv input files')
|
||||||
|
args = parser.parse_args()
|
||||||
|
# Store options in dict to ease passing through functions
|
||||||
|
bench = Benchmark()
|
||||||
|
|
||||||
|
fns_in = args.fns_in
|
||||||
|
if not fns_in:
|
||||||
|
fns_in = glob.glob('specimen_*/timing3.csv')
|
||||||
|
|
||||||
|
try:
|
||||||
|
run(
|
||||||
|
fns_in=fns_in,
|
||||||
|
corner=args.corner,
|
||||||
|
bounds_csv=args.bounds_csv,
|
||||||
|
verbose=args.verbose)
|
||||||
|
finally:
|
||||||
|
print('Exiting after %s' % bench)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
specimen_*
|
||||||
|
build
|
||||||
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
N := 1
|
||||||
|
SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
|
||||||
|
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
|
||||||
|
|
||||||
|
all: $(SPECIMENS_OK)
|
||||||
|
|
||||||
|
$(SPECIMENS_OK):
|
||||||
|
bash generate.sh $(subst /OK,,$@) || (if [ "$(BADPRJ_OK)" != 'Y' ] ; then exit 1; fi; exit 0)
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
run:
|
||||||
|
$(MAKE) clean
|
||||||
|
$(MAKE) all
|
||||||
|
touch run.ok
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf specimen_[0-9][0-9][0-9]/ seg_clblx.segbits __pycache__ run.ok
|
||||||
|
rm -rf vivado*.log vivado_*.str vivado*.jou design *.bits *.dcp *.bit
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
.PHONY: all run clean
|
||||||
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
Characterizes how attributes vary across pips, wires, and nodes. Usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make
|
||||||
|
$ python3 pip_unique.py
|
||||||
|
$ python3 wire_unique.py
|
||||||
|
$ python3 node_unique.py
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: "make" will take a long time (about 2.5 hours on my machine)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
source ${XRAY_GENHEADER}
|
||||||
|
TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing
|
||||||
|
|
||||||
|
vivado -mode batch -source ../generate.tcl
|
||||||
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
source ../../../../utils/utils.tcl
|
||||||
|
|
||||||
|
proc build_design {} {
|
||||||
|
create_project -force -part $::env(XRAY_PART) design design
|
||||||
|
read_verilog ../../src/picorv32.v
|
||||||
|
read_verilog ../top.v
|
||||||
|
synth_design -top top
|
||||||
|
|
||||||
|
puts "Locking pins"
|
||||||
|
set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||||
|
[get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||||
|
|
||||||
|
puts "Package stuff"
|
||||||
|
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||||
|
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||||
|
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||||
|
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||||
|
|
||||||
|
puts "pblocking"
|
||||||
|
create_pblock roi
|
||||||
|
set roipb [get_pblocks roi]
|
||||||
|
set_property EXCLUDE_PLACEMENT 1 $roipb
|
||||||
|
add_cells_to_pblock $roipb [get_cells roi]
|
||||||
|
resize_pblock $roipb -add "$::env(XRAY_ROI)"
|
||||||
|
|
||||||
|
puts "randplace"
|
||||||
|
randplace_pblock 50 roi
|
||||||
|
|
||||||
|
set_property CFGBVS VCCO [current_design]
|
||||||
|
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||||
|
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||||
|
|
||||||
|
puts "dedicated route"
|
||||||
|
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||||
|
|
||||||
|
place_design
|
||||||
|
route_design
|
||||||
|
|
||||||
|
write_checkpoint -force design.dcp
|
||||||
|
# disable combinitorial loop
|
||||||
|
# set_property IS_ENABLED 0 [get_drc_checks {LUTLP-1}]
|
||||||
|
#write_bitstream -force design.bit
|
||||||
|
}
|
||||||
|
|
||||||
|
proc pips_all {} {
|
||||||
|
set outdir "."
|
||||||
|
set fp [open "$outdir/pip_all.txt" w]
|
||||||
|
set items [get_pips]
|
||||||
|
puts "Items: [llength $items]"
|
||||||
|
|
||||||
|
set needspace 0
|
||||||
|
set properties [list_property [lindex $items 0]]
|
||||||
|
foreach item $items {
|
||||||
|
set needspace 0
|
||||||
|
foreach property $properties {
|
||||||
|
set val [get_property $property $item]
|
||||||
|
if {"$val" ne ""} {
|
||||||
|
if $needspace {
|
||||||
|
puts -nonewline $fp " "
|
||||||
|
}
|
||||||
|
puts -nonewline $fp "$property:$val"
|
||||||
|
set needspace 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
puts $fp ""
|
||||||
|
}
|
||||||
|
close $fp
|
||||||
|
}
|
||||||
|
proc wires_all {} {
|
||||||
|
set outdir "."
|
||||||
|
set fp [open "$outdir/wire_all.txt" w]
|
||||||
|
set items [get_wires]
|
||||||
|
puts "Items: [llength $items]"
|
||||||
|
|
||||||
|
set needspace 0
|
||||||
|
set properties [list_property [lindex $items 0]]
|
||||||
|
foreach item $items {
|
||||||
|
set needspace 0
|
||||||
|
foreach property $properties {
|
||||||
|
set val [get_property $property $item]
|
||||||
|
if {"$val" ne ""} {
|
||||||
|
if $needspace {
|
||||||
|
puts -nonewline $fp " "
|
||||||
|
}
|
||||||
|
puts -nonewline $fp "$property:$val"
|
||||||
|
set needspace 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
puts $fp ""
|
||||||
|
}
|
||||||
|
close $fp
|
||||||
|
}
|
||||||
|
|
||||||
|
build_design
|
||||||
|
pips_all
|
||||||
|
wires_all
|
||||||
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,8 +42,8 @@ def run(node_fin, verbose=0):
|
||||||
return anode['wname']
|
return anode['wname']
|
||||||
#return (anode['tile_type'], anode['wname'])
|
#return (anode['tile_type'], anode['wname'])
|
||||||
|
|
||||||
if nodei % 1000 == 0:
|
if nodei % 100000 == 0:
|
||||||
print 'Check node %d' % nodei
|
print('Check node %d, %u node types' % (nodei, len(refnodes)))
|
||||||
# Existing node?
|
# Existing node?
|
||||||
try:
|
try:
|
||||||
refnode = refnodes[getk(anode)]
|
refnode = refnodes[getk(anode)]
|
||||||
|
|
@ -62,10 +64,10 @@ def run(node_fin, verbose=0):
|
||||||
if k in refnode and k in anode:
|
if k in refnode and k in anode:
|
||||||
|
|
||||||
def fail():
|
def fail():
|
||||||
print 'Mismatch on %s' % k
|
print('Mismatch on %s' % k)
|
||||||
print refnode[k], anode[k]
|
print(refnode[k], anode[k])
|
||||||
print refnode['l']
|
print(refnode['l'])
|
||||||
print anode['l']
|
print(anode['l'])
|
||||||
#assert 0
|
#assert 0
|
||||||
|
|
||||||
if k == 'SPEED_CLASS':
|
if k == 'SPEED_CLASS':
|
||||||
|
|
@ -97,6 +99,9 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
parser.add_argument('--verbose', type=int, help='')
|
parser.add_argument('--verbose', type=int, help='')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
|
'node_fn_in',
|
||||||
|
default='specimen_001/wire_all.txt',
|
||||||
|
nargs='?',
|
||||||
|
help='Input file')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,8 +31,8 @@ def run(node_fin, verbose=0):
|
||||||
return anode['wname']
|
return anode['wname']
|
||||||
return (anode['tile_type'], anode['wname'])
|
return (anode['tile_type'], anode['wname'])
|
||||||
|
|
||||||
if nodei % 1000 == 0:
|
if nodei % 100000 == 0:
|
||||||
print 'Check node %d' % nodei
|
print('Check node %d, %u node types' % (nodei, len(refnodes)))
|
||||||
# Existing node?
|
# Existing node?
|
||||||
try:
|
try:
|
||||||
refnode = refnodes[getk(anode)]
|
refnode = refnodes[getk(anode)]
|
||||||
|
|
@ -56,14 +58,14 @@ def run(node_fin, verbose=0):
|
||||||
if k in refnode and k in anode:
|
if k in refnode and k in anode:
|
||||||
|
|
||||||
def fail():
|
def fail():
|
||||||
print 'Mismatch on %s' % k
|
print('Mismatch on %s' % k)
|
||||||
print refnode[k], anode[k]
|
print(refnode[k], anode[k])
|
||||||
print refnode['l']
|
print(refnode['l'])
|
||||||
print anode['l']
|
print(anode['l'])
|
||||||
#assert 0
|
#assert 0
|
||||||
|
|
||||||
if refnode[k] != anode[k]:
|
if refnode[k] != anode[k]:
|
||||||
print
|
print('')
|
||||||
fail()
|
fail()
|
||||||
# A key in one but not the other?
|
# A key in one but not the other?
|
||||||
elif k in refnode or k in anode:
|
elif k in refnode or k in anode:
|
||||||
|
|
@ -81,6 +83,9 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
parser.add_argument('--verbose', type=int, help='')
|
parser.add_argument('--verbose', type=int, help='')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
|
'node_fn_in',
|
||||||
|
default='specimen_001/pip_all.txt',
|
||||||
|
nargs='?',
|
||||||
|
help='Input file')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
//move some stuff to minitests/ncy0
|
||||||
|
|
||||||
|
`define SEED 32'h12345678
|
||||||
|
|
||||||
|
module top(input clk, stb, di, output do);
|
||||||
|
localparam integer DIN_N = 42;
|
||||||
|
localparam integer DOUT_N = 79;
|
||||||
|
|
||||||
|
reg [DIN_N-1:0] din;
|
||||||
|
wire [DOUT_N-1:0] dout;
|
||||||
|
|
||||||
|
reg [DIN_N-1:0] din_shr;
|
||||||
|
reg [DOUT_N-1:0] dout_shr;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
din_shr <= {din_shr, di};
|
||||||
|
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||||
|
if (stb) begin
|
||||||
|
din <= din_shr;
|
||||||
|
dout_shr <= dout;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign do = dout_shr[DOUT_N-1];
|
||||||
|
|
||||||
|
roi #(.DIN_N(DIN_N), .DOUT_N(DOUT_N))
|
||||||
|
roi (
|
||||||
|
.clk(clk),
|
||||||
|
.din(din),
|
||||||
|
.dout(dout)
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module roi(input clk, input [DIN_N-1:0] din, output [DOUT_N-1:0] dout);
|
||||||
|
parameter integer DIN_N = -1;
|
||||||
|
parameter integer DOUT_N = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
//Take out for now to make sure LUTs are more predictable
|
||||||
|
picorv32 picorv32 (
|
||||||
|
.clk(clk),
|
||||||
|
.resetn(din[0]),
|
||||||
|
.mem_valid(dout[0]),
|
||||||
|
.mem_instr(dout[1]),
|
||||||
|
.mem_ready(din[1]),
|
||||||
|
.mem_addr(dout[33:2]),
|
||||||
|
.mem_wdata(dout[66:34]),
|
||||||
|
.mem_wstrb(dout[70:67]),
|
||||||
|
.mem_rdata(din[33:2])
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
randluts randluts (
|
||||||
|
.din(din[41:34]),
|
||||||
|
.dout(dout[78:71])
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
randluts #(.N(150)) randluts (
|
||||||
|
.din(din[41:34]),
|
||||||
|
.dout(dout[78:71])
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module randluts(input [7:0] din, output [7:0] dout);
|
||||||
|
parameter integer N = 250;
|
||||||
|
|
||||||
|
function [31:0] xorshift32(input [31:0] xorin);
|
||||||
|
begin
|
||||||
|
xorshift32 = xorin;
|
||||||
|
xorshift32 = xorshift32 ^ (xorshift32 << 13);
|
||||||
|
xorshift32 = xorshift32 ^ (xorshift32 >> 17);
|
||||||
|
xorshift32 = xorshift32 ^ (xorshift32 << 5);
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function [63:0] lutinit(input [7:0] a, b);
|
||||||
|
begin
|
||||||
|
lutinit[63:32] = xorshift32(xorshift32(xorshift32(xorshift32({a, b} ^ `SEED))));
|
||||||
|
lutinit[31: 0] = xorshift32(xorshift32(xorshift32(xorshift32({b, a} ^ `SEED))));
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
wire [(N+1)*8-1:0] nets;
|
||||||
|
|
||||||
|
assign nets[7:0] = din;
|
||||||
|
assign dout = nets[(N+1)*8-1:N*8];
|
||||||
|
|
||||||
|
genvar i, j;
|
||||||
|
generate
|
||||||
|
for (i = 0; i < N; i = i+1) begin:is
|
||||||
|
for (j = 0; j < 8; j = j+1) begin:js
|
||||||
|
localparam integer k = xorshift32(xorshift32(xorshift32(xorshift32((i << 20) ^ (j << 10) ^ `SEED)))) & 255;
|
||||||
|
(* KEEP, DONT_TOUCH *)
|
||||||
|
LUT6 #(
|
||||||
|
.INIT(lutinit(i, j))
|
||||||
|
) lut (
|
||||||
|
.I0(nets[8*i+(k+0)%8]),
|
||||||
|
.I1(nets[8*i+(k+1)%8]),
|
||||||
|
.I2(nets[8*i+(k+2)%8]),
|
||||||
|
.I3(nets[8*i+(k+3)%8]),
|
||||||
|
.I4(nets[8*i+(k+4)%8]),
|
||||||
|
.I5(nets[8*i+(k+5)%8]),
|
||||||
|
.O(nets[8*i+8+j])
|
||||||
|
);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
endmodule
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,8 +31,8 @@ def run(node_fin, verbose=0):
|
||||||
return anode['wname']
|
return anode['wname']
|
||||||
#return (anode['tile_type'], anode['wname'])
|
#return (anode['tile_type'], anode['wname'])
|
||||||
|
|
||||||
if nodei % 1000 == 0:
|
if nodei % 100000 == 0:
|
||||||
print 'Check node %d' % nodei
|
print('Check node %d, %u node types' % (nodei, len(refnodes)))
|
||||||
# Existing node?
|
# Existing node?
|
||||||
try:
|
try:
|
||||||
refnode = refnodes[getk(anode)]
|
refnode = refnodes[getk(anode)]
|
||||||
|
|
@ -61,10 +63,10 @@ def run(node_fin, verbose=0):
|
||||||
if k in refnode and k in anode:
|
if k in refnode and k in anode:
|
||||||
|
|
||||||
def fail():
|
def fail():
|
||||||
print 'Mismatch on %s' % k
|
print('Mismatch on %s' % k)
|
||||||
print refnode[k], anode[k]
|
print(refnode[k], anode[k])
|
||||||
print refnode['l']
|
print(refnode['l'])
|
||||||
print anode['l']
|
print(anode['l'])
|
||||||
#assert 0
|
#assert 0
|
||||||
|
|
||||||
if refnode[k] != anode[k]:
|
if refnode[k] != anode[k]:
|
||||||
|
|
@ -86,6 +88,9 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
parser.add_argument('--verbose', type=int, help='')
|
parser.add_argument('--verbose', type=int, help='')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
|
'node_fn_in',
|
||||||
|
default='specimen_001/wire_all.txt',
|
||||||
|
nargs='?',
|
||||||
|
help='Input file')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Collect info on ZERO speed classes
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def run_types(tilej, verbose=False):
|
||||||
|
def process(etype):
|
||||||
|
# dict[model] = set((tile, wire/pip))
|
||||||
|
zeros = {}
|
||||||
|
print('Processing %s' % etype)
|
||||||
|
# Index delay models by type, recording where they occured
|
||||||
|
for tilek, tilev in tilej['tiles'].items():
|
||||||
|
for ename, emodel in tilev[etype].items():
|
||||||
|
if emodel.find('ZERO') >= 0:
|
||||||
|
zeros.setdefault(emodel, set()).add((tilek, ename))
|
||||||
|
# Print out delay model instances
|
||||||
|
print('%s ZERO types: %u, %s' % (etype, len(zeros), zeros.keys()))
|
||||||
|
print(
|
||||||
|
'%s ZERO instances: %u' %
|
||||||
|
(etype, sum([len(x) for x in zeros.values()])))
|
||||||
|
for model in sorted(zeros.keys()):
|
||||||
|
modelv = zeros[model]
|
||||||
|
print('Model: %s' % model)
|
||||||
|
for tile_name, element_name in sorted(list(modelv)):
|
||||||
|
print(' %s: %s' % (tile_name, element_name))
|
||||||
|
|
||||||
|
process('wires')
|
||||||
|
print('')
|
||||||
|
process('pips')
|
||||||
|
|
||||||
|
|
||||||
|
def run_prefix(tilej, verbose=False):
|
||||||
|
def process(etype):
|
||||||
|
prefixes = set()
|
||||||
|
print('Processing %s' % etype)
|
||||||
|
# Index delay models by type, recording where they occured
|
||||||
|
for tilek, tilev in tilej['tiles'].items():
|
||||||
|
for ename, emodel in tilev[etype].items():
|
||||||
|
prefix = emodel.split('_')[0]
|
||||||
|
prefixes.add(prefix)
|
||||||
|
print('%s prefixes: %u' % (etype, len(prefixes)))
|
||||||
|
for prefix in sorted(prefixes):
|
||||||
|
print(' %s' % prefix)
|
||||||
|
|
||||||
|
process('wires')
|
||||||
|
print('')
|
||||||
|
process('pips')
|
||||||
|
|
||||||
|
|
||||||
|
def run(fnin, verbose=False):
|
||||||
|
tilej = json.load((open(fnin, 'r')))
|
||||||
|
run_types(tilej)
|
||||||
|
print('')
|
||||||
|
print('')
|
||||||
|
run_prefix(tilej)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Solve timing solution')
|
||||||
|
parser.add_argument(
|
||||||
|
'fnin',
|
||||||
|
default="../timgrid/build/timgrid-s.json",
|
||||||
|
nargs='?',
|
||||||
|
help='input timgrid JSON')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
run(args.fnin, verbose=False)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
@ -51,7 +51,7 @@ def main():
|
||||||
default='build/timgrid-vc.json',
|
default='build/timgrid-vc.json',
|
||||||
help='tilegrid timing delay values at corner (timgrid-vc.json)')
|
help='tilegrid timing delay values at corner (timgrid-vc.json)')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'fn_ins', nargs='+', help='Input flattened timing csv (flat.json)')
|
'fn_ins', nargs='+', help='Input flattened timing csv (flat.csv)')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
run(args.fn_ins, args.out, args.timgrid_s, verbose=False)
|
run(args.fn_ins, args.out, args.timgrid_s, verbose=False)
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,76 @@ import sys
|
||||||
import random
|
import random
|
||||||
import glob
|
import glob
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
import collections
|
||||||
|
|
||||||
from benchmark import Benchmark
|
from benchmark import Benchmark
|
||||||
|
|
||||||
NAME_ZERO = set(
|
|
||||||
|
# Equations are filtered out until nothing is left
|
||||||
|
class SimplifiedToZero(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# http://code.activestate.com/recipes/576694/
|
||||||
|
class OrderedSet(collections.MutableSet):
|
||||||
|
def __init__(self, iterable=None):
|
||||||
|
self.end = end = []
|
||||||
|
end += [None, end, end] # sentinel node for doubly linked list
|
||||||
|
self.map = {} # key --> [key, prev, next]
|
||||||
|
if iterable is not None:
|
||||||
|
self |= iterable
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.map)
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
return key in self.map
|
||||||
|
|
||||||
|
def add(self, key):
|
||||||
|
if key not in self.map:
|
||||||
|
end = self.end
|
||||||
|
curr = end[1]
|
||||||
|
curr[2] = end[1] = self.map[key] = [key, curr, end]
|
||||||
|
|
||||||
|
def discard(self, key):
|
||||||
|
if key in self.map:
|
||||||
|
key, prev, next = self.map.pop(key)
|
||||||
|
prev[2] = next
|
||||||
|
next[1] = prev
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
end = self.end
|
||||||
|
curr = end[2]
|
||||||
|
while curr is not end:
|
||||||
|
yield curr[0]
|
||||||
|
curr = curr[2]
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
end = self.end
|
||||||
|
curr = end[1]
|
||||||
|
while curr is not end:
|
||||||
|
yield curr[0]
|
||||||
|
curr = curr[1]
|
||||||
|
|
||||||
|
def pop(self, last=True):
|
||||||
|
if not self:
|
||||||
|
raise KeyError('set is empty')
|
||||||
|
key = self.end[1][0] if last else self.end[2][0]
|
||||||
|
self.discard(key)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if not self:
|
||||||
|
return '%s()' % (self.__class__.__name__, )
|
||||||
|
return '%s(%r)' % (self.__class__.__name__, list(self))
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, OrderedSet):
|
||||||
|
return len(self) == len(other) and list(self) == list(other)
|
||||||
|
return set(self) == set(other)
|
||||||
|
|
||||||
|
|
||||||
|
NAME_ZERO = OrderedSet(
|
||||||
[
|
[
|
||||||
"BSW_CLK_ZERO",
|
"BSW_CLK_ZERO",
|
||||||
"BSW_ZERO",
|
"BSW_ZERO",
|
||||||
|
|
@ -40,12 +106,8 @@ corner_s2i = OrderedDict(
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
# Equations are filtered out until nothing is left
|
|
||||||
class SimplifiedToZero(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def allow_zero_eqns():
|
def allow_zero_eqns():
|
||||||
|
'''If true, allow a system of equations with no equations'''
|
||||||
return os.getenv('ALLOW_ZERO_EQN', 'N') == 'Y'
|
return os.getenv('ALLOW_ZERO_EQN', 'N') == 'Y'
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -148,7 +210,7 @@ def check_feasible(A_ub, b_ub):
|
||||||
def Ab_ub_dt2d(eqns):
|
def Ab_ub_dt2d(eqns):
|
||||||
'''Convert dict using the rows as keys into a list of dicts + b_ub list (ie return A_ub, b_ub)'''
|
'''Convert dict using the rows as keys into a list of dicts + b_ub list (ie return A_ub, b_ub)'''
|
||||||
#return [dict(rowt) for rowt in eqns]
|
#return [dict(rowt) for rowt in eqns]
|
||||||
rows = [(dict(rowt), b) for rowt, b in eqns.items()]
|
rows = [(OrderedDict(rowt), b) for rowt, b in eqns.items()]
|
||||||
A_ubd, b_ub = zip(*rows)
|
A_ubd, b_ub = zip(*rows)
|
||||||
return list(A_ubd), list(b_ub)
|
return list(A_ubd), list(b_ub)
|
||||||
|
|
||||||
|
|
@ -411,7 +473,7 @@ def derive_eq_by_col(A_ubd, b_ub, verbose=0):
|
||||||
b_ub[row_refi] /= v
|
b_ub[row_refi] /= v
|
||||||
knowns[k] = b_ub[row_refi]
|
knowns[k] = b_ub[row_refi]
|
||||||
print(' done')
|
print(' done')
|
||||||
#knowns_set = set(knowns.keys())
|
#knowns_set = OrderedSet(knowns.keys())
|
||||||
print('%d constrained' % len(knowns))
|
print('%d constrained' % len(knowns))
|
||||||
'''
|
'''
|
||||||
Now see what we can do
|
Now see what we can do
|
||||||
|
|
@ -648,7 +710,7 @@ def loadc_Ads_raw(fns):
|
||||||
|
|
||||||
|
|
||||||
def index_names(Ads):
|
def index_names(Ads):
|
||||||
names = set()
|
names = OrderedSet()
|
||||||
for row_ds in Ads:
|
for row_ds in Ads:
|
||||||
for k1 in row_ds.keys():
|
for k1 in row_ds.keys():
|
||||||
names.add(k1)
|
names.add(k1)
|
||||||
|
|
@ -671,7 +733,7 @@ def row_sub_vars(row, sub_json, strict=False, verbose=False):
|
||||||
print(row.items())
|
print(row.items())
|
||||||
|
|
||||||
delvars = 0
|
delvars = 0
|
||||||
for k in sub_json['drop_names']:
|
for k in sub_json['zero_names']:
|
||||||
try:
|
try:
|
||||||
del row[k]
|
del row[k]
|
||||||
delvars += 1
|
delvars += 1
|
||||||
|
|
@ -740,7 +802,7 @@ def run_sub_json(Ads, sub_json, strict=False, verbose=False):
|
||||||
ncols_new = 0
|
ncols_new = 0
|
||||||
|
|
||||||
print('Subbing %u rows' % len(Ads))
|
print('Subbing %u rows' % len(Ads))
|
||||||
prints = set()
|
prints = OrderedSet()
|
||||||
|
|
||||||
for rowi, row in enumerate(Ads):
|
for rowi, row in enumerate(Ads):
|
||||||
if 0 and verbose:
|
if 0 and verbose:
|
||||||
|
|
@ -847,3 +909,8 @@ def tilej_stats(tilej):
|
||||||
print(
|
print(
|
||||||
' %s: %u / %u solved, %u / %u covered' %
|
' %s: %u / %u solved, %u / %u covered' %
|
||||||
(etype, solved, net, covered, net))
|
(etype, solved, net, covered, net))
|
||||||
|
|
||||||
|
|
||||||
|
def load_bounds(bounds_csv, corner, ico=True):
|
||||||
|
Ads, b = loadc_Ads_b([bounds_csv], corner, ico=ico)
|
||||||
|
return Ads2bounds(Ads, b)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from timfuz import simplify_rows, loadc_Ads_b, index_names, A_ds2np, run_sub_json, print_eqns, Ads2bounds, instances, SimplifiedToZero, allow_zero_eqns
|
from timfuz import simplify_rows, loadc_Ads_b, index_names, A_ds2np, run_sub_json, print_eqns, Ads2bounds, instances, SimplifiedToZero, allow_zero_eqns, corner_s2i, acorner2csv
|
||||||
from timfuz_massage import massage_equations
|
from timfuz_massage import massage_equations
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import sys
|
import sys
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
def check_feasible(A_ub, b_ub):
|
def check_feasible(A_ub, b_ub):
|
||||||
|
|
@ -93,12 +94,50 @@ def filter_bounds(Ads, b, bounds, corner):
|
||||||
return ret_Ads, ret_b
|
return ret_Ads, ret_b
|
||||||
|
|
||||||
|
|
||||||
|
def solve_save(outfn, xvals, names, corner, save_zero=True, verbose=False):
|
||||||
|
# ballpark minimum actual observed delay is around 7 (carry chain)
|
||||||
|
# anything less than one is probably a solver artifact
|
||||||
|
delta = 0.5
|
||||||
|
corneri = corner_s2i[corner]
|
||||||
|
|
||||||
|
roundf = {
|
||||||
|
'fast_max': math.ceil,
|
||||||
|
'fast_min': math.floor,
|
||||||
|
'slow_max': math.ceil,
|
||||||
|
'slow_min': math.floor,
|
||||||
|
}[corner]
|
||||||
|
|
||||||
|
print('Writing results')
|
||||||
|
zeros = 0
|
||||||
|
with open(outfn, 'w') as fout:
|
||||||
|
# write as one variable per line
|
||||||
|
# this natively forms a bound if fed into linprog solver
|
||||||
|
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
||||||
|
for xval, name in zip(xvals, names):
|
||||||
|
row_ico = 1
|
||||||
|
|
||||||
|
if xval < delta:
|
||||||
|
if verbose:
|
||||||
|
print('WARNING: near 0 delay on %s: %0.6f' % (name, xval))
|
||||||
|
zeros += 1
|
||||||
|
if not save_zero:
|
||||||
|
continue
|
||||||
|
items = [str(row_ico), acorner2csv(roundf(xval), corneri)]
|
||||||
|
items.append('%u %s' % (1, name))
|
||||||
|
fout.write(','.join(items) + '\n')
|
||||||
|
nonzeros = len(names) - zeros
|
||||||
|
print(
|
||||||
|
'Wrote: %u / %u constrained delays, %u zeros' %
|
||||||
|
(nonzeros, len(names), zeros))
|
||||||
|
assert nonzeros, 'Failed to estimate delay'
|
||||||
|
|
||||||
|
|
||||||
def run(
|
def run(
|
||||||
fns_in,
|
fns_in,
|
||||||
corner,
|
corner,
|
||||||
run_corner,
|
run_corner,
|
||||||
sub_json=None,
|
sub_json=None,
|
||||||
sub_csv=None,
|
bounds_csv=None,
|
||||||
dedup=True,
|
dedup=True,
|
||||||
massage=False,
|
massage=False,
|
||||||
outfn=None,
|
outfn=None,
|
||||||
|
|
@ -132,8 +171,8 @@ def run(
|
||||||
Special .csv containing one variable per line
|
Special .csv containing one variable per line
|
||||||
Used primarily for multiple optimization passes, such as different algorithms or additional constraints
|
Used primarily for multiple optimization passes, such as different algorithms or additional constraints
|
||||||
'''
|
'''
|
||||||
if sub_csv:
|
if bounds_csv:
|
||||||
Ads2, b2 = loadc_Ads_b([sub_csv], corner, ico=True)
|
Ads2, b2 = loadc_Ads_b([bounds_csv], corner, ico=True)
|
||||||
bounds = Ads2bounds(Ads2, b2)
|
bounds = Ads2bounds(Ads2, b2)
|
||||||
assert len(bounds), 'Failed to load bounds'
|
assert len(bounds), 'Failed to load bounds'
|
||||||
rows_old = len(Ads)
|
rows_old = len(Ads)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue