mirror of https://github.com/openXC7/prjxray.git
commit
867853076d
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
@ -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='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_*/timing3.csv')
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = None
|
||||
if args.sub_json:
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@ 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='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='timing3.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('timing3.csv', 'timing3c.csv')
|
||||
assert fnout != fnin, 'Expect timing3.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_*/timing3.csv')
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
run(fout=fout, fns_in=fns_in, corner=args.corner, verbose=args.verbose)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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='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_*/timing3.csv')
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = load_sub(args.sub_json)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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:
|
||||
|
|
@ -27,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
|
||||
|
|
@ -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
|
||||
python3 $(TIMFUZ_DIR)/solve_qor.py --corner $(CORNER) --bounds-csv $(BUILD_DIR)/$(CORNER)/flat.csv specimen_*/timing3.csv >$(BUILD_DIR)/$(CORNER)/qor.txt.tmp
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,13 @@ 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 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
|
||||
|
||||
# delete really large file, see https://github.com/SymbiFlow/prjxray/issues/137
|
||||
rm timing4.txt
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
@ -43,5 +43,5 @@ proc build_design {} {
|
|||
}
|
||||
|
||||
build_design
|
||||
write_info3
|
||||
write_info4
|
||||
|
||||
|
|
|
|||
|
|
@ -44,5 +44,5 @@ proc build_design {} {
|
|||
}
|
||||
|
||||
build_design
|
||||
write_info3
|
||||
write_info4
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ proc build_design {} {
|
|||
}
|
||||
|
||||
build_design
|
||||
write_info3
|
||||
write_info4
|
||||
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ proc build_design {} {
|
|||
}
|
||||
|
||||
build_design
|
||||
write_info3
|
||||
write_info4
|
||||
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ proc build_design {} {
|
|||
}
|
||||
|
||||
build_design
|
||||
write_info3
|
||||
write_info4
|
||||
|
||||
|
|
|
|||
|
|
@ -4,38 +4,26 @@
|
|||
N := 1
|
||||
SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
|
||||
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
|
||||
CSVS := $(addsuffix /timing3.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)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,166 +1,205 @@
|
|||
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: <const0>
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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: <const0>)
|
||||
|
||||
|
||||
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 pin_bel {pin} {
|
||||
set cell [get_cells -of_objects $pin]
|
||||
set bel [get_bels -of_objects $cell]
|
||||
return $bel
|
||||
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"
|
||||
}
|
||||
|
||||
# Changed to group wires and nodes
|
||||
# This allows tracing the full path along with pips
|
||||
proc write_info3 {} {
|
||||
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 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/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_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 verbose 0
|
||||
set equations 0
|
||||
set site_src_nets 0
|
||||
set site_dst_nets 0
|
||||
set delays_net 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 clk]
|
||||
#set nets [get_nets roi/counter[0]_i_2_n_0]
|
||||
set nnets [llength $nets]
|
||||
foreach net $nets {
|
||||
incr neti
|
||||
#if {$neti >= 10} {
|
||||
# puts "Debug break"
|
||||
# break
|
||||
#}
|
||||
|
||||
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:
|
||||
# -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_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
|
||||
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 ""} {
|
||||
# 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
|
||||
}
|
||||
|
||||
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]
|
||||
set src_site_type [get_property SITE_TYPE $src_site]
|
||||
# optional
|
||||
set src_site_pin [get_site_pins -of_objects $src_cell_pin]
|
||||
|
||||
# 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
|
||||
}
|
||||
foreach src_site_pin $src_site_pins {
|
||||
if $verbose {
|
||||
puts "Source: $src_pin at site $src_site:$src_bel, spin $src_site_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]
|
||||
}
|
||||
set delays_net [expr "$delays_net + [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]
|
||||
# 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]
|
||||
# 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 ""} {
|
||||
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
|
||||
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 ""} {
|
||||
puts "ERROR: should have been filtered"
|
||||
return
|
||||
# Ideally query from and to cell pins
|
||||
} else {
|
||||
set delays [get_net_delays -of_objects $net]
|
||||
}
|
||||
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_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 <args> - (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 nodes [get_nodes -of_objects $net -from $src_site_pin -to $dst_site_pin]
|
||||
if {$nodes eq ""} {
|
||||
puts "ERROR: no nodes"
|
||||
return
|
||||
}
|
||||
|
||||
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?
|
||||
if {"$dst_site_pins" eq ""} {
|
||||
continue
|
||||
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"
|
||||
}
|
||||
# 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 wires_out [list_format "$wires_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 $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"
|
||||
|
||||
# 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
|
||||
}
|
||||
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 $wires_out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -168,7 +207,16 @@ proc write_info3 {} {
|
|||
close $fp
|
||||
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 "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"
|
||||
puts "Lines"
|
||||
puts " No interconnect: $lines_no_int"
|
||||
puts " Has interconnect: $lines_some_int"
|
||||
}
|
||||
|
||||
# for debugging
|
||||
# source ../project.tcl
|
||||
# write_info4
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
@ -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='timing4i.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_*/timing4i.csv')
|
||||
|
||||
try:
|
||||
run(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -119,14 +128,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='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_*/timing3.csv')
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = None
|
||||
if args.sub_json:
|
||||
|
|
|
|||
|
|
@ -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='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_*/timing3.csv')
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = None
|
||||
if args.sub_json:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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='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_*/timing3.csv')
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
try:
|
||||
run(
|
||||
|
|
|
|||
|
|
@ -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_SW_EI, PREFIX_SW_EO, PREFIX_SW_I, sw_ei_s2vals, sw_eo_s2vals, sw_i_s2vals
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
|
@ -9,22 +9,117 @@ 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?
|
||||
# Naming discussion https://github.com/SymbiFlow/prjxray/pull/126
|
||||
|
||||
sitej = tilej.setdefault('sites', {})
|
||||
|
||||
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 = {PREFIX_SW_EI: {}, PREFIX_SW_EO: {}, PREFIX_SW_I: {}}
|
||||
for k, v in bounds.items():
|
||||
prefix = k.split(':')[0]
|
||||
if prefix in (PREFIX_W, PREFIX_P):
|
||||
pw[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
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,51 @@ 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'
|
||||
# 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 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_SW_EI, site_type, src_site_pin, dst_bel, dst_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
|
||||
class SimplifiedToZero(Exception):
|
||||
|
|
@ -524,11 +569,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 +585,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 +782,63 @@ 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():
|
||||
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()
|
||||
|
||||
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 w/ %u solved / %u covered site wires' % (
|
||||
sstats['sites'], sstats['wires_solved'][corneri],
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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('')
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
@ -165,7 +168,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 +196,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)
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -1,293 +0,0 @@
|
|||
#!/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
|
||||
|
||||
# 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')
|
||||
for val in vals:
|
||||
node_wires = 0
|
||||
for _node, wiresn in val['nodes']:
|
||||
node_wires += wiresn
|
||||
assert node_wires == len(val['wires'])
|
||||
print('Done')
|
||||
assert 0
|
||||
|
||||
|
||||
def vals2Adi(vals, speed_i2s, name_tr={}, name_drop=[], verbose=False):
|
||||
def pip2speed(pip):
|
||||
_site, _name, speed_index = pip
|
||||
return PREFIX_P + speed_i2s[speed_index]
|
||||
|
||||
def wire2speed(wire):
|
||||
_site, _name, speed_index = wire
|
||||
return PREFIX_W + speed_i2s[speed_index]
|
||||
|
||||
# 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):
|
||||
|
||||
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
|
||||
|
||||
# Start with 0 occurances
|
||||
#row = [0 for _i in range(len(names))]
|
||||
row_di = {}
|
||||
|
||||
#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 Adi, names
|
||||
|
||||
|
||||
# TODO: load directly as Ads
|
||||
# remove names_tr, names_drop
|
||||
def vals2Ads(vals, speed_i2s, verbose=False):
|
||||
Adi, names = vals2Adi(vals, speed_i2s, verbose=False)
|
||||
return A_di2ds(Adi, names)
|
||||
|
||||
|
||||
def load_Ads(speed_json_f, f_ins):
|
||||
|
||||
print('Loading data')
|
||||
|
||||
_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)
|
||||
|
||||
Ads = vals2Ads(vals, speed_i2s)
|
||||
|
||||
def mkb(val):
|
||||
return (
|
||||
val['fast_max'], val['fast_min'], val['slow_max'], val['slow_min'])
|
||||
|
||||
b = [mkb(val) for val in vals]
|
||||
ico = [val['ico'] for val in vals]
|
||||
|
||||
return Ads, b, ico
|
||||
|
||||
|
||||
def run(speed_json_f, fout, f_ins, verbose=0, corner=None):
|
||||
Ads, bs, ico = load_Ads(speed_json_f, f_ins)
|
||||
|
||||
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):
|
||||
# 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])]
|
||||
for k, v in sorted(row_ds.items()):
|
||||
items.append('%u %s' % (v, k))
|
||||
fout.write(','.join(items) + '\n')
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Convert obscure timing3.txt into more readable but roughly equivilent timing3.csv'
|
||||
)
|
||||
|
||||
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')
|
||||
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('fns_in', nargs='+', help='Input timing3.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', '.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_*/timing3.txt')
|
||||
|
||||
run(
|
||||
speed_json_f=open(args.speed_json, 'r'),
|
||||
fout=fout,
|
||||
f_ins=[(open(fn_in, 'r'), fn_in) for fn_in in fns_in],
|
||||
verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, A_di2ds, PREFIX_W, PREFIX_P
|
||||
from timing_txt2json import gen_timing4n, load_speed_json
|
||||
|
||||
import glob
|
||||
import math
|
||||
import json
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
# Verify the nodes and wires really do line up
|
||||
def vals2Adi_check(vals, names):
|
||||
print('Checking')
|
||||
for val in vals:
|
||||
node_wires = 0
|
||||
for _node, wiresn in val['nodes']:
|
||||
node_wires += wiresn
|
||||
assert node_wires == len(val['wires'])
|
||||
print('Done')
|
||||
assert 0
|
||||
|
||||
|
||||
def row_json2Ads(val, verbose=False):
|
||||
'''Convert timing4 JSON into Ads interconnect equations'''
|
||||
|
||||
def pip2speed(pip):
|
||||
_site, _name, pip_name = pip
|
||||
return PREFIX_P + ':' + pip_name
|
||||
|
||||
def wire2speed(wire):
|
||||
_site, _name, wire_name = wire
|
||||
return PREFIX_W + ':' + wire_name
|
||||
|
||||
def add_name(name):
|
||||
row_ds[name] = row_ds.get(name, 0) + 1
|
||||
|
||||
row_ds = {}
|
||||
|
||||
for pip in val['pips']:
|
||||
add_name(pip2speed(pip))
|
||||
for wire in val['wires']:
|
||||
add_name(wire2speed(wire))
|
||||
return row_ds
|
||||
|
||||
|
||||
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:
|
||||
|
||||
def mkb(val):
|
||||
t = val['t']
|
||||
return (t['fast_max'], t['fast_min'], t['slow_max'], t['slow_min'])
|
||||
|
||||
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_gen(speed_json_f, fns_in):
|
||||
# XXX: consider removing ico column
|
||||
# its obsolete at this point
|
||||
if not row_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])]
|
||||
for k, v in sorted(row_ds.items()):
|
||||
items.append('%u %s' % (v, k))
|
||||
fout.write(','.join(items) + '\n')
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Convert obscure timing4.txt into timing4i.csv (interconnect 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', 'i.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()
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
#!/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_pips(pips, speed_i2s):
|
||||
if not pips:
|
||||
return []
|
||||
return [parse_pip(pip, speed_i2s) for pip in pips.split('|')]
|
||||
|
||||
|
||||
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 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,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, "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': {
|
||||
'site': src_site,
|
||||
'site_type': src_site_type,
|
||||
'site_pin': src_site_pin,
|
||||
'bel': src_bel,
|
||||
'bel_pin': src_bel_pin,
|
||||
},
|
||||
'dst': {
|
||||
'site': dst_site,
|
||||
'site_type': dst_site_type,
|
||||
'site_pin': dst_site_pin,
|
||||
'bel': dst_bel,
|
||||
'bel_pin': filt_passthru_lut(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_pips(pips, speed_i2s),
|
||||
'wires': parse_wires(wires, speed_i2s),
|
||||
'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 lines' % (fn, bads, rets))
|
||||
|
||||
|
||||
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_timing4a(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)
|
||||
rets = 0
|
||||
while True:
|
||||
|
||||
def get_ico(exp_ico):
|
||||
ret = []
|
||||
try:
|
||||
lintype, val = next(timgen)
|
||||
except StopIteration:
|
||||
return None
|
||||
assert lintype == 'GROUP'
|
||||
ico, delays = val
|
||||
assert ico == exp_ico
|
||||
for _ in range(delays):
|
||||
lintype, val = next(timgen)
|
||||
assert lintype == 'NET'
|
||||
ret.append(val)
|
||||
return ret
|
||||
|
||||
ico0s = get_ico(0)
|
||||
if ico0s 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
|
||||
rets += 1
|
||||
print(' load %s: %u aggregated lines' % (fn, rets))
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
# 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_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
|
||||
|
||||
|
||||
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 not val['src']['site_pin'] and not val['dst']['site_pin']:
|
||||
# add one delay model for the path
|
||||
# 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
|
||||
row_ds = {}
|
||||
|
||||
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
|
||||
|
||||
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]
|
||||
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()
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue