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..e9d7e2b1 --- /dev/null +++ b/fuzzers/007-timing/bel/Makefile @@ -0,0 +1,14 @@ +all: build/sdf + +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 + python3 ${XRAY_UTILS_DIR}/makesdf.py --json=${PWD}/build/bel_timings.json --sdf=${PWD}/build + 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