diff --git a/.gitmodules b/.gitmodules index 62e557c1..3430dba8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "third_party/fasm"] path = third_party/fasm url = https://github.com/SymbiFlow/fasm.git +[submodule "third_party/python-sdf-timing"] + path = third_party/python-sdf-timing + url = https://github.com/SymbiFlow/python-sdf-timing.git diff --git a/Makefile b/Makefile index 601a3863..7dba3517 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,8 @@ env: $(IN_ENV) python -c "import prjxray" # Install fasm from third_party $(IN_ENV) pip install --upgrade -e third_party/fasm + # Install sdfparse form third party + $(IN_ENV) pip install --upgrade -e third_party/python-sdf-timing # Install project dependencies $(IN_ENV) pip install -r requirements.txt # Install project's documentation dependencies @@ -17,6 +19,9 @@ env: # Check fasm library was installed $(IN_ENV) python -c "import fasm" $(IN_ENV) python -c "import fasm.output" + # Check sdfparse lib was installed + $(IN_ENV) python -c "import sdf_timing" + $(IN_ENV) python -c "import sdf_timing.sdfparse" # Check YAML is installed $(IN_ENV) python -c "import yaml" || (echo "Unable to find python-yaml" && exit 1) diff --git a/fuzzers/007-timing/Makefile b/fuzzers/007-timing/Makefile index 20da5da5..1b4f37bf 100644 --- a/fuzzers/007-timing/Makefile +++ b/fuzzers/007-timing/Makefile @@ -3,14 +3,20 @@ PRJ?=oneblinkw PRJN?=1 -all: build/timgrid-v.json +run: all +all: build/timgrid-v.json 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) diff --git a/fuzzers/007-timing/bel/.gitignore b/fuzzers/007-timing/bel/.gitignore new file mode 100644 index 00000000..38b3c213 --- /dev/null +++ b/fuzzers/007-timing/bel/.gitignore @@ -0,0 +1,2 @@ +build +debug*.json diff --git a/fuzzers/007-timing/bel/Makefile b/fuzzers/007-timing/bel/Makefile new file mode 100644 index 00000000..46d32eb8 --- /dev/null +++ b/fuzzers/007-timing/bel/Makefile @@ -0,0 +1,25 @@ +all: pushdb + +clean: + rm -rf build + +build/bel_timings.txt: + bash runme.sh + +build/bel_timings.json: build/bel_timings.txt + python3 tim2json.py --timings=build/bel_timings.txt --json=build/bel_timings.json --properties=build/bel_properties.txt --belpins=build/bel_pins.txt --sitepins=build/tile_pins.txt + +build/sdf: build/bel_timings.json + python ${XRAY_UTILS_DIR}/makesdf.py --json=${PWD}/build/bel_timings.json --sdf=${PWD}/build + +cleandb: + rm -rf ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/timings + +mergesdfs: build/sdf + python ${XRAY_UTILS_DIR}/sdfmerge.py --sdfs build/CLBLL_L.sdf build/CLBLL_R.sdf build/CLBLM_L.sdf build/CLBLM_R.sdf --site SLICEM --out build/slicem.sdf + python ${XRAY_UTILS_DIR}/sdfmerge.py --sdfs build/CLBLL_L.sdf build/CLBLL_R.sdf build/CLBLM_L.sdf build/CLBLM_R.sdf --site SLICEL --out build/slicel.sdf + +pushdb: mergesdfs + mkdir -p ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/timings + cp build/*sdf ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/timings + touch run.ok diff --git a/fuzzers/007-timing/bel/runme.sh b/fuzzers/007-timing/bel/runme.sh new file mode 100644 index 00000000..00f88914 --- /dev/null +++ b/fuzzers/007-timing/bel/runme.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ex + +# Create build dir +export SRC_DIR=$PWD +export BUILD_DIR=build + +mkdir -p $BUILD_DIR +cd $BUILD_DIR + +${XRAY_VIVADO} -mode batch -source $SRC_DIR/runme.tcl +test -z "$(fgrep CRITICAL vivado.log)" && touch run.ok diff --git a/fuzzers/007-timing/bel/runme.tcl b/fuzzers/007-timing/bel/runme.tcl new file mode 100644 index 00000000..8e32f5e9 --- /dev/null +++ b/fuzzers/007-timing/bel/runme.tcl @@ -0,0 +1,138 @@ +source "$::env(XRAY_DIR)/utils/utils.tcl" + +proc create_design {} { + + create_project -force -part $::env(XRAY_PART) design design + read_verilog $::env(SRC_DIR)/top.v + synth_design -top top -flatten_hierarchy none + + set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports di] + set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) 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] +} + +proc place_and_route_design {} { + + place_design + route_design + + write_checkpoint -force design.dcp +} + +proc dump_tile_timings {tile timing_fp config_fp pins_fp tile_pins_fp} { + + set properties [list "DELAY" "FAST_MAX" "FAST_MIN" "SLOW_MAX" "SLOW_MIN"] + set timing_line {} + set config_line {} + set pins_line {} + set tile_pins_line {} + + lappend timing_line [get_property TYPE $tile] + lappend config_line [get_property TYPE $tile] + lappend pins_line [get_property TYPE $tile] + lappend tile_pins_line [get_property TYPE $tile] + + set sites [get_sites -of_objects [get_tiles $tile]] + + lappend tile_pins_line [llength $sites] + lappend timing_line [llength $sites] + lappend config_line [llength $sites] + lappend pins_line [llength $sites] + + foreach site $sites { + set site_type [get_property SITE_TYPE $site] + + lappend tile_pins_line $site_type + lappend pins_line $site_type + lappend timing_line $site_type + lappend config_line $site_type + + # dump site pins + set site_pins [get_site_pins -of_objects [get_sites $site]] + lappend tile_pins_line [llength $site_pins] + + foreach pin $site_pins { + set direction [get_property DIRECTION $pin] + regexp {\/(.*)$} $pin -> pin + lappend tile_pins_line $pin $direction + } + + # dump bel pins, speed_models and configs + set bels [get_bels -of_objects $site] + + lappend pins_line [llength $bels] + lappend timing_line [llength $bels] + lappend config_line [llength $bels] + + foreach bel $bels { + set speed_models [get_speed_models -of_objects $bel] + set bel_type [get_property TYPE $bel] + set bel_configs [list_property $bel CONFIG*] + set bel_pins [get_bel_pins -of_objects [get_bels $bel]] + + lappend pins_line $bel_type + lappend pins_line [llength $bel_pins] + foreach pin $bel_pins { + set direction [get_property DIRECTION $pin] + set is_clock [get_property IS_CLOCK $pin] + regexp {\/.*\/(.*)$} $pin -> pin + lappend pins_line $pin $direction $is_clock + } + + lappend config_line $bel_type + lappend config_line [llength $bel_configs] + foreach config $bel_configs { + set config_vals [get_property $config $bel] + lappend config_line $config + lappend config_line [llength $config_vals] + foreach val $config_vals { + lappend config_line $val + } + } + + lappend timing_line "$bel_type" + lappend timing_line [llength $speed_models] + foreach speed_model $speed_models { + lappend timing_line $speed_model + foreach property $properties { + set value [get_property $property $speed_model] + lappend timing_line "$property:$value" + } + } + } + } + + + puts $tile_pins_fp $tile_pins_line + puts $pins_fp $pins_line + puts $timing_fp $timing_line + puts $config_fp $config_line +} + +proc dump {} { + + set types [get_tile_types] + set timing_fp [open "bel_timings.txt" w] + set property_fp [open "bel_properties.txt" w] + set pins_fp [open "bel_pins.txt" w] + set tile_pins_fp [open "tile_pins.txt" w] + foreach type $types { + set tile [randsample_list 1 [get_tiles -filter "TYPE == $type"]] + dump_tile_timings $tile $timing_fp $property_fp $pins_fp $tile_pins_fp + } + close $pins_fp + close $timing_fp + close $property_fp +} + +proc run {} { + create_design + place_and_route_design + dump + write_bitstream -force design.bit +} + +run diff --git a/fuzzers/007-timing/bel/tim2json.py b/fuzzers/007-timing/bel/tim2json.py new file mode 100644 index 00000000..71c6eef0 --- /dev/null +++ b/fuzzers/007-timing/bel/tim2json.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python3 + +import argparse +import json + + +def check_sequential(speed_model): + # combinational path models do not contain + # the following keywords + timing_keywords = { + 'setup': 'setup', + 'remov': 'removal', + 'hold': 'hold', + 'recov': 'recovery', + 'removal': 'removal', + 'recovery': 'recovery' + } + tmp = speed_model.split('_') + for keyword in timing_keywords: + if keyword in tmp: + # return found keyword and it's map in SDF + return [keyword, timing_keywords[keyword]] + + return None + + +# FF's can be configured to work as FF or latch +def check_ff_latch(speed_model): + + tmp = speed_model.split('_') + if 'ff' in tmp: + return 'ff' + elif 'lat' in tmp: + return 'lat' + else: + return None + + +# some bels have duplicate names e.g bufmrce_bufmrce +# this function cleans them +def clean_bname(bname): + tmp = bname.split('_') + if len(tmp) > 1 and tmp[0] == tmp[1]: + return '_'.join(tmp[1:]) + return bname + + +def pin_in_model(pin, model): + + if len(pin.split('_')) == 1: + # pin name is one word, search it in the model + return pin in model.split('_') + else: + # pin name is multi word, search for a string + return pin in model + + +def remove_pin_from_model(pin, model): + + if len(pin.split('_')) == 1: + # pin name is one word, search it in the model + tmp = model.split('_') + tmp.remove(pin) + return "_".join(tmp) + else: + # pin name is multi word, search for a string + return "_".join( + list(filter(None, + speed_model.replace(pin, '').split('_')))) + + +def read_raw_timings(fin, properties, pins, site_pins): + + timings = dict() + with open(fin, "r") as f: + for line in f: + + raw_data = line.split() + slice = raw_data[0] + + #XXX: debug + if slice.startswith('DSP'): + continue + + sites_count = int(raw_data[1]) + loc = 2 + timings[slice] = dict() + for site in range(0, sites_count): + + site_name = raw_data[loc] + bels_count = int(raw_data[loc + 1]) + print(slice, site_name) + + # read all BELs data within + loc += 2 + for bel in range(0, bels_count): + tmp_data = raw_data[loc:] + btype = (tmp_data[0]).lower() + delay_count = int(tmp_data[1]) + + # get all the delays + delay_loc = 2 + for delay in range(0, delay_count): + speed_model = tmp_data[delay_loc] + delay_btype = clean_bname(btype) + delay_btype_orig = delay_btype + # all the bel names seem to start with "bel_d_" + # let's get rid of it + if speed_model.startswith('bel_d_'): + speed_model = speed_model[6:] + + # keep original speed model string to use as unique dict entry + speed_model_orig = speed_model + + # if more than one BEL type exists in the slice + # location is added at the end of the name + tmp = speed_model.split(':') + speed_model = tmp[0] + + bel_location = site_name + if len(tmp) > 2: + bel_location += "/" + "/".join(tmp[2:]) + + bel_location = bel_location.upper() + + sequential = check_sequential(speed_model) + if sequential is not None: + tmp = speed_model.split('_') + tmp.remove(sequential[0]) + speed_model = '_'.join(tmp) + + bel_input = None + bel_output = None + bel_clock = None + + # strip btype from speed model so we can search for pins + speed_model_clean = speed_model + if speed_model.startswith(delay_btype): + speed_model_clean = speed_model[len(delay_btype):] + + # locate pins + for pin in pins[slice][site_name][delay_btype_orig]: + if pin_in_model(pin.lower(), speed_model_clean): + if pins[slice][site_name][delay_btype_orig][ + pin]['direction'] == 'IN': + bel_input = pin + if pins[slice][site_name][delay_btype_orig][ + pin]['direction'] == 'OUT': + bel_output = pin + if pins[slice][site_name][delay_btype_orig][ + pin]['is_clock']: + bel_clock = pin + speed_model_clean = remove_pin_from_model( + pin.lower(), speed_model_clean) + + # Some speed models describe delays from/to site pins instead of BEL pins + if bel_input is None: + # search site inputs + for pin in site_pins[slice][site_name.lower()]: + if pin_in_model(pin.lower(), + speed_model_clean): + if site_pins[slice][site_name.lower( + )][pin]['direction'] == 'IN': + bel_input = pin + speed_model_clean = remove_pin_from_model( + pin.lower(), speed_model_clean) + + if bel_output is None: + for pin in site_pins[slice][site_name.lower()]: + if pin_in_model(pin.lower(), + speed_model_clean): + if site_pins[slice][site_name.lower( + )][pin]['direction'] == 'OUT': + bel_output = pin + speed_model_clean = remove_pin_from_model( + pin.lower(), speed_model_clean) + + if bel_clock is None: + for pin in site_pins[slice][site_name.lower()]: + if pin_in_model(pin.lower(), + speed_model_clean): + if site_pins[slice][site_name.lower( + )][pin]['is_clock']: + bel_clock = pin + speed_model_clean = remove_pin_from_model( + pin.lower(), speed_model_clean) + + # restore speed model name + speed_model = delay_btype + speed_model_clean + + if sequential is not None: + if bel_output is None and bel_clock is None: + delay_loc += 6 + continue + else: + if bel_input is None or bel_output is None: + delay_loc += 6 + continue + + delay_btype = speed_model + extra_ports = None + + if bel_location not in timings[slice]: + timings[slice][bel_location] = dict() + + if delay_btype not in timings[slice][bel_location]: + timings[slice][bel_location][delay_btype] = dict() + + timings[slice][bel_location][delay_btype][ + speed_model_orig] = dict() + timings[slice][bel_location][delay_btype][ + speed_model_orig]['type'] = btype.upper() + timings[slice][bel_location][delay_btype][ + speed_model_orig]['input'] = bel_input.upper() + if bel_output is not None: + timings[slice][bel_location][delay_btype][ + speed_model_orig]['output'] = bel_output.upper( + ) + if bel_clock is not None: + timings[slice][bel_location][delay_btype][ + speed_model_orig]['clock'] = bel_clock.upper() + timings[slice][bel_location][delay_btype][ + speed_model_orig]['location'] = bel_location.upper( + ) + + #XXX: debug + timings[slice][bel_location][delay_btype][ + speed_model_orig]['model'] = speed_model_orig + if sequential is not None: + timings[slice][bel_location][delay_btype][ + speed_model_orig]['sequential'] = sequential[1] + if extra_ports is not None: + timings[slice][bel_location][delay_btype][ + speed_model_orig]['extra_ports'] = extra_ports + + # each timing entry reports 5 delays + for d in range(0, 5): + (t, v) = tmp_data[d + 1 + delay_loc].split(':') + timings[slice][bel_location][delay_btype][ + speed_model_orig][t] = v + + # 5 delay values + name + delay_loc += 6 + loc += delay_loc + return timings + + +def read_bel_properties(properties_file): + + properties = dict() + with open(properties_file, 'r') as f: + for line in f: + raw_props = line.split() + tile = raw_props[0] + sites_count = int(raw_props[1]) + prop_loc = 2 + properties[tile] = dict() + for site in range(0, sites_count): + site_name = raw_props[prop_loc] + bels_count = int(raw_props[prop_loc + 1]) + prop_loc += 2 + properties[tile][site_name] = dict() + for bel in range(0, bels_count): + bel_name = raw_props[prop_loc] + bel_name = clean_bname(bel_name) + bel_name = bel_name.lower() + bel_properties_count = int(raw_props[prop_loc + 1]) + properties[tile][site_name][bel_name] = dict() + prop_loc += 2 + for prop in range(0, bel_properties_count): + prop_name = raw_props[prop_loc] + # the name always starts with "CONFIG." and ends with ".VALUES" + # let's get rid of that + prop_name = prop_name[7:-7] + prop_values_count = int(raw_props[prop_loc + 1]) + properties[tile][site_name][bel_name][ + prop_name] = raw_props[prop_loc + 2:prop_loc + 2 + + prop_values_count] + prop_loc += 2 + prop_values_count + + return properties + + +def read_bel_pins(pins_file): + + pins = dict() + with open(pins_file, 'r') as f: + for line in f: + raw_pins = line.split() + tile = raw_pins[0] + sites_count = int(raw_pins[1]) + pin_loc = 2 + pins[tile] = dict() + for site in range(0, sites_count): + site_name = raw_pins[pin_loc] + bels_count = int(raw_pins[pin_loc + 1]) + pin_loc += 2 + pins[tile][site_name] = dict() + for bel in range(0, bels_count): + bel_name = raw_pins[pin_loc] + bel_name = clean_bname(bel_name) + bel_name = bel_name.lower() + bel_pins_count = int(raw_pins[pin_loc + 1]) + pins[tile][site_name][bel_name] = dict() + pin_loc += 2 + for pin in range(0, bel_pins_count): + pin_name = raw_pins[pin_loc] + pin_direction = raw_pins[pin_loc + 1] + pin_is_clock = raw_pins[pin_loc + 2] + pins[tile][site_name][bel_name][pin_name] = dict() + pins[tile][site_name][bel_name][pin_name][ + 'direction'] = pin_direction + pins[tile][site_name][bel_name][pin_name][ + 'is_clock'] = int(pin_is_clock) == 1 + pin_loc += 3 + return pins + + +def read_site_pins(pins_file): + + pins = dict() + with open(pins_file, 'r') as f: + for line in f: + raw_pins = line.split() + tile = raw_pins[0] + site_count = int(raw_pins[1]) + pin_loc = 2 + pins[tile] = dict() + for site in range(0, site_count): + site_name = raw_pins[pin_loc] + site_name = site_name.lower() + site_pins_count = int(raw_pins[pin_loc + 1]) + pins[tile][site_name] = dict() + pin_loc += 2 + for pin in range(0, site_pins_count): + pin_name = raw_pins[pin_loc] + pin_direction = raw_pins[pin_loc + 1] + pins[tile][site_name][pin_name] = dict() + pins[tile][site_name][pin_name][ + 'direction'] = pin_direction + # site clock pins are always named 'CLK' + pins[tile][site_name][pin_name][ + 'is_clock'] = pin_name.lower() == 'clk' + pin_loc += 2 + return pins + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--timings', type=str, help='Raw timing input file') + parser.add_argument('--json', type=str, help='json output file') + parser.add_argument( + '--properties', type=str, help='Bel properties input file') + parser.add_argument('--belpins', type=str, help='Bel pins input file') + parser.add_argument('--sitepins', type=str, help='Site pins input file') + parser.add_argument( + '--debug', type=bool, default=False, help='Enable debug json dumps') + args = parser.parse_args() + properties = read_bel_properties(args.properties) + + if args.debug: + with open('debug_prop.json', 'w') as fp: + json.dump(properties, fp, indent=4, sort_keys=True) + + pins = read_bel_pins(args.belpins) + if args.debug: + with open('debug_pins.json', 'w') as fp: + json.dump(pins, fp, indent=4, sort_keys=True) + + site_pins = read_site_pins(args.sitepins) + if args.debug: + with open('debug_site_pins.json', 'w') as fp: + json.dump(site_pins, fp, indent=4, sort_keys=True) + + timings = read_raw_timings(args.timings, properties, pins, site_pins) + with open(args.json, 'w') as fp: + json.dump(timings, fp, indent=4, sort_keys=True) + + +if __name__ == '__main__': + main() diff --git a/fuzzers/007-timing/bel/top.v b/fuzzers/007-timing/bel/top.v new file mode 100644 index 00000000..a234addd --- /dev/null +++ b/fuzzers/007-timing/bel/top.v @@ -0,0 +1,5 @@ +module top(input di, output do); + + assign do = di; + +endmodule diff --git a/fuzzers/Makefile b/fuzzers/Makefile index cd9d0d53..f2e82c29 100644 --- a/fuzzers/Makefile +++ b/fuzzers/Makefile @@ -61,6 +61,9 @@ all:: 005-tilegrid/run.ok touch 005-tilegrid/run.ok endif +ifneq ($(XRAY_DATABASE),kintex7) +$(eval $(call fuzzer,007-timing,005-tilegrid)) +endif $(eval $(call fuzzer,010-clb-lutinit,005-tilegrid)) $(eval $(call fuzzer,011-clb-ffconfig,005-tilegrid)) $(eval $(call fuzzer,012-clb-n5ffmux,005-tilegrid)) diff --git a/third_party/python-sdf-timing b/third_party/python-sdf-timing new file mode 160000 index 00000000..4c9d5414 --- /dev/null +++ b/third_party/python-sdf-timing @@ -0,0 +1 @@ +Subproject commit 4c9d5414ce8977f7f58691ba4941e8775340ef74 diff --git a/utils/makesdf.py b/utils/makesdf.py new file mode 100644 index 00000000..6c3a89ac --- /dev/null +++ b/utils/makesdf.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 + +import json +import argparse + + +def get_elems_count(timings, slice, site, bel_type): + combinational = 0 + sequential = 0 + for delay in timings[slice][site][bel_type]: + if 'sequential' in timings[slice][site][bel_type][delay]: + sequential += 1 + else: + combinational += 1 + return combinational, sequential + + +def produce_sdf(timings, outdir): + + for slice in timings: + sdf = \ +""" +(DELAYFILE + (SDFVERSION \"3.0\") + (TIMESCALE 1ps) +""" + for site in timings[slice]: + for bel_type in timings[slice][site]: + combinational, sequential = get_elems_count( + timings, slice, site, bel_type) + #define CELL + cell= \ +""" + (CELL + (CELLTYPE \"{name}\") + (INSTANCE {location})""".format(name=bel_type.upper(), location=site) + sdf += cell + + #define delay header (if needed) + if combinational > 0: + delay_hdr = \ +""" + (DELAY + (ABSOLUTE""" + sdf += delay_hdr + # add all delays definitions + for delay in sorted(timings[slice][site][bel_type]): + if 'sequential' in timings[slice][site][bel_type][ + delay]: + continue + dly = \ +""" + (IOPATH {input} {output} ({FAST_MIN}::{FAST_MAX})({SLOW_MIN}::{SLOW_MAX}))""".format(**timings[slice][site][bel_type][delay]) + if 'extra_ports' in timings[slice][site][bel_type][ + delay] is not None: + dly += \ +""" #extra ports {}""".format(timings[slice][site][bel_type][delay]['extra_ports']) + + sdf += dly + + # close DELAY definition + enddelay = \ +""" + ) + )""" + sdf += enddelay + + # define TIMINGCHECK header (if needed) + if sequential > 0: + timingcheck_hdr = \ +""" + (TIMINGCHECK""" + sdf += timingcheck_hdr + + for delay in sorted(timings[slice][site][bel_type]): + if 'sequential' not in timings[slice][site][bel_type][ + delay]: + continue + timingcheck = \ +""" + ({prop} {input} (posedge {clock}) ({SLOW_MIN}::{SLOW_MAX}))""".format( + prop=timings[slice][site][bel_type][delay]['sequential'].upper(), + **timings[slice][site][bel_type][delay]) + + if 'extra_ports' in timings[slice][site][bel_type][ + delay] is not None: + timingcheck += \ +""" #extra ports {}""".format(timings[slice][site][bel_type][delay]['extra_ports']) + + sdf += timingcheck + + # close TIMINGCHECK definition + endtimingcheck = \ +""" + )""" + sdf += endtimingcheck + + endcell = \ +""" + )""" + sdf += endcell + # end of SDF + sdf += \ +""" +)""" + + with open(outdir + '/' + slice + '.sdf', "w") as fp: + fp.write(sdf) + + +def main(): + + parser = argparse.ArgumentParser() + parser.add_argument('--json', type=str, help="Input JSON file") + parser.add_argument('--sdf', type=str, help="SDF files output directory") + + args = parser.parse_args() + + with open(args.json, 'r') as fp: + timings = json.load(fp) + + produce_sdf(timings, args.sdf) + + +if __name__ == '__main__': + main() diff --git a/utils/sdfmerge.py b/utils/sdfmerge.py new file mode 100644 index 00000000..1ad6aa5f --- /dev/null +++ b/utils/sdfmerge.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +import argparse +import json +from sdf_timing import sdfparse + + +def merge(timings_list, site): + + merged_timings = dict() + + for timings in timings_list: + divider = '/' + if 'divider' in timings['header']: + divider = timings['header']['divider'] + + for cell in timings['cells']: + for cell_instance in timings['cells'][cell]: + if site in cell_instance.split(divider): + if 'cells' not in merged_timings: + merged_timings['cells'] = dict() + + if cell in merged_timings['cells']: + assert merged_timings['cells'][cell] == \ + timings['cells'][cell], \ + "Attempting to merge differing cells" + + merged_timings['cells'][cell] = timings['cells'][cell] + + return merged_timings + + +def main(): + + parser = argparse.ArgumentParser() + parser.add_argument( + '--sdfs', nargs='+', type=str, help="List of sdf files to merge") + parser.add_argument('--site', type=str, help="Site we want to merge") + parser.add_argument('--json', type=str, help="Debug JSON") + parser.add_argument('--out', type=str, help="Merged sdf name") + + args = parser.parse_args() + + timings_list = list() + + for sdf in args.sdfs: + with open(sdf, 'r') as fp: + timing = sdfparse.parse(fp.read()) + timings_list.append(timing) + + merged_sdf = merge(timings_list, args.site) + open(args.out, 'w').write(sdfparse.emit(merged_sdf)) + + if args.json is not None: + with open(args.json, 'w') as fp: + json.dump(merged_sdf, fp, indent=4, sort_keys=True) + + +if __name__ == '__main__': + main() diff --git a/utils/utils.tcl b/utils/utils.tcl index 9fea66c1..34fd4e21 100644 --- a/utils/utils.tcl +++ b/utils/utils.tcl @@ -96,6 +96,19 @@ proc pblock_tiles {pblock} { return [get_tiles "$clb_tiles $int_tiles"] } +# returns list of unique tile types +proc get_tile_types {} { + set all_tiles [get_tiles] + set types {} + foreach tile $all_tiles { + set type [get_property TYPE $tile] + #ignore empty tiles + if {$type == "NULL"} { continue } + if {[lsearch -exact $types $type] == -1} {lappend types $type} + } + return $types +} + proc lintersect {lst1 lst2} { set rlst {} foreach el $lst1 {