diff --git a/fuzzers/007-timing/Makefile b/fuzzers/007-timing/Makefile index 40e42a35..5734d78a 100644 --- a/fuzzers/007-timing/Makefile +++ b/fuzzers/007-timing/Makefile @@ -3,13 +3,13 @@ PRJ?=picorv32 PRJN?=8 -all: build/tilea.json +all: build/timgrid-v.json clean: rm -rf build cd speed && $(MAKE) clean cd timgrid && $(MAKE) clean - cd projects && $(MAKE) clean + cd projects/$(PRJ) && $(MAKE) clean speed/build/speed.json: cd speed && $(MAKE) @@ -17,10 +17,10 @@ speed/build/speed.json: timgrid/build/timgrid.json: cd timgrid && $(MAKE) -build/tilea.json: projects/$(PRJ)/build/tilea.json +build/timgrid-v.json: projects/$(PRJ)/build/timgrid-v.json mkdir -p build - cp projects/$(PRJ)/build/tilea.json build/tilea.json + cp projects/$(PRJ)/build/timgrid-v.json build/timgrid-v.json -projects/$(PRJ)/build/tilea.json: speed/build/speed.json timgrid/build/timgrid.json +projects/$(PRJ)/build/timgrid-v.json: speed/build/speed.json timgrid/build/timgrid.json cd projects/$(PRJ) && $(MAKE) N=$(PRJN) diff --git a/fuzzers/007-timing/README.md b/fuzzers/007-timing/README.md index a7c79eb2..db5a98bc 100644 --- a/fuzzers/007-timing/README.md +++ b/fuzzers/007-timing/README.md @@ -13,10 +13,15 @@ Currently this document focuses exclusively on fabric timing delays. ## Quick start ``` -make +make -j$(nproc) ``` -This will take a relatively long time (say 45 min) and generate build/tilea.json +This will take a relatively long time (say 45 min) and generate build/timgrid-v.json. +You can do a quicker test run (say 3 min) using: + +``` +make PRJ=oneblinkw PRJN=1 -j$(nproc) +``` ## Vivado background diff --git a/fuzzers/007-timing/checksub.py b/fuzzers/007-timing/checksub.py index 5387eaf1..86823805 100644 --- a/fuzzers/007-timing/checksub.py +++ b/fuzzers/007-timing/checksub.py @@ -58,7 +58,7 @@ def pds(Ads, s): def run(fns_in, sub_json=None, verbose=False): - assert len(fn_ins) > 0 + assert len(fns_in) > 0 # arbitrary corner...data is thrown away Ads, b = loadc_Ads_b(fns_in, "slow_max", ico=True) diff --git a/fuzzers/007-timing/csv_flat2group.py b/fuzzers/007-timing/csv_flat2group.py index a1defef2..a0f76f1d 100644 --- a/fuzzers/007-timing/csv_flat2group.py +++ b/fuzzers/007-timing/csv_flat2group.py @@ -35,7 +35,7 @@ def main(): import argparse parser = argparse.ArgumentParser( - description='Substitute .csv to group correlated symbols') + description='Substitute .csv to group correlated variables') parser.add_argument('--verbose', action='store_true', help='') parser.add_argument('--strict', action='store_true', help='') diff --git a/fuzzers/007-timing/csv_group2flat.py b/fuzzers/007-timing/csv_group2flat.py index bba0cc5b..403b2991 100644 --- a/fuzzers/007-timing/csv_group2flat.py +++ b/fuzzers/007-timing/csv_group2flat.py @@ -3,8 +3,8 @@ from timfuz import Benchmark, loadc_Ads_bs, load_sub, Ads2bounds, corners2csv, corner_s2i -def gen_flat(fnin, sub_json, corner=None): - Ads, bs = loadc_Ads_bs([fnin], ico=True) +def gen_flat(fns_in, sub_json, corner=None): + Ads, bs = loadc_Ads_bs(fns_in, ico=True) bounds = Ads2bounds(Ads, bs) zeros = set() nonzeros = set() @@ -49,20 +49,19 @@ def run(fns_in, fnout, sub_json, corner=None, sort=False, verbose=False): with open(fnout, 'w') as fout: fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n') - for fnin in fns_in: - #for name, corners in sortf(gen_flat(fnin, sub_json)): - for name, corners in gen_flat(fnin, sub_json, corner=corner): - row_ico = 1 - items = [str(row_ico), corners2csv(corners)] - items.append('%u %s' % (1, name)) - fout.write(','.join(items) + '\n') + #for name, corners in sortf(gen_flat(fnin, sub_json)): + for name, corners in gen_flat(fns_in, sub_json, corner=corner): + row_ico = 1 + items = [str(row_ico), corners2csv(corners)] + items.append('%u %s' % (1, name)) + fout.write(','.join(items) + '\n') def main(): import argparse parser = argparse.ArgumentParser( - description='Substitute .csv to ungroup correlated symbols') + description='Substitute .csv to ungroup correlated variables') parser.add_argument('--verbose', action='store_true', help='') #parser.add_argument('--sort', action='store_true', help='') @@ -75,7 +74,7 @@ def main(): parser.add_argument('--out', default=None, help='output timing delay .csv') parser.add_argument( 'fns_in', - default=None, + nargs='+', help='input timing delay .csv (NOTE: must be single column)') args = parser.parse_args() # Store options in dict to ease passing through functions diff --git a/fuzzers/007-timing/projects/corner.mk b/fuzzers/007-timing/projects/corner.mk index 3ed37050..f332d727 100644 --- a/fuzzers/007-timing/projects/corner.mk +++ b/fuzzers/007-timing/projects/corner.mk @@ -5,7 +5,7 @@ CORNER=slow_max ALLOW_ZERO_EQN?=N BADPRJ_OK?=N -all: build/$(CORNER)/tilea.json +all: build/$(CORNER)/timgrid-s.json run: $(MAKE) clean @@ -37,11 +37,11 @@ build/$(CORNER)/linprog.csv: build/$(CORNER)/leastsq.csv build/grouped.csv build/$(CORNER)/flat.csv: build/$(CORNER)/linprog.csv # Take separated variables and back-annotate them to the original timing variables - python3 $(TIMFUZ_DIR)/csv_group2flat.py --sub-json build/sub.json --corner $(CORNER) --sort --out build/$(CORNER)/flat.csv.tmp build/$(CORNER)/linprog.csv + python3 $(TIMFUZ_DIR)/csv_group2flat.py --sub-json build/sub.json --corner $(CORNER) --out build/$(CORNER)/flat.csv.tmp build/$(CORNER)/linprog.csv mv build/$(CORNER)/flat.csv.tmp build/$(CORNER)/flat.csv -build/$(CORNER)/tilea.json: build/$(CORNER)/flat.csv +build/$(CORNER)/timgrid-s.json: build/$(CORNER)/flat.csv # Final processing # Insert timing delays into actual tile layouts - python3 $(TIMFUZ_DIR)/tile_annotate.py --tile-json $(TIMFUZ_DIR)/timgrid/build/timgrid.json build/$(CORNER)/flat.csv build/$(CORNER)/tilea.json + python3 $(TIMFUZ_DIR)/tile_annotate.py --timgrid-s $(TIMFUZ_DIR)/timgrid/build/timgrid-s.json --out build/$(CORNER)/timgrid-vc.json build/$(CORNER)/flat.csv diff --git a/fuzzers/007-timing/projects/project.mk b/fuzzers/007-timing/projects/project.mk index 7e2b4f1f..dc29c28e 100644 --- a/fuzzers/007-timing/projects/project.mk +++ b/fuzzers/007-timing/projects/project.mk @@ -10,23 +10,19 @@ RREF_CORNER=slow_max ALLOW_ZERO_EQN?=N BADPRJ_OK?=N -TILEA_JSONS=build/fast_max/tilea.json build/fast_min/tilea.json build/slow_max/tilea.json build/slow_min/tilea.json +TIMGRID_VCS=build/fast_max/timgrid-vc.json build/fast_min/timgrid-vc.json build/slow_max/timgrid-vc.json build/slow_min/timgrid-vc.json -all: build/tilea.json +all: build/timgrid-v.json # make build/checksub first -build/fast_max/tilea.json: build/checksub +build/fast_max/timgrid-vc.json: build/checksub $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=fast_max -build/fast_min/tilea.json: build/checksub +build/fast_min/timgrid-vc.json: build/checksub $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=fast_min -build/slow_max/tilea.json: build/checksub +build/slow_max/timgrid-vc.json: build/checksub $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=slow_max -build/slow_min/tilea.json: build/checksub +build/slow_min/timgrid-vc.json: build/checksub $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=slow_min -fast_max: build/fast_max/tilea.json -fast_min: build/fast_min/tilea.json -slow_max: build/slow_max/tilea.json -slow_min: build/slow_min/tilea.json $(SPECIMENS_OK): bash generate.sh $(subst /OK,,$@) || (if [ "$(BADPRJ_OK)" != 'Y' ] ; then exit 1; fi; exit 0) @@ -76,6 +72,6 @@ build/checksub: build/grouped.csv build/sub.json python3 $(TIMFUZ_DIR)/checksub.py --sub-json build/sub.json build/grouped.csv touch build/checksub -build/tilea.json: $(TILEA_JSONS) - python3 $(TIMFUZ_DIR)/tile_combine.py --out build/tilea.json $(TILEA_JSONS) +build/timgrid-v.json: $(TIMGRID_VCS) + python3 $(TIMFUZ_DIR)/timgrid_vc2v.py --out build/timgrid-v.json $(TIMGRID_VCS) diff --git a/fuzzers/007-timing/rref.py b/fuzzers/007-timing/rref.py index e160768c..fedcfef5 100644 --- a/fuzzers/007-timing/rref.py +++ b/fuzzers/007-timing/rref.py @@ -29,7 +29,7 @@ class State(object): # known zero delay elements self.drop_names = set(drop_names) # active names in rows - # includes sub symbols, excludes symbols that have been substituted out + # includes sub variables, excludes variables that have been substituted out self.base_names = set(self.names) self.names = set(self.base_names) # List of variable substitutions @@ -173,10 +173,10 @@ def state_rref(state, verbose=False): rowdsf = row_sym2dsf(rowsym, names) state.subs[group_name] = rowdsf - # Add the new symbol + # Add the new variables state.names.add(group_name) - # Remove substituted symbols - # Note: symbols may appear multiple times + # Remove substituted variables + # Note: variables may appear multiple times state.names.difference_update(set(rowdsf.keys())) pivot_name = names[row_pivot] state.pivots[group_name] = pivot_name diff --git a/fuzzers/007-timing/solve_leastsq.py b/fuzzers/007-timing/solve_leastsq.py index d77acd2d..db22b168 100644 --- a/fuzzers/007-timing/solve_leastsq.py +++ b/fuzzers/007-timing/solve_leastsq.py @@ -160,11 +160,15 @@ def main(): parser.add_argument('--corner', required=True, default="slow_max", help='') parser.add_argument( '--out', default=None, help='output timing delay .json') - parser.add_argument('fns_in', nargs='+', '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() + fns_in = args.fns_in + if not fns_in: + fns_in = glob.glob('specimen_*/timing3.csv') + sub_json = None if args.sub_json: sub_json = load_sub(args.sub_json) @@ -173,7 +177,7 @@ def main(): timfuz_solve.run( run_corner=run_corner, sub_json=sub_json, - fns_in=args.fns_in, + fns_in=fns_in, corner=args.corner, massage=args.massage, outfn=args.out, diff --git a/fuzzers/007-timing/solve_linprog.py b/fuzzers/007-timing/solve_linprog.py index 6a27ac4c..d22e14d1 100644 --- a/fuzzers/007-timing/solve_linprog.py +++ b/fuzzers/007-timing/solve_linprog.py @@ -216,7 +216,7 @@ def main(): run_corner=run_corner, sub_json=sub_json, sub_csv=args.sub_csv, - fn_in=fn_in, + fns_in=fns_in, corner=args.corner, massage=args.massage, outfn=args.out, diff --git a/fuzzers/007-timing/tile_annotate.py b/fuzzers/007-timing/tile_annotate.py index 7e87f2fa..ffc07a21 100644 --- a/fuzzers/007-timing/tile_annotate.py +++ b/fuzzers/007-timing/tile_annotate.py @@ -9,50 +9,24 @@ import time import json -def run(fnin, fnout, tile_json_fn, verbose=False): +def run(fns_in, fnout, tile_json_fn, verbose=False): # modified in place tilej = json.load(open(tile_json_fn, 'r')) - Ads, bs = loadc_Ads_bs([fnin], ico=True) - bounds = Ads2bounds(Ads, bs) + for fnin in fns_in: + Ads, bs = loadc_Ads_bs([fnin], ico=True) + bounds = Ads2bounds(Ads, bs) - pipn_net = 0 - pipn_solved = [0, 0, 0, 0] - pipn_covered = [0, 0, 0, 0] - wiren_net = 0 - wiren_solved = [0, 0, 0, 0] - wiren_covered = [0, 0, 0, 0] + for tile in tilej['tiles'].values(): + pips = tile['pips'] + for k, v in pips.items(): + pips[k] = bounds.get('PIP_' + v, [None, None, None, None]) - for tile in tilej['tiles'].values(): - pips = tile['pips'] - for k, v in pips.items(): - pips[k] = bounds.get('PIP_' + v, [None, None, None, None]) - pipn_net += 1 - for i in range(4): - if pips[k][i]: - pipn_solved[i] += 1 - if pips[k][i] is not None: - pipn_covered[i] += 1 + wires = tile['wires'] + for k, v in wires.items(): + wires[k] = bounds.get('WIRE_' + v, [None, None, None, None]) - wires = tile['wires'] - for k, v in wires.items(): - wires[k] = bounds.get('WIRE_' + v, [None, None, None, None]) - wiren_net += 1 - for i in range(4): - if wires[k][i]: - wiren_solved[i] += 1 - if wires[k][i] is not None: - wiren_covered[i] += 1 - - for corner, corneri in timfuz.corner_s2i.items(): - print('Corner %s' % corner) - print( - ' Pips: %u / %u solved, %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)) + timfuz.tilej_stats(tilej) json.dump( tilej, @@ -68,13 +42,19 @@ def main(): parser = argparse.ArgumentParser( description= 'Substitute timgrid timing model names for real timing values') - parser.add_argument('--tile-json', default='tiles.json', help='') parser.add_argument( - 'fnin', default=None, help='Input flattened timing csv (flat.json)') - parser.add_argument('fnout', default=None, help='output tile .json') + '--timgrid-s', + default='../../timgrid/build/timgrid-s.json', + help='tilegrid timing delay symbolic input (timgrid-s.json)') + parser.add_argument( + '--out', + default='build/timgrid-vc.json', + help='tilegrid timing delay values at corner (timgrid-vc.json)') + parser.add_argument( + 'fn_ins', nargs='+', help='Input flattened timing csv (flat.json)') args = parser.parse_args() - run(args.fnin, args.fnout, args.tile_json, verbose=False) + run(args.fn_ins, args.out, args.timgrid_s, verbose=False) if __name__ == '__main__': diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index 45569ed2..581de3a0 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -665,20 +665,20 @@ def load_sub(fn): return j -def row_sub_syms(row, sub_json, strict=False, verbose=False): +def row_sub_vars(row, sub_json, strict=False, verbose=False): if 0 and verbose: print("") print(row.items()) - delsyms = 0 + delvars = 0 for k in sub_json['drop_names']: try: del row[k] - delsyms += 1 + delvars += 1 except KeyError: pass if verbose: - print("Deleted %u symbols" % delsyms) + print("Deleted %u variables" % delvars) if verbose: print('Checking pivots') @@ -750,7 +750,7 @@ def run_sub_json(Ads, sub_json, strict=False, verbose=False): print('Row %u w/ %u elements' % (rowi, len(row))) row_orig = dict(row) - row_sub_syms(row, sub_json, strict=strict, verbose=verbose) + row_sub_vars(row, sub_json, strict=strict, verbose=verbose) nrows += 1 if row_orig != row: nsubs += 1 @@ -817,3 +817,33 @@ def corners2csv(bs): assert len(bs) == 4 corners = ["None" if b is None else str(b) for b in bs] return ' '.join(corners) + + +def tilej_stats(tilej): + stats = {} + for etype in ('pips', 'wires'): + tm = stats.setdefault(etype, {}) + tm['net'] = 0 + tm['solved'] = [0, 0, 0, 0] + tm['covered'] = [0, 0, 0, 0] + + for tile in tilej['tiles'].values(): + for etype in ('pips', 'wires'): + pips = tile[etype] + for k, v in pips.items(): + stats[etype]['net'] += 1 + for i in range(4): + if pips[k][i]: + stats[etype]['solved'][i] += 1 + if pips[k][i] is not None: + stats[etype]['covered'][i] += 1 + + for corner, corneri in corner_s2i.items(): + print('Corner %s' % corner) + for etype in ('pips', 'wires'): + net = stats[etype]['net'] + solved = stats[etype]['solved'][corneri] + covered = stats[etype]['covered'][corneri] + print( + ' %s: %u / %u solved, %u / %u covered' % + (etype, solved, net, covered, net)) diff --git a/fuzzers/007-timing/timgrid/Makefile b/fuzzers/007-timing/timgrid/Makefile index af0a72da..30458303 100644 --- a/fuzzers/007-timing/timgrid/Makefile +++ b/fuzzers/007-timing/timgrid/Makefile @@ -5,7 +5,7 @@ build/timgrid.txt: generate.tcl cd build && vivado -mode batch -source ../generate.tcl build/timgrid.json: build/timgrid.txt - cd build && python3 ../tile_txt2json.py --speed-json ../../speed/build/speed.json timgrid.txt timgrid.json + cd build && python3 ../tile_txt2json.py --speed-json ../../speed/build/speed.json timgrid.txt timgrid-s.json clean: rm -rf build diff --git a/fuzzers/007-timing/tile_combine.py b/fuzzers/007-timing/timgrid_vc2v.py similarity index 92% rename from fuzzers/007-timing/tile_combine.py rename to fuzzers/007-timing/timgrid_vc2v.py index 940d628f..8c188d81 100644 --- a/fuzzers/007-timing/tile_combine.py +++ b/fuzzers/007-timing/timgrid_vc2v.py @@ -5,6 +5,7 @@ import os import time import json from collections import OrderedDict +import timfuz corner_s2i = OrderedDict( [ @@ -77,6 +78,7 @@ def check_corner_minmax(tilej, verbose=False): print('Checking for min/max consistency') checks = 0 bad = 0 + for tilev in tilej['tiles'].values(): def process_type(etype): @@ -102,7 +104,10 @@ def check_corner_minmax(tilej, verbose=False): process_type('pips') process_type('wires') - print('minmax: %u / %u bad' % (bad, checks)) + + print('') + print('minmax: %u / %u pairs bad pairs adjusted' % (bad, checks)) + timfuz.tilej_stats(tilej) def check_corners_minmax(tilej, verbose=False): @@ -127,8 +132,9 @@ def main(): parser = argparse.ArgumentParser( 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') + parser.add_argument( + '--out', required=True, help='Combined timgrid-v.json files') + parser.add_argument('fnins', nargs='+', help='Input timgrid-vc.json files') args = parser.parse_args() run(args.fnins, args.out, verbose=False) diff --git a/fuzzers/007-timing/timing_txt2csv.py b/fuzzers/007-timing/timing_txt2csv.py index 3f0e62af..0ba2b36d 100644 --- a/fuzzers/007-timing/timing_txt2csv.py +++ b/fuzzers/007-timing/timing_txt2csv.py @@ -1,11 +1,9 @@ #!/usr/bin/env python3 from timfuz import Benchmark, A_di2ds -import numpy as np import glob import math import json -import sympy import sys from collections import OrderedDict