mirror of https://github.com/openXC7/prjxray.git
commit
867853076d
|
|
@ -1,7 +1,7 @@
|
||||||
# for now hammering on just picorv32
|
# for now hammering on just picorv32
|
||||||
# consider instead aggregating multiple projects
|
# consider instead aggregating multiple projects
|
||||||
PRJ?=picorv32
|
PRJ?=oneblinkw
|
||||||
PRJN?=8
|
PRJN?=1
|
||||||
|
|
||||||
all: build/timgrid-v.json
|
all: build/timgrid-v.json
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ def pds(Ads, s):
|
||||||
def run(fns_in, sub_json=None, verbose=False):
|
def run(fns_in, sub_json=None, verbose=False):
|
||||||
assert len(fns_in) > 0
|
assert len(fns_in) > 0
|
||||||
# arbitrary corner...data is thrown away
|
# 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:
|
if sub_json:
|
||||||
print('Subbing JSON %u rows' % len(Ads))
|
print('Subbing JSON %u rows' % len(Ads))
|
||||||
|
|
@ -111,14 +111,14 @@ def main():
|
||||||
|
|
||||||
parser.add_argument('--verbose', action='store_true', help='')
|
parser.add_argument('--verbose', action='store_true', help='')
|
||||||
parser.add_argument('--sub-json', 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()
|
args = parser.parse_args()
|
||||||
# Store options in dict to ease passing through functions
|
# Store options in dict to ease passing through functions
|
||||||
bench = Benchmark()
|
bench = Benchmark()
|
||||||
|
|
||||||
fns_in = args.fns_in
|
fns_in = args.fns_in
|
||||||
if not fns_in:
|
if not fns_in:
|
||||||
fns_in = glob.glob('specimen_*/timing3.csv')
|
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||||
|
|
||||||
sub_json = None
|
sub_json = None
|
||||||
if args.sub_json:
|
if args.sub_json:
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,12 @@ def main():
|
||||||
|
|
||||||
parser.add_argument('--verbose', type=int, help='')
|
parser.add_argument('--verbose', type=int, help='')
|
||||||
parser.add_argument(
|
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('--out', default=None, help='Output csv')
|
||||||
parser.add_argument('--corner', 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()
|
args = parser.parse_args()
|
||||||
bench = Benchmark()
|
bench = Benchmark()
|
||||||
|
|
||||||
|
|
@ -40,8 +42,8 @@ def main():
|
||||||
if args.auto_name:
|
if args.auto_name:
|
||||||
assert len(args.fns_in) == 1
|
assert len(args.fns_in) == 1
|
||||||
fnin = args.fns_in[0]
|
fnin = args.fns_in[0]
|
||||||
fnout = fnin.replace('timing3.csv', 'timing3c.csv')
|
fnout = fnin.replace('timing4i.csv', 'timing4c.csv')
|
||||||
assert fnout != fnin, 'Expect timing3.csv in'
|
assert fnout != fnin, 'Expect timing4i.csv in'
|
||||||
else:
|
else:
|
||||||
fnout = '/dev/stdout'
|
fnout = '/dev/stdout'
|
||||||
print("Writing to %s" % fnout)
|
print("Writing to %s" % fnout)
|
||||||
|
|
@ -49,7 +51,7 @@ def main():
|
||||||
|
|
||||||
fns_in = args.fns_in
|
fns_in = args.fns_in
|
||||||
if not 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)
|
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):
|
def gen_group(fnin, sub_json, strict=False, verbose=False):
|
||||||
print('Loading data')
|
print('Loading data')
|
||||||
Ads, bs = loadc_Ads_bs([fnin], ico=True)
|
Ads, bs = loadc_Ads_bs([fnin])
|
||||||
|
|
||||||
print('Sub: %u rows' % len(Ads))
|
print('Sub: %u rows' % len(Ads))
|
||||||
iold = instances(Ads)
|
iold = instances(Ads)
|
||||||
|
|
@ -45,14 +45,14 @@ def main():
|
||||||
required=True,
|
required=True,
|
||||||
help='Group substitutions to make fully ranked')
|
help='Group substitutions to make fully ranked')
|
||||||
parser.add_argument('--out', help='Output sub.json substitution result')
|
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()
|
args = parser.parse_args()
|
||||||
# Store options in dict to ease passing through functions
|
# Store options in dict to ease passing through functions
|
||||||
bench = Benchmark()
|
bench = Benchmark()
|
||||||
|
|
||||||
fns_in = args.fns_in
|
fns_in = args.fns_in
|
||||||
if not 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)
|
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):
|
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)
|
bounds = Ads2bounds(Ads, bs)
|
||||||
# Elements with zero delay assigned due to sub group
|
# Elements with zero delay assigned due to sub group
|
||||||
group_zeros = set()
|
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
|
CORNER=slow_max
|
||||||
ALLOW_ZERO_EQN?=N
|
ALLOW_ZERO_EQN?=N
|
||||||
BADPRJ_OK?=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
|
all: $(BUILD_DIR)/$(CORNER)/timgrid-vc.json $(BUILD_DIR)/$(CORNER)/qor.txt
|
||||||
|
|
||||||
|
|
@ -19,7 +20,7 @@ clean:
|
||||||
.PHONY: all run clean
|
.PHONY: all run clean
|
||||||
|
|
||||||
$(BUILD_DIR)/$(CORNER):
|
$(BUILD_DIR)/$(CORNER):
|
||||||
mkdir $(BUILD_DIR)/$(CORNER)
|
mkdir -p $(BUILD_DIR)/$(CORNER)
|
||||||
|
|
||||||
# parent should have built this
|
# parent should have built this
|
||||||
$(BUILD_DIR)/checksub:
|
$(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)
|
$(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
|
# 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
|
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
|
$(BUILD_DIR)/$(CORNER)/linprog.csv: $(BUILD_DIR)/$(CORNER)/leastsq.csv $(BUILD_DIR)/grouped.csv
|
||||||
# Tweak rough timing model, making sure all constraints are satisfied
|
# 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
|
mv $(BUILD_DIR)/$(CORNER)/linprog.csv.tmp $(BUILD_DIR)/$(CORNER)/linprog.csv
|
||||||
|
|
||||||
$(BUILD_DIR)/$(CORNER)/flat.csv: $(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
|
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
|
$(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
|
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
|
TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing
|
||||||
|
|
||||||
timing_txt2csv () {
|
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
|
build_design
|
||||||
write_info3
|
write_info4
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,5 +44,5 @@ proc build_design {} {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
python ../generate.py --sdx 4 --sdy 4 >top.v
|
||||||
vivado -mode batch -source ../generate.tcl
|
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
|
build_design
|
||||||
write_info3
|
write_info4
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,5 +43,5 @@ proc build_design {} {
|
||||||
}
|
}
|
||||||
|
|
||||||
build_design
|
build_design
|
||||||
write_info3
|
write_info4
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,5 +43,5 @@ proc build_design {} {
|
||||||
}
|
}
|
||||||
|
|
||||||
build_design
|
build_design
|
||||||
write_info3
|
write_info4
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,38 +4,26 @@
|
||||||
N := 1
|
N := 1
|
||||||
SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
|
SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
|
||||||
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
|
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
|
||||||
CSVS := $(addsuffix /timing3.csv,$(SPECIMENS))
|
|
||||||
TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing
|
TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing
|
||||||
RREF_CORNER=slow_max
|
|
||||||
# Allow an empty system of equations?
|
# Allow an empty system of equations?
|
||||||
# for testing only on small projects
|
# for testing only on small projects
|
||||||
ALLOW_ZERO_EQN?=N
|
ALLOW_ZERO_EQN?=N
|
||||||
# Constrained projects may fail to build
|
# Constrained projects may fail to build
|
||||||
# Set to Y to make a best effort to suck in the ones that did build
|
# Set to Y to make a best effort to suck in the ones that did build
|
||||||
BADPRJ_OK?=N
|
BADPRJ_OK?=N
|
||||||
# Set ZERO elements to zero delay (as is expected they should be)
|
|
||||||
RMZERO?=N
|
|
||||||
BUILD_DIR?=build
|
BUILD_DIR?=build
|
||||||
|
# interconnect
|
||||||
|
BUILD_DIR_I?=$(BUILD_DIR)/i
|
||||||
|
# site
|
||||||
|
BUILD_DIR_S?=$(BUILD_DIR)/s
|
||||||
|
|
||||||
RREF_ARGS=
|
CORNERS=fast_max fast_min slow_max slow_min
|
||||||
ifeq ($(RMZERO),Y)
|
|
||||||
RREF_ARGS+=--rm-zero
|
|
||||||
endif
|
|
||||||
|
|
||||||
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
|
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):
|
$(SPECIMENS_OK):
|
||||||
bash generate.sh $(subst /OK,,$@) || (if [ "$(BADPRJ_OK)" != 'Y' ] ; then exit 1; fi; exit 0)
|
bash generate.sh $(subst /OK,,$@) || (if [ "$(BADPRJ_OK)" != 'Y' ] ; then exit 1; fi; exit 0)
|
||||||
touch $@
|
touch $@
|
||||||
|
|
@ -52,38 +40,16 @@ clean:
|
||||||
|
|
||||||
.PHONY: all run clean
|
.PHONY: all run clean
|
||||||
|
|
||||||
# Normally require all projects to complete
|
$(TIMGRID_V_I): $(SPECIMENS_OK)
|
||||||
# If BADPRJ_OK is allowed, only take projects that were successful
|
$(MAKE) -f $(TIMFUZ_DIR)/projects/is.mk BUILD_DIR=$(BUILD_DIR_I) SOLVING=i SPECIMENS=$(SPECIMENS) all
|
||||||
# FIXME: couldn't get call to work
|
i: $(TIMGRID_V_I)
|
||||||
exist_csvs = \
|
|
||||||
for f in $(CSVS); do \
|
|
||||||
if [ "$(BADPRJ_OK)" != 'Y' -o -f $$f ] ; then \
|
|
||||||
echo $$f; \
|
|
||||||
fi; \
|
|
||||||
done
|
|
||||||
|
|
||||||
# rref should be the same regardless of corner
|
$(TIMGRID_V_S): $(SPECIMENS_OK)
|
||||||
$(BUILD_DIR)/sub.json: $(SPECIMENS_OK)
|
$(MAKE) -f $(TIMFUZ_DIR)/projects/is.mk BUILD_DIR=$(BUILD_DIR_S) SOLVING=s SPECIMENS=$(SPECIMENS) all
|
||||||
mkdir -p $(BUILD_DIR)
|
s: $(TIMGRID_V_S)
|
||||||
# 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
|
|
||||||
|
|
||||||
$(BUILD_DIR)/grouped.csv: $(SPECIMENS_OK) $(BUILD_DIR)/sub.json
|
.PHONY: i s
|
||||||
# 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
|
$(BUILD_DIR)/timgrid-v.json: $(TIMGRID_V_I) $(TIMGRID_V_S)
|
||||||
# Verify sub.json makes a cleanly solvable solution with no non-pivot leftover
|
python3 $(TIMFUZ_DIR)/timgrid_vc2v.py --out $(BUILD_DIR)/timgrid-v.json $(TIMGRID_V_I) $(TIMGRID_V_S)
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,166 +1,205 @@
|
||||||
proc pin_info {pin} {
|
# General notes:
|
||||||
set cell [get_cells -of_objects $pin]
|
# Observed nets with 1 to 2 source cell pins
|
||||||
set bel [get_bels -of_objects $cell]
|
# 2 example
|
||||||
set site [get_sites -of_objects $bel]
|
# [get_pins -filter {DIRECTION == OUT} -of_objects $net]
|
||||||
return "$site $bel"
|
# 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} {
|
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} {
|
||||||
set cell [get_cells -of_objects $pin]
|
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"
|
||||||
set bel [get_bels -of_objects $cell]
|
|
||||||
return $bel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Changed to group wires and nodes
|
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} {
|
||||||
# This allows tracing the full path along with pips
|
set src_site $site
|
||||||
proc write_info3 {} {
|
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 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
|
# 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 TIME_start [clock clicks -milliseconds]
|
||||||
set verbose 0
|
set delays_net 0
|
||||||
set equations 0
|
set nets_no_src_cell 0
|
||||||
set site_src_nets 0
|
set nets_no_src_bel 0
|
||||||
set site_dst_nets 0
|
set lines_no_int 0
|
||||||
|
set lines_some_int 0
|
||||||
set neti 0
|
set neti 0
|
||||||
set nets [get_nets -hierarchical]
|
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]
|
set nnets [llength $nets]
|
||||||
foreach net $nets {
|
foreach net $nets {
|
||||||
incr neti
|
incr neti
|
||||||
#if {$neti >= 10} {
|
|
||||||
# puts "Debug break"
|
|
||||||
# break
|
|
||||||
#}
|
|
||||||
|
|
||||||
puts "Net $neti / $nnets: $net"
|
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
|
# The semantics of get_pins -leaf is kind of odd
|
||||||
# When no passthrough LUTs exist, it has no effect
|
# When no passthrough LUTs exist, it has no effect
|
||||||
# When passthrough LUT present:
|
# When passthrough LUT present:
|
||||||
# -w/o -leaf: some pins + passthrough LUT pins
|
# -w/o -leaf: some pins + passthrough LUT pins
|
||||||
# -w/ -leaf: different pins + passthrough LUT pins
|
# -w/ -leaf: different pins + passthrough LUT pins
|
||||||
# With OUT filter this seems to be sufficient
|
# With OUT filter this seems to be sufficient
|
||||||
set src_pin [get_pins -leaf -filter {DIRECTION == OUT} -of_objects $net]
|
set src_cell_pins [get_pins -leaf -filter {DIRECTION == OUT} -of_objects $net]
|
||||||
set src_bel [pin_bel $src_pin]
|
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]
|
set src_site [get_sites -of_objects $src_bel]
|
||||||
# Only one net driver
|
set src_site_type [get_property SITE_TYPE $src_site]
|
||||||
set src_site_pins [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]
|
# optional
|
||||||
|
set src_site_pin [get_site_pins -of_objects $src_cell_pin]
|
||||||
|
|
||||||
# Sometimes this is empty for reasons that escape me
|
# Report delays with and without interconnect only
|
||||||
# Emitting direction doesn't help
|
foreach ico "0 1" {
|
||||||
if {[llength $src_site_pins] < 1} {
|
if $ico {
|
||||||
if $verbose {
|
set delays [get_net_delays -interconnect_only -of_objects $net]
|
||||||
puts " Ignoring site internal net"
|
} else {
|
||||||
}
|
set delays [get_net_delays -of_objects $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"
|
|
||||||
}
|
}
|
||||||
|
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
|
set fast_max [get_property "FAST_MAX" $delay]
|
||||||
foreach ico "0 1" {
|
set fast_min [get_property "FAST_MIN" $delay]
|
||||||
set ico_flag ""
|
set slow_max [get_property "SLOW_MAX" $delay]
|
||||||
if $ico {
|
set slow_min [get_property "SLOW_MIN" $delay]
|
||||||
set ico_flag "-interconnect_only"
|
|
||||||
set delays [get_net_delays $ico_flag -of_objects $net]
|
# 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 {
|
} else {
|
||||||
set delays [get_net_delays -of_objects $net]
|
# Nested list delimination precedence: ",|:"
|
||||||
}
|
|
||||||
foreach delay $delays {
|
# Pips in between
|
||||||
set delaystr [get_property NAME $delay]
|
# Walk net, looking for interesting elements in between
|
||||||
set dst_pins [get_property TO_PIN $delay]
|
# -from <args> - (Optional) Defines the starting points of the pips to get
|
||||||
set dst_pin [get_pins $dst_pins]
|
# from wire or site_pin
|
||||||
#puts " $delaystr: $src_pin => $dst_pin"
|
set pips [get_pips -of_objects $net -from $src_site_pin -to $dst_site_pin]
|
||||||
set dst_bel [pin_bel $dst_pin]
|
# Write pips w/ speed index
|
||||||
set dst_site [get_sites -of_objects $dst_bel]
|
foreach pip $pips {
|
||||||
if $verbose {
|
set speed_index [get_property SPEED_INDEX $pip]
|
||||||
puts " Dest: $dst_pin at site $dst_site:$dst_bel"
|
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]
|
set wires [get_wires -of_objects $nodes]
|
||||||
# Some nets are internal
|
# Write wires
|
||||||
# But should this happen on dest if we've already filtered source?
|
foreach wire $wires {
|
||||||
if {"$dst_site_pins" eq ""} {
|
set speed_index [get_property SPEED_INDEX $wire]
|
||||||
continue
|
lappend wires_out "$wire:$speed_index"
|
||||||
}
|
}
|
||||||
# Also apparantly you can have multiple of these as well
|
set wires_out [list_format "$wires_out" "|"]
|
||||||
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]
|
|
||||||
|
|
||||||
# Want:
|
incr lines_some_int
|
||||||
# Site / BEL at src
|
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
|
||||||
# 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +207,16 @@ proc write_info3 {} {
|
||||||
close $fp
|
close $fp
|
||||||
set TIME_taken [expr [clock clicks -milliseconds] - $TIME_start]
|
set TIME_taken [expr [clock clicks -milliseconds] - $TIME_start]
|
||||||
puts "Took ms: $TIME_taken"
|
puts "Took ms: $TIME_taken"
|
||||||
puts "Generated $equations equations"
|
puts "Checked $delays_net delays"
|
||||||
puts "Skipped $site_src_nets (+ $site_dst_nets) site nets"
|
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):
|
def load(fn_ins, simplify=False, corner=None, rm_zero=False):
|
||||||
zero_names = OrderedSet()
|
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:
|
if rm_zero:
|
||||||
zero_names = rm_zero_cols(Ads)
|
zero_names = rm_zero_cols(Ads)
|
||||||
if simplify:
|
if simplify:
|
||||||
|
|
@ -223,13 +223,13 @@ def main():
|
||||||
default='build_speed/speed.json',
|
default='build_speed/speed.json',
|
||||||
help='Provides speed index to name translation')
|
help='Provides speed index to name translation')
|
||||||
parser.add_argument('--out', help='Output sub.json substitution result')
|
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()
|
args = parser.parse_args()
|
||||||
bench = Benchmark()
|
bench = Benchmark()
|
||||||
|
|
||||||
fns_in = args.fns_in
|
fns_in = args.fns_in
|
||||||
if not fns_in:
|
if not fns_in:
|
||||||
fns_in = glob.glob('specimen_*/timing3.csv')
|
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
run(
|
run(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from timfuz import Benchmark, load_sub, corner_s2i, acorner2csv
|
from timfuz import Benchmark, load_sub
|
||||||
import timfuz
|
import timfuz
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import math
|
import math
|
||||||
|
|
@ -22,11 +22,20 @@ def mkestimate(Anp, b):
|
||||||
x0 = np.array([1e3 for _x in range(cols)])
|
x0 = np.array([1e3 for _x in range(cols)])
|
||||||
for row_np, row_b in zip(Anp, b):
|
for row_np, row_b in zip(Anp, b):
|
||||||
for coli, val in enumerate(row_np):
|
for coli, val in enumerate(row_np):
|
||||||
if val:
|
# favor non-trivial values
|
||||||
# Scale by number occurances
|
if val <= 0:
|
||||||
ub = row_b / val
|
continue
|
||||||
if ub >= 0:
|
# Scale by number occurances
|
||||||
x0[coli] = min(x0[coli], ub)
|
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
|
return x0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -119,14 +128,14 @@ def main():
|
||||||
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--out', default=None, help='output timing delay .json')
|
'--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()
|
args = parser.parse_args()
|
||||||
# Store options in dict to ease passing through functions
|
# Store options in dict to ease passing through functions
|
||||||
bench = Benchmark()
|
bench = Benchmark()
|
||||||
|
|
||||||
fns_in = args.fns_in
|
fns_in = args.fns_in
|
||||||
if not fns_in:
|
if not fns_in:
|
||||||
fns_in = glob.glob('specimen_*/timing3.csv')
|
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||||
|
|
||||||
sub_json = None
|
sub_json = None
|
||||||
if args.sub_json:
|
if args.sub_json:
|
||||||
|
|
|
||||||
|
|
@ -162,14 +162,14 @@ def main():
|
||||||
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--out', default=None, help='output timing delay .json')
|
'--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()
|
args = parser.parse_args()
|
||||||
# Store options in dict to ease passing through functions
|
# Store options in dict to ease passing through functions
|
||||||
bench = Benchmark()
|
bench = Benchmark()
|
||||||
|
|
||||||
fns_in = args.fns_in
|
fns_in = args.fns_in
|
||||||
if not fns_in:
|
if not fns_in:
|
||||||
fns_in = glob.glob('specimen_*/timing3.csv')
|
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||||
|
|
||||||
sub_json = None
|
sub_json = None
|
||||||
if args.sub_json:
|
if args.sub_json:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import numpy as np
|
||||||
|
|
||||||
def run(fns_in, corner, bounds_csv, verbose=False):
|
def run(fns_in, corner, bounds_csv, verbose=False):
|
||||||
print('Loading data')
|
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)
|
bounds = load_bounds(bounds_csv, corner)
|
||||||
# verify is flattened
|
# verify is flattened
|
||||||
|
|
@ -37,14 +37,14 @@ def main():
|
||||||
'--bounds-csv',
|
'--bounds-csv',
|
||||||
required=True,
|
required=True,
|
||||||
help='Previous solve result starting point')
|
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()
|
args = parser.parse_args()
|
||||||
# Store options in dict to ease passing through functions
|
# Store options in dict to ease passing through functions
|
||||||
bench = Benchmark()
|
bench = Benchmark()
|
||||||
|
|
||||||
fns_in = args.fns_in
|
fns_in = args.fns_in
|
||||||
if not fns_in:
|
if not fns_in:
|
||||||
fns_in = glob.glob('specimen_*/timing3.csv')
|
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
run(
|
run(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import timfuz
|
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 sys
|
||||||
import os
|
import os
|
||||||
|
|
@ -9,22 +9,117 @@ import time
|
||||||
import json
|
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):
|
def run(fns_in, fnout, tile_json_fn, verbose=False):
|
||||||
# modified in place
|
# modified in place
|
||||||
tilej = json.load(open(tile_json_fn, 'r'))
|
tilej = json.load(open(tile_json_fn, 'r'))
|
||||||
|
|
||||||
for fnin in fns_in:
|
for fnin in fns_in:
|
||||||
Ads, bs = loadc_Ads_bs([fnin], ico=True)
|
Ads, bs = loadc_Ads_bs([fnin])
|
||||||
bounds = Ads2bounds(Ads, bs)
|
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():
|
add_pip_wire(tilej, bounds_pw)
|
||||||
pips = tile['pips']
|
add_sites(tilej, bounds_sites)
|
||||||
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])
|
|
||||||
|
|
||||||
timfuz.tilej_stats(tilej)
|
timfuz.tilej_stats(tilej)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,51 @@ import collections
|
||||||
|
|
||||||
from benchmark import Benchmark
|
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
|
# Equations are filtered out until nothing is left
|
||||||
class SimplifiedToZero(Exception):
|
class SimplifiedToZero(Exception):
|
||||||
|
|
@ -524,11 +569,14 @@ def loadc_Ads_mkb(fns, mkb, filt):
|
||||||
def loadc_Ads_b(fns, corner, ico=None):
|
def loadc_Ads_b(fns, corner, ico=None):
|
||||||
corner = corner or "slow_max"
|
corner = corner or "slow_max"
|
||||||
corneri = corner_s2i[corner]
|
corneri = corner_s2i[corner]
|
||||||
|
'''
|
||||||
if ico is not None:
|
if ico is not None:
|
||||||
filt = lambda ico_, corners, vars: ico_ == ico
|
filt = lambda ico_, corners, vars: ico_ == ico
|
||||||
else:
|
else:
|
||||||
filt = lambda ico_, corners, vars: True
|
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):
|
def mkb(val):
|
||||||
return val[corneri]
|
return val[corneri]
|
||||||
|
|
@ -537,10 +585,14 @@ def loadc_Ads_b(fns, corner, ico=None):
|
||||||
|
|
||||||
|
|
||||||
def loadc_Ads_bs(fns, ico=None):
|
def loadc_Ads_bs(fns, ico=None):
|
||||||
|
'''
|
||||||
if ico is not None:
|
if ico is not None:
|
||||||
filt = lambda ico_, corners, vars: ico_ == ico
|
filt = lambda ico_, corners, vars: ico_ == ico
|
||||||
else:
|
else:
|
||||||
filt = lambda ico_, corners, vars: True
|
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):
|
def mkb(val):
|
||||||
return val
|
return val
|
||||||
|
|
@ -730,35 +782,63 @@ def corners2csv(bs):
|
||||||
|
|
||||||
|
|
||||||
def tilej_stats(tilej):
|
def tilej_stats(tilej):
|
||||||
stats = {}
|
def tile_stats():
|
||||||
for etype in ('pips', 'wires'):
|
stats = {}
|
||||||
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'):
|
for etype in ('pips', 'wires'):
|
||||||
pips = tile[etype]
|
tm = stats.setdefault(etype, {})
|
||||||
for k, v in pips.items():
|
tm['net'] = 0
|
||||||
stats[etype]['net'] += 1
|
tm['solved'] = [0, 0, 0, 0]
|
||||||
for i in range(4):
|
tm['covered'] = [0, 0, 0, 0]
|
||||||
if pips[k][i]:
|
|
||||||
stats[etype]['solved'][i] += 1
|
for tile in tilej['tiles'].values():
|
||||||
if pips[k][i] is not None:
|
for etype in ('pips', 'wires'):
|
||||||
stats[etype]['covered'][i] += 1
|
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():
|
for corner, corneri in corner_s2i.items():
|
||||||
print('Corner %s' % corner)
|
print('Corner %s' % corner)
|
||||||
for etype in ('pips', 'wires'):
|
for etype in ('pips', 'wires'):
|
||||||
net = stats[etype]['net']
|
net = tstats[etype]['net']
|
||||||
solved = stats[etype]['solved'][corneri]
|
solved = tstats[etype]['solved'][corneri]
|
||||||
covered = stats[etype]['covered'][corneri]
|
covered = tstats[etype]['covered'][corneri]
|
||||||
print(
|
print(
|
||||||
' %s: %u / %u solved, %u / %u covered' %
|
' %s: %u / %u solved, %u / %u covered' %
|
||||||
(etype, solved, net, covered, net))
|
(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):
|
def load_bounds(bounds_csv, corner):
|
||||||
Ads, b = loadc_Ads_b([bounds_csv], corner, ico=ico)
|
Ads, b = loadc_Ads_b([bounds_csv], corner)
|
||||||
return Ads2bounds(Ads, b)
|
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
|
# 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
|
Subtract equations from each other to generate additional constraints
|
||||||
Helps provide additional guidance to solver for realistic delays
|
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
|
# Each iteration one more column is allowed until all columns are included
|
||||||
# (and the system is stable)
|
# (and the system is stable)
|
||||||
col_lim = 15
|
|
||||||
di = 0
|
di = 0
|
||||||
while True:
|
while True:
|
||||||
print
|
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)
|
col_dist(Ads, 'derive done iter %d, lim %d' % (di, col_lim), lim=12)
|
||||||
|
|
||||||
rows = len(Ads)
|
rows = len(Ads)
|
||||||
|
di += 1
|
||||||
|
dend = len(b)
|
||||||
# possible that a new equation was generated and taken away, but close enough
|
# 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
|
break
|
||||||
col_lim += col_lim / 5
|
col_lim += col_lim / 5
|
||||||
di += 1
|
|
||||||
|
|
||||||
dend = len(b)
|
|
||||||
print('')
|
print('')
|
||||||
print('Derive net: %d => %d' % (dstart, dend))
|
print('Derive net: %d => %d' % (dstart, dend))
|
||||||
print('')
|
print('')
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,10 @@ def solve_save(outfn, xvals, names, corner, save_zero=True, verbose=False):
|
||||||
print(
|
print(
|
||||||
'Wrote: %u / %u constrained delays, %u zeros' %
|
'Wrote: %u / %u constrained delays, %u zeros' %
|
||||||
(nonzeros, len(names), 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(
|
def run(
|
||||||
|
|
@ -165,7 +168,7 @@ def run(
|
||||||
verbose=False,
|
verbose=False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
print('Loading data')
|
print('Loading data')
|
||||||
Ads, b = loadc_Ads_b(fns_in, corner, ico=True)
|
Ads, b = loadc_Ads_b(fns_in, corner)
|
||||||
|
|
||||||
# Remove duplicate rows
|
# Remove duplicate rows
|
||||||
# is this necessary?
|
# is this necessary?
|
||||||
|
|
@ -193,7 +196,7 @@ def run(
|
||||||
Used primarily for multiple optimization passes, such as different algorithms or additional constraints
|
Used primarily for multiple optimization passes, such as different algorithms or additional constraints
|
||||||
'''
|
'''
|
||||||
if bounds_csv:
|
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)
|
bounds = Ads2bounds(Ads2, b2)
|
||||||
assert len(bounds), 'Failed to load bounds'
|
assert len(bounds), 'Failed to load bounds'
|
||||||
rows_old = len(Ads)
|
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": {
|
"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:
|
for fnin in fnins:
|
||||||
tileji = json.load(open(fnin, 'r'))
|
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):
|
merge_tiles(tileji['tiles'], tilejo['tiles'])
|
||||||
for pipk, pipvi in tilevi[etype].items():
|
merge_sites(tileji['sites'], tilejo['sites'])
|
||||||
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)
|
|
||||||
|
|
||||||
process_type('pips')
|
|
||||||
process_type('wires')
|
|
||||||
return tilejo
|
return tilejo
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -110,15 +135,11 @@ def check_corner_minmax(tilej, verbose=False):
|
||||||
timfuz.tilej_stats(tilej)
|
timfuz.tilej_stats(tilej)
|
||||||
|
|
||||||
|
|
||||||
def check_corners_minmax(tilej, verbose=False):
|
|
||||||
# TODO: check fast vs slow
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def run(fnins, fnout, verbose=False):
|
def run(fnins, fnout, verbose=False):
|
||||||
tilejo = build_tilejo(fnins)
|
tilejo = build_tilejo(fnins)
|
||||||
check_corner_minmax(tilejo)
|
check_corner_minmax(tilejo)
|
||||||
check_corners_minmax(tilejo)
|
# XXX: check fast vs slow?
|
||||||
|
# check_corners_minmax(tilejo)
|
||||||
json.dump(
|
json.dump(
|
||||||
tilejo,
|
tilejo,
|
||||||
open(fnout, 'w'),
|
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
|
set -ex
|
||||||
|
|
||||||
test $# = 1
|
# for some reason on sourced script set -e doesn't work
|
||||||
test ! -e $1
|
test $# = 1 || exit 1
|
||||||
mkdir $1
|
test ! -e "$1"
|
||||||
cd $1
|
mkdir "$1"
|
||||||
|
cd "$1"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue