From b9f8f962f150ab232805c515be8397bdd6802d08 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 11 Feb 2020 16:52:12 -0800 Subject: [PATCH 1/8] Start of SERDES timing. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/007-timing/bel/runme.tcl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fuzzers/007-timing/bel/runme.tcl b/fuzzers/007-timing/bel/runme.tcl index 8e32f5e9..7ceb4243 100644 --- a/fuzzers/007-timing/bel/runme.tcl +++ b/fuzzers/007-timing/bel/runme.tcl @@ -123,6 +123,17 @@ proc dump {} { set tile [randsample_list 1 [get_tiles -filter "TYPE == $type"]] dump_tile_timings $tile $timing_fp $property_fp $pins_fp $tile_pins_fp } + + set other_site_types [list ISERDESE2 OSERDESE2] + foreach site_type $other_site_types { + set cell [create_cell -reference $site_type test] + place_design + set tile [get_tiles -of [get_sites -of $cell]] + dump_tile_timings $tile $timing_fp $property_fp $pins_fp $tile_pins_fp + unplace_cell $cell + remove_cell $cell + } + close $pins_fp close $timing_fp close $property_fp From e17f9e8140ce6e43f47f033caa7065a1d4b4f3b5 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 11 Feb 2020 20:09:17 -0800 Subject: [PATCH 2/8] Refactor routines to read pins, props, and site pins. Also fix Makefile intermediate. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/007-timing/bel/Makefile | 1 + fuzzers/007-timing/bel/tim2json.py | 290 ++++++++++++++++++----------- 2 files changed, 186 insertions(+), 105 deletions(-) diff --git a/fuzzers/007-timing/bel/Makefile b/fuzzers/007-timing/bel/Makefile index da76f091..e30e7268 100644 --- a/fuzzers/007-timing/bel/Makefile +++ b/fuzzers/007-timing/bel/Makefile @@ -19,6 +19,7 @@ build/fixup_timings: build/run.ok python3 fixup_timings_txt.py --txtin build/bel_pins.txt --txtout build/bel_pins.txt --site RAMBFIFO36E1 --slice BRAM_R --type pins python3 fixup_timings_txt.py --txtin build/bel_properties.txt --txtout build/bel_properties.txt --site RAMBFIFO36E1 --slice BRAM_L --type properties python3 fixup_timings_txt.py --txtin build/bel_properties.txt --txtout build/bel_properties.txt --site RAMBFIFO36E1 --slice BRAM_R --type properties + touch build/fixup_timings build/bel_timings.json: build/fixup_timings python3 tim2json.py --timings=build/bel_timings.txt --json=build/bel_timings.json --properties=build/bel_properties.txt --propertiesmap=properties_map.json --pinaliasmap=pin_alias_map.json --belpins=build/bel_pins.txt --sitepins=build/tile_pins.txt --debug true diff --git a/fuzzers/007-timing/bel/tim2json.py b/fuzzers/007-timing/bel/tim2json.py index 1c012835..b902ad27 100644 --- a/fuzzers/007-timing/bel/tim2json.py +++ b/fuzzers/007-timing/bel/tim2json.py @@ -1,7 +1,12 @@ #!/usr/bin/env python3 +import re import argparse import json +import functools +import progressbar + +NUMBER_RE = re.compile(r'\d+$') def check_sequential(speed_model): @@ -120,7 +125,7 @@ def instance_in_model(instance, model): return instance in model -def pin_in_model(pin, pin_aliases, model, direction=None): +def create_pin_in_model(pin_aliases, model): """ Checks if a given pin belongs to the model. @@ -166,24 +171,29 @@ def pin_in_model(pin, pin_aliases, model, direction=None): # strip site location model = model.split(':')[0] - extended_pin_name = pin - aliased_pin, aliased_pin_name = find_aliased_pin( - pin.upper(), model, pin_aliases) - # some timings reports pins with their directions - # this happens for e.g. CLB reg_init D pin, which - # timing is reported as DIN - if direction is not None: - extended_pin_name = pin + direction + @functools.lru_cache(maxsize=10000) + def pin_in_model(pin, direction=None): + extended_pin_name = pin + aliased_pin, aliased_pin_name = find_aliased_pin( + pin.upper(), model, pin_aliases) - if instance_in_model(pin, model): - return True, pin - elif instance_in_model(extended_pin_name, model): - return True, extended_pin_name - elif aliased_pin: - return True, aliased_pin_name - else: - return False, None + # some timings reports pins with their directions + # this happens for e.g. CLB reg_init D pin, which + # timing is reported as DIN + if direction is not None: + extended_pin_name = pin + direction + + if instance_in_model(pin, model): + return True, pin + elif instance_in_model(extended_pin_name, model): + return True, extended_pin_name + elif aliased_pin: + return True, aliased_pin_name + else: + return False, None + + return pin_in_model def remove_pin_from_model(pin, model): @@ -227,6 +237,43 @@ def remove_pin_from_model(pin, model): return "_".join(list(filter(None, model.replace(pin, '').split('_')))) +def merged_dict(itr): + """ Create a merged dict of dict (of dict) based on input. + + Input is an iteratable of (keys, value). + + Return value is root dictionary + + Keys are successive dictionaries indicies. For example: + (('a', 'b', 'c'), 1) + + would set: + + output['a']['b']['c'] = 1 + + This function returns an error if two values conflict. + + >>> merged_dict(((('a', 'b', 'c'), 1), (('a', 'b', 'd'), 2))) + {'a': {'b': {'c': 1, 'd': 2}}} + + """ + + output = {} + for keys, value in itr: + target = output + for key in keys[:-1]: + if key not in target: + target[key] = {} + target = target[key] + + if keys[-1] in target: + assert target[keys[-1]] == value, (keys, value, target[keys[-1]]) + else: + target[keys[-1]] = value + + return output + + def extract_properties(tile, site, bel, properties, model): if tile not in properties: @@ -526,107 +573,140 @@ def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): def read_bel_properties(properties_file, properties_map): + def inner(): + 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 = 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() + if sites_count == 0: + yield (tile,), {} + + for site in range(0, sites_count): + site_name = raw_props[prop_loc] + bels_count = int(raw_props[prop_loc + 1]) 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] - # append name prop name mappings - if bel_name in properties_map: - if prop_name in properties_map[bel_name]: - prop_name = properties_map[bel_name][prop_name] - 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 + 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]) + + props = 0 + 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 + if prop_name.startswith( + 'CONFIG.') and prop_name.endswith( + '.VALUES'): + prop_name = prop_name[7:-7] + + prop_values_count = int(raw_props[prop_loc + 1]) + + if prop_name not in [ + 'RAM_MODE', + 'WRITE_WIDTH_A', + 'WRITE_WIDTH_B', + 'READ_WIDTH_A', + 'READ_WIDTH_B', + ]: + if bel_name in properties_map: + if prop_name in properties_map[bel_name]: + prop_name = properties_map[bel_name][ + prop_name] + + yield (tile, site_name, bel_name, prop_name), \ + raw_props[prop_loc + 2:prop_loc + 2 + + prop_values_count] + props += 1 + + prop_loc += 2 + prop_values_count + + if props == 0: + yield (tile, site_name, bel_name), {} + + return merged_dict(inner()) def read_bel_pins(pins_file): + def inner(): + 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 = 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() + if sites_count == 0: + yield (tile,), {} + + for site in range(0, sites_count): + site_name = raw_pins[pin_loc] + bels_count = int(raw_pins[pin_loc + 1]) 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 + + 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]) + + 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] + + yield ( + tile, site_name, bel_name, pin_name, + 'direction'), pin_direction + yield ( + tile, site_name, bel_name, pin_name, + 'is_clock'), int(pin_is_clock) == 1 + pin_loc += 3 + + return merged_dict(inner()) def read_site_pins(pins_file): + def inner(): + 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 + + if site_count == 0: + yield (tile,), {} + + 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 = 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 + for pin in range(0, site_pins_count): + pin_name = raw_pins[pin_loc] + pin_direction = raw_pins[pin_loc + 1] + + yield ( + (tile, site_name, pin_name, 'direction'), + pin_direction) + yield ( + (tile, site_name, pin_name, 'is_clock'), + pin_name.lower() == 'clk') + + # site clock pins are always named 'CLK' + pin_loc += 2 + + return merged_dict(inner()) def main(): From 564863ccaddb299ed0f7fbd6799d275d036913bb Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 11 Feb 2020 20:28:52 -0800 Subject: [PATCH 3/8] Refactor remaining function in tim2json.py Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/007-timing/bel/tim2json.py | 489 ++++++++++++++--------------- 1 file changed, 241 insertions(+), 248 deletions(-) diff --git a/fuzzers/007-timing/bel/tim2json.py b/fuzzers/007-timing/bel/tim2json.py index b902ad27..9867089b 100644 --- a/fuzzers/007-timing/bel/tim2json.py +++ b/fuzzers/007-timing/bel/tim2json.py @@ -125,7 +125,7 @@ def instance_in_model(instance, model): return instance in model -def create_pin_in_model(pin_aliases, model): +def create_pin_in_model(pin_aliases): """ Checks if a given pin belongs to the model. @@ -169,11 +169,11 @@ def create_pin_in_model(pin_aliases, model): (True, 'o') """ - # strip site location - model = model.split(':')[0] - @functools.lru_cache(maxsize=10000) - def pin_in_model(pin, direction=None): + def pin_in_model(pin, model, direction=None): + # strip site location + model = model.split(':')[0] + extended_pin_name = pin aliased_pin, aliased_pin_name = find_aliased_pin( pin.upper(), model, pin_aliases) @@ -301,275 +301,268 @@ def extract_properties(tile, site, bel, properties, model): return model_properties -def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): - - timings = dict() +def parse_raw_timing(fin): 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 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): - btype = (raw_data[loc]).lower() + bel = raw_data[loc] delay_count = int(raw_data[loc + 1]) # get all the delays loc += 2 for delay in range(0, delay_count): speed_model = raw_data[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):] - - # remove properties from the model - speed_model_properties = extract_properties( - slice, site_name, delay_btype_orig, properties, - speed_model_clean) - if speed_model_properties is not None: - for prop in speed_model_properties: - # properties values in the model always follow properties name - prop_string = "_".join( - [prop, speed_model_properties[prop]]) - speed_model_clean = remove_pin_from_model( - prop_string.lower(), speed_model_clean) - - # Get pin alias map - pin_aliases = pin_alias_map.get(delay_btype, None) - - # locate pins - for pin in pins[slice][site_name][delay_btype_orig]: - orig_pin = pin - pim, pin = pin_in_model( - pin.lower(), pin_aliases, speed_model_clean, - 'in') - - if pim: - if pins[slice][site_name][delay_btype_orig][ - orig_pin]['is_clock']: - bel_clock = pin - bel_clock_orig_pin = orig_pin - elif pins[slice][site_name][delay_btype_orig][ - orig_pin]['direction'] == 'IN': - bel_input = pin - elif pins[slice][site_name][delay_btype_orig][ - orig_pin]['direction'] == 'OUT': - bel_output = 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_clock is None: - for pin in site_pins[slice][site_name.lower()]: - orig_pin = pin - pim, pin = pin_in_model( - pin.lower(), pin_aliases, - speed_model_clean) - if pim: - if site_pins[slice][site_name.lower( - )][orig_pin]['is_clock']: - bel_clock = pin - bel_clock_orig_pin = orig_pin - speed_model_clean = remove_pin_from_model( - pin.lower(), speed_model_clean) - - if bel_input is None: - # search site inputs - for pin in site_pins[slice][site_name.lower()]: - orig_pin = pin - pim, pin = pin_in_model( - pin.lower(), pin_aliases, - speed_model_clean, 'in') - if pim: - if site_pins[slice][site_name.lower( - )][orig_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()]: - orig_pin = pin - pim, pin = pin_in_model( - pin.lower(), pin_aliases, - speed_model_clean) - if pim: - if site_pins[slice][site_name.lower( - )][orig_pin]['direction'] == 'OUT': - bel_output = pin - speed_model_clean = remove_pin_from_model( - pin.lower(), speed_model_clean) - - # if we couldn't find input, check if the clock is the - # only input. This applies only to combinational paths - if (sequential is None) and (bel_input is None) and ( - bel_clock is not None): - if bel_clock_orig_pin in site_pins[slice][site_name.lower()] and \ - site_pins[slice][site_name.lower( - )][bel_clock_orig_pin]['direction'] == 'IN': - bel_input = bel_clock - - # if we still don't have the input check if the input - # is wider than 1 bit and timing defined for the whole - # port - import re - if (bel_input is None) or (bel_output is None): - for pin in pins[slice][site_name][ - delay_btype_orig]: - number = re.search(r'\d+$', pin) - if number is not None: - orig_pin = pin[:-( - len(str(number.group())))] - orig_pin_full = pin - pim, pin = pin_in_model( - orig_pin.lower(), pin_aliases, - speed_model_clean) - if not pim: - # some inputs pins are named with unsignificant zeros - # remove ti and try again - orig_pin = orig_pin + str( - int(number.group())) - pim, pin = pin_in_model( - orig_pin.lower(), pin_aliases, - speed_model_clean) - - if pim: - if pins[slice][site_name][delay_btype_orig][orig_pin_full]['direction'] == 'IN' \ - and bel_input is None: - bel_input = pin - if pins[slice][site_name][delay_btype_orig][orig_pin_full]['direction'] == 'OUT' \ - and bel_output is None: - bel_output = pin - speed_model_clean = remove_pin_from_model( - orig_pin.lower(), - speed_model_clean) - - # check if the input is not a BEL property - if bel_input is None: - # if there is anything not yet decoded - if len(speed_model_clean.split("_")) > 1: - if len(speed_model_properties.keys()) == 1: - bel_input = list( - speed_model_properties.keys())[0] - - # if we still don't have input, give up - if bel_input is None: - loc += 6 - continue - - # 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 or \ - bel_output is None and bel_clock == bel_input: - loc += 6 - continue - else: - if bel_input is None or bel_output is None: - loc += 6 - continue - - delay_btype = speed_model - # add properties to the delay_btype - for prop in sorted(speed_model_properties): - prop_string = "_".join( - [prop, speed_model_properties[prop]]) - delay_btype += "_" + prop_string - extra_ports = None - - if slice not in timings: - timings[slice] = dict() - - 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) = raw_data[d + 1 + loc].split(':') - timings[slice][bel_location][delay_btype][ - speed_model_orig][t] = v + timing = [ + raw_data[d + 1 + loc].split(':') + for d in range(0, 5) + ] + + yield slice, site_name, bel, speed_model, timing # 5 delay values + name loc += 6 - return timings + + +def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): + def inner(): + raw = list(parse_raw_timing(fin)) + + pin_in_models = {} + + for slice, site_name, bel, speed_model, timing in progressbar.progressbar( + raw): + btype = bel.lower() + 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):] + + # remove properties from the model + speed_model_properties = extract_properties( + slice, site_name, delay_btype_orig, properties, + speed_model_clean) + if speed_model_properties is not None: + for prop in speed_model_properties: + # properties values in the model always follow properties name + prop_string = "_".join( + [prop, speed_model_properties[prop]]) + speed_model_clean = remove_pin_from_model( + prop_string.lower(), speed_model_clean) + + # Get pin alias map + if delay_btype not in pin_in_models: + pin_aliases = pin_alias_map.get(delay_btype, None) + pin_in_models[delay_btype] = create_pin_in_model( + pin_aliases) + + pin_in_model = pin_in_models[delay_btype] + + # locate pins + for pin in pins[slice][site_name][delay_btype_orig]: + orig_pin = pin + pim, pin = pin_in_model(pin.lower(), speed_model_clean, 'in') + + if pim: + if pins[slice][site_name][delay_btype_orig][orig_pin][ + 'is_clock']: + bel_clock = pin + bel_clock_orig_pin = orig_pin + elif pins[slice][site_name][delay_btype_orig][orig_pin][ + 'direction'] == 'IN': + bel_input = pin + elif pins[slice][site_name][delay_btype_orig][orig_pin][ + 'direction'] == 'OUT': + bel_output = 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_clock is None: + for pin in site_pins[slice][site_name.lower()]: + orig_pin = pin + pim, pin = pin_in_model(pin.lower(), speed_model_clean) + if pim: + if site_pins[slice][site_name. + lower()][orig_pin]['is_clock']: + bel_clock = pin + bel_clock_orig_pin = orig_pin + speed_model_clean = remove_pin_from_model( + pin.lower(), speed_model_clean) + + if bel_input is None: + # search site inputs + for pin in site_pins[slice][site_name.lower()]: + orig_pin = pin + pim, pin = pin_in_model(pin.lower(), speed_model_clean, 'in') + if pim: + if site_pins[slice][site_name.lower( + )][orig_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()]: + orig_pin = pin + pim, pin = pin_in_model(pin.lower(), speed_model_clean) + if pim: + if site_pins[slice][site_name.lower( + )][orig_pin]['direction'] == 'OUT': + bel_output = pin + speed_model_clean = remove_pin_from_model( + pin.lower(), speed_model_clean) + + # if we couldn't find input, check if the clock is the + # only input. This applies only to combinational paths + if (sequential is None) and (bel_input is None) and (bel_clock is + not None): + if bel_clock_orig_pin in site_pins[slice][site_name.lower()] and \ + site_pins[slice][site_name.lower( + )][bel_clock_orig_pin]['direction'] == 'IN': + bel_input = bel_clock + + # if we still don't have the input check if the input + # is wider than 1 bit and timing defined for the whole + # port + if (bel_input is None) or (bel_output is None): + for pin in pins[slice][site_name][delay_btype_orig]: + number = NUMBER_RE.search(pin) + if number is not None: + orig_pin = pin[:-(len(str(number.group())))] + orig_pin_full = pin + pim, pin = pin_in_model(orig_pin.lower(), speed_model_clean) + if not pim: + # some inputs pins are named with unsignificant zeros + # remove ti and try again + orig_pin = orig_pin + str(int(number.group())) + pim, pin = pin_in_model(orig_pin.lower(), speed_model_clean) + + if pim: + if pins[slice][site_name][delay_btype_orig][orig_pin_full]['direction'] == 'IN' \ + and bel_input is None: + bel_input = pin + if pins[slice][site_name][delay_btype_orig][orig_pin_full]['direction'] == 'OUT' \ + and bel_output is None: + bel_output = pin + speed_model_clean = remove_pin_from_model( + orig_pin.lower(), speed_model_clean) + + # check if the input is not a BEL property + if bel_input is None: + # if there is anything not yet decoded + if len(speed_model_clean.split("_")) > 1: + if len(speed_model_properties.keys()) == 1: + bel_input = list(speed_model_properties.keys())[0] + + # if we still don't have input, give up + if bel_input is None: + continue + + # restore speed model name + speed_model = delay_btype + speed_model_clean + + if sequential is not None: + if bel_clock is None: + continue + + if bel_output is None and bel_clock is None or \ + bel_output is None and bel_clock == bel_input: + continue + else: + if bel_input is None or bel_output is None: + continue + + delay_btype = speed_model + # add properties to the delay_btype + if speed_model_properties is not None: + for prop in sorted(speed_model_properties): + prop_string = "_".join( + [prop, speed_model_properties[prop]]) + delay_btype += "_" + prop_string + + yield (slice, bel_location, delay_btype, speed_model_orig, + 'type'), btype.upper() + yield ( + slice, bel_location, delay_btype, speed_model_orig, + 'input'), bel_input.upper() + + if bel_output is not None: + yield ( + slice, bel_location, delay_btype, speed_model_orig, + 'output'), bel_output.upper() + + if bel_clock is not None: + yield ( + slice, bel_location, delay_btype, speed_model_orig, + 'clock'), bel_clock.upper() + + yield ( + slice, bel_location, delay_btype, speed_model_orig, + 'location'), bel_location.upper() + + #XXX: debug + yield ( + slice, bel_location, delay_btype, speed_model_orig, + 'model'), speed_model_orig + + if sequential is not None: + assert bel_clock is not None, ( + slice, bel_location, delay_btype, speed_model_orig) + yield ( + slice, bel_location, delay_btype, speed_model_orig, + 'sequential'), sequential[1] + + for t, v in timing: + yield ( + slice, bel_location, delay_btype, speed_model_orig, t), v + + return merged_dict(inner()) def read_bel_properties(properties_file, properties_map): From 5837b757cb3e90359b3de9fd432698b3427a42ce Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 11 Feb 2020 20:38:47 -0800 Subject: [PATCH 4/8] Add sdf parser library to requirements. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 4a9f9c7f..775254c5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -e third_party/fasm +-e third_party/python-sdf-timing intervaltree junit-xml numpy From 0c1a404ab1376d14d0514378ccd460953dcf00b0 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 11 Feb 2020 20:49:09 -0800 Subject: [PATCH 5/8] Run make format. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/007-timing/bel/tim2json.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fuzzers/007-timing/bel/tim2json.py b/fuzzers/007-timing/bel/tim2json.py index 9867089b..889298eb 100644 --- a/fuzzers/007-timing/bel/tim2json.py +++ b/fuzzers/007-timing/bel/tim2json.py @@ -398,8 +398,7 @@ def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): # Get pin alias map if delay_btype not in pin_in_models: pin_aliases = pin_alias_map.get(delay_btype, None) - pin_in_models[delay_btype] = create_pin_in_model( - pin_aliases) + pin_in_models[delay_btype] = create_pin_in_model(pin_aliases) pin_in_model = pin_in_models[delay_btype] @@ -439,7 +438,8 @@ def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): # search site inputs for pin in site_pins[slice][site_name.lower()]: orig_pin = pin - pim, pin = pin_in_model(pin.lower(), speed_model_clean, 'in') + pim, pin = pin_in_model( + pin.lower(), speed_model_clean, 'in') if pim: if site_pins[slice][site_name.lower( )][orig_pin]['direction'] == 'IN': @@ -476,12 +476,14 @@ def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): if number is not None: orig_pin = pin[:-(len(str(number.group())))] orig_pin_full = pin - pim, pin = pin_in_model(orig_pin.lower(), speed_model_clean) + pim, pin = pin_in_model( + orig_pin.lower(), speed_model_clean) if not pim: # some inputs pins are named with unsignificant zeros # remove ti and try again orig_pin = orig_pin + str(int(number.group())) - pim, pin = pin_in_model(orig_pin.lower(), speed_model_clean) + pim, pin = pin_in_model( + orig_pin.lower(), speed_model_clean) if pim: if pins[slice][site_name][delay_btype_orig][orig_pin_full]['direction'] == 'IN' \ @@ -575,7 +577,7 @@ def read_bel_properties(properties_file, properties_map): prop_loc = 2 if sites_count == 0: - yield (tile,), {} + yield (tile, ), {} for site in range(0, sites_count): site_name = raw_props[prop_loc] @@ -637,7 +639,7 @@ def read_bel_pins(pins_file): pin_loc = 2 if sites_count == 0: - yield (tile,), {} + yield (tile, ), {} for site in range(0, sites_count): site_name = raw_pins[pin_loc] @@ -677,7 +679,7 @@ def read_site_pins(pins_file): pin_loc = 2 if site_count == 0: - yield (tile,), {} + yield (tile, ), {} for site in range(0, site_count): site_name = raw_pins[pin_loc] From ec69db772d56d867b03dcc7b8b010cc223886e4d Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 11 Feb 2020 20:50:50 -0800 Subject: [PATCH 6/8] Remove progressbar Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/007-timing/bel/tim2json.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fuzzers/007-timing/bel/tim2json.py b/fuzzers/007-timing/bel/tim2json.py index 889298eb..7c51d023 100644 --- a/fuzzers/007-timing/bel/tim2json.py +++ b/fuzzers/007-timing/bel/tim2json.py @@ -4,7 +4,6 @@ import re import argparse import json import functools -import progressbar NUMBER_RE = re.compile(r'\d+$') @@ -343,8 +342,7 @@ def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): pin_in_models = {} - for slice, site_name, bel, speed_model, timing in progressbar.progressbar( - raw): + for slice, site_name, bel, speed_model, timing in raw: btype = bel.lower() delay_btype = clean_bname(btype) delay_btype_orig = delay_btype From 2f388235e441fd607212c48cbec576742ed42d82 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 11 Feb 2020 20:55:07 -0800 Subject: [PATCH 7/8] Update doctests. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/007-timing/bel/tim2json.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fuzzers/007-timing/bel/tim2json.py b/fuzzers/007-timing/bel/tim2json.py index 7c51d023..3535e9c0 100644 --- a/fuzzers/007-timing/bel/tim2json.py +++ b/fuzzers/007-timing/bel/tim2json.py @@ -149,22 +149,22 @@ def create_pin_in_model(pin_aliases): The second returned value contains found pin name. If the pin is not found, None is returned. - >>> pin_in_model("d", None, "ff_init_din_q", "in") + >>> create_pin_in_model(None)("d", "ff_init_din_q", "in") (True, 'din') - >>> pin_in_model("q", None, "ff_init_clk_q", None) + >>> create_pin_in_model(None)("q", "ff_init_clk_q", None) (True, 'q') - >>> pin_in_model("q", {"Q": {"names" : ["QL", "QH"], "is_property_related" : True}}, "ff_init_clk_ql", None) + >>> create_pin_in_model({"Q": {"names" : ["QL", "QH"], "is_property_related" : True}})("q", "ff_init_clk_ql", None) (True, 'q') - >>> pin_in_model("logic_out", None, "my_cell_i_logic_out", None) + >>> create_pin_in_model(None)("logic_out", "my_cell_i_logic_out", None) (True, 'logic_out') - >>> pin_in_model("logic_out", {"LOGIC_OUT": {"names" : ["LOGIC_O", "O"], "is_property_related" : False}}, "my_cell_i_logic_o", None) + >>> create_pin_in_model({"LOGIC_OUT": {"names" : ["LOGIC_O", "O"], "is_property_related" : False}})("logic_out", "my_cell_i_logic_o", None) (True, 'logic_o') - >>> pin_in_model("logic_out", {"LOGIC_OUT": {"names" : ["LOGIC_O", "O"], "is_property_related" : False}}, "my_cell_i_o", None) + >>> create_pin_in_model({"LOGIC_OUT": {"names" : ["LOGIC_O", "O"], "is_property_related" : False}})("logic_out", "my_cell_i_o", None) (True, 'o') """ From 49b5a8cde69297bb528ef7e6d8345c6b449394e9 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Wed, 12 Feb 2020 12:50:54 -0800 Subject: [PATCH 8/8] Handle weird bel pins that aren't really clocks. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/007-timing/bel/fixup_timings_txt.py | 2 +- fuzzers/007-timing/bel/runme.tcl | 6 ++++-- fuzzers/007-timing/bel/tim2json.py | 21 ++++++++++++++++----- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/fuzzers/007-timing/bel/fixup_timings_txt.py b/fuzzers/007-timing/bel/fixup_timings_txt.py index 71c1a2fa..4f1f570f 100644 --- a/fuzzers/007-timing/bel/fixup_timings_txt.py +++ b/fuzzers/007-timing/bel/fixup_timings_txt.py @@ -56,7 +56,7 @@ def fix_line(line, site, filetype): entries.append(line[loc]) loc += 1 elif filetype == 'pins': - for pin_word in range(0, 3): + for pin_word in range(0, 4): entries.append(line[loc]) loc += 1 elif filetype == 'properties': diff --git a/fuzzers/007-timing/bel/runme.tcl b/fuzzers/007-timing/bel/runme.tcl index 7ceb4243..2d20c3d3 100644 --- a/fuzzers/007-timing/bel/runme.tcl +++ b/fuzzers/007-timing/bel/runme.tcl @@ -56,8 +56,9 @@ proc dump_tile_timings {tile timing_fp config_fp pins_fp tile_pins_fp} { foreach pin $site_pins { set direction [get_property DIRECTION $pin] + set is_part_of_bus [get_property IS_PART_OF_BUS $pin] regexp {\/(.*)$} $pin -> pin - lappend tile_pins_line $pin $direction + lappend tile_pins_line $pin $direction $is_part_of_bus } # dump bel pins, speed_models and configs @@ -78,8 +79,9 @@ proc dump_tile_timings {tile timing_fp config_fp pins_fp tile_pins_fp} { foreach pin $bel_pins { set direction [get_property DIRECTION $pin] set is_clock [get_property IS_CLOCK $pin] + set is_part_of_bus [get_property IS_PART_OF_BUS $pin] regexp {\/.*\/(.*)$} $pin -> pin - lappend pins_line $pin $direction $is_clock + lappend pins_line $pin $direction $is_clock $is_part_of_bus } lappend config_line $bel_type diff --git a/fuzzers/007-timing/bel/tim2json.py b/fuzzers/007-timing/bel/tim2json.py index 3535e9c0..ade19179 100644 --- a/fuzzers/007-timing/bel/tim2json.py +++ b/fuzzers/007-timing/bel/tim2json.py @@ -407,7 +407,8 @@ def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): if pim: if pins[slice][site_name][delay_btype_orig][orig_pin][ - 'is_clock']: + 'is_clock'] and not pins[slice][site_name][ + delay_btype_orig][orig_pin]['is_part_of_bus']: bel_clock = pin bel_clock_orig_pin = orig_pin elif pins[slice][site_name][delay_btype_orig][orig_pin][ @@ -425,8 +426,9 @@ def read_raw_timings(fin, properties, pins, site_pins, pin_alias_map): orig_pin = pin pim, pin = pin_in_model(pin.lower(), speed_model_clean) if pim: - if site_pins[slice][site_name. - lower()][orig_pin]['is_clock']: + if site_pins[slice][site_name.lower( + )][orig_pin]['is_clock'] and not site_pins[slice][ + site_name.lower()][orig_pin]['is_part_of_bus']: bel_clock = pin bel_clock_orig_pin = orig_pin speed_model_clean = remove_pin_from_model( @@ -655,6 +657,7 @@ def read_bel_pins(pins_file): pin_name = raw_pins[pin_loc] pin_direction = raw_pins[pin_loc + 1] pin_is_clock = raw_pins[pin_loc + 2] + pin_is_part_of_bus = raw_pins[pin_loc + 3] yield ( tile, site_name, bel_name, pin_name, @@ -662,7 +665,11 @@ def read_bel_pins(pins_file): yield ( tile, site_name, bel_name, pin_name, 'is_clock'), int(pin_is_clock) == 1 - pin_loc += 3 + yield ( + tile, site_name, bel_name, pin_name, + 'is_part_of_bus' + ), int(pin_is_part_of_bus) == 1 + pin_loc += 4 return merged_dict(inner()) @@ -688,6 +695,7 @@ def read_site_pins(pins_file): for pin in range(0, site_pins_count): pin_name = raw_pins[pin_loc] pin_direction = raw_pins[pin_loc + 1] + pin_is_part_of_bus = raw_pins[pin_loc + 2] yield ( (tile, site_name, pin_name, 'direction'), @@ -695,9 +703,12 @@ def read_site_pins(pins_file): yield ( (tile, site_name, pin_name, 'is_clock'), pin_name.lower() == 'clk') + yield ( + (tile, site_name, pin_name, 'is_part_of_bus'), + int(pin_is_part_of_bus)) # site clock pins are always named 'CLK' - pin_loc += 2 + pin_loc += 3 return merged_dict(inner())