diff --git a/fuzzers/007-timing/checksub.py b/fuzzers/007-timing/checksub.py index 559b6c61..34ab981a 100644 --- a/fuzzers/007-timing/checksub.py +++ b/fuzzers/007-timing/checksub.py @@ -60,7 +60,7 @@ def pds(Ads, s): def run(fns_in, sub_json=None, verbose=False): assert len(fns_in) > 0 # arbitrary corner...data is thrown away - Ads, b = loadc_Ads_b(fns_in, "slow_max", ico=True) + Ads, b = loadc_Ads_b(fns_in, "slow_max") if sub_json: print('Subbing JSON %u rows' % len(Ads)) diff --git a/fuzzers/007-timing/csv_flat2group.py b/fuzzers/007-timing/csv_flat2group.py index aa7d6a1d..d382e6f1 100644 --- a/fuzzers/007-timing/csv_flat2group.py +++ b/fuzzers/007-timing/csv_flat2group.py @@ -5,7 +5,7 @@ from timfuz import Benchmark, loadc_Ads_bs, index_names, load_sub, run_sub_json, def gen_group(fnin, sub_json, strict=False, verbose=False): print('Loading data') - Ads, bs = loadc_Ads_bs([fnin], ico=True) + Ads, bs = loadc_Ads_bs([fnin]) print('Sub: %u rows' % len(Ads)) iold = instances(Ads) diff --git a/fuzzers/007-timing/csv_group2flat.py b/fuzzers/007-timing/csv_group2flat.py index c6adf0c6..6f1e31b1 100644 --- a/fuzzers/007-timing/csv_group2flat.py +++ b/fuzzers/007-timing/csv_group2flat.py @@ -4,7 +4,7 @@ from timfuz import Benchmark, loadc_Ads_bs, load_sub, Ads2bounds, corners2csv, c def gen_flat(fns_in, sub_json, corner=None): - Ads, bs = loadc_Ads_bs(fns_in, ico=True) + Ads, bs = loadc_Ads_bs(fns_in) bounds = Ads2bounds(Ads, bs) # Elements with zero delay assigned due to sub group group_zeros = set() diff --git a/fuzzers/007-timing/projects/corner.mk b/fuzzers/007-timing/projects/corner.mk index 618719ec..bb3b76f6 100644 --- a/fuzzers/007-timing/projects/corner.mk +++ b/fuzzers/007-timing/projects/corner.mk @@ -4,7 +4,8 @@ TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing CORNER=slow_max ALLOW_ZERO_EQN?=N BADPRJ_OK?=N -BUILD_DIR?=build +BUILD_DIR?=build/MUST_SET +CSV_BASENAME=timing4i.csv all: $(BUILD_DIR)/$(CORNER)/timgrid-vc.json $(BUILD_DIR)/$(CORNER)/qor.txt @@ -19,7 +20,7 @@ clean: .PHONY: all run clean $(BUILD_DIR)/$(CORNER): - mkdir $(BUILD_DIR)/$(CORNER) + mkdir -p $(BUILD_DIR)/$(CORNER) # parent should have built this $(BUILD_DIR)/checksub: @@ -46,6 +47,11 @@ $(BUILD_DIR)/$(CORNER)/timgrid-vc.json: $(BUILD_DIR)/$(CORNER)/flat.csv python3 $(TIMFUZ_DIR)/tile_annotate.py --timgrid-s $(TIMFUZ_DIR)/timgrid/build/timgrid-s.json --out $(BUILD_DIR)/$(CORNER)/timgrid-vc.json $(BUILD_DIR)/$(CORNER)/flat.csv $(BUILD_DIR)/$(CORNER)/qor.txt: $(BUILD_DIR)/$(CORNER)/flat.csv +ifeq ($(SOLVING),i) python3 $(TIMFUZ_DIR)/solve_qor.py --corner $(CORNER) --bounds-csv $(BUILD_DIR)/$(CORNER)/flat.csv specimen_*/timing4i.csv >$(BUILD_DIR)/$(CORNER)/qor.txt.tmp mv $(BUILD_DIR)/$(CORNER)/qor.txt.tmp $(BUILD_DIR)/$(CORNER)/qor.txt +else + # FIXME + touch $(BUILD_DIR)/$(CORNER)/qor.txt +endif diff --git a/fuzzers/007-timing/projects/generate.sh b/fuzzers/007-timing/projects/generate.sh index 220f40c0..16f99e3f 100644 --- a/fuzzers/007-timing/projects/generate.sh +++ b/fuzzers/007-timing/projects/generate.sh @@ -6,5 +6,8 @@ TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing timing_txt2csv () { python3 $TIMFUZ_DIR/timing_txt2icsv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing4i.csv.tmp timing4.txt mv timing4i.csv.tmp timing4i.csv + + python3 $TIMFUZ_DIR/timing_txt2scsv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing4s.csv.tmp timing4.txt + mv timing4s.csv.tmp timing4s.csv } diff --git a/fuzzers/007-timing/projects/is.mk b/fuzzers/007-timing/projects/is.mk new file mode 100644 index 00000000..5c419487 --- /dev/null +++ b/fuzzers/007-timing/projects/is.mk @@ -0,0 +1,74 @@ +# Interconnect and site (IS) high level aggregation +# Creates corner data and aggregates them together + +TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing +SOLVING=i +CSV_BASENAME=timing4$(SOLVING).csv +BUILD_DIR?=build/MUST_SET +SPECIMENS := +CSVS := $(addsuffix /$(CSV_BASENAME),$(SPECIMENS)) +RREF_CORNER=slow_max + +# Set ZERO elements to zero delay (as is expected they should be) +RMZERO?=N + +RREF_ARGS= +ifeq ($(RMZERO),Y) +RREF_ARGS+=--rm-zero +endif + + +# FIXME: clean this up by generating targets from CORNERS + +# fast_max => build/i/fast_max/timgrid-vc.json +TIMGRID_VCS=$(BUILD_DIR)/fast_max/timgrid-vc.json $(BUILD_DIR)/fast_min/timgrid-vc.json $(BUILD_DIR)/slow_max/timgrid-vc.json $(BUILD_DIR)/slow_min/timgrid-vc.json +#TIMGRID_VCS=$(addsuffix /timgrid-vc.json,$(addprefix $(BUILD_DIR_I)/,$(CORNERS))) +# make $(BUILD_DIR)/checksub first +$(BUILD_DIR)/fast_max/timgrid-vc.json: $(BUILD_DIR)/checksub + $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=fast_max +$(BUILD_DIR)/fast_min/timgrid-vc.json: $(BUILD_DIR)/checksub + $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=fast_min +$(BUILD_DIR)/slow_max/timgrid-vc.json: $(BUILD_DIR)/checksub + $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=slow_max +$(BUILD_DIR)/slow_min/timgrid-vc.json: $(BUILD_DIR)/checksub + $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=slow_min + + +# Normally require all projects to complete +# If BADPRJ_OK is allowed, only take projects that were successful +# FIXME: couldn't get call to work +exist_csvs = \ + for f in $(CSVS); do \ + if [ "$(BADPRJ_OK)" != 'Y' -o -f $$f ] ; then \ + echo $$f; \ + fi; \ + done + +all: $(BUILD_DIR)/timgrid-v.json + +# rref should be the same regardless of corner +$(BUILD_DIR)/sub.json: $(SPECIMENS_OK) + mkdir -p $(BUILD_DIR) + # Discover which variables can be separated + # This is typically the longest running operation + \ + csvs=$$(for f in $(CSVS); do if [ "$(BADPRJ_OK)" != 'Y' -o -f $$f ] ; then echo $$f; fi; done) ; \ + echo $$csvs ; \ + python3 $(TIMFUZ_DIR)/rref.py --corner $(RREF_CORNER) --simplify $(RREF_ARGS) --out $(BUILD_DIR)/sub.json.tmp $$csvs + mv $(BUILD_DIR)/sub.json.tmp $(BUILD_DIR)/sub.json + +$(BUILD_DIR)/grouped.csv: $(SPECIMENS_OK) $(BUILD_DIR)/sub.json + # Separate variables + \ + csvs=$$(for f in $(CSVS); do if [ "$(BADPRJ_OK)" != 'Y' -o -f $$f ] ; then echo $$f; fi; done) ; \ + python3 $(TIMFUZ_DIR)/csv_flat2group.py --sub-json $(BUILD_DIR)/sub.json --strict --out $(BUILD_DIR)/grouped.csv.tmp $$csvs + mv $(BUILD_DIR)/grouped.csv.tmp $(BUILD_DIR)/grouped.csv + +$(BUILD_DIR)/checksub: $(BUILD_DIR)/grouped.csv $(BUILD_DIR)/sub.json + # Verify sub.json makes a cleanly solvable solution with no non-pivot leftover + python3 $(TIMFUZ_DIR)/checksub.py --sub-json $(BUILD_DIR)/sub.json $(BUILD_DIR)/grouped.csv + touch $(BUILD_DIR)/checksub + +$(BUILD_DIR)/timgrid-v.json: $(TIMGRID_VCS) + python3 $(TIMFUZ_DIR)/timgrid_vc2v.py --out $(BUILD_DIR)/timgrid-v.json $(TIMGRID_VCS) + diff --git a/fuzzers/007-timing/projects/project.mk b/fuzzers/007-timing/projects/project.mk index 9545c888..f21f803e 100644 --- a/fuzzers/007-timing/projects/project.mk +++ b/fuzzers/007-timing/projects/project.mk @@ -4,38 +4,26 @@ N := 1 SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N))) SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS)) -CSVS := $(addsuffix /timing4i.csv,$(SPECIMENS)) TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing -RREF_CORNER=slow_max # Allow an empty system of equations? # for testing only on small projects ALLOW_ZERO_EQN?=N # Constrained projects may fail to build # Set to Y to make a best effort to suck in the ones that did build BADPRJ_OK?=N -# Set ZERO elements to zero delay (as is expected they should be) -RMZERO?=N BUILD_DIR?=build +# interconnect +BUILD_DIR_I?=$(BUILD_DIR)/i +# site +BUILD_DIR_S?=$(BUILD_DIR)/s -RREF_ARGS= -ifeq ($(RMZERO),Y) -RREF_ARGS+=--rm-zero -endif +CORNERS=fast_max fast_min slow_max slow_min -TIMGRID_VCS=$(BUILD_DIR)/fast_max/timgrid-vc.json $(BUILD_DIR)/fast_min/timgrid-vc.json $(BUILD_DIR)/slow_max/timgrid-vc.json $(BUILD_DIR)/slow_min/timgrid-vc.json +TIMGRID_V_I=$(BUILD_DIR_I)/timgrid-v.json +TIMGRID_V_S=$(BUILD_DIR_S)/timgrid-v.json all: $(BUILD_DIR)/timgrid-v.json -# make $(BUILD_DIR)/checksub first -$(BUILD_DIR)/fast_max/timgrid-vc.json: $(BUILD_DIR)/checksub - $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=fast_max -$(BUILD_DIR)/fast_min/timgrid-vc.json: $(BUILD_DIR)/checksub - $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=fast_min -$(BUILD_DIR)/slow_max/timgrid-vc.json: $(BUILD_DIR)/checksub - $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=slow_max -$(BUILD_DIR)/slow_min/timgrid-vc.json: $(BUILD_DIR)/checksub - $(MAKE) -f $(TIMFUZ_DIR)/projects/corner.mk CORNER=slow_min - $(SPECIMENS_OK): bash generate.sh $(subst /OK,,$@) || (if [ "$(BADPRJ_OK)" != 'Y' ] ; then exit 1; fi; exit 0) touch $@ @@ -52,38 +40,16 @@ clean: .PHONY: all run clean -# Normally require all projects to complete -# If BADPRJ_OK is allowed, only take projects that were successful -# FIXME: couldn't get call to work -exist_csvs = \ - for f in $(CSVS); do \ - if [ "$(BADPRJ_OK)" != 'Y' -o -f $$f ] ; then \ - echo $$f; \ - fi; \ - done +$(TIMGRID_V_I): $(SPECIMENS_OK) + $(MAKE) -f $(TIMFUZ_DIR)/projects/is.mk BUILD_DIR=$(BUILD_DIR_I) SOLVING=i SPECIMENS=$(SPECIMENS) all +i: $(TIMGRID_V_I) -# rref should be the same regardless of corner -$(BUILD_DIR)/sub.json: $(SPECIMENS_OK) - mkdir -p $(BUILD_DIR) - # Discover which variables can be separated - # This is typically the longest running operation - \ - csvs=$$(for f in $(CSVS); do if [ "$(BADPRJ_OK)" != 'Y' -o -f $$f ] ; then echo $$f; fi; done) ; \ - python3 $(TIMFUZ_DIR)/rref.py --corner $(RREF_CORNER) --simplify $(RREF_ARGS) --out $(BUILD_DIR)/sub.json.tmp $$csvs - mv $(BUILD_DIR)/sub.json.tmp $(BUILD_DIR)/sub.json +$(TIMGRID_V_S): $(SPECIMENS_OK) + $(MAKE) -f $(TIMFUZ_DIR)/projects/is.mk BUILD_DIR=$(BUILD_DIR_S) SOLVING=s SPECIMENS=$(SPECIMENS) all +s: $(TIMGRID_V_S) -$(BUILD_DIR)/grouped.csv: $(SPECIMENS_OK) $(BUILD_DIR)/sub.json - # Separate variables - \ - csvs=$$(for f in $(CSVS); do if [ "$(BADPRJ_OK)" != 'Y' -o -f $$f ] ; then echo $$f; fi; done) ; \ - python3 $(TIMFUZ_DIR)/csv_flat2group.py --sub-json $(BUILD_DIR)/sub.json --strict --out $(BUILD_DIR)/grouped.csv.tmp $$csvs - mv $(BUILD_DIR)/grouped.csv.tmp $(BUILD_DIR)/grouped.csv +.PHONY: i s -$(BUILD_DIR)/checksub: $(BUILD_DIR)/grouped.csv $(BUILD_DIR)/sub.json - # Verify sub.json makes a cleanly solvable solution with no non-pivot leftover - python3 $(TIMFUZ_DIR)/checksub.py --sub-json $(BUILD_DIR)/sub.json $(BUILD_DIR)/grouped.csv - touch $(BUILD_DIR)/checksub - -$(BUILD_DIR)/timgrid-v.json: $(TIMGRID_VCS) - python3 $(TIMFUZ_DIR)/timgrid_vc2v.py --out $(BUILD_DIR)/timgrid-v.json $(TIMGRID_VCS) +$(BUILD_DIR)/timgrid-v.json: $(TIMGRID_V_I) $(TIMGRID_V_S) + python3 $(TIMFUZ_DIR)/timgrid_vc2v.py --out $(BUILD_DIR)/timgrid-v.json $(TIMGRID_V_I) $(TIMGRID_V_S) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 23e6e7e1..a5fdfaad 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -17,7 +17,7 @@ proc write_info3 {} { set outdir "." set fp [open "$outdir/timing4.txt" w] # bel as site/bel, so don't bother with site - puts $fp "linetype net src_site src_site_pin src_bel src_bel_pin dst_site dst_site_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min pips inodes wires" + puts $fp "linetype net src_site src_site_type src_site_pin src_bel src_bel_pin dst_site dst_site_type dst_site_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min pips inodes wires" set TIME_start [clock clicks -milliseconds] set verbose 0 @@ -63,6 +63,7 @@ proc write_info3 {} { incr site_dst_nets continue } + set src_site_type [get_property SITE_TYPE $src_site] foreach src_site_pin $src_site_pins { if $verbose { puts "Source: $src_pin at site $src_site:$src_bel, spin $src_site_pin" @@ -93,9 +94,11 @@ proc write_info3 {} { set dst_site_pins [get_site_pins -of_objects $dst_pin] # Some nets are internal # But should this happen on dest if we've already filtered source? + # FIXME: need these for inter site model if {"$dst_site_pins" eq ""} { continue } + set dst_site_type [get_property SITE_TYPE $dst_site] # Also apparantly you can have multiple of these as well foreach dst_site_pin $dst_site_pins { set fast_max [get_property "FAST_MAX" $delay] @@ -118,7 +121,7 @@ proc write_info3 {} { #set wires [get_wires -of_objects $net -from $src_site_pin -to $dst_site_pin] set wires [get_wires -of_objects $nodes] - puts -nonewline $fp "NET $net $src_site $src_site_pin $src_bel $src_bel_pin $dst_site $dst_site_pin $dst_bel $dst_bel_pin $ico $fast_max $fast_min $slow_max $slow_min" + puts -nonewline $fp "NET $net $src_site $src_site_type $src_site_pin $src_bel $src_bel_pin $dst_site $dst_site_type $dst_site_pin $dst_bel $dst_bel_pin $ico $fast_max $fast_min $slow_max $slow_min" # Write pips w/ speed index puts -nonewline $fp " " diff --git a/fuzzers/007-timing/rref.py b/fuzzers/007-timing/rref.py index 95d1abb6..7d6a449b 100644 --- a/fuzzers/007-timing/rref.py +++ b/fuzzers/007-timing/rref.py @@ -73,7 +73,7 @@ class State(object): def load(fn_ins, simplify=False, corner=None, rm_zero=False): zero_names = OrderedSet() - Ads, b = loadc_Ads_b(fn_ins, corner=corner, ico=True) + Ads, b = loadc_Ads_b(fn_ins, corner=corner) if rm_zero: zero_names = rm_zero_cols(Ads) if simplify: diff --git a/fuzzers/007-timing/solve_qor.py b/fuzzers/007-timing/solve_qor.py index 55da0583..5a0bcf17 100644 --- a/fuzzers/007-timing/solve_qor.py +++ b/fuzzers/007-timing/solve_qor.py @@ -6,7 +6,7 @@ import numpy as np def run(fns_in, corner, bounds_csv, verbose=False): print('Loading data') - Ads, borig = loadc_Ads_b(fns_in, corner, ico=True) + Ads, borig = loadc_Ads_b(fns_in, corner) bounds = load_bounds(bounds_csv, corner) # verify is flattened diff --git a/fuzzers/007-timing/tile_annotate.py b/fuzzers/007-timing/tile_annotate.py index af3d3d2a..c3771f8f 100644 --- a/fuzzers/007-timing/tile_annotate.py +++ b/fuzzers/007-timing/tile_annotate.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import timfuz -from timfuz import loadc_Ads_bs, Ads2bounds +from timfuz import loadc_Ads_bs, Ads2bounds, PREFIX_W, PREFIX_P, PREFIX_SITEW, sitew_s2vals import sys import os @@ -9,22 +9,90 @@ import time import json +def add_pip_wire(tilej, bounds, verbose=False): + ''' + We know all possible pips and wires from tilej + Iterate over them and see if a result was generated + ''' + used_bounds = set() + + for tile in tilej['tiles'].values(): + + def addk(pws, prefix, k, v): + variable = prefix + ':' + v + val = bounds.get(variable, None) + # print(variable, val) + if val: + used_bounds.add(variable) + else: + val = [None, None, None, None] + pws[k] = val + + pips = tile['pips'] + for k, v in pips.items(): + #pips[k] = bounds.get('PIP_' + v, [None, None, None, None]) + addk(pips, PREFIX_P, k, v) + + wires = tile['wires'] + for k, v in wires.items(): + #wires[k] = bounds.get('WIRE_' + v, [None, None, None, None]) + addk(wires, PREFIX_W, k, v) + + # verify all the variables that should be used were applied + # ...except tilecon may be an ROI and we solved everything + print( + "Interconnect: %u / %u variables used" % + (len(used_bounds), len(bounds))) + if verbose: + print('Remainder: %s' % (set(bounds.keys()) - used_bounds)) + + +def add_sites(tilej, bounds): + # XXX: no source of truth currently + # is there a way we could get this? + + sitej = tilej.setdefault('sites', {}) + for variable, bound in bounds.items(): + # group delays by site + site_type, site_pin, bel_type, bel_pin = sitew_s2vals(variable) + asitej = sitej.setdefault(site_type, {}) + # group together? + # wish there was a way to do tuple keys + k = ('%s:%s:%s' % (site_pin, bel_type, bel_pin)) + #print(site_type, k) + asitej[k] = bound + #nsites = sum([len(v) for v in sitej.values()]) + print('Sites: added %u sites, %u site wires' % (len(sitej), len(bounds))) + + +def sep_bounds(bounds): + pw = {} + sites = {} + for k, v in bounds.items(): + prefix = k.split(':')[0] + if prefix == PREFIX_W: + pw[k] = v + elif prefix == PREFIX_P: + pw[k] = v + elif prefix == PREFIX_SITEW: + sites[k] = v + else: + assert 0, 'Unknown delay: %s %s' % (k, prefix) + return pw, sites + + def run(fns_in, fnout, tile_json_fn, verbose=False): # modified in place tilej = json.load(open(tile_json_fn, 'r')) for fnin in fns_in: - Ads, bs = loadc_Ads_bs([fnin], ico=True) + Ads, bs = loadc_Ads_bs([fnin]) bounds = Ads2bounds(Ads, bs) + bounds_pw, bounds_sites = sep_bounds(bounds) + print(len(bounds), len(bounds_pw), len(bounds_sites)) - 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]) - - wires = tile['wires'] - for k, v in wires.items(): - wires[k] = bounds.get('WIRE_' + v, [None, None, None, None]) + add_pip_wire(tilej, bounds_pw) + add_sites(tilej, bounds_sites) timfuz.tilej_stats(tilej) diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index 7e5a970e..8b7fa75d 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -17,6 +17,25 @@ import collections from benchmark import Benchmark +# prefix to make easier to track +# models do not overlap between PIPs and WIREs +PREFIX_W = 'WIRE' +PREFIX_P = 'PIP' +# site wire (a to b) +PREFIX_SITEW = 'SITEW' + + +def sitew_vals2s(site_type, site_pin, bel_type, bel_pin): + '''Pack site wire components into a variable string''' + return '%s:%s:%s:%s:%s' % ( + PREFIX_SITEW, site_type, site_pin, bel_type, bel_pin) + + +def sitew_s2vals(s): + prefix, site_type, site_pin, bel_type, bel_pin = s.split(':') + assert prefix == 'SITEW' + return site_type, site_pin, bel_type, bel_pin + # Equations are filtered out until nothing is left class SimplifiedToZero(Exception): @@ -524,11 +543,14 @@ def loadc_Ads_mkb(fns, mkb, filt): def loadc_Ads_b(fns, corner, ico=None): corner = corner or "slow_max" corneri = corner_s2i[corner] - + ''' if ico is not None: filt = lambda ico_, corners, vars: ico_ == ico else: filt = lambda ico_, corners, vars: True + ''' + assert ico is None, 'ICO filtering moved to higher levels' + filt = lambda ico_, corners, vars: True def mkb(val): return val[corneri] @@ -537,10 +559,14 @@ def loadc_Ads_b(fns, corner, ico=None): def loadc_Ads_bs(fns, ico=None): + ''' if ico is not None: filt = lambda ico_, corners, vars: ico_ == ico else: filt = lambda ico_, corners, vars: True + ''' + assert ico is None, 'ICO filtering moved to higher levels' + filt = lambda ico_, corners, vars: True def mkb(val): return val @@ -730,35 +756,50 @@ def corners2csv(bs): 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(): + def tile_stats(): + stats = {} 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 + 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 + return stats + + def site_stats(): + sitej = tilej['sites'] + return { + 'wires': sum([len(v) for v in sitej.values()]), + 'sites': len(sitej) + } + + tstats = tile_stats() + sstats = site_stats() 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] + net = tstats[etype]['net'] + solved = tstats[etype]['solved'][corneri] + covered = tstats[etype]['covered'][corneri] print( ' %s: %u / %u solved, %u / %u covered' % (etype, solved, net, covered, net)) + print( + ' sites: %u sites, %u site wires' % + (sstats['sites'], sstats['wires'])) -def load_bounds(bounds_csv, corner, ico=True): - Ads, b = loadc_Ads_b([bounds_csv], corner, ico=ico) +def load_bounds(bounds_csv, corner): + Ads, b = loadc_Ads_b([bounds_csv], corner) return Ads2bounds(Ads, b) diff --git a/fuzzers/007-timing/timfuz_solve.py b/fuzzers/007-timing/timfuz_solve.py index 4cded620..c37a6228 100644 --- a/fuzzers/007-timing/timfuz_solve.py +++ b/fuzzers/007-timing/timfuz_solve.py @@ -165,7 +165,7 @@ def run( verbose=False, **kwargs): print('Loading data') - Ads, b = loadc_Ads_b(fns_in, corner, ico=True) + Ads, b = loadc_Ads_b(fns_in, corner) # Remove duplicate rows # is this necessary? @@ -193,7 +193,7 @@ def run( Used primarily for multiple optimization passes, such as different algorithms or additional constraints ''' if bounds_csv: - Ads2, b2 = loadc_Ads_b([bounds_csv], corner, ico=True) + Ads2, b2 = loadc_Ads_b([bounds_csv], corner) bounds = Ads2bounds(Ads2, b2) assert len(bounds), 'Failed to load bounds' rows_old = len(Ads) diff --git a/fuzzers/007-timing/timgrid_vc2v.py b/fuzzers/007-timing/timgrid_vc2v.py index 8c188d81..8cb19162 100644 --- a/fuzzers/007-timing/timgrid_vc2v.py +++ b/fuzzers/007-timing/timgrid_vc2v.py @@ -23,7 +23,31 @@ corner2minmax = { } -def build_tilejo(fnins): +def merge_bdict(vi, vo): + ''' + vi: input dictionary + vo: output dictionary + values are corner delay 4 tuples + ''' + + for name, bis in vi.items(): + bos = vo.get(name, [None, None, None, None]) + for cornerk, corneri in corner_s2i.items(): + bo = bos[corneri] + bi = bis[corneri] + # no new data + if bi is None: + pass + # no previous data + elif bo is None: + bos[corneri] = bi + # combine + else: + minmax = corner2minmax[cornerk] + bos[corneri] = minmax(bi, bo) + + +def merge_tiles(tileji, tilejo): ''' { "tiles": { @@ -38,36 +62,37 @@ def build_tilejo(fnins): ], ''' - tilejo = {"tiles": {}} + for tilek, tilevi in tileji.items(): + # No previous data? Copy + tilevo = tilejo.get(tilek, None) + if tilevo is None: + tilejo[tilek] = tilevi + # Otherwise combine + else: + merge_bdict(tilevi['pips'], tilevo['pips']) + merge_bdict(tilevi['wires'], tilevo['wires']) + + +def merge_sites(siteji, sitejo): + for k, vi in siteji.items(): + vo = sitejo.get(k, None) + # No previous data? Copy + if vo is None: + sitejo[k] = vi + # Otherwise combine + else: + merge_bdict(vi, vo) + + +def build_tilejo(fnins): + # merge all inputs into blank output + tilejo = {"tiles": {}, "sites": {}} for fnin in fnins: tileji = json.load(open(fnin, 'r')) - for tilek, tilevi in tileji['tiles'].items(): - # No previous data? Copy - tilevo = tilejo['tiles'].get(tilek, None) - if tilevo is None: - tilejo['tiles'][tilek] = tilevi - # Otherwise combine - else: - def process_type(etype): - for pipk, pipvi in tilevi[etype].items(): - pipvo = tilevo[etype][pipk] - for cornerk, corneri in corner_s2i.items(): - cornervo = pipvo[corneri] - cornervi = pipvi[corneri] - # no new data - if cornervi is None: - pass - # no previous data - elif cornervo is None: - pipvo[corneri] = cornervi - # combine - else: - minmax = corner2minmax[cornerk] - pipvo[corneri] = minmax(cornervi, cornervo) + merge_tiles(tileji['tiles'], tilejo['tiles']) + merge_sites(tileji['sites'], tilejo['sites']) - process_type('pips') - process_type('wires') return tilejo @@ -110,15 +135,11 @@ def check_corner_minmax(tilej, verbose=False): timfuz.tilej_stats(tilej) -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) + # XXX: check fast vs slow? + # check_corners_minmax(tilejo) json.dump( tilejo, open(fnout, 'w'), diff --git a/fuzzers/007-timing/timing_txt2icsv.py b/fuzzers/007-timing/timing_txt2icsv.py index e1a04bae..9f719120 100644 --- a/fuzzers/007-timing/timing_txt2icsv.py +++ b/fuzzers/007-timing/timing_txt2icsv.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from timfuz import Benchmark, A_di2ds +from timfuz import Benchmark, A_di2ds, PREFIX_W, PREFIX_P from timing_txt2json import gen_timing4n, load_speed_json import glob @@ -9,11 +9,6 @@ import json import sys from collections import OrderedDict -# prefix to make easier to track -# models do not overlap between PIPs and WIREs -PREFIX_W = 'WIRE_' -PREFIX_P = 'PIP_' - # Verify the nodes and wires really do line up def vals2Adi_check(vals, names): @@ -32,11 +27,11 @@ def json2Ads(vals, verbose=False): def pip2speed(pip): _site, _name, pip_name = pip - return PREFIX_P + pip_name + return PREFIX_P + ':' + pip_name def wire2speed(wire): _site, _name, wire_name = wire - return PREFIX_W + wire_name + return PREFIX_W + ':' + wire_name print('Making equations') @@ -96,7 +91,7 @@ def main(): parser = argparse.ArgumentParser( description= - 'Convert obscure timing4.txt into more readable but roughly equivilent timing4i.csv (interconnect)' + 'Convert obscure timing4.txt into timing4i.csv (interconnect delay variable occurances)' ) parser.add_argument('--verbose', type=int, help='') diff --git a/fuzzers/007-timing/timing_txt2json.py b/fuzzers/007-timing/timing_txt2json.py index 573911e8..97024985 100644 --- a/fuzzers/007-timing/timing_txt2json.py +++ b/fuzzers/007-timing/timing_txt2json.py @@ -37,7 +37,7 @@ def parse_wire(s, speed_i2s): def gen_timing4(fn, speed_i2s): f = open(fn, 'r') - header_want = 'linetype net src_site src_site_pin src_bel src_bel_pin dst_site dst_site_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min pips inodes wires' + header_want = 'linetype net src_site src_site_type src_site_pin src_bel src_bel_pin dst_site dst_site_type dst_site_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min pips inodes wires' ncols = len(header_want.split()) # src_bel dst_bel ico fast_max fast_min slow_max slow_min pips @@ -50,6 +50,7 @@ def gen_timing4(fn, speed_i2s): bads = 0 net_lines = 0 for l in f: + def group_line(): ncols = len('lintype ico delays'.split()) assert len(parts) == ncols @@ -58,23 +59,25 @@ def gen_timing4(fn, speed_i2s): def net_line(): assert len(parts) == ncols - _lintype, net, src_site, src_site_pin, src_bel, src_bel_pin, dst_site, dst_site_pin, dst_bel, dst_bel_pin, ico, fast_max, fast_min, slow_max, slow_min, pips, nodes, wires = parts + _lintype, net, src_site, src_site_type, src_site_pin, src_bel, src_bel_pin, dst_site, dst_site_type, dst_site_pin, dst_bel, dst_bel_pin, ico, fast_max, fast_min, slow_max, slow_min, pips, nodes, wires = parts pips = pips.split('|') nodes = nodes.split('|') wires = wires.split('|') return { 'net': net, 'src': { - 'site': src_site, + 'site': src_site, + 'site_type': src_site_type, 'site_pin': src_site_pin, - 'bel': src_bel, - 'bel_pin': src_bel_pin, + 'bel': src_bel, + 'bel_pin': src_bel_pin, }, 'dst': { - 'site': dst_site, + 'site': dst_site, + 'site_type': dst_site_type, 'site_pin': dst_site_pin, - 'bel': dst_bel, - 'bel_pin': dst_bel_pin, + 'bel': dst_bel, + 'bel_pin': dst_bel_pin, }, 't': { # ps @@ -99,13 +102,11 @@ def gen_timing4(fn, speed_i2s): val = { 'NET': net_line, 'GROUP': group_line, - }[lintype]() + }[lintype]() yield lintype, val rets += 1 - print( - ' load %s: %d bad, %d good, %u net lines' % - (fn, bads, rets, net_lines)) + print(' load %s: %d bad, %d good lines' % (fn, bads, rets)) def gen_timing4n(fn, speed_i2s): @@ -115,7 +116,7 @@ def gen_timing4n(fn, speed_i2s): yield val -def gen_timing4i(fn, speed_i2s): +def gen_timing4a(fn, speed_i2s): ''' Like above, but aggregate ico + non-ico into single entries Key these based on uniqueness of (src_bel, dst_bel) @@ -126,24 +127,26 @@ def gen_timing4i(fn, speed_i2s): ''' entries = {} timgen = gen_timing4(fn, speed_i2s) + rets = 0 while True: + def get_ico(exp_ico): ret = [] try: - lintype, val = gen.next() + lintype, val = next(timgen) except StopIteration: return None assert lintype == 'GROUP' ico, delays = val assert ico == exp_ico for _ in range(delays): - lintype, val = gen.next() + lintype, val = next(timgen) assert lintype == 'NET' ret.append(val) return ret ico0s = get_ico(0) - if ico0 is None: + if ico0s is None: break ico1s = get_ico(1) # TODO: verify this is actually true @@ -151,7 +154,8 @@ def gen_timing4i(fn, speed_i2s): def same_path(l, r): # if source and dest are the same, should be the same thing - return l['src']['bel_pin'] == r['src']['bel_pin'] and l['dst']['bel_pin'] == r['dst']['bel_pin'] + return l['src']['bel_pin'] == r['src']['bel_pin'] and l['dst'][ + 'bel_pin'] == r['dst']['bel_pin'] for ico0, ico1 in zip(ico0s, ico1s): # TODO: verify this is actually true @@ -161,8 +165,11 @@ def gen_timing4i(fn, speed_i2s): ico0['t'] = ( ico0['t'], ico1['t'], - ) + ) yield ico0 + rets += 1 + print(' load %s: %u aggregated lines' % (fn, rets)) + def load_speed_json(f): j = json.load(f) @@ -175,6 +182,7 @@ def load_speed_json(f): return j, speed_i2s +''' def run(speed_json_f, fout, fns_in, verbose=0, corner=None): print('Loading data') _speedj, speed_i2s = load_speed_json(speed_json_f) @@ -235,3 +243,4 @@ def main(): if __name__ == '__main__': main() +''' diff --git a/fuzzers/007-timing/timing_txt2scsv.py b/fuzzers/007-timing/timing_txt2scsv.py new file mode 100644 index 00000000..51c78ab4 --- /dev/null +++ b/fuzzers/007-timing/timing_txt2scsv.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 + +from timfuz import Benchmark, A_di2ds, PREFIX_SITEW, sitew_vals2s +from timing_txt2json import gen_timing4a, load_speed_json + +import glob +import math +import json +import sys +from collections import OrderedDict + + +def gen_diffs(speed_json_f, fns_in): + print('Loading data') + _speedj, speed_i2s = load_speed_json(speed_json_f) + + for fn_in in fns_in: + for val in gen_timing4a(fn_in, speed_i2s): + # diff to get site only delay + tsites = {} + for k in val['t'][0].keys(): + v = val['t'][0][k] - val['t'][1][k] + assert v >= 0 + tsites[k] = v + yield val, tsites + + +''' +def run(speed_json_f, fout, fns_in, verbose=0, corner=None): + fout.write('fast_max fast_min slow_max slow_min,src_site_type,src_site,src_bel,src_bel_pin,dst_site_type,dst_site,dst_bel,dst_bel_pin\n') + for val, tsites in gen_diffs(speed_json_f, fns_in): + def mkb(t): + return (t['fast_max'], t['fast_min'], t['slow_max'], t['slow_min']) + bstr = ' '.join([str(x) for x in mkb(tsites)]) + + def srcdst(which): + sd = val[which] + # IOB_X0Y106 IOB_X0Y106/INBUF_EN IOB_X0Y106/INBUF_EN/OUT + # print(sd['site'], sd['bel'], sd['bel_pin']) + site, bel, bel_pin = sd['bel_pin'].split('/') + assert sd['site'] == site + assert sd['bel'] == site + '/' + bel + return sd['site_type'], site, bel, bel_pin + + items = [bstr] + items.extend(srcdst('src')) + items.extend(srcdst('dst')) + fout.write(','.join(items) + '\n') + print('done') +''' + + +# XXX: move to json converter? +def sd_parts(sd): + '''Return site_type, site_pin, bel_type, bel_pin as non-prefixed strings''' + # IOB_X0Y106 IOB_X0Y106/INBUF_EN IOB_X0Y106/INBUF_EN/OUT + # print(sd['site'], sd['bel'], sd['bel_pin']) + site_type = sd['site_type'] + site, bel_type, bel_pin = sd['bel_pin'].split('/') + assert sd['site'] == site + assert sd['bel'] == site + '/' + bel_type + site, site_pin = sd['site_pin'].split('/') + assert sd['site_pin'] == sd['site'] + '/' + site_pin + return site_type, site_pin, bel_type, bel_pin + + +def run(speed_json_f, fout, fns_in, verbose=0, corner=None): + ''' + instead of writing to a simplified csv, lets just go directly to a delay format identical to what fabric uses + Path types: + -inter site: think these are removed for now? + 1 model + NOTE: be careful of a net that goes external and comes back in, which isn't inter site + definition is that it doesn't have any site pins + -intra site + 2 models + ''' + + fout.write( + 'ico,fast_max fast_min slow_max slow_min,src_site_type,src_site,src_bel,src_bel_pin,dst_site_type,dst_site,dst_bel,dst_bel_pin\n' + ) + for val, tsites in gen_diffs(speed_json_f, fns_in): + + def mkb(t): + return (t['fast_max'], t['fast_min'], t['slow_max'], t['slow_min']) + + bstr = ' '.join([str(x) for x in mkb(tsites)]) + + # Identify inter site transaction (SITEI) + if val['src']['site_pin'] is None and val['dst']['site_pin'] is None: + # add one delay model for the path + assert 0, 'FIXME: inter site transaction' + row_ds = {'SITEI_BLAH': None} + else: + # if it exits a site it should enter another (possibly the same site) + # site in (SITEI) or site out (SITEO)? + # nah, keep things simple and just call them SITEW + assert val['src']['site_pin'] and val['dst']['site_pin'] + row_ds = {} + + def add_delay(sd): + site_type, site_pin, bel_type, bel_pin = sd_parts(sd) + # there are _ in some of the names + # use some other chars + k = sitew_vals2s(site_type, site_pin, bel_type, bel_pin) + # even if its the same site src and dst, input and output should be different types + assert k not in row_ds + row_ds[k] = 1 + + add_delay(val['src']) + add_delay(val['dst']) + + row_ico = 0 + items = [str(row_ico), bstr] + for k, v in sorted(row_ds.items()): + items.append('%u %s' % (v, k)) + fout.write(','.join(items) + '\n') + print('done') + + +def main(): + import argparse + + parser = argparse.ArgumentParser( + description= + 'Convert obscure timing4.txt into timing4s.csv (site delay variable occurances)' + ) + + parser.add_argument('--verbose', type=int, help='') + # made a bulk conversion easier...keep? + parser.add_argument( + '--auto-name', action='store_true', help='timing4.txt => timing4i.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 timing4i.csv file') + parser.add_argument('fns_in', nargs='+', help='Input timing4.txt files') + args = parser.parse_args() + bench = Benchmark() + + fnout = args.out + if fnout is None: + if args.auto_name: + assert len(args.fns_in) == 1 + fnin = args.fns_in[0] + fnout = fnin.replace('.txt', 's.csv') + assert fnout != fnin, 'Expect .txt in' + else: + # practically there are too many stray prints to make this work as expected + assert 0, 'File name required' + fnout = '/dev/stdout' + print("Writing to %s" % fnout) + fout = open(fnout, 'w') + + fns_in = args.fns_in + if not fns_in: + fns_in = glob.glob('specimen_*/timing4.txt') + + run( + speed_json_f=open(args.speed_json, 'r'), + fout=fout, + fns_in=fns_in, + verbose=args.verbose) + + +if __name__ == '__main__': + main()