From 9e8f6ea3e445cf5a0fb6d0029889f787e15670d5 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Mon, 24 Sep 2018 16:53:18 -0700 Subject: [PATCH 01/23] timfuz: timing3.csv => timing3i.csv (interconnect) Signed-off-by: John McMaster --- fuzzers/007-timing/checksub.py | 4 ++-- fuzzers/007-timing/corner_csv.py | 10 +++++----- fuzzers/007-timing/csv_flat2group.py | 4 ++-- fuzzers/007-timing/projects/corner.mk | 2 +- fuzzers/007-timing/projects/generate.sh | 2 +- fuzzers/007-timing/projects/project.mk | 2 +- fuzzers/007-timing/rref.py | 4 ++-- fuzzers/007-timing/solve_leastsq.py | 4 ++-- fuzzers/007-timing/solve_linprog.py | 4 ++-- fuzzers/007-timing/solve_qor.py | 4 ++-- .../{timing_txt2csv.py => timing_txt2icsv.py} | 12 ++++++++---- 11 files changed, 28 insertions(+), 24 deletions(-) rename fuzzers/007-timing/{timing_txt2csv.py => timing_txt2icsv.py} (97%) diff --git a/fuzzers/007-timing/checksub.py b/fuzzers/007-timing/checksub.py index 86823805..407156d6 100644 --- a/fuzzers/007-timing/checksub.py +++ b/fuzzers/007-timing/checksub.py @@ -111,14 +111,14 @@ def main(): 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='timing3i.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') + fns_in = glob.glob('specimen_*/timing3i.csv') sub_json = None if args.sub_json: diff --git a/fuzzers/007-timing/corner_csv.py b/fuzzers/007-timing/corner_csv.py index a4ccbf84..15e82ef8 100644 --- a/fuzzers/007-timing/corner_csv.py +++ b/fuzzers/007-timing/corner_csv.py @@ -28,10 +28,10 @@ def main(): parser.add_argument('--verbose', type=int, help='') parser.add_argument( - '--auto-name', action='store_true', help='timing3.csv => timing3c.csv') + '--auto-name', action='store_true', help='timing3i.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='timing3i.csv input files') args = parser.parse_args() bench = Benchmark() @@ -40,8 +40,8 @@ def main(): if args.auto_name: assert len(args.fns_in) == 1 fnin = args.fns_in[0] - fnout = fnin.replace('timing3.csv', 'timing3c.csv') - assert fnout != fnin, 'Expect timing3.csv in' + fnout = fnin.replace('timing3i.csv', 'timing3c.csv') + assert fnout != fnin, 'Expect timing3i.csv in' else: fnout = '/dev/stdout' print("Writing to %s" % fnout) @@ -49,7 +49,7 @@ def main(): fns_in = args.fns_in if not fns_in: - fns_in = glob.glob('specimen_*/timing3.csv') + fns_in = glob.glob('specimen_*/timing3i.csv') run(fout=fout, fns_in=fns_in, corner=args.corner, verbose=args.verbose) diff --git a/fuzzers/007-timing/csv_flat2group.py b/fuzzers/007-timing/csv_flat2group.py index a0f76f1d..c58f2a45 100644 --- a/fuzzers/007-timing/csv_flat2group.py +++ b/fuzzers/007-timing/csv_flat2group.py @@ -45,14 +45,14 @@ def main(): 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.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing3i.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') + fns_in = glob.glob('specimen_*/timing3i.csv') sub_json = load_sub(args.sub_json) diff --git a/fuzzers/007-timing/projects/corner.mk b/fuzzers/007-timing/projects/corner.mk index da62dbd8..1d84d688 100644 --- a/fuzzers/007-timing/projects/corner.mk +++ b/fuzzers/007-timing/projects/corner.mk @@ -46,6 +46,6 @@ $(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 - python3 $(TIMFUZ_DIR)/solve_qor.py --corner $(CORNER) --bounds-csv $(BUILD_DIR)/$(CORNER)/flat.csv specimen_*/timing3.csv >$(BUILD_DIR)/$(CORNER)/qor.txt.tmp + python3 $(TIMFUZ_DIR)/solve_qor.py --corner $(CORNER) --bounds-csv $(BUILD_DIR)/$(CORNER)/flat.csv specimen_*/timing3i.csv >$(BUILD_DIR)/$(CORNER)/qor.txt.tmp mv $(BUILD_DIR)/$(CORNER)/qor.txt.tmp $(BUILD_DIR)/$(CORNER)/qor.txt diff --git a/fuzzers/007-timing/projects/generate.sh b/fuzzers/007-timing/projects/generate.sh index f8e6e094..4dfcd690 100644 --- a/fuzzers/007-timing/projects/generate.sh +++ b/fuzzers/007-timing/projects/generate.sh @@ -4,6 +4,6 @@ source ${XRAY_GENHEADER} TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing timing_txt2csv () { - python3 $TIMFUZ_DIR/timing_txt2csv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing3.csv timing3.txt + python3 $TIMFUZ_DIR/timing_txt2icsv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing3i.csv timing3.txt } diff --git a/fuzzers/007-timing/projects/project.mk b/fuzzers/007-timing/projects/project.mk index 4cced328..31379388 100644 --- a/fuzzers/007-timing/projects/project.mk +++ b/fuzzers/007-timing/projects/project.mk @@ -4,7 +4,7 @@ N := 1 SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N))) SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS)) -CSVS := $(addsuffix /timing3.csv,$(SPECIMENS)) +CSVS := $(addsuffix /timing3i.csv,$(SPECIMENS)) TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing RREF_CORNER=slow_max # Allow an empty system of equations? diff --git a/fuzzers/007-timing/rref.py b/fuzzers/007-timing/rref.py index be13c805..c003a3e8 100644 --- a/fuzzers/007-timing/rref.py +++ b/fuzzers/007-timing/rref.py @@ -223,13 +223,13 @@ def main(): 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.csv input files') + parser.add_argument('fns_in', nargs='*', help='timing3i.csv input files') args = parser.parse_args() bench = Benchmark() fns_in = args.fns_in if not fns_in: - fns_in = glob.glob('specimen_*/timing3.csv') + fns_in = glob.glob('specimen_*/timing3i.csv') try: run( diff --git a/fuzzers/007-timing/solve_leastsq.py b/fuzzers/007-timing/solve_leastsq.py index 953c4dcf..06939bdc 100644 --- a/fuzzers/007-timing/solve_leastsq.py +++ b/fuzzers/007-timing/solve_leastsq.py @@ -119,14 +119,14 @@ 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='+', help='timing3.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing3i.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') + fns_in = glob.glob('specimen_*/timing3i.csv') sub_json = None if args.sub_json: diff --git a/fuzzers/007-timing/solve_linprog.py b/fuzzers/007-timing/solve_linprog.py index 0bb0dd6f..1f2eecc4 100644 --- a/fuzzers/007-timing/solve_linprog.py +++ b/fuzzers/007-timing/solve_linprog.py @@ -162,14 +162,14 @@ 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='+', help='timing3.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing3i.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') + fns_in = glob.glob('specimen_*/timing3i.csv') sub_json = None if args.sub_json: diff --git a/fuzzers/007-timing/solve_qor.py b/fuzzers/007-timing/solve_qor.py index d3bb1eea..51edc0a2 100644 --- a/fuzzers/007-timing/solve_qor.py +++ b/fuzzers/007-timing/solve_qor.py @@ -37,14 +37,14 @@ def main(): '--bounds-csv', required=True, help='Previous solve result starting point') - parser.add_argument('fns_in', nargs='+', help='timing3.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing3i.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') + fns_in = glob.glob('specimen_*/timing3i.csv') try: run( diff --git a/fuzzers/007-timing/timing_txt2csv.py b/fuzzers/007-timing/timing_txt2icsv.py similarity index 97% rename from fuzzers/007-timing/timing_txt2csv.py rename to fuzzers/007-timing/timing_txt2icsv.py index 0ba2b36d..6e09a3d6 100644 --- a/fuzzers/007-timing/timing_txt2csv.py +++ b/fuzzers/007-timing/timing_txt2icsv.py @@ -235,6 +235,10 @@ def run(speed_json_f, fout, f_ins, verbose=0, corner=None): fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n') for row_bs, row_ds, row_ico in zip(bs, Ads, ico): + # XXX: consider removing ico column + # its obsolete at this point + if not ico: + continue # like: 123 456 120 450, 1 a, 2 b # first column has delay corners, followed by delay element count items = [str(row_ico), ' '.join([str(x) for x in row_bs])] @@ -248,18 +252,18 @@ def main(): parser = argparse.ArgumentParser( description= - 'Convert obscure timing3.txt into more readable but roughly equivilent timing3.csv' + 'Convert obscure timing3.txt into more readable but roughly equivilent timing3i.csv (interconnect)' ) parser.add_argument('--verbose', type=int, help='') # made a bulk conversion easier...keep? parser.add_argument( - '--auto-name', action='store_true', help='timing3.txt => timing3.csv') + '--auto-name', action='store_true', help='timing3.txt => timing3i.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 timing3.csv file') + parser.add_argument('--out', default=None, help='Output timing3i.csv file') parser.add_argument('fns_in', nargs='+', help='Input timing3.txt files') args = parser.parse_args() bench = Benchmark() @@ -269,7 +273,7 @@ def main(): if args.auto_name: assert len(args.fns_in) == 1 fnin = args.fns_in[0] - fnout = fnin.replace('.txt', '.csv') + fnout = fnin.replace('.txt', 'i.csv') assert fnout != fnin, 'Expect .txt in' else: # practically there are too many stray prints to make this work as expected From 2c6c3487a47bcc48f2ad84705e49e661f943f988 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Mon, 24 Sep 2018 18:27:25 -0700 Subject: [PATCH 02/23] timfuz: timing4 format w/ site/bel info Signed-off-by: John McMaster --- fuzzers/007-timing/checksub.py | 4 +- fuzzers/007-timing/corner_csv.py | 12 +- fuzzers/007-timing/csv_flat2group.py | 4 +- fuzzers/007-timing/projects/corner.mk | 2 +- fuzzers/007-timing/projects/generate.sh | 3 +- .../007-timing/projects/placelut/generate.sh | 2 +- fuzzers/007-timing/projects/project.mk | 2 +- fuzzers/007-timing/projects/project.tcl | 10 +- fuzzers/007-timing/rref.py | 4 +- fuzzers/007-timing/solve_leastsq.py | 4 +- fuzzers/007-timing/solve_linprog.py | 4 +- fuzzers/007-timing/solve_qor.py | 4 +- fuzzers/007-timing/timing_txt2icsv.py | 226 +++-------------- fuzzers/007-timing/timing_txt2json.py | 237 ++++++++++++++++++ 14 files changed, 302 insertions(+), 216 deletions(-) create mode 100644 fuzzers/007-timing/timing_txt2json.py diff --git a/fuzzers/007-timing/checksub.py b/fuzzers/007-timing/checksub.py index 407156d6..559b6c61 100644 --- a/fuzzers/007-timing/checksub.py +++ b/fuzzers/007-timing/checksub.py @@ -111,14 +111,14 @@ def main(): parser.add_argument('--verbose', action='store_true', help='') parser.add_argument('--sub-json', help='') - parser.add_argument('fns_in', nargs='*', help='timing3i.csv input files') + parser.add_argument('fns_in', nargs='*', help='timing4i.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_*/timing3i.csv') + fns_in = glob.glob('specimen_*/timing4i.csv') sub_json = None if args.sub_json: diff --git a/fuzzers/007-timing/corner_csv.py b/fuzzers/007-timing/corner_csv.py index 15e82ef8..8b7290a3 100644 --- a/fuzzers/007-timing/corner_csv.py +++ b/fuzzers/007-timing/corner_csv.py @@ -28,10 +28,12 @@ def main(): parser.add_argument('--verbose', type=int, help='') parser.add_argument( - '--auto-name', action='store_true', help='timing3i.csv => timing3c.csv') + '--auto-name', + action='store_true', + help='timing4i.csv => timing4c.csv') parser.add_argument('--out', default=None, help='Output csv') parser.add_argument('--corner', help='Output csv') - parser.add_argument('fns_in', nargs='+', help='timing3i.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing4i.csv input files') args = parser.parse_args() bench = Benchmark() @@ -40,8 +42,8 @@ def main(): if args.auto_name: assert len(args.fns_in) == 1 fnin = args.fns_in[0] - fnout = fnin.replace('timing3i.csv', 'timing3c.csv') - assert fnout != fnin, 'Expect timing3i.csv in' + fnout = fnin.replace('timing4i.csv', 'timing4c.csv') + assert fnout != fnin, 'Expect timing4i.csv in' else: fnout = '/dev/stdout' print("Writing to %s" % fnout) @@ -49,7 +51,7 @@ def main(): fns_in = args.fns_in if not fns_in: - fns_in = glob.glob('specimen_*/timing3i.csv') + fns_in = glob.glob('specimen_*/timing4i.csv') run(fout=fout, fns_in=fns_in, corner=args.corner, verbose=args.verbose) diff --git a/fuzzers/007-timing/csv_flat2group.py b/fuzzers/007-timing/csv_flat2group.py index c58f2a45..aa7d6a1d 100644 --- a/fuzzers/007-timing/csv_flat2group.py +++ b/fuzzers/007-timing/csv_flat2group.py @@ -45,14 +45,14 @@ def main(): 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='timing3i.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing4i.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_*/timing3i.csv') + fns_in = glob.glob('specimen_*/timing4i.csv') sub_json = load_sub(args.sub_json) diff --git a/fuzzers/007-timing/projects/corner.mk b/fuzzers/007-timing/projects/corner.mk index 1d84d688..618719ec 100644 --- a/fuzzers/007-timing/projects/corner.mk +++ b/fuzzers/007-timing/projects/corner.mk @@ -46,6 +46,6 @@ $(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 - python3 $(TIMFUZ_DIR)/solve_qor.py --corner $(CORNER) --bounds-csv $(BUILD_DIR)/$(CORNER)/flat.csv specimen_*/timing3i.csv >$(BUILD_DIR)/$(CORNER)/qor.txt.tmp + 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 diff --git a/fuzzers/007-timing/projects/generate.sh b/fuzzers/007-timing/projects/generate.sh index 4dfcd690..220f40c0 100644 --- a/fuzzers/007-timing/projects/generate.sh +++ b/fuzzers/007-timing/projects/generate.sh @@ -4,6 +4,7 @@ source ${XRAY_GENHEADER} TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing timing_txt2csv () { - python3 $TIMFUZ_DIR/timing_txt2icsv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing3i.csv timing3.txt + 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 } diff --git a/fuzzers/007-timing/projects/placelut/generate.sh b/fuzzers/007-timing/projects/placelut/generate.sh index fff71d31..40bc4550 100755 --- a/fuzzers/007-timing/projects/placelut/generate.sh +++ b/fuzzers/007-timing/projects/placelut/generate.sh @@ -7,5 +7,5 @@ TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing python ../generate.py --sdx 4 --sdy 4 >top.v vivado -mode batch -source ../generate.tcl -python3 $TIMFUZ_DIR/timing_txt2csv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing3.csv timing3.txt +python3 $TIMFUZ_DIR/timing_txt2csv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing4.csv timing4.txt diff --git a/fuzzers/007-timing/projects/project.mk b/fuzzers/007-timing/projects/project.mk index 31379388..9545c888 100644 --- a/fuzzers/007-timing/projects/project.mk +++ b/fuzzers/007-timing/projects/project.mk @@ -4,7 +4,7 @@ N := 1 SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N))) SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS)) -CSVS := $(addsuffix /timing3i.csv,$(SPECIMENS)) +CSVS := $(addsuffix /timing4i.csv,$(SPECIMENS)) TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing RREF_CORNER=slow_max # Allow an empty system of equations? diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index b1478201..23e6e7e1 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -15,9 +15,9 @@ proc pin_bel {pin} { # This allows tracing the full path along with pips proc write_info3 {} { set outdir "." - set fp [open "$outdir/timing3.txt" w] + set fp [open "$outdir/timing4.txt" w] # bel as site/bel, so don't bother with site - puts $fp "net src_bel dst_bel ico fast_max fast_min slow_max slow_min pips inodes wires" + 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" set TIME_start [clock clicks -milliseconds] set verbose 0 @@ -43,6 +43,7 @@ proc write_info3 {} { # With OUT filter this seems to be sufficient set src_pin [get_pins -leaf -filter {DIRECTION == OUT} -of_objects $net] set src_bel [pin_bel $src_pin] + set src_bel_pin [get_bel_pins -of_objects $src_pin] set src_site [get_sites -of_objects $src_bel] # Only one net driver set src_site_pins [get_site_pins -filter {DIRECTION == OUT} -of_objects $net] @@ -76,12 +77,14 @@ proc write_info3 {} { } else { set delays [get_net_delays -of_objects $net] } + puts $fp "GROUP $ico [llength $delays]" foreach delay $delays { set delaystr [get_property NAME $delay] set dst_pins [get_property TO_PIN $delay] set dst_pin [get_pins $dst_pins] #puts " $delaystr: $src_pin => $dst_pin" set dst_bel [pin_bel $dst_pin] + set dst_bel_pin [get_bel_pins -of_objects $dst_pin] set dst_site [get_sites -of_objects $dst_bel] if $verbose { puts " Dest: $dst_pin at site $dst_site:$dst_bel" @@ -115,8 +118,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 $fp "$net $src_bel $dst_bel $ico $fast_max $fast_min $slow_max $slow_min $pips" - puts -nonewline $fp "$net $src_bel $dst_bel $ico $fast_max $fast_min $slow_max $slow_min" + 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" # Write pips w/ speed index puts -nonewline $fp " " diff --git a/fuzzers/007-timing/rref.py b/fuzzers/007-timing/rref.py index c003a3e8..95d1abb6 100644 --- a/fuzzers/007-timing/rref.py +++ b/fuzzers/007-timing/rref.py @@ -223,13 +223,13 @@ def main(): 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='timing3i.csv input files') + parser.add_argument('fns_in', nargs='*', help='timing4i.csv input files') args = parser.parse_args() bench = Benchmark() fns_in = args.fns_in if not fns_in: - fns_in = glob.glob('specimen_*/timing3i.csv') + fns_in = glob.glob('specimen_*/timing4i.csv') try: run( diff --git a/fuzzers/007-timing/solve_leastsq.py b/fuzzers/007-timing/solve_leastsq.py index 06939bdc..8f01b54e 100644 --- a/fuzzers/007-timing/solve_leastsq.py +++ b/fuzzers/007-timing/solve_leastsq.py @@ -119,14 +119,14 @@ 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='+', help='timing3i.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing4i.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_*/timing3i.csv') + fns_in = glob.glob('specimen_*/timing4i.csv') sub_json = None if args.sub_json: diff --git a/fuzzers/007-timing/solve_linprog.py b/fuzzers/007-timing/solve_linprog.py index 1f2eecc4..40586f5e 100644 --- a/fuzzers/007-timing/solve_linprog.py +++ b/fuzzers/007-timing/solve_linprog.py @@ -162,14 +162,14 @@ 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='+', help='timing3i.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing4i.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_*/timing3i.csv') + fns_in = glob.glob('specimen_*/timing4i.csv') sub_json = None if args.sub_json: diff --git a/fuzzers/007-timing/solve_qor.py b/fuzzers/007-timing/solve_qor.py index 51edc0a2..55da0583 100644 --- a/fuzzers/007-timing/solve_qor.py +++ b/fuzzers/007-timing/solve_qor.py @@ -37,14 +37,14 @@ def main(): '--bounds-csv', required=True, help='Previous solve result starting point') - parser.add_argument('fns_in', nargs='+', help='timing3i.csv input files') + parser.add_argument('fns_in', nargs='+', help='timing4i.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_*/timing3i.csv') + fns_in = glob.glob('specimen_*/timing4i.csv') try: run( diff --git a/fuzzers/007-timing/timing_txt2icsv.py b/fuzzers/007-timing/timing_txt2icsv.py index 6e09a3d6..e1a04bae 100644 --- a/fuzzers/007-timing/timing_txt2icsv.py +++ b/fuzzers/007-timing/timing_txt2icsv.py @@ -1,106 +1,20 @@ #!/usr/bin/env python3 from timfuz import Benchmark, A_di2ds +from timing_txt2json import gen_timing4n, load_speed_json + import glob import math import json import sys from collections import OrderedDict -# Speed index: some sort of special value -SI_NONE = 0xFFFF - # prefix to make easier to track # models do not overlap between PIPs and WIREs PREFIX_W = 'WIRE_' PREFIX_P = 'PIP_' -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 - # Convert to (site, type, pip_junction, pip) - pipstr, speed_index = s.split(':') - speed_index = int(speed_index) - site, instance = pipstr.split('/') - #type, pip_junction, pip = others.split('.') - #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() - ret = [] - bads = 0 - for l in f: - # FIXME: hack - if 0 and 'CLK' in l: - continue - - l = l.strip() - if not l: - continue - parts = l.split(' ') - # FIXME: deal with these nodes - if len(parts) != 11: - bads += 1 - continue - net, src_bel, dst_bel, ico, fast_max, fast_min, slow_max, slow_min, pips, nodes, wires = parts - 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, - }) - 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 - speed_i2s = {} - for k, v in j['speed_model'].items(): - i = v['speed_index'] - if i != SI_NONE: - speed_i2s[i] = k - return j, speed_i2s - - # Verify the nodes and wires really do line up def vals2Adi_check(vals, names): print('Checking') @@ -113,131 +27,61 @@ def vals2Adi_check(vals, names): assert 0 -def vals2Adi(vals, speed_i2s, name_tr={}, name_drop=[], verbose=False): +def json2Ads(vals, verbose=False): + '''Convert timing4 JSON into Ads interconnect equations''' + def pip2speed(pip): - _site, _name, speed_index = pip - return PREFIX_P + speed_i2s[speed_index] + _site, _name, pip_name = pip + return PREFIX_P + pip_name def wire2speed(wire): - _site, _name, speed_index = wire - return PREFIX_W + speed_i2s[speed_index] + _site, _name, wire_name = wire + return PREFIX_W + wire_name - # Want this ordered - names = OrderedDict() - - 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) - # Highest count while still getting valid result - - # First index all of the given pip types - # Start out as set then convert to list to keep matrix order consistent - sys.stdout.write('Indexing delay elements ') - sys.stdout.flush() - progress = max(1, len(vals) / 100) - for vali, val in enumerate(vals): - if vali % progress == 0: - sys.stdout.write('.') - sys.stdout.flush() - odl = [(pip2speed(pip), None) for pip in val['pips']] - names.update(OrderedDict(odl)) - - odl = [(wire2speed(wire), None) for wire in val['wires']] - names.update(OrderedDict(odl)) - print(' done') - - # Apply transform - orig_names = len(names) - for k in (list(name_drop) + list(name_tr.keys())): - if k in names: - del names[k] - else: - print('WARNING: failed to remove %s' % k) - names.update(OrderedDict([(name, None) for name in name_tr.values()])) - print('Names tr %d => %d' % (orig_names, len(names))) - - # Make unique list - names = list(names.keys()) - name_s2i = {} - for namei, name in enumerate(names): - name_s2i[name] = namei - if verbose: - for name in names: - print('NAME: ', name) - for name in name_drop: - print('DROP: ', name) - for l, r in name_tr.items(): - print('TR: %s => %s' % (l, r)) - - # Now create a matrix with all of these delays - # Each row needs len(names) elements - # -2 means 2 elements present, 0 means absent - # (could hit same pip twice) - print('Creating delay element matrix w/ %d names' % len(names)) - Adi = [None for _i in range(len(vals))] - for vali, val in enumerate(vals): + print('Making equations') + def mk_row(val): def add_name(name): - if name in name_drop: - return - name = name_tr.get(name, name) - namei = name_s2i[name] - row_di[namei] = row_di.get(namei, 0) + 1 + row_ds[name] = row_ds.get(name, 0) + 1 - # Start with 0 occurances - #row = [0 for _i in range(len(names))] - row_di = {} + row_ds = {} - #print('pips: ', val['pips'] for pip in val['pips']: add_name(pip2speed(pip)) for wire in val['wires']: add_name(wire2speed(wire)) - #A_ub.append(row) - Adi[vali] = row_di + return row_ds - return Adi, names + return [mk_row(val) for val in vals] -# 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): +def load_Ads(speed_json_f, fn_ins): print('Loading data') _speedj, speed_i2s = load_speed_json(speed_json_f) - vals = [] - for avals in [load_timing3(f_in, name) for f_in, name in f_ins]: - vals.extend(avals) + for fn_in in fn_ins: + vals = list(gen_timing4n(fn_in, speed_i2s)) + Ads = json2Ads(vals) - Ads = vals2Ads(vals, speed_i2s) + def mkb(val): + t = val['t'] + return (t['fast_max'], t['fast_min'], t['slow_max'], t['slow_min']) - def mkb(val): - return ( - val['fast_max'], val['fast_min'], val['slow_max'], val['slow_min']) + bs = [mkb(val) for val in vals] + ico = [val['ico'] for val in vals] - b = [mkb(val) for val in vals] - ico = [val['ico'] for val in vals] - - return Ads, b, ico + for row_bs, row_ds, row_ico in zip(bs, Ads, ico): + yield row_bs, row_ds, row_ico -def run(speed_json_f, fout, f_ins, verbose=0, corner=None): - Ads, bs, ico = load_Ads(speed_json_f, f_ins) - +def run(speed_json_f, fout, fns_in, verbose=0, corner=None): fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n') - for row_bs, row_ds, row_ico in zip(bs, Ads, ico): + for row_bs, row_ds, row_ico in load_Ads(speed_json_f, fns_in): # XXX: consider removing ico column # its obsolete at this point - if not ico: + if not row_ico: continue # like: 123 456 120 450, 1 a, 2 b # first column has delay corners, followed by delay element count @@ -252,19 +96,19 @@ def main(): parser = argparse.ArgumentParser( description= - 'Convert obscure timing3.txt into more readable but roughly equivilent timing3i.csv (interconnect)' + 'Convert obscure timing4.txt into more readable but roughly equivilent timing4i.csv (interconnect)' ) parser.add_argument('--verbose', type=int, help='') # made a bulk conversion easier...keep? parser.add_argument( - '--auto-name', action='store_true', help='timing3.txt => timing3i.csv') + '--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 timing3i.csv file') - parser.add_argument('fns_in', nargs='+', help='Input timing3.txt files') + 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() @@ -284,12 +128,12 @@ def main(): fns_in = args.fns_in if not fns_in: - fns_in = glob.glob('specimen_*/timing3.txt') + fns_in = glob.glob('specimen_*/timing4.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], + fns_in=fns_in, verbose=args.verbose) diff --git a/fuzzers/007-timing/timing_txt2json.py b/fuzzers/007-timing/timing_txt2json.py new file mode 100644 index 00000000..573911e8 --- /dev/null +++ b/fuzzers/007-timing/timing_txt2json.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 + +from timfuz import Benchmark, A_di2ds +import glob +import math +import json +import sys +from collections import OrderedDict + +# Speed index: some sort of special value +SI_NONE = 0xFFFF + + +def parse_pip(s, speed_i2s): + # Entries like + # CLK_BUFG_REBUF_X60Y117/CLK_BUFG_REBUF.CLK_BUFG_REBUF_R_CK_GCLK0_BOT<<->>CLK_BUFG_REBUF_R_CK_GCLK0_TOP + # Convert to (site, type, pip_junction, pip) + pipstr, speed_index = s.split(':') + speed_index = int(speed_index) + site, instance = pipstr.split('/') + #type, pip_junction, pip = others.split('.') + #return (site, type, pip_junction, pip) + return site, instance, speed_i2s[int(speed_index)] + + +def parse_node(s): + node, nwires = s.split(':') + return node, int(nwires) + + +def parse_wire(s, speed_i2s): + # CLBLM_R_X3Y80/CLBLM_M_D6:952 + wirestr, speed_index = s.split(':') + site, instance = wirestr.split('/') + return site, instance, speed_i2s[int(speed_index)] + + +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' + ncols = len(header_want.split()) + + # src_bel dst_bel ico fast_max fast_min slow_max slow_min pips + header_got = f.readline().strip() + if header_got != header_want: + raise Exception("Unexpected columns") + + rets = 0 + # XXX: there were malformed lines, but think they are fixed now? + bads = 0 + net_lines = 0 + for l in f: + def group_line(): + ncols = len('lintype ico delays'.split()) + assert len(parts) == ncols + _lintype, ico, delays = parts + return int(ico), int(delays) + + 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 + pips = pips.split('|') + nodes = nodes.split('|') + wires = wires.split('|') + return { + 'net': net, + 'src': { + 'site': src_site, + 'site_pin': src_site_pin, + 'bel': src_bel, + 'bel_pin': src_bel_pin, + }, + 'dst': { + 'site': dst_site, + 'site_pin': dst_site_pin, + 'bel': dst_bel, + 'bel_pin': dst_bel_pin, + }, + 't': { + # ps + 'fast_max': int(fast_max), + 'fast_min': int(fast_min), + 'slow_max': int(slow_max), + 'slow_min': int(slow_min), + }, + 'ico': int(ico), + 'pips': [parse_pip(pip, speed_i2s) for pip in pips], + 'nodes': [parse_node(node) for node in nodes], + 'wires': [parse_wire(wire, speed_i2s) for wire in wires], + 'line': l, + } + + l = l.strip() + if not l: + continue + parts = l.split(' ') + lintype = parts[0] + + val = { + 'NET': net_line, + 'GROUP': group_line, + }[lintype]() + yield lintype, val + + rets += 1 + print( + ' load %s: %d bad, %d good, %u net lines' % + (fn, bads, rets, net_lines)) + + +def gen_timing4n(fn, speed_i2s): + '''Only generate nets''' + for lintype, val in gen_timing4(fn, speed_i2s): + if lintype == 'NET': + yield val + + +def gen_timing4i(fn, speed_i2s): + ''' + Like above, but aggregate ico + non-ico into single entries + Key these based on uniqueness of (src_bel, dst_bel) + + ico 0 is followed by 1 + They should probably even be in the same order + Maybe just assert that? + ''' + entries = {} + timgen = gen_timing4(fn, speed_i2s) + while True: + def get_ico(exp_ico): + ret = [] + try: + lintype, val = gen.next() + except StopIteration: + return None + assert lintype == 'GROUP' + ico, delays = val + assert ico == exp_ico + for _ in range(delays): + lintype, val = gen.next() + assert lintype == 'NET' + ret.append(val) + return ret + + ico0s = get_ico(0) + if ico0 is None: + break + ico1s = get_ico(1) + # TODO: verify this is actually true + assert len(ico0s) == len(ico1s) + + 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'] + + for ico0, ico1 in zip(ico0s, ico1s): + # TODO: verify this is actually true + # otherwise move to more complex algorithm + assert same_path(ico0, ico1) + # aggregate timing info as (ic0, ic1) into ico0 + ico0['t'] = ( + ico0['t'], + ico1['t'], + ) + yield ico0 + +def load_speed_json(f): + j = json.load(f) + # Index speed indexes to names + speed_i2s = {} + for k, v in j['speed_model'].items(): + i = v['speed_index'] + if i != SI_NONE: + speed_i2s[i] = k + 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) + + fnout = open(fout, 'w') + + vals = [] + for fn_in in fns_in: + for j in load_timing4(fn_in, speed_i2s): + fnout.write(json.dumps(j) + '\n') + + +def main(): + import argparse + + parser = argparse.ArgumentParser( + description= + 'Convert obscure timing4.txt into more readable but roughly equivilent timing4.json' + ) + + 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', '.json') + 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() From 649daab2a28e10b950acd3b0406acad243f015d2 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Mon, 24 Sep 2018 19:44:04 -0700 Subject: [PATCH 03/23] timfuz: site alpha Signed-off-by: John McMaster --- fuzzers/007-timing/checksub.py | 2 +- fuzzers/007-timing/csv_flat2group.py | 2 +- fuzzers/007-timing/csv_group2flat.py | 2 +- fuzzers/007-timing/projects/corner.mk | 10 +- fuzzers/007-timing/projects/generate.sh | 3 + fuzzers/007-timing/projects/is.mk | 74 +++++++++++ fuzzers/007-timing/projects/project.mk | 66 +++------- fuzzers/007-timing/projects/project.tcl | 7 +- fuzzers/007-timing/rref.py | 2 +- fuzzers/007-timing/solve_qor.py | 2 +- fuzzers/007-timing/tile_annotate.py | 88 +++++++++++-- fuzzers/007-timing/timfuz.py | 85 ++++++++---- fuzzers/007-timing/timfuz_solve.py | 4 +- fuzzers/007-timing/timgrid_vc2v.py | 87 +++++++----- fuzzers/007-timing/timing_txt2icsv.py | 13 +- fuzzers/007-timing/timing_txt2json.py | 45 ++++--- fuzzers/007-timing/timing_txt2scsv.py | 168 ++++++++++++++++++++++++ 17 files changed, 507 insertions(+), 153 deletions(-) create mode 100644 fuzzers/007-timing/projects/is.mk create mode 100644 fuzzers/007-timing/timing_txt2scsv.py 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() From 882257ea3dbc33eae682b41dddfc78ba129d6bcf Mon Sep 17 00:00:00 2001 From: John McMaster Date: Mon, 1 Oct 2018 19:13:37 -0700 Subject: [PATCH 04/23] timfuz: s/write_info3/write_info4 for timing4.txt Signed-off-by: John McMaster --- fuzzers/007-timing/projects/oneblinkw/generate.tcl | 2 +- fuzzers/007-timing/projects/picorv32/generate.tcl | 2 +- fuzzers/007-timing/projects/placelut/generate.tcl | 2 +- fuzzers/007-timing/projects/placelut_fb/generate.tcl | 2 +- fuzzers/007-timing/projects/placelut_ff_fb/generate.tcl | 2 +- fuzzers/007-timing/projects/project.tcl | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fuzzers/007-timing/projects/oneblinkw/generate.tcl b/fuzzers/007-timing/projects/oneblinkw/generate.tcl index e7946556..19a3b799 100644 --- a/fuzzers/007-timing/projects/oneblinkw/generate.tcl +++ b/fuzzers/007-timing/projects/oneblinkw/generate.tcl @@ -43,5 +43,5 @@ proc build_design {} { } build_design -write_info3 +write_info4 diff --git a/fuzzers/007-timing/projects/picorv32/generate.tcl b/fuzzers/007-timing/projects/picorv32/generate.tcl index a928a3bd..56a2ebea 100644 --- a/fuzzers/007-timing/projects/picorv32/generate.tcl +++ b/fuzzers/007-timing/projects/picorv32/generate.tcl @@ -44,5 +44,5 @@ proc build_design {} { } build_design -write_info3 +write_info4 diff --git a/fuzzers/007-timing/projects/placelut/generate.tcl b/fuzzers/007-timing/projects/placelut/generate.tcl index 0b536169..11152586 100644 --- a/fuzzers/007-timing/projects/placelut/generate.tcl +++ b/fuzzers/007-timing/projects/placelut/generate.tcl @@ -43,5 +43,5 @@ proc build_design {} { } build_design -write_info3 +write_info4 diff --git a/fuzzers/007-timing/projects/placelut_fb/generate.tcl b/fuzzers/007-timing/projects/placelut_fb/generate.tcl index 0b536169..11152586 100644 --- a/fuzzers/007-timing/projects/placelut_fb/generate.tcl +++ b/fuzzers/007-timing/projects/placelut_fb/generate.tcl @@ -43,5 +43,5 @@ proc build_design {} { } build_design -write_info3 +write_info4 diff --git a/fuzzers/007-timing/projects/placelut_ff_fb/generate.tcl b/fuzzers/007-timing/projects/placelut_ff_fb/generate.tcl index 0b536169..11152586 100644 --- a/fuzzers/007-timing/projects/placelut_ff_fb/generate.tcl +++ b/fuzzers/007-timing/projects/placelut_ff_fb/generate.tcl @@ -43,5 +43,5 @@ proc build_design {} { } build_design -write_info3 +write_info4 diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index a5fdfaad..0183b6dc 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -13,7 +13,7 @@ proc pin_bel {pin} { # Changed to group wires and nodes # This allows tracing the full path along with pips -proc write_info3 {} { +proc write_info4 {} { set outdir "." set fp [open "$outdir/timing4.txt" w] # bel as site/bel, so don't bother with site From e1e7c8005f7177cd7512195686b4ad8c1a661910 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Tue, 2 Oct 2018 16:48:12 -0700 Subject: [PATCH 05/23] timfuz: project.tcl rewrite W I P Signed-off-by: John McMaster --- fuzzers/007-timing/projects/project.tcl | 279 ++++++++++++------------ 1 file changed, 143 insertions(+), 136 deletions(-) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 0183b6dc..40671970 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -1,18 +1,42 @@ -proc pin_info {pin} { - set cell [get_cells -of_objects $pin] - set bel [get_bels -of_objects $cell] - set site [get_sites -of_objects $bel] - return "$site $bel" -} +# General notes: +# Observed nets with 1 to 2 source cell pins +# 2 example +# [get_pins -filter {DIRECTION == OUT} -of_objects $net] +# roi/dout_shr[0]_i_1/O: BEL, visible +# roi/dout_shr_reg[0]: no BEL, not visible, named after the net +# I don't understand what the relationship between these two is +# Maybe register retiming? +# Observed nets with 0 to 1 source bel pins +# 0 example: +# Recommendation: assume there is at most one source BEL +# Take that as the source BEL if it exists, otherwise don't do any source analysis -proc pin_bel {pin} { - set cell [get_cells -of_objects $pin] - set bel [get_bels -of_objects $cell] - return $bel -} +# get_timing_paths vs get_net_delays +# get_timing_paths is built on top of get_net_delays? +# has some different info, but is fixed on the slow max corner +# *possibly* reports slightly better info within a site, but would need to verify that -# Changed to group wires and nodes -# This allows tracing the full path along with pips +# Things safe to assume: +# -Each net has at least one delay? +# -Cell exists for every delay path +# -Each net has at least one destination BEL pin +# Things not safe to assume +# -Each net has at least one source BEL pin (ex: ) + + +proc list_format {l delim} { + set ret "" + set needspace 0 + foreach x $l { + if $needspace { + set ret "$ret$delim$x" + } else { + set ret "$x" + } + set needspace 1 + } + return $ret +} proc write_info4 {} { set outdir "." set fp [open "$outdir/timing4.txt" w] @@ -20,152 +44,129 @@ proc write_info4 {} { 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 set equations 0 - set site_src_nets 0 - set site_dst_nets 0 + set nets_no_src_bel 0 + set lines_no_int 0 + set lines_some_int 0 set neti 0 set nets [get_nets -hierarchical] + #set nets [get_nets roi/counter_reg_n_0_[8]] set nnets [llength $nets] foreach net $nets { incr neti - #if {$neti >= 10} { - # puts "Debug break" - # break - #} puts "Net $neti / $nnets: $net" + # The semantics of get_pins -leaf is kind of odd # When no passthrough LUTs exist, it has no effect # When passthrough LUT present: # -w/o -leaf: some pins + passthrough LUT pins # -w/ -leaf: different pins + passthrough LUT pins # With OUT filter this seems to be sufficient - set src_pin [get_pins -leaf -filter {DIRECTION == OUT} -of_objects $net] - set src_bel [pin_bel $src_pin] - set src_bel_pin [get_bel_pins -of_objects $src_pin] + set src_cell_pins [get_pins -leaf -filter {DIRECTION == OUT} -of_objects $net] + # 0 to 2 of these + # Seems when 2 of them they basically have the same bel + cell + # Make a best effort and move forward + set src_cell_pin [lindex $src_cell_pins 0] + set src_cell [get_cells -of_objects $src_cell_pin] + + # Only applicable if in a site + set src_bel [get_bels -of_objects $src_cell] + if {$src_bel eq ""} { + incr nets_no_src_bel + set src_site "" + set src_site_type "" + set src_site_pin "" + + # actually just throw out cases where source site doesn't exist + # these are very special, don't really have timing info anyway + # rework these later if they become problematic + continue + } + + set src_bel_pin [get_bel_pins -of_objects $src_cell_pin] set src_site [get_sites -of_objects $src_bel] - # Only one net driver - set src_site_pins [get_site_pins -filter {DIRECTION == OUT} -of_objects $net] - - # Sometimes this is empty for reasons that escape me - # Emitting direction doesn't help - if {[llength $src_site_pins] < 1} { - if $verbose { - puts " Ignoring site internal net" - } - incr site_src_nets - continue - } - set dst_site_pins_net [get_site_pins -filter {DIRECTION == IN} -of_objects $net] - if {[llength $dst_site_pins_net] < 1} { - puts " Skipping site internal source net" - 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" + # optional + set src_site_pin [get_site_pins -of_objects $src_cell_pin] + + # Report delays with and without interconnect only + foreach ico "0 1" { + if $ico { + set delays [get_net_delays -interconnect_only -of_objects $net] + } else { + set delays [get_net_delays -of_objects $net] + # only increment on one of the paths + set equations [expr "$equations + [llength $delays]"] } + puts $fp "GROUP $ico [llength $delays]" + foreach delay $delays { + #set delaystr [get_property NAME $delay] - # Run with and without interconnect only - foreach ico "0 1" { - set ico_flag "" - if $ico { - set ico_flag "-interconnect_only" - set delays [get_net_delays $ico_flag -of_objects $net] + set fast_max [get_property "FAST_MAX" $delay] + set fast_min [get_property "FAST_MIN" $delay] + set slow_max [get_property "SLOW_MAX" $delay] + set slow_min [get_property "SLOW_MIN" $delay] + + # Does this always exist? + set dst_cell_pin_str [get_property TO_PIN $delay] + set dst_cell_pin [get_pins $dst_cell_pin_str] + set dst_cell [get_cells -of_objects $dst_cell_pin] + set dst_bel [get_bels -of_objects $dst_cell] + set dst_bel_pin [get_bel_pins -of_objects $dst_cell_pin] + set dst_site [get_sites -of_objects $dst_bel] + set dst_site_type [get_property SITE_TYPE $dst_site] + # optional + set dst_site_pin [get_site_pins -of_objects $dst_cell_pin] + + # No fabric on this net? + # don't try querying sites and such + if {[get_nodes -of_objects $net] eq ""} { + # Already have everything we could query + # Just output + incr lines_no_int + puts $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 $pips_out $nodes_out $wires_out" + # At least some fabric exists + # Does dest BEL exist but not source BEL? + } elseif {$src_bel eq ""} { + puts "ERROR: should have been filtered" + return + # Ideally query from and to cell pins } else { - set delays [get_net_delays -of_objects $net] - } - puts $fp "GROUP $ico [llength $delays]" - foreach delay $delays { - set delaystr [get_property NAME $delay] - set dst_pins [get_property TO_PIN $delay] - set dst_pin [get_pins $dst_pins] - #puts " $delaystr: $src_pin => $dst_pin" - set dst_bel [pin_bel $dst_pin] - set dst_bel_pin [get_bel_pins -of_objects $dst_pin] - set dst_site [get_sites -of_objects $dst_bel] - if $verbose { - puts " Dest: $dst_pin at site $dst_site:$dst_bel" + # Nested list delimination precedence: " |:" + + # Pips in between + # Walk net, looking for interesting elements in between + # -from - (Optional) Defines the starting points of the pips to get + # from wire or site_pin + set pips [get_pips -of_objects $net -from $src_site_pin -to $dst_site_pin] + # Write pips w/ speed index + foreach pip $pips { + set speed_index [get_property SPEED_INDEX $pip] + lappend pips_out "$pip:$speed_index" } + set pips_out [list_format "$pips_out" "|"] - 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 + # Write nodes + # XXX: remove? don't think I care about these + # most for debugging at this point + set nodes [get_nodes -of_objects $net -from $src_site_pin -to $dst_site_pin] + foreach node $nodes { + set nwires [llength [get_wires -of_objects $node]] + lappend nodes_out "$node:$nwires" } - 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] - set fast_min [get_property "FAST_MIN" $delay] - set slow_max [get_property "SLOW_MAX" $delay] - set slow_min [get_property "SLOW_MIN" $delay] + set nodes_out [list_format "$nodes_out" "|"] - # Want: - # Site / BEL at src - # Site / BEL at dst - # Pips in between - # Walk net, looking for interesting elements in between - set pips [get_pips -of_objects $net -from $src_site_pin -to $dst_site_pin] - if $verbose { - foreach pip $pips { - puts " PIP $pip" - } - } - set nodes [get_nodes -of_objects $net -from $src_site_pin -to $dst_site_pin] - #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_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 " " - set needspace 0 - foreach pip $pips { - if $needspace { - puts -nonewline $fp "|" - } - set speed_index [get_property SPEED_INDEX $pip] - puts -nonewline $fp "$pip:$speed_index" - set needspace 1 - } - - # Write nodes - #set nodes_str [string map {" " "|"} $nodes] - #puts -nonewline $fp " $nodes_str" - puts -nonewline $fp " " - set needspace 0 - foreach node $nodes { - if $needspace { - puts -nonewline $fp "|" - } - set nwires [llength [get_wires -of_objects $node]] - puts -nonewline $fp "$node:$nwires" - set needspace 1 - } - - # Write wires - puts -nonewline $fp " " - set needspace 0 - foreach wire $wires { - if $needspace { - puts -nonewline $fp "|" - } - set speed_index [get_property SPEED_INDEX $wire] - puts -nonewline $fp "$wire:$speed_index" - set needspace 1 - } - - puts $fp "" - - incr equations - break + set wires [get_wires -of_objects $nodes] + # Write wires + foreach wire $wires { + set speed_index [get_property SPEED_INDEX $wire] + lappend wires_out "$wire:$speed_index" } + set wires_out [list_format "$wires_out" "|"] + + incr lines_some_int + puts $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 $pips_out $nodes_out $wires_out" } } } @@ -174,6 +175,12 @@ proc write_info4 {} { set TIME_taken [expr [clock clicks -milliseconds] - $TIME_start] puts "Took ms: $TIME_taken" puts "Generated $equations equations" - puts "Skipped $site_src_nets (+ $site_dst_nets) site nets" + puts "Nets: $nnets" + puts " No source bel: $nets_no_src_bel" + puts "Lines" + puts " No interconnect: $lines_no_int" + puts " Has interconnect: $lines_some_int" } +write_info4 + From 6f32de407e72a70278452331dc3d92f7aae087d1 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Tue, 2 Oct 2018 17:05:49 -0700 Subject: [PATCH 06/23] timfuz: project.tcl cleanup Signed-off-by: John McMaster --- fuzzers/007-timing/projects/project.tcl | 27 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 40671970..7d1acc71 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -45,18 +45,21 @@ proc write_info4 {} { set TIME_start [clock clicks -milliseconds] set equations 0 + set nets_no_src_cell 0 set nets_no_src_bel 0 set lines_no_int 0 set lines_some_int 0 set neti 0 set nets [get_nets -hierarchical] - #set nets [get_nets roi/counter_reg_n_0_[8]] + #set nets [get_nets clk] set nnets [llength $nets] foreach net $nets { incr neti puts "Net $neti / $nnets: $net" + # there are some special places like on IOB where you may not have a cell source pin + # this is due to special treatment where BEL vs SITE are blurred # The semantics of get_pins -leaf is kind of odd # When no passthrough LUTs exist, it has no effect # When passthrough LUT present: @@ -64,6 +67,12 @@ proc write_info4 {} { # -w/ -leaf: different pins + passthrough LUT pins # With OUT filter this seems to be sufficient set src_cell_pins [get_pins -leaf -filter {DIRECTION == OUT} -of_objects $net] + if {$src_cell_pins eq ""} { + incr nets_no_src_cell + # ex: IOB internal bel net + puts " SKIP: no source cell" + continue + } # 0 to 2 of these # Seems when 2 of them they basically have the same bel + cell # Make a best effort and move forward @@ -73,14 +82,11 @@ proc write_info4 {} { # Only applicable if in a site set src_bel [get_bels -of_objects $src_cell] if {$src_bel eq ""} { - incr nets_no_src_bel - set src_site "" - set src_site_type "" - set src_site_pin "" - - # actually just throw out cases where source site doesn't exist + # just throw out cases where source site doesn't exist # these are very special, don't really have timing info anyway # rework these later if they become problematic + incr nets_no_src_bel + puts " SKIP: no source bel" continue } @@ -151,6 +157,10 @@ proc write_info4 {} { # XXX: remove? don't think I care about these # most for debugging at this point set nodes [get_nodes -of_objects $net -from $src_site_pin -to $dst_site_pin] + if {$nodes eq ""} { + puts "ERROR: no nodes" + return + } foreach node $nodes { set nwires [llength [get_wires -of_objects $node]] lappend nodes_out "$node:$nwires" @@ -176,7 +186,8 @@ proc write_info4 {} { puts "Took ms: $TIME_taken" puts "Generated $equations equations" puts "Nets: $nnets" - puts " No source bel: $nets_no_src_bel" + puts " Skipped (no source cell): $nets_no_src_cell" + puts " Skipped (no source bel): $nets_no_src_bel" puts "Lines" puts " No interconnect: $lines_no_int" puts " Has interconnect: $lines_some_int" From b95eaba1f5ceddfee0717e3536d0e29ccc578582 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Tue, 2 Oct 2018 18:32:12 -0700 Subject: [PATCH 07/23] timfuz: site delay cleanup Signed-off-by: John McMaster --- fuzzers/007-timing/projects/project.tcl | 2 - fuzzers/007-timing/tile_annotate.py | 63 ++++++++++++++++++------- fuzzers/007-timing/timfuz.py | 42 +++++++++++++---- fuzzers/007-timing/timing_txt2scsv.py | 56 +++++++++++++++------- 4 files changed, 119 insertions(+), 44 deletions(-) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 7d1acc71..84354794 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -193,5 +193,3 @@ proc write_info4 {} { puts " Has interconnect: $lines_some_int" } -write_info4 - diff --git a/fuzzers/007-timing/tile_annotate.py b/fuzzers/007-timing/tile_annotate.py index c3771f8f..e989d9a2 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, PREFIX_W, PREFIX_P, PREFIX_SITEW, sitew_s2vals +from timfuz import loadc_Ads_bs, Ads2bounds, PREFIX_W, PREFIX_P, PREFIX_SW_EI, PREFIX_SW_EO, PREFIX_SW_I, sw_ei_s2vals, sw_eo_s2vals, sw_i_s2vals import sys import os @@ -50,32 +50,59 @@ def add_pip_wire(tilej, bounds, verbose=False): def add_sites(tilej, bounds): # XXX: no source of truth currently # is there a way we could get this? + # Naming discussion https://github.com/SymbiFlow/prjxray/pull/126 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 add_ei(): + for variable, bound in bounds[PREFIX_SW_EI].items(): + site_type, src_site_pin, dst_bel_type, dst_bel_pin = sw_ei_s2vals( + variable) + # ex: SLICEL:AX->AFF.D + k = ( + '%s:%s->%s.%s' % + (site_type, src_site_pin, dst_bel_type, dst_bel_pin)) + sitej.setdefault(site_type, {})[k] = bound + + def add_eo(): + for variable, bound in bounds[PREFIX_SW_EO].items(): + site_type, src_bel_type, src_bel_pin, dst_site_pin = sw_eo_s2vals( + variable) + # ex: SLICEL:AFF.Q->AQ + k = ( + '%s:%s.%s->%s' % + (site_type, src_bel_type, src_bel_pin, dst_site_pin)) + sitej.setdefault(site_type, {})[k] = bound + + def add_i(): + for variable, bound in bounds[PREFIX_SW_I].items(): + site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin = sw_i_s2vals( + variable) + # ex: SLICEL:LUT6.O2->AFF.D + k = ( + '%s:%s.%s->%s.%s' % + (site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin)) + sitej.setdefault(site_type, {})[k] = bound + + add_ei() + add_eo() + add_i() + print('Sites: added %u sites' % len(sitej)) + print('Added site wires') + print(' Site external in: %u' % len(bounds[PREFIX_SW_EI])) + print(' Site external out: %u' % len(bounds[PREFIX_SW_EO])) + print(' Site internal: %u' % len(bounds[PREFIX_SW_I])) def sep_bounds(bounds): pw = {} - sites = {} + sites = {PREFIX_SW_EI: {}, PREFIX_SW_EO: {}, PREFIX_SW_I: {}} for k, v in bounds.items(): prefix = k.split(':')[0] - if prefix == PREFIX_W: + if prefix in (PREFIX_W, PREFIX_P): pw[k] = v - elif prefix == PREFIX_P: - pw[k] = v - elif prefix == PREFIX_SITEW: - sites[k] = v + elif prefix in (PREFIX_SW_EI, PREFIX_SW_EO, PREFIX_SW_I): + sites[prefix][k] = v else: assert 0, 'Unknown delay: %s %s' % (k, prefix) return pw, sites diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index 8b7fa75d..20b175d8 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -21,20 +21,46 @@ from benchmark import Benchmark # models do not overlap between PIPs and WIREs PREFIX_W = 'WIRE' PREFIX_P = 'PIP' -# site wire (a to b) -PREFIX_SITEW = 'SITEW' +# extneral site wire (ie site a to b) +PREFIX_SW_EI = 'SITEW-EI' +PREFIX_SW_EO = 'SITEW-EO' +# internal site wire (ie bel a to b within a site) +PREFIX_SW_I = 'SITEW-I' -def sitew_vals2s(site_type, site_pin, bel_type, bel_pin): +def sw_ei_vals2s(site_type, src_site_pin, dst_bel, dst_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) + PREFIX_SW_EI, site_type, src_site_pin, dst_bel, dst_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 +def sw_ei_s2vals(s): + prefix, site_type, src_site_pin, dst_bel, dst_bel_pin = s.split(':') + assert prefix == PREFIX_SW_EI + return site_type, src_site_pin, dst_bel, dst_bel_pin + + +def sw_eo_vals2s(site_type, src_bel, src_bel_pin, dst_site_pin): + return '%s:%s:%s:%s:%s' % ( + PREFIX_SW_EO, site_type, src_bel, src_bel_pin, dst_site_pin) + + +def sw_eo_s2vals(s): + prefix, site_type, src_bel, src_bel_pin, dst_site_pin = s.split(':') + assert prefix == PREFIX_SW_EO + return site_type, src_bel, src_bel_pin, dst_site_pin + + +def sw_i_vals2s(site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin): + return '%s:%s:%s:%s:%s:%s' % ( + PREFIX_SW_I, site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin) + + +def sw_i_s2vals(s): + prefix, site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin = s.split( + ':') + assert prefix == PREFIX_SW_I + return site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin # Equations are filtered out until nothing is left diff --git a/fuzzers/007-timing/timing_txt2scsv.py b/fuzzers/007-timing/timing_txt2scsv.py index 51c78ab4..e9a6f9f0 100644 --- a/fuzzers/007-timing/timing_txt2scsv.py +++ b/fuzzers/007-timing/timing_txt2scsv.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from timfuz import Benchmark, A_di2ds, PREFIX_SITEW, sitew_vals2s +from timfuz import Benchmark, A_di2ds, sw_ei_vals2s, sw_eo_vals2s, sw_i_vals2s from timing_txt2json import gen_timing4a, load_speed_json import glob @@ -59,8 +59,13 @@ def sd_parts(sd): 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 + + site_pin_str = sd['site_pin'] + if site_pin_str: + site, site_pin = sd['site_pin'].split('/') + assert sd['site_pin'] == sd['site'] + '/' + site_pin + else: + site_pin = None return site_type, site_pin, bel_type, bel_pin @@ -87,28 +92,47 @@ def run(speed_json_f, fout, fns_in, verbose=0, corner=None): 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: + if not val['src']['site_pin'] and not val['dst']['site_pin']: # add one delay model for the path - assert 0, 'FIXME: inter site transaction' - row_ds = {'SITEI_BLAH': None} - else: + # XXX: can these be solved exactly? + # might still have fanout and such + + src_site_type, _src_site_pin, src_bel_type, src_bel_pin = sd_parts( + val['src']) + dst_site_type, _dst_site_pin, dst_bel_type, dst_bel_pin = sd_parts( + val['dst']) + assert src_site_type == dst_site_type + assert (src_bel_type, src_bel_pin) != (dst_bel_type, dst_bel_pin) + + k = sw_i_vals2s( + src_site_type, src_bel_type, src_bel_pin, dst_bel_type, + dst_bel_pin) + row_ds = {k: 1} + elif val['src']['site_pin'] and val['dst']['site_pin']: # 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 + def add_dst_delay(): + sd = val['dst'] + site_type, src_site_pin, dst_bel, dst_bel_pin = sd_parts(sd) + k = sw_ei_vals2s(site_type, src_site_pin, dst_bel, dst_bel_pin) assert k not in row_ds row_ds[k] = 1 - add_delay(val['src']) - add_delay(val['dst']) + def add_src_delay(): + sd = val['src'] + site_type, dst_site_pin, src_bel, src_bel_pin = sd_parts(sd) + k = sw_eo_vals2s(site_type, src_bel, src_bel_pin, dst_site_pin) + assert k not in row_ds + row_ds[k] = 1 + + add_dst_delay() + add_src_delay() + else: + # dropped by the tcl script + raise Exception("FIXME: handle destination but no source") row_ico = 0 items = [str(row_ico), bstr] From 6df3b2b364581951aec6aa8b313590bf25ce86b9 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:17:54 -0700 Subject: [PATCH 08/23] timfuz corner.mk: remove unused --sub-json invocations obsoleted by grouped.csv Signed-off-by: John McMaster --- fuzzers/007-timing/projects/corner.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/007-timing/projects/corner.mk b/fuzzers/007-timing/projects/corner.mk index bb3b76f6..d7b27a83 100644 --- a/fuzzers/007-timing/projects/corner.mk +++ b/fuzzers/007-timing/projects/corner.mk @@ -28,12 +28,12 @@ $(BUILD_DIR)/checksub: $(BUILD_DIR)/$(CORNER)/leastsq.csv: $(BUILD_DIR)/sub.json $(BUILD_DIR)/grouped.csv $(BUILD_DIR)/checksub $(BUILD_DIR)/$(CORNER) # Create a rough timing model that approximately fits the given paths - python3 $(TIMFUZ_DIR)/solve_leastsq.py --sub-json $(BUILD_DIR)/sub.json $(BUILD_DIR)/grouped.csv --corner $(CORNER) --out $(BUILD_DIR)/$(CORNER)/leastsq.csv.tmp + python3 $(TIMFUZ_DIR)/solve_leastsq.py $(BUILD_DIR)/grouped.csv --corner $(CORNER) --out $(BUILD_DIR)/$(CORNER)/leastsq.csv.tmp mv $(BUILD_DIR)/$(CORNER)/leastsq.csv.tmp $(BUILD_DIR)/$(CORNER)/leastsq.csv $(BUILD_DIR)/$(CORNER)/linprog.csv: $(BUILD_DIR)/$(CORNER)/leastsq.csv $(BUILD_DIR)/grouped.csv # Tweak rough timing model, making sure all constraints are satisfied - ALLOW_ZERO_EQN=$(ALLOW_ZERO_EQN) python3 $(TIMFUZ_DIR)/solve_linprog.py --sub-json $(BUILD_DIR)/sub.json --bounds-csv $(BUILD_DIR)/$(CORNER)/leastsq.csv --massage $(BUILD_DIR)/grouped.csv --corner $(CORNER) --out $(BUILD_DIR)/$(CORNER)/linprog.csv.tmp + ALLOW_ZERO_EQN=$(ALLOW_ZERO_EQN) python3 $(TIMFUZ_DIR)/solve_linprog.py --bounds-csv $(BUILD_DIR)/$(CORNER)/leastsq.csv --massage $(BUILD_DIR)/grouped.csv --corner $(CORNER) --out $(BUILD_DIR)/$(CORNER)/linprog.csv.tmp mv $(BUILD_DIR)/$(CORNER)/linprog.csv.tmp $(BUILD_DIR)/$(CORNER)/linprog.csv $(BUILD_DIR)/$(CORNER)/flat.csv: $(BUILD_DIR)/$(CORNER)/linprog.csv From 409dd72f3b00305f5e6cba241ebc7d5d06848315 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:20:23 -0700 Subject: [PATCH 09/23] genheader.sh: add extra check for when script is sourced Signed-off-by: John McMaster --- utils/genheader.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/utils/genheader.sh b/utils/genheader.sh index 7c9bba0f..71766634 100644 --- a/utils/genheader.sh +++ b/utils/genheader.sh @@ -7,8 +7,9 @@ fi set -ex -test $# = 1 -test ! -e $1 -mkdir $1 -cd $1 +# for some reason on sourced script set -e doesn't work +test $# = 1 || exit 1 +test ! -e "$1" +mkdir "$1" +cd "$1" From 7f61ff99fd0eef493c808476f3612a2dc4f048c7 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:20:41 -0700 Subject: [PATCH 10/23] timing_txt2scsv.py: remove dead code Signed-off-by: John McMaster --- fuzzers/007-timing/timing_txt2scsv.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/fuzzers/007-timing/timing_txt2scsv.py b/fuzzers/007-timing/timing_txt2scsv.py index e9a6f9f0..1dedb470 100644 --- a/fuzzers/007-timing/timing_txt2scsv.py +++ b/fuzzers/007-timing/timing_txt2scsv.py @@ -25,31 +25,6 @@ def gen_diffs(speed_json_f, fns_in): 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''' From f023e0dcf0edf278a8b44f70c8317b8507ed5be5 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:26:41 -0700 Subject: [PATCH 11/23] solve_leastsq.py:mkestimate(): ignore 0 delay rows, assert non-trivial seed Signed-off-by: John McMaster --- fuzzers/007-timing/solve_leastsq.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/fuzzers/007-timing/solve_leastsq.py b/fuzzers/007-timing/solve_leastsq.py index 8f01b54e..3e6f5742 100644 --- a/fuzzers/007-timing/solve_leastsq.py +++ b/fuzzers/007-timing/solve_leastsq.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from timfuz import Benchmark, load_sub, corner_s2i, acorner2csv +from timfuz import Benchmark, load_sub import timfuz import numpy as np import math @@ -22,11 +22,20 @@ def mkestimate(Anp, b): x0 = np.array([1e3 for _x in range(cols)]) for row_np, row_b in zip(Anp, b): for coli, val in enumerate(row_np): - if val: - # Scale by number occurances - ub = row_b / val - if ub >= 0: - x0[coli] = min(x0[coli], ub) + # favor non-trivial values + if val <= 0: + continue + # Scale by number occurances + ub = row_b / val + if ub <= 0: + continue + if x0[coli] == 0: + x0[coli] = ub + else: + x0[coli] = min(x0[coli], ub) + # reject solutions that don't provide a seed value + # these lead to bad optimizations + assert sum(x0) != 0 return x0 From f4faa272437b91a3bcc59c7b912a7ed834c62406 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:27:07 -0700 Subject: [PATCH 12/23] timfuz: timing.txt "," delim, line_net procs (cleanup + bug fix) Signed-off-by: John McMaster --- fuzzers/007-timing/projects/project.tcl | 45 +++++++++-- fuzzers/007-timing/timfuz_massage.py | 11 +-- fuzzers/007-timing/timfuz_solve.py | 5 +- fuzzers/007-timing/timing_txt2json.py | 102 +++++++----------------- 4 files changed, 77 insertions(+), 86 deletions(-) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 84354794..38ba25ed 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -37,11 +37,32 @@ proc list_format {l delim} { } return $ret } + +proc line_net_external {fp 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_out nodes_out wires_out} { + puts $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,$pips_out,$nodes_out,$wires_out" +} + +proc line_net_internal {fp net site site_type src_bel src_bel_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min} { + set src_site $site + set src_site_type $site_type + set src_site_pin "" + + set dst_site $site + set dst_site_type $site_type + set dst_site_pin "" + + set pips_out "" + set nodes_out "" + set wires_out "" + + puts $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,$pips_out,$nodes_out,$wires_out" +} + proc write_info4 {} { 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_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" + 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 equations 0 @@ -52,6 +73,7 @@ proc write_info4 {} { set neti 0 set nets [get_nets -hierarchical] #set nets [get_nets clk] + #set nets [get_nets roi/counter[0]_i_2_n_0] set nnets [llength $nets] foreach net $nets { incr neti @@ -105,7 +127,7 @@ proc write_info4 {} { # only increment on one of the paths set equations [expr "$equations + [llength $delays]"] } - puts $fp "GROUP $ico [llength $delays]" + puts $fp "GROUP,$ico,[llength $delays]" foreach delay $delays { #set delaystr [get_property NAME $delay] @@ -128,10 +150,19 @@ proc write_info4 {} { # No fabric on this net? # don't try querying sites and such if {[get_nodes -of_objects $net] eq ""} { + if {$src_site ne $dst_site} { + puts "ERROR: site mismatch" + return + } + if {$src_site_type ne $dst_site_type} { + puts "ERROR: site mismatch" + return + } + # Already have everything we could query # Just output incr lines_no_int - puts $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 $pips_out $nodes_out $wires_out" + line_net_internal $fp $net $src_site $src_site_type $src_bel $src_bel_pin $dst_bel $dst_bel_pin $ico $fast_max $fast_min $slow_max $slow_min # At least some fabric exists # Does dest BEL exist but not source BEL? } elseif {$src_bel eq ""} { @@ -139,7 +170,7 @@ proc write_info4 {} { return # Ideally query from and to cell pins } else { - # Nested list delimination precedence: " |:" + # Nested list delimination precedence: ",|:" # Pips in between # Walk net, looking for interesting elements in between @@ -176,7 +207,7 @@ proc write_info4 {} { set wires_out [list_format "$wires_out" "|"] incr lines_some_int - puts $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 $pips_out $nodes_out $wires_out" + line_net_external $fp $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_out $nodes_out $wires_out } } } @@ -193,3 +224,7 @@ proc write_info4 {} { puts " Has interconnect: $lines_some_int" } +# for debugging +# source ../project.tcl +# write_info4 + diff --git a/fuzzers/007-timing/timfuz_massage.py b/fuzzers/007-timing/timfuz_massage.py index 90d318f3..d64853c2 100644 --- a/fuzzers/007-timing/timfuz_massage.py +++ b/fuzzers/007-timing/timfuz_massage.py @@ -220,7 +220,9 @@ def derive_eq_by_col(Ads, b_ub, verbose=0, keep_orig=True): # iteratively increasing column limit until all columns are added -def massage_equations(Ads, b, verbose=False, corner=None): +def massage_equations( + Ads, b, verbose=False, corner=None, iter_lim=1, col_lim=100000): + #col_lim = 15 ''' Subtract equations from each other to generate additional constraints Helps provide additional guidance to solver for realistic delays @@ -262,7 +264,6 @@ def massage_equations(Ads, b, verbose=False, corner=None): # Each iteration one more column is allowed until all columns are included # (and the system is stable) - col_lim = 15 di = 0 while True: print @@ -298,13 +299,13 @@ def massage_equations(Ads, b, verbose=False, corner=None): col_dist(Ads, 'derive done iter %d, lim %d' % (di, col_lim), lim=12) rows = len(Ads) + di += 1 + dend = len(b) # possible that a new equation was generated and taken away, but close enough - if n_orig == len(b) and col_lim >= cols: + if n_orig == len(b) and col_lim >= cols or di >= iter_lim: break col_lim += col_lim / 5 - di += 1 - dend = len(b) print('') print('Derive net: %d => %d' % (dstart, dend)) print('') diff --git a/fuzzers/007-timing/timfuz_solve.py b/fuzzers/007-timing/timfuz_solve.py index c37a6228..3e09cf07 100644 --- a/fuzzers/007-timing/timfuz_solve.py +++ b/fuzzers/007-timing/timfuz_solve.py @@ -150,7 +150,10 @@ def solve_save(outfn, xvals, names, corner, save_zero=True, verbose=False): print( 'Wrote: %u / %u constrained delays, %u zeros' % (nonzeros, len(names), zeros)) - assert nonzeros, 'Failed to estimate delay' + # max only...min corner seems to like 0 + # see https://github.com/SymbiFlow/prjxray/issues/136 + if 'max' in corner: + assert nonzeros, 'Failed to estimate delay' def run( diff --git a/fuzzers/007-timing/timing_txt2json.py b/fuzzers/007-timing/timing_txt2json.py index 97024985..b064b99b 100644 --- a/fuzzers/007-timing/timing_txt2json.py +++ b/fuzzers/007-timing/timing_txt2json.py @@ -23,11 +23,23 @@ def parse_pip(s, speed_i2s): return site, instance, speed_i2s[int(speed_index)] +def parse_pips(pips, speed_i2s): + if not pips: + return [] + return [parse_pip(pip, speed_i2s) for pip in pips.split('|')] + + def parse_node(s): node, nwires = s.split(':') return node, int(nwires) +def parse_nodes(nodes): + if not nodes: + return [] + return [parse_node(node) for node in nodes.split('|')] + + def parse_wire(s, speed_i2s): # CLBLM_R_X3Y80/CLBLM_M_D6:952 wirestr, speed_index = s.split(':') @@ -35,10 +47,16 @@ def parse_wire(s, speed_i2s): return site, instance, speed_i2s[int(speed_index)] +def parse_wires(wires, speed_i2s): + if not wires: + return [] + return [parse_wire(wire, speed_i2s) for wire in wires.split('|')] + + def gen_timing4(fn, speed_i2s): f = open(fn, 'r') - 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()) + 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 header_got = f.readline().strip() @@ -52,17 +70,15 @@ def gen_timing4(fn, speed_i2s): for l in f: def group_line(): - ncols = len('lintype ico delays'.split()) + ncols = len('lintype,ico,delays'.split(',')) assert len(parts) == ncols _lintype, ico, delays = parts return int(ico), int(delays) def net_line(): - assert len(parts) == ncols + assert len(parts) == ncols, "Expected %u parts, got %u" % ( + ncols, len(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': { @@ -87,16 +103,16 @@ def gen_timing4(fn, speed_i2s): 'slow_min': int(slow_min), }, 'ico': int(ico), - 'pips': [parse_pip(pip, speed_i2s) for pip in pips], - 'nodes': [parse_node(node) for node in nodes], - 'wires': [parse_wire(wire, speed_i2s) for wire in wires], + 'pips': parse_pips(pips, speed_i2s), + 'nodes': parse_nodes(nodes), + 'wires': parse_wires(wires, speed_i2s), 'line': l, } l = l.strip() if not l: continue - parts = l.split(' ') + parts = l.split(',') lintype = parts[0] val = { @@ -180,67 +196,3 @@ def load_speed_json(f): if i != SI_NONE: speed_i2s[i] = k 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) - - fnout = open(fout, 'w') - - vals = [] - for fn_in in fns_in: - for j in load_timing4(fn_in, speed_i2s): - fnout.write(json.dumps(j) + '\n') - - -def main(): - import argparse - - parser = argparse.ArgumentParser( - description= - 'Convert obscure timing4.txt into more readable but roughly equivilent timing4.json' - ) - - 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', '.json') - 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() -''' From d7e972f83da51f6dc94d326714f6923bc38035f5 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:31:21 -0700 Subject: [PATCH 13/23] timfuz: move tests to minitest dir Signed-off-by: John McMaster --- fuzzers/007-timing/{ => minitest}/test_unique/.gitignore | 0 fuzzers/007-timing/{ => minitest}/test_unique/Makefile | 0 fuzzers/007-timing/{ => minitest}/test_unique/README.md | 0 fuzzers/007-timing/{ => minitest}/test_unique/generate.sh | 0 fuzzers/007-timing/{ => minitest}/test_unique/generate.tcl | 0 fuzzers/007-timing/{ => minitest}/test_unique/node_unique.py | 0 fuzzers/007-timing/{ => minitest}/test_unique/perf_test.py | 0 fuzzers/007-timing/{ => minitest}/test_unique/pip_unique.py | 0 fuzzers/007-timing/{ => minitest}/test_unique/top.v | 0 fuzzers/007-timing/{ => minitest}/test_unique/wire_unique.py | 0 fuzzers/007-timing/{ => minitest}/test_zero/README.md | 0 fuzzers/007-timing/{ => minitest}/test_zero/process.py | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename fuzzers/007-timing/{ => minitest}/test_unique/.gitignore (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/Makefile (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/README.md (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/generate.sh (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/generate.tcl (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/node_unique.py (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/perf_test.py (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/pip_unique.py (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/top.v (100%) rename fuzzers/007-timing/{ => minitest}/test_unique/wire_unique.py (100%) rename fuzzers/007-timing/{ => minitest}/test_zero/README.md (100%) rename fuzzers/007-timing/{ => minitest}/test_zero/process.py (100%) diff --git a/fuzzers/007-timing/test_unique/.gitignore b/fuzzers/007-timing/minitest/test_unique/.gitignore similarity index 100% rename from fuzzers/007-timing/test_unique/.gitignore rename to fuzzers/007-timing/minitest/test_unique/.gitignore diff --git a/fuzzers/007-timing/test_unique/Makefile b/fuzzers/007-timing/minitest/test_unique/Makefile similarity index 100% rename from fuzzers/007-timing/test_unique/Makefile rename to fuzzers/007-timing/minitest/test_unique/Makefile diff --git a/fuzzers/007-timing/test_unique/README.md b/fuzzers/007-timing/minitest/test_unique/README.md similarity index 100% rename from fuzzers/007-timing/test_unique/README.md rename to fuzzers/007-timing/minitest/test_unique/README.md diff --git a/fuzzers/007-timing/test_unique/generate.sh b/fuzzers/007-timing/minitest/test_unique/generate.sh similarity index 100% rename from fuzzers/007-timing/test_unique/generate.sh rename to fuzzers/007-timing/minitest/test_unique/generate.sh diff --git a/fuzzers/007-timing/test_unique/generate.tcl b/fuzzers/007-timing/minitest/test_unique/generate.tcl similarity index 100% rename from fuzzers/007-timing/test_unique/generate.tcl rename to fuzzers/007-timing/minitest/test_unique/generate.tcl diff --git a/fuzzers/007-timing/test_unique/node_unique.py b/fuzzers/007-timing/minitest/test_unique/node_unique.py similarity index 100% rename from fuzzers/007-timing/test_unique/node_unique.py rename to fuzzers/007-timing/minitest/test_unique/node_unique.py diff --git a/fuzzers/007-timing/test_unique/perf_test.py b/fuzzers/007-timing/minitest/test_unique/perf_test.py similarity index 100% rename from fuzzers/007-timing/test_unique/perf_test.py rename to fuzzers/007-timing/minitest/test_unique/perf_test.py diff --git a/fuzzers/007-timing/test_unique/pip_unique.py b/fuzzers/007-timing/minitest/test_unique/pip_unique.py similarity index 100% rename from fuzzers/007-timing/test_unique/pip_unique.py rename to fuzzers/007-timing/minitest/test_unique/pip_unique.py diff --git a/fuzzers/007-timing/test_unique/top.v b/fuzzers/007-timing/minitest/test_unique/top.v similarity index 100% rename from fuzzers/007-timing/test_unique/top.v rename to fuzzers/007-timing/minitest/test_unique/top.v diff --git a/fuzzers/007-timing/test_unique/wire_unique.py b/fuzzers/007-timing/minitest/test_unique/wire_unique.py similarity index 100% rename from fuzzers/007-timing/test_unique/wire_unique.py rename to fuzzers/007-timing/minitest/test_unique/wire_unique.py diff --git a/fuzzers/007-timing/test_zero/README.md b/fuzzers/007-timing/minitest/test_zero/README.md similarity index 100% rename from fuzzers/007-timing/test_zero/README.md rename to fuzzers/007-timing/minitest/test_zero/README.md diff --git a/fuzzers/007-timing/test_zero/process.py b/fuzzers/007-timing/minitest/test_zero/process.py similarity index 100% rename from fuzzers/007-timing/test_zero/process.py rename to fuzzers/007-timing/minitest/test_zero/process.py From 0fcd6958d896f1ad15726a924f8f3fe365673354 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:31:39 -0700 Subject: [PATCH 14/23] timfuz: linprog simple example Signed-off-by: John McMaster --- fuzzers/007-timing/minitest/test_linprog.py | 139 ++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 fuzzers/007-timing/minitest/test_linprog.py diff --git a/fuzzers/007-timing/minitest/test_linprog.py b/fuzzers/007-timing/minitest/test_linprog.py new file mode 100644 index 00000000..78c8c24a --- /dev/null +++ b/fuzzers/007-timing/minitest/test_linprog.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +import scipy.optimize as optimize +import numpy as np +import glob +import json +import math +import sys +import os +import time + + +def run_max(Anp, b, verbose=False): + cols = len(Anp[0]) + + b_ub = -1.0 * b + A_ub = [-1.0 * x for x in Anp] + + res = optimize.linprog( + c=[1 for _i in range(cols)], + A_ub=A_ub, + b_ub=b_ub, + bounds=(0, None), + options={ + "disp": True, + 'maxiter': 1000000, + 'bland': True, + 'tol': 1e-6, + }) + nonzeros = 0 + print('Ran') + if res.success: + print('Result sample (%d elements)' % (len(res.x))) + plim = 3 + for xi, x in enumerate(res.x): + nonzero = x >= 0.001 + if nonzero: + nonzeros += 1 + if nonzero and (verbose or ( + (nonzeros < 100 or nonzeros % 20 == 0) and nonzeros <= plim)): + print(' % 4u % 10.1f' % (xi, x)) + print('Delay on %d / %d' % (nonzeros, len(res.x))) + + +def run_min(Anp, b, verbose=False): + cols = len(Anp[0]) + + b_ub = b + A_ub = Anp + + res = optimize.linprog( + c=[-1 for _i in range(cols)], + A_ub=A_ub, + b_ub=b_ub, + bounds=(0, None), + options={ + "disp": True, + 'maxiter': 1000000, + 'bland': True, + 'tol': 1e-6, + }) + nonzeros = 0 + print('Ran') + if res.success: + print('Result sample (%d elements)' % (len(res.x))) + plim = 3 + for xi, x in enumerate(res.x): + nonzero = x >= 0.001 + if nonzero: + nonzeros += 1 + if nonzero and (verbose or ( + (nonzeros < 100 or nonzeros % 20 == 0) and nonzeros <= plim)): + print(' % 4u % 10.1f' % (xi, x)) + print('Delay on %d / %d' % (nonzeros, len(res.x))) + + +def run(verbose=False): + ''' + 1 * x0 = 10 + 1 * x0 + 1 * x1 = 100 + 1 * x0 = 40 + 2 * x1 = 140 + ''' + Anp = np.array([ + [1, 0], + [1, 1], + [1, 0], + [0, 2], + ]) + b = np.array([ + 10, + 100, + 40, + 140, + ]) + ''' + Max + Optimization terminated successfully. + Current function value: 110.000000 + Iterations: 4 + Ran + Result sample (2 elements) + 0 40.0 + 1 70.0 + Delay on 2 / 2 + ''' + print('Max') + run_max(Anp, b) + print('') + print('') + print('') + ''' + Min + Optimization terminated successfully. + Current function value: -80.000000 + Iterations: 2 + Ran + Result sample (2 elements) + 0 10.0 + 1 70.0 + Delay on 2 / 2 + ''' + print('Min') + run_min(Anp, b) + + +def main(): + import argparse + + parser = argparse.ArgumentParser(description='Test') + + parser.add_argument('--verbose', action='store_true', help='') + args = parser.parse_args() + + run(verbose=args.verbose) + + +if __name__ == '__main__': + main() From 8d5a1611d372b7e645677f8e7144ea23d407fc15 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:32:59 -0700 Subject: [PATCH 15/23] timfuz: BEL multiple output pin test script Signed-off-by: John McMaster --- .../007-timing/minitest/test_cell_opins.tcl | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 fuzzers/007-timing/minitest/test_cell_opins.tcl diff --git a/fuzzers/007-timing/minitest/test_cell_opins.tcl b/fuzzers/007-timing/minitest/test_cell_opins.tcl new file mode 100644 index 00000000..a62fb407 --- /dev/null +++ b/fuzzers/007-timing/minitest/test_cell_opins.tcl @@ -0,0 +1,30 @@ +# Demonstrates that cells can have 0 to 2 BEL output pins +# roi/dout_shr_reg[0]: roi/dout_shr[0]_i_1/O roi/dout_shr_reg[0] + +set TIME_start [clock clicks -milliseconds] +set site_src_nets 0 +set neti 0 +set nets [get_nets -hierarchical] +set nnets [llength $nets] +set opins_zero 0 +set opins_multi 0 +foreach net $nets { + incr neti + puts "Net $neti / $nnets: $net" + + set out_pins [get_pins -filter {DIRECTION == OUT} -of_objects $net] + set npins [llength $out_pins] + if {$npins == 0} { + puts " $net zero source pins: $src_pin" + incr opins_zero + } + if {$npins > 1} { + puts " $net multi source pins: $src_pin" + incr opins_multi + } +} +set TIME_taken [expr [clock clicks -milliseconds] - $TIME_start] +puts "Took ms: $TIME_taken" +puts "Result: $opins_zero / $nnets zero" +puts "Result: $opins_multi / $nnets multi" + From 0a7c68b5a01ef7987a02d2aade2845bf4dea5be5 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 16:48:54 -0700 Subject: [PATCH 16/23] timfuz stats: site wires report solved in addition to covered Signed-off-by: John McMaster --- fuzzers/007-timing/timfuz.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index 20b175d8..ba5822e7 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -803,11 +803,15 @@ def tilej_stats(tilej): return stats def site_stats(): - sitej = tilej['sites'] - return { - 'wires': sum([len(v) for v in sitej.values()]), - 'sites': len(sitej) + sitesj = tilej['sites'] + ret = { + 'sites': len(sitesj), + 'wires': sum([len(wiresj) for wiresj in sitesj.values()]), + 'wires_solved': {}, } + for corneri in corner_s2i.values(): + ret['wires_solved'][corneri] = sum([sum([1 if site_wire[corneri] else 0 for site_wire in sitej.values()]) for sitej in sitesj.values()]) + return ret tstats = tile_stats() sstats = site_stats() @@ -822,8 +826,8 @@ def tilej_stats(tilej): ' %s: %u / %u solved, %u / %u covered' % (etype, solved, net, covered, net)) print( - ' sites: %u sites, %u site wires' % - (sstats['sites'], sstats['wires'])) + ' sites: %u sites w/ %u solved / %u covered site wires' % + (sstats['sites'], sstats['wires_solved'][corneri], sstats['wires'])) def load_bounds(bounds_csv, corner): From 85862394ced9868a755571885a49aa72da5d6aa1 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 17:19:49 -0700 Subject: [PATCH 17/23] timfuz: remove nodes from timing4.txt Signed-off-by: John McMaster --- fuzzers/007-timing/projects/project.tcl | 19 +++++-------------- fuzzers/007-timing/timing_txt2json.py | 16 ++-------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 38ba25ed..7e80e20e 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -38,8 +38,8 @@ proc list_format {l delim} { return $ret } -proc line_net_external {fp 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_out nodes_out wires_out} { - puts $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,$pips_out,$nodes_out,$wires_out" +proc line_net_external {fp 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_out wires_out} { + puts $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,$pips_out,$wires_out" } proc line_net_internal {fp net site site_type src_bel src_bel_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min} { @@ -52,17 +52,16 @@ proc line_net_internal {fp net site site_type src_bel src_bel_pin dst_bel dst_be set dst_site_pin "" set pips_out "" - set nodes_out "" set wires_out "" - puts $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,$pips_out,$nodes_out,$wires_out" + puts $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,$pips_out,$wires_out" } proc write_info4 {} { 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_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" + 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,wires" set TIME_start [clock clicks -milliseconds] set equations 0 @@ -184,19 +183,11 @@ proc write_info4 {} { } set pips_out [list_format "$pips_out" "|"] - # Write nodes - # XXX: remove? don't think I care about these - # most for debugging at this point set nodes [get_nodes -of_objects $net -from $src_site_pin -to $dst_site_pin] if {$nodes eq ""} { puts "ERROR: no nodes" return } - foreach node $nodes { - set nwires [llength [get_wires -of_objects $node]] - lappend nodes_out "$node:$nwires" - } - set nodes_out [list_format "$nodes_out" "|"] set wires [get_wires -of_objects $nodes] # Write wires @@ -207,7 +198,7 @@ proc write_info4 {} { set wires_out [list_format "$wires_out" "|"] incr lines_some_int - line_net_external $fp $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_out $nodes_out $wires_out + line_net_external $fp $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_out $wires_out } } } diff --git a/fuzzers/007-timing/timing_txt2json.py b/fuzzers/007-timing/timing_txt2json.py index b064b99b..c20c8c9f 100644 --- a/fuzzers/007-timing/timing_txt2json.py +++ b/fuzzers/007-timing/timing_txt2json.py @@ -29,17 +29,6 @@ def parse_pips(pips, speed_i2s): return [parse_pip(pip, speed_i2s) for pip in pips.split('|')] -def parse_node(s): - node, nwires = s.split(':') - return node, int(nwires) - - -def parse_nodes(nodes): - if not nodes: - return [] - return [parse_node(node) for node in nodes.split('|')] - - def parse_wire(s, speed_i2s): # CLBLM_R_X3Y80/CLBLM_M_D6:952 wirestr, speed_index = s.split(':') @@ -55,7 +44,7 @@ def parse_wires(wires, speed_i2s): def gen_timing4(fn, speed_i2s): f = open(fn, 'r') - 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" + 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,wires" ncols = len(header_want.split(',')) # src_bel dst_bel ico fast_max fast_min slow_max slow_min pips @@ -78,7 +67,7 @@ def gen_timing4(fn, speed_i2s): def net_line(): assert len(parts) == ncols, "Expected %u parts, got %u" % ( ncols, len(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 + _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, wires = parts return { 'net': net, 'src': { @@ -104,7 +93,6 @@ def gen_timing4(fn, speed_i2s): }, 'ico': int(ico), 'pips': parse_pips(pips, speed_i2s), - 'nodes': parse_nodes(nodes), 'wires': parse_wires(wires, speed_i2s), 'line': l, } From fd5bfceb7ab1a917e74ff82f99b601421d5047b4 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 17:40:40 -0700 Subject: [PATCH 18/23] timfuz: delete timing4.txt after processing to save disk space Signed-off-by: John McMaster --- fuzzers/007-timing/projects/generate.sh | 3 +++ fuzzers/007-timing/timfuz.py | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/fuzzers/007-timing/projects/generate.sh b/fuzzers/007-timing/projects/generate.sh index 16f99e3f..a43fadcf 100644 --- a/fuzzers/007-timing/projects/generate.sh +++ b/fuzzers/007-timing/projects/generate.sh @@ -9,5 +9,8 @@ timing_txt2csv () { 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 + + # delete really large file, see https://github.com/SymbiFlow/prjxray/issues/137 + rm timing4.txt } diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index ba5822e7..d380e525 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -810,7 +810,15 @@ def tilej_stats(tilej): 'wires_solved': {}, } for corneri in corner_s2i.values(): - ret['wires_solved'][corneri] = sum([sum([1 if site_wire[corneri] else 0 for site_wire in sitej.values()]) for sitej in sitesj.values()]) + ret['wires_solved'][corneri] = sum( + [ + sum( + [ + 1 if site_wire[corneri] else 0 + for site_wire in sitej.values() + ]) + for sitej in sitesj.values() + ]) return ret tstats = tile_stats() @@ -826,8 +834,9 @@ def tilej_stats(tilej): ' %s: %u / %u solved, %u / %u covered' % (etype, solved, net, covered, net)) print( - ' sites: %u sites w/ %u solved / %u covered site wires' % - (sstats['sites'], sstats['wires_solved'][corneri], sstats['wires'])) + ' sites: %u sites w/ %u solved / %u covered site wires' % ( + sstats['sites'], sstats['wires_solved'][corneri], + sstats['wires'])) def load_bounds(bounds_csv, corner): From 5f43accedfc9941fd685699cf05fe217fc872a65 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 18:21:36 -0700 Subject: [PATCH 19/23] timfuz timing_txt2icsv.py: stream instead of loading all data Signed-off-by: John McMaster --- fuzzers/007-timing/timing_txt2icsv.py | 39 +++++++++++---------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/fuzzers/007-timing/timing_txt2icsv.py b/fuzzers/007-timing/timing_txt2icsv.py index 9f719120..abcc50a2 100644 --- a/fuzzers/007-timing/timing_txt2icsv.py +++ b/fuzzers/007-timing/timing_txt2icsv.py @@ -22,7 +22,7 @@ def vals2Adi_check(vals, names): assert 0 -def json2Ads(vals, verbose=False): +def row_json2Ads(val, verbose=False): '''Convert timing4 JSON into Ads interconnect equations''' def pip2speed(pip): @@ -33,47 +33,40 @@ def json2Ads(vals, verbose=False): _site, _name, wire_name = wire return PREFIX_W + ':' + wire_name - print('Making equations') + def add_name(name): + row_ds[name] = row_ds.get(name, 0) + 1 - def mk_row(val): - def add_name(name): - row_ds[name] = row_ds.get(name, 0) + 1 + row_ds = {} - row_ds = {} - - for pip in val['pips']: - add_name(pip2speed(pip)) - for wire in val['wires']: - add_name(wire2speed(wire)) - return row_ds - - return [mk_row(val) for val in vals] + for pip in val['pips']: + add_name(pip2speed(pip)) + for wire in val['wires']: + add_name(wire2speed(wire)) + return row_ds -def load_Ads(speed_json_f, fn_ins): +def load_Ads_gen(speed_json_f, fn_ins): print('Loading data') _speedj, speed_i2s = load_speed_json(speed_json_f) for fn_in in fn_ins: - vals = list(gen_timing4n(fn_in, speed_i2s)) - Ads = json2Ads(vals) - def mkb(val): t = val['t'] return (t['fast_max'], t['fast_min'], t['slow_max'], t['slow_min']) - bs = [mkb(val) for val in vals] - ico = [val['ico'] for val in vals] - - for row_bs, row_ds, row_ico in zip(bs, Ads, ico): + for val in gen_timing4n(fn_in, speed_i2s): + row_ds = row_json2Ads(val) + row_bs = mkb(val) + row_ico = val['ico'] + yield row_bs, row_ds, row_ico def run(speed_json_f, fout, fns_in, verbose=0, corner=None): fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n') - for row_bs, row_ds, row_ico in load_Ads(speed_json_f, fns_in): + for row_bs, row_ds, row_ico in load_Ads_gen(speed_json_f, fns_in): # XXX: consider removing ico column # its obsolete at this point if not row_ico: From dd88616a88171dddb0e0e142b38e4d54bef38db2 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 3 Oct 2018 18:21:55 -0700 Subject: [PATCH 20/23] timfuz project.tcl: better print statement message Signed-off-by: John McMaster --- fuzzers/007-timing/projects/project.tcl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 7e80e20e..8f9e7dcf 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -64,7 +64,7 @@ proc write_info4 {} { 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,wires" set TIME_start [clock clicks -milliseconds] - set equations 0 + set delays_net 0 set nets_no_src_cell 0 set nets_no_src_bel 0 set lines_no_int 0 @@ -123,9 +123,8 @@ proc write_info4 {} { set delays [get_net_delays -interconnect_only -of_objects $net] } else { set delays [get_net_delays -of_objects $net] - # only increment on one of the paths - set equations [expr "$equations + [llength $delays]"] } + set delays_net [expr "$delays_net + [llength $delays]"] puts $fp "GROUP,$ico,[llength $delays]" foreach delay $delays { #set delaystr [get_property NAME $delay] @@ -206,7 +205,7 @@ proc write_info4 {} { close $fp set TIME_taken [expr [clock clicks -milliseconds] - $TIME_start] puts "Took ms: $TIME_taken" - puts "Generated $equations equations" + puts "Checked $delays_net delays" puts "Nets: $nnets" puts " Skipped (no source cell): $nets_no_src_cell" puts " Skipped (no source bel): $nets_no_src_bel" From 0d8c5aaa29bf1bf549f280a931d720cf15cc2aa9 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Mon, 8 Oct 2018 11:12:50 -0700 Subject: [PATCH 21/23] timfuz: handle passthrough lut bel_pin Signed-off-by: John McMaster --- fuzzers/007-timing/projects/project.tcl | 2 ++ fuzzers/007-timing/timing_txt2json.py | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/fuzzers/007-timing/projects/project.tcl b/fuzzers/007-timing/projects/project.tcl index 8f9e7dcf..9d45128e 100644 --- a/fuzzers/007-timing/projects/project.tcl +++ b/fuzzers/007-timing/projects/project.tcl @@ -139,6 +139,8 @@ proc write_info4 {} { set dst_cell_pin [get_pins $dst_cell_pin_str] set dst_cell [get_cells -of_objects $dst_cell_pin] set dst_bel [get_bels -of_objects $dst_cell] + # WARNING: when there is a passthrough LUT, this can be multiple values + # (one for the real dest pin, and one for the passthrough lut input) set dst_bel_pin [get_bel_pins -of_objects $dst_cell_pin] set dst_site [get_sites -of_objects $dst_bel] set dst_site_type [get_property SITE_TYPE $dst_site] diff --git a/fuzzers/007-timing/timing_txt2json.py b/fuzzers/007-timing/timing_txt2json.py index c20c8c9f..4d26a5f6 100644 --- a/fuzzers/007-timing/timing_txt2json.py +++ b/fuzzers/007-timing/timing_txt2json.py @@ -68,6 +68,21 @@ def gen_timing4(fn, speed_i2s): assert len(parts) == ncols, "Expected %u parts, got %u" % ( ncols, len(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, wires = parts + + def filt_passthru_lut(bel_pins): + ''' + Ex: SLICE_X11Y110/A6LUT/A6 SLICE_X11Y110/AFF/D + ''' + parts = bel_pins.split() + if len(parts) == 1: + return parts[0] + else: + assert len(parts) == 2 + # the LUT shoudl always go first? + bel_pin_lut, bel_pin_dst = parts + assert '6LUT' in bel_pin_lut + return bel_pin_dst + return { 'net': net, 'src': { @@ -82,7 +97,7 @@ def gen_timing4(fn, speed_i2s): 'site_type': dst_site_type, 'site_pin': dst_site_pin, 'bel': dst_bel, - 'bel_pin': dst_bel_pin, + 'bel_pin': filt_passthru_lut(dst_bel_pin), }, 't': { # ps From 6bafa960cfbf27079d5d6fbb4546dbd561d24cfe Mon Sep 17 00:00:00 2001 From: John McMaster Date: Tue, 9 Oct 2018 16:37:47 -0700 Subject: [PATCH 22/23] timfuz: pep8 fix Signed-off-by: John McMaster --- fuzzers/007-timing/timing_txt2icsv.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzzers/007-timing/timing_txt2icsv.py b/fuzzers/007-timing/timing_txt2icsv.py index abcc50a2..469df204 100644 --- a/fuzzers/007-timing/timing_txt2icsv.py +++ b/fuzzers/007-timing/timing_txt2icsv.py @@ -52,6 +52,7 @@ def load_Ads_gen(speed_json_f, fn_ins): _speedj, speed_i2s = load_speed_json(speed_json_f) for fn_in in fn_ins: + def mkb(val): t = val['t'] return (t['fast_max'], t['fast_min'], t['slow_max'], t['slow_min']) @@ -60,7 +61,7 @@ def load_Ads_gen(speed_json_f, fn_ins): row_ds = row_json2Ads(val) row_bs = mkb(val) row_ico = val['ico'] - + yield row_bs, row_ds, row_ico From 2c1fc70d658b4e465c00a9493e4e1401acb05c43 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Tue, 9 Oct 2018 16:39:25 -0700 Subject: [PATCH 23/23] timfuz: switch default to easy project until disk/memory issues are resolved Signed-off-by: John McMaster --- fuzzers/007-timing/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/007-timing/Makefile b/fuzzers/007-timing/Makefile index 5734d78a..20da5da5 100644 --- a/fuzzers/007-timing/Makefile +++ b/fuzzers/007-timing/Makefile @@ -1,7 +1,7 @@ # for now hammering on just picorv32 # consider instead aggregating multiple projects -PRJ?=picorv32 -PRJN?=8 +PRJ?=oneblinkw +PRJN?=1 all: build/timgrid-v.json