timfuz: track corners properly

Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
John McMaster 2018-09-11 19:45:33 -07:00
parent a4950a8602
commit a55b766c44
8 changed files with 111 additions and 92 deletions

View File

@ -1,11 +1,10 @@
#!/usr/bin/env python3
from timfuz import Benchmark, loadc_Ads_b, index_names, load_sub, run_sub_json, instances
from timfuz import Benchmark, loadc_Ads_bs, index_names, load_sub, run_sub_json, instances
def gen_group(fnin, sub_json, strict=False, verbose=False):
print('Loading data')
# FIXME: preserve corners
Ads, b = loadc_Ads_b([fnin], corner=None, ico=True)
Ads, bs = loadc_Ads_bs([fnin], ico=True)
print('Sub: %u rows' % len(Ads))
iold = instances(Ads)
@ -15,10 +14,10 @@ def gen_group(fnin, sub_json, strict=False, verbose=False):
print("Sub: %u => %u names" % (len(names_old), len(names)))
print('Sub: %u => %u instances' % (iold, instances(Ads)))
for row_ds, row_b in zip(Ads, b):
yield row_ds, [row_b for _ in range(4)]
for row_ds, row_bs in zip(Ads, bs):
yield row_ds, row_bs
def run(fns_in, fnout, sub_json, corner=None, strict=False, verbose=False):
def run(fns_in, fnout, sub_json, strict=False, verbose=False):
with open(fnout, 'w') as fout:
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
for fn_in in fns_in:
@ -38,7 +37,6 @@ def main():
)
parser.add_argument('--verbose', action='store_true', help='')
parser.add_argument('--massage', action='store_true', help='')
parser.add_argument('--strict', action='store_true', help='')
parser.add_argument('--sub-csv', help='')
parser.add_argument('--sub-json', required=True, help='Group substitutions to make fully ranked')

View File

@ -2,7 +2,7 @@
# https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.optimize.linprog.html
from scipy.optimize import linprog
from timfuz import Benchmark, Ar_di2np, Ar_ds2t, A_di2ds, A_ds2di, simplify_rows, loadc_Ads_b, index_names, A_ds2np, load_sub, run_sub_json, A_ub_np2d, print_eqns, print_eqns_np, Ads2bounds, loadc_Ads_raw
from timfuz import Benchmark, loadc_Ads_bs, load_sub, Ads2bounds, corners2csv, corner_s2i
from timfuz_massage import massage_equations
import numpy as np
import glob
@ -15,15 +15,13 @@ import datetime
import os
import time
def gen_flat(fnin, sub_json):
# FIXME: preserve all bounds
# Ads, bs = loadc_Ads_raw([csv_fn_in])
Ads, b = loadc_Ads_b([fnin], corner=None, ico=True)
bounds = Ads2bounds(Ads, b)
def gen_flat(fnin, sub_json, corner=None):
Ads, bs = loadc_Ads_bs([fnin], ico=True)
bounds = Ads2bounds(Ads, bs)
zeros = set()
nonzeros = set()
for bound_name, bound_b in bounds.items():
for bound_name, bound_bs in bounds.items():
sub = sub_json['subs'].get(bound_name, None)
if sub:
# put entire delay into pivot
@ -34,18 +32,23 @@ def gen_flat(fnin, sub_json):
#for name in non_pivot:
# assert name not in nonzeros, (pivot, name, nonzeros)
zeros.update(non_pivot)
yield pivot, bound_b
yield pivot, bound_bs
else:
nonzeros.add(bound_name)
yield bound_name, bound_b
yield bound_name, bound_bs
# non-pivots can appear multiple times, but they should always be zero
# however, due to substitution limitations, just warn
violations = zeros.intersection(nonzeros)
if len(violations):
print('WARNING: %s non-0 non-pivot' % (len(violations)))
for zero in zeros - violations:
yield zero, 0
# XXX: how to best handle these?
# should they be fixed 0?
if corner:
zero_row = [None, None, None, None]
zero_row[corner_s2i[corner]] = 0
for zero in zeros - violations:
yield zero, zero_row
def run(fnin, fnout, sub_json, corner=None, sort=False, verbose=False):
if sort:
@ -55,10 +58,10 @@ def run(fnin, fnout, sub_json, corner=None, sort=False, verbose=False):
with open(fnout, 'w') as fout:
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
for name, delay in sortf(gen_flat(fnin, sub_json)):
#for name, corners in sortf(gen_flat(fnin, sub_json)):
for name, corners in gen_flat(fnin, sub_json, corner=corner):
row_ico = 1
corners = [delay for _ in range(4)]
items = [str(row_ico), ' '.join([str(x) for x in corners])]
items = [str(row_ico), corners2csv(corners)]
items.append('%u %s' % (1, name))
fout.write(','.join(items) + '\n')
@ -74,6 +77,7 @@ def main():
parser.add_argument('--sort', action='store_true', help='')
parser.add_argument('--sub-csv', help='')
parser.add_argument('--sub-json', required=True, help='Group substitutions to make fully ranked')
parser.add_argument('--corner', default=None, help='')
parser.add_argument('fnin', default=None, help='input timing delay .csv')
parser.add_argument('fnout', default=None, help='output timing delay .csv')
args = parser.parse_args()
@ -83,7 +87,7 @@ def main():
sub_json = load_sub(args.sub_json)
try:
run(args.fnin, args.fnout, sub_json=sub_json, sort=args.sort, verbose=args.verbose)
run(args.fnin, args.fnout, sub_json=sub_json, sort=args.sort, verbose=args.verbose, corner=args.corner)
finally:
print('Exiting after %s' % bench)

View File

@ -3,6 +3,7 @@ SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
CSVS := $(addsuffix /timing3.csv,$(SPECIMENS))
TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing
CORNER=slow_max
all: build/tilea.json
@ -26,7 +27,7 @@ build/sub.json: $(SPECIMENS_OK)
mkdir -p build
# Discover which variables can be separated
# This is typically the longest running operation
python3 $(TIMFUZ_DIR)/rref.py --simplify --out build/sub.json.tmp $(CSVS)
python3 $(TIMFUZ_DIR)/rref.py --corner $(CORNER) --simplify --out build/sub.json.tmp $(CSVS)
mv build/sub.json.tmp build/sub.json
build/grouped.csv: $(SPECIMENS_OK) build/sub.json
@ -41,17 +42,17 @@ build/checksub: build/grouped.csv build/sub.json
build/leastsq.csv: build/sub.json build/grouped.csv build/checksub
# Create a rough timing model that approximately fits the given paths
python3 $(TIMFUZ_DIR)/solve_leastsq.py --sub-json build/sub.json build/grouped.csv --out build/leastsq.csv.tmp
python3 $(TIMFUZ_DIR)/solve_leastsq.py --sub-json build/sub.json build/grouped.csv --corner $(CORNER) --out build/leastsq.csv.tmp
mv build/leastsq.csv.tmp build/leastsq.csv
build/linprog.csv: build/leastsq.csv build/grouped.csv
# Tweak rough timing model, making sure all constraints are satisfied
python3 $(TIMFUZ_DIR)/solve_linprog.py --sub-json build/sub.json --sub-csv build/leastsq.csv --massage build/grouped.csv --out build/linprog.csv.tmp
python3 $(TIMFUZ_DIR)/solve_linprog.py --sub-json build/sub.json --sub-csv build/leastsq.csv --massage build/grouped.csv --corner $(CORNER) --out build/linprog.csv.tmp
mv build/linprog.csv.tmp build/linprog.csv
build/flat.csv: build/linprog.csv
# Take separated variables and back-annotate them to the original timing variables
python3 $(TIMFUZ_DIR)/csv_group2flat.py --sub-json build/sub.json --sort build/linprog.csv build/flat.csv.tmp
python3 $(TIMFUZ_DIR)/csv_group2flat.py --sub-json build/sub.json --corner $(CORNER) --sort build/linprog.csv build/flat.csv.tmp
mv build/flat.csv.tmp build/flat.csv
build/tilea.json: build/flat.csv

View File

@ -2,20 +2,16 @@
# https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.optimize.linprog.html
from scipy.optimize import linprog
from timfuz import Benchmark, Ar_di2np, Ar_ds2t, A_di2ds, A_ds2di, simplify_rows, loadc_Ads_b, index_names, A_ds2np, load_sub, run_sub_json, A_ub_np2d, print_eqns, print_eqns_np
from timfuz_massage import massage_equations
from timfuz import Benchmark, load_sub, corner_s2i, acorner2csv
import timfuz
import numpy as np
import glob
import json
import math
from collections import OrderedDict
from fractions import Fraction
import sys
import datetime
import os
import time
import timfuz_solve
import numpy
import scipy.optimize as optimize
from scipy.optimize import least_squares
@ -37,10 +33,11 @@ def mkestimate(Anp, b):
x0[coli] = min(x0[coli], ub)
return x0
def save(outfn, res, names):
def save(outfn, res, 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]
print('Writing resutls')
skips = 0
@ -59,14 +56,13 @@ def save(outfn, res, names):
skips += 1
continue
#xvali = round(xval)
xvali = math.ceil(xval)
corners = [xvali for _ in range(4)]
items = [str(row_ico), ' '.join([str(x) for x in corners])]
items = [str(row_ico), acorner2csv(math.ceil(xval), corneri)]
items.append('%u %s' % (1, name))
fout.write(','.join(items) + '\n')
print('Wrote: skip %u => %u / %u valid delays' % (skips, len(names) - skips, len(names)))
def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None):
def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
# Given timing scores for above delays (-ps)
assert type(Anp[0]) is np.ndarray, type(Anp[0])
assert type(b) is np.ndarray, type(b)
@ -135,7 +131,7 @@ def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None):
print('Done')
if outfn:
save(outfn, res, names)
save(outfn, res, names, corner)
def main():
import argparse

View File

@ -2,21 +2,44 @@
# https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.optimize.linprog.html
from scipy.optimize import linprog
from timfuz import Benchmark, Ar_di2np, Ar_ds2t, A_di2ds, A_ds2di, simplify_rows, loadc_Ads_b, index_names, A_ds2np, load_sub, run_sub_json, A_ub_np2d, print_eqns, print_eqns_np
from timfuz_massage import massage_equations
from timfuz import Benchmark, load_sub, A_ub_np2d, acorner2csv, corner_s2i
import numpy as np
import glob
import json
import math
from collections import OrderedDict
from fractions import Fraction
import sys
import datetime
import os
import time
import timfuz_solve
def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None):
def save(outfn, res, 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]
print('Writing resutls')
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(res.x, 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(math.ceil(xval), corneri)]
items.append('%u %s' % (1, name))
fout.write(','.join(items) + '\n')
print('Wrote: zeros %u => %u / %u constrained delays' % (zeros, len(names) - zeros, len(names)))
def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
# Given timing scores for above delays (-ps)
assert type(Anp[0]) is np.ndarray, type(Anp[0])
assert type(b) is np.ndarray, type(b)
@ -41,12 +64,6 @@ def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None):
#A_ub = -1.0 * Anp
A_ub = [-1.0 * x for x in Anp]
if verbose:
print('')
print('A_ub b_ub')
print_eqns_np(A_ub, b_ub, verbose=verbose)
print('')
print('Creating misc constants...')
# Minimization function scalars
# Treat all logic elements as equally important
@ -107,33 +124,7 @@ def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None):
print('Delay on %d / %d' % (nonzeros, len(res.x)))
if outfn:
# ballpark minimum actual observed delay is around 7 (carry chain)
# anything less than one is probably a solver artifact
delta = 0.5
print('Writing resutls')
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(res.x, 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
#xvali = round(xval)
xvali = math.ceil(xval)
corners = [xvali for _ in range(4)]
items = [str(row_ico), ' '.join([str(x) for x in corners])]
items.append('%u %s' % (1, name))
fout.write(','.join(items) + '\n')
print('Wrote: zeros %u => %u / %u constrained delays' % (zeros, len(names) - zeros, len(names)))
save(outfn, res, names, corner)
def main():
import argparse
@ -147,7 +138,7 @@ def main():
parser.add_argument('--massage', action='store_true', help='')
parser.add_argument('--sub-csv', help='')
parser.add_argument('--sub-json', help='Group substitutions to make fully ranked')
parser.add_argument('--corner', default="slow_max", help='')
parser.add_argument('--corner', default=None, required=True, help='')
parser.add_argument('--out', default=None, help='output timing delay .json')
parser.add_argument(
'fns_in',

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
import timfuz
from timfuz import loadc_Ads_b, Ads2bounds
from timfuz import loadc_Ads_bs, Ads2bounds
import sys
import os
@ -17,37 +17,41 @@ def run(fnin, fnout, tile_json_fn, verbose=False):
tilej = json.load(open(tile_json_fn, 'r'))
# FIXME: all corners
Ads, b = loadc_Ads_b([fnin], corner=None, ico=True)
bounds = Ads2bounds(Ads, b)
Ads, bs = loadc_Ads_bs([fnin], ico=True)
bounds = Ads2bounds(Ads, bs)
pipn_net = 0
pipn_solved = [0, 0, 0, 0]
pipn_covered = [0, 0, 0, 0]
wiren_net = 0
wiren_solved = [0, 0, 0, 0]
wiren_covered = [0, 0, 0, 0]
for tile in tilej['tiles'].values():
pips = tile['pips']
for k, v in pips.items():
val = bounds.get('PIP_' + v, None)
pips[k] = quad(val)
pips[k] = bounds.get('PIP_' + v, [None, None, None, None])
pipn_net += 1
for i in range(4):
if pips[k][i]:
pipn_solved[i] += 1
if pips[k][i] is not None:
pipn_covered[i] += 1
wires = tile['wires']
for k, v in wires.items():
val = bounds.get('WIRE_' + v, None)
wires[k] = quad(val)
wires[k] = bounds.get('WIRE_' + v, [None, None, None, None])
wiren_net += 1
for i in range(4):
if wires[k][i]:
wiren_solved[i] += 1
if wires[k][i] is not None:
wiren_covered[i] += 1
for corner, corneri in timfuz.corner_s2i.items():
print('Corner %s' % corner)
print(' Pips: %u / %u solved' % (pipn_solved[corneri], pipn_net))
print(' Wires: %u / %u solved' % (wiren_solved[corneri], wiren_net))
print(' Pips: %u / %u solved, %u / %u covered' % (pipn_solved[corneri], pipn_net, pipn_covered[corneri], pipn_net))
print(' Wires: %u / %u solved, %u / %u covered' % (wiren_solved[corneri], wiren_net, wiren_covered[corneri], wiren_net))
json.dump(tilej, open(fnout, 'w'), sort_keys=True, indent=4, separators=(',', ': '))

View File

@ -530,7 +530,12 @@ def loadc_Ads_mkb(fns, mkb, filt):
corners = cols[1]
vars = cols[2:]
corners = [int(x) for x in corners.split()]
def mkcorner(bstr):
if bstr == 'None':
return None
else:
return int(bstr)
corners = [mkcorner(corner) for corner in corners.split()]
def mkvar(x):
i, var = x.split()
return (var, int(i))
@ -556,6 +561,16 @@ def loadc_Ads_b(fns, corner, ico=None):
return val[corneri]
return loadc_Ads_mkb(fns, mkb, filt)
def loadc_Ads_bs(fns, ico=None):
if ico is not None:
filt = lambda ico_, corners, vars: ico_ == ico
else:
filt = lambda ico_, corners, vars: True
def mkb(val):
return val
return loadc_Ads_mkb(fns, mkb, filt)
def loadc_Ads_raw(fns):
filt = lambda ico, corners, vars: True
@ -699,13 +714,13 @@ def print_eqns_np(A_ub, b_ub, verbose=0):
Adi = A_ub_np2d(A_ub)
print_eqns(Adi, b_ub, verbose=verbose)
def Ads2bounds(Ads, b):
def Ads2bounds(Ads, bs):
ret = {}
for row_ds, row_b in zip(Ads, b):
for row_ds, row_bs in zip(Ads, bs):
assert len(row_ds) == 1
k, v = list(row_ds.items())[0]
assert v == 1
ret[k] = row_b
ret[k] = row_bs
return ret
def instances(Ads):
@ -713,3 +728,13 @@ def instances(Ads):
for row_ds in Ads:
ret += sum(row_ds.values())
return ret
def acorner2csv(b, corneri):
corners = ["None" for _ in range(4)]
corners[corneri] = str(b)
return ' '.join(corners)
def corners2csv(bs):
assert len(bs) == 4
corners = ["None" if b is None else str(b) for b in bs]
return ' '.join(corners)

View File

@ -143,4 +143,4 @@ def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, mas
print('Converting to numpy...')
names, Anp = A_ds2np(Ads)
run_corner(Anp, np.asarray(b), names, outfn=outfn, verbose=verbose, **kwargs)
run_corner(Anp, np.asarray(b), names, corner, outfn=outfn, verbose=verbose, **kwargs)