mirror of https://github.com/openXC7/prjxray.git
Merge pull request #897 from antmicro/clean-007
Fuzzers: 007-timings: remove unused code
This commit is contained in:
commit
0548c105e4
|
|
@ -1,32 +1,10 @@
|
|||
# for now hammering on just picorv32
|
||||
# consider instead aggregating multiple projects
|
||||
PRJ?=oneblinkw
|
||||
PRJN?=1
|
||||
|
||||
run: all
|
||||
all: build/timgrid-v.json bel/build/sdf
|
||||
all: bel/build/sdf
|
||||
touch run.ok
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
cd speed && $(MAKE) clean
|
||||
cd timgrid && $(MAKE) clean
|
||||
cd bel && $(MAKE) clean
|
||||
cd projects/$(PRJ) && $(MAKE) clean
|
||||
|
||||
bel/build/sdf:
|
||||
cd bel && $(MAKE)
|
||||
|
||||
speed/build/speed.json:
|
||||
cd speed && $(MAKE)
|
||||
|
||||
timgrid/build/timgrid.json:
|
||||
cd timgrid && $(MAKE)
|
||||
|
||||
build/timgrid-v.json: projects/$(PRJ)/build/timgrid-v.json
|
||||
mkdir -p build
|
||||
cp projects/$(PRJ)/build/timgrid-v.json build/timgrid-v.json
|
||||
|
||||
projects/$(PRJ)/build/timgrid-v.json: speed/build/speed.json timgrid/build/timgrid.json
|
||||
cd projects/$(PRJ) && $(MAKE) N=$(PRJN)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,275 +0,0 @@
|
|||
# Timing analysis fuzzer (timfuz)
|
||||
|
||||
WIP: 2018-09-10: this process is just starting together and is going to get significant cleanup. But heres the general idea
|
||||
|
||||
This runs various designs through Vivado and processes the
|
||||
resulting timing informationin order to create very simple timing models.
|
||||
While Vivado might have more involved models (say RC delays, fanout, etc),
|
||||
timfuz creates simple models that bound realistic min and max element delays.
|
||||
|
||||
Currently this document focuses exclusively on fabric timing delays.
|
||||
|
||||
|
||||
## Quick start
|
||||
|
||||
```
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
This will take a relatively long time (say 45 min) and generate build/timgrid-v.json.
|
||||
You can do a quicker test run (say 3 min) using:
|
||||
|
||||
```
|
||||
make PRJ=oneblinkw PRJN=1 -j$(nproc)
|
||||
```
|
||||
|
||||
|
||||
## Vivado background
|
||||
|
||||
Examples are for a XC750T on Vivado 2017.2.
|
||||
|
||||
TODO maybe move to: https://github.com/SymbiFlow/prjxray/wiki/Timing
|
||||
|
||||
|
||||
### Speed index
|
||||
|
||||
Vivado seems to associate each delay model with a "speed index".
|
||||
The fabric has these in two elements: wires (ie one delay element per tile) and pips.
|
||||
For example, LUT output node A (ex: CLBLL_L_X12Y100/CLBLL_LL_A) has a single wire, also called CLBLL_L_X12Y100/CLBLL_LL_A.
|
||||
This has speed index 733. Speed models can be queried and we find this corresponds to model C_CLBLL_LL_A.
|
||||
|
||||
There are various speed model types:
|
||||
* bel_delay
|
||||
* buffer
|
||||
* buffer_switch
|
||||
* content_version
|
||||
* functional
|
||||
* inpin
|
||||
* outpin
|
||||
* parameters
|
||||
* pass_transistor
|
||||
* switch
|
||||
* table_lookup
|
||||
* tl_buffer
|
||||
* vt_limits
|
||||
* wire
|
||||
|
||||
IIRC the interconnect is only composed of switch and wire types.
|
||||
|
||||
Indices with value 65535 (0xFFFF) never appear. Presumably these are unused models.
|
||||
They are used for some special models such as those of type "content_version".
|
||||
For example, the "xilinx" model is of type "content_version".
|
||||
|
||||
There are also "cost codes", but these seem to be very course (only around 30 of these)
|
||||
and are suspected to be related more to PnR than timing model.
|
||||
|
||||
|
||||
### Timing paths
|
||||
|
||||
The Vivado timing analyzer can easily output the following:
|
||||
* Full: delay from BEL pin to BEL pin
|
||||
* Interconnect only (ICO): delay from BEL pin to BEL pin, but only report interconnect delays (ie exclude site delays)
|
||||
|
||||
There is also theoretically an option to report delays up to a specific pip,
|
||||
but this option is poorly documented and I was unable to get it to work.
|
||||
|
||||
Each timing path reports a fast process and a slow process min and max value. So four process values are reported in total:
|
||||
* fast_max
|
||||
* fast_min
|
||||
* slow_max
|
||||
* slow_min
|
||||
|
||||
For example, if the device is end of life, was poorly made, and at an extreme temperature, the delay may be up to the slow_max value.
|
||||
Since ICO can be reported for each of these, fully analyzing a timing path results in 8 values.
|
||||
|
||||
Finally, part of this was analyzing tile regularity to discover what a reasonably compact timing model was.
|
||||
We verified that all tiles of the same type have exactly the same delay elements.
|
||||
|
||||
|
||||
|
||||
## Methodology
|
||||
|
||||
Make sure you've read the Vivado background section first
|
||||
|
||||
|
||||
### Background
|
||||
|
||||
This section briefly describes some of the mathmatics used by this technique that readers may not be familiar with.
|
||||
These definitions are intended to be good enough to provide a high level understanding and may not be precise.
|
||||
|
||||
Numerical analysis: the study of algorithms that use numerical approximation (as opposed to general symbolic manipulations)
|
||||
|
||||
numpy: a popular numerical analysis python library. Often written np (import numpy as np).
|
||||
|
||||
scipy: provides higher level functionality on top of numpy
|
||||
|
||||
sympy ("symbolic python"): like numpy, but is designed to work with rational numbers.
|
||||
For example, python actually stores 0.1 as 0.1000000000000000055511151231257827021181583404541015625.
|
||||
However, sympy can represent this as the fraction 1/10, eliminating numerical approximation issues.
|
||||
|
||||
Least squares (ex: scipy.optimize.least_squares): approximation method to do a best fit of several variables to a set of equations.
|
||||
For example, given the equations "x = 1" and "x = 2" there isn't an exact solution.
|
||||
However, "x = 1.5" is a good compromise since its reasonably solves both equations.
|
||||
|
||||
Linear programming (ex: scipy.optimize.linprog aka linprog): approximation method that finds a set of variables that satisfy a set of inequalities.
|
||||
For example,
|
||||
|
||||
Reduced row echelon form (RREF, ex: sympy.Matrix.rref): the simplest form that a system of linear equations can be solved to.
|
||||
For example, given "x = 1" and "x + y = 9", one can solve for "x = 1" and "y = 8".
|
||||
However, given "x + y = 1" and "x + y + z = 9", there aren't enough variables to solve this fully.
|
||||
In this case RREF provides a best effort by giving the ratios between correlated variables.
|
||||
One variable is normalized to 1 in each of these ratios and is called the "pivot".
|
||||
Note that if numpy.linalg.solve encounters an unsolvable matrix it may either complain
|
||||
or generate a false solution due to numerical approximation issues.
|
||||
|
||||
|
||||
### What didn't work
|
||||
|
||||
First some quick background on things that didn't work to illustrate why the current approach was chosen.
|
||||
I first tried to directly through things into linprog, but it unfairly weighted towards arbitrary shared variables. For example, feeding in:
|
||||
* t0 >= 10
|
||||
* t0 + t1 >= 100
|
||||
|
||||
It would declare "t0 = 100", "t1 = 0" instead of the more intuitive "t0 = 10", "t1 = 90".
|
||||
I tried to work around this in several ways, notably subtracting equations from each other to produce additional constraints.
|
||||
This worked okay, but was relatively slow and wasn't approaching nearly solved solutions, even when throwing a lot of data at it.
|
||||
|
||||
Next we tried randomly combining a bunch of the equations together and solving them like a regular linear algebra matrix (numpy.linalg.solve).
|
||||
However, this illustrated that the system was under-constrained.
|
||||
Further analysis revealed that there are some delay element combinations that simply can't be linearly separated.
|
||||
This was checked primarily using numpy.linalg.matrix_rank, with some use of numpy.linalg.slogdet.
|
||||
matrix_rank was preferred over slogdet since its more flexible against non-square matrices.
|
||||
|
||||
|
||||
### Process
|
||||
|
||||
Above ultimately led to the idea that we should come up with a set of substitutions that would make the system solvable. This has several advantages:
|
||||
* Easy to evaluate which variables aren't covered well enough by source data
|
||||
* Easy to evaluate which variables weren't solved properly (if its fully constrained it should have had a non-zero delay)
|
||||
|
||||
At a high level, the above learnings gave this process:
|
||||
* Find correlated variables by using RREF (sympy.Matrix.rref) to create variable groups
|
||||
- Note pivots
|
||||
- You must input a fractional type (ex: fractions.Fraction, but surprisingly not int) to get exact results, otherwise it seems to fall back to numerical approximation
|
||||
- This is by far the most computationally expensive step
|
||||
- Mixing RREF substitutions from one data set to another may not be recommended
|
||||
* Use RREF result to substitute groups on input data, creating new meta variables, but ultimately reducing the number of columns
|
||||
* Pick a corner
|
||||
- Examples assume fast_max, but other corners are applicable with appropriate column and sign changes
|
||||
* De-duplicate by removing equations that are less constrained
|
||||
- Ex: if solving for a max corner and given:
|
||||
- t0 + t1 >= 10
|
||||
- t0 + t1 >= 12
|
||||
- The first equation is redundant since the second provides a stricter constraint
|
||||
- This significantly reduces computational time
|
||||
* Use least squares (scipy.optimize.least_squares) to fit variables near input constraints
|
||||
- Helps fairly weight delays vs the original input constraints
|
||||
- Does not guarantee all constraints are met. For example, if this was put in (ignoring these would have been de-duplicated):
|
||||
- t0 = 10
|
||||
- t0 = 12
|
||||
- It may decide something like t0 = 11, which means that the second constraint was not satisfied given we actually want t0 >= 12
|
||||
* Use linear programming (scipy.optimize.linprog aka linprog) to formally meet all remaining constraints
|
||||
- Start by filtering out all constraints that are already met. This should eliminate nearly all equations
|
||||
* Map resulting constraints onto different tile types
|
||||
- Group delays map onto the group pivot variable, typically setting other elements to 0 (if the processed set is not the one used to create the pivots they may be non-zero)
|
||||
|
||||
|
||||
## TODO, suggestions
|
||||
|
||||
Includes
|
||||
* Consider removing rref
|
||||
- Intended to understand what can't be solved, maybe not useful in production
|
||||
* Need more coverage
|
||||
- Consider instrumenting all fuzzers to output data to feed into timing anlayzer
|
||||
- Justification: we need a lot of weird cases, we have code that does that in the other fuzzers
|
||||
* Tune performance parameters
|
||||
- Can we improve quality of results?
|
||||
- Do we have a good enough quality checker? (solve_qor.py)
|
||||
- Compare our vs Xilinx output on random designs
|
||||
- Does the solve take too long? What could speed it up?
|
||||
* Investigate min corner
|
||||
- Tends to solve towards 0, making this not useful
|
||||
- Low priority: most designs just close timing with setup time
|
||||
* Investigate characterizing full RC timing model
|
||||
* Can we split pivot delays among elements instead of entirely into pivot?
|
||||
* Consider breaking out timing analyzer into its own project / library so it can be re-used on other projects
|
||||
* Review "--massage". Does this help?
|
||||
* Review computed site delays vs published Xilinx numbers (DC and AC Switching Characteristics)
|
||||
* Fabric delay models are RC, but are the site delay models RC as well or maybe just linear?
|
||||
* Can we create antenna nets to get simpler solves?
|
||||
* Can we get tcl timing analyzer to analyze a partial route?
|
||||
- Option says you should be able to do this
|
||||
- I could not actually get it to work
|
||||
|
||||
|
||||
### Improve test cases
|
||||
|
||||
Test cases are somewhat random right now. We could make much more targetted cases using custom routing to improve various fanout estimates and such.
|
||||
Also there are a lot more elements that are not covered.
|
||||
At a minimum these should be moved to their own directory.
|
||||
|
||||
|
||||
### ZERO models
|
||||
|
||||
Background: there are a number of speed models with the name ZERO in them.
|
||||
These generally seem to be zero delay, although needs more investigation.
|
||||
|
||||
Example: see pseudo pip item below
|
||||
|
||||
The timing models will probably significantly improve if these are removed.
|
||||
In the past I was removing them, but decided to keep them in for now in the spirit of being more conservative.
|
||||
|
||||
They include:
|
||||
* _BSW_CLK_ZERO
|
||||
* BSW_CLK_ZERO
|
||||
* _BSW_ZERO
|
||||
* BSW_ZERO
|
||||
* _B_ZERO
|
||||
* B_ZERO
|
||||
* C_CLK_ZERO
|
||||
* C_DSP_ZERO
|
||||
* C_ZERO
|
||||
* I_ZERO
|
||||
* _O_ZERO
|
||||
* O_ZERO
|
||||
* RC_ZERO
|
||||
* _R_ZERO
|
||||
* R_ZERO
|
||||
|
||||
|
||||
### Virtual switchboxes
|
||||
|
||||
Background: several low level configuration details are abstracted with virtual configurable elements.
|
||||
For example, LUT inputs can be rearranged to reduce routing congestion.
|
||||
However, the LUT configuratioon must be changed to match the switched inputs.
|
||||
This is handled by the CLBLL_L_INTER switchbox, which doesn't encode any physical configuration bits.
|
||||
However, this contains PIPs with delay models.
|
||||
|
||||
For example, LUT A, input A1 has node CLBLM_M_A1 coming from pip junction CLBLM_M_A1 has PIP CLBLM_IMUX7->CLBLM_M_A1
|
||||
with speed index 659 (R_ZERO).
|
||||
|
||||
This might be further evidence on related issue that ZERO models should probably be removed.
|
||||
|
||||
|
||||
### Incporporate fanout
|
||||
|
||||
We could probably significantly improve model granularity by studying delay impact on fanout
|
||||
|
||||
|
||||
### Investigate RC delays
|
||||
|
||||
Suspect accuracy could be significantly improved by moving to SPICE based models. But this will take significantly more characterization
|
||||
|
||||
|
||||
### Characterize real hardware
|
||||
|
||||
A few people have expressed interest on running tests on real hardware. Will take some thought given we don't have direct access
|
||||
|
||||
|
||||
### Review approximation errors
|
||||
|
||||
Ex: one known issue is that the objective function linearly weights small and large delays.
|
||||
This is only recommended when variables are approximately the same order of magnitude.
|
||||
For example, carry chain delays are on the order of 7 ps while other delays are 100 ps.
|
||||
Its very easy to put a large delay on the carry chain while it could have been more appropriately put somewhere else.
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
'''
|
||||
pr0ntools
|
||||
Benchmarking utility
|
||||
Copyright 2010 John McMaster
|
||||
'''
|
||||
|
||||
import time
|
||||
|
||||
|
||||
def time_str(delta):
|
||||
fraction = delta % 1
|
||||
delta -= fraction
|
||||
delta = int(delta)
|
||||
seconds = delta % 60
|
||||
delta /= 60
|
||||
minutes = delta % 60
|
||||
delta /= 60
|
||||
hours = delta
|
||||
return '%02d:%02d:%02d.%04d' % (hours, minutes, seconds, fraction * 10000)
|
||||
|
||||
|
||||
class Benchmark:
|
||||
start_time = None
|
||||
end_time = None
|
||||
|
||||
def __init__(self, max_items=None):
|
||||
# For the lazy
|
||||
self.start_time = time.time()
|
||||
self.end_time = None
|
||||
self.max_items = max_items
|
||||
self.cur_items = 0
|
||||
|
||||
def start(self):
|
||||
self.start_time = time.time()
|
||||
self.end_time = None
|
||||
self.cur_items = 0
|
||||
|
||||
def stop(self):
|
||||
self.end_time = time.time()
|
||||
|
||||
def advance(self, n=1):
|
||||
self.cur_items += n
|
||||
|
||||
def set_cur_items(self, n):
|
||||
self.cur_items = n
|
||||
|
||||
def delta_s(self):
|
||||
if self.end_time:
|
||||
return self.end_time - self.start_time
|
||||
else:
|
||||
return time.time() - self.start_time
|
||||
|
||||
def __str__(self):
|
||||
if self.end_time:
|
||||
return time_str(self.end_time - self.start_time)
|
||||
elif self.max_items:
|
||||
cur_time = time.time()
|
||||
delta_t = cur_time - self.start_time
|
||||
rate_s = 'N/A'
|
||||
if delta_t > 0.000001:
|
||||
rate = self.cur_items / (delta_t)
|
||||
rate_s = '%f items / sec' % rate
|
||||
if rate == 0:
|
||||
eta_str = 'inf'
|
||||
else:
|
||||
remaining = (self.max_items - self.cur_items) / rate
|
||||
eta_str = time_str(remaining)
|
||||
else:
|
||||
eta_str = "indeterminate"
|
||||
return '%d / %d, ETA: %s @ %s' % (
|
||||
self.cur_items, self.max_items, eta_str, rate_s)
|
||||
else:
|
||||
return time_str(time.time() - self.start_time)
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, Ar_di2np, Ar_ds2t, A_di2ds, A_ds2di, loadc_Ads_b, index_names, A_ds2np, load_sub, run_sub_json
|
||||
import numpy as np
|
||||
import glob
|
||||
import json
|
||||
import math
|
||||
from collections import OrderedDict
|
||||
from fractions import Fraction
|
||||
|
||||
|
||||
def Adi2matrix_random(A_ubd, b_ub, names):
|
||||
# random assignment
|
||||
# was making some empty rows
|
||||
|
||||
A_ret = [np.zeros(len(names)) for _i in range(len(names))]
|
||||
b_ret = np.zeros(len(names))
|
||||
|
||||
for row, b in zip(A_ubd, b_ub):
|
||||
# Randomly assign to a row
|
||||
dst_rowi = random.randint(0, len(names) - 1)
|
||||
rownp = Ar_di2np(row, cols=len(names), sf=1)
|
||||
|
||||
A_ret[dst_rowi] = np.add(A_ret[dst_rowi], rownp)
|
||||
b_ret[dst_rowi] += b
|
||||
return A_ret, b_ret
|
||||
|
||||
|
||||
def Ads2matrix_linear(Ads, b):
|
||||
names, Adi = A_ds2di(Ads)
|
||||
cols = len(names)
|
||||
rows_out = len(b)
|
||||
|
||||
A_ret = [np.zeros(cols) for _i in range(rows_out)]
|
||||
b_ret = np.zeros(rows_out)
|
||||
|
||||
dst_rowi = 0
|
||||
for row_di, row_b in zip(Adi, b):
|
||||
row_np = Ar_di2np(row_di, cols)
|
||||
|
||||
A_ret[dst_rowi] = np.add(A_ret[dst_rowi], row_np)
|
||||
b_ret[dst_rowi] += row_b
|
||||
dst_rowi = (dst_rowi + 1) % rows_out
|
||||
return A_ret, b_ret
|
||||
|
||||
|
||||
def pmatrix(Anp, s):
|
||||
import sympy
|
||||
msym = sympy.Matrix(Anp)
|
||||
print(s)
|
||||
sympy.pprint(msym)
|
||||
|
||||
|
||||
def pds(Ads, s):
|
||||
names, Anp = A_ds2np(Ads)
|
||||
pmatrix(Anp, s)
|
||||
print('Names: %s' % (names, ))
|
||||
|
||||
|
||||
def run(fns_in, sub_json=None, verbose=False):
|
||||
assert len(fns_in) > 0
|
||||
# arbitrary corner...data is thrown away
|
||||
Ads, b = loadc_Ads_b(fns_in, "slow_max")
|
||||
|
||||
if sub_json:
|
||||
print('Subbing JSON %u rows' % len(Ads))
|
||||
#pds(Ads, 'Orig')
|
||||
names_old = index_names(Ads)
|
||||
run_sub_json(Ads, sub_json, verbose=verbose)
|
||||
names_new = index_names(Ads)
|
||||
print("Sub: %u => %u names" % (len(names_old), len(names_new)))
|
||||
print(names_new)
|
||||
print('Subbed JSON %u rows' % len(Ads))
|
||||
names = names_new
|
||||
#pds(Ads, 'Sub')
|
||||
else:
|
||||
names = index_names(Ads)
|
||||
|
||||
# Squash into a matrix
|
||||
# A_ub2, b_ub2 = Adi2matrix_random(A_ubd, b, names)
|
||||
Amat, _bmat = Ads2matrix_linear(Ads, b)
|
||||
#pmatrix(Amat, 'Matrix')
|
||||
'''
|
||||
The matrix must be fully ranked to even be considered reasonable
|
||||
Even then, floating point error *possibly* could make it fully ranked, although probably not since we have whole numbers
|
||||
Hence the slogdet check
|
||||
'''
|
||||
print
|
||||
# https://docs.scipy.org/doc/numpy-dev/reference/generated/numpy.linalg.matrix_rank.html
|
||||
rank = np.linalg.matrix_rank(Amat)
|
||||
print('rank: %s / %d col' % (rank, len(names)))
|
||||
# doesn't work on non-square matrices
|
||||
if 0:
|
||||
# https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.linalg.slogdet.html
|
||||
sign, logdet = np.linalg.slogdet(Amat)
|
||||
# If the determinant is zero, then sign will be 0 and logdet will be -Inf
|
||||
if sign == 0 and logdet == float('-inf'):
|
||||
print('slogdet :( : 0')
|
||||
else:
|
||||
print('slogdet :) : %s, %s' % (sign, logdet))
|
||||
if rank != len(names):
|
||||
raise Exception(
|
||||
"Matrix not fully ranked w/ %u / %u" % (rank, len(names)))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Check if sub.json would make a linear equation solvable')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--sub-json', help='')
|
||||
parser.add_argument('fns_in', nargs='*', help='timing4i.csv input files')
|
||||
args = parser.parse_args()
|
||||
# Store options in dict to ease passing through functions
|
||||
bench = Benchmark()
|
||||
|
||||
fns_in = args.fns_in
|
||||
if not fns_in:
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = None
|
||||
if args.sub_json:
|
||||
sub_json = load_sub(args.sub_json)
|
||||
|
||||
try:
|
||||
run(sub_json=sub_json, fns_in=fns_in, verbose=args.verbose)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, simplify_rows, loadc_Ads_b
|
||||
import glob
|
||||
|
||||
|
||||
def run(fout, fns_in, corner, verbose=0):
|
||||
Ads, b = loadc_Ads_b(fns_in, corner, ico=True)
|
||||
Ads, b = simplify_rows(Ads, b, corner=corner)
|
||||
|
||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
||||
for row_b, row_ds in zip(b, Ads):
|
||||
# write in same format, but just stick to this corner
|
||||
out_b = [str(row_b) for _i in range(4)]
|
||||
ico = '1'
|
||||
items = [ico, ' '.join(out_b)]
|
||||
|
||||
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='Create a .csv with a single process corner')
|
||||
|
||||
parser.add_argument('--verbose', type=int, help='')
|
||||
parser.add_argument(
|
||||
'--auto-name',
|
||||
action='store_true',
|
||||
help='timing4i.csv => timing4c.csv')
|
||||
parser.add_argument('--out', default=None, help='Output csv')
|
||||
parser.add_argument('--corner', help='Output csv')
|
||||
parser.add_argument('fns_in', nargs='+', help='timing4i.csv input 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('timing4i.csv', 'timing4c.csv')
|
||||
assert fnout != fnin, 'Expect timing4i.csv in'
|
||||
else:
|
||||
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_*/timing4i.csv')
|
||||
|
||||
run(fout=fout, fns_in=fns_in, corner=args.corner, verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, loadc_Ads_bs, index_names, load_sub, run_sub_json, instances
|
||||
|
||||
|
||||
def gen_group(fnin, sub_json, strict=False, verbose=False):
|
||||
print('Loading data')
|
||||
Ads, bs = loadc_Ads_bs([fnin])
|
||||
|
||||
print('Sub: %u rows' % len(Ads))
|
||||
iold = instances(Ads)
|
||||
names_old = index_names(Ads)
|
||||
run_sub_json(Ads, sub_json, strict=strict, verbose=verbose)
|
||||
names = index_names(Ads)
|
||||
print("Sub: %u => %u names" % (len(names_old), len(names)))
|
||||
print('Sub: %u => %u instances' % (iold, instances(Ads)))
|
||||
|
||||
for row_ds, row_bs in zip(Ads, bs):
|
||||
yield row_ds, row_bs
|
||||
|
||||
|
||||
def run(fns_in, fnout, sub_json, strict=False, verbose=False):
|
||||
with open(fnout, 'w') as fout:
|
||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
||||
for fn_in in fns_in:
|
||||
for row_ds, row_bs in gen_group(fn_in, sub_json, strict=strict):
|
||||
row_ico = 1
|
||||
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='Substitute .csv to group correlated variables')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--strict', action='store_true', help='')
|
||||
parser.add_argument('--sub-csv', help='')
|
||||
parser.add_argument(
|
||||
'--sub-json',
|
||||
required=True,
|
||||
help='Group substitutions to make fully ranked')
|
||||
parser.add_argument('--out', help='Output sub.json substitution result')
|
||||
parser.add_argument('fns_in', nargs='+', help='timing4i.csv input files')
|
||||
args = parser.parse_args()
|
||||
# Store options in dict to ease passing through functions
|
||||
bench = Benchmark()
|
||||
|
||||
fns_in = args.fns_in
|
||||
if not fns_in:
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = load_sub(args.sub_json)
|
||||
|
||||
try:
|
||||
run(
|
||||
fns_in,
|
||||
args.out,
|
||||
sub_json=sub_json,
|
||||
strict=args.strict,
|
||||
verbose=args.verbose)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, loadc_Ads_bs, load_sub, Ads2bounds, corners2csv, corner_s2i
|
||||
|
||||
|
||||
def gen_flat(fns_in, sub_json, corner=None):
|
||||
Ads, bs = loadc_Ads_bs(fns_in)
|
||||
bounds = Ads2bounds(Ads, bs)
|
||||
# Elements with zero delay assigned due to sub group
|
||||
group_zeros = set()
|
||||
# Elements with a concrete delay
|
||||
nonzeros = set()
|
||||
|
||||
if corner:
|
||||
zero_row = [None, None, None, None]
|
||||
zero_row[corner_s2i[corner]] = 0
|
||||
else:
|
||||
zero_row = None
|
||||
|
||||
for bound_name, bound_bs in bounds.items():
|
||||
sub = sub_json['subs'].get(bound_name, None)
|
||||
if bound_name in sub_json['zero_names']:
|
||||
if zero_row:
|
||||
yield bound_name, 0
|
||||
elif sub:
|
||||
#print('sub', sub)
|
||||
# put entire delay into pivot
|
||||
pivot = sub_json['pivots'][bound_name]
|
||||
assert pivot not in group_zeros
|
||||
nonzeros.add(pivot)
|
||||
non_pivot = set(sub.keys() - set([pivot]))
|
||||
#for name in non_pivot:
|
||||
# assert name not in nonzeros, (pivot, name, nonzeros)
|
||||
group_zeros.update(non_pivot)
|
||||
#print('yield PIVOT', pivot)
|
||||
yield pivot, bound_bs
|
||||
else:
|
||||
nonzeros.add(bound_name)
|
||||
yield bound_name, bound_bs
|
||||
# non-pivots can appear multiple times, but they should always be zero
|
||||
# however, due to substitution limitations, just warn
|
||||
violations = group_zeros.intersection(nonzeros)
|
||||
if len(violations):
|
||||
print('WARNING: %s non-0 non-pivot' % (len(violations)))
|
||||
|
||||
# XXX: how to best handle these?
|
||||
# should they be fixed 0?
|
||||
if zero_row:
|
||||
# ZERO names should always be zero
|
||||
#print('ZEROs: %u' % len(sub_json['zero_names']))
|
||||
for zero in sub_json['zero_names']:
|
||||
#print('yield ZERO', zero)
|
||||
yield zero, zero_row
|
||||
|
||||
real_zeros = group_zeros - violations
|
||||
print(
|
||||
'Zero candidates: %u w/ %u non-pivot conflicts => %u zeros as solved'
|
||||
% (len(group_zeros), len(violations), len(real_zeros)))
|
||||
# Only yield elements not already yielded
|
||||
for zero in real_zeros:
|
||||
#print('yield solve-0', zero)
|
||||
yield zero, zero_row
|
||||
|
||||
|
||||
def run(fns_in, fnout, sub_json, corner=None, verbose=False):
|
||||
with open(fnout, 'w') as fout:
|
||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
||||
for name, corners in sorted(list(gen_flat(fns_in, sub_json,
|
||||
corner=corner))):
|
||||
row_ico = 1
|
||||
items = [str(row_ico), corners2csv(corners)]
|
||||
items.append('%u %s' % (1, name))
|
||||
fout.write(','.join(items) + '\n')
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Substitute .csv to ungroup correlated variables')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--sub-csv', help='')
|
||||
parser.add_argument(
|
||||
'--sub-json',
|
||||
required=True,
|
||||
help='Group substitutions to make fully ranked')
|
||||
parser.add_argument('--corner', default=None, help='')
|
||||
parser.add_argument('--out', default=None, help='output timing delay .csv')
|
||||
parser.add_argument(
|
||||
'fns_in',
|
||||
nargs='+',
|
||||
help='input timing delay .csv (NOTE: must be single column)')
|
||||
args = parser.parse_args()
|
||||
# Store options in dict to ease passing through functions
|
||||
bench = Benchmark()
|
||||
|
||||
sub_json = load_sub(args.sub_json)
|
||||
|
||||
try:
|
||||
run(
|
||||
args.fns_in,
|
||||
args.out,
|
||||
sub_json=sub_json,
|
||||
verbose=args.verbose,
|
||||
corner=args.corner)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
# 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"
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
#!/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()
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
N := 1
|
||||
SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
|
||||
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
|
||||
|
||||
all: $(SPECIMENS_OK)
|
||||
|
||||
$(SPECIMENS_OK):
|
||||
bash generate.sh $(subst /OK,,$@) || (if [ "$(BADPRJ_OK)" != 'Y' ] ; then exit 1; fi; exit 0)
|
||||
touch $@
|
||||
|
||||
run:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
||||
touch run.ok
|
||||
|
||||
clean:
|
||||
rm -rf specimen_[0-9][0-9][0-9]/ seg_clblx.db __pycache__ run.ok
|
||||
rm -rf vivado*.log vivado_*.str vivado*.jou design *.bits *.dcp *.bit
|
||||
rm -rf build
|
||||
|
||||
.PHONY: all run clean
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
Characterizes how attributes vary across pips, wires, and nodes. Usage:
|
||||
|
||||
```
|
||||
$ make
|
||||
$ python3 pip_unique.py
|
||||
$ python3 wire_unique.py
|
||||
$ python3 node_unique.py
|
||||
```
|
||||
|
||||
NOTE: "make" will take a long time (about 2.5 hours on my machine)
|
||||
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
source ${XRAY_GENHEADER}
|
||||
TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing
|
||||
|
||||
${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
source ../../../../utils/utils.tcl
|
||||
|
||||
proc build_design {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog ../../src/picorv32.v
|
||||
read_verilog ../top.v
|
||||
synth_design -top top
|
||||
|
||||
puts "Locking pins"
|
||||
set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||
[get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||
|
||||
puts "Package stuff"
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
puts "pblocking"
|
||||
create_pblock roi
|
||||
set roipb [get_pblocks roi]
|
||||
add_cells_to_pblock $roipb [get_cells roi]
|
||||
resize_pblock $roipb -add "$::env(XRAY_ROI)"
|
||||
|
||||
puts "randplace"
|
||||
randplace_pblock 50 roi
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
puts "dedicated route"
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
# disable combinitorial loop
|
||||
# set_property IS_ENABLED 0 [get_drc_checks {LUTLP-1}]
|
||||
#write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
proc pips_all {} {
|
||||
set outdir "."
|
||||
set fp [open "$outdir/pip_all.txt" w]
|
||||
set items [get_pips]
|
||||
puts "Items: [llength $items]"
|
||||
|
||||
set needspace 0
|
||||
set properties [list_property [lindex $items 0]]
|
||||
foreach item $items {
|
||||
set needspace 0
|
||||
foreach property $properties {
|
||||
set val [get_property $property $item]
|
||||
if {"$val" ne ""} {
|
||||
if $needspace {
|
||||
puts -nonewline $fp " "
|
||||
}
|
||||
puts -nonewline $fp "$property:$val"
|
||||
set needspace 1
|
||||
}
|
||||
}
|
||||
puts $fp ""
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
proc wires_all {} {
|
||||
set outdir "."
|
||||
set fp [open "$outdir/wire_all.txt" w]
|
||||
set items [get_wires]
|
||||
puts "Items: [llength $items]"
|
||||
|
||||
set needspace 0
|
||||
set properties [list_property [lindex $items 0]]
|
||||
foreach item $items {
|
||||
set needspace 0
|
||||
foreach property $properties {
|
||||
set val [get_property $property $item]
|
||||
if {"$val" ne ""} {
|
||||
if $needspace {
|
||||
puts -nonewline $fp " "
|
||||
}
|
||||
puts -nonewline $fp "$property:$val"
|
||||
set needspace 1
|
||||
}
|
||||
}
|
||||
puts $fp ""
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
|
||||
build_design
|
||||
pips_all
|
||||
wires_all
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def gen_nodes(fin):
|
||||
for l in fin:
|
||||
lj = {}
|
||||
l = l.strip()
|
||||
for kvs in l.split():
|
||||
name, value = kvs.split(':')
|
||||
'''
|
||||
NAME:LIOB33_SING_X0Y199/IOB_IBUF0
|
||||
|
||||
IS_BAD:0
|
||||
IS_COMPLETE:1
|
||||
IS_GND:0 IS_INPUT_PIN:1 IS_OUTPUT_PIN:0 IS_PIN:1 IS_VCC:0
|
||||
NUM_WIRES:2
|
||||
PIN_WIRE:1
|
||||
'''
|
||||
if name in ('COST_CODE', 'SPEED_CLASS'):
|
||||
value = int(value)
|
||||
lj[name] = value
|
||||
|
||||
tile_type, xy, wname = re.match(
|
||||
r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
|
||||
lj['tile_type'] = tile_type
|
||||
lj['xy'] = xy
|
||||
lj['wname'] = wname
|
||||
|
||||
lj['l'] = l
|
||||
|
||||
yield lj
|
||||
|
||||
|
||||
def run(node_fin, verbose=0):
|
||||
refnodes = {}
|
||||
nodei = 0
|
||||
for nodei, anode in enumerate(gen_nodes(node_fin)):
|
||||
|
||||
def getk(anode):
|
||||
return anode['wname']
|
||||
#return (anode['tile_type'], anode['wname'])
|
||||
|
||||
if nodei % 100000 == 0:
|
||||
print('Check node %d, %u node types' % (nodei, len(refnodes)))
|
||||
# Existing node?
|
||||
try:
|
||||
refnode = refnodes[getk(anode)]
|
||||
except KeyError:
|
||||
# Set as reference
|
||||
refnodes[getk(anode)] = anode
|
||||
continue
|
||||
# Verify equivilence
|
||||
for k in (
|
||||
'SPEED_CLASS',
|
||||
'COST_CODE',
|
||||
'COST_CODE_NAME',
|
||||
'IS_BAD',
|
||||
'IS_COMPLETE',
|
||||
'IS_GND',
|
||||
'IS_VCC',
|
||||
):
|
||||
if k in refnode and k in anode:
|
||||
|
||||
def fail():
|
||||
print('Mismatch on %s' % k)
|
||||
print(refnode[k], anode[k])
|
||||
print(refnode['l'])
|
||||
print(anode['l'])
|
||||
#assert 0
|
||||
|
||||
if k == 'SPEED_CLASS':
|
||||
# Parameters known to effect SPEED_CLASS
|
||||
# Verify at least one parameter is different
|
||||
if refnode[k] != anode[k]:
|
||||
for k2 in ('IS_PIN', 'IS_INPUT_PIN', 'IS_OUTPUT_PIN',
|
||||
'PIN_WIRE', 'NUM_WIRES'):
|
||||
if refnode[k2] != anode[k2]:
|
||||
break
|
||||
else:
|
||||
if 0:
|
||||
print
|
||||
fail()
|
||||
elif refnode[k] != anode[k]:
|
||||
print
|
||||
fail()
|
||||
# A key in one but not the other?
|
||||
elif k in refnode or k in anode:
|
||||
assert 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Determines which info is consistent across nodes with the same name')
|
||||
|
||||
parser.add_argument('--verbose', type=int, help='')
|
||||
parser.add_argument(
|
||||
'node_fn_in',
|
||||
default='specimen_001/wire_all.txt',
|
||||
nargs='?',
|
||||
help='Input file')
|
||||
args = parser.parse_args()
|
||||
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
'''
|
||||
Triaging tool to help understand where we need more timing coverage
|
||||
Finds correlated variables to help make better test cases
|
||||
'''
|
||||
|
||||
from timfuz import Benchmark, Ar_di2np, loadc_Ads_b, index_names, A_ds2np, simplify_rows
|
||||
import numpy as np
|
||||
import glob
|
||||
import math
|
||||
import json
|
||||
import sympy
|
||||
from collections import OrderedDict
|
||||
from fractions import Fraction
|
||||
import random
|
||||
from sympy import Rational
|
||||
|
||||
|
||||
def intr(r):
|
||||
DELTA = 0.0001
|
||||
|
||||
for i, x in enumerate(r):
|
||||
if type(x) is float:
|
||||
xi = int(x)
|
||||
assert abs(xi - x) < DELTA
|
||||
r[i] = xi
|
||||
|
||||
|
||||
def fracr(r):
|
||||
intr(r)
|
||||
return [Fraction(x) for x in r]
|
||||
|
||||
|
||||
def fracm(m):
|
||||
return [fracr(r) for r in m]
|
||||
|
||||
|
||||
def symratr(r):
|
||||
intr(r)
|
||||
return [Rational(x) for x in r]
|
||||
|
||||
|
||||
def symratm(m):
|
||||
return [symratr(r) for r in m]
|
||||
|
||||
|
||||
def intm(m):
|
||||
[intr(r) for r in m]
|
||||
return m
|
||||
|
||||
|
||||
def create_matrix(rows, cols):
|
||||
ret = np.zeros((rows, cols))
|
||||
for rowi in range(rows):
|
||||
for coli in range(cols):
|
||||
ret[rowi][coli] = random.randint(1, 10)
|
||||
return ret
|
||||
|
||||
|
||||
def create_matrix_sparse(rows, cols):
|
||||
ret = np.zeros((rows, cols))
|
||||
for rowi in range(rows):
|
||||
for coli in range(cols):
|
||||
if random.randint(0, 5) < 1:
|
||||
ret[rowi][coli] = random.randint(1, 10)
|
||||
return ret
|
||||
|
||||
|
||||
def run(
|
||||
rows=35,
|
||||
cols=200,
|
||||
verbose=False,
|
||||
encoding='np',
|
||||
sparse=False,
|
||||
normalize_last=True):
|
||||
random.seed(0)
|
||||
if sparse:
|
||||
mnp = create_matrix_sparse(rows, cols)
|
||||
else:
|
||||
mnp = create_matrix(rows, cols)
|
||||
#print(mnp[0])
|
||||
|
||||
if encoding == 'fraction':
|
||||
msym = sympy.Matrix(fracm(mnp))
|
||||
elif encoding == 'np':
|
||||
msym = sympy.Matrix(mnp)
|
||||
elif encoding == 'sympy':
|
||||
msym = sympy.Matrix(symratm(mnp))
|
||||
# this actually produces float results
|
||||
elif encoding == 'int':
|
||||
msym = sympy.Matrix(intm(mnp))
|
||||
else:
|
||||
assert 0, 'bad encoding: %s' % encoding
|
||||
print(type(msym[0]), str(msym[0]))
|
||||
|
||||
if verbose:
|
||||
print('names')
|
||||
print(names)
|
||||
print('Matrix')
|
||||
sympy.pprint(msym)
|
||||
|
||||
print(
|
||||
'%s matrix, %u rows x %u cols, sparse: %s, normlast: %s' %
|
||||
(encoding, len(mnp), len(mnp[0]), sparse, normalize_last))
|
||||
bench = Benchmark()
|
||||
try:
|
||||
rref, pivots = msym.rref(normalize_last=normalize_last)
|
||||
finally:
|
||||
print('rref exiting after %s' % bench)
|
||||
print(type(rref[0]), str(rref[0]))
|
||||
|
||||
if verbose:
|
||||
print('Pivots')
|
||||
sympy.pprint(pivots)
|
||||
print('rref')
|
||||
sympy.pprint(rref)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Matrix solving performance tests')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--sparse', action='store_true', help='')
|
||||
parser.add_argument('--rows', type=int, help='')
|
||||
parser.add_argument('--cols', type=int, help='')
|
||||
parser.add_argument('--normalize-last', type=int, help='')
|
||||
parser.add_argument('--encoding', default='np', help='')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(
|
||||
encoding=args.encoding,
|
||||
rows=args.rows,
|
||||
cols=args.cols,
|
||||
sparse=args.sparse,
|
||||
normalize_last=bool(args.normalize_last),
|
||||
verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def gen_wires(fin):
|
||||
for l in fin:
|
||||
lj = {}
|
||||
l = l.strip()
|
||||
for kvs in l.split():
|
||||
name, value = kvs.split(':')
|
||||
lj[name] = value
|
||||
|
||||
tile_type, xy, wname = re.match(
|
||||
r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
|
||||
lj['tile_type'] = tile_type
|
||||
lj['xy'] = xy
|
||||
lj['wname'] = wname
|
||||
|
||||
lj['l'] = l
|
||||
|
||||
yield lj
|
||||
|
||||
|
||||
def run(node_fin, verbose=0):
|
||||
refnodes = {}
|
||||
nodei = 0
|
||||
for nodei, anode in enumerate(gen_wires(node_fin)):
|
||||
|
||||
def getk(anode):
|
||||
return anode['wname']
|
||||
return (anode['tile_type'], anode['wname'])
|
||||
|
||||
if nodei % 100000 == 0:
|
||||
print('Check node %d, %u node types' % (nodei, len(refnodes)))
|
||||
# Existing node?
|
||||
try:
|
||||
refnode = refnodes[getk(anode)]
|
||||
except KeyError:
|
||||
# Set as reference
|
||||
refnodes[getk(anode)] = anode
|
||||
continue
|
||||
k_invariant = (
|
||||
'CAN_INVERT',
|
||||
'IS_BUFFERED_2_0',
|
||||
'IS_BUFFERED_2_1',
|
||||
'IS_DIRECTIONAL',
|
||||
'IS_EXCLUDED_PIP',
|
||||
'IS_FIXED_INVERSION',
|
||||
'IS_INVERTED',
|
||||
'IS_PSEUDO',
|
||||
'IS_SITE_PIP',
|
||||
'IS_TEST_PIP',
|
||||
)
|
||||
k_varies = ('TILE', )
|
||||
# Verify equivilence
|
||||
for k in k_invariant:
|
||||
if k in refnode and k in anode:
|
||||
|
||||
def fail():
|
||||
print('Mismatch on %s' % k)
|
||||
print(refnode[k], anode[k])
|
||||
print(refnode['l'])
|
||||
print(anode['l'])
|
||||
#assert 0
|
||||
|
||||
if refnode[k] != anode[k]:
|
||||
print('')
|
||||
fail()
|
||||
# A key in one but not the other?
|
||||
elif k in refnode or k in anode:
|
||||
assert 0
|
||||
elif k not in k_varies:
|
||||
assert 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Determines which info is consistent across PIPs with the same name')
|
||||
|
||||
parser.add_argument('--verbose', type=int, help='')
|
||||
parser.add_argument(
|
||||
'node_fn_in',
|
||||
default='specimen_001/pip_all.txt',
|
||||
nargs='?',
|
||||
help='Input file')
|
||||
args = parser.parse_args()
|
||||
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
//move some stuff to minitests/ncy0
|
||||
|
||||
`define SEED 32'h12345678
|
||||
|
||||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 42;
|
||||
localparam integer DOUT_N = 79;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
|
||||
roi #(.DIN_N(DIN_N), .DOUT_N(DOUT_N))
|
||||
roi (
|
||||
.clk(clk),
|
||||
.din(din),
|
||||
.dout(dout)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module roi(input clk, input [DIN_N-1:0] din, output [DOUT_N-1:0] dout);
|
||||
parameter integer DIN_N = -1;
|
||||
parameter integer DOUT_N = -1;
|
||||
|
||||
/*
|
||||
//Take out for now to make sure LUTs are more predictable
|
||||
picorv32 picorv32 (
|
||||
.clk(clk),
|
||||
.resetn(din[0]),
|
||||
.mem_valid(dout[0]),
|
||||
.mem_instr(dout[1]),
|
||||
.mem_ready(din[1]),
|
||||
.mem_addr(dout[33:2]),
|
||||
.mem_wdata(dout[66:34]),
|
||||
.mem_wstrb(dout[70:67]),
|
||||
.mem_rdata(din[33:2])
|
||||
);
|
||||
*/
|
||||
|
||||
/*
|
||||
randluts randluts (
|
||||
.din(din[41:34]),
|
||||
.dout(dout[78:71])
|
||||
);
|
||||
*/
|
||||
randluts #(.N(150)) randluts (
|
||||
.din(din[41:34]),
|
||||
.dout(dout[78:71])
|
||||
);
|
||||
endmodule
|
||||
|
||||
module randluts(input [7:0] din, output [7:0] dout);
|
||||
parameter integer N = 250;
|
||||
|
||||
function [31:0] xorshift32(input [31:0] xorin);
|
||||
begin
|
||||
xorshift32 = xorin;
|
||||
xorshift32 = xorshift32 ^ (xorshift32 << 13);
|
||||
xorshift32 = xorshift32 ^ (xorshift32 >> 17);
|
||||
xorshift32 = xorshift32 ^ (xorshift32 << 5);
|
||||
end
|
||||
endfunction
|
||||
|
||||
function [63:0] lutinit(input [7:0] a, b);
|
||||
begin
|
||||
lutinit[63:32] = xorshift32(xorshift32(xorshift32(xorshift32({a, b} ^ `SEED))));
|
||||
lutinit[31: 0] = xorshift32(xorshift32(xorshift32(xorshift32({b, a} ^ `SEED))));
|
||||
end
|
||||
endfunction
|
||||
|
||||
wire [(N+1)*8-1:0] nets;
|
||||
|
||||
assign nets[7:0] = din;
|
||||
assign dout = nets[(N+1)*8-1:N*8];
|
||||
|
||||
genvar i, j;
|
||||
generate
|
||||
for (i = 0; i < N; i = i+1) begin:is
|
||||
for (j = 0; j < 8; j = j+1) begin:js
|
||||
localparam integer k = xorshift32(xorshift32(xorshift32(xorshift32((i << 20) ^ (j << 10) ^ `SEED)))) & 255;
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
LUT6 #(
|
||||
.INIT(lutinit(i, j))
|
||||
) lut (
|
||||
.I0(nets[8*i+(k+0)%8]),
|
||||
.I1(nets[8*i+(k+1)%8]),
|
||||
.I2(nets[8*i+(k+2)%8]),
|
||||
.I3(nets[8*i+(k+3)%8]),
|
||||
.I4(nets[8*i+(k+4)%8]),
|
||||
.I5(nets[8*i+(k+5)%8]),
|
||||
.O(nets[8*i+8+j])
|
||||
);
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def gen_wires(fin):
|
||||
for l in fin:
|
||||
lj = {}
|
||||
l = l.strip()
|
||||
for kvs in l.split():
|
||||
name, value = kvs.split(':')
|
||||
lj[name] = value
|
||||
|
||||
tile_type, xy, wname = re.match(
|
||||
r'(.*)_(X[0-9]*Y[0-9]*)/(.*)', lj['NAME']).groups()
|
||||
lj['tile_type'] = tile_type
|
||||
lj['xy'] = xy
|
||||
lj['wname'] = wname
|
||||
|
||||
lj['l'] = l
|
||||
|
||||
yield lj
|
||||
|
||||
|
||||
def run(node_fin, verbose=0):
|
||||
refnodes = {}
|
||||
nodei = 0
|
||||
for nodei, anode in enumerate(gen_wires(node_fin)):
|
||||
|
||||
def getk(anode):
|
||||
return anode['wname']
|
||||
#return (anode['tile_type'], anode['wname'])
|
||||
|
||||
if nodei % 100000 == 0:
|
||||
print('Check node %d, %u node types' % (nodei, len(refnodes)))
|
||||
# Existing node?
|
||||
try:
|
||||
refnode = refnodes[getk(anode)]
|
||||
except KeyError:
|
||||
# Set as reference
|
||||
refnodes[getk(anode)] = anode
|
||||
continue
|
||||
k_invariant = (
|
||||
'COST_CODE',
|
||||
'IS_INPUT_PIN',
|
||||
'IS_OUTPUT_PIN',
|
||||
'IS_PART_OF_BUS',
|
||||
'NUM_INTERSECTS',
|
||||
'NUM_TILE_PORTS',
|
||||
'SPEED_INDEX',
|
||||
'TILE_PATTERN_OFFSET',
|
||||
)
|
||||
k_varies = (
|
||||
'ID_IN_TILE_TYPE',
|
||||
'IS_CONNECTED',
|
||||
'NUM_DOWNHILL_PIPS',
|
||||
'NUM_PIPS',
|
||||
'NUM_UPHILL_PIPS',
|
||||
'TILE_NAME',
|
||||
)
|
||||
# Verify equivilence
|
||||
for k in k_invariant:
|
||||
if k in refnode and k in anode:
|
||||
|
||||
def fail():
|
||||
print('Mismatch on %s' % k)
|
||||
print(refnode[k], anode[k])
|
||||
print(refnode['l'])
|
||||
print(anode['l'])
|
||||
#assert 0
|
||||
|
||||
if refnode[k] != anode[k]:
|
||||
print
|
||||
fail()
|
||||
# A key in one but not the other?
|
||||
elif k in refnode or k in anode:
|
||||
assert 0
|
||||
elif k not in k_varies:
|
||||
assert 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Determines which info is consistent across wires with the same name')
|
||||
|
||||
parser.add_argument('--verbose', type=int, help='')
|
||||
parser.add_argument(
|
||||
'node_fn_in',
|
||||
default='specimen_001/wire_all.txt',
|
||||
nargs='?',
|
||||
help='Input file')
|
||||
args = parser.parse_args()
|
||||
run(open(args.node_fn_in, 'r'), verbose=args.verbose)
|
||||
|
|
@ -1 +0,0 @@
|
|||
Collect info on ZERO speed classes
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
|
||||
def run_types(tilej, verbose=False):
|
||||
def process(etype):
|
||||
# dict[model] = set((tile, wire/pip))
|
||||
zeros = {}
|
||||
print('Processing %s' % etype)
|
||||
# Index delay models by type, recording where they occured
|
||||
for tilek, tilev in tilej['tiles'].items():
|
||||
for ename, emodel in tilev[etype].items():
|
||||
if emodel.find('ZERO') >= 0:
|
||||
zeros.setdefault(emodel, set()).add((tilek, ename))
|
||||
# Print out delay model instances
|
||||
print('%s ZERO types: %u, %s' % (etype, len(zeros), zeros.keys()))
|
||||
print(
|
||||
'%s ZERO instances: %u' %
|
||||
(etype, sum([len(x) for x in zeros.values()])))
|
||||
for model in sorted(zeros.keys()):
|
||||
modelv = zeros[model]
|
||||
print('Model: %s' % model)
|
||||
for tile_name, element_name in sorted(list(modelv)):
|
||||
print(' %s: %s' % (tile_name, element_name))
|
||||
|
||||
process('wires')
|
||||
print('')
|
||||
process('pips')
|
||||
|
||||
|
||||
def run_prefix(tilej, verbose=False):
|
||||
def process(etype):
|
||||
prefixes = set()
|
||||
print('Processing %s' % etype)
|
||||
# Index delay models by type, recording where they occured
|
||||
for tilek, tilev in tilej['tiles'].items():
|
||||
for ename, emodel in tilev[etype].items():
|
||||
prefix = emodel.split('_')[0]
|
||||
prefixes.add(prefix)
|
||||
print('%s prefixes: %u' % (etype, len(prefixes)))
|
||||
for prefix in sorted(prefixes):
|
||||
print(' %s' % prefix)
|
||||
|
||||
process('wires')
|
||||
print('')
|
||||
process('pips')
|
||||
|
||||
|
||||
def run(fnin, verbose=False):
|
||||
tilej = json.load((open(fnin, 'r')))
|
||||
run_types(tilej)
|
||||
print('')
|
||||
print('')
|
||||
run_prefix(tilej)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Solve timing solution')
|
||||
parser.add_argument(
|
||||
'fnin',
|
||||
default="../timgrid/build/timgrid-s.json",
|
||||
nargs='?',
|
||||
help='input timgrid JSON')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(args.fnin, verbose=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
specimen_*
|
||||
build
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
all:
|
||||
echo "FIXME: tie projects together"
|
||||
false
|
||||
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# Run corner specific calculations
|
||||
|
||||
TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing
|
||||
CORNER=slow_max
|
||||
ALLOW_ZERO_EQN?=N
|
||||
BADPRJ_OK?=N
|
||||
BUILD_DIR?=build/MUST_SET
|
||||
CSV_BASENAME=timing4i.csv
|
||||
|
||||
all: $(BUILD_DIR)/$(CORNER)/timgrid-vc.json $(BUILD_DIR)/$(CORNER)/qor.txt
|
||||
|
||||
run:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
||||
touch run.ok
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)/$(CORNER)
|
||||
|
||||
.PHONY: all run clean
|
||||
|
||||
$(BUILD_DIR)/$(CORNER):
|
||||
mkdir -p $(BUILD_DIR)/$(CORNER)
|
||||
|
||||
# parent should have built this
|
||||
$(BUILD_DIR)/checksub:
|
||||
false
|
||||
|
||||
$(BUILD_DIR)/$(CORNER)/leastsq.csv: $(BUILD_DIR)/sub.json $(BUILD_DIR)/grouped.csv $(BUILD_DIR)/checksub $(BUILD_DIR)/$(CORNER)
|
||||
# Create a rough timing model that approximately fits the given paths
|
||||
python3 $(TIMFUZ_DIR)/solve_leastsq.py $(BUILD_DIR)/grouped.csv --corner $(CORNER) --out $(BUILD_DIR)/$(CORNER)/leastsq.csv.tmp
|
||||
mv $(BUILD_DIR)/$(CORNER)/leastsq.csv.tmp $(BUILD_DIR)/$(CORNER)/leastsq.csv
|
||||
|
||||
$(BUILD_DIR)/$(CORNER)/linprog.csv: $(BUILD_DIR)/$(CORNER)/leastsq.csv $(BUILD_DIR)/grouped.csv
|
||||
# Tweak rough timing model, making sure all constraints are satisfied
|
||||
ALLOW_ZERO_EQN=$(ALLOW_ZERO_EQN) python3 $(TIMFUZ_DIR)/solve_linprog.py --bounds-csv $(BUILD_DIR)/$(CORNER)/leastsq.csv --massage $(BUILD_DIR)/grouped.csv --corner $(CORNER) --out $(BUILD_DIR)/$(CORNER)/linprog.csv.tmp
|
||||
mv $(BUILD_DIR)/$(CORNER)/linprog.csv.tmp $(BUILD_DIR)/$(CORNER)/linprog.csv
|
||||
|
||||
$(BUILD_DIR)/$(CORNER)/flat.csv: $(BUILD_DIR)/$(CORNER)/linprog.csv
|
||||
# Take separated variables and back-annotate them to the original timing variables
|
||||
python3 $(TIMFUZ_DIR)/csv_group2flat.py --sub-json $(BUILD_DIR)/sub.json --corner $(CORNER) --out $(BUILD_DIR)/$(CORNER)/flat.csv.tmp $(BUILD_DIR)/$(CORNER)/linprog.csv
|
||||
mv $(BUILD_DIR)/$(CORNER)/flat.csv.tmp $(BUILD_DIR)/$(CORNER)/flat.csv
|
||||
|
||||
$(BUILD_DIR)/$(CORNER)/timgrid-vc.json: $(BUILD_DIR)/$(CORNER)/flat.csv
|
||||
# Final processing
|
||||
# Insert timing delays into actual tile layouts
|
||||
python3 $(TIMFUZ_DIR)/tile_annotate.py --timgrid-s $(TIMFUZ_DIR)/timgrid/build/timgrid-s.json --out $(BUILD_DIR)/$(CORNER)/timgrid-vc.json $(BUILD_DIR)/$(CORNER)/flat.csv
|
||||
|
||||
$(BUILD_DIR)/$(CORNER)/qor.txt: $(BUILD_DIR)/$(CORNER)/flat.csv
|
||||
ifeq ($(SOLVING),i)
|
||||
python3 $(TIMFUZ_DIR)/solve_qor.py --corner $(CORNER) --bounds-csv $(BUILD_DIR)/$(CORNER)/flat.csv specimen_*/timing4i.csv >$(BUILD_DIR)/$(CORNER)/qor.txt.tmp
|
||||
mv $(BUILD_DIR)/$(CORNER)/qor.txt.tmp $(BUILD_DIR)/$(CORNER)/qor.txt
|
||||
else
|
||||
# FIXME
|
||||
touch $(BUILD_DIR)/$(CORNER)/qor.txt
|
||||
endif
|
||||
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
source ${XRAY_GENHEADER}
|
||||
TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing
|
||||
|
||||
timing_txt2csv () {
|
||||
python3 $TIMFUZ_DIR/timing_txt2icsv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out 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
|
||||
}
|
||||
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# 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)
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
# so simple that likely no linprog adjustments will be needed
|
||||
ALLOW_ZERO_EQN?=Y
|
||||
include ../project.mk
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
Minimal, simple test
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
source ../generate.sh
|
||||
|
||||
${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
timing_txt2csv
|
||||
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
source ../../../../../utils/utils.tcl
|
||||
source ../../project.tcl
|
||||
|
||||
proc build_design {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog ../top.v
|
||||
synth_design -top top
|
||||
|
||||
puts "Locking pins"
|
||||
set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||
[get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||
|
||||
puts "Package stuff"
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
puts "pblocking"
|
||||
create_pblock roi
|
||||
set roipb [get_pblocks roi]
|
||||
add_cells_to_pblock $roipb [get_cells roi]
|
||||
resize_pblock $roipb -add "$::env(XRAY_ROI)"
|
||||
|
||||
puts "randplace"
|
||||
#randplace_pblock 50 roi
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
puts "dedicated route"
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
# disable combinitorial loop
|
||||
# set_property IS_ENABLED 0 [get_drc_checks {LUTLP-1}]
|
||||
#write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
build_design
|
||||
write_info4
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
module roi (
|
||||
input wire clk,
|
||||
output wire out);
|
||||
reg [23:0] counter;
|
||||
assign out = counter[23] ^ counter[22] ^ counter[2] && counter[1] || counter[0];
|
||||
|
||||
always @(posedge clk) begin
|
||||
counter <= counter + 1;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module top(input wire clk, input wire stb, input wire di, output wire do);
|
||||
localparam integer DIN_N = 0;
|
||||
localparam integer DOUT_N = 1;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
roi roi(
|
||||
.clk(clk),
|
||||
.out(dout[0])
|
||||
);
|
||||
endmodule
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
include ../project.mk
|
||||
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
picorv32 uses more advanced structures than simple LUT/FF tests (such as carry chain)
|
||||
It attempts to provide some realistic timing data wrt fanout and used fabric
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
source ../generate.sh
|
||||
|
||||
${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
timing_txt2csv
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
source ../../../../../utils/utils.tcl
|
||||
source ../../project.tcl
|
||||
|
||||
proc build_design {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog ../../../src/picorv32.v
|
||||
read_verilog ../top.v
|
||||
synth_design -top top
|
||||
|
||||
puts "Locking pins"
|
||||
set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||
[get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||
|
||||
puts "Package stuff"
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
puts "pblocking"
|
||||
create_pblock roi
|
||||
set roipb [get_pblocks roi]
|
||||
add_cells_to_pblock $roipb [get_cells roi]
|
||||
resize_pblock $roipb -add "$::env(XRAY_ROI)"
|
||||
|
||||
puts "randplace"
|
||||
randplace_pblock 50 roi
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
puts "dedicated route"
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
# disable combinitorial loop
|
||||
# set_property IS_ENABLED 0 [get_drc_checks {LUTLP-1}]
|
||||
#write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
build_design
|
||||
write_info4
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
//move some stuff to minitests/ncy0
|
||||
|
||||
`define SEED 32'h12345678
|
||||
|
||||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 42;
|
||||
localparam integer DOUT_N = 79;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
|
||||
roi #(.DIN_N(DIN_N), .DOUT_N(DOUT_N))
|
||||
roi (
|
||||
.clk(clk),
|
||||
.din(din),
|
||||
.dout(dout)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module roi(input clk, input [DIN_N-1:0] din, output [DOUT_N-1:0] dout);
|
||||
parameter integer DIN_N = -1;
|
||||
parameter integer DOUT_N = -1;
|
||||
|
||||
/*
|
||||
//Take out for now to make sure LUTs are more predictable
|
||||
picorv32 picorv32 (
|
||||
.clk(clk),
|
||||
.resetn(din[0]),
|
||||
.mem_valid(dout[0]),
|
||||
.mem_instr(dout[1]),
|
||||
.mem_ready(din[1]),
|
||||
.mem_addr(dout[33:2]),
|
||||
.mem_wdata(dout[66:34]),
|
||||
.mem_wstrb(dout[70:67]),
|
||||
.mem_rdata(din[33:2])
|
||||
);
|
||||
*/
|
||||
|
||||
/*
|
||||
randluts randluts (
|
||||
.din(din[41:34]),
|
||||
.dout(dout[78:71])
|
||||
);
|
||||
*/
|
||||
randluts #(.N(150)) randluts (
|
||||
.din(din[41:34]),
|
||||
.dout(dout[78:71])
|
||||
);
|
||||
endmodule
|
||||
|
||||
module randluts(input [7:0] din, output [7:0] dout);
|
||||
parameter integer N = 250;
|
||||
|
||||
function [31:0] xorshift32(input [31:0] xorin);
|
||||
begin
|
||||
xorshift32 = xorin;
|
||||
xorshift32 = xorshift32 ^ (xorshift32 << 13);
|
||||
xorshift32 = xorshift32 ^ (xorshift32 >> 17);
|
||||
xorshift32 = xorshift32 ^ (xorshift32 << 5);
|
||||
end
|
||||
endfunction
|
||||
|
||||
function [63:0] lutinit(input [7:0] a, b);
|
||||
begin
|
||||
lutinit[63:32] = xorshift32(xorshift32(xorshift32(xorshift32({a, b} ^ `SEED))));
|
||||
lutinit[31: 0] = xorshift32(xorshift32(xorshift32(xorshift32({b, a} ^ `SEED))));
|
||||
end
|
||||
endfunction
|
||||
|
||||
wire [(N+1)*8-1:0] nets;
|
||||
|
||||
assign nets[7:0] = din;
|
||||
assign dout = nets[(N+1)*8-1:N*8];
|
||||
|
||||
genvar i, j;
|
||||
generate
|
||||
for (i = 0; i < N; i = i+1) begin:is
|
||||
for (j = 0; j < 8; j = j+1) begin:js
|
||||
localparam integer k = xorshift32(xorshift32(xorshift32(xorshift32((i << 20) ^ (j << 10) ^ `SEED)))) & 255;
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
LUT6 #(
|
||||
.INIT(lutinit(i, j))
|
||||
) lut (
|
||||
.I0(nets[8*i+(k+0)%8]),
|
||||
.I1(nets[8*i+(k+1)%8]),
|
||||
.I2(nets[8*i+(k+2)%8]),
|
||||
.I3(nets[8*i+(k+3)%8]),
|
||||
.I4(nets[8*i+(k+4)%8]),
|
||||
.I5(nets[8*i+(k+5)%8]),
|
||||
.O(nets[8*i+8+j])
|
||||
);
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
include ../project.mk
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
LUTs are physically laid out in an array and directly connected to a test pattern generator
|
||||
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='')
|
||||
parser.add_argument('--sdx', default='8', help='')
|
||||
parser.add_argument('--sdy', default='4', help='')
|
||||
args = parser.parse_args()
|
||||
'''
|
||||
Generate in pairs
|
||||
Fill up switchbox quad for now
|
||||
Create random connections between the LUTs
|
||||
See how much routing pressure we can generate
|
||||
|
||||
Start with non-random connections to the LFSR for solver comparison
|
||||
|
||||
Start at SLICE_X16Y102
|
||||
'''
|
||||
SBASE = (16, 102)
|
||||
SDX = int(args.sdx, 0)
|
||||
SDY = int(args.sdy, 0)
|
||||
nlut = 4 * SDX * SDY
|
||||
|
||||
nin = 6 * nlut
|
||||
nout = nlut
|
||||
|
||||
print('//placelut simple')
|
||||
print('//SBASE: %s' % (SBASE, ))
|
||||
print('//SDX: %s' % (SDX, ))
|
||||
print('//SDY: %s' % (SDX, ))
|
||||
print('//nlut: %s' % (nlut, ))
|
||||
print(
|
||||
'''\
|
||||
module roi (
|
||||
input wire clk,
|
||||
input wire [%u:0] ins,
|
||||
output wire [%u:0] outs);''') % (nin - 1, nout - 1)
|
||||
|
||||
ini = 0
|
||||
outi = 0
|
||||
for lutx in xrange(SBASE[0], SBASE[0] + SDX):
|
||||
for luty in xrange(SBASE[1], SBASE[1] + SDY):
|
||||
loc = "SLICE_X%uY%u" % (lutx, luty)
|
||||
for belc in 'ABCD':
|
||||
bel = '%c6LUT' % belc
|
||||
print(
|
||||
'''\
|
||||
|
||||
(* KEEP, DONT_TOUCH, LOC="%s", BEL="%s" *)
|
||||
LUT6 #(
|
||||
.INIT(64'hBAD1DEA_1DEADCE0)
|
||||
) %s (''') % (loc, bel, 'lut_x%uy%u_%c' % (lutx, luty, belc))
|
||||
for i in xrange(6):
|
||||
print('''\
|
||||
.I%u(ins[%u]),''' % (i, ini))
|
||||
ini += 1
|
||||
print('''\
|
||||
.O(outs[%u]));''') % (outi, )
|
||||
outi += 1
|
||||
assert nin == ini
|
||||
assert nout == outi
|
||||
|
||||
print(
|
||||
'''
|
||||
endmodule
|
||||
|
||||
module top(input wire clk, input wire stb, input wire di, output wire do);
|
||||
localparam integer DIN_N = %u;
|
||||
localparam integer DOUT_N = %u;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
roi roi(
|
||||
.clk(clk),
|
||||
.ins(din),
|
||||
.outs(dout)
|
||||
);
|
||||
endmodule''') % (nin, nout)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
source ${XRAY_GENHEADER}
|
||||
TIMFUZ_DIR=$XRAY_DIR/fuzzers/007-timing
|
||||
|
||||
python3 ../generate.py --sdx 4 --sdy 4 >top.v
|
||||
${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
python3 $TIMFUZ_DIR/timing_txt2csv.py --speed-json $TIMFUZ_DIR/speed/build/speed.json --out timing4.csv timing4.txt
|
||||
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
source ../../../../../utils/utils.tcl
|
||||
source ../../project.tcl
|
||||
|
||||
proc build_design {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog top.v
|
||||
synth_design -top top
|
||||
|
||||
puts "Locking pins"
|
||||
set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||
[get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||
|
||||
puts "Package stuff"
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
puts "pblocking"
|
||||
create_pblock roi
|
||||
set roipb [get_pblocks roi]
|
||||
add_cells_to_pblock $roipb [get_cells roi]
|
||||
resize_pblock $roipb -add "$::env(XRAY_ROI)"
|
||||
|
||||
puts "randplace"
|
||||
randplace_pblock 50 roi
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
puts "dedicated route"
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
# disable combinitorial loop
|
||||
# set_property IS_ENABLED 0 [get_drc_checks {LUTLP-1}]
|
||||
#write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
build_design
|
||||
write_info4
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
include ../project.mk
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
LUTs are physically laid out in an array and connected to test pattern generator and fed back to other LUTs
|
||||
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
'''
|
||||
Note: vivado will (by default) fail bitgen DRC on LUT feedback loops
|
||||
Looks like can probably be disabled, but we actually don't need a bitstream for timing analysis
|
||||
'''
|
||||
|
||||
import argparse
|
||||
import random
|
||||
|
||||
random.seed()
|
||||
|
||||
parser = argparse.ArgumentParser(description='')
|
||||
parser.add_argument('--sdx', default='8', help='')
|
||||
parser.add_argument('--sdy', default='4', help='')
|
||||
args = parser.parse_args()
|
||||
'''
|
||||
Generate in pairs
|
||||
Fill up switchbox quad for now
|
||||
Create random connections between the LUTs
|
||||
See how much routing pressure we can generate
|
||||
|
||||
Start with non-random connections to the LFSR for solver comparison
|
||||
|
||||
Start at SLICE_X16Y102
|
||||
'''
|
||||
SBASE = (16, 102)
|
||||
SDX = int(args.sdx, 0)
|
||||
SDY = int(args.sdy, 0)
|
||||
nlut = 4 * SDX * SDY
|
||||
|
||||
nin = 6 * nlut
|
||||
nout = nlut
|
||||
|
||||
print('//placelut w/ feedback')
|
||||
print('//SBASE: %s' % (SBASE, ))
|
||||
print('//SDX: %s' % (SDX, ))
|
||||
print('//SDY: %s' % (SDX, ))
|
||||
print('//nlut: %s' % (nlut, ))
|
||||
print(
|
||||
'''\
|
||||
module roi (
|
||||
input wire clk,
|
||||
input wire [%u:0] ins,
|
||||
output wire [%u:0] outs);''') % (nin - 1, nout - 1)
|
||||
|
||||
ini = 0
|
||||
outi = 0
|
||||
for lutx in xrange(SBASE[0], SBASE[0] + SDX):
|
||||
for luty in xrange(SBASE[1], SBASE[1] + SDY):
|
||||
loc = "SLICE_X%uY%u" % (lutx, luty)
|
||||
for belc in 'ABCD':
|
||||
bel = '%c6LUT' % belc
|
||||
print(
|
||||
'''\
|
||||
|
||||
(* KEEP, DONT_TOUCH, LOC="%s", BEL="%s" *)
|
||||
LUT6 #(
|
||||
.INIT(64'hBAD1DEA_1DEADCE0)
|
||||
) %s (''') % (loc, bel, 'lut_x%uy%u_%c' % (lutx, luty, belc))
|
||||
for i in xrange(6):
|
||||
if random.randint(0, 9) < 1:
|
||||
wfrom = 'ins[%u]' % ini
|
||||
ini += 1
|
||||
else:
|
||||
wfrom = 'outs[%u]' % random.randint(0, nout - 1)
|
||||
print('''\
|
||||
.I%u(%s),''' % (i, wfrom))
|
||||
print('''\
|
||||
.O(outs[%u]));''') % (outi, )
|
||||
outi += 1
|
||||
#assert nin == ini
|
||||
assert nout == outi
|
||||
|
||||
print(
|
||||
'''
|
||||
endmodule
|
||||
|
||||
module top(input wire clk, input wire stb, input wire di, output wire do);
|
||||
localparam integer DIN_N = %u;
|
||||
localparam integer DOUT_N = %u;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
roi roi(
|
||||
.clk(clk),
|
||||
.ins(din),
|
||||
.outs(dout)
|
||||
);
|
||||
endmodule''') % (nin, nout)
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
source ../generate.sh
|
||||
|
||||
python3 ../generate.py --sdx 4 --sdy 4 >top.v
|
||||
${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
timing_txt2csv
|
||||
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
source ../../../../../utils/utils.tcl
|
||||
source ../../project.tcl
|
||||
|
||||
proc build_design {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog top.v
|
||||
synth_design -top top
|
||||
|
||||
puts "Locking pins"
|
||||
set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||
[get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||
|
||||
puts "Package stuff"
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
puts "pblocking"
|
||||
create_pblock roi
|
||||
set roipb [get_pblocks roi]
|
||||
add_cells_to_pblock $roipb [get_cells roi]
|
||||
resize_pblock $roipb -add "$::env(XRAY_ROI)"
|
||||
|
||||
puts "randplace"
|
||||
randplace_pblock 50 roi
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
puts "dedicated route"
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
# disable combinitorial loop
|
||||
# set_property IS_ENABLED 0 [get_drc_checks {LUTLP-1}]
|
||||
#write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
build_design
|
||||
write_info4
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
include ../project.mk
|
||||
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
LUTs are physically laid out in an array and connected to test pattern generator and fed back to other LUTs.
|
||||
FFs are randomly inserted between connections.
|
||||
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
'''
|
||||
Note: vivado will (by default) fail bitgen DRC on LUT feedback loops
|
||||
Looks like can probably be disabled, but we actually don't need a bitstream for timing analysis
|
||||
|
||||
ERROR: [Vivado 12-2285] Cannot set LOC property of instance 'roi/lut_x22y102_D', Instance roi/lut_x22y102_D can not be placed in D6LUT of site SLICE_X18Y103 because the bel is occupied by roi/lut_x18y103_D(port:). This could be caused by bel constraint conflict
|
||||
Resolution: When using BEL constraints, ensure the BEL constraints are defined before the LOC constraints to avoid conflicts at a given site.
|
||||
|
||||
'''
|
||||
|
||||
import argparse
|
||||
import random
|
||||
|
||||
random.seed()
|
||||
|
||||
parser = argparse.ArgumentParser(description='')
|
||||
parser.add_argument('--sdx', default='8', help='')
|
||||
parser.add_argument('--sdy', default='4', help='')
|
||||
args = parser.parse_args()
|
||||
'''
|
||||
Generate in pairs
|
||||
Fill up switchbox quad for now
|
||||
Create random connections between the LUTs
|
||||
See how much routing pressure we can generate
|
||||
|
||||
Start with non-random connections to the LFSR for solver comparison
|
||||
|
||||
Start at SLICE_X16Y102
|
||||
'''
|
||||
SBASE = (16, 102)
|
||||
SDX = int(args.sdx, 0)
|
||||
SDY = int(args.sdy, 0)
|
||||
nlut = 4 * SDX * SDY
|
||||
|
||||
nin = 6 * nlut
|
||||
nout = nlut
|
||||
|
||||
print('//placelut w/ FF + feedback')
|
||||
print('//SBASE: %s' % (SBASE, ))
|
||||
print('//SDX: %s' % (SDX, ))
|
||||
print('//SDY: %s' % (SDX, ))
|
||||
print('//nlut: %s' % (nlut, ))
|
||||
print(
|
||||
'''\
|
||||
module roi (
|
||||
input wire clk,
|
||||
input wire [%u:0] ins,
|
||||
output wire [%u:0] outs);''') % (nin - 1, nout - 1)
|
||||
|
||||
ini = 0
|
||||
outi = 0
|
||||
for lutx in xrange(SBASE[0], SBASE[0] + SDX):
|
||||
for luty in xrange(SBASE[1], SBASE[1] + SDY):
|
||||
loc = "SLICE_X%uY%u" % (lutx, luty)
|
||||
for belc in 'ABCD':
|
||||
bel = '%c6LUT' % belc
|
||||
name = 'lut_x%uy%u_%c' % (lutx, luty, belc)
|
||||
print(
|
||||
'''\
|
||||
|
||||
(* KEEP, DONT_TOUCH, LOC="%s", BEL="%s" *)
|
||||
LUT6 #(
|
||||
.INIT(64'hBAD1DEA_1DEADCE0)
|
||||
) %s (''') % (loc, bel, name)
|
||||
for i in xrange(6):
|
||||
rval = random.randint(0, 9)
|
||||
if rval < 3:
|
||||
wfrom = 'ins[%u]' % ini
|
||||
ini += 1
|
||||
#elif rval < 6:
|
||||
# wfrom = 'outsr[%u]' % random.randint(0, nout - 1)
|
||||
else:
|
||||
wfrom = 'outs[%u]' % random.randint(0, nout - 1)
|
||||
print('''\
|
||||
.I%u(%s),''' % (i, wfrom))
|
||||
out_w = name + '_o'
|
||||
print('''\
|
||||
.O(%s));''') % (out_w, )
|
||||
|
||||
outs_w = "outs[%u]" % outi
|
||||
if random.randint(0, 9) < 5:
|
||||
print(' assign %s = %s;' % (outs_w, out_w))
|
||||
else:
|
||||
out_r = name + '_or'
|
||||
print(
|
||||
'''\
|
||||
reg %s;
|
||||
assign %s = %s;
|
||||
always @(posedge clk) begin
|
||||
%s = %s;
|
||||
end
|
||||
''' % (out_r, outs_w, out_r, out_r, out_w))
|
||||
outi += 1
|
||||
|
||||
#assert nin == ini
|
||||
assert nout == outi
|
||||
|
||||
print(
|
||||
'''
|
||||
endmodule
|
||||
|
||||
module top(input wire clk, input wire stb, input wire di, output wire do);
|
||||
localparam integer DIN_N = %u;
|
||||
localparam integer DOUT_N = %u;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
roi roi(
|
||||
.clk(clk),
|
||||
.ins(din),
|
||||
.outs(dout)
|
||||
);
|
||||
endmodule''') % (nin, nout)
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
source ../generate.sh
|
||||
|
||||
python3 ../generate.py --sdx 4 --sdy 4 >top.v
|
||||
${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
timing_txt2csv
|
||||
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
source ../../../../../utils/utils.tcl
|
||||
source ../../project.tcl
|
||||
|
||||
proc build_design {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog top.v
|
||||
synth_design -top top
|
||||
|
||||
puts "Locking pins"
|
||||
set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||
[get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||
|
||||
puts "Package stuff"
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
puts "pblocking"
|
||||
create_pblock roi
|
||||
set roipb [get_pblocks roi]
|
||||
add_cells_to_pblock $roipb [get_cells roi]
|
||||
resize_pblock $roipb -add "$::env(XRAY_ROI)"
|
||||
|
||||
puts "randplace"
|
||||
randplace_pblock 50 roi
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
puts "dedicated route"
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
# disable combinitorial loop
|
||||
# set_property IS_ENABLED 0 [get_drc_checks {LUTLP-1}]
|
||||
#write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
build_design
|
||||
write_info4
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
# project.mk: build specimens (run vivado), compute rref
|
||||
# corner.mk: run corner specific calculations
|
||||
|
||||
N := 1
|
||||
SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
|
||||
SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
|
||||
TIMFUZ_DIR=$(XRAY_DIR)/fuzzers/007-timing
|
||||
# Allow an empty system of equations?
|
||||
# for testing only on small projects
|
||||
ALLOW_ZERO_EQN?=N
|
||||
# Constrained projects may fail to build
|
||||
# Set to Y to make a best effort to suck in the ones that did build
|
||||
BADPRJ_OK?=N
|
||||
BUILD_DIR?=build
|
||||
# interconnect
|
||||
BUILD_DIR_I?=$(BUILD_DIR)/i
|
||||
# site
|
||||
BUILD_DIR_S?=$(BUILD_DIR)/s
|
||||
|
||||
CORNERS=fast_max fast_min slow_max slow_min
|
||||
|
||||
TIMGRID_V_I=$(BUILD_DIR_I)/timgrid-v.json
|
||||
TIMGRID_V_S=$(BUILD_DIR_S)/timgrid-v.json
|
||||
|
||||
all: $(BUILD_DIR)/timgrid-v.json
|
||||
|
||||
$(SPECIMENS_OK):
|
||||
bash generate.sh $(subst /OK,,$@) || (if [ "$(BADPRJ_OK)" != 'Y' ] ; then exit 1; fi; exit 0)
|
||||
touch $@
|
||||
|
||||
run:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
||||
touch run.ok
|
||||
|
||||
clean:
|
||||
rm -rf specimen_[0-9][0-9][0-9]/ seg_clblx.segbits __pycache__ run.ok
|
||||
rm -rf vivado*.log vivado_*.str vivado*.jou design *.bits *.dcp *.bit
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
.PHONY: all run clean
|
||||
|
||||
$(TIMGRID_V_I): $(SPECIMENS_OK)
|
||||
$(MAKE) -f $(TIMFUZ_DIR)/projects/is.mk BUILD_DIR=$(BUILD_DIR_I) SOLVING=i SPECIMENS=$(SPECIMENS) all
|
||||
i: $(TIMGRID_V_I)
|
||||
|
||||
$(TIMGRID_V_S): $(SPECIMENS_OK)
|
||||
$(MAKE) -f $(TIMFUZ_DIR)/projects/is.mk BUILD_DIR=$(BUILD_DIR_S) SOLVING=s SPECIMENS=$(SPECIMENS) all
|
||||
s: $(TIMGRID_V_S)
|
||||
|
||||
.PHONY: i s
|
||||
|
||||
$(BUILD_DIR)/timgrid-v.json: $(TIMGRID_V_I) $(TIMGRID_V_S)
|
||||
python3 $(TIMFUZ_DIR)/timgrid_vc2v.py --out $(BUILD_DIR)/timgrid-v.json $(TIMGRID_V_I) $(TIMGRID_V_S)
|
||||
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
# General notes:
|
||||
# Observed nets with 1 to 2 source cell pins
|
||||
# 2 example
|
||||
# [get_pins -filter {DIRECTION == OUT} -of_objects $net]
|
||||
# roi/dout_shr[0]_i_1/O: BEL, visible
|
||||
# roi/dout_shr_reg[0]: no BEL, not visible, named after the net
|
||||
# I don't understand what the relationship between these two is
|
||||
# Maybe register retiming?
|
||||
# Observed nets with 0 to 1 source bel pins
|
||||
# 0 example: <const0>
|
||||
# Recommendation: assume there is at most one source BEL
|
||||
# Take that as the source BEL if it exists, otherwise don't do any source analysis
|
||||
|
||||
# get_timing_paths vs get_net_delays
|
||||
# get_timing_paths is built on top of get_net_delays?
|
||||
# has some different info, but is fixed on the slow max corner
|
||||
# *possibly* reports slightly better info within a site, but would need to verify that
|
||||
|
||||
# Things safe to assume:
|
||||
# -Each net has at least one delay?
|
||||
# -Cell exists for every delay path
|
||||
# -Each net has at least one destination BEL pin
|
||||
# Things not safe to assume
|
||||
# -Each net has at least one source BEL pin (ex: <const0>)
|
||||
|
||||
|
||||
proc list_format {l delim} {
|
||||
set ret ""
|
||||
set needspace 0
|
||||
foreach x $l {
|
||||
if $needspace {
|
||||
set ret "$ret$delim$x"
|
||||
} else {
|
||||
set ret "$x"
|
||||
}
|
||||
set needspace 1
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
proc line_net_external {fp net src_site src_site_type src_site_pin src_bel src_bel_pin dst_site dst_site_type dst_site_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min pips_out wires_out} {
|
||||
puts $fp "NET,$net,$src_site,$src_site_type,$src_site_pin,$src_bel,$src_bel_pin,$dst_site,$dst_site_type,$dst_site_pin,$dst_bel,$dst_bel_pin,$ico,$fast_max,$fast_min,$slow_max,$slow_min,$pips_out,$wires_out"
|
||||
}
|
||||
|
||||
proc line_net_internal {fp net site site_type src_bel src_bel_pin dst_bel dst_bel_pin ico fast_max fast_min slow_max slow_min} {
|
||||
set src_site $site
|
||||
set src_site_type $site_type
|
||||
set src_site_pin ""
|
||||
|
||||
set dst_site $site
|
||||
set dst_site_type $site_type
|
||||
set dst_site_pin ""
|
||||
|
||||
set pips_out ""
|
||||
set wires_out ""
|
||||
|
||||
puts $fp "NET,$net,$src_site,$src_site_type,$src_site_pin,$src_bel,$src_bel_pin,$dst_site,$dst_site_type,$dst_site_pin,$dst_bel,$dst_bel_pin,$ico,$fast_max,$fast_min,$slow_max,$slow_min,$pips_out,$wires_out"
|
||||
}
|
||||
|
||||
proc write_info4 {} {
|
||||
set outdir "."
|
||||
set fp [open "$outdir/timing4.txt" w]
|
||||
# bel as site/bel, so don't bother with site
|
||||
puts $fp "linetype,net,src_site,src_site_type,src_site_pin,src_bel,src_bel_pin,dst_site,dst_site_type,dst_site_pin,dst_bel,dst_bel_pin,ico,fast_max,fast_min,slow_max,slow_min,pips,wires"
|
||||
|
||||
set TIME_start [clock clicks -milliseconds]
|
||||
set delays_net 0
|
||||
set nets_no_src_cell 0
|
||||
set nets_no_src_bel 0
|
||||
set lines_no_int 0
|
||||
set lines_some_int 0
|
||||
set neti 0
|
||||
set nets [get_nets -hierarchical]
|
||||
#set nets [get_nets clk]
|
||||
#set nets [get_nets roi/counter[0]_i_2_n_0]
|
||||
set nnets [llength $nets]
|
||||
foreach net $nets {
|
||||
incr neti
|
||||
|
||||
puts "Net $neti / $nnets: $net"
|
||||
|
||||
# there are some special places like on IOB where you may not have a cell source pin
|
||||
# this is due to special treatment where BEL vs SITE are blurred
|
||||
# The semantics of get_pins -leaf is kind of odd
|
||||
# When no passthrough LUTs exist, it has no effect
|
||||
# When passthrough LUT present:
|
||||
# -w/o -leaf: some pins + passthrough LUT pins
|
||||
# -w/ -leaf: different pins + passthrough LUT pins
|
||||
# With OUT filter this seems to be sufficient
|
||||
set src_cell_pins [get_pins -leaf -filter {DIRECTION == OUT} -of_objects $net]
|
||||
if {$src_cell_pins eq ""} {
|
||||
incr nets_no_src_cell
|
||||
# ex: IOB internal bel net
|
||||
puts " SKIP: no source cell"
|
||||
continue
|
||||
}
|
||||
# 0 to 2 of these
|
||||
# Seems when 2 of them they basically have the same bel + cell
|
||||
# Make a best effort and move forward
|
||||
set src_cell_pin [lindex $src_cell_pins 0]
|
||||
set src_cell [get_cells -of_objects $src_cell_pin]
|
||||
|
||||
# Only applicable if in a site
|
||||
set src_bel [get_bels -of_objects $src_cell]
|
||||
if {$src_bel eq ""} {
|
||||
# just throw out cases where source site doesn't exist
|
||||
# these are very special, don't really have timing info anyway
|
||||
# rework these later if they become problematic
|
||||
incr nets_no_src_bel
|
||||
puts " SKIP: no source bel"
|
||||
continue
|
||||
}
|
||||
|
||||
set src_bel_pin [get_bel_pins -of_objects $src_cell_pin]
|
||||
set src_site [get_sites -of_objects $src_bel]
|
||||
set src_site_type [get_property SITE_TYPE $src_site]
|
||||
# optional
|
||||
set src_site_pin [get_site_pins -of_objects $src_cell_pin]
|
||||
|
||||
# Report delays with and without interconnect only
|
||||
foreach ico "0 1" {
|
||||
if $ico {
|
||||
set delays [get_net_delays -interconnect_only -of_objects $net]
|
||||
} else {
|
||||
set delays [get_net_delays -of_objects $net]
|
||||
}
|
||||
set delays_net [expr "$delays_net + [llength $delays]"]
|
||||
puts $fp "GROUP,$ico,[llength $delays]"
|
||||
foreach delay $delays {
|
||||
#set delaystr [get_property NAME $delay]
|
||||
|
||||
set fast_max [get_property "FAST_MAX" $delay]
|
||||
set fast_min [get_property "FAST_MIN" $delay]
|
||||
set slow_max [get_property "SLOW_MAX" $delay]
|
||||
set slow_min [get_property "SLOW_MIN" $delay]
|
||||
|
||||
# Does this always exist?
|
||||
set dst_cell_pin_str [get_property TO_PIN $delay]
|
||||
set dst_cell_pin [get_pins $dst_cell_pin_str]
|
||||
set dst_cell [get_cells -of_objects $dst_cell_pin]
|
||||
set dst_bel [get_bels -of_objects $dst_cell]
|
||||
# WARNING: when there is a passthrough LUT, this can be multiple values
|
||||
# (one for the real dest pin, and one for the passthrough lut input)
|
||||
set dst_bel_pin [get_bel_pins -of_objects $dst_cell_pin]
|
||||
set dst_site [get_sites -of_objects $dst_bel]
|
||||
set dst_site_type [get_property SITE_TYPE $dst_site]
|
||||
# optional
|
||||
set dst_site_pin [get_site_pins -of_objects $dst_cell_pin]
|
||||
|
||||
# No fabric on this net?
|
||||
# don't try querying sites and such
|
||||
if {[get_nodes -of_objects $net] eq ""} {
|
||||
if {$src_site ne $dst_site} {
|
||||
puts "ERROR: site mismatch"
|
||||
return
|
||||
}
|
||||
if {$src_site_type ne $dst_site_type} {
|
||||
puts "ERROR: site mismatch"
|
||||
return
|
||||
}
|
||||
|
||||
# Already have everything we could query
|
||||
# Just output
|
||||
incr lines_no_int
|
||||
line_net_internal $fp $net $src_site $src_site_type $src_bel $src_bel_pin $dst_bel $dst_bel_pin $ico $fast_max $fast_min $slow_max $slow_min
|
||||
# At least some fabric exists
|
||||
# Does dest BEL exist but not source BEL?
|
||||
} elseif {$src_bel eq ""} {
|
||||
puts "ERROR: should have been filtered"
|
||||
return
|
||||
# Ideally query from and to cell pins
|
||||
} else {
|
||||
# Nested list delimination precedence: ",|:"
|
||||
|
||||
# Pips in between
|
||||
# Walk net, looking for interesting elements in between
|
||||
# -from <args> - (Optional) Defines the starting points of the pips to get
|
||||
# from wire or site_pin
|
||||
set pips [get_pips -of_objects $net -from $src_site_pin -to $dst_site_pin]
|
||||
# Write pips w/ speed index
|
||||
foreach pip $pips {
|
||||
set speed_index [get_property SPEED_INDEX $pip]
|
||||
lappend pips_out "$pip:$speed_index"
|
||||
}
|
||||
set pips_out [list_format "$pips_out" "|"]
|
||||
|
||||
set nodes [get_nodes -of_objects $net -from $src_site_pin -to $dst_site_pin]
|
||||
if {$nodes eq ""} {
|
||||
puts "ERROR: no nodes"
|
||||
return
|
||||
}
|
||||
|
||||
set wires [get_wires -of_objects $nodes]
|
||||
# Write wires
|
||||
foreach wire $wires {
|
||||
set speed_index [get_property SPEED_INDEX $wire]
|
||||
lappend wires_out "$wire:$speed_index"
|
||||
}
|
||||
set wires_out [list_format "$wires_out" "|"]
|
||||
|
||||
incr lines_some_int
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close $fp
|
||||
set TIME_taken [expr [clock clicks -milliseconds] - $TIME_start]
|
||||
puts "Took ms: $TIME_taken"
|
||||
puts "Checked $delays_net delays"
|
||||
puts "Nets: $nnets"
|
||||
puts " Skipped (no source cell): $nets_no_src_cell"
|
||||
puts " Skipped (no source bel): $nets_no_src_bel"
|
||||
puts "Lines"
|
||||
puts " No interconnect: $lines_no_int"
|
||||
puts " Has interconnect: $lines_some_int"
|
||||
}
|
||||
|
||||
# for debugging
|
||||
# source ../project.tcl
|
||||
# write_info4
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, Ar_di2np, loadc_Ads_b, index_names, A_ds2np, simplify_rows, OrderedSet
|
||||
import numpy as np
|
||||
import glob
|
||||
import math
|
||||
import json
|
||||
import sympy
|
||||
from collections import OrderedDict
|
||||
from fractions import Fraction
|
||||
|
||||
|
||||
def rm_zero_cols(Ads, verbose=True):
|
||||
removed = OrderedSet()
|
||||
|
||||
print('Removing ZERO elements')
|
||||
for row_ds in Ads:
|
||||
for k in set(row_ds.keys()):
|
||||
if k in removed:
|
||||
del row_ds[k]
|
||||
elif k.find('ZERO') >= 0:
|
||||
del row_ds[k]
|
||||
removed.add(k)
|
||||
if verbose:
|
||||
print(' Removing %s' % k)
|
||||
return removed
|
||||
|
||||
|
||||
def fracr_quick(r):
|
||||
return [Fraction(numerator=int(x), denominator=1) for x in r]
|
||||
|
||||
|
||||
def fracm_quick(m):
|
||||
'''Convert integer matrix to Fraction matrix'''
|
||||
t = type(m[0][0])
|
||||
print('fracm_quick type: %s' % t)
|
||||
return [fracr_quick(r) for r in m]
|
||||
|
||||
|
||||
class State(object):
|
||||
def __init__(self, Ads, zero_names=[]):
|
||||
self.Ads = Ads
|
||||
self.names = index_names(self.Ads)
|
||||
|
||||
# known zero delay elements
|
||||
self.zero_names = OrderedSet(zero_names)
|
||||
# active names in rows
|
||||
# includes sub variables, excludes variables that have been substituted out
|
||||
self.base_names = OrderedSet(self.names)
|
||||
#self.names = OrderedSet(self.base_names)
|
||||
self.names = set(self.base_names)
|
||||
# List of variable substitutions
|
||||
# k => dict of v:n entries that it came from
|
||||
self.subs = OrderedDict()
|
||||
self.verbose = True
|
||||
|
||||
def print_stats(self):
|
||||
print("Stats")
|
||||
print(" Substitutions: %u" % len(self.subs))
|
||||
if self.subs:
|
||||
print(
|
||||
" Largest: %u" % max([len(x) for x in self.subs.values()]))
|
||||
print(" Rows: %u" % len(self.Ads))
|
||||
print(
|
||||
" Cols (in): %u" % (len(self.base_names) + len(self.zero_names)))
|
||||
print(" Cols (preprocessed): %u" % len(self.base_names))
|
||||
print(" ZERO names: %u" % len(self.zero_names))
|
||||
print(" Cols (out): %u" % len(self.names))
|
||||
print(" Solvable vars: %u" % len(self.names & self.base_names))
|
||||
assert len(self.names) >= len(self.subs)
|
||||
|
||||
@staticmethod
|
||||
def load(fn_ins, simplify=False, corner=None, rm_zero=False):
|
||||
zero_names = OrderedSet()
|
||||
|
||||
Ads, b = loadc_Ads_b(fn_ins, corner=corner)
|
||||
if rm_zero:
|
||||
zero_names = rm_zero_cols(Ads)
|
||||
if simplify:
|
||||
print('Simplifying corner %s' % (corner, ))
|
||||
Ads, b = simplify_rows(Ads, b, remove_zd=False, corner=corner)
|
||||
return State(Ads, zero_names=zero_names)
|
||||
|
||||
|
||||
def write_state(state, fout):
|
||||
j = {
|
||||
'names':
|
||||
OrderedDict([(x, None) for x in state.names]),
|
||||
'zero_names':
|
||||
sorted(list(state.zero_names)),
|
||||
'base_names':
|
||||
sorted(list(state.base_names)),
|
||||
'subs':
|
||||
OrderedDict([(name, values) for name, values in state.subs.items()]),
|
||||
'pivots':
|
||||
state.pivots,
|
||||
}
|
||||
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
|
||||
def row_np2ds(rownp, names):
|
||||
ret = OrderedDict()
|
||||
assert len(rownp) == len(names), (len(rownp), len(names))
|
||||
for namei, name in enumerate(names):
|
||||
v = rownp[namei]
|
||||
if v:
|
||||
ret[name] = v
|
||||
return ret
|
||||
|
||||
|
||||
def row_sym2dsf(rowsym, names):
|
||||
'''Convert a sympy row into a dictionary of keys to (numerator, denominator) tuples'''
|
||||
from sympy import fraction
|
||||
|
||||
ret = OrderedDict()
|
||||
assert len(rowsym) == len(names), (len(rowsym), len(names))
|
||||
for namei, name in enumerate(names):
|
||||
v = rowsym[namei]
|
||||
if v:
|
||||
(num, den) = fraction(v)
|
||||
ret[name] = (int(num), int(den))
|
||||
return ret
|
||||
|
||||
|
||||
def state_rref(state, verbose=False):
|
||||
print('Converting rows to integer keys')
|
||||
names, Anp = A_ds2np(state.Ads)
|
||||
|
||||
print('np: %u rows x %u cols' % (len(Anp), len(Anp[0])))
|
||||
mnp = Anp
|
||||
print('Matrix: %u rows x %u cols' % (len(mnp), len(mnp[0])))
|
||||
print('Converting np to sympy matrix')
|
||||
mfrac = fracm_quick(mnp)
|
||||
# doesn't seem to change anything
|
||||
#msym = sympy.MutableSparseMatrix(mfrac)
|
||||
msym = sympy.Matrix(mfrac)
|
||||
# internal encoding has significnat performance implications
|
||||
#assert type(msym[0]) is sympy.Integer
|
||||
|
||||
if verbose:
|
||||
print('names')
|
||||
print(names)
|
||||
print('Matrix')
|
||||
sympy.pprint(msym)
|
||||
print('Making rref')
|
||||
rref, pivots = msym.rref(normalize_last=False)
|
||||
|
||||
if verbose:
|
||||
print('Pivots')
|
||||
sympy.pprint(pivots)
|
||||
print('rref')
|
||||
sympy.pprint(rref)
|
||||
|
||||
state.pivots = OrderedDict()
|
||||
|
||||
def row_solved(rowsym, row_pivot):
|
||||
for ci, c in enumerate(rowsym):
|
||||
if ci == row_pivot:
|
||||
continue
|
||||
if c != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
#rrefnp = np.array(rref).astype(np.float64)
|
||||
#print('Computing groups w/ rref %u row x %u col' % (len(rrefnp), len(rrefnp[0])))
|
||||
#print(rrefnp)
|
||||
# rows that have a single 1 are okay
|
||||
# anything else requires substitution (unless all 0)
|
||||
# pivots may be fewer than the rows
|
||||
# remaining rows should be 0s
|
||||
for row_i, row_pivot in enumerate(pivots):
|
||||
rowsym = rref.row(row_i)
|
||||
# yipee! nothign to report
|
||||
if row_solved(rowsym, row_pivot):
|
||||
continue
|
||||
|
||||
# a grouping
|
||||
group_name = "GRP_%u" % row_i
|
||||
rowdsf = row_sym2dsf(rowsym, names)
|
||||
|
||||
state.subs[group_name] = rowdsf
|
||||
# Add the new variables
|
||||
state.names.add(group_name)
|
||||
# Remove substituted variables
|
||||
# Note: variables may appear multiple times
|
||||
state.names.difference_update(OrderedSet(rowdsf.keys()))
|
||||
pivot_name = names[row_pivot]
|
||||
state.pivots[group_name] = pivot_name
|
||||
if verbose:
|
||||
print("%s (%s): %s" % (group_name, pivot_name, rowdsf))
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def run(fnout, fn_ins, simplify=False, corner=None, rm_zero=False, verbose=0):
|
||||
print('Loading data')
|
||||
|
||||
assert len(fn_ins) > 0
|
||||
state = State.load(
|
||||
fn_ins, simplify=simplify, corner=corner, rm_zero=rm_zero)
|
||||
state_rref(state, verbose=verbose)
|
||||
state.print_stats()
|
||||
if fnout:
|
||||
with open(fnout, 'w') as fout:
|
||||
write_state(state, fout)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Compute reduced row echelon (RREF) to form sub.json (variable groups)'
|
||||
)
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--simplify', action='store_true', help='')
|
||||
parser.add_argument('--corner', default="slow_max", help='')
|
||||
parser.add_argument(
|
||||
'--rm-zero', action='store_true', help='Remove ZERO elements')
|
||||
parser.add_argument(
|
||||
'--speed-json',
|
||||
default='build_speed/speed.json',
|
||||
help='Provides speed index to name translation')
|
||||
parser.add_argument('--out', help='Output sub.json substitution result')
|
||||
parser.add_argument('fns_in', nargs='*', help='timing4i.csv input files')
|
||||
args = parser.parse_args()
|
||||
bench = Benchmark()
|
||||
|
||||
fns_in = args.fns_in
|
||||
if not fns_in:
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
try:
|
||||
run(
|
||||
fnout=args.out,
|
||||
fn_ins=fns_in,
|
||||
simplify=args.simplify,
|
||||
corner=args.corner,
|
||||
rm_zero=args.rm_zero,
|
||||
verbose=args.verbose)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, load_sub
|
||||
import timfuz
|
||||
import numpy as np
|
||||
import math
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import timfuz_solve
|
||||
import scipy.optimize as optimize
|
||||
|
||||
|
||||
def mkestimate(Anp, b):
|
||||
'''
|
||||
Ballpark upper bound estimate assuming variables contribute all of the delay in their respective row
|
||||
Return the min of all of the occurances
|
||||
|
||||
XXX: should this be corner adjusted?
|
||||
'''
|
||||
cols = len(Anp[0])
|
||||
x0 = np.array([1e3 for _x in range(cols)])
|
||||
for row_np, row_b in zip(Anp, b):
|
||||
for coli, val in enumerate(row_np):
|
||||
# favor non-trivial values
|
||||
if val <= 0:
|
||||
continue
|
||||
# Scale by number occurances
|
||||
ub = row_b / val
|
||||
if ub <= 0:
|
||||
continue
|
||||
if x0[coli] == 0:
|
||||
x0[coli] = ub
|
||||
else:
|
||||
x0[coli] = min(x0[coli], ub)
|
||||
# reject solutions that don't provide a seed value
|
||||
# these lead to bad optimizations
|
||||
assert sum(x0) != 0
|
||||
return x0
|
||||
|
||||
|
||||
def run_corner(
|
||||
Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
|
||||
# Given timing scores for above delays (-ps)
|
||||
assert type(Anp[0]) is np.ndarray, type(Anp[0])
|
||||
assert type(b) is np.ndarray, type(b)
|
||||
|
||||
#check_feasible(Anp, b)
|
||||
'''
|
||||
Be mindful of signs
|
||||
Have something like
|
||||
timing1/timing 2 are constants
|
||||
delay1 + delay2 + delay4 >= timing1
|
||||
delay2 + delay3 >= timing2
|
||||
|
||||
But need it in compliant form:
|
||||
-delay1 + -delay2 + -delay4 <= -timing1
|
||||
-delay2 + -delay3 <= -timing2
|
||||
'''
|
||||
rows = len(Anp)
|
||||
cols = len(Anp[0])
|
||||
|
||||
print('Unique delay elements: %d' % len(names))
|
||||
print('Input paths')
|
||||
print(' # timing scores: %d' % len(b))
|
||||
print(' Rows: %d' % rows)
|
||||
'''
|
||||
You must have at least as many things to optimize as variables
|
||||
That is, the system must be plausibly constrained for it to attempt a solve
|
||||
If not, you'll get a message like
|
||||
TypeError: Improper input: N=3 must not exceed M=2
|
||||
'''
|
||||
if rows < cols:
|
||||
raise Exception("rows must be >= cols")
|
||||
|
||||
tlast = [None]
|
||||
iters = [0]
|
||||
printn = [0]
|
||||
|
||||
def progress_print():
|
||||
iters[0] += 1
|
||||
if tlast[0] is None:
|
||||
tlast[0] = time.time()
|
||||
if time.time() - tlast[0] > 1.0:
|
||||
sys.stdout.write('I:%d ' % iters[0])
|
||||
tlast[0] = time.time()
|
||||
printn[0] += 1
|
||||
if printn[0] % 10 == 0:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
def func(params):
|
||||
progress_print()
|
||||
return (b - np.dot(Anp, params))
|
||||
|
||||
print('')
|
||||
# Now find smallest values for delay constants
|
||||
# Due to input bounds (ex: column limit), some delay elements may get eliminated entirely
|
||||
print('Running leastsq w/ %d r, %d c (%d name)' % (rows, cols, len(names)))
|
||||
# starting at 0 completes quicky, but gives a solution near 0 with terrible results
|
||||
# maybe give a starting estimate to the smallest net delay with the indicated variable
|
||||
#x0 = np.array([1000.0 for _x in range(cols)])
|
||||
print('Creating x0 estimate')
|
||||
x0 = mkestimate(Anp, b)
|
||||
|
||||
print('Solving')
|
||||
res = optimize.least_squares(func, x0, bounds=(0, float('inf')))
|
||||
print('Done')
|
||||
|
||||
if outfn:
|
||||
timfuz_solve.solve_save(outfn, res.x, names, corner, verbose=verbose)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Solve timing solution using least squares objective function')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument(
|
||||
'--massage',
|
||||
action='store_true',
|
||||
help='Derive additional constraints to improve solution')
|
||||
parser.add_argument(
|
||||
'--sub-json', help='Group substitutions to make fully ranked')
|
||||
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
||||
parser.add_argument(
|
||||
'--out', default=None, help='output timing delay .json')
|
||||
parser.add_argument('fns_in', nargs='+', help='timing4i.csv input files')
|
||||
args = parser.parse_args()
|
||||
# Store options in dict to ease passing through functions
|
||||
bench = Benchmark()
|
||||
|
||||
fns_in = args.fns_in
|
||||
if not fns_in:
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = None
|
||||
if args.sub_json:
|
||||
sub_json = load_sub(args.sub_json)
|
||||
|
||||
try:
|
||||
timfuz_solve.run(
|
||||
run_corner=run_corner,
|
||||
sub_json=sub_json,
|
||||
fns_in=fns_in,
|
||||
corner=args.corner,
|
||||
massage=args.massage,
|
||||
outfn=args.out,
|
||||
verbose=args.verbose)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import scipy.optimize as optimize
|
||||
from timfuz import Benchmark, load_sub, A_ub_np2d, acorner2csv, corner_s2i
|
||||
import numpy as np
|
||||
import glob
|
||||
import json
|
||||
import math
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import timfuz_solve
|
||||
|
||||
|
||||
def run_corner(
|
||||
Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None):
|
||||
if len(Anp) == 0:
|
||||
print('WARNING: zero equations')
|
||||
if outfn:
|
||||
timfuz_solve.solve_save(outfn, [], [], corner)
|
||||
return
|
||||
maxcorner = {
|
||||
'slow_max': True,
|
||||
'slow_min': False,
|
||||
'fast_max': True,
|
||||
'fast_min': False,
|
||||
}[corner]
|
||||
|
||||
# Given timing scores for above delays (-ps)
|
||||
assert type(Anp[0]) is np.ndarray, type(Anp[0])
|
||||
assert type(b) is np.ndarray, type(b)
|
||||
|
||||
#check_feasible(Anp, b)
|
||||
'''
|
||||
Be mindful of signs
|
||||
t1, t2: total delay contants
|
||||
d1, d2..: variables to solve for
|
||||
|
||||
Max corner intuitive form:
|
||||
d1 + d2 + d4 >= t1
|
||||
d2 + d3 >= t2
|
||||
|
||||
But need it in compliant form:
|
||||
-d1 + -d2 + -d4 <= -t1
|
||||
-d2 + -d3 <= -t2
|
||||
Minimize delay elements
|
||||
|
||||
|
||||
Min corner intuitive form:
|
||||
d1 + d2 + d4 <= t1
|
||||
d2 + d3 <= t2
|
||||
Maximize delay elements
|
||||
'''
|
||||
|
||||
rows = len(Anp)
|
||||
cols = len(Anp[0])
|
||||
if maxcorner:
|
||||
print('maxcorner => scaling to solution form...')
|
||||
b_ub = -1.0 * b
|
||||
#A_ub = -1.0 * Anp
|
||||
A_ub = [-1.0 * x for x in Anp]
|
||||
else:
|
||||
print('mincorner => no scaling required')
|
||||
b_ub = b
|
||||
A_ub = Anp
|
||||
|
||||
print('Creating misc constants...')
|
||||
# Minimization function scalars
|
||||
# Treat all logic elements as equally important
|
||||
if maxcorner:
|
||||
# Best result are min delays
|
||||
c = [1 for _i in range(len(names))]
|
||||
else:
|
||||
# Best result are max delays
|
||||
c = [-1 for _i in range(len(names))]
|
||||
# Delays cannot be negative
|
||||
# (this is also the default constraint)
|
||||
#bounds = [(0, None) for _i in range(len(names))]
|
||||
# Also you can provide one to apply to all
|
||||
bounds = (0, None)
|
||||
|
||||
# Seems to take about rows + 3 iterations
|
||||
# Give some margin
|
||||
#maxiter = int(1.1 * rows + 100)
|
||||
#maxiter = max(1000, int(1000 * rows + 1000))
|
||||
# Most of the time I want it to just keep going unless I ^C it
|
||||
maxiter = 1000000
|
||||
|
||||
if verbose >= 2:
|
||||
print('b_ub', b)
|
||||
print('Unique delay elements: %d' % len(names))
|
||||
print(' # delay minimization weights: %d' % len(c))
|
||||
print(' # delay constraints: %d' % len(bounds))
|
||||
print('Input paths')
|
||||
print(' # timing scores: %d' % len(b))
|
||||
print(' Rows: %d' % rows)
|
||||
|
||||
tlast = [time.time()]
|
||||
iters = [0]
|
||||
printn = [0]
|
||||
|
||||
def callback(xk, **kwargs):
|
||||
iters[0] = xk['nit']
|
||||
if time.time() - tlast[0] > 1.0:
|
||||
sys.stdout.write('I:%d ' % xk['nit'])
|
||||
tlast[0] = time.time()
|
||||
printn[0] += 1
|
||||
if printn[0] % 10 == 0:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
print('')
|
||||
# Now find smallest values for delay constants
|
||||
# Due to input bounds (ex: column limit), some delay elements may get eliminated entirely
|
||||
# https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.optimize.linprog.html
|
||||
print('Running linprog w/ %d r, %d c (%d name)' % (rows, cols, len(names)))
|
||||
res = optimize.linprog(
|
||||
c,
|
||||
A_ub=A_ub,
|
||||
b_ub=b_ub,
|
||||
bounds=bounds,
|
||||
callback=callback,
|
||||
options={
|
||||
"disp": True,
|
||||
'maxiter': maxiter,
|
||||
'bland': True,
|
||||
'tol': 1e-6,
|
||||
})
|
||||
nonzeros = 0
|
||||
print('Ran %d iters' % iters[0])
|
||||
if res.success:
|
||||
print('Result sample (%d elements)' % (len(res.x)))
|
||||
plim = 3
|
||||
for xi, (name, x) in enumerate(zip(names, res.x)):
|
||||
nonzero = x >= 0.001
|
||||
if nonzero:
|
||||
nonzeros += 1
|
||||
#if nonzero and (verbose >= 1 or xi > 30):
|
||||
if nonzero and (verbose or (
|
||||
(nonzeros < 100 or nonzeros % 20 == 0) and nonzeros <= plim)):
|
||||
print(' % 4u % -80s % 10.1f' % (xi, name, x))
|
||||
print('Delay on %d / %d' % (nonzeros, len(res.x)))
|
||||
|
||||
if outfn:
|
||||
timfuz_solve.solve_save(
|
||||
outfn, res.x, names, corner, verbose=verbose)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Solve timing solution using linear programming inequalities')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--massage', action='store_true', help='')
|
||||
parser.add_argument(
|
||||
'--bounds-csv', help='Previous solve result starting point')
|
||||
parser.add_argument(
|
||||
'--sub-json', help='Group substitutions to make fully ranked')
|
||||
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
||||
parser.add_argument(
|
||||
'--out', default=None, help='output timing delay .json')
|
||||
parser.add_argument('fns_in', nargs='+', help='timing4i.csv input files')
|
||||
args = parser.parse_args()
|
||||
# Store options in dict to ease passing through functions
|
||||
bench = Benchmark()
|
||||
|
||||
fns_in = args.fns_in
|
||||
if not fns_in:
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
sub_json = None
|
||||
if args.sub_json:
|
||||
sub_json = load_sub(args.sub_json)
|
||||
|
||||
try:
|
||||
timfuz_solve.run(
|
||||
run_corner=run_corner,
|
||||
sub_json=sub_json,
|
||||
bounds_csv=args.bounds_csv,
|
||||
fns_in=fns_in,
|
||||
corner=args.corner,
|
||||
massage=args.massage,
|
||||
outfn=args.out,
|
||||
verbose=args.verbose)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark, load_sub, load_bounds, loadc_Ads_b
|
||||
import numpy as np
|
||||
|
||||
|
||||
def run(fns_in, corner, bounds_csv, verbose=False):
|
||||
print('Loading data')
|
||||
Ads, borig = loadc_Ads_b(fns_in, corner)
|
||||
|
||||
bounds = load_bounds(bounds_csv, corner)
|
||||
# verify is flattened
|
||||
for k in bounds.keys():
|
||||
assert 'GROUP_' not in k, 'Must operate on flattened bounds'
|
||||
|
||||
# compute our timing model delay at the given corner
|
||||
bgots = []
|
||||
for row_ds in Ads:
|
||||
delays = [n * bounds[x] for x, n in row_ds.items()]
|
||||
bgots.append(sum(delays))
|
||||
|
||||
ses = (np.asarray(bgots) - np.asarray(borig))**2
|
||||
mse = (ses).mean(axis=None)
|
||||
print('MSE aggregate: %0.1f' % mse)
|
||||
print('Min SE: %0.1f' % min(ses))
|
||||
print('Max SE: %0.1f' % max(ses))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Report a timing fit score')
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--corner', required=True, default="slow_max", help='')
|
||||
parser.add_argument(
|
||||
'--bounds-csv',
|
||||
required=True,
|
||||
help='Previous solve result starting point')
|
||||
parser.add_argument('fns_in', nargs='+', help='timing4i.csv input files')
|
||||
args = parser.parse_args()
|
||||
# Store options in dict to ease passing through functions
|
||||
bench = Benchmark()
|
||||
|
||||
fns_in = args.fns_in
|
||||
if not fns_in:
|
||||
fns_in = glob.glob('specimen_*/timing4i.csv')
|
||||
|
||||
try:
|
||||
run(
|
||||
fns_in=fns_in,
|
||||
corner=args.corner,
|
||||
bounds_csv=args.bounds_csv,
|
||||
verbose=args.verbose)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
build
|
||||
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
all: build/speed.json
|
||||
|
||||
build/node.txt: speed_json.py generate.tcl
|
||||
mkdir -p build
|
||||
cd build && ${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
|
||||
build/speed.json: build/node.txt
|
||||
cd build && python3 ../speed_json.py speed_model.txt node.txt speed.json
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Generates speed.json, describing speed index to string translation.
|
||||
These indices appear to be fixed between runtimes.
|
||||
It is unknown how stable they are across versions.
|
||||
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
proc pin_info {pin} {
|
||||
set cell [get_cells -of_objects $pin]
|
||||
set bel [get_bels -of_objects $cell]
|
||||
set site [get_sites -of_objects $bel]
|
||||
return "$site $bel"
|
||||
}
|
||||
|
||||
proc pin_bel {pin} {
|
||||
set cell [get_cells -of_objects $pin]
|
||||
set bel [get_bels -of_objects $cell]
|
||||
return $bel
|
||||
}
|
||||
|
||||
proc build_design_full {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
read_verilog ../top.v
|
||||
read_verilog ../../src/picorv32.v
|
||||
synth_design -top top
|
||||
|
||||
#set_property LOCK_PINS {I0:A1 I1:A2 I2:A3 I3:A4 I4:A5 I5:A6} \
|
||||
# [get_cells -quiet -filter {REF_NAME == LUT6} -hierarchical]
|
||||
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
|
||||
proc build_design_synth {} {
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
|
||||
read_verilog ../top.v
|
||||
read_verilog ../picorv32.v
|
||||
synth_design -top top
|
||||
}
|
||||
|
||||
# WARNING: [Common 17-673] Cannot get value of property 'FORWARD' because this property is not valid in conjunction with other property setting on this object.
|
||||
# WARNING: [Common 17-673] Cannot get value of property 'REVERSE' because this property is not valid in conjunction with other property setting on this object.
|
||||
proc speed_models1 {} {
|
||||
set outdir "."
|
||||
set fp [open "$outdir/speed_model.txt" w]
|
||||
# list_property [lindex [get_speed_models] 0]
|
||||
set speed_models [get_speed_models]
|
||||
set properties [list_property [lindex $speed_models 0]]
|
||||
# "CLASS DELAY FAST_MAX FAST_MIN IS_INSTANCE_SPECIFIC NAME NAME_LOGICAL SLOW_MAX SLOW_MIN SPEED_INDEX TYPE"
|
||||
puts $fp $properties
|
||||
|
||||
set needspace 0
|
||||
foreach speed_model $speed_models {
|
||||
foreach property $properties {
|
||||
if $needspace {
|
||||
puts -nonewline $fp " "
|
||||
}
|
||||
puts -nonewline $fp [get_property $property $speed_model]
|
||||
set needspace 1
|
||||
}
|
||||
puts $fp ""
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
|
||||
proc speed_models2 {} {
|
||||
set outdir "."
|
||||
set fp [open "$outdir/speed_model.txt" w]
|
||||
# list_property [lindex [get_speed_models] 0]
|
||||
set speed_models [get_speed_models]
|
||||
puts "Items: [llength $speed_models]"
|
||||
|
||||
set needspace 0
|
||||
# Not all objects have the same properties
|
||||
# But they do seem to have the same list
|
||||
set properties [list_property [lindex $speed_models 0]]
|
||||
foreach speed_model $speed_models {
|
||||
set needspace 0
|
||||
foreach property $properties {
|
||||
set val [get_property $property $speed_model]
|
||||
if {"$val" ne ""} {
|
||||
if $needspace {
|
||||
puts -nonewline $fp " "
|
||||
}
|
||||
puts -nonewline $fp "$property:$val"
|
||||
set needspace 1
|
||||
}
|
||||
}
|
||||
puts $fp ""
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
|
||||
|
||||
# For cost codes
|
||||
# Items: 2663055s
|
||||
# Hmm too much
|
||||
# Lets filter out items we really want
|
||||
proc nodes_all {} {
|
||||
set outdir "."
|
||||
set fp [open "$outdir/node_all.txt" w]
|
||||
set items [get_nodes]
|
||||
puts "Items: [llength $items]"
|
||||
|
||||
set needspace 0
|
||||
set properties [list_property [lindex $items 0]]
|
||||
foreach item $items {
|
||||
set needspace 0
|
||||
foreach property $properties {
|
||||
set val [get_property $property $item]
|
||||
if {"$val" ne ""} {
|
||||
if $needspace {
|
||||
puts -nonewline $fp " "
|
||||
}
|
||||
puts -nonewline $fp "$property:$val"
|
||||
set needspace 1
|
||||
}
|
||||
}
|
||||
puts $fp ""
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
|
||||
# Only writes out items with unique cost codes
|
||||
# (much faster)
|
||||
proc nodes_unique_cc {} {
|
||||
set outdir "."
|
||||
set fp [open "$outdir/node.txt" w]
|
||||
set items [get_nodes]
|
||||
puts "Computing cost codes with [llength $items] items"
|
||||
|
||||
set needspace 0
|
||||
set properties [list_property [lindex $items 0]]
|
||||
set cost_codes_known [dict create]
|
||||
set itemi 0
|
||||
foreach item $items {
|
||||
incr itemi
|
||||
set cost_code [get_property COST_CODE $item]
|
||||
if {[ dict exists $cost_codes_known $cost_code ]} {
|
||||
continue
|
||||
}
|
||||
puts " Adding cost code $cost_code @ item $itemi"
|
||||
dict set cost_codes_known $cost_code 1
|
||||
|
||||
set needspace 0
|
||||
foreach property $properties {
|
||||
set val [get_property $property $item]
|
||||
if {"$val" ne ""} {
|
||||
if $needspace {
|
||||
puts -nonewline $fp " "
|
||||
}
|
||||
puts -nonewline $fp "$property:$val"
|
||||
set needspace 1
|
||||
}
|
||||
}
|
||||
puts $fp ""
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
|
||||
build_design_full
|
||||
speed_models2
|
||||
nodes_unique_cc
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
import json
|
||||
|
||||
|
||||
def load_speed(fin):
|
||||
speed_models = {}
|
||||
speed_types = {}
|
||||
for l in fin:
|
||||
delay = {}
|
||||
l = l.strip()
|
||||
for kvs in l.split():
|
||||
name, value = kvs.split(':')
|
||||
name = name.lower()
|
||||
if name in ('class', ):
|
||||
continue
|
||||
if name in ('speed_index', ):
|
||||
value = int(value)
|
||||
if name == 'type':
|
||||
speed_types.setdefault(value, {})
|
||||
delay[name] = value
|
||||
delayk = delay['name']
|
||||
if delayk in delay:
|
||||
raise Exception("Duplicate name")
|
||||
|
||||
if "name" in delay and "name_logical" in delay:
|
||||
# Always true
|
||||
if delay['name'] != delay['name_logical']:
|
||||
raise Exception("nope!")
|
||||
# Found a counter example
|
||||
if 0 and delay['name'] != delay['forward']:
|
||||
# ('BSW_NONTLFW_TLRV', '_BSW_LONG_NONTLFORWARD')
|
||||
print(delay['name'], delay['forward'])
|
||||
raise Exception("nope!")
|
||||
# Found a counter example
|
||||
if 0 and delay['forward'] != delay['reverse']:
|
||||
# _BSW_LONG_NONTLFORWARD _BSW_LONG_TLREVERSE
|
||||
print(delay['forward'], delay['reverse'])
|
||||
raise Exception("nope!")
|
||||
|
||||
speed_models[delayk] = delay
|
||||
return speed_models, speed_types
|
||||
|
||||
|
||||
def load_cost_code(fin):
|
||||
# COST_CODE:4 COST_CODE_NAME:SLOWSINGLE
|
||||
cost_codes = {}
|
||||
for l in fin:
|
||||
lj = {}
|
||||
l = l.strip()
|
||||
for kvs in l.split():
|
||||
name, value = kvs.split(':')
|
||||
name = name.lower()
|
||||
lj[name] = value
|
||||
cost_code = {
|
||||
'name': lj['cost_code_name'],
|
||||
'code': int(lj['cost_code']),
|
||||
# Hmm is this unique per type?
|
||||
#'speed_class': int(lj['speed_class']),
|
||||
}
|
||||
|
||||
cost_codes[cost_code['name']] = cost_code
|
||||
return cost_codes
|
||||
|
||||
|
||||
def run(speed_fin, node_fin, fout, verbose=0):
|
||||
print('Loading data')
|
||||
speed_models, speed_types = load_speed(speed_fin)
|
||||
cost_codes = load_cost_code(node_fin)
|
||||
|
||||
j = {
|
||||
'speed_model': speed_models,
|
||||
'speed_type': speed_types,
|
||||
'cost_code': cost_codes,
|
||||
}
|
||||
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Timing fuzzer')
|
||||
|
||||
parser.add_argument('--verbose', type=int, help='')
|
||||
parser.add_argument(
|
||||
'speed_fn_in', default='/dev/stdin', nargs='?', help='Input file')
|
||||
parser.add_argument(
|
||||
'node_fn_in', default='/dev/stdin', nargs='?', help='Input file')
|
||||
parser.add_argument(
|
||||
'fn_out', default='/dev/stdout', nargs='?', help='Output file')
|
||||
args = parser.parse_args()
|
||||
run(
|
||||
open(args.speed_fn_in, 'r'),
|
||||
open(args.node_fn_in, 'r'),
|
||||
open(args.fn_out, 'w'),
|
||||
verbose=args.verbose)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
module top(input clk, stb, di, output do);
|
||||
reg dor;
|
||||
always @(posedge clk) begin
|
||||
dor <= stb & di;
|
||||
end
|
||||
assign do = dor;
|
||||
endmodule
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import Benchmark
|
||||
import json
|
||||
|
||||
|
||||
def write_state(state, fout):
|
||||
j = {
|
||||
'names': dict([(x, None) for x in state.names]),
|
||||
'drop_names': list(state.drop_names),
|
||||
'base_names': list(state.base_names),
|
||||
'subs': dict([(name, values) for name, values in state.subs.items()]),
|
||||
'pivots': state.pivots,
|
||||
}
|
||||
json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
|
||||
def gen_rows(fn_ins):
|
||||
for fn_in in fn_ins:
|
||||
try:
|
||||
print('Loading %s' % fn_in)
|
||||
j = json.load(open(fn_in, 'r'))
|
||||
|
||||
group0 = list(j['subs'].values())[0]
|
||||
value0 = list(group0.values())[0]
|
||||
if type(value0) is float:
|
||||
print("WARNING: skipping old format JSON")
|
||||
continue
|
||||
else:
|
||||
print("Value OK")
|
||||
|
||||
for sub in j['subs'].values():
|
||||
row_ds = {}
|
||||
# TODO: convert to gcd
|
||||
# den may not always be 0
|
||||
# lazy solution...just multiply out all the fractions
|
||||
n = 1
|
||||
for _var, (_num, den) in sub.items():
|
||||
n *= den
|
||||
|
||||
for var, (num, den) in sub.items():
|
||||
num2 = n * num
|
||||
assert num2 % den == 0
|
||||
row_ds[var] = num2 / den
|
||||
yield row_ds
|
||||
except:
|
||||
print("Error processing %s" % fn_in)
|
||||
raise
|
||||
|
||||
|
||||
def run(fnout, fn_ins, verbose=0):
|
||||
print('Loading data')
|
||||
|
||||
with open(fnout, 'w') as fout:
|
||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
||||
for row_ds in gen_rows(fn_ins):
|
||||
ico = '1'
|
||||
out_b = [1e9, 1e9, 1e9, 1e9]
|
||||
items = [ico, ' '.join(['%u' % x for x in out_b])]
|
||||
|
||||
for k, v in sorted(row_ds.items()):
|
||||
items.append('%i %s' % (v, k))
|
||||
fout.write(','.join(items) + '\n')
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Convert substitution groups into .csv to allow incremental rref results'
|
||||
)
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--out', help='Output csv')
|
||||
parser.add_argument('fns_in', nargs='+', help='sub.json input files')
|
||||
args = parser.parse_args()
|
||||
bench = Benchmark()
|
||||
|
||||
fns_in = args.fns_in
|
||||
|
||||
try:
|
||||
run(fnout=args.out, fn_ins=args.fns_in, verbose=args.verbose)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import timfuz
|
||||
from timfuz import loadc_Ads_bs, Ads2bounds, PREFIX_W, PREFIX_P, PREFIX_SW_EI, PREFIX_SW_EO, PREFIX_SW_I, sw_ei_s2vals, sw_eo_s2vals, sw_i_s2vals
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
|
||||
def add_pip_wire(tilej, bounds, verbose=False):
|
||||
'''
|
||||
We know all possible pips and wires from tilej
|
||||
Iterate over them and see if a result was generated
|
||||
'''
|
||||
used_bounds = set()
|
||||
|
||||
for tile in tilej['tiles'].values():
|
||||
|
||||
def addk(pws, prefix, k, v):
|
||||
variable = prefix + ':' + v
|
||||
val = bounds.get(variable, None)
|
||||
# print(variable, val)
|
||||
if val:
|
||||
used_bounds.add(variable)
|
||||
else:
|
||||
val = [None, None, None, None]
|
||||
pws[k] = val
|
||||
|
||||
pips = tile['pips']
|
||||
for k, v in pips.items():
|
||||
#pips[k] = bounds.get('PIP_' + v, [None, None, None, None])
|
||||
addk(pips, PREFIX_P, k, v)
|
||||
|
||||
wires = tile['wires']
|
||||
for k, v in wires.items():
|
||||
#wires[k] = bounds.get('WIRE_' + v, [None, None, None, None])
|
||||
addk(wires, PREFIX_W, k, v)
|
||||
|
||||
# verify all the variables that should be used were applied
|
||||
# ...except tilecon may be an ROI and we solved everything
|
||||
print(
|
||||
"Interconnect: %u / %u variables used" %
|
||||
(len(used_bounds), len(bounds)))
|
||||
if verbose:
|
||||
print('Remainder: %s' % (set(bounds.keys()) - used_bounds))
|
||||
|
||||
|
||||
def add_sites(tilej, bounds):
|
||||
# XXX: no source of truth currently
|
||||
# is there a way we could get this?
|
||||
# Naming discussion https://github.com/SymbiFlow/prjxray/pull/126
|
||||
|
||||
sitej = tilej.setdefault('sites', {})
|
||||
|
||||
def add_ei():
|
||||
for variable, bound in bounds[PREFIX_SW_EI].items():
|
||||
site_type, src_site_pin, dst_bel_type, dst_bel_pin = sw_ei_s2vals(
|
||||
variable)
|
||||
# ex: SLICEL:AX->AFF.D
|
||||
k = (
|
||||
'%s:%s->%s.%s' %
|
||||
(site_type, src_site_pin, dst_bel_type, dst_bel_pin))
|
||||
sitej.setdefault(site_type, {})[k] = bound
|
||||
|
||||
def add_eo():
|
||||
for variable, bound in bounds[PREFIX_SW_EO].items():
|
||||
site_type, src_bel_type, src_bel_pin, dst_site_pin = sw_eo_s2vals(
|
||||
variable)
|
||||
# ex: SLICEL:AFF.Q->AQ
|
||||
k = (
|
||||
'%s:%s.%s->%s' %
|
||||
(site_type, src_bel_type, src_bel_pin, dst_site_pin))
|
||||
sitej.setdefault(site_type, {})[k] = bound
|
||||
|
||||
def add_i():
|
||||
for variable, bound in bounds[PREFIX_SW_I].items():
|
||||
site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin = sw_i_s2vals(
|
||||
variable)
|
||||
# ex: SLICEL:LUT6.O2->AFF.D
|
||||
k = (
|
||||
'%s:%s.%s->%s.%s' %
|
||||
(site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin))
|
||||
sitej.setdefault(site_type, {})[k] = bound
|
||||
|
||||
add_ei()
|
||||
add_eo()
|
||||
add_i()
|
||||
print('Sites: added %u sites' % len(sitej))
|
||||
print('Added site wires')
|
||||
print(' Site external in: %u' % len(bounds[PREFIX_SW_EI]))
|
||||
print(' Site external out: %u' % len(bounds[PREFIX_SW_EO]))
|
||||
print(' Site internal: %u' % len(bounds[PREFIX_SW_I]))
|
||||
|
||||
|
||||
def sep_bounds(bounds):
|
||||
pw = {}
|
||||
sites = {PREFIX_SW_EI: {}, PREFIX_SW_EO: {}, PREFIX_SW_I: {}}
|
||||
for k, v in bounds.items():
|
||||
prefix = k.split(':')[0]
|
||||
if prefix in (PREFIX_W, PREFIX_P):
|
||||
pw[k] = v
|
||||
elif prefix in (PREFIX_SW_EI, PREFIX_SW_EO, PREFIX_SW_I):
|
||||
sites[prefix][k] = v
|
||||
else:
|
||||
assert 0, 'Unknown delay: %s %s' % (k, prefix)
|
||||
return pw, sites
|
||||
|
||||
|
||||
def run(fns_in, fnout, tile_json_fn, verbose=False):
|
||||
# modified in place
|
||||
tilej = json.load(open(tile_json_fn, 'r'))
|
||||
|
||||
for fnin in fns_in:
|
||||
Ads, bs = loadc_Ads_bs([fnin])
|
||||
bounds = Ads2bounds(Ads, bs)
|
||||
bounds_pw, bounds_sites = sep_bounds(bounds)
|
||||
print(len(bounds), len(bounds_pw), len(bounds_sites))
|
||||
|
||||
add_pip_wire(tilej, bounds_pw)
|
||||
add_sites(tilej, bounds_sites)
|
||||
|
||||
timfuz.tilej_stats(tilej)
|
||||
|
||||
json.dump(
|
||||
tilej,
|
||||
open(fnout, 'w'),
|
||||
sort_keys=True,
|
||||
indent=4,
|
||||
separators=(',', ': '))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Substitute timgrid timing model names for real timing values')
|
||||
parser.add_argument(
|
||||
'--timgrid-s',
|
||||
default='../../timgrid/build/timgrid-s.json',
|
||||
help='tilegrid timing delay symbolic input (timgrid-s.json)')
|
||||
parser.add_argument(
|
||||
'--out',
|
||||
default='build/timgrid-vc.json',
|
||||
help='tilegrid timing delay values at corner (timgrid-vc.json)')
|
||||
parser.add_argument(
|
||||
'fn_ins', nargs='+', help='Input flattened timing csv (flat.csv)')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(args.fn_ins, args.out, args.timgrid_s, verbose=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,844 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
from collections import OrderedDict
|
||||
import time
|
||||
import re
|
||||
import os
|
||||
import datetime
|
||||
import json
|
||||
import copy
|
||||
import sys
|
||||
import random
|
||||
import glob
|
||||
from fractions import Fraction
|
||||
import collections
|
||||
|
||||
from benchmark import Benchmark
|
||||
|
||||
# prefix to make easier to track
|
||||
# models do not overlap between PIPs and WIREs
|
||||
PREFIX_W = 'WIRE'
|
||||
PREFIX_P = 'PIP'
|
||||
# extneral site wire (ie site a to b)
|
||||
PREFIX_SW_EI = 'SITEW-EI'
|
||||
PREFIX_SW_EO = 'SITEW-EO'
|
||||
# internal site wire (ie bel a to b within a site)
|
||||
PREFIX_SW_I = 'SITEW-I'
|
||||
|
||||
|
||||
def sw_ei_vals2s(site_type, src_site_pin, dst_bel, dst_bel_pin):
|
||||
'''Pack site wire components into a variable string'''
|
||||
return '%s:%s:%s:%s:%s' % (
|
||||
PREFIX_SW_EI, site_type, src_site_pin, dst_bel, dst_bel_pin)
|
||||
|
||||
|
||||
def sw_ei_s2vals(s):
|
||||
prefix, site_type, src_site_pin, dst_bel, dst_bel_pin = s.split(':')
|
||||
assert prefix == PREFIX_SW_EI
|
||||
return site_type, src_site_pin, dst_bel, dst_bel_pin
|
||||
|
||||
|
||||
def sw_eo_vals2s(site_type, src_bel, src_bel_pin, dst_site_pin):
|
||||
return '%s:%s:%s:%s:%s' % (
|
||||
PREFIX_SW_EO, site_type, src_bel, src_bel_pin, dst_site_pin)
|
||||
|
||||
|
||||
def sw_eo_s2vals(s):
|
||||
prefix, site_type, src_bel, src_bel_pin, dst_site_pin = s.split(':')
|
||||
assert prefix == PREFIX_SW_EO
|
||||
return site_type, src_bel, src_bel_pin, dst_site_pin
|
||||
|
||||
|
||||
def sw_i_vals2s(site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin):
|
||||
return '%s:%s:%s:%s:%s:%s' % (
|
||||
PREFIX_SW_I, site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin)
|
||||
|
||||
|
||||
def sw_i_s2vals(s):
|
||||
prefix, site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin = s.split(
|
||||
':')
|
||||
assert prefix == PREFIX_SW_I
|
||||
return site_type, src_bel, src_bel_pin, dst_bel, dst_bel_pin
|
||||
|
||||
|
||||
# Equations are filtered out until nothing is left
|
||||
class SimplifiedToZero(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# http://code.activestate.com/recipes/576694/
|
||||
class OrderedSet(collections.MutableSet):
|
||||
def __init__(self, iterable=None):
|
||||
self.end = end = []
|
||||
end += [None, end, end] # sentinel node for doubly linked list
|
||||
self.map = {} # key --> [key, prev, next]
|
||||
if iterable is not None:
|
||||
self |= iterable
|
||||
|
||||
def __len__(self):
|
||||
return len(self.map)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.map
|
||||
|
||||
def add(self, key):
|
||||
if key not in self.map:
|
||||
end = self.end
|
||||
curr = end[1]
|
||||
curr[2] = end[1] = self.map[key] = [key, curr, end]
|
||||
|
||||
def discard(self, key):
|
||||
if key in self.map:
|
||||
key, prev, next = self.map.pop(key)
|
||||
prev[2] = next
|
||||
next[1] = prev
|
||||
|
||||
def __iter__(self):
|
||||
end = self.end
|
||||
curr = end[2]
|
||||
while curr is not end:
|
||||
yield curr[0]
|
||||
curr = curr[2]
|
||||
|
||||
def __reversed__(self):
|
||||
end = self.end
|
||||
curr = end[1]
|
||||
while curr is not end:
|
||||
yield curr[0]
|
||||
curr = curr[1]
|
||||
|
||||
def pop(self, last=True):
|
||||
if not self:
|
||||
raise KeyError('set is empty')
|
||||
key = self.end[1][0] if last else self.end[2][0]
|
||||
self.discard(key)
|
||||
return key
|
||||
|
||||
def __repr__(self):
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__, )
|
||||
return '%s(%r)' % (self.__class__.__name__, list(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, OrderedSet):
|
||||
return len(self) == len(other) and list(self) == list(other)
|
||||
return set(self) == set(other)
|
||||
|
||||
|
||||
NAME_ZERO = OrderedSet(
|
||||
[
|
||||
"BSW_CLK_ZERO",
|
||||
"BSW_ZERO",
|
||||
"B_ZERO",
|
||||
"C_CLK_ZERO",
|
||||
"C_DSP_ZERO",
|
||||
"C_ZERO",
|
||||
"I_ZERO",
|
||||
"O_ZERO",
|
||||
"RC_ZERO",
|
||||
"R_ZERO",
|
||||
])
|
||||
|
||||
# csv index
|
||||
corner_s2i = OrderedDict(
|
||||
[
|
||||
('fast_max', 0),
|
||||
('fast_min', 1),
|
||||
('slow_max', 2),
|
||||
('slow_min', 3),
|
||||
])
|
||||
|
||||
|
||||
def allow_zero_eqns():
|
||||
'''If true, allow a system of equations with no equations'''
|
||||
return os.getenv('ALLOW_ZERO_EQN', 'N') == 'Y'
|
||||
|
||||
|
||||
def print_eqns(A_ubd, b_ub, verbose=0, lim=3, label=''):
|
||||
rows = len(b_ub)
|
||||
|
||||
print('Sample equations (%s) from %d r' % (label, rows))
|
||||
prints = 0
|
||||
#verbose = 1
|
||||
for rowi, row in enumerate(A_ubd):
|
||||
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and
|
||||
(not lim or prints < lim)):
|
||||
line = ' EQN: p%u: ' % rowi
|
||||
for k, v in sorted(row.items()):
|
||||
line += '%u*t%d ' % (v, k)
|
||||
line += '= %d' % b_ub[rowi]
|
||||
print(line)
|
||||
prints += 1
|
||||
|
||||
|
||||
def print_name_eqns(A_ubd, b_ub, names, verbose=0, lim=3, label=''):
|
||||
rows = len(b_ub)
|
||||
|
||||
print('Sample equations (%s) from %d r' % (label, rows))
|
||||
prints = 0
|
||||
#verbose = 1
|
||||
for rowi, row in enumerate(A_ubd):
|
||||
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and
|
||||
(not lim or prints < lim)):
|
||||
line = ' EQN: p%u: ' % rowi
|
||||
for k, v in sorted(row.items()):
|
||||
line += '%u*%s ' % (v, names[k])
|
||||
line += '= %d' % b_ub[rowi]
|
||||
print(line)
|
||||
prints += 1
|
||||
|
||||
|
||||
def print_names(names, verbose=1):
|
||||
print('Names: %d' % len(names))
|
||||
for xi, name in enumerate(names):
|
||||
print(' % 4u % -80s' % (xi, name))
|
||||
|
||||
|
||||
def invb(b_ub):
|
||||
#return [-b for b in b_ub]
|
||||
return -np.array(b_ub)
|
||||
|
||||
|
||||
def check_feasible_d(A_ubd, b_ub, names):
|
||||
A_ub, b_ub_inv = Ab_d2np(A_ubd, b_ub, names)
|
||||
check_feasible(A_ub, b_ub_inv)
|
||||
|
||||
|
||||
def check_feasible(A_ub, b_ub):
|
||||
sys.stdout.write('Check feasible ')
|
||||
sys.stdout.flush()
|
||||
|
||||
rows = len(b_ub)
|
||||
cols = len(A_ub[0])
|
||||
|
||||
progress = max(1, rows / 100)
|
||||
|
||||
# Chose a high arbitrary value for x
|
||||
# Delays should be in order of ns, so a 10 ns delay should be way above what anything should be
|
||||
xs = [10e3 for _i in range(cols)]
|
||||
|
||||
# FIXME: use the correct np function to do this for me
|
||||
# Verify bounds
|
||||
#b_res = np.matmul(A_ub, xs)
|
||||
#print(type(A_ub), type(xs)
|
||||
#A_ub = np.array(A_ub)
|
||||
#xs = np.array(xs)
|
||||
#b_res = np.matmul(A_ub, xs)
|
||||
def my_mul(A_ub, xs):
|
||||
#print('cols', cols
|
||||
#print('rows', rows
|
||||
ret = [None] * rows
|
||||
for row in range(rows):
|
||||
this = 0
|
||||
for col in range(cols):
|
||||
this += A_ub[row][col] * xs[col]
|
||||
ret[row] = this
|
||||
return ret
|
||||
|
||||
b_res = my_mul(A_ub, xs)
|
||||
|
||||
# Verify bound was respected
|
||||
for rowi, (this_b, this_b_ub) in enumerate(zip(b_res, b_ub)):
|
||||
if rowi % progress == 0:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
if this_b >= this_b_ub or this_b > 0:
|
||||
print(
|
||||
'% 4d Want res % 10.1f <= % 10.1f <= 0' %
|
||||
(rowi, this_b, this_b_ub))
|
||||
raise Exception("Bad ")
|
||||
print(' done')
|
||||
|
||||
|
||||
def Ab_ub_dt2d(eqns):
|
||||
'''Convert dict using the rows as keys into a list of dicts + b_ub list (ie return A_ub, b_ub)'''
|
||||
#return [dict(rowt) for rowt in eqns]
|
||||
rows = [(OrderedDict(rowt), b) for rowt, b in eqns.items()]
|
||||
A_ubd, b_ub = zip(*rows)
|
||||
return list(A_ubd), list(b_ub)
|
||||
|
||||
|
||||
# This significantly reduces runtime
|
||||
def simplify_rows(Ads, b_ub, remove_zd=False, corner=None):
|
||||
'''Remove duplicate equations, taking highest delay'''
|
||||
# dict of constants to highest delay
|
||||
eqns = OrderedDict()
|
||||
assert len(Ads) == len(b_ub), (len(Ads), len(b_ub))
|
||||
|
||||
assert corner is not None
|
||||
minmax = {
|
||||
'fast_max': max,
|
||||
'fast_min': min,
|
||||
'slow_max': max,
|
||||
'slow_min': min,
|
||||
}[corner]
|
||||
# An outlier to make unknown values be ignored
|
||||
T_UNK = {
|
||||
'fast_max': 0,
|
||||
'fast_min': 10e9,
|
||||
'slow_max': 0,
|
||||
'slow_min': 10e9,
|
||||
}[corner]
|
||||
|
||||
sys.stdout.write('SimpR ')
|
||||
sys.stdout.flush()
|
||||
progress = int(max(1, len(b_ub) / 100))
|
||||
# Equations with a total delay of zero
|
||||
zero_ds = 0
|
||||
# Equations with zero elements
|
||||
# These should have zero delay
|
||||
zero_es = 0
|
||||
for loopi, (b, rowd) in enumerate(zip(b_ub, Ads)):
|
||||
if loopi % progress == 0:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
|
||||
if remove_zd and not b:
|
||||
zero_ds += 1
|
||||
continue
|
||||
|
||||
# think ran into these before when taking out ZERO elements
|
||||
if len(rowd) == 0:
|
||||
if b != 0:
|
||||
assert zero_es == 0, 'Unexpected zero element row with non-zero delay'
|
||||
zero_es += 1
|
||||
continue
|
||||
'''
|
||||
Reduce any constants to canonical form
|
||||
this simplifies later steps that optimize based on assuming there aren't duplicate constants
|
||||
|
||||
Ex:
|
||||
a = 10
|
||||
2 a = 30
|
||||
max corner will get simplified to
|
||||
a = 15
|
||||
|
||||
Otherwise these are not identical equations and would not get simplied
|
||||
Don't try handling the general case of non-trivial equations
|
||||
'''
|
||||
if len(rowd) == 1:
|
||||
k, v = list(rowd.items())[0]
|
||||
if v != 1:
|
||||
rowd = {k: 1}
|
||||
b = 1.0 * b / v
|
||||
|
||||
rowt = Ar_ds2t(rowd)
|
||||
eqns[rowt] = minmax(eqns.get(rowt, T_UNK), b)
|
||||
|
||||
print(' done')
|
||||
|
||||
print(
|
||||
'Simplify rows: %d => %d rows w/ rm zd %d, rm ze %d' %
|
||||
(len(b_ub), len(eqns), zero_ds, zero_es))
|
||||
if len(eqns) == 0:
|
||||
raise SimplifiedToZero()
|
||||
A_ubd_ret, b_ub_ret = Ab_ub_dt2d(eqns)
|
||||
#print_eqns(A_ubd_ret, b_ub_ret, verbose=True, label='Debug')
|
||||
return A_ubd_ret, b_ub_ret
|
||||
|
||||
|
||||
def A_ubr_np2d(row):
|
||||
'''Convert a single row'''
|
||||
#d = {}
|
||||
d = OrderedDict()
|
||||
for coli, val in enumerate(row):
|
||||
if val:
|
||||
d[coli] = val
|
||||
return d
|
||||
|
||||
|
||||
def A_ub_np2d(A_ub):
|
||||
'''Convert A_ub entries in numpy matrix to dictionary / sparse form'''
|
||||
Adi = [None] * len(A_ub)
|
||||
for i, row in enumerate(A_ub):
|
||||
Adi[i] = A_ubr_np2d(row)
|
||||
return Adi
|
||||
|
||||
|
||||
def Ar_di2np(row_di, cols):
|
||||
rownp = np.zeros(cols)
|
||||
for coli, val in row_di.items():
|
||||
# Sign inversion due to way solver works
|
||||
rownp[coli] = val
|
||||
return rownp
|
||||
|
||||
|
||||
# NOTE: sign inversion
|
||||
def A_di2np(Adi, cols):
|
||||
'''Convert A_ub entries in dictionary / sparse to numpy matrix form'''
|
||||
return [Ar_di2np(row_di, cols) for row_di in Adi]
|
||||
|
||||
|
||||
def Ar_ds2t(rowd):
|
||||
'''Convert a dictionary row into a tuple with (column number, value) tuples'''
|
||||
return tuple(sorted(rowd.items()))
|
||||
|
||||
|
||||
def A_ubr_t2d(rowt):
|
||||
'''Convert a dictionary row into a tuple with (column number, value) tuples'''
|
||||
return OrderedDict(rowt)
|
||||
|
||||
|
||||
def A_ub_d2t(A_ubd):
|
||||
'''Convert rows as dicts to rows as tuples'''
|
||||
return [Ar_ds2t(rowd) for rowd in A_ubd]
|
||||
|
||||
|
||||
def A_ub_t2d(A_ubd):
|
||||
'''Convert rows as tuples to rows as dicts'''
|
||||
return [OrderedDict(rowt) for rowt in A_ubd]
|
||||
|
||||
|
||||
def Ab_d2np(A_ubd, b_ub, names):
|
||||
A_ub = A_di2np(A_ubd, len(names))
|
||||
b_ub_inv = invb(b_ub)
|
||||
return A_ub, b_ub_inv
|
||||
|
||||
|
||||
def Ab_np2d(A_ub, b_ub_inv):
|
||||
A_ubd = A_ub_np2d(A_ub)
|
||||
b_ub = invb(b_ub_inv)
|
||||
return A_ubd, b_ub
|
||||
|
||||
|
||||
def sort_equations(Ads, b):
|
||||
# Track rows with value column
|
||||
# Hmm can't sort against np arrays
|
||||
tosort = [(sorted(row.items()), rowb) for row, rowb in zip(Ads, b)]
|
||||
#res = sorted(tosort, key=lambda e: e[0])
|
||||
res = sorted(tosort)
|
||||
A_ubtr, b_ubr = zip(*res)
|
||||
return [OrderedDict(rowt) for rowt in A_ubtr], b_ubr
|
||||
|
||||
|
||||
def col_dist(Ads, desc='of', names=[], lim=0):
|
||||
'''print(frequency distribution of number of elements in a given row'''
|
||||
rows = len(Ads)
|
||||
cols = len(names)
|
||||
|
||||
fs = {}
|
||||
for row in Ads:
|
||||
this_cols = len(row)
|
||||
fs[this_cols] = fs.get(this_cols, 0) + 1
|
||||
|
||||
print(
|
||||
'Col count distribution (%s) for %dr x %dc w/ %d freqs' %
|
||||
(desc, rows, cols, len(fs)))
|
||||
prints = 0
|
||||
for i, (k, v) in enumerate(sorted(fs.items())):
|
||||
if lim == 0 or (lim and prints < lim or i == len(fs) - 1):
|
||||
print(' %d: %d' % (k, v))
|
||||
prints += 1
|
||||
if lim and prints == lim:
|
||||
print(' ...')
|
||||
|
||||
|
||||
def name_dist(A_ubd, desc='of', names=[], lim=0):
|
||||
'''print(frequency distribution of number of times an element appears'''
|
||||
rows = len(A_ubd)
|
||||
cols = len(names)
|
||||
|
||||
fs = {i: 0 for i in range(len(names))}
|
||||
for row in A_ubd:
|
||||
for k in row.keys():
|
||||
fs[k] += 1
|
||||
|
||||
print('Name count distribution (%s) for %dr x %dc' % (desc, rows, cols))
|
||||
prints = 0
|
||||
for namei, name in enumerate(names):
|
||||
if lim == 0 or (lim and prints < lim or namei == len(fs) - 1):
|
||||
print(' %s: %d' % (name, fs[namei]))
|
||||
prints += 1
|
||||
if lim and prints == lim:
|
||||
print(' ...')
|
||||
|
||||
fs2 = {}
|
||||
for v in fs.values():
|
||||
fs2[v] = fs2.get(v, 0) + 1
|
||||
prints = 0
|
||||
print('Distribution distribution (%d items)' % len(fs2))
|
||||
for i, (k, v) in enumerate(sorted(fs2.items())):
|
||||
if lim == 0 or (lim and prints < lim or i == len(fs2) - 1):
|
||||
print(' %s: %s' % (k, v))
|
||||
prints += 1
|
||||
if lim and prints == lim:
|
||||
print(' ...')
|
||||
|
||||
zeros = fs2.get(0, 0)
|
||||
if zeros:
|
||||
raise Exception("%d names without equation" % zeros)
|
||||
|
||||
|
||||
def filter_ncols(A_ubd, b_ub, cols_min=0, cols_max=0):
|
||||
'''Only keep equations with a few delay elements'''
|
||||
A_ubd_ret = []
|
||||
b_ub_ret = []
|
||||
|
||||
#print('Removing large rows')
|
||||
for rowd, b in zip(A_ubd, b_ub):
|
||||
if (not cols_min or len(rowd) >= cols_min) and (not cols_max or
|
||||
len(rowd) <= cols_max):
|
||||
A_ubd_ret.append(rowd)
|
||||
b_ub_ret.append(b)
|
||||
|
||||
print(
|
||||
'Filter ncols w/ %d <= cols <= %d: %d ==> %d rows' %
|
||||
(cols_min, cols_max, len(b_ub), len(b_ub_ret)))
|
||||
assert len(b_ub_ret)
|
||||
return A_ubd_ret, b_ub_ret
|
||||
|
||||
|
||||
def Ar_di2ds(rowA, names):
|
||||
row = OrderedDict()
|
||||
for k, v in rowA.items():
|
||||
row[names[k]] = v
|
||||
return row
|
||||
|
||||
|
||||
def A_di2ds(Adi, names):
|
||||
rows = []
|
||||
for row_di in Adi:
|
||||
rows.append(Ar_di2ds(row_di, names))
|
||||
return rows
|
||||
|
||||
|
||||
def Ar_ds2di(row_ds, names):
|
||||
def keyi(name):
|
||||
if name not in names:
|
||||
names[name] = len(names)
|
||||
return names[name]
|
||||
|
||||
row_di = OrderedDict()
|
||||
for k, v in row_ds.items():
|
||||
row_di[keyi(k)] = v
|
||||
return row_di
|
||||
|
||||
|
||||
def A_ds2di(rows):
|
||||
names = OrderedDict()
|
||||
|
||||
A_ubd = []
|
||||
for row_ds in rows:
|
||||
A_ubd.append(Ar_ds2di(row_ds, names))
|
||||
|
||||
return list(names.keys()), A_ubd
|
||||
|
||||
|
||||
def A_ds2np(Ads):
|
||||
names, Adi = A_ds2di(Ads)
|
||||
return names, A_di2np(Adi, len(names))
|
||||
|
||||
|
||||
def loadc_Ads_mkb(fns, mkb, filt):
|
||||
bs = []
|
||||
Ads = []
|
||||
for fn in fns:
|
||||
with open(fn, 'r') as f:
|
||||
# skip header
|
||||
f.readline()
|
||||
for l in f:
|
||||
cols = l.split(',')
|
||||
ico = bool(int(cols[0]))
|
||||
corners = cols[1]
|
||||
vars = cols[2:]
|
||||
|
||||
def mkcorner(bstr):
|
||||
if bstr == 'None':
|
||||
return None
|
||||
else:
|
||||
return int(bstr)
|
||||
|
||||
corners = [mkcorner(corner) for corner in corners.split()]
|
||||
|
||||
def mkvar(x):
|
||||
i, var = x.split()
|
||||
return (var, int(i))
|
||||
|
||||
vars = OrderedDict([mkvar(var) for var in vars])
|
||||
if not filt(ico, corners, vars):
|
||||
continue
|
||||
|
||||
bs.append(mkb(corners))
|
||||
Ads.append(vars)
|
||||
|
||||
return Ads, bs
|
||||
|
||||
|
||||
def loadc_Ads_b(fns, corner, ico=None):
|
||||
corner = corner or "slow_max"
|
||||
corneri = corner_s2i[corner]
|
||||
'''
|
||||
if ico is not None:
|
||||
filt = lambda ico_, corners, vars: ico_ == ico
|
||||
else:
|
||||
filt = lambda ico_, corners, vars: True
|
||||
'''
|
||||
assert ico is None, 'ICO filtering moved to higher levels'
|
||||
filt = lambda ico_, corners, vars: True
|
||||
|
||||
def mkb(val):
|
||||
return val[corneri]
|
||||
|
||||
return loadc_Ads_mkb(fns, mkb, filt)
|
||||
|
||||
|
||||
def loadc_Ads_bs(fns, ico=None):
|
||||
'''
|
||||
if ico is not None:
|
||||
filt = lambda ico_, corners, vars: ico_ == ico
|
||||
else:
|
||||
filt = lambda ico_, corners, vars: True
|
||||
'''
|
||||
assert ico is None, 'ICO filtering moved to higher levels'
|
||||
filt = lambda ico_, corners, vars: True
|
||||
|
||||
def mkb(val):
|
||||
return val
|
||||
|
||||
return loadc_Ads_mkb(fns, mkb, filt)
|
||||
|
||||
|
||||
def loadc_Ads_raw(fns):
|
||||
filt = lambda ico, corners, vars: True
|
||||
|
||||
def mkb(val):
|
||||
return val
|
||||
|
||||
return loadc_Ads_mkb(fns, mkb, filt)
|
||||
|
||||
|
||||
def index_names(Ads):
|
||||
names = OrderedSet()
|
||||
for row_ds in Ads:
|
||||
for k1 in row_ds.keys():
|
||||
names.add(k1)
|
||||
return names
|
||||
|
||||
|
||||
def load_sub(fn):
|
||||
j = json.load(open(fn, 'r'))
|
||||
|
||||
for name, vals in sorted(j['subs'].items()):
|
||||
for k, v in vals.items():
|
||||
vals[k] = Fraction(v[0], v[1])
|
||||
|
||||
return j
|
||||
|
||||
|
||||
def row_sub_vars(row, sub_json, strict=False, verbose=False):
|
||||
if 0 and verbose:
|
||||
print("")
|
||||
print(row.items())
|
||||
|
||||
delvars = 0
|
||||
for k in sub_json['zero_names']:
|
||||
try:
|
||||
del row[k]
|
||||
delvars += 1
|
||||
except KeyError:
|
||||
pass
|
||||
if verbose:
|
||||
print("Deleted %u variables" % delvars)
|
||||
|
||||
if verbose:
|
||||
print('Checking pivots')
|
||||
print(sorted(row.items()))
|
||||
for group, pivot in sorted(sub_json['pivots'].items()):
|
||||
if pivot not in row:
|
||||
continue
|
||||
n = row[pivot]
|
||||
print(' pivot %u %s' % (n, pivot))
|
||||
|
||||
#pivots = set(sub_json['pivots'].values()).intersection(row.keys())
|
||||
for group, pivot in sorted(sub_json['pivots'].items()):
|
||||
if pivot not in row:
|
||||
continue
|
||||
# take the sub out n times
|
||||
# note constants may be negative
|
||||
n = row[pivot]
|
||||
if verbose:
|
||||
print('pivot %i %s' % (n, pivot))
|
||||
for subk, subv in sorted(sub_json['subs'][group].items()):
|
||||
oldn = row.get(subk, type(subv)(0))
|
||||
rown = -n * subv
|
||||
rown += oldn
|
||||
if verbose:
|
||||
print(" %s: %d => %d" % (subk, oldn, rown))
|
||||
if rown == 0:
|
||||
# only becomes zero if didn't previously exist
|
||||
del row[subk]
|
||||
if verbose:
|
||||
print(" del")
|
||||
else:
|
||||
row[subk] = rown
|
||||
row[group] = n
|
||||
assert pivot not in row
|
||||
|
||||
# after all constants are applied, the row should end up positive?
|
||||
# numeric precision issues previously limited this
|
||||
# Ex: AssertionError: ('PIP_BSW_2ELSING0', -2.220446049250313e-16)
|
||||
if strict:
|
||||
# verify no subs are left
|
||||
for subs in sub_json['subs'].values():
|
||||
for sub in subs:
|
||||
assert sub not in row, 'non-pivot element after group sub %s' % sub
|
||||
|
||||
# Verify all constants are positive
|
||||
for k, v in sorted(row.items()):
|
||||
assert v > 0, (k, v)
|
||||
|
||||
|
||||
def run_sub_json(Ads, sub_json, strict=False, verbose=False):
|
||||
'''
|
||||
strict: complain if a sub doesn't go in evenly
|
||||
'''
|
||||
|
||||
nrows = 0
|
||||
nsubs = 0
|
||||
|
||||
ncols_old = 0
|
||||
ncols_new = 0
|
||||
|
||||
print('Subbing %u rows' % len(Ads))
|
||||
prints = OrderedSet()
|
||||
|
||||
for rowi, row in enumerate(Ads):
|
||||
if 0 and verbose:
|
||||
print(row)
|
||||
if verbose:
|
||||
print('')
|
||||
print('Row %u w/ %u elements' % (rowi, len(row)))
|
||||
|
||||
row_orig = dict(row)
|
||||
row_sub_vars(row, sub_json, strict=strict, verbose=verbose)
|
||||
nrows += 1
|
||||
if row_orig != row:
|
||||
nsubs += 1
|
||||
if verbose:
|
||||
rowt = Ar_ds2t(row)
|
||||
if rowt not in prints:
|
||||
print('row', row)
|
||||
prints.add(rowt)
|
||||
ncols_old += len(row_orig)
|
||||
ncols_new += len(row)
|
||||
|
||||
if verbose:
|
||||
print('')
|
||||
|
||||
print("Sub: %u / %u rows changed" % (nsubs, nrows))
|
||||
print("Sub: %u => %u non-zero row cols" % (ncols_old, ncols_new))
|
||||
|
||||
|
||||
def print_eqns(Ads, b, verbose=0, lim=3, label=''):
|
||||
rows = len(b)
|
||||
|
||||
print('Sample equations (%s) from %d r' % (label, rows))
|
||||
prints = 0
|
||||
for rowi, row in enumerate(Ads):
|
||||
if verbose or ((rowi < 10 or rowi % max(1, (rows / 20)) == 0) and
|
||||
(not lim or prints < lim)):
|
||||
line = ' EQN: p%u: ' % rowi
|
||||
for k, v in sorted(row.items()):
|
||||
line += '%u*t%s ' % (v, k)
|
||||
line += '= %d' % b[rowi]
|
||||
print(line)
|
||||
prints += 1
|
||||
|
||||
|
||||
def print_eqns_np(A_ub, b_ub, verbose=0):
|
||||
Adi = A_ub_np2d(A_ub)
|
||||
print_eqns(Adi, b_ub, verbose=verbose)
|
||||
|
||||
|
||||
def Ads2bounds(Ads, bs):
|
||||
ret = {}
|
||||
for row_ds, row_bs in zip(Ads, bs):
|
||||
assert len(row_ds) == 1
|
||||
k, v = list(row_ds.items())[0]
|
||||
assert v == 1
|
||||
ret[k] = row_bs
|
||||
return ret
|
||||
|
||||
|
||||
def instances(Ads):
|
||||
ret = 0
|
||||
for row_ds in Ads:
|
||||
ret += sum(row_ds.values())
|
||||
return ret
|
||||
|
||||
|
||||
def acorner2csv(b, corneri):
|
||||
corners = ["None" for _ in range(4)]
|
||||
corners[corneri] = str(b)
|
||||
return ' '.join(corners)
|
||||
|
||||
|
||||
def corners2csv(bs):
|
||||
assert len(bs) == 4
|
||||
corners = ["None" if b is None else str(b) for b in bs]
|
||||
return ' '.join(corners)
|
||||
|
||||
|
||||
def tilej_stats(tilej):
|
||||
def tile_stats():
|
||||
stats = {}
|
||||
for etype in ('pips', 'wires'):
|
||||
tm = stats.setdefault(etype, {})
|
||||
tm['net'] = 0
|
||||
tm['solved'] = [0, 0, 0, 0]
|
||||
tm['covered'] = [0, 0, 0, 0]
|
||||
|
||||
for tile in tilej['tiles'].values():
|
||||
for etype in ('pips', 'wires'):
|
||||
pips = tile[etype]
|
||||
for k, v in pips.items():
|
||||
stats[etype]['net'] += 1
|
||||
for i in range(4):
|
||||
if pips[k][i]:
|
||||
stats[etype]['solved'][i] += 1
|
||||
if pips[k][i] is not None:
|
||||
stats[etype]['covered'][i] += 1
|
||||
return stats
|
||||
|
||||
def site_stats():
|
||||
sitesj = tilej['sites']
|
||||
ret = {
|
||||
'sites': len(sitesj),
|
||||
'wires': sum([len(wiresj) for wiresj in sitesj.values()]),
|
||||
'wires_solved': {},
|
||||
}
|
||||
for corneri in corner_s2i.values():
|
||||
ret['wires_solved'][corneri] = sum(
|
||||
[
|
||||
sum(
|
||||
[
|
||||
1 if site_wire[corneri] else 0
|
||||
for site_wire in sitej.values()
|
||||
])
|
||||
for sitej in sitesj.values()
|
||||
])
|
||||
return ret
|
||||
|
||||
tstats = tile_stats()
|
||||
sstats = site_stats()
|
||||
|
||||
for corner, corneri in corner_s2i.items():
|
||||
print('Corner %s' % corner)
|
||||
for etype in ('pips', 'wires'):
|
||||
net = tstats[etype]['net']
|
||||
solved = tstats[etype]['solved'][corneri]
|
||||
covered = tstats[etype]['covered'][corneri]
|
||||
print(
|
||||
' %s: %u / %u solved, %u / %u covered' %
|
||||
(etype, solved, net, covered, net))
|
||||
print(
|
||||
' sites: %u sites w/ %u solved / %u covered site wires' % (
|
||||
sstats['sites'], sstats['wires_solved'][corneri],
|
||||
sstats['wires']))
|
||||
|
||||
|
||||
def load_bounds(bounds_csv, corner):
|
||||
Ads, b = loadc_Ads_b([bounds_csv], corner)
|
||||
return Ads2bounds(Ads, b)
|
||||
|
|
@ -1,322 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import simplify_rows, print_eqns, print_eqns_np, sort_equations, col_dist, index_names
|
||||
import numpy as np
|
||||
import math
|
||||
import sys
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
import copy
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
def lte_const(row_ref, row_cmp):
|
||||
'''Return true if all constants are smaller magnitude in row_cmp than row_ref'''
|
||||
#return False
|
||||
for k, vc in row_cmp.items():
|
||||
vr = row_ref.get(k, None)
|
||||
# Not in reference?
|
||||
if vr is None:
|
||||
return False
|
||||
if vr < vc:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def sub_rows(row_ref, row_cmp):
|
||||
'''Do row_ref - row_cmp'''
|
||||
#ret = {}
|
||||
ret = OrderedDict()
|
||||
ks = set(row_ref.keys())
|
||||
ks.update(set(row_cmp.keys()))
|
||||
for k in ks:
|
||||
vr = row_ref.get(k, 0)
|
||||
vc = row_cmp.get(k, 0)
|
||||
res = vr - vc
|
||||
if res:
|
||||
ret[k] = res
|
||||
return ret
|
||||
|
||||
|
||||
def derive_eq_by_row(Ads, b, verbose=0, col_lim=0, cmp_heuristic=True):
|
||||
'''
|
||||
Derive equations by subtracting whole rows
|
||||
|
||||
cmp_heuristic: drop large generated rows since these are unlikely to constrain the solution
|
||||
|
||||
|
||||
Given equations like:
|
||||
t0 >= 10
|
||||
t0 + t1 >= 15
|
||||
t0 + t1 + t2 >= 17
|
||||
|
||||
When I look at these, I think of a solution something like:
|
||||
t0 = 10f
|
||||
t1 = 5
|
||||
t2 = 2
|
||||
|
||||
However, linprog tends to choose solutions like:
|
||||
t0 = 17
|
||||
t1 = 0
|
||||
t2 = 0
|
||||
|
||||
To this end, add additional constraints by finding equations that are subsets of other equations
|
||||
How to do this in a reasonable time span?
|
||||
Also equations are sparse, which makes this harder to compute
|
||||
'''
|
||||
assert len(Ads) == len(b), 'Ads, b length mismatch'
|
||||
rows = len(Ads)
|
||||
|
||||
# Index equations into hash maps so can lookup sparse elements quicker
|
||||
assert len(Ads) == len(b)
|
||||
Ads_ret = copy.copy(Ads)
|
||||
assert len(Ads) == len(Ads_ret)
|
||||
|
||||
#print('Finding subsets')
|
||||
ltes = 0
|
||||
b_ret = list(b)
|
||||
sys.stdout.write('Deriving rows (%u) ' % rows)
|
||||
sys.stdout.flush()
|
||||
progress = int(max(1, rows / 100))
|
||||
b_warns = 0
|
||||
for row_refi, row_ref in enumerate(Ads):
|
||||
if row_refi % progress == 0:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
if col_lim and len(row_ref) > col_lim:
|
||||
continue
|
||||
|
||||
for row_cmpi, row_cmp in enumerate(Ads):
|
||||
if row_refi == row_cmpi or col_lim and len(row_cmp) > col_lim:
|
||||
continue
|
||||
if not lte_const(row_ref, row_cmp):
|
||||
continue
|
||||
ltes += 1
|
||||
if verbose:
|
||||
print('')
|
||||
print('match')
|
||||
print(' ', row_ref, b[row_refi])
|
||||
print(' ', row_cmp, b[row_cmpi])
|
||||
|
||||
# Reduce
|
||||
row_new = sub_rows(row_ref, row_cmp)
|
||||
|
||||
# Did this actually significantly reduce the search space?
|
||||
# should be a relatively small number of rows and be significantly smaller than at least one of them
|
||||
def is_smaller_row():
|
||||
# Keep any generally small rows
|
||||
if len(row_new) <= 8:
|
||||
return True
|
||||
# And anything that reduced at least one row by half
|
||||
# Ex: going from 120 and 100 element rows to a 20 element row
|
||||
return len(row_new) <= len(row_cmp) / 2 or len(
|
||||
row_new) <= len(row_ref) / 2
|
||||
|
||||
if cmp_heuristic and not is_smaller_row():
|
||||
continue
|
||||
b_new = b[row_refi] - b[row_cmpi]
|
||||
# Definitely possible
|
||||
# Maybe filter these out if they occur?
|
||||
if verbose:
|
||||
print(b_new)
|
||||
# Also inverted sign
|
||||
if b_new < 0:
|
||||
if verbose:
|
||||
print("Unexpected b")
|
||||
b_warns += 1
|
||||
continue
|
||||
if verbose:
|
||||
print('OK')
|
||||
Ads_ret.append(row_new)
|
||||
b_ret.append(b_new)
|
||||
print(' done')
|
||||
|
||||
#A_ub_ret = A_di2np(Ads2, cols=cols)
|
||||
print(
|
||||
'Derive row: %d => %d rows using %d lte' % (len(b), len(b_ret), ltes))
|
||||
print('Dropped %u invalid equations' % b_warns)
|
||||
assert len(Ads_ret) == len(b_ret)
|
||||
return Ads_ret, b_ret
|
||||
|
||||
|
||||
def derive_eq_by_col(Ads, b_ub, verbose=0, keep_orig=True):
|
||||
'''
|
||||
Derive equations by subtracting out all bounded constants (ie "known" columns)
|
||||
XXX: is this now redundant with derive_row?
|
||||
Seems like a degenerate case, although maybe quicker
|
||||
'''
|
||||
rows = len(Ads)
|
||||
|
||||
# Index equations with a single constraint
|
||||
knowns = {}
|
||||
sys.stdout.write('Derive col indexing ')
|
||||
sys.stdout.flush()
|
||||
progress = max(1, rows / 100)
|
||||
for row_refi, row_refd in enumerate(Ads):
|
||||
if row_refi % progress == 0:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
if len(row_refd) == 1:
|
||||
k, v = list(row_refd.items())[0]
|
||||
# assume simplify_equations handles de-duplicating
|
||||
new_b = 1.0 * b_ub[row_refi] / v
|
||||
assert k not in knowns, "Got new %s w/ val %u, old val %u" % (
|
||||
k, new_b, knowns[k])
|
||||
knowns[k] = new_b
|
||||
print(' done')
|
||||
#knowns_set = set(knowns.keys())
|
||||
print('%d constrained' % len(knowns))
|
||||
'''
|
||||
Now see what we can do
|
||||
Rows that are already constrained: eliminate
|
||||
TODO: maybe keep these if this would violate their constraint
|
||||
Otherwise eliminate the original row and generate a simplified result now
|
||||
'''
|
||||
b_ret = []
|
||||
Ads_ret = []
|
||||
sys.stdout.write('Derive col main ')
|
||||
sys.stdout.flush()
|
||||
progress = max(1, rows / 100)
|
||||
for row_refi, row_refd in enumerate(Ads):
|
||||
if row_refi % progress == 0:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
b_ref = b_ub[row_refi]
|
||||
if keep_orig:
|
||||
Ads_ret.append(row_refd)
|
||||
b_ret.append(b_ref)
|
||||
|
||||
# Reduce as much as possible
|
||||
#row_new = {}
|
||||
row_new = OrderedDict()
|
||||
b_new = b_ref
|
||||
# Copy over single entries
|
||||
if len(row_refd) == 1:
|
||||
row_new = row_refd
|
||||
else:
|
||||
for k, v in row_refd.items():
|
||||
if k in knowns:
|
||||
# Remove column and take out corresponding delay
|
||||
b_new -= v * knowns[k]
|
||||
# Copy over
|
||||
else:
|
||||
row_new[k] = v
|
||||
|
||||
# Possibly reduced all usable contants out
|
||||
if len(row_new) == 0:
|
||||
continue
|
||||
# invalid?
|
||||
if b_new < 0:
|
||||
continue
|
||||
|
||||
if not keep_orig or row_new != row_refd:
|
||||
Ads_ret.append(row_new)
|
||||
b_ret.append(b_new)
|
||||
print(' done')
|
||||
|
||||
print('Derive col: %d => %d rows' % (len(b_ub), len(b_ret)))
|
||||
return Ads_ret, b_ret
|
||||
|
||||
|
||||
# iteratively increasing column limit until all columns are added
|
||||
def massage_equations(
|
||||
Ads, b, verbose=False, corner=None, iter_lim=1, col_lim=100000):
|
||||
#col_lim = 15
|
||||
'''
|
||||
Subtract equations from each other to generate additional constraints
|
||||
Helps provide additional guidance to solver for realistic delays
|
||||
|
||||
Ex: given:
|
||||
a >= 10
|
||||
a + b >= 100
|
||||
A valid solution is:
|
||||
a = 100
|
||||
However, a better solution is something like
|
||||
a = 10
|
||||
b = 90
|
||||
This creates derived constraints to provide more realistic results
|
||||
|
||||
Equation pipeline
|
||||
Some operations may generate new equations
|
||||
Simplify after these to avoid unnecessary overhead on redundant constraints
|
||||
Similarly some operations may eliminate equations, potentially eliminating a column (ie variable)
|
||||
Remove these columns as necessary to speed up solving
|
||||
'''
|
||||
|
||||
assert len(Ads) == len(b), 'Ads, b length mismatch'
|
||||
|
||||
def check_cols():
|
||||
assert len(index_names(Ads)) == cols
|
||||
|
||||
def debug(what):
|
||||
check_cols()
|
||||
if 1 or verbose:
|
||||
print('')
|
||||
print_eqns(Ads, b, verbose=verbose, label=what, lim=20)
|
||||
col_dist(Ads, what)
|
||||
#check_feasible_d(Ads, b)
|
||||
|
||||
# Try to (intelligently) subtract equations to generate additional constraints
|
||||
# This helps avoid putting all delay in a single shared variable
|
||||
dstart = len(b)
|
||||
cols = len(index_names(Ads))
|
||||
|
||||
# Each iteration one more column is allowed until all columns are included
|
||||
# (and the system is stable)
|
||||
di = 0
|
||||
while True:
|
||||
print
|
||||
n_orig = len(b)
|
||||
|
||||
print('Loop %d, lim %d' % (di + 1, col_lim))
|
||||
# Meat of the operation
|
||||
Ads, b = derive_eq_by_row(Ads, b, col_lim=col_lim, cmp_heuristic=True)
|
||||
debug("der_rows")
|
||||
# Run another simplify pass since new equations may have overlap with original
|
||||
Ads, b = simplify_rows(Ads, b, corner=corner)
|
||||
print('Derive row: %d => %d equations' % (n_orig, len(b)))
|
||||
debug("der_rows simp")
|
||||
|
||||
# derive_cols is mostly degenerate case of derive_rows
|
||||
# however, it will sub out single variables a lot faster if there are a lot of them
|
||||
# linear vs above quadratic, might as well keep it in
|
||||
if 1:
|
||||
n_orig2 = len(b)
|
||||
# Meat of the operation
|
||||
Ads, b = derive_eq_by_col(Ads, b)
|
||||
debug("der_cols")
|
||||
# Run another simplify pass since new equations may have overlap with original
|
||||
Ads, b = simplify_rows(Ads, b, corner=corner)
|
||||
print(
|
||||
'Derive col %d: %d => %d equations' %
|
||||
(di + 1, n_orig2, len(b)))
|
||||
debug("der_cols simp")
|
||||
|
||||
# Doesn't help computation, but helps debugging
|
||||
Ads, b = sort_equations(Ads, b)
|
||||
debug("loop done")
|
||||
col_dist(Ads, 'derive done iter %d, lim %d' % (di, col_lim), lim=12)
|
||||
|
||||
rows = len(Ads)
|
||||
di += 1
|
||||
dend = len(b)
|
||||
# possible that a new equation was generated and taken away, but close enough
|
||||
if n_orig == len(b) and col_lim >= cols or di >= iter_lim:
|
||||
break
|
||||
col_lim += col_lim / 5
|
||||
|
||||
print('')
|
||||
print('Derive net: %d => %d' % (dstart, dend))
|
||||
print('')
|
||||
# Was experimentting to see how much the higher order columns really help
|
||||
|
||||
# Helps debug readability
|
||||
Ads, b = sort_equations(Ads, b)
|
||||
debug("final (sorted)")
|
||||
print('')
|
||||
print('Massage final: %d => %d rows' % (dstart, dend))
|
||||
cols_end = len(index_names(Ads))
|
||||
print('Massage final: %d => %d cols' % (cols, cols_end))
|
||||
assert cols_end == cols
|
||||
return Ads, b
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from timfuz import simplify_rows, loadc_Ads_b, index_names, A_ds2np, run_sub_json, print_eqns, Ads2bounds, instances, SimplifiedToZero, allow_zero_eqns, corner_s2i, acorner2csv
|
||||
from timfuz_massage import massage_equations
|
||||
import numpy as np
|
||||
import sys
|
||||
import math
|
||||
|
||||
|
||||
def check_feasible(A_ub, b_ub):
|
||||
'''
|
||||
Put large timing constants into the equations
|
||||
See if that would solve it
|
||||
|
||||
Its having trouble giving me solutions as this gets bigger
|
||||
Make a terrible baseline guess to confirm we aren't doing something bad
|
||||
'''
|
||||
|
||||
sys.stdout.write('Check feasible ')
|
||||
sys.stdout.flush()
|
||||
|
||||
rows = len(b_ub)
|
||||
cols = len(A_ub[0])
|
||||
|
||||
progress = max(1, rows / 100)
|
||||
'''
|
||||
Delays should be in order of ns, so a 10 ns delay should be way above what anything should be
|
||||
Series can have several hundred delay elements
|
||||
Max delay in ballpark
|
||||
'''
|
||||
xs = [1e9 for _i in range(cols)]
|
||||
|
||||
# FIXME: use the correct np function to do this for me
|
||||
# Verify bounds
|
||||
#b_res = np.matmul(A_ub, xs)
|
||||
#print(type(A_ub), type(xs)
|
||||
#A_ub = np.array(A_ub)
|
||||
#xs = np.array(xs)
|
||||
#b_res = np.matmul(A_ub, xs)
|
||||
def my_mul(A_ub, xs):
|
||||
#print('cols', cols
|
||||
#print('rows', rows
|
||||
ret = [None] * rows
|
||||
for row in range(rows):
|
||||
this = 0
|
||||
for col in range(cols):
|
||||
this += A_ub[row][col] * xs[col]
|
||||
ret[row] = this
|
||||
return ret
|
||||
|
||||
b_res = my_mul(A_ub, xs)
|
||||
|
||||
# Verify bound was respected
|
||||
for rowi, (this_b, this_b_ub) in enumerate(zip(b_res, b_ub)):
|
||||
if rowi % progress == 0:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
if this_b >= this_b_ub or this_b > 0:
|
||||
print(
|
||||
'% 4d Want res % 10.1f <= % 10.1f <= 0' %
|
||||
(rowi, this_b, this_b_ub))
|
||||
raise Exception("Bad ")
|
||||
print(' done')
|
||||
|
||||
|
||||
def filter_bounds(Ads, b, bounds, corner):
|
||||
'''
|
||||
Given min variable delays, remove rows that won't constrain solution
|
||||
Ex for max corner:
|
||||
Given bounds:
|
||||
a >= 10
|
||||
b >= 1
|
||||
c >= 0
|
||||
Given equations:
|
||||
a + b >= 10
|
||||
a + c >= 100
|
||||
The first equation is already satisfied
|
||||
However, the second needs either an increase in a or an increase in c '''
|
||||
|
||||
if 'max' in corner:
|
||||
# Keep delays possibly larger than current bound
|
||||
def keep(row_b, est):
|
||||
return row_b > est
|
||||
|
||||
T_UNK = 0
|
||||
elif 'min' in corner:
|
||||
# Keep delays possibly smaller than current bound
|
||||
def keep(row_b, est):
|
||||
return row_b < est
|
||||
|
||||
T_UNK = 1e9
|
||||
else:
|
||||
assert 0
|
||||
|
||||
ret_Ads = []
|
||||
ret_b = []
|
||||
unknowns = set()
|
||||
for row_ds, row_b in zip(Ads, b):
|
||||
# some variables get estimated at 0
|
||||
def getvar(k):
|
||||
#return bounds.get(k, T_UNK)
|
||||
ret = bounds.get(k, None)
|
||||
if ret is not None:
|
||||
return ret
|
||||
unknowns.add(k)
|
||||
return T_UNK
|
||||
|
||||
est = sum([getvar(k) * v for k, v in row_ds.items()])
|
||||
# will this row potentially constrain us more?
|
||||
if keep(row_b, est):
|
||||
ret_Ads.append(row_ds)
|
||||
ret_b.append(row_b)
|
||||
if len(unknowns):
|
||||
print('WARNING: %u encountered undefined bounds' % len(unknowns))
|
||||
return ret_Ads, ret_b
|
||||
|
||||
|
||||
def solve_save(outfn, xvals, names, corner, save_zero=True, verbose=False):
|
||||
# ballpark minimum actual observed delay is around 7 (carry chain)
|
||||
# anything less than one is probably a solver artifact
|
||||
delta = 0.5
|
||||
corneri = corner_s2i[corner]
|
||||
|
||||
roundf = {
|
||||
'fast_max': math.ceil,
|
||||
'fast_min': math.floor,
|
||||
'slow_max': math.ceil,
|
||||
'slow_min': math.floor,
|
||||
}[corner]
|
||||
|
||||
print('Writing results')
|
||||
zeros = 0
|
||||
with open(outfn, 'w') as fout:
|
||||
# write as one variable per line
|
||||
# this natively forms a bound if fed into linprog solver
|
||||
fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n')
|
||||
for xval, name in zip(xvals, names):
|
||||
row_ico = 1
|
||||
|
||||
if xval < delta:
|
||||
if verbose:
|
||||
print('WARNING: near 0 delay on %s: %0.6f' % (name, xval))
|
||||
zeros += 1
|
||||
if not save_zero:
|
||||
continue
|
||||
items = [str(row_ico), acorner2csv(roundf(xval), corneri)]
|
||||
items.append('%u %s' % (1, name))
|
||||
fout.write(','.join(items) + '\n')
|
||||
nonzeros = len(names) - zeros
|
||||
print(
|
||||
'Wrote: %u / %u constrained delays, %u zeros' %
|
||||
(nonzeros, len(names), zeros))
|
||||
# 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(
|
||||
fns_in,
|
||||
corner,
|
||||
run_corner,
|
||||
sub_json=None,
|
||||
bounds_csv=None,
|
||||
dedup=True,
|
||||
massage=False,
|
||||
outfn=None,
|
||||
verbose=False,
|
||||
**kwargs):
|
||||
print('Loading data')
|
||||
Ads, b = loadc_Ads_b(fns_in, corner)
|
||||
|
||||
# Remove duplicate rows
|
||||
# is this necessary?
|
||||
# maybe better to just add them into the matrix directly
|
||||
if dedup:
|
||||
oldn = len(Ads)
|
||||
iold = instances(Ads)
|
||||
Ads, b = simplify_rows(Ads, b, corner=corner)
|
||||
print('Simplify %u => %u rows' % (oldn, len(Ads)))
|
||||
print('Simplify %u => %u instances' % (iold, instances(Ads)))
|
||||
|
||||
if sub_json:
|
||||
print('Sub: %u rows' % len(Ads))
|
||||
iold = instances(Ads)
|
||||
names_old = index_names(Ads)
|
||||
run_sub_json(Ads, sub_json, verbose=verbose)
|
||||
names = index_names(Ads)
|
||||
print("Sub: %u => %u names" % (len(names_old), len(names)))
|
||||
print('Sub: %u => %u instances' % (iold, instances(Ads)))
|
||||
else:
|
||||
names = index_names(Ads)
|
||||
'''
|
||||
Substitution .csv
|
||||
Special .csv containing one variable per line
|
||||
Used primarily for multiple optimization passes, such as different algorithms or additional constraints
|
||||
'''
|
||||
if bounds_csv:
|
||||
Ads2, b2 = loadc_Ads_b([bounds_csv], corner)
|
||||
bounds = Ads2bounds(Ads2, b2)
|
||||
assert len(bounds), 'Failed to load bounds'
|
||||
rows_old = len(Ads)
|
||||
Ads, b = filter_bounds(Ads, b, bounds, corner)
|
||||
print(
|
||||
'Filter bounds: %s => %s + %s rows' %
|
||||
(rows_old, len(Ads), len(Ads2)))
|
||||
Ads = Ads + Ads2
|
||||
b = b + b2
|
||||
assert len(Ads) or allow_zero_eqns()
|
||||
assert len(Ads) == len(b), 'Ads, b length mismatch'
|
||||
|
||||
if verbose:
|
||||
print
|
||||
print_eqns(Ads, b, verbose=verbose)
|
||||
|
||||
#print
|
||||
#col_dist(A_ubd, 'final', names)
|
||||
if massage:
|
||||
try:
|
||||
Ads, b = massage_equations(Ads, b, corner=corner)
|
||||
except SimplifiedToZero:
|
||||
if not allow_zero_eqns():
|
||||
raise
|
||||
print('WARNING: simplified to zero equations')
|
||||
Ads = []
|
||||
b = []
|
||||
|
||||
print('Converting to numpy...')
|
||||
names, Anp = A_ds2np(Ads)
|
||||
run_corner(
|
||||
Anp,
|
||||
np.asarray(b),
|
||||
names,
|
||||
corner,
|
||||
outfn=outfn,
|
||||
verbose=verbose,
|
||||
**kwargs)
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
all: build/timgrid.json
|
||||
|
||||
build/timgrid.txt: generate.tcl
|
||||
mkdir -p build
|
||||
cd build && ${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
|
||||
build/timgrid.json: build/timgrid.txt
|
||||
cd build && python3 ../tile_txt2json.py --speed-json ../../speed/build/speed.json timgrid.txt timgrid-s.json
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/bash -x
|
||||
|
||||
source ${XRAY_GENHEADER}
|
||||
|
||||
${XRAY_VIVADO} -mode batch -source ../generate.tcl
|
||||
|
||||
for x in design*.bit; do
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${x}s -z -y $x
|
||||
done
|
||||
|
||||
for x in design_*.bits; do
|
||||
diff -u design.bits $x | grep '^[-+]bit' > ${x%.bits}.delta
|
||||
done
|
||||
|
||||
python3 ../generate.py design_*.delta > tilegrid.json
|
||||
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
proc build_project {} {
|
||||
if 0 {
|
||||
set grid_min_x -1
|
||||
set grid_max_x -1
|
||||
set grid_min_y -1
|
||||
set grid_max_y -1
|
||||
} {
|
||||
set grid_min_x $::env(XRAY_ROI_GRID_X1)
|
||||
set grid_max_x $::env(XRAY_ROI_GRID_X2)
|
||||
set grid_min_y $::env(XRAY_ROI_GRID_Y1)
|
||||
set grid_max_y $::env(XRAY_ROI_GRID_Y2)
|
||||
}
|
||||
|
||||
create_project -force -part $::env(XRAY_PART) design design
|
||||
|
||||
read_verilog ../top.v
|
||||
synth_design -top top
|
||||
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports di]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports do]
|
||||
set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports stb]
|
||||
|
||||
create_pblock roi
|
||||
add_cells_to_pblock [get_pblocks roi] [get_cells roi]
|
||||
resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
|
||||
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
|
||||
set_param tcl.collectionResultDisplayLimit 0
|
||||
|
||||
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
|
||||
|
||||
set luts [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT]
|
||||
set selected_luts {}
|
||||
set lut_index 0
|
||||
|
||||
# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per column)
|
||||
# Also, if GRID_MIN/MAX is not defined, automatically create it based on used CLBs
|
||||
# See caveat in README on automatic creation
|
||||
foreach lut $luts {
|
||||
set tile [get_tile -of_objects $lut]
|
||||
set grid_x [get_property GRID_POINT_X $tile]
|
||||
set grid_y [get_property GRID_POINT_Y $tile]
|
||||
|
||||
if [expr $grid_min_x < 0 || $grid_x < $grid_min_x] {set grid_min_x $grid_x}
|
||||
if [expr $grid_max_x < 0 || $grid_x > $grid_max_x] {set grid_max_x $grid_x}
|
||||
|
||||
if [expr $grid_min_y < 0 || $grid_y < $grid_min_y] {set grid_min_y $grid_y}
|
||||
if [expr $grid_max_y < 0 || $grid_y > $grid_max_y] {set grid_max_y $grid_y}
|
||||
|
||||
# 50 per column => 50, 100, 150, etc
|
||||
if [regexp "Y(0|[0-9]*[05]0)/" $lut] {
|
||||
set cell [get_cells roi/is[$lut_index].lut]
|
||||
set_property LOC [get_sites -of_objects $lut] $cell
|
||||
set lut_index [expr $lut_index + 1]
|
||||
lappend selected_luts $lut
|
||||
}
|
||||
}
|
||||
|
||||
place_design
|
||||
route_design
|
||||
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
}
|
||||
|
||||
proc write_data {} {
|
||||
if 0 {
|
||||
set grid_min_x -1
|
||||
set grid_max_x -1
|
||||
set grid_min_y -1
|
||||
set grid_max_y -1
|
||||
} {
|
||||
set grid_min_x $::env(XRAY_ROI_GRID_X1)
|
||||
set grid_max_x $::env(XRAY_ROI_GRID_X2)
|
||||
set grid_min_y $::env(XRAY_ROI_GRID_Y1)
|
||||
set grid_max_y $::env(XRAY_ROI_GRID_Y2)
|
||||
}
|
||||
|
||||
# Get all tiles in ROI, ie not just the selected LUTs
|
||||
set tiles [get_tiles -filter "GRID_POINT_X >= $grid_min_x && GRID_POINT_X <= $grid_max_x && GRID_POINT_Y >= $grid_min_y && GRID_POINT_Y <= $grid_max_y"]
|
||||
|
||||
# Write tiles.txt with site metadata
|
||||
set fp [open "timgrid.txt" w]
|
||||
foreach tile $tiles {
|
||||
set type [get_property TYPE $tile]
|
||||
set grid_x [get_property GRID_POINT_X $tile]
|
||||
set grid_y [get_property GRID_POINT_Y $tile]
|
||||
|
||||
set items {}
|
||||
|
||||
set wires [get_wires -of_objects $tile]
|
||||
if [llength $wires] {
|
||||
foreach wire $wires {
|
||||
set name [get_property NAME $wire]
|
||||
set speed_index [get_property SPEED_INDEX $wire]
|
||||
lappend items wire $name $speed_index
|
||||
}
|
||||
}
|
||||
|
||||
set pips [get_pips -of_objects $tile]
|
||||
if [llength $pips] {
|
||||
foreach pip $pips {
|
||||
set name [get_property NAME $pip]
|
||||
set speed_index [get_property SPEED_INDEX $pip]
|
||||
lappend items pip $name $speed_index
|
||||
}
|
||||
}
|
||||
|
||||
puts $fp "$type $tile $grid_x $grid_y $items"
|
||||
}
|
||||
close $fp
|
||||
}
|
||||
|
||||
build_project
|
||||
write_data
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
SI_NONE = 0xFFFF
|
||||
|
||||
|
||||
def load_speed_json(f):
|
||||
j = json.load(f)
|
||||
# Index speed indexes to names
|
||||
speed_i2s = {}
|
||||
for k, v in j['speed_model'].items():
|
||||
i = v['speed_index']
|
||||
if i != SI_NONE:
|
||||
speed_i2s[i] = k
|
||||
return j, speed_i2s
|
||||
|
||||
|
||||
def gen_tiles(fnin, speed_i2s):
|
||||
for l in open(fnin):
|
||||
# lappend items pip $name $speed_index
|
||||
# puts $fp "$type $tile $grid_x $grid_y $items"
|
||||
parts = l.strip().split()
|
||||
tile_type, tile_name, grid_x, grid_y = parts[0:4]
|
||||
grid_x, grid_y = int(grid_x), int(grid_y)
|
||||
tuples = parts[4:]
|
||||
assert len(tuples) % 3 == 0
|
||||
pips = {}
|
||||
wires = {}
|
||||
for i in range(0, len(tuples), 3):
|
||||
ttype, name, speed_index = tuples[i:i + 3]
|
||||
name_local = name.split('/')[1]
|
||||
{
|
||||
'pip': pips,
|
||||
'wire': wires,
|
||||
}[ttype][name_local] = speed_i2s[int(speed_index)]
|
||||
yield (tile_type, tile_name, grid_x, grid_y, pips, wires)
|
||||
|
||||
|
||||
def run(fnin, fnout, speed_json_fn, verbose=False):
|
||||
speedj, speed_i2s = load_speed_json(open(speed_json_fn, 'r'))
|
||||
|
||||
tiles = {}
|
||||
for tile in gen_tiles(fnin, speed_i2s):
|
||||
(tile_type, tile_name, grid_x, grid_y, pips, wires) = tile
|
||||
this_dat = {'pips': pips, 'wires': wires}
|
||||
if tile_type not in tiles:
|
||||
tiles[tile_type] = this_dat
|
||||
else:
|
||||
if tiles[tile_type] != this_dat:
|
||||
print(tile_name, tile_type)
|
||||
print(this_dat)
|
||||
print(tiles[tile_type])
|
||||
assert 0
|
||||
|
||||
j = {'tiles': tiles}
|
||||
json.dump(
|
||||
j, open(fnout, 'w'), sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Solve timing solution')
|
||||
parser.add_argument(
|
||||
'--speed-json',
|
||||
default='../../speed/build/speed.json',
|
||||
help='Provides speed index to name translation')
|
||||
parser.add_argument('fnin', default=None, help='input tcl output .txt')
|
||||
parser.add_argument('fnout', default=None, help='output .json')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(args.fnin, args.fnout, speed_json_fn=args.speed_json, verbose=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
//Need at least one LUT per frame base address we want
|
||||
`define N 100
|
||||
|
||||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 6;
|
||||
localparam integer DOUT_N = `N;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
|
||||
roi roi (
|
||||
.clk(clk),
|
||||
.din(din),
|
||||
.dout(dout)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module roi(input clk, input [5:0] din, output [`N-1:0] dout);
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < `N; i = i+1) begin:is
|
||||
LUT6 #(
|
||||
.INIT(64'h8000_0000_0000_0001 + (i << 16))
|
||||
) lut (
|
||||
.I0(din[0]),
|
||||
.I1(din[1]),
|
||||
.I2(din[2]),
|
||||
.I3(din[3]),
|
||||
.I4(din[4]),
|
||||
.I5(din[5]),
|
||||
.O(dout[i])
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
`define N 100
|
||||
|
||||
module top(input clk, stb, di, output do);
|
||||
localparam integer DIN_N = 8;
|
||||
localparam integer DOUT_N = `N;
|
||||
|
||||
reg [DIN_N-1:0] din;
|
||||
wire [DOUT_N-1:0] dout;
|
||||
|
||||
reg [DIN_N-1:0] din_shr;
|
||||
reg [DOUT_N-1:0] dout_shr;
|
||||
|
||||
always @(posedge clk) begin
|
||||
din_shr <= {din_shr, di};
|
||||
dout_shr <= {dout_shr, din_shr[DIN_N-1]};
|
||||
if (stb) begin
|
||||
din <= din_shr;
|
||||
dout_shr <= dout;
|
||||
end
|
||||
end
|
||||
|
||||
assign do = dout_shr[DOUT_N-1];
|
||||
|
||||
roi roi (
|
||||
.clk(clk),
|
||||
.din(din),
|
||||
.dout(dout)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module roi(input clk, input [7:0] din, output [`N-1:0] dout);
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < `N; i = i+1) begin:is
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
RAMB36E1 #(.INIT_00(256'h0000000000000000000000000000000000000000000000000000000000000000)) ram (
|
||||
.CLKARDCLK(din[0]),
|
||||
.CLKBWRCLK(din[1]),
|
||||
.ENARDEN(din[2]),
|
||||
.ENBWREN(din[3]),
|
||||
.REGCEAREGCE(din[4]),
|
||||
.REGCEB(din[5]),
|
||||
.RSTRAMARSTRAM(din[6]),
|
||||
.RSTRAMB(din[7]),
|
||||
.RSTREGARSTREG(din[0]),
|
||||
.RSTREGB(din[1]),
|
||||
.ADDRARDADDR(din[2]),
|
||||
.ADDRBWRADDR(din[3]),
|
||||
.DIADI(din[4]),
|
||||
.DIBDI(din[5]),
|
||||
.DIPADIP(din[6]),
|
||||
.DIPBDIP(din[7]),
|
||||
.WEA(din[0]),
|
||||
.WEBWE(din[1]),
|
||||
.DOADO(dout[0]),
|
||||
.DOBDO(),
|
||||
.DOPADOP(),
|
||||
.DOPBDOP());
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
import timfuz
|
||||
|
||||
corner_s2i = OrderedDict(
|
||||
[
|
||||
('fast_max', 0),
|
||||
('fast_min', 1),
|
||||
('slow_max', 2),
|
||||
('slow_min', 3),
|
||||
])
|
||||
|
||||
corner2minmax = {
|
||||
'fast_max': max,
|
||||
'fast_min': min,
|
||||
'slow_max': max,
|
||||
'slow_min': min,
|
||||
}
|
||||
|
||||
|
||||
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": {
|
||||
"BRKH_B_TERM_INT": {
|
||||
"pips": {},
|
||||
"wires": {
|
||||
"B_TERM_UTURN_INT_ER1BEG0": [
|
||||
null,
|
||||
null,
|
||||
93,
|
||||
null
|
||||
],
|
||||
'''
|
||||
|
||||
for tilek, tilevi in tileji.items():
|
||||
# No previous data? Copy
|
||||
tilevo = tilejo.get(tilek, None)
|
||||
if tilevo is None:
|
||||
tilejo[tilek] = tilevi
|
||||
# Otherwise combine
|
||||
else:
|
||||
merge_bdict(tilevi['pips'], tilevo['pips'])
|
||||
merge_bdict(tilevi['wires'], tilevo['wires'])
|
||||
|
||||
|
||||
def merge_sites(siteji, sitejo):
|
||||
for k, vi in siteji.items():
|
||||
vo = sitejo.get(k, None)
|
||||
# No previous data? Copy
|
||||
if vo is None:
|
||||
sitejo[k] = vi
|
||||
# Otherwise combine
|
||||
else:
|
||||
merge_bdict(vi, vo)
|
||||
|
||||
|
||||
def build_tilejo(fnins):
|
||||
# merge all inputs into blank output
|
||||
tilejo = {"tiles": {}, "sites": {}}
|
||||
for fnin in fnins:
|
||||
tileji = json.load(open(fnin, 'r'))
|
||||
|
||||
merge_tiles(tileji['tiles'], tilejo['tiles'])
|
||||
merge_sites(tileji['sites'], tilejo['sites'])
|
||||
|
||||
return tilejo
|
||||
|
||||
|
||||
def check_corner_minmax(tilej, verbose=False):
|
||||
# Post processing pass looking for min/max inconsistencies
|
||||
# Especially an issue due to complexities around under-constrained elements
|
||||
# (ex: pivots set to 0 delay)
|
||||
print('Checking for min/max consistency')
|
||||
checks = 0
|
||||
bad = 0
|
||||
|
||||
for tilev in tilej['tiles'].values():
|
||||
|
||||
def process_type(etype):
|
||||
nonlocal checks
|
||||
nonlocal bad
|
||||
|
||||
for pipk, pipv in tilev[etype].items():
|
||||
for corner in ('slow', 'fast'):
|
||||
mini = corner_s2i[corner + '_min']
|
||||
minv = pipv[mini]
|
||||
maxi = corner_s2i[corner + '_max']
|
||||
maxv = pipv[maxi]
|
||||
if minv is not None and maxv is not None:
|
||||
checks += 1
|
||||
if minv > maxv:
|
||||
if verbose:
|
||||
print(
|
||||
'WARNING: element %s %s min/max adjusted on corner %s'
|
||||
% (etype, pipk, corner))
|
||||
bad += 1
|
||||
pipv[mini] = maxv
|
||||
pipv[maxi] = minv
|
||||
|
||||
process_type('pips')
|
||||
process_type('wires')
|
||||
|
||||
print('')
|
||||
print('minmax: %u / %u pairs bad pairs adjusted' % (bad, checks))
|
||||
timfuz.tilej_stats(tilej)
|
||||
|
||||
|
||||
def run(fnins, fnout, verbose=False):
|
||||
tilejo = build_tilejo(fnins)
|
||||
check_corner_minmax(tilejo)
|
||||
# XXX: check fast vs slow?
|
||||
# check_corners_minmax(tilejo)
|
||||
json.dump(
|
||||
tilejo,
|
||||
open(fnout, 'w'),
|
||||
sort_keys=True,
|
||||
indent=4,
|
||||
separators=(',', ': '))
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Combine multiple tile corners into one .json file')
|
||||
parser.add_argument(
|
||||
'--out', required=True, help='Combined timgrid-v.json files')
|
||||
parser.add_argument('fnins', nargs='+', help='Input timgrid-vc.json files')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(args.fnins, args.out, verbose=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
#!/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()
|
||||
|
|
@ -1,201 +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
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
#!/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()
|
||||
Loading…
Reference in New Issue