diff --git a/fuzzers/007-timing/benchmark.py b/fuzzers/007-timing/benchmark.py index 6da3e132..39a6e797 100644 --- a/fuzzers/007-timing/benchmark.py +++ b/fuzzers/007-timing/benchmark.py @@ -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) - diff --git a/fuzzers/007-timing/checksub.py b/fuzzers/007-timing/checksub.py index 00559735..7a307e14 100644 --- a/fuzzers/007-timing/checksub.py +++ b/fuzzers/007-timing/checksub.py @@ -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() diff --git a/fuzzers/007-timing/corner_csv.py b/fuzzers/007-timing/corner_csv.py index d64c64ab..429e1e55 100644 --- a/fuzzers/007-timing/corner_csv.py +++ b/fuzzers/007-timing/corner_csv.py @@ -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() diff --git a/fuzzers/007-timing/csv_flat2group.py b/fuzzers/007-timing/csv_flat2group.py index f1398b87..4cde0e61 100644 --- a/fuzzers/007-timing/csv_flat2group.py +++ b/fuzzers/007-timing/csv_flat2group.py @@ -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() diff --git a/fuzzers/007-timing/csv_group2flat.py b/fuzzers/007-timing/csv_group2flat.py index 2449b916..438be40e 100644 --- a/fuzzers/007-timing/csv_group2flat.py +++ b/fuzzers/007-timing/csv_group2flat.py @@ -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() diff --git a/fuzzers/007-timing/node_unique.py b/fuzzers/007-timing/node_unique.py index e5793e64..83b054b9 100644 --- a/fuzzers/007-timing/node_unique.py +++ b/fuzzers/007-timing/node_unique.py @@ -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) diff --git a/fuzzers/007-timing/perf_test.py b/fuzzers/007-timing/perf_test.py index df1998a8..cb400809 100644 --- a/fuzzers/007-timing/perf_test.py +++ b/fuzzers/007-timing/perf_test.py @@ -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() diff --git a/fuzzers/007-timing/pip_unique.py b/fuzzers/007-timing/pip_unique.py index 39b45d44..b38c9f4e 100644 --- a/fuzzers/007-timing/pip_unique.py +++ b/fuzzers/007-timing/pip_unique.py @@ -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) diff --git a/fuzzers/007-timing/projects/placelut/generate.py b/fuzzers/007-timing/projects/placelut/generate.py index a56d4c55..68687f91 100644 --- a/fuzzers/007-timing/projects/placelut/generate.py +++ b/fuzzers/007-timing/projects/placelut/generate.py @@ -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) - diff --git a/fuzzers/007-timing/projects/placelut_fb/generate.py b/fuzzers/007-timing/projects/placelut_fb/generate.py index 7e973da0..8f22dd37 100644 --- a/fuzzers/007-timing/projects/placelut_fb/generate.py +++ b/fuzzers/007-timing/projects/placelut_fb/generate.py @@ -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) - diff --git a/fuzzers/007-timing/projects/placelut_ff_fb/generate.py b/fuzzers/007-timing/projects/placelut_ff_fb/generate.py index 3f8b2784..410e995f 100644 --- a/fuzzers/007-timing/projects/placelut_ff_fb/generate.py +++ b/fuzzers/007-timing/projects/placelut_ff_fb/generate.py @@ -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) - diff --git a/fuzzers/007-timing/rref.py b/fuzzers/007-timing/rref.py index bad37401..97b4efa0 100644 --- a/fuzzers/007-timing/rref.py +++ b/fuzzers/007-timing/rref.py @@ -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() diff --git a/fuzzers/007-timing/solve_leastsq.py b/fuzzers/007-timing/solve_leastsq.py index 5bdd4fad..1a1b55e6 100644 --- a/fuzzers/007-timing/solve_leastsq.py +++ b/fuzzers/007-timing/solve_leastsq.py @@ -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() diff --git a/fuzzers/007-timing/solve_linprog.py b/fuzzers/007-timing/solve_linprog.py index ee73a702..29e4d826 100644 --- a/fuzzers/007-timing/solve_linprog.py +++ b/fuzzers/007-timing/solve_linprog.py @@ -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() diff --git a/fuzzers/007-timing/speed/speed_json.py b/fuzzers/007-timing/speed/speed_json.py index 07dfd20b..1f0a3910 100644 --- a/fuzzers/007-timing/speed/speed_json.py +++ b/fuzzers/007-timing/speed/speed_json.py @@ -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) diff --git a/fuzzers/007-timing/sub2csv.py b/fuzzers/007-timing/sub2csv.py index 9ea9bbbc..8567d5e3 100644 --- a/fuzzers/007-timing/sub2csv.py +++ b/fuzzers/007-timing/sub2csv.py @@ -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() diff --git a/fuzzers/007-timing/tile_annotate.py b/fuzzers/007-timing/tile_annotate.py index 5b897b4d..58b89235 100644 --- a/fuzzers/007-timing/tile_annotate.py +++ b/fuzzers/007-timing/tile_annotate.py @@ -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() diff --git a/fuzzers/007-timing/tile_combine.py b/fuzzers/007-timing/tile_combine.py index 35ea2291..940d628f 100644 --- a/fuzzers/007-timing/tile_combine.py +++ b/fuzzers/007-timing/tile_combine.py @@ -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() diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index 6287cce2..45569ed2 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -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] diff --git a/fuzzers/007-timing/timfuz_massage.py b/fuzzers/007-timing/timfuz_massage.py index 1f52af5b..4be04737 100644 --- a/fuzzers/007-timing/timfuz_massage.py +++ b/fuzzers/007-timing/timfuz_massage.py @@ -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 diff --git a/fuzzers/007-timing/timfuz_solve.py b/fuzzers/007-timing/timfuz_solve.py index 52a4c43e..4f6446ef 100644 --- a/fuzzers/007-timing/timfuz_solve.py +++ b/fuzzers/007-timing/timfuz_solve.py @@ -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) diff --git a/fuzzers/007-timing/timgrid/tile_txt2json.py b/fuzzers/007-timing/timgrid/tile_txt2json.py index b6e410ff..4342805d 100644 --- a/fuzzers/007-timing/timgrid/tile_txt2json.py +++ b/fuzzers/007-timing/timgrid/tile_txt2json.py @@ -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() diff --git a/fuzzers/007-timing/timing_txt2csv.py b/fuzzers/007-timing/timing_txt2csv.py index 51f04f42..1a4891eb 100644 --- a/fuzzers/007-timing/timing_txt2csv.py +++ b/fuzzers/007-timing/timing_txt2csv.py @@ -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() diff --git a/fuzzers/007-timing/wire_unique.py b/fuzzers/007-timing/wire_unique.py index 391c2e0c..efae6e26 100644 --- a/fuzzers/007-timing/wire_unique.py +++ b/fuzzers/007-timing/wire_unique.py @@ -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)