timfuz: pep8 reformat

Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
John McMaster 2018-09-17 11:15:42 -07:00
parent c104e70724
commit 12978f8051
24 changed files with 586 additions and 337 deletions

View File

@ -6,6 +6,7 @@ Copyright 2010 John McMaster
import time
def time_str(delta):
fraction = delta % 1
delta -= fraction
@ -17,11 +18,12 @@ def time_str(delta):
hours = delta
return '%02d:%02d:%02d.%04d' % (hours, minutes, seconds, fraction * 10000)
class Benchmark:
start_time = None
end_time = None
def __init__(self, max_items = None):
def __init__(self, max_items=None):
# For the lazy
self.start_time = time.time()
self.end_time = None
@ -35,19 +37,19 @@ class Benchmark:
def stop(self):
self.end_time = time.time()
def advance(self, n = 1):
def advance(self, n=1):
self.cur_items += n
def set_cur_items(self, n):
self.cur_items = n
def delta_s(self):
def delta_s(self):
if self.end_time:
return self.end_time - self.start_time
else:
return time.time() - self.start_time
def __str__(self):
if self.end_time:
return time_str(self.end_time - self.start_time)
@ -65,7 +67,7 @@ class Benchmark:
eta_str = time_str(remaining)
else:
eta_str = "indeterminate"
return '%d / %d, ETA: %s @ %s' % (self.cur_items, self.max_items, eta_str, rate_s)
return '%d / %d, ETA: %s @ %s' % (
self.cur_items, self.max_items, eta_str, rate_s)
else:
return time_str(time.time() - self.start_time)

View File

@ -8,6 +8,7 @@ import math
from collections import OrderedDict
from fractions import Fraction
def Adi2matrix_random(A_ubd, b_ub, names):
# random assignment
# was making some empty rows
@ -24,6 +25,7 @@ def Adi2matrix_random(A_ubd, b_ub, names):
b_ret[dst_rowi] += b
return A_ret, b_ret
def Ads2matrix_linear(Ads, b):
names, Adi = A_ds2di(Ads)
cols = len(names)
@ -41,16 +43,19 @@ def Ads2matrix_linear(Ads, b):
dst_rowi = (dst_rowi + 1) % rows_out
return A_ret, b_ret
def pmatrix(Anp, s):
import sympy
msym = sympy.Matrix(Anp)
print(s)
sympy.pprint(msym)
def pds(Ads, s):
names, Anp = A_ds2np(Ads)
pmatrix(Anp, s)
print('Names: %s' % (names,))
print('Names: %s' % (names, ))
def run(fns_in, sub_json=None, verbose=False):
# arbitrary...data is thrown away
@ -76,7 +81,6 @@ def run(fns_in, sub_json=None, verbose=False):
# A_ub2, b_ub2 = Adi2matrix_random(A_ubd, b, names)
Amat, _bmat = Ads2matrix_linear(Ads, b)
#pmatrix(Amat, 'Matrix')
'''
The matrix must be fully ranked to even be considered reasonable
Even then, floating point error *possibly* could make it fully ranked, although probably not since we have whole numbers
@ -96,22 +100,19 @@ def run(fns_in, sub_json=None, verbose=False):
else:
print('slogdet :) : %s, %s' % (sign, logdet))
if rank != len(names):
raise Exception("Matrix not fully ranked w/ %u / %u" % (rank, len(names)))
raise Exception(
"Matrix not fully ranked w/ %u / %u" % (rank, len(names)))
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Check sub.json solution feasibility'
)
description='Check sub.json solution feasibility')
parser.add_argument('--verbose', action='store_true', help='')
parser.add_argument('--sub-json', help='')
parser.add_argument(
'fns_in',
nargs='*',
help='timing3.csv input files')
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()
@ -125,10 +126,10 @@ def main():
sub_json = load_sub(args.sub_json)
try:
run(sub_json=sub_json,
fns_in=fns_in, verbose=args.verbose)
run(sub_json=sub_json, fns_in=fns_in, verbose=args.verbose)
finally:
print('Exiting after %s' % bench)
if __name__ == '__main__':
main()

View File

@ -3,6 +3,7 @@
from timfuz import Benchmark, simplify_rows, loadc_Ads_b
import glob
def run(fout, fns_in, corner, verbose=0):
Ads, b = loadc_Ads_b(fns_in, corner, ico=True)
Ads, b = simplify_rows(Ads, b, corner=corner)
@ -18,22 +19,19 @@ def run(fout, fns_in, corner, verbose=0):
items.append('%u %s' % (v, k))
fout.write(','.join(items) + '\n')
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Create a .csv with a single process corner'
)
description='Create a .csv with a single process corner')
parser.add_argument('--verbose', type=int, help='')
parser.add_argument('--auto-name', action='store_true', help='timing3.csv => timing3c.csv')
parser.add_argument(
'--auto-name', action='store_true', help='timing3.csv => timing3c.csv')
parser.add_argument('--out', default=None, help='Output csv')
parser.add_argument('--corner', help='Output csv')
parser.add_argument(
'fns_in',
nargs='*',
help='timing3.csv input files')
parser.add_argument('fns_in', nargs='*', help='timing3.csv input files')
args = parser.parse_args()
bench = Benchmark()
@ -53,8 +51,8 @@ def main():
if not fns_in:
fns_in = glob.glob('specimen_*/timing3.csv')
run(fout=fout,
fns_in=fns_in, corner=args.corner, verbose=args.verbose)
run(fout=fout, fns_in=fns_in, corner=args.corner, verbose=args.verbose)
if __name__ == '__main__':
main()

View File

@ -2,6 +2,7 @@
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')
Ads, bs = loadc_Ads_bs([fnin], ico=True)
@ -17,6 +18,7 @@ def gen_group(fnin, sub_json, strict=False, verbose=False):
for row_ds, row_bs in zip(Ads, bs):
yield row_ds, row_bs
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')
@ -28,23 +30,21 @@ def run(fns_in, fnout, sub_json, strict=False, verbose=False):
items.append('%u %s' % (v, k))
fout.write(','.join(items) + '\n')
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Solve timing solution'
)
parser = argparse.ArgumentParser(description='Solve timing solution')
parser.add_argument('--verbose', 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')
parser.add_argument('--out', help='Output sub.json substitution result')
parser.add_argument(
'fns_in',
nargs='*',
help='timing3.txt input files')
'--sub-json',
required=True,
help='Group substitutions to make fully ranked')
parser.add_argument('--out', help='Output sub.json substitution result')
parser.add_argument('fns_in', nargs='*', help='timing3.txt input files')
args = parser.parse_args()
# Store options in dict to ease passing through functions
bench = Benchmark()
@ -52,9 +52,15 @@ def main():
sub_json = load_sub(args.sub_json)
try:
run(args.fns_in, args.out, sub_json=sub_json, strict=args.strict, verbose=args.verbose)
run(
args.fns_in,
args.out,
sub_json=sub_json,
strict=args.strict,
verbose=args.verbose)
finally:
print('Exiting after %s' % bench)
if __name__ == '__main__':
main()

View File

@ -15,6 +15,7 @@ import datetime
import os
import time
def gen_flat(fnin, sub_json, corner=None):
Ads, bs = loadc_Ads_bs([fnin], ico=True)
bounds = Ads2bounds(Ads, bs)
@ -50,6 +51,7 @@ def gen_flat(fnin, sub_json, corner=None):
for zero in zeros - violations:
yield zero, zero_row
def run(fnin, fnout, sub_json, corner=None, sort=False, verbose=False):
if sort:
sortf = sorted
@ -65,18 +67,19 @@ def run(fnin, fnout, sub_json, corner=None, sort=False, verbose=False):
items.append('%u %s' % (1, name))
fout.write(','.join(items) + '\n')
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Solve timing solution'
)
parser = argparse.ArgumentParser(description='Solve timing solution')
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-json', required=True, help='Group substitutions to make fully ranked')
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')
@ -87,9 +90,16 @@ 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, corner=args.corner)
run(
args.fnin,
args.fnout,
sub_json=sub_json,
sort=args.sort,
verbose=args.verbose,
corner=args.corner)
finally:
print('Exiting after %s' % bench)
if __name__ == '__main__':
main()

View File

@ -4,6 +4,7 @@ Verifies that node timing info is unique
import re
def gen_nodes(fin):
for l in fin:
lj = {}
@ -22,8 +23,9 @@ def gen_nodes(fin):
if name in ('COST_CODE', 'SPEED_CLASS'):
value = int(value)
lj[name] = value
tile_type, xy, wname = re.match(r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
tile_type, xy, wname = re.match(
r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
lj['tile_type'] = tile_type
lj['xy'] = xy
lj['wname'] = wname
@ -32,10 +34,12 @@ def gen_nodes(fin):
yield lj
def run(node_fin, verbose=0):
refnodes = {}
nodei = 0
for nodei, anode in enumerate(gen_nodes(node_fin)):
def getk(anode):
return anode['wname']
#return (anode['tile_type'], anode['wname'])
@ -52,22 +56,28 @@ def run(node_fin, verbose=0):
# Verify equivilence
for k in (
'SPEED_CLASS',
'COST_CODE', 'COST_CODE_NAME',
'IS_BAD', 'IS_COMPLETE', 'IS_GND', 'IS_VCC',
):
'COST_CODE',
'COST_CODE_NAME',
'IS_BAD',
'IS_COMPLETE',
'IS_GND',
'IS_VCC',
):
if k in refnode and k in anode:
def fail():
print 'Mismatch on %s' % k
print refnode[k], anode[k]
print refnode['l']
print anode['l']
#assert 0
if k == 'SPEED_CLASS':
# Parameters known to effect SPEED_CLASS
# Verify at least one parameter is different
if refnode[k] != anode[k]:
for k2 in ('IS_PIN', 'IS_INPUT_PIN', 'IS_OUTPUT_PIN', 'PIN_WIRE', 'NUM_WIRES'):
for k2 in ('IS_PIN', 'IS_INPUT_PIN', 'IS_OUTPUT_PIN',
'PIN_WIRE', 'NUM_WIRES'):
if refnode[k2] != anode[k2]:
break
else:
@ -81,19 +91,14 @@ def run(node_fin, verbose=0):
elif k in refnode or k in anode:
assert 0
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description=
'Timing fuzzer'
)
parser = argparse.ArgumentParser(description='Timing fuzzer')
parser.add_argument('--verbose', type=int, help='')
parser.add_argument(
'node_fn_in',
default='/dev/stdin',
nargs='?',
help='Input file')
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
args = parser.parse_args()
run(open(args.node_fn_in, 'r'), verbose=args.verbose)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3
'''
Triaging tool to help understand where we need more timing coverage
Finds correlated variables to help make better test cases
@ -16,6 +15,7 @@ from fractions import Fraction
import random
from sympy import Rational
def intr(r):
DELTA = 0.0001
@ -25,24 +25,30 @@ def intr(r):
assert abs(xi - x) < DELTA
r[i] = xi
def fracr(r):
intr(r)
return [Fraction(x) for x in r]
def fracm(m):
return [fracr(r) for r in m]
def symratr(r):
intr(r)
return [Rational(x) for x in r]
def symratm(m):
return [symratr(r) for r in m]
def intm(m):
[intr(r) for r in m]
return m
def create_matrix(rows, cols):
ret = np.zeros((rows, cols))
for rowi in range(rows):
@ -50,6 +56,7 @@ def create_matrix(rows, cols):
ret[rowi][coli] = random.randint(1, 10)
return ret
def create_matrix_sparse(rows, cols):
ret = np.zeros((rows, cols))
for rowi in range(rows):
@ -58,7 +65,14 @@ def create_matrix_sparse(rows, cols):
ret[rowi][coli] = random.randint(1, 10)
return ret
def run(rows=35, cols=200, verbose=False, encoding='np', sparse=False, normalize_last=True):
def run(
rows=35,
cols=200,
verbose=False,
encoding='np',
sparse=False,
normalize_last=True):
random.seed(0)
if sparse:
mnp = create_matrix_sparse(rows, cols)
@ -85,7 +99,9 @@ def run(rows=35, cols=200, verbose=False, encoding='np', sparse=False, normalize
print('Matrix')
sympy.pprint(msym)
print('%s matrix, %u rows x %u cols, sparse: %s, normlast: %s' % (encoding, len(mnp), len(mnp[0]), sparse, normalize_last))
print(
'%s matrix, %u rows x %u cols, sparse: %s, normlast: %s' %
(encoding, len(mnp), len(mnp[0]), sparse, normalize_last))
bench = Benchmark()
try:
rref, pivots = msym.rref(normalize_last=normalize_last)
@ -99,13 +115,11 @@ def run(rows=35, cols=200, verbose=False, encoding='np', sparse=False, normalize
print('rref')
sympy.pprint(rref)
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Timing fuzzer'
)
parser = argparse.ArgumentParser(description='Timing fuzzer')
parser.add_argument('--verbose', action='store_true', help='')
parser.add_argument('--sparse', action='store_true', help='')
@ -115,7 +129,14 @@ def main():
parser.add_argument('--encoding', default='np', help='')
args = parser.parse_args()
run(encoding=args.encoding, rows=args.rows, cols=args.cols, sparse=args.sparse, normalize_last=bool(args.normalize_last), verbose=args.verbose)
run(
encoding=args.encoding,
rows=args.rows,
cols=args.cols,
sparse=args.sparse,
normalize_last=bool(args.normalize_last),
verbose=args.verbose)
if __name__ == '__main__':
main()

View File

@ -4,6 +4,7 @@ Verifies that node timing info is unique
import re
def gen_wires(fin):
for l in fin:
lj = {}
@ -12,7 +13,8 @@ def gen_wires(fin):
name, value = kvs.split(':')
lj[name] = value
tile_type, xy, wname = re.match(r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
tile_type, xy, wname = re.match(
r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
lj['tile_type'] = tile_type
lj['xy'] = xy
lj['wname'] = wname
@ -21,10 +23,12 @@ def gen_wires(fin):
yield lj
def run(node_fin, verbose=0):
refnodes = {}
nodei = 0
for nodei, anode in enumerate(gen_wires(node_fin)):
def getk(anode):
return anode['wname']
return (anode['tile_type'], anode['wname'])
@ -39,29 +43,29 @@ def run(node_fin, verbose=0):
refnodes[getk(anode)] = anode
continue
k_invariant = (
'CAN_INVERT',
'IS_BUFFERED_2_0',
'IS_BUFFERED_2_1',
'IS_DIRECTIONAL',
'IS_EXCLUDED_PIP',
'IS_FIXED_INVERSION',
'IS_INVERTED',
'IS_PSEUDO',
'IS_SITE_PIP',
'IS_TEST_PIP',
)
k_varies = (
'TILE',
)
'CAN_INVERT',
'IS_BUFFERED_2_0',
'IS_BUFFERED_2_1',
'IS_DIRECTIONAL',
'IS_EXCLUDED_PIP',
'IS_FIXED_INVERSION',
'IS_INVERTED',
'IS_PSEUDO',
'IS_SITE_PIP',
'IS_TEST_PIP',
)
k_varies = ('TILE', )
# Verify equivilence
for k in k_invariant:
if k in refnode and k in anode:
def fail():
print 'Mismatch on %s' % k
print refnode[k], anode[k]
print refnode['l']
print anode['l']
#assert 0
if refnode[k] != anode[k]:
print
fail()
@ -71,19 +75,14 @@ def run(node_fin, verbose=0):
elif k not in k_varies:
assert 0
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description=
'Timing fuzzer'
)
parser = argparse.ArgumentParser(description='Timing fuzzer')
parser.add_argument('--verbose', type=int, help='')
parser.add_argument(
'node_fn_in',
default='/dev/stdin',
nargs='?',
help='Input file')
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
args = parser.parse_args()
run(open(args.node_fn_in, 'r'), verbose=args.verbose)

View File

@ -6,7 +6,6 @@ parser = argparse.ArgumentParser(description='')
parser.add_argument('--sdx', default='8', help='')
parser.add_argument('--sdy', default='4', help='')
args = parser.parse_args()
'''
Generate in pairs
Fill up switchbox quad for now
@ -26,15 +25,16 @@ nin = 6 * nlut
nout = nlut
print('//placelut simple')
print('//SBASE: %s' % (SBASE,))
print('//SDX: %s' % (SDX,))
print('//SDY: %s' % (SDX,))
print('//nlut: %s' % (nlut,))
print('''\
print('//SBASE: %s' % (SBASE, ))
print('//SDX: %s' % (SDX, ))
print('//SDY: %s' % (SDX, ))
print('//nlut: %s' % (nlut, ))
print(
'''\
module roi (
input wire clk,
input wire [%u:0] ins,
output wire [%u:0] outs);''') % (nin - 1, nout -1)
output wire [%u:0] outs);''') % (nin - 1, nout - 1)
ini = 0
outi = 0
@ -43,7 +43,8 @@ for lutx in xrange(SBASE[0], SBASE[0] + SDX):
loc = "SLICE_X%uY%u" % (lutx, luty)
for belc in 'ABCD':
bel = '%c6LUT' % belc
print('''\
print(
'''\
(* KEEP, DONT_TOUCH, LOC="%s", BEL="%s" *)
LUT6 #(
@ -54,12 +55,13 @@ for lutx in xrange(SBASE[0], SBASE[0] + SDX):
.I%u(ins[%u]),''' % (i, ini))
ini += 1
print('''\
.O(outs[%u]));''') % (outi,)
.O(outs[%u]));''') % (outi, )
outi += 1
assert nin == ini
assert nout == outi
print('''
print(
'''
endmodule
module top(input wire clk, input wire stb, input wire di, output wire do);
@ -88,4 +90,3 @@ module top(input wire clk, input wire stb, input wire di, output wire do);
.outs(dout)
);
endmodule''') % (nin, nout)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
'''
Note: vivado will (by default) fail bitgen DRC on LUT feedback loops
Looks like can probably be disabled, but we actually don't need a bitstream for timing analysis
@ -14,7 +13,6 @@ parser = argparse.ArgumentParser(description='')
parser.add_argument('--sdx', default='8', help='')
parser.add_argument('--sdy', default='4', help='')
args = parser.parse_args()
'''
Generate in pairs
Fill up switchbox quad for now
@ -34,15 +32,16 @@ nin = 6 * nlut
nout = nlut
print('//placelut w/ feedback')
print('//SBASE: %s' % (SBASE,))
print('//SDX: %s' % (SDX,))
print('//SDY: %s' % (SDX,))
print('//nlut: %s' % (nlut,))
print('''\
print('//SBASE: %s' % (SBASE, ))
print('//SDX: %s' % (SDX, ))
print('//SDY: %s' % (SDX, ))
print('//nlut: %s' % (nlut, ))
print(
'''\
module roi (
input wire clk,
input wire [%u:0] ins,
output wire [%u:0] outs);''') % (nin - 1, nout -1)
output wire [%u:0] outs);''') % (nin - 1, nout - 1)
ini = 0
outi = 0
@ -51,7 +50,8 @@ for lutx in xrange(SBASE[0], SBASE[0] + SDX):
loc = "SLICE_X%uY%u" % (lutx, luty)
for belc in 'ABCD':
bel = '%c6LUT' % belc
print('''\
print(
'''\
(* KEEP, DONT_TOUCH, LOC="%s", BEL="%s" *)
LUT6 #(
@ -66,12 +66,13 @@ for lutx in xrange(SBASE[0], SBASE[0] + SDX):
print('''\
.I%u(%s),''' % (i, wfrom))
print('''\
.O(outs[%u]));''') % (outi,)
.O(outs[%u]));''') % (outi, )
outi += 1
#assert nin == ini
assert nout == outi
print('''
print(
'''
endmodule
module top(input wire clk, input wire stb, input wire di, output wire do);
@ -100,4 +101,3 @@ module top(input wire clk, input wire stb, input wire di, output wire do);
.outs(dout)
);
endmodule''') % (nin, nout)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
'''
Note: vivado will (by default) fail bitgen DRC on LUT feedback loops
Looks like can probably be disabled, but we actually don't need a bitstream for timing analysis
@ -18,7 +17,6 @@ parser = argparse.ArgumentParser(description='')
parser.add_argument('--sdx', default='8', help='')
parser.add_argument('--sdy', default='4', help='')
args = parser.parse_args()
'''
Generate in pairs
Fill up switchbox quad for now
@ -38,15 +36,16 @@ nin = 6 * nlut
nout = nlut
print('//placelut w/ FF + feedback')
print('//SBASE: %s' % (SBASE,))
print('//SDX: %s' % (SDX,))
print('//SDY: %s' % (SDX,))
print('//nlut: %s' % (nlut,))
print('''\
print('//SBASE: %s' % (SBASE, ))
print('//SDX: %s' % (SDX, ))
print('//SDY: %s' % (SDX, ))
print('//nlut: %s' % (nlut, ))
print(
'''\
module roi (
input wire clk,
input wire [%u:0] ins,
output wire [%u:0] outs);''') % (nin - 1, nout -1)
output wire [%u:0] outs);''') % (nin - 1, nout - 1)
ini = 0
outi = 0
@ -56,7 +55,8 @@ for lutx in xrange(SBASE[0], SBASE[0] + SDX):
for belc in 'ABCD':
bel = '%c6LUT' % belc
name = 'lut_x%uy%u_%c' % (lutx, luty, belc)
print('''\
print(
'''\
(* KEEP, DONT_TOUCH, LOC="%s", BEL="%s" *)
LUT6 #(
@ -75,14 +75,15 @@ for lutx in xrange(SBASE[0], SBASE[0] + SDX):
.I%u(%s),''' % (i, wfrom))
out_w = name + '_o'
print('''\
.O(%s));''') % (out_w,)
.O(%s));''') % (out_w, )
outs_w = "outs[%u]" % outi
if random.randint(0, 9) < 5:
print(' assign %s = %s;' % (outs_w, out_w))
else:
out_r = name + '_or'
print('''\
print(
'''\
reg %s;
assign %s = %s;
always @(posedge clk) begin
@ -94,7 +95,8 @@ for lutx in xrange(SBASE[0], SBASE[0] + SDX):
#assert nin == ini
assert nout == outi
print('''
print(
'''
endmodule
module top(input wire clk, input wire stb, input wire di, output wire do);
@ -123,4 +125,3 @@ module top(input wire clk, input wire stb, input wire di, output wire do);
.outs(dout)
);
endmodule''') % (nin, nout)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3
'''
Triaging tool to help understand where we need more timing coverage
Finds correlated variables to help make better test cases
@ -14,6 +13,7 @@ import sympy
from collections import OrderedDict
from fractions import Fraction
def fracr(r):
DELTA = 0.0001
@ -24,12 +24,15 @@ def fracr(r):
r[i] = xi
return [Fraction(x) for x in r]
def fracm(m):
return [fracr(r) for r in m]
def fracr_quick(r):
return [Fraction(numerator=int(x), denominator=1) for x in r]
# the way I'm doing thing they should all be even integers
# hmm this was only slightly faster
def fracm_quick(m):
@ -37,6 +40,7 @@ def fracm_quick(m):
print('fracm_quick type: %s' % t)
return [fracr_quick(r) for r in m]
class State(object):
def __init__(self, Ads, drop_names=[]):
self.Ads = Ads
@ -57,9 +61,11 @@ class State(object):
print("Stats")
print(" Substitutions: %u" % len(self.subs))
if self.subs:
print(" Largest: %u" % max([len(x) for x in self.subs.values()]))
print(
" Largest: %u" % max([len(x) for x in self.subs.values()]))
print(" Rows: %u" % len(self.Ads))
print(" Cols (in): %u" % (len(self.base_names) + len(self.drop_names)))
print(
" Cols (in): %u" % (len(self.base_names) + len(self.drop_names)))
print(" Cols (preprocessed): %u" % len(self.base_names))
print(" Drop names: %u" % len(self.drop_names))
print(" Cols (out): %u" % len(self.names))
@ -70,10 +76,11 @@ class State(object):
def load(fn_ins, simplify=False, corner=None):
Ads, b = loadc_Ads_b(fn_ins, corner=corner, ico=True)
if simplify:
print('Simplifying corner %s' % (corner,))
print('Simplifying corner %s' % (corner, ))
Ads, b = simplify_rows(Ads, b, remove_zd=False, corner=corner)
return State(Ads)
def write_state(state, fout):
j = {
'names': dict([(x, None) for x in state.names]),
@ -84,6 +91,7 @@ def write_state(state, fout):
}
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
def Anp2matrix(Anp):
'''
Original idea was to make into a square matrix
@ -99,6 +107,7 @@ def Anp2matrix(Anp):
dst_rowi = (dst_rowi + 1) % ncols
return A_ub2
def row_np2ds(rownp, names):
ret = {}
assert len(rownp) == len(names), (len(rownp), len(names))
@ -108,6 +117,7 @@ def row_np2ds(rownp, names):
ret[name] = v
return ret
def row_sym2dsf(rowsym, names):
'''Convert a sympy row into a dictionary of keys to (numerator, denominator) tuples'''
from sympy import fraction
@ -121,6 +131,7 @@ def row_sym2dsf(rowsym, names):
ret[name] = (int(num), int(den))
return ret
def state_rref(state, verbose=False):
print('Converting rows to integer keys')
names, Anp = A_ds2np(state.Ads)
@ -195,6 +206,7 @@ def state_rref(state, verbose=False):
return state
def run(fnout, fn_ins, simplify=False, corner=None, verbose=0):
print('Loading data')
@ -205,24 +217,21 @@ def run(fnout, fn_ins, simplify=False, corner=None, verbose=0):
with open(fnout, 'w') as fout:
write_state(state, fout)
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Timing fuzzer'
)
parser = argparse.ArgumentParser(description='Timing fuzzer')
parser.add_argument('--verbose', action='store_true', help='')
parser.add_argument('--simplify', action='store_true', help='')
parser.add_argument('--corner', default="slow_max", help='')
parser.add_argument('--speed-json', default='build_speed/speed.json',
parser.add_argument(
'--speed-json',
default='build_speed/speed.json',
help='Provides speed index to name translation')
parser.add_argument('--out', help='Output sub.json substitution result')
parser.add_argument(
'fns_in',
nargs='+',
help='timing3.txt input files')
parser.add_argument('fns_in', nargs='+', help='timing3.txt input files')
args = parser.parse_args()
bench = Benchmark()
@ -231,10 +240,15 @@ def main():
fns_in = glob.glob('specimen_*/timing3.csv')
try:
run(fnout=args.out,
fn_ins=args.fns_in, simplify=args.simplify, corner=args.corner, verbose=args.verbose)
run(
fnout=args.out,
fn_ins=args.fns_in,
simplify=args.simplify,
corner=args.corner,
verbose=args.verbose)
finally:
print('Exiting after %s' % bench)
if __name__ == '__main__':
main()

View File

@ -15,6 +15,7 @@ import timfuz_solve
import scipy.optimize as optimize
from scipy.optimize import least_squares
def mkestimate(Anp, b):
'''
Ballpark upper bound estimate assuming variables contribute all of the delay in their respective row
@ -33,6 +34,7 @@ def mkestimate(Anp, b):
x0[coli] = min(x0[coli], ub)
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
@ -45,7 +47,7 @@ def save(outfn, xvals, names, corner):
'fast_min': math.floor,
'slow_max': math.ceil,
'slow_min': math.floor,
}[corner]
}[corner]
print('Writing results')
skips = 0
@ -69,16 +71,18 @@ def save(outfn, xvals, names, corner):
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)))
print(
'Wrote: skip %u => %u / %u valid delays' % (skips, keeps, len(names)))
assert keeps, 'Failed to estimate delay'
def run_corner(Anp, b, names, corner, 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)
#check_feasible(Anp, b)
'''
Be mindful of signs
Have something like
@ -97,7 +101,6 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non
print('Input paths')
print(' # timing scores: %d' % len(b))
print(' Rows: %d' % rows)
'''
You must have at least as many things to optimize as variables
That is, the system must be plausibly constrained for it to attempt a solve
@ -110,10 +113,11 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non
tlast = [None]
iters = [0]
printn = [0]
def progress_print():
iters[0] += 1
if tlast[0] is None:
tlast[0]= time.time()
tlast[0] = time.time()
if time.time() - tlast[0] > 1.0:
sys.stdout.write('I:%d ' % iters[0])
tlast[0] = time.time()
@ -143,23 +147,22 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non
if outfn:
save(outfn, res.x, names, corner)
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Solve timing solution using least squares objective function'
)
'Solve timing solution using least squares objective function')
parser.add_argument('--verbose', action='store_true', help='')
parser.add_argument('--massage', action='store_true', help='')
parser.add_argument('--sub-json', help='Group substitutions to make fully ranked')
parser.add_argument('--corner', default="slow_max", help='')
parser.add_argument('--out', default=None, help='output timing delay .json')
parser.add_argument(
'fns_in',
nargs='+',
help='timing3.csv input files')
'--sub-json', help='Group substitutions to make fully ranked')
parser.add_argument('--corner', default="slow_max", help='')
parser.add_argument(
'--out', default=None, help='output timing delay .json')
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()
@ -169,10 +172,17 @@ def main():
sub_json = load_sub(args.sub_json)
try:
timfuz_solve.run(run_corner=run_corner, sub_json=sub_json,
fns_in=args.fns_in, corner=args.corner, massage=args.massage, outfn=args.out, verbose=args.verbose)
timfuz_solve.run(
run_corner=run_corner,
sub_json=sub_json,
fns_in=args.fns_in,
corner=args.corner,
massage=args.massage,
outfn=args.out,
verbose=args.verbose)
finally:
print('Exiting after %s' % bench)
if __name__ == '__main__':
main()

View File

@ -12,6 +12,7 @@ import os
import time
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
@ -23,7 +24,7 @@ def save(outfn, xvals, names, corner):
'fast_min': math.floor,
'slow_max': math.ceil,
'slow_min': math.floor,
}[corner]
}[corner]
print('Writing results')
zeros = 0
@ -45,9 +46,13 @@ def save(outfn, xvals, names, corner):
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))
print(
'Wrote: %u / %u constrained delays, %u zeros' %
(nonzeros, len(names), zeros))
def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
def run_corner(
Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
if len(Anp) == 0:
print('WARNING: zero equations')
if outfn:
@ -58,14 +63,13 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non
'slow_min': False,
'fast_max': True,
'fast_min': False,
}[corner]
}[corner]
# 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)
#check_feasible(Anp, b)
'''
Be mindful of signs
t1, t2: total delay contants
@ -133,6 +137,7 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non
tlast = [time.time()]
iters = [0]
printn = [0]
def callback(xk, **kwargs):
iters[0] = kwargs['nit']
if time.time() - tlast[0] > 1.0:
@ -147,8 +152,18 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non
# Now find smallest values for delay constants
# Due to input bounds (ex: column limit), some delay elements may get eliminated entirely
print('Running linprog w/ %d r, %d c (%d name)' % (rows, cols, len(names)))
res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, callback=callback,
options={"disp": True, 'maxiter': maxiter, 'bland': True, 'tol': 1e-6,})
res = linprog(
c,
A_ub=A_ub,
b_ub=b_ub,
bounds=bounds,
callback=callback,
options={
"disp": True,
'maxiter': maxiter,
'bland': True,
'tol': 1e-6,
})
nonzeros = 0
print('Ran %d iters' % iters[0])
if res.success:
@ -159,31 +174,31 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non
if nonzero:
nonzeros += 1
#if nonzero and (verbose >= 1 or xi > 30):
if nonzero and (verbose or ((nonzeros < 100 or nonzeros % 20 == 0) and nonzeros <= plim)):
if nonzero and (verbose or (
(nonzeros < 100 or nonzeros % 20 == 0) and nonzeros <= plim)):
print(' % 4u % -80s % 10.1f' % (xi, name, x))
print('Delay on %d / %d' % (nonzeros, len(res.x)))
if outfn:
save(outfn, res.x, names, corner)
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Solve timing solution using linear programming inequalities'
)
'Solve timing solution using linear programming inequalities')
parser.add_argument('--verbose', action='store_true', help='')
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=None, required=True, help='')
parser.add_argument('--out', default=None, help='output timing delay .json')
parser.add_argument(
'fns_in',
nargs='*',
help='timing3.csv input files')
'--sub-json', help='Group substitutions to make fully ranked')
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', nargs='*', help='timing3.csv input files')
args = parser.parse_args()
# Store options in dict to ease passing through functions
bench = Benchmark()
@ -197,10 +212,18 @@ def main():
sub_json = load_sub(args.sub_json)
try:
timfuz_solve.run(run_corner=run_corner, sub_json=sub_json, sub_csv=args.sub_csv,
fns_in=fns_in, corner=args.corner, massage=args.massage, outfn=args.out, verbose=args.verbose)
timfuz_solve.run(
run_corner=run_corner,
sub_json=sub_json,
sub_csv=args.sub_csv,
fns_in=fns_in,
corner=args.corner,
massage=args.massage,
outfn=args.out,
verbose=args.verbose)
finally:
print('Exiting after %s' % bench)
if __name__ == '__main__':
main()

View File

@ -1,5 +1,6 @@
import json
def load_speed(fin):
speed_models = {}
speed_types = {}
@ -9,9 +10,9 @@ def load_speed(fin):
for kvs in l.split():
name, value = kvs.split(':')
name = name.lower()
if name in ('class',):
if name in ('class', ):
continue
if name in ('speed_index',):
if name in ('speed_index', ):
value = int(value)
if name == 'type':
speed_types.setdefault(value, {})
@ -38,6 +39,7 @@ def load_speed(fin):
speed_models[delayk] = delay
return speed_models, speed_types
def load_cost_code(fin):
# COST_CODE:4 COST_CODE_NAME:SLOWSINGLE
cost_codes = {}
@ -53,11 +55,12 @@ def load_cost_code(fin):
'code': int(lj['cost_code']),
# Hmm is this unique per type?
#'speed_class': int(lj['speed_class']),
}
}
cost_codes[cost_code['name']] = cost_code
return cost_codes
def run(speed_fin, node_fin, fout, verbose=0):
print('Loading data')
speed_models, speed_types = load_speed(speed_fin)
@ -67,32 +70,25 @@ def run(speed_fin, node_fin, fout, verbose=0):
'speed_model': speed_models,
'speed_type': speed_types,
'cost_code': cost_codes,
}
}
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description=
'Timing fuzzer'
)
parser = argparse.ArgumentParser(description='Timing fuzzer')
parser.add_argument('--verbose', type=int, help='')
parser.add_argument(
'speed_fn_in',
default='/dev/stdin',
nargs='?',
help='Input file')
'speed_fn_in', default='/dev/stdin', nargs='?', help='Input file')
parser.add_argument(
'node_fn_in',
default='/dev/stdin',
nargs='?',
help='Input file')
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
parser.add_argument(
'fn_out',
default='/dev/stdout',
nargs='?',
help='Output file')
'fn_out', default='/dev/stdout', nargs='?', help='Output file')
args = parser.parse_args()
run(open(args.speed_fn_in, 'r'), open(args.node_fn_in, 'r'), open(args.fn_out, 'w'), verbose=args.verbose)
run(
open(args.speed_fn_in, 'r'),
open(args.node_fn_in, 'r'),
open(args.fn_out, 'w'),
verbose=args.verbose)

View File

@ -9,6 +9,7 @@ import sympy
from collections import OrderedDict
from fractions import Fraction
def mlcm(xs):
'''
Find the LCM between elements in a group
@ -20,6 +21,7 @@ def mlcm(xs):
l = int(max(lthis, l))
return ret
def write_state(state, fout):
j = {
'names': dict([(x, None) for x in state.names]),
@ -30,6 +32,7 @@ def write_state(state, fout):
}
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
def gen_rows(fn_ins):
for fn_in in fn_ins:
try:
@ -52,7 +55,7 @@ def gen_rows(fn_ins):
n = 1
for _var, (_num, den) in sub.items():
n *= den
for var, (num, den) in sub.items():
num2 = n * num
assert num2 % den == 0
@ -62,6 +65,7 @@ def gen_rows(fn_ins):
print("Error processing %s" % fn_in)
raise
def run(fnout, fn_ins, verbose=0):
print('Loading data')
@ -76,6 +80,7 @@ def run(fnout, fn_ins, verbose=0):
items.append('%i %s' % (v, k))
fout.write(','.join(items) + '\n')
def main():
import argparse
@ -86,20 +91,17 @@ def main():
parser.add_argument('--verbose', action='store_true', help='')
parser.add_argument('--out', help='Output csv')
parser.add_argument(
'fns_in',
nargs='*',
help='sub.json input files')
parser.add_argument('fns_in', nargs='*', help='sub.json input files')
args = parser.parse_args()
bench = Benchmark()
fns_in = args.fns_in
try:
run(fnout=args.out,
fn_ins=args.fns_in, verbose=args.verbose)
run(fnout=args.out, fn_ins=args.fns_in, verbose=args.verbose)
finally:
print('Exiting after %s' % bench)
if __name__ == '__main__':
main()

View File

@ -8,10 +8,12 @@ import os
import time
import json
# corner wokraround
def quad(x):
return [x for _ in range(4)]
def run(fnin, fnout, tile_json_fn, verbose=False):
# modified in place
tilej = json.load(open(tile_json_fn, 'r'))
@ -50,18 +52,26 @@ def run(fnin, fnout, tile_json_fn, verbose=False):
for corner, corneri in timfuz.corner_s2i.items():
print('Corner %s' % corner)
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))
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=(',', ': '))
json.dump(tilej, open(fnout, 'w'), sort_keys=True, indent=4, separators=(',', ': '))
def main():
import argparse
parser = argparse.ArgumentParser(
description=
''
)
parser = argparse.ArgumentParser(description='')
parser.add_argument('--tile-json', default='tiles.json', help='')
parser.add_argument('fnin', default=None, help='Flattened timing csv')
parser.add_argument('fnout', default=None, help='output tile .json')
@ -69,5 +79,6 @@ def main():
run(args.fnin, args.fnout, args.tile_json, verbose=False)
if __name__ == '__main__':
main()

View File

@ -6,11 +6,12 @@ import time
import json
from collections import OrderedDict
corner_s2i = OrderedDict([
('fast_max', 0),
('fast_min', 1),
('slow_max', 2),
('slow_min', 3),
corner_s2i = OrderedDict(
[
('fast_max', 0),
('fast_min', 1),
('slow_max', 2),
('slow_min', 3),
])
corner2minmax = {
@ -18,7 +19,8 @@ corner2minmax = {
'fast_min': min,
'slow_max': max,
'slow_min': min,
}
}
def build_tilejo(fnins):
'''
@ -45,6 +47,7 @@ def build_tilejo(fnins):
tilejo['tiles'][tilek] = tilevi
# Otherwise combine
else:
def process_type(etype):
for pipk, pipvi in tilevi[etype].items():
pipvo = tilevo[etype][pipk]
@ -61,10 +64,12 @@ def build_tilejo(fnins):
else:
minmax = corner2minmax[cornerk]
pipvo[corneri] = minmax(cornervi, cornervo)
process_type('pips')
process_type('wires')
return tilejo
def check_corner_minmax(tilej, verbose=False):
# Post processing pass looking for min/max inconsistencies
# Especially an issue due to complexities around under-constrained elements
@ -73,12 +78,13 @@ def check_corner_minmax(tilej, verbose=False):
checks = 0
bad = 0
for tilev in tilej['tiles'].values():
def process_type(etype):
nonlocal checks
nonlocal bad
for pipk, pipv in tilev[etype].items():
for corner in ('slow', 'fast'):
for corner in ('slow', 'fast'):
mini = corner_s2i[corner + '_min']
minv = pipv[mini]
maxi = corner_s2i[corner + '_max']
@ -87,7 +93,9 @@ def check_corner_minmax(tilej, verbose=False):
checks += 1
if minv > maxv:
if verbose:
print('WARNING: element %s %s min/max adjusted on corner %s' % (etype, pipk, corner))
print(
'WARNING: element %s %s min/max adjusted on corner %s'
% (etype, pipk, corner))
bad += 1
pipv[mini] = maxv
pipv[maxi] = minv
@ -96,28 +104,35 @@ def check_corner_minmax(tilej, verbose=False):
process_type('wires')
print('minmax: %u / %u bad' % (bad, checks))
def check_corners_minmax(tilej, verbose=False):
# TODO: check fast vs slow
pass
def run(fnins, fnout, verbose=False):
tilejo = build_tilejo(fnins)
check_corner_minmax(tilejo)
check_corners_minmax(tilejo)
json.dump(tilejo, open(fnout, 'w'), sort_keys=True, indent=4, separators=(',', ': '))
json.dump(
tilejo,
open(fnout, 'w'),
sort_keys=True,
indent=4,
separators=(',', ': '))
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Combine multiple tile corners into one .json file'
)
description='Combine multiple tile corners into one .json file')
parser.add_argument('--out', required=True, help='Combined .json file')
parser.add_argument('fnins', nargs='+', help='Input .json files')
args = parser.parse_args()
run(args.fnins, args.out, verbose=False)
if __name__ == '__main__':
main()

View File

@ -16,27 +16,30 @@ from fractions import Fraction
from benchmark import Benchmark
NAME_ZERO = set([
"BSW_CLK_ZERO",
"BSW_ZERO",
"B_ZERO",
"C_CLK_ZERO",
"C_DSP_ZERO",
"C_ZERO",
"I_ZERO",
"O_ZERO",
"RC_ZERO",
"R_ZERO",
])
NAME_ZERO = set(
[
"BSW_CLK_ZERO",
"BSW_ZERO",
"B_ZERO",
"C_CLK_ZERO",
"C_DSP_ZERO",
"C_ZERO",
"I_ZERO",
"O_ZERO",
"RC_ZERO",
"R_ZERO",
])
# csv index
corner_s2i = OrderedDict([
('fast_max', 0),
('fast_min', 1),
('slow_max', 2),
('slow_min', 3),
corner_s2i = OrderedDict(
[
('fast_max', 0),
('fast_min', 1),
('slow_max', 2),
('slow_min', 3),
])
# Equations are filtered out until nothing is left
class SimplifiedToZero(Exception):
pass
@ -45,6 +48,7 @@ class SimplifiedToZero(Exception):
def allow_zero_eqns():
return os.getenv('ALLOW_ZERO_EQN', 'N') == 'Y'
def print_eqns(A_ubd, b_ub, verbose=0, lim=3, label=''):
rows = len(b_ub)
@ -52,7 +56,8 @@ def print_eqns(A_ubd, b_ub, verbose=0, lim=3, label=''):
prints = 0
#verbose = 1
for rowi, row in enumerate(A_ubd):
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and (not lim or prints < lim)):
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and
(not lim or prints < lim)):
line = ' EQN: p%u: ' % rowi
for k, v in sorted(row.items()):
line += '%u*t%d ' % (v, k)
@ -60,6 +65,7 @@ def print_eqns(A_ubd, b_ub, verbose=0, lim=3, label=''):
print(line)
prints += 1
def print_name_eqns(A_ubd, b_ub, names, verbose=0, lim=3, label=''):
rows = len(b_ub)
@ -67,7 +73,8 @@ def print_name_eqns(A_ubd, b_ub, names, verbose=0, lim=3, label=''):
prints = 0
#verbose = 1
for rowi, row in enumerate(A_ubd):
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and (not lim or prints < lim)):
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and
(not lim or prints < lim)):
line = ' EQN: p%u: ' % rowi
for k, v in sorted(row.items()):
line += '%u*%s ' % (v, names[k])
@ -75,19 +82,23 @@ def print_name_eqns(A_ubd, b_ub, names, verbose=0, lim=3, label=''):
print(line)
prints += 1
def print_names(names, verbose=1):
print('Names: %d' % len(names))
for xi, name in enumerate(names):
print(' % 4u % -80s' % (xi, name))
def invb(b_ub):
#return [-b for b in b_ub]
return -np.array(b_ub)
def check_feasible_d(A_ubd, b_ub, names):
A_ub, b_ub_inv = Ab_d2np(A_ubd, b_ub, names)
check_feasible(A_ub, b_ub_inv)
def check_feasible(A_ub, b_ub):
sys.stdout.write('Check feasible ')
sys.stdout.flush()
@ -118,6 +129,7 @@ def check_feasible(A_ub, b_ub):
this += A_ub[row][col] * xs[col]
ret[row] = this
return ret
b_res = my_mul(A_ub, xs)
# Verify bound was respected
@ -126,10 +138,13 @@ def check_feasible(A_ub, b_ub):
sys.stdout.write('.')
sys.stdout.flush()
if this_b >= this_b_ub or this_b > 0:
print('% 4d Want res % 10.1f <= % 10.1f <= 0' % (rowi, this_b, this_b_ub))
print(
'% 4d Want res % 10.1f <= % 10.1f <= 0' %
(rowi, this_b, this_b_ub))
raise Exception("Bad ")
print(' done')
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)'''
#return [dict(rowt) for rowt in eqns]
@ -137,6 +152,7 @@ def Ab_ub_dt2d(eqns):
A_ubd, b_ub = zip(*rows)
return list(A_ubd), list(b_ub)
# This significantly reduces runtime
def simplify_rows(Ads, b_ub, remove_zd=False, corner=None):
'''Remove duplicate equations, taking highest delay'''
@ -150,14 +166,14 @@ def simplify_rows(Ads, b_ub, remove_zd=False, corner=None):
'fast_min': min,
'slow_max': max,
'slow_min': min,
}[corner]
}[corner]
# An outlier to make unknown values be ignored
T_UNK = {
'fast_max': 0,
'fast_min': 10e9,
'slow_max': 0,
'slow_min': 10e9,
}[corner]
}[corner]
sys.stdout.write('SimpR ')
sys.stdout.flush()
@ -188,12 +204,15 @@ def simplify_rows(Ads, b_ub, remove_zd=False, corner=None):
print(' done')
print('Simplify rows: %d => %d rows w/ zd %d, ze %d' % (len(b_ub), len(eqns), zero_ds, zero_es))
print(
'Simplify rows: %d => %d rows w/ zd %d, ze %d' %
(len(b_ub), len(eqns), zero_ds, zero_es))
if len(eqns) == 0:
raise SimplifiedToZero()
A_ubd_ret, b_ub_ret = Ab_ub_dt2d(eqns)
return A_ubd_ret, b_ub_ret
def A_ubr_np2d(row):
'''Convert a single row'''
#d = {}
@ -203,6 +222,7 @@ def A_ubr_np2d(row):
d[coli] = val
return d
def A_ub_np2d(A_ub):
'''Convert A_ub entries in numpy matrix to dictionary / sparse form'''
Adi = [None] * len(A_ub)
@ -210,6 +230,7 @@ def A_ub_np2d(A_ub):
Adi[i] = A_ubr_np2d(row)
return Adi
def Ar_di2np(row_di, cols):
rownp = np.zeros(cols)
for coli, val in row_di.items():
@ -217,37 +238,45 @@ def Ar_di2np(row_di, cols):
rownp[coli] = val
return rownp
# NOTE: sign inversion
def A_di2np(Adi, cols):
'''Convert A_ub entries in dictionary / sparse to numpy matrix form'''
return [Ar_di2np(row_di, cols) for row_di in Adi]
def Ar_ds2t(rowd):
'''Convert a dictionary row into a tuple with (column number, value) tuples'''
return tuple(sorted(rowd.items()))
def A_ubr_t2d(rowt):
'''Convert a dictionary row into a tuple with (column number, value) tuples'''
return OrderedDict(rowt)
def A_ub_d2t(A_ubd):
'''Convert rows as dicts to rows as tuples'''
return [Ar_ds2t(rowd) for rowd in A_ubd]
def A_ub_t2d(A_ubd):
'''Convert rows as tuples to rows as dicts'''
return [OrderedDict(rowt) for rowt in A_ubd]
def Ab_d2np(A_ubd, b_ub, names):
A_ub = A_di2np(A_ubd, len(names))
b_ub_inv = invb(b_ub)
return A_ub, b_ub_inv
def Ab_np2d(A_ub, b_ub_inv):
A_ubd = A_ub_np2d(A_ub)
b_ub = invb(b_ub_inv)
return A_ubd, b_ub
def sort_equations(Ads, b):
# Track rows with value column
# Hmm can't sort against np arrays
@ -257,6 +286,7 @@ def sort_equations(Ads, b):
A_ubtr, b_ubr = zip(*res)
return [OrderedDict(rowt) for rowt in A_ubtr], b_ubr
def derive_eq_by_row(A_ubd, b_ub, verbose=0, col_lim=0, tweak=False):
'''
Derive equations by subtracting whole rows
@ -348,10 +378,13 @@ def derive_eq_by_row(A_ubd, b_ub, verbose=0, col_lim=0, tweak=False):
print(' done')
#A_ub_ret = A_di2np(A_ubd2, cols=cols)
print('Derive row: %d => %d rows using %d lte, %d sc' % (len(b_ub), len(b_ub_ret), ltes, scs))
print(
'Derive row: %d => %d rows using %d lte, %d sc' %
(len(b_ub), len(b_ub_ret), ltes, scs))
assert len(A_ubd_ret) == len(b_ub_ret)
return A_ubd_ret, b_ub_ret
def derive_eq_by_col(A_ubd, b_ub, verbose=0):
'''
Derive equations by subtracting out all bounded constants (ie "known" columns)
@ -380,7 +413,6 @@ def derive_eq_by_col(A_ubd, b_ub, verbose=0):
print(' done')
#knowns_set = set(knowns.keys())
print('%d constrained' % len(knowns))
'''
Now see what we can do
Rows that are already constrained: eliminate
@ -425,6 +457,7 @@ def derive_eq_by_col(A_ubd, b_ub, verbose=0):
print('Derive col: %d => %d rows' % (len(b_ub), len(b_ub_ret)))
return A_ubd_ret, b_ub_ret
def col_dist(Ads, desc='of', names=[], lim=0):
'''print(frequency distribution of number of elements in a given row'''
rows = len(Ads)
@ -435,7 +468,9 @@ def col_dist(Ads, desc='of', names=[], lim=0):
this_cols = len(row)
fs[this_cols] = fs.get(this_cols, 0) + 1
print('Col count distribution (%s) for %dr x %dc w/ %d freqs' % (desc, rows, cols, len(fs)))
print(
'Col count distribution (%s) for %dr x %dc w/ %d freqs' %
(desc, rows, cols, len(fs)))
prints = 0
for i, (k, v) in enumerate(sorted(fs.items())):
if lim == 0 or (lim and prints < lim or i == len(fs) - 1):
@ -444,6 +479,7 @@ def col_dist(Ads, desc='of', names=[], lim=0):
if lim and prints == lim:
print(' ...')
def name_dist(A_ubd, desc='of', names=[], lim=0):
'''print(frequency distribution of number of times an element appears'''
rows = len(A_ubd)
@ -467,7 +503,7 @@ def name_dist(A_ubd, desc='of', names=[], lim=0):
for v in fs.values():
fs2[v] = fs2.get(v, 0) + 1
prints = 0
print('Distribution distribution (%d items)'% len(fs2))
print('Distribution distribution (%d items)' % len(fs2))
for i, (k, v) in enumerate(sorted(fs2.items())):
if lim == 0 or (lim and prints < lim or i == len(fs2) - 1):
print(' %s: %s' % (k, v))
@ -479,6 +515,7 @@ def name_dist(A_ubd, desc='of', names=[], lim=0):
if zeros:
raise Exception("%d names without equation" % zeros)
def filter_ncols(A_ubd, b_ub, cols_min=0, cols_max=0):
'''Only keep equations with a few delay elements'''
A_ubd_ret = []
@ -486,26 +523,32 @@ def filter_ncols(A_ubd, b_ub, cols_min=0, cols_max=0):
#print('Removing large rows')
for rowd, b in zip(A_ubd, b_ub):
if (not cols_min or len(rowd) >= cols_min) and (not cols_max or len(rowd) <= cols_max):
if (not cols_min or len(rowd) >= cols_min) and (not cols_max or
len(rowd) <= cols_max):
A_ubd_ret.append(rowd)
b_ub_ret.append(b)
print('Filter ncols w/ %d <= cols <= %d: %d ==> %d rows' % (cols_min, cols_max, len(b_ub), len(b_ub_ret)))
print(
'Filter ncols w/ %d <= cols <= %d: %d ==> %d rows' %
(cols_min, cols_max, len(b_ub), len(b_ub_ret)))
assert len(b_ub_ret)
return A_ubd_ret, b_ub_ret
def Ar_di2ds(rowA, names):
row = OrderedDict()
for k, v in rowA.items():
row[names[k]] = v
return row
def A_di2ds(Adi, names):
rows = []
for row_di in Adi:
rows.append(Ar_di2ds(row_di, names))
return rows
def Ar_ds2di(row_ds, names):
def keyi(name):
if name not in names:
@ -517,6 +560,7 @@ def Ar_ds2di(row_ds, names):
row_di[keyi(k)] = v
return row_di
def A_ds2di(rows):
names = OrderedDict()
@ -526,10 +570,12 @@ def A_ds2di(rows):
return list(names.keys()), A_ubd
def A_ds2np(Ads):
names, Adi = A_ds2di(Ads)
return names, A_di2np(Adi, len(names))
def loadc_Ads_mkb(fns, mkb, filt):
bs = []
Ads = []
@ -548,10 +594,13 @@ def loadc_Ads_mkb(fns, mkb, filt):
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))
vars = OrderedDict([mkvar(var) for var in vars])
if not filt(ico, corners, vars):
continue
@ -561,6 +610,7 @@ def loadc_Ads_mkb(fns, mkb, filt):
return Ads, bs
def loadc_Ads_b(fns, corner, ico=None):
corner = corner or "slow_max"
corneri = corner_s2i[corner]
@ -572,8 +622,10 @@ def loadc_Ads_b(fns, corner, ico=None):
def mkb(val):
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
@ -582,15 +634,19 @@ def loadc_Ads_bs(fns, ico=None):
def mkb(val):
return val
return loadc_Ads_mkb(fns, mkb, filt)
def loadc_Ads_raw(fns):
filt = lambda ico, corners, vars: True
def mkb(val):
return val
return loadc_Ads_mkb(fns, mkb, filt)
def index_names(Ads):
names = set()
for row_ds in Ads:
@ -598,6 +654,7 @@ def index_names(Ads):
names.add(k1)
return names
def load_sub(fn):
j = json.load(open(fn, 'r'))
@ -607,6 +664,7 @@ def load_sub(fn):
return j
def row_sub_syms(row, sub_json, strict=False, verbose=False):
if 0 and verbose:
print("")
@ -669,6 +727,7 @@ def row_sub_syms(row, sub_json, strict=False, verbose=False):
for k, v in sorted(row.items()):
assert v > 0, (k, v)
def run_sub_json(Ads, sub_json, strict=False, verbose=False):
'''
strict: complain if a sub doesn't go in evenly
@ -709,13 +768,15 @@ def run_sub_json(Ads, sub_json, strict=False, verbose=False):
print("Sub: %u / %u rows changed" % (nsubs, nrows))
print("Sub: %u => %u non-zero row cols" % (ncols_old, ncols_new))
def print_eqns(Ads, b, verbose=0, lim=3, label=''):
rows = len(b)
print('Sample equations (%s) from %d r' % (label, rows))
prints = 0
for rowi, row in enumerate(Ads):
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and (not lim or prints < lim)):
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and
(not lim or prints < lim)):
line = ' EQN: p%u: ' % rowi
for k, v in sorted(row.items()):
line += '%u*t%s ' % (v, k)
@ -723,10 +784,12 @@ def print_eqns(Ads, b, verbose=0, lim=3, label=''):
print(line)
prints += 1
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, bs):
ret = {}
for row_ds, row_bs in zip(Ads, bs):
@ -736,17 +799,20 @@ def Ads2bounds(Ads, bs):
ret[k] = row_bs
return ret
def instances(Ads):
ret = 0
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]

View File

@ -10,6 +10,7 @@ import time
import copy
from collections import OrderedDict
def lte_const(row_ref, row_cmp):
'''Return true if all constants are smaller magnitude in row_cmp than row_ref'''
#return False
@ -22,6 +23,7 @@ def lte_const(row_ref, row_cmp):
return False
return True
def shared_const(row_ref, row_cmp):
'''Return true if more constants are equal than not equal'''
#return False
@ -43,6 +45,7 @@ def shared_const(row_ref, row_cmp):
# Will equation reduce if subtracted?
return matches > unmatches
def reduce_const(row_ref, row_cmp):
'''Subtract cmp constants from ref'''
#ret = {}
@ -57,6 +60,7 @@ def reduce_const(row_ref, row_cmp):
ret[k] = res
return ret
def derive_eq_by_row(Ads, b, verbose=0, col_lim=0, tweak=False):
'''
Derive equations by subtracting whole rows
@ -148,10 +152,13 @@ def derive_eq_by_row(Ads, b, verbose=0, col_lim=0, tweak=False):
print(' done')
#A_ub_ret = A_di2np(Ads2, cols=cols)
print('Derive row: %d => %d rows using %d lte, %d sc' % (len(b), len(b_ret), ltes, scs))
print(
'Derive row: %d => %d rows using %d lte, %d sc' %
(len(b), len(b_ret), ltes, scs))
assert len(Ads_ret) == len(b_ret)
return Ads_ret, b_ret
def derive_eq_by_near_row(Ads, b, verbose=0, col_lim=0, tweak=False):
'''
Derive equations by subtracting whole rows
@ -199,7 +206,8 @@ def derive_eq_by_near_row(Ads, b, verbose=0, col_lim=0, tweak=False):
continue
#for row_cmpi, row_cmp in enumerate(Ads):
for row_cmpi in range(max(0, row_refi - rowdelta), min(len(Ads), row_refi + rowdelta)):
for row_cmpi in range(max(0, row_refi - rowdelta),
min(len(Ads), row_refi + rowdelta)):
if row_refi == row_cmpi or col_lim and len(row_cmp) > col_lim:
continue
row_cmp = Ads[row_cmpi]
@ -246,10 +254,13 @@ def derive_eq_by_near_row(Ads, b, verbose=0, col_lim=0, tweak=False):
print(' done')
#A_ub_ret = A_di2np(Ads2, cols=cols)
print('Derive row: %d => %d rows using %d lte, %d sc' % (len(b), len(b_ret), ltes, scs))
print(
'Derive row: %d => %d rows using %d lte, %d sc' %
(len(b), len(b_ret), ltes, scs))
assert len(Ads_ret) == len(b_ret)
return Ads_ret, b_ret
def derive_eq_by_col(Ads, b_ub, verbose=0):
'''
Derive equations by subtracting out all bounded constants (ie "known" columns)
@ -277,7 +288,6 @@ def derive_eq_by_col(Ads, b_ub, verbose=0):
print(' done')
#knowns_set = set(knowns.keys())
print('%d constrained' % len(knowns))
'''
Now see what we can do
Rows that are already constrained: eliminate
@ -322,6 +332,7 @@ def derive_eq_by_col(Ads, b_ub, verbose=0):
print('Derive col: %d => %d rows' % (len(b_ub), len(b_ret)))
return Ads_ret, b_ret
# keep derriving until solution is (probably) stable
def massage_equations_old(Ads, b, verbose=False, derive_lim=3, corner=None):
'''
@ -357,7 +368,9 @@ def massage_equations_old(Ads, b, verbose=False, derive_lim=3, corner=None):
debug("der_rows")
# Run another simplify pass since new equations may have overlap with original
Ads, b = simplify_rows(Ads, b, corner=corner)
print('Derive row %d / %d: %d => %d equations' % (di + 1, derive_lim, n_orig, len(b)))
print(
'Derive row %d / %d: %d => %d equations' %
(di + 1, derive_lim, n_orig, len(b)))
debug("der_rows simp")
n_orig2 = len(b)
@ -366,19 +379,19 @@ def massage_equations_old(Ads, b, verbose=False, derive_lim=3, corner=None):
debug("der_cols")
# Run another simplify pass since new equations may have overlap with original
Ads, b = simplify_rows(Ads, b, corner=corner)
print('Derive col %d / %d: %d => %d equations' % (di + 1, derive_lim, n_orig2, len(b)))
print(
'Derive col %d / %d: %d => %d equations' %
(di + 1, derive_lim, n_orig2, len(b)))
debug("der_cols simp")
if n_orig == len(b):
break
dend = len(b)
print('')
print('Derive net: %d => %d' % (dstart, dend))
print('')
# Was experimentting to see how much the higher order columns really help
'''
cols_min_post = opts.get('cols_min_post', None)
cols_max_post = opts.get('cols_max_post', None)
@ -393,6 +406,7 @@ def massage_equations_old(Ads, b, verbose=False, derive_lim=3, corner=None):
debug("final (sorted)")
return Ads, b
# iteratively increasing column limit until all columns are added
def massage_equations_inc_col_lim(Ads, b, verbose=False, corner=None):
'''
@ -467,6 +481,7 @@ def massage_equations_inc_col_lim(Ads, b, verbose=False, corner=None):
print('Massage final: %d => %d rows' % (dstart, dend))
return Ads, b
# only derive based on nearby equations
# theory is they will be the best to diff
def massage_equations_near(Ads, b, verbose=False, corner=None):
@ -532,4 +547,5 @@ def massage_equations_near(Ads, b, verbose=False, corner=None):
print('Massage final: %d => %d rows' % (dstart, dend))
return Ads, b
massage_equations = massage_equations_inc_col_lim

View File

@ -15,6 +15,7 @@ import datetime
import os
import time
def check_feasible(A_ub, b_ub):
'''
Put large timing constants into the equations
@ -31,7 +32,6 @@ def check_feasible(A_ub, b_ub):
cols = len(A_ub[0])
progress = max(1, rows / 100)
'''
Delays should be in order of ns, so a 10 ns delay should be way above what anything should be
Series can have several hundred delay elements
@ -56,6 +56,7 @@ def check_feasible(A_ub, b_ub):
this += A_ub[row][col] * xs[col]
ret[row] = this
return ret
b_res = my_mul(A_ub, xs)
# Verify bound was respected
@ -64,10 +65,13 @@ def check_feasible(A_ub, b_ub):
sys.stdout.write('.')
sys.stdout.flush()
if this_b >= this_b_ub or this_b > 0:
print('% 4d Want res % 10.1f <= % 10.1f <= 0' % (rowi, this_b, this_b_ub))
print(
'% 4d Want res % 10.1f <= % 10.1f <= 0' %
(rowi, this_b, this_b_ub))
raise Exception("Bad ")
print(' done')
def filter_bounds(Ads, b, bounds, corner):
'''Given min variable delays, remove rows that won't constrain solution'''
#assert len(bounds) > 0
@ -76,11 +80,13 @@ def filter_bounds(Ads, b, bounds, corner):
# Keep delays possibly larger than current bound
def keep(row_b, est):
return row_b > est
T_UNK = 0
elif 'min' in corner:
# Keep delays possibly smaller than current bound
def keep(row_b, est):
return row_b < est
T_UNK = 1e9
else:
assert 0
@ -96,7 +102,18 @@ def filter_bounds(Ads, b, bounds, corner):
ret_b.append(row_b)
return ret_Ads, ret_b
def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, massage=False, outfn=None, verbose=False, **kwargs):
def run(
fns_in,
corner,
run_corner,
sub_json=None,
sub_csv=None,
dedup=True,
massage=False,
outfn=None,
verbose=False,
**kwargs):
print('Loading data')
Ads, b = loadc_Ads_b(fns_in, corner, ico=True)
@ -120,7 +137,6 @@ def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, mas
print('Sub: %u => %u instances' % (iold, instances(Ads)))
else:
names = index_names(Ads)
'''
Substitution .csv
Special .csv containing one variable per line
@ -132,7 +148,9 @@ def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, mas
assert len(bounds), 'Failed to load bounds'
rows_old = len(Ads)
Ads, b = filter_bounds(Ads, b, bounds, corner)
print('Filter bounds: %s => %s + %s rows' % (rows_old, len(Ads), len(Ads2)))
print(
'Filter bounds: %s => %s + %s rows' %
(rows_old, len(Ads), len(Ads2)))
Ads = Ads + Ads2
b = b + b2
assert len(Ads) or allow_zero_eqns()
@ -145,7 +163,6 @@ def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, mas
#print
#col_dist(A_ubd, 'final', names)
print('b10', b[0:100])
'''
Given:
a >= 10
@ -169,4 +186,11 @@ 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, corner, outfn=outfn, verbose=verbose, **kwargs)
run_corner(
Anp,
np.asarray(b),
names,
corner,
outfn=outfn,
verbose=verbose,
**kwargs)

View File

@ -6,6 +6,8 @@ import time
import json
SI_NONE = 0xFFFF
def load_speed_json(f):
j = json.load(f)
# Index speed indexes to names
@ -16,6 +18,7 @@ def load_speed_json(f):
speed_i2s[i] = k
return j, speed_i2s
def gen_tiles(fnin, speed_i2s):
for l in open(fnin):
# lappend items pip $name $speed_index
@ -28,7 +31,7 @@ def gen_tiles(fnin, speed_i2s):
pips = {}
wires = {}
for i in range(0, len(tuples), 3):
ttype, name, speed_index = tuples[i:i+3]
ttype, name, speed_index = tuples[i:i + 3]
name_local = name.split('/')[1]
{
'pip': pips,
@ -36,6 +39,7 @@ def gen_tiles(fnin, speed_i2s):
}[ttype][name_local] = speed_i2s[int(speed_index)]
yield (tile_type, tile_name, grid_x, grid_y, pips, wires)
def run(fnin, fnout, speed_json_fn, verbose=False):
speedj, speed_i2s = load_speed_json(open(speed_json_fn, 'r'))
@ -46,23 +50,24 @@ def run(fnin, fnout, speed_json_fn, verbose=False):
if tile_type not in tiles:
tiles[tile_type] = this_dat
else:
if tiles[tile_type] != this_dat:
print(tile_name, tile_type)
print(this_dat)
print(tiles[tile_type])
assert 0
if tiles[tile_type] != this_dat:
print(tile_name, tile_type)
print(this_dat)
print(tiles[tile_type])
assert 0
j = {'tiles': tiles}
json.dump(j, open(fnout, 'w'), sort_keys=True, indent=4, separators=(',', ': '))
json.dump(
j, open(fnout, 'w'), sort_keys=True, indent=4, separators=(',', ': '))
def main():
import argparse
parser = argparse.ArgumentParser(
description=
'Solve timing solution'
)
parser.add_argument('--speed-json', default='../../speed/build/speed.json',
parser = argparse.ArgumentParser(description='Solve timing solution')
parser.add_argument(
'--speed-json',
default='../../speed/build/speed.json',
help='Provides speed index to name translation')
parser.add_argument('fnin', default=None, help='input tcl output .txt')
parser.add_argument('fnout', default=None, help='output .json')
@ -70,5 +75,6 @@ def main():
run(args.fnin, args.fnout, speed_json_fn=args.speed_json, verbose=False)
if __name__ == '__main__':
main()

View File

@ -14,9 +14,11 @@ 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
@ -28,22 +30,26 @@ def parse_pip(s):
#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()
@ -66,25 +72,27 @@ def load_timing3(f, name='file'):
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,
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
@ -95,6 +103,7 @@ def load_speed_json(f):
speed_i2s[i] = k
return j, speed_i2s
# Verify the nodes and wires really do line up
def vals2Adi_check(vals, names):
print('Checking')
@ -106,12 +115,12 @@ def vals2Adi_check(vals, names):
print('Done')
assert 0
def vals2Adi(vals, speed_i2s,
name_tr={}, name_drop=[],
verbose=False):
def vals2Adi(vals, speed_i2s, name_tr={}, name_drop=[], verbose=False):
def pip2speed(pip):
_site, _name, speed_index = 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]
@ -119,7 +128,8 @@ def vals2Adi(vals, speed_i2s,
# Want this ordered
names = OrderedDict()
print('Creating matrix w/ tr: %d, drop: %d' % (len(name_tr), len(name_drop)))
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)
@ -171,6 +181,7 @@ def vals2Adi(vals, speed_i2s,
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
@ -192,12 +203,14 @@ def vals2Adi(vals, speed_i2s,
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')
@ -211,12 +224,15 @@ def load_Ads(speed_json_f, f_ins):
Ads = vals2Ads(vals, speed_i2s)
def mkb(val):
return (val['fast_max'], val['fast_min'], val['slow_max'], val['slow_min'])
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)
@ -229,6 +245,7 @@ def run(speed_json_f, fout, f_ins, verbose=0, corner=None):
items.append('%u %s' % (v, k))
fout.write(','.join(items) + '\n')
def main():
import argparse
@ -238,14 +255,14 @@ def main():
)
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',
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')
parser.add_argument('fns_in', nargs='*', help='timing3.txt input files')
args = parser.parse_args()
bench = Benchmark()
@ -265,8 +282,12 @@ def main():
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)
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()

View File

@ -4,6 +4,7 @@ Verifies that node timing info is unique
import re
def gen_wires(fin):
for l in fin:
lj = {}
@ -12,7 +13,8 @@ def gen_wires(fin):
name, value = kvs.split(':')
lj[name] = value
tile_type, xy, wname = re.match(r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
tile_type, xy, wname = re.match(
r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
lj['tile_type'] = tile_type
lj['xy'] = xy
lj['wname'] = wname
@ -21,10 +23,12 @@ def gen_wires(fin):
yield lj
def run(node_fin, verbose=0):
refnodes = {}
nodei = 0
for nodei, anode in enumerate(gen_wires(node_fin)):
def getk(anode):
return anode['wname']
#return (anode['tile_type'], anode['wname'])
@ -39,32 +43,34 @@ def run(node_fin, verbose=0):
refnodes[getk(anode)] = anode
continue
k_invariant = (
'COST_CODE',
'IS_INPUT_PIN',
'IS_OUTPUT_PIN',
'IS_PART_OF_BUS',
'NUM_INTERSECTS',
'NUM_TILE_PORTS',
'SPEED_INDEX',
'TILE_PATTERN_OFFSET',
)
'COST_CODE',
'IS_INPUT_PIN',
'IS_OUTPUT_PIN',
'IS_PART_OF_BUS',
'NUM_INTERSECTS',
'NUM_TILE_PORTS',
'SPEED_INDEX',
'TILE_PATTERN_OFFSET',
)
k_varies = (
'ID_IN_TILE_TYPE',
'IS_CONNECTED',
'NUM_DOWNHILL_PIPS',
'NUM_PIPS',
'NUM_UPHILL_PIPS',
'TILE_NAME',
)
'ID_IN_TILE_TYPE',
'IS_CONNECTED',
'NUM_DOWNHILL_PIPS',
'NUM_PIPS',
'NUM_UPHILL_PIPS',
'TILE_NAME',
)
# Verify equivilence
for k in k_invariant:
if k in refnode and k in anode:
def fail():
print 'Mismatch on %s' % k
print refnode[k], anode[k]
print refnode['l']
print anode['l']
#assert 0
if refnode[k] != anode[k]:
print
fail()
@ -74,19 +80,14 @@ def run(node_fin, verbose=0):
elif k not in k_varies:
assert 0
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description=
'Timing fuzzer'
)
parser = argparse.ArgumentParser(description='Timing fuzzer')
parser.add_argument('--verbose', type=int, help='')
parser.add_argument(
'node_fn_in',
default='/dev/stdin',
nargs='?',
help='Input file')
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
args = parser.parse_args()
run(open(args.node_fn_in, 'r'), verbose=args.verbose)