Fuzzers: 007: add bel timing fuzzer

Signed-off-by: Karol Gugala <kgugala@antmicro.com>
This commit is contained in:
Karol Gugala 2019-04-01 21:05:16 +02:00 committed by Tim 'mithro' Ansell
parent 3ccc2df586
commit 5d9da26f78
6 changed files with 553 additions and 0 deletions

2
fuzzers/007-timing/bel/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
debug*.json

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -0,0 +1,5 @@
module top(input di, output do);
assign do = di;
endmodule