diff --git a/fuzzers/005-tilegrid/Makefile b/fuzzers/005-tilegrid/Makefile index 46edf091..83556908 100644 --- a/fuzzers/005-tilegrid/Makefile +++ b/fuzzers/005-tilegrid/Makefile @@ -13,6 +13,7 @@ TILEGRID_TDB_DEPENDENCIES += dsp/build/segbits_tilegrid.tdb TILEGRID_TDB_DEPENDENCIES += fifo_int/build/segbits_tilegrid.tdb TILEGRID_TDB_DEPENDENCIES += cfg_int/build/segbits_tilegrid.tdb TILEGRID_TDB_DEPENDENCIES += monitor_int/build/segbits_tilegrid.tdb +TILEGRID_TDB_DEPENDENCIES += clk_hrow/build/segbits_tilegrid.tdb GENERATE_FULL_ARGS= ifeq (${XRAY_DATABASE}, zynq7) @@ -96,6 +97,9 @@ cfg_int/build/segbits_tilegrid.tdb: build/basicdb/tilegrid.json orphan_int_column/build/segbits_tilegrid.tdb: build/basicdb/tilegrid.json cd orphan_int_column && $(MAKE) +clk_hrow/build/segbits_tilegrid.tdb: build/basicdb/tilegrid.json + cd clk_hrow && $(MAKE) + build/tilegrid_tdb.json: add_tdb.py $(TILEGRID_TDB_DEPENDENCIES) python3 add_tdb.py \ --fn-in build/basicdb/tilegrid.json \ @@ -129,6 +133,7 @@ clean: cd monitor_int && $(MAKE) clean cd cfg_int && $(MAKE) clean cd orphan_int_column && $(MAKE) clean + cd clk_hrow && $(MAKE) clean .PHONY: database pushdb clean run diff --git a/fuzzers/005-tilegrid/add_tdb.py b/fuzzers/005-tilegrid/add_tdb.py index a98e6a68..b27d5960 100644 --- a/fuzzers/005-tilegrid/add_tdb.py +++ b/fuzzers/005-tilegrid/add_tdb.py @@ -84,6 +84,7 @@ def run(fn_in, fn_out, verbose=False): ("bram_block/build/segbits_tilegrid.tdb", 128, 10), ("clb/build/segbits_tilegrid.tdb", 36, 2), ("dsp/build/segbits_tilegrid.tdb", 28, 10), + ("clk_hrow/build/segbits_tilegrid.tdb", 30, 7), ("clb_int/build/segbits_tilegrid.tdb", int_frames, int_words), ("iob_int/build/segbits_tilegrid.tdb", int_frames, int_words), ("bram_int/build/segbits_tilegrid.tdb", int_frames, int_words), diff --git a/fuzzers/005-tilegrid/clk_hrow/Makefile b/fuzzers/005-tilegrid/clk_hrow/Makefile new file mode 100644 index 00000000..17297e84 --- /dev/null +++ b/fuzzers/005-tilegrid/clk_hrow/Makefile @@ -0,0 +1,4 @@ +N ?= 5 +GENERATE_ARGS?="--oneval 1 --design params.csv --dword 1 --dframe 1A" +include ../fuzzaddr/common.mk + diff --git a/fuzzers/005-tilegrid/clk_hrow/generate.tcl b/fuzzers/005-tilegrid/clk_hrow/generate.tcl new file mode 100644 index 00000000..49e429ba --- /dev/null +++ b/fuzzers/005-tilegrid/clk_hrow/generate.tcl @@ -0,0 +1,21 @@ +source "$::env(XRAY_DIR)/utils/utils.tcl" + +proc run {} { + create_project -force -part $::env(XRAY_PART) design design + read_verilog top.v + synth_design -top top + + #set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk] + + set_property CFGBVS VCCO [current_design] + set_property CONFIG_VOLTAGE 3.3 [current_design] + set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] + + place_design + route_design + + write_checkpoint -force design.dcp + write_bitstream -force design.bit +} + +run diff --git a/fuzzers/005-tilegrid/clk_hrow/top.py b/fuzzers/005-tilegrid/clk_hrow/top.py new file mode 100644 index 00000000..66b7d67a --- /dev/null +++ b/fuzzers/005-tilegrid/clk_hrow/top.py @@ -0,0 +1,59 @@ +import os +import random +random.seed(int(os.getenv("SEED"), 16)) +from prjxray import util +from prjxray.db import Database + + +def gen_sites(): + db = Database(util.get_db_root()) + grid = db.grid() + for tile_name in grid.tiles(): + loc = grid.loc_of_tilename(tile_name) + gridinfo = grid.gridinfo_at_loc(loc) + sites = [] + for site, site_type in gridinfo.sites.items(): + if site_type == 'BUFHCE': + sites.append(site) + + if sites: + yield tile_name, sorted(sites) + + +def write_params(params): + pinstr = 'tile,val,site\n' + for tile, (site, val) in sorted(params.items()): + pinstr += '%s,%s,%s\n' % (tile, val, site) + open('params.csv', 'w').write(pinstr) + + +def run(): + print(''' +module top(); + ''') + + params = {} + + sites = list(gen_sites()) + for (tile_name, sites), isone in zip(sites, + util.gen_fuzz_states(len(sites))): + site_name = sites[0] + params[tile_name] = (site_name, isone) + + print( + ''' + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFHCE #( + .INIT_OUT({isone}) + ) buf_{site} (); +'''.format( + site=site_name, + isone=isone, + )) + + print("endmodule") + write_params(params) + + +if __name__ == '__main__': + run() diff --git a/fuzzers/040-clk-hrow-config/Makefile b/fuzzers/040-clk-hrow-config/Makefile new file mode 100644 index 00000000..f20a6d84 --- /dev/null +++ b/fuzzers/040-clk-hrow-config/Makefile @@ -0,0 +1,26 @@ +N ?= 15 + +include ../fuzzer.mk + +database: build/segbits_clk_hrow.db + +build/segbits_clk_hrow.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} -o build/segbits_clk_hrow.rdb \ + $(addsuffix /segdata_clk_hrow_top_r.txt,$(SPECIMENS)) \ + $(addsuffix /segdata_clk_hrow_bot_r.txt,$(SPECIMENS)) + +build/segbits_clk_hrow.db: build/segbits_clk_hrow.rdb + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_clk_hrow.rdb \ + --seg-fn-out build/segbits_clk_hrow.db + ${XRAY_MASKMERGE} build/mask_clk_hrow.db \ + $(addsuffix /segdata_clk_hrow_top_r.txt,$(SPECIMENS)) \ + $(addsuffix /segdata_clk_hrow_bot_r.txt,$(SPECIMENS)) + +pushdb: database + ${XRAY_MERGEDB} clk_hrow_bot_r build/segbits_clk_hrow.db + ${XRAY_MERGEDB} clk_hrow_top_r build/segbits_clk_hrow.db + ${XRAY_MERGEDB} mask_clk_hrow_bot_r build/mask_clk_hrow.db + ${XRAY_MERGEDB} mask_clk_hrow_top_r build/mask_clk_hrow.db + +.PHONY: database pushdb diff --git a/fuzzers/040-clk-hrow-config/bits.dbf b/fuzzers/040-clk-hrow-config/bits.dbf new file mode 100644 index 00000000..e69de29b diff --git a/fuzzers/040-clk-hrow-config/generate.py b/fuzzers/040-clk-hrow-config/generate.py new file mode 100644 index 00000000..615f03b0 --- /dev/null +++ b/fuzzers/040-clk-hrow-config/generate.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import json + +from prjxray.segmaker import Segmaker +from prjxray import verilog + + +def main(): + segmk = Segmaker("design.bits") + + print("Loading tags") + with open('params.json') as f: + params = json.load(f) + + for row in params: + base_name = 'BUFHCE_X{}Y{}'.format(row['x'], row['y']) + + segmk.add_site_tag( + row['site'], '{}.IN_USE'.format(base_name), row['IN_USE']) + if not row['IN_USE']: + continue + + segmk.add_site_tag( + row['site'], '{}.INIT_OUT'.format(base_name), row['INIT_OUT']) + + segmk.add_site_tag( + row['site'], '{}.ZINV_CE'.format(base_name), + 1 ^ row['IS_CE_INVERTED']) + + # SYNC is a zero pattern + for opt in ['ASYNC']: + segmk.add_site_tag( + row['site'], '{}.CE_TYPE.'.format(base_name) + opt, + verilog.unquote(row['CE_TYPE']) == opt) + + segmk.compile() + segmk.write() + + +if __name__ == '__main__': + main() diff --git a/fuzzers/040-clk-hrow-config/generate.tcl b/fuzzers/040-clk-hrow-config/generate.tcl new file mode 100644 index 00000000..3044e100 --- /dev/null +++ b/fuzzers/040-clk-hrow-config/generate.tcl @@ -0,0 +1,17 @@ +proc run {} { + create_project -force -part $::env(XRAY_PART) design design + read_verilog top.v + synth_design -top top + + set_property CFGBVS VCCO [current_design] + set_property CONFIG_VOLTAGE 3.3 [current_design] + set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] + + place_design + route_design + + write_checkpoint -force design.dcp + write_bitstream -force design.bit +} + +run diff --git a/fuzzers/040-clk-hrow-config/top.py b/fuzzers/040-clk-hrow-config/top.py new file mode 100644 index 00000000..56a0d54f --- /dev/null +++ b/fuzzers/040-clk-hrow-config/top.py @@ -0,0 +1,73 @@ +import json +import os +import random +random.seed(int(os.getenv("SEED"), 16)) +from prjxray import util +from prjxray import verilog +from prjxray.db import Database + + +def gen_sites(): + get_xy = util.create_xy_fun('BUFHCE_') + db = Database(util.get_db_root()) + grid = db.grid() + for tile_name in sorted(grid.tiles()): + loc = grid.loc_of_tilename(tile_name) + gridinfo = grid.gridinfo_at_loc(loc) + sites = [] + + xs = [] + ys = [] + for site, site_type in gridinfo.sites.items(): + if site_type == 'BUFHCE': + x, y = get_xy(site) + xs.append(x) + ys.append(y) + + sites.append((site, x, y)) + + if sites: + yield tile_name, min(xs), min(ys), sorted(sites) + + +def main(): + print(''' +module top(); + ''') + + params_list = [] + for tile_name, x_min, y_min, sites in gen_sites(): + + for site, x, y in sites: + params = {} + params['tile'] = tile_name + params['site'] = site + params['x'] = x - x_min + params['y'] = y - y_min + params['IN_USE'] = random.randint(0, 1) + + if params['IN_USE']: + params['INIT_OUT'] = random.randint(0, 1) + params['CE_TYPE'] = verilog.quote( + random.choice(('SYNC', 'ASYNC'))) + params['IS_CE_INVERTED'] = random.randint(0, 1) + print( + ''' + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFHCE #( + .INIT_OUT({INIT_OUT}), + .CE_TYPE({CE_TYPE}), + .IS_CE_INVERTED({IS_CE_INVERTED}) + ) buf_{site} (); + '''.format(**params)) + + params_list.append(params) + + print("endmodule") + + with open('params.json', 'w') as f: + json.dump(params_list, f, indent=2) + + +if __name__ == '__main__': + main() diff --git a/fuzzers/041-clk-hrow-pips/Makefile b/fuzzers/041-clk-hrow-pips/Makefile new file mode 100644 index 00000000..1bb8bc01 --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/Makefile @@ -0,0 +1,56 @@ +export FUZDIR=$(shell pwd) +PIP_TYPE?=clk_hrow +PIPLIST_TCL=$(FUZDIR)/clk_hrow_pip_list.tcl +MAKETODO_FLAGS=--no-l --pip-type clk_hrow_bot --seg-type clk_hrow_bot --re "[^\.]+\.CLK_HROW_CK_MUX_OUT_" +N = 50 + +# These PIPs all appear to be either a 0 or 2 bit solution. +SEGMATCH_FLAGS=-m 20 -M 45 -c 2 +SPECIMENS_DEPS=build/cmt_regions.csv +A_PIPLIST=clk_hrow_bot_r.txt + +include ../pip_loop.mk + +database: build/segbits_clk_hrow.db + +build/cmt_regions.csv: output_cmt.tcl + mkdir -p build + cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/output_cmt.tcl + +build/segbits_clk_hrow.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} -o build/segbits_clk_hrow.rdb \ + $(addsuffix /segdata_clk_hrow_top_r.txt,$(SPECIMENS)) \ + $(addsuffix /segdata_clk_hrow_bot_r.txt,$(SPECIMENS)) + +build/segbits_clk_hrow.db: build/segbits_clk_hrow.rdb $(XRAY_FUZZERS_DIR)/piplist/build/clk_hrow_bot_r.txt + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_clk_hrow.rdb \ + --seg-fn-out build/segbits_clk_hrow_rc.db + + # Convert row/column into PIP definition. + python3 merge_clk_entries.py \ + build/segbits_clk_hrow_rc.db \ + $(XRAY_FUZZERS_DIR)/piplist/build/clk_hrow_bot_r.txt \ + build/segbits_clk_hrow.db + + # Keep a copy to track iter progress + cp build/segbits_clk_hrow.rdb build/$(ITER)/segbits_clk_hrow.rdb + cp build/segbits_clk_hrow_rc.db build/$(ITER)/segbits_clk_hrow_rc.db + + + ${XRAY_MASKMERGE} build/mask_clk_hrow.db \ + $(addsuffix /segdata_clk_hrow_top_r.txt,$(SPECIMENS)) \ + $(addsuffix /segdata_clk_hrow_bot_r.txt,$(SPECIMENS)) + + # Clobber existing .db to eliminate potential conflicts + cp ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/segbits*.db build/database/${XRAY_DATABASE} + XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} clk_hrow_bot_r build/segbits_clk_hrow.db + XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} clk_hrow_top_r build/segbits_clk_hrow.db + +pushdb: database + ${XRAY_MERGEDB} clk_hrow_bot_r build/segbits_clk_hrow.db + ${XRAY_MERGEDB} clk_hrow_top_r build/segbits_clk_hrow.db + ${XRAY_MERGEDB} mask_clk_hrow_bot_r build/mask_clk_hrow.db + ${XRAY_MERGEDB} mask_clk_hrow_top_r build/mask_clk_hrow.db + +.PHONY: database pushdb diff --git a/fuzzers/041-clk-hrow-pips/bits.dbf b/fuzzers/041-clk-hrow-pips/bits.dbf new file mode 100644 index 00000000..e69de29b diff --git a/fuzzers/041-clk-hrow-pips/clk_hrow_pip_list.tcl b/fuzzers/041-clk-hrow-pips/clk_hrow_pip_list.tcl new file mode 100644 index 00000000..83872c61 --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/clk_hrow_pip_list.tcl @@ -0,0 +1,20 @@ +proc print_tile_pips {tile_type filename} { + set tile [lindex [get_tiles -filter "TYPE == $tile_type"] 0] + puts "Dumping PIPs for tile $tile ($tile_type) to $filename." + set fp [open $filename w] + foreach pip [lsort [get_pips -of_objects [get_tiles $tile]]] { + set src [get_wires -uphill -of_objects $pip] + set dst [get_wires -downhill -of_objects $pip] + if {[llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst]]] != 1} { + puts $fp "$tile_type.[regsub {.*/} $dst ""].[regsub {.*/} $src ""]" + } + } + close $fp +} + +create_project -force -part $::env(XRAY_PART) design design +set_property design_mode PinPlanning [current_fileset] +open_io_design -name io_1 + +print_tile_pips CLK_HROW_TOP_R clk_hrow_top_r.txt +print_tile_pips CLK_HROW_BOT_R clk_hrow_bot_r.txt diff --git a/fuzzers/041-clk-hrow-pips/clk_table.py b/fuzzers/041-clk-hrow-pips/clk_table.py new file mode 100644 index 00000000..9b3ccdfd --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/clk_table.py @@ -0,0 +1,34 @@ +HCLKS = 24 +GCLKS = 32 +SIDE_CLK_INPUTS = 14 + +CLK_TABLE_NUM_ROWS = 8 +CLK_TABLE_NUM_COLS = 8 + + +def get_clk_table(): + clk_table = {} + for gclk in range(GCLKS): + gclk_name = 'CLK_HROW_R_CK_GCLK{}'.format(gclk) + row = gclk % CLK_TABLE_NUM_ROWS + column = int(gclk / CLK_TABLE_NUM_ROWS) + clk_table[gclk_name] = (row, column) + + for row in range(8): + clk_table['CLK_HROW_CK_IN_L{}'.format(row)] = (row, 4) + for row in range(6): + clk_table['CLK_HROW_CK_IN_L{}'.format(row + 8)] = (row, 5) + + for row in range(8): + clk_table['CLK_HROW_CK_IN_R{}'.format(row)] = (row, 6) + for row in range(6): + clk_table['CLK_HROW_CK_IN_R{}'.format(row + 8)] = (row, 7) + + # HROW_CK_INT__, Y == Y share the same bits, and only X = 0 or X = 1 + # are present on a particular HROW. + for y in range(2): + for x in range(2): + int_clk_name = 'CLK_HROW_CK_INT_{}_{}'.format(x, y) + clk_table[int_clk_name] = (y + 6, 7) + + return clk_table diff --git a/fuzzers/041-clk-hrow-pips/generate.py b/fuzzers/041-clk-hrow-pips/generate.py new file mode 100644 index 00000000..fe4e53c0 --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/generate.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +from prjxray.segmaker import Segmaker +import clk_table + + +def main(): + segmk = Segmaker("design.bits") + table = clk_table.get_clk_table() + + print("Loading tags from design.txt.") + with open("design.txt", "r") as f: + for line in f: + tile, pip, src, dst, pnum, pdir = line.split() + + if not tile.startswith('CLK_HROW'): + continue + + pip_prefix, pip = pip.split(".") + tile_from_pip, tile_type = pip_prefix.split('/') + assert tile == tile_from_pip + _, src = src.split("/") + _, dst = dst.split("/") + + rows = set(range(clk_table.CLK_TABLE_NUM_ROWS)) + columns = set(range(clk_table.CLK_TABLE_NUM_COLS)) + + if src in table: + row, column = table[src] + + segmk.add_tile_tag( + tile, '{}.HCLK_ENABLE_ROW{}'.format(dst, row), 1) + segmk.add_tile_tag( + tile, '{}.HCLK_ENABLE_COLUMN{}'.format(dst, column), 1) + + rows.remove(row) + columns.remove(column) + + for row in rows: + segmk.add_tile_tag( + tile, '{}.HCLK_ENABLE_ROW{}'.format(dst, row), 0) + + for column in columns: + segmk.add_tile_tag( + tile, '{}.HCLK_ENABLE_COLUMN{}'.format(dst, column), 0) + + segmk.compile() + segmk.write() + + +if __name__ == '__main__': + main() diff --git a/fuzzers/041-clk-hrow-pips/generate.tcl b/fuzzers/041-clk-hrow-pips/generate.tcl new file mode 100644 index 00000000..e9f672b4 --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/generate.tcl @@ -0,0 +1,167 @@ +source "$::env(XRAY_DIR)/utils/utils.tcl" + +proc write_pip_txtdata {filename} { + puts "FUZ([pwd]): Writing $filename." + set fp [open $filename w] + set nets [get_nets -hierarchical] + set nnets [llength $nets] + set neti 0 + foreach net $nets { + incr neti + if {($neti % 100) == 0 } { + puts "FUZ([pwd]): Dumping pips from net $net ($neti / $nnets)" + } + foreach pip [get_pips -of_objects $net] { + set tile [get_tiles -of_objects $pip] + set src_wire [get_wires -uphill -of_objects $pip] + set dst_wire [get_wires -downhill -of_objects $pip] + set num_pips [llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst_wire]]] + set dir_prop [get_property IS_DIRECTIONAL $pip] + puts $fp "$tile $pip $src_wire $dst_wire $num_pips $dir_prop" + } + } + close $fp +} + +proc load_todo {} { + set fp [open "../../todo.txt" r] + set todo_lines {} + for {gets $fp line} {$line != ""} {gets $fp line} { + lappend todo_lines [split $line .] + } + close $fp + return $todo_lines +} + +proc route_todo {} { + puts "Checking TODO's" + set todo_lines [load_todo] + set srcs {} + foreach todo $todo_lines { + set src [lindex $todo 2] + + if [string match "*CLK_HROW_CK_IN_*" $src] { + lappend srcs $src + } + } + + set srcs [lsort -unique $srcs] + + set nets [get_nets -hierarchical "*clock*"] + set found_wires {} + set remaining_nets {} + foreach net $nets { + set wires [get_wires -of_objects $net] + + foreach wire $wires { + if [regexp "CLK_HROW_CK_IN_\[LR\]\[0-9\]+" $wire] { + # Route already going where we want it, continue + puts "Checking wire $wire." + set wire [lindex [split $wire "/"] 1] + if {[lsearch -regexp $srcs "$wire$"] != -1} { + puts "Found in TODO list, removing from list." + lappend found_wires $wire + # Fix route that is using target net. + set_property is_route_fixed 1 $net + } else { + puts "Wire not in TODO list, adding to reroute list." + lappend remaining_nets $net + } + break + } + } + } + + set found_wires [lsort -unique $found_wires] + foreach wire $found_wires { + puts "Removing $wire" + set srcs [lsearch -regexp -all -inline -not $srcs "$wire$"] + } + + puts "Remaining TODOs:" + foreach src $srcs { + puts $src + } + + set remaining_nets [lsort -unique $remaining_nets] + set completed_todos {} + + foreach net $remaining_nets { + set wires [get_wires -of_objects $net] + + set clk_in_wire "" + foreach wire $wires { + if [regexp "CLK_HROW_CK_IN_(\[LR\])\[0-9\]+" $wire match lr] { + set clk_in_wire $wire + break + } + } + + if {$clk_in_wire == ""} { + error "$net does not appear to be correct net for rerouting?" + } + + puts "" + puts "Rerouting net $net at $clk_in_wire ($lr)" + + # Find an input in the todo list that this can can drive. + foreach src $srcs { + if {[lsearch -exact $completed_todos $src] != -1} { + continue + } + + if [regexp "CLK_HROW_CK_IN_$lr\[0-9\]+" $src] { + puts "Found target pip $src for net $net." + set tile [get_tiles -of_objects $clk_in_wire] + + set target_wire [get_wires "$tile/$src"] + set target_node [get_nodes -of_objects $target_wire] + if {[llength $target_node] == 0} { + error "Failed to find node for $tile/$src." + } + + set origin_node [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]] + set destination_nodes [get_nodes -of_objects [get_site_pins -filter {DIRECTION == IN} -of_objects $net]] + route_design -unroute -nets $net + set new_route [find_routing_path -to $target_node -from $origin_node] + puts "Origin node: $origin_node" + puts "Target wire: $target_wire" + puts "Target node: $target_node" + puts "Destination nodes: $destination_nodes" + + # Only need to set route to one of the destinations. + # Router will handle the rest. + set_property FIXED_ROUTE [concat $new_route [lindex $destination_nodes 0]] $net + + # Remove wire, as we've found a clock to set + lappend completed_todos $src + break + } + } + } +} + +proc run {} { + create_project -force -part $::env(XRAY_PART) design design + read_verilog top.v + synth_design -top top + + set_property CFGBVS VCCO [current_design] + set_property CONFIG_VOLTAGE 3.3 [current_design] + set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] + set_property IS_ENABLED 0 [get_drc_checks {REQP-161}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-123}] + + set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets] + + place_design + route_design + route_todo + route_design + + write_checkpoint -force design.dcp + write_bitstream -force design.bit + write_pip_txtdata design.txt +} + +run diff --git a/fuzzers/041-clk-hrow-pips/merge_clk_entries.py b/fuzzers/041-clk-hrow-pips/merge_clk_entries.py new file mode 100644 index 00000000..f7e2ea27 --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/merge_clk_entries.py @@ -0,0 +1,80 @@ +import argparse +import clk_table + + +def main(): + parser = argparse.ArgumentParser( + description="Convert HCLK ROW/COLUMN definitions into HCLK pips.") + parser.add_argument('in_segbit') + parser.add_argument('piplist') + parser.add_argument('out_segbit') + + args = parser.parse_args() + + hrow_outs = {} + tile = None + with open(args.in_segbit) as f: + for l in f: + parts = l.strip().split(' ') + feature = parts[0] + bits = ' '.join(parts[1:]) + + tile1, dst, src = feature.split('.') + if tile is None: + tile = tile1 + else: + assert tile == tile1 + + n = int(src[-1]) + + if dst not in hrow_outs: + hrow_outs[dst] = { + 'rows': {}, + 'columns': {}, + } + + if src[-4:-1] == 'ROW': + hrow_outs[dst]['rows'][n] = bits + else: + assert src[-7:-1] == 'COLUMN', src + hrow_outs[dst]['columns'][n] = bits + + piplists = {} + with open(args.piplist) as f: + for l in f: + tile, dst, src = l.strip().split('.') + assert tile == 'CLK_HROW_BOT_R', tile + + if dst not in piplists: + piplists[dst] = [] + + piplists[dst].append(src) + + table = clk_table.get_clk_table() + + with open(args.out_segbit, 'w') as f: + for dst in sorted(hrow_outs): + for src in sorted(piplists[dst]): + if src not in table: + continue + + row, column = table[src] + + if row not in hrow_outs[dst]['rows']: + continue + + if column not in hrow_outs[dst]['columns']: + continue + + print( + 'CLK_HROW.{dst}.{inclk} {row_bits} {column_bits}'.format( + dst=dst, + inclk=src, + row_bits=hrow_outs[dst]['rows'][row], + column_bits=hrow_outs[dst]['columns'][column], + ), + file=f) + + +if __name__ == "__main__": + main() diff --git a/fuzzers/041-clk-hrow-pips/output_cmt.tcl b/fuzzers/041-clk-hrow-pips/output_cmt.tcl new file mode 100644 index 00000000..d1af528c --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/output_cmt.tcl @@ -0,0 +1,11 @@ +create_project -force -part $::env(XRAY_PART) design design +set_property design_mode PinPlanning [current_fileset] +open_io_design -name io_1 + +set fp [open "cmt_regions.csv" "w"] +foreach site_type {MMCME2_ADV PLLE2_ADV BUFHCE} { + foreach site [get_sites -filter "SITE_TYPE == $site_type"] { + puts $fp "$site,[get_property CLOCK_REGION $site]" + } +} +close $fp diff --git a/fuzzers/041-clk-hrow-pips/top.py b/fuzzers/041-clk-hrow-pips/top.py new file mode 100644 index 00000000..fe72f644 --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/top.py @@ -0,0 +1,298 @@ +""" Emits top.v's for various BUFHCE routing inputs. """ +import os +import random +random.seed(int(os.getenv("SEED"), 16)) +from prjxray import util +from prjxray.db import Database + +CMT_XY_FUN = util.create_xy_fun(prefix='') + + +def gen_sites(desired_site_type): + db = Database(util.get_db_root()) + grid = db.grid() + + for tile_name in sorted(grid.tiles()): + loc = grid.loc_of_tilename(tile_name) + gridinfo = grid.gridinfo_at_loc(loc) + for site, site_type in gridinfo.sites.items(): + if site_type == desired_site_type: + yield site + + +def gen_bufhce_sites(): + db = Database(util.get_db_root()) + grid = db.grid() + for tile_name in sorted(grid.tiles()): + loc = grid.loc_of_tilename(tile_name) + gridinfo = grid.gridinfo_at_loc(loc) + sites = [] + + for site, site_type in gridinfo.sites.items(): + if site_type == 'BUFHCE': + sites.append(site) + + if sites: + yield tile_name, sorted(sites) + + +def read_site_to_cmt(): + """ Yields clock sources and which CMT they route within. """ + with open(os.path.join(os.getenv('FUZDIR'), 'build', + 'cmt_regions.csv')) as f: + for l in f: + site, cmt = l.strip().split(',') + yield (site, cmt) + + +class ClockSources(object): + """ Class for tracking clock sources. + + Some clock sources can be routed to any CMT, for these, cmt='ANY'. + For clock sources that belong to a CMT, cmt should be set to the CMT of + the source. + + """ + + def __init__(self): + self.sources = {} + self.merged_sources = {} + self.source_to_cmt = {} + self.used_sources_from_cmt = {} + + def add_clock_source(self, source, cmt): + """ Adds a source from a specific CMT. + + cmt='ANY' indicates that this source can be routed to any CMT. + """ + if cmt not in self.sources: + self.sources[cmt] = [] + + self.sources[cmt].append(source) + assert source not in self.source_to_cmt or self.source_to_cmt[ + source] == cmt, source + self.source_to_cmt[source] = cmt + + def get_random_source(self, cmt): + """ Get a random source that is routable to the specific CMT. + + get_random_source will return a source that is either cmt='ANY', + cmt equal to the input CMT, or the adjecent CMT. + + """ + if cmt not in self.merged_sources: + choices = [] + if 'ANY' in self.sources: + choices.extend(self.sources['ANY']) + + if cmt in self.sources: + choices.extend(self.sources[cmt]) + + x, y = CMT_XY_FUN(cmt) + + if x % 2 == 0: + x += 1 + else: + x -= 1 + + paired_cmt = 'X{}Y{}'.format(x, y) + + if paired_cmt in self.sources: + choices.extend(self.sources[paired_cmt]) + + self.merged_sources[cmt] = choices + + if self.merged_sources[cmt]: + source = random.choice(self.merged_sources[cmt]) + + source_cmt = self.source_to_cmt[source] + if source_cmt not in self.used_sources_from_cmt: + self.used_sources_from_cmt[source_cmt] = set() + + self.used_sources_from_cmt[source_cmt].add(source) + + if source_cmt != 'ANY' and len( + self.used_sources_from_cmt[source_cmt]) > 14: + print('//', self.used_sources_from_cmt) + self.used_sources_from_cmt[source_cmt].remove(source) + return None + else: + return source + + +def check_allowed(mmcm_pll_dir, cmt): + """ Check whether the CMT specified is in the allowed direction. + + This function is designed to bias sources to either the left or right + input lines. + + """ + if mmcm_pll_dir == 'BOTH': + return True + elif mmcm_pll_dir == 'ODD': + x, y = CMT_XY_FUN(cmt) + return (x & 1) == 1 + elif mmcm_pll_dir == 'EVEN': + x, y = CMT_XY_FUN(cmt) + return (x & 1) == 0 + else: + assert False, mmcm_pll_dir + + +def main(): + """ + BUFHCE's can be driven from: + + MMCME2_ADV + PLLE2_ADV + BUFGCTRL + Local INT connect + + """ + + print(''' +module top(); + ''') + + site_to_cmt = dict(read_site_to_cmt()) + + clock_sources = ClockSources() + + # To ensure that all left or right sources are used, sometimes only MMCM/PLL + # sources are allowed. The force of ODD/EVEN/BOTH further biases the + # clock sources to the left or right column inputs. + mmcm_pll_only = random.randint(0, 1) + mmcm_pll_dir = random.choice(('ODD', 'EVEN', 'BOTH')) + + if not mmcm_pll_only: + for _ in range(2): + clock_sources.add_clock_source('one', 'ANY') + clock_sources.add_clock_source('zero', 'ANY') + + print(""" + wire zero = 0; + wire one = 1;""") + + for site in gen_sites('MMCME2_ADV'): + mmcm_clocks = [ + 'mmcm_clock_{site}_{idx}'.format(site=site, idx=idx) + for idx in range(13) + ] + + if not check_allowed(mmcm_pll_dir, site_to_cmt[site]): + continue + + for clk in mmcm_clocks: + clock_sources.add_clock_source(clk, site_to_cmt[site]) + + print( + """ + wire {c0}, {c1}, {c2}, {c3}, {c4}, {c5}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + MMCME2_ADV pll_{site} ( + .CLKOUT0({c0}), + .CLKOUT0B({c1}), + .CLKOUT1({c2}), + .CLKOUT1B({c3}), + .CLKOUT2({c4}), + .CLKOUT2B({c5}), + .CLKOUT3({c6}), + .CLKOUT3B({c7}), + .CLKOUT4({c8}), + .CLKOUT5({c9}), + .CLKOUT6({c10}), + .CLKFBOUT({c11}), + .CLKFBOUTB({c12}) + ); + """.format( + site=site, + c0=mmcm_clocks[0], + c1=mmcm_clocks[1], + c2=mmcm_clocks[2], + c3=mmcm_clocks[3], + c4=mmcm_clocks[4], + c5=mmcm_clocks[5], + c6=mmcm_clocks[6], + c7=mmcm_clocks[7], + c8=mmcm_clocks[8], + c9=mmcm_clocks[9], + c10=mmcm_clocks[10], + c11=mmcm_clocks[11], + c12=mmcm_clocks[12], + )) + + for site in gen_sites('PLLE2_ADV'): + pll_clocks = [ + 'pll_clock_{site}_{idx}'.format(site=site, idx=idx) + for idx in range(6) + ] + + if not check_allowed(mmcm_pll_dir, site_to_cmt[site]): + continue + + for clk in pll_clocks: + clock_sources.add_clock_source(clk, site_to_cmt[site]) + + print( + """ + wire {c0}, {c1}, {c2}, {c3}, {c4}, {c5}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + PLLE2_ADV pll_{site} ( + .CLKOUT0({c0}), + .CLKOUT1({c1}), + .CLKOUT2({c2}), + .CLKOUT3({c3}), + .CLKOUT4({c4}), + .CLKOUT5({c5}) + ); + """.format( + site=site, + c0=pll_clocks[0], + c1=pll_clocks[1], + c2=pll_clocks[2], + c3=pll_clocks[3], + c4=pll_clocks[4], + c5=pll_clocks[5], + )) + + for site in sorted(gen_sites("BUFGCTRL"), + key=util.create_xy_fun('BUFGCTRL_')): + wire_name = 'clk_{}'.format(site) + + if not mmcm_pll_only: + clock_sources.add_clock_source(wire_name, 'ANY') + + print( + """ + wire {wire_name}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFG bufg_{site} ( + .O({wire_name}) + ); + """.format( + site=site, + wire_name=wire_name, + )) + + for tile_name, sites in gen_bufhce_sites(): + for site in sites: + wire_name = clock_sources.get_random_source(site_to_cmt[site]) + if wire_name is None: + continue + + print( + """ + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFHCE buf_{site} ( + .I({wire_name}) + ); + """.format( + site=site, + wire_name=wire_name, + )) + + print("endmodule") + + +if __name__ == '__main__': + main() diff --git a/fuzzers/057-pip-bi/Makefile b/fuzzers/057-pip-bi/Makefile index 0052906d..33aebca5 100644 --- a/fuzzers/057-pip-bi/Makefile +++ b/fuzzers/057-pip-bi/Makefile @@ -1,4 +1,4 @@ -MAKETODO_FLAGS=--re ".*" +MAKETODO_FLAGS=--pip-type bipips_int --re ".*" export FUZDIR=$(shell pwd) PIPLIST_TCL=$(FUZDIR)/bipiplist.tcl PIP_TYPE?=bipips_int diff --git a/fuzzers/057-pip-bi/generate.tcl b/fuzzers/057-pip-bi/generate.tcl index fb295ebf..1743bd0f 100644 --- a/fuzzers/057-pip-bi/generate.tcl +++ b/fuzzers/057-pip-bi/generate.tcl @@ -84,8 +84,7 @@ for {set idx 0} {$idx < [llength $todo_lines]} {incr idx} { # sometimes it gets stuck in specific src -> dst locations if {$tries >= 3} { - puts "WARNING: failed to route net after $tries tries" - error + error "WARNING: failed to route net after $tries tries" } } diff --git a/fuzzers/060-bram-cascades/Makefile b/fuzzers/060-bram-cascades/Makefile index 2b8d61bc..bf8f531b 100644 --- a/fuzzers/060-bram-cascades/Makefile +++ b/fuzzers/060-bram-cascades/Makefile @@ -1,11 +1,12 @@ -MAKETODO_FLAGS=--re "BRAM_.\.BRAM_(?!LOGIC_OUTS).*" export FUZDIR=$(shell pwd) -PIPLIST_TCL=$(FUZDIR)/bram_pip_list.tcl PIP_TYPE?=bram_pips_int -SEG_TYPE?=bram +PIPLIST_TCL=$(FUZDIR)/bram_pip_list.tcl +MAKETODO_FLAGS=--pip-type ${PIP_TYPE} --seg-type bram --re "BRAM_.\.BRAM_(?!LOGIC_OUTS).*" N = 50 + # These PIPs all appear to be either a 0 or 2 bit solution. SEGMATCH_FLAGS=-m 20 -M 45 -c 2 + include ../pip_loop.mk # # Specimens from current run must complete, but previous iterations may exist diff --git a/fuzzers/Makefile b/fuzzers/Makefile index 3e4943d2..32ecad40 100644 --- a/fuzzers/Makefile +++ b/fuzzers/Makefile @@ -70,6 +70,8 @@ $(eval $(call fuzzer,026-bram-data,005-tilegrid)) $(eval $(call fuzzer,027-bram36-config,005-tilegrid)) $(eval $(call fuzzer,028-fifo-config,005-tilegrid)) $(eval $(call fuzzer,029-bram-fifo-config,005-tilegrid)) +$(eval $(call fuzzer,040-clk-hrow-config,005-tilegrid)) +$(eval $(call fuzzer,041-clk-hrow-pips,005-tilegrid)) $(eval $(call fuzzer,050-pip-seed,005-tilegrid)) $(eval $(call fuzzer,051-pip-imuxlout-bypalts,050-pip-seed)) $(eval $(call fuzzer,052-pip-clkin,050-pip-seed)) diff --git a/fuzzers/fuzzer.mk b/fuzzers/fuzzer.mk index 0f260ea2..d7cc8010 100644 --- a/fuzzers/fuzzer.mk +++ b/fuzzers/fuzzer.mk @@ -2,13 +2,14 @@ N ?= 1 SPECIMENS := $(addprefix build/specimen_,$(shell seq -f '%03.0f' $(N))) SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS)) ENV_VAR ?= +SPECIMENS_DEPS ?= FUZDIR ?= ${PWD} all: database # generate.sh / top_generate.sh call make, hence the command must # have a + before it. -$(SPECIMENS_OK): +$(SPECIMENS_OK): $(SPECIMENS_DEPS) mkdir -p build +if [ -f $(FUZDIR)/generate.sh ]; then \ export $(ENV_VAR); \ diff --git a/fuzzers/int_maketodo.py b/fuzzers/int_maketodo.py index 69b1d9e3..8ed0774a 100644 --- a/fuzzers/int_maketodo.py +++ b/fuzzers/int_maketodo.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import os, re -import sys from prjxray import util @@ -90,6 +89,8 @@ def run( db_dir, pip_dir, intre, + l, + r, pip_type, seg_type, not_endswith=None, @@ -102,18 +103,22 @@ def run( pip_dir = "%s/piplist/build" % (os.getenv("XRAY_FUZZERS_DIR")) assert intre, "RE is required" - maketodo( - "%s/%s_l.txt" % (pip_dir, pip_type), - "%s/segbits_%s_l.db" % (db_dir, seg_type), - intre, - not_endswith, - verbose=verbose) - maketodo( - "%s/%s_r.txt" % (pip_dir, pip_type), - "%s/segbits_%s_r.db" % (db_dir, seg_type), - intre, - not_endswith, - verbose=verbose) + + if l: + maketodo( + "%s/%s_l.txt" % (pip_dir, pip_type), + "%s/segbits_%s_l.db" % (db_dir, seg_type), + intre, + not_endswith, + verbose=verbose) + + if r: + maketodo( + "%s/%s_r.txt" % (pip_dir, pip_type), + "%s/segbits_%s_r.db" % (db_dir, seg_type), + intre, + not_endswith, + verbose=verbose) def main(): @@ -129,6 +134,8 @@ def main(): parser.add_argument('--re', required=True, help='') parser.add_argument('--pip-type', default="pips_int", help='') parser.add_argument('--seg-type', default="int", help='') + util.add_bool_arg(parser, '--l', default=True, help='') + util.add_bool_arg(parser, '--r', default=True, help='') parser.add_argument( '--not-endswith', help='Drop lines if they end with this') args = parser.parse_args() @@ -138,6 +145,8 @@ def main(): db_dir=args.db_dir, pip_dir=args.pip_dir, intre=args.re, + l=args.l, + r=args.r, pip_type=args.pip_type, seg_type=args.seg_type, not_endswith=args.not_endswith, diff --git a/fuzzers/pip_loop.mk b/fuzzers/pip_loop.mk index 1c1497b9..6fad6d66 100644 --- a/fuzzers/pip_loop.mk +++ b/fuzzers/pip_loop.mk @@ -12,10 +12,11 @@ endif # Iteration number (each containing N specimens) # Driven by int_loop.sh ITER ?= 1 -MAKETODO_FLAGS ?= PIP_TYPE?=pips_int -SEG_TYPE?=int +MAKETODO_FLAGS ?=--pip-type pips_int --seg-type int +A_PIPLIST?=$(PIP_TYPE)_l.txt PIPLIST_TCL?=$(XRAY_FUZZERS_DIR)/piplist/piplist.tcl +SPECIMENS_DEPS ?= # See int_loop_check.py # rempips took 35 iters once, so set 50 as a good start point @@ -27,18 +28,20 @@ export FUZDIR=$(shell pwd) all: database -$(SPECIMENS_OK): build/todo.txt +$(SPECIMENS_OK): build/todo.txt $(SPECIMENS_DEPS) mkdir -p build/$(ITER) bash ${XRAY_DIR}/utils/top_generate.sh $(subst /OK,,$@) touch $@ -$(XRAY_FUZZERS_DIR)/piplist/build/$(PIP_TYPE)_l.txt: $(PIPLIST_TCL) +$(XRAY_FUZZERS_DIR)/piplist/build/$(A_PIPLIST): $(PIPLIST_TCL) mkdir -p $(XRAY_FUZZERS_DIR)/piplist/build cd $(XRAY_FUZZERS_DIR)/piplist/build && ${XRAY_VIVADO} -mode batch -source $(PIPLIST_TCL) # Used 1) to see if we are done 2) pips to try in generate.tcl -build/todo.txt: $(XRAY_FUZZERS_DIR)/piplist/build/$(PIP_TYPE)_l.txt $(XRAY_DIR)/fuzzers/int_maketodo.py build/database/seeded - XRAY_DATABASE_DIR=${FUZDIR}/build/database python3 $(XRAY_DIR)/fuzzers/int_maketodo.py --pip-type $(PIP_TYPE) --seg-type $(SEG_TYPE) $(MAKETODO_FLAGS) |sort >build/todo_all.txt +build/todo.txt: $(XRAY_FUZZERS_DIR)/piplist/build/$(A_PIPLIST) $(XRAY_DIR)/fuzzers/int_maketodo.py build/database/seeded + XRAY_DATABASE_DIR=${FUZDIR}/build/database \ + python3 $(XRAY_DIR)/fuzzers/int_maketodo.py \ + $(MAKETODO_FLAGS) |sort >build/todo_all.txt cat build/todo_all.txt | sort -R | head -n$(TODO_N) > build/todo.txt.tmp mv build/todo.txt.tmp build/todo.txt # Per iter files diff --git a/prjxray/segmaker.py b/prjxray/segmaker.py index 5fccbf57..559556a1 100644 --- a/prjxray/segmaker.py +++ b/prjxray/segmaker.py @@ -335,8 +335,9 @@ class Segmaker: Simplify names by simplifying like: -CLBLM_L => CLB -CENTER_INTER_R => CENTER_INTER + -CLK_HROW_TOP_R => CLK_HROW ''' - tile_type_norm = re.sub("(LL|LM)?_[LR]$", "", tile_type) + tile_type_norm = re.sub("(_TOP|_BOT|LL|LM)?_[LR]$", "", tile_type) # ignore dummy tiles (ex: VBRK) if len(tiledata['bits']) == 0: diff --git a/prjxray/util.py b/prjxray/util.py index 35f9939f..fae1cdf0 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -22,6 +22,41 @@ def roi_xy(): return (x1, x2), (y1, y2) +def create_xy_fun(prefix): + """ Create function that extracts X and Y coordinate from a prefixed string + + >>> fun = create_xy_fun(prefix='') + >>> fun('X5Y23') + (5, 23) + >>> fun('X0Y0') + (0, 0) + >>> fun('X50Y100') + (50, 100) + + >>> fun = create_xy_fun(prefix='SITE_') + >>> fun('SITE_X5Y23') + (5, 23) + >>> fun('SITE_X0Y0') + (0, 0) + >>> fun('SITE_X50Y100') + (50, 100) + + """ + compiled_re = re.compile( + '^{prefix}X([0-9]+)Y([0-9]+)$'.format(prefix=prefix)) + + def get_xy(s): + m = compiled_re.match(s) + assert m, (prefix, s) + + x = int(m.group(1)) + y = int(m.group(2)) + + return x, y + + return get_xy + + def slice_xy(): '''Return (X1, X2), (Y1, Y2) from XRAY_ROI, exclusive end (for xrange)''' # SLICE_X12Y100:SLICE_X27Y149 diff --git a/utils/mergedb.py b/utils/mergedb.py index 70088dda..af9b5e4e 100755 --- a/utils/mergedb.py +++ b/utils/mergedb.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import sys, re import os from prjxray import util diff --git a/utils/mergedb.sh b/utils/mergedb.sh index 93011e51..2f955c88 100755 --- a/utils/mergedb.sh +++ b/utils/mergedb.sh @@ -73,6 +73,11 @@ case "$1" in hclk_r) sed < "$2" > "$tmp1" -e 's/^HCLK\./HCLK_R./' ;; + clk_hrow_bot_r) + sed < "$2" > "$tmp1" -e 's/^CLK_HROW\./CLK_HROW_BOT_R./' ;; + clk_hrow_top_r) + sed < "$2" > "$tmp1" -e 's/^CLK_HROW\./CLK_HROW_TOP_R./' ;; + liob33) cp "$2" "$tmp1" ;;