diff --git a/fuzzers/005-tilegrid/Makefile b/fuzzers/005-tilegrid/Makefile index e6cffaad..fc2b3c5d 100644 --- a/fuzzers/005-tilegrid/Makefile +++ b/fuzzers/005-tilegrid/Makefile @@ -15,6 +15,7 @@ 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 TILEGRID_TDB_DEPENDENCIES += clk_bufg/build/segbits_tilegrid.tdb +TILEGRID_TDB_DEPENDENCIES += hclk_cmt/build/segbits_tilegrid.tdb GENERATE_FULL_ARGS= ifeq (${XRAY_DATABASE}, zynq7) @@ -104,6 +105,9 @@ clk_hrow/build/segbits_tilegrid.tdb: build/basicdb/tilegrid.json clk_bufg/build/segbits_tilegrid.tdb: build/basicdb/tilegrid.json cd clk_bufg && $(MAKE) +hclk_cmt/build/segbits_tilegrid.tdb: build/basicdb/tilegrid.json + cd hclk_cmt && $(MAKE) + build/tilegrid_tdb.json: add_tdb.py $(TILEGRID_TDB_DEPENDENCIES) python3 add_tdb.py \ --fn-in build/basicdb/tilegrid.json \ @@ -139,6 +143,7 @@ clean: cd orphan_int_column && $(MAKE) clean cd clk_hrow && $(MAKE) clean cd clk_bufg && $(MAKE) clean + cd hclk_cmt && $(MAKE) clean .PHONY: database pushdb clean run diff --git a/fuzzers/005-tilegrid/add_tdb.py b/fuzzers/005-tilegrid/add_tdb.py index 793b2a3f..f5b32147 100644 --- a/fuzzers/005-tilegrid/add_tdb.py +++ b/fuzzers/005-tilegrid/add_tdb.py @@ -84,8 +84,9 @@ 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), + ("clk_hrow/build/segbits_tilegrid.tdb", 30, 18), ("clk_bufg/build/segbits_tilegrid.tdb", 30, 8), + ("hclk_cmt/build/segbits_tilegrid.tdb", 30, 10), ("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 index 17297e84..1934a3f7 100644 --- a/fuzzers/005-tilegrid/clk_hrow/Makefile +++ b/fuzzers/005-tilegrid/clk_hrow/Makefile @@ -1,4 +1,4 @@ N ?= 5 -GENERATE_ARGS?="--oneval 1 --design params.csv --dword 1 --dframe 1A" +GENERATE_ARGS?="--oneval 1 --design params.csv --dword 6 --dframe 1A" include ../fuzzaddr/common.mk diff --git a/fuzzers/005-tilegrid/generate_full.py b/fuzzers/005-tilegrid/generate_full.py index 476c9221..11aabfa6 100644 --- a/fuzzers/005-tilegrid/generate_full.py +++ b/fuzzers/005-tilegrid/generate_full.py @@ -233,7 +233,7 @@ def propagate_rebuf(database, tiles_by_grid): rebuf_below]['type'] assert database[tile_name]['bits']['CLB_IO_CLK'][ - 'offset'] == 47, database[tile_name]['bits']['CLB_IO_CLK'] + 'offset'] == 42, database[tile_name]['bits']['CLB_IO_CLK'] database[rebuf_below]['bits'] = copy.deepcopy( database[tile_name]['bits']) database[rebuf_below]['bits']['CLB_IO_CLK']['offset'] = 73 diff --git a/fuzzers/005-tilegrid/hclk_cmt/Makefile b/fuzzers/005-tilegrid/hclk_cmt/Makefile new file mode 100644 index 00000000..c6172fe8 --- /dev/null +++ b/fuzzers/005-tilegrid/hclk_cmt/Makefile @@ -0,0 +1,4 @@ +N ?= 5 +GENERATE_ARGS?="--oneval 1 --design params.csv --dword 5 --dframe 1C" +include ../fuzzaddr/common.mk + diff --git a/fuzzers/005-tilegrid/hclk_cmt/generate.tcl b/fuzzers/005-tilegrid/hclk_cmt/generate.tcl new file mode 100644 index 00000000..5a69791f --- /dev/null +++ b/fuzzers/005-tilegrid/hclk_cmt/generate.tcl @@ -0,0 +1,3 @@ +source "$::env(XRAY_DIR)/utils/utils.tcl" + +generate_top diff --git a/fuzzers/005-tilegrid/hclk_cmt/top.py b/fuzzers/005-tilegrid/hclk_cmt/top.py new file mode 100644 index 00000000..3fd68bc0 --- /dev/null +++ b/fuzzers/005-tilegrid/hclk_cmt/top.py @@ -0,0 +1,66 @@ +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 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 == 'BUFMRCE': + 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( + ''' + wire clk_{site}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFMRCE #( + .INIT_OUT({isone}) + ) buf_{site} ( + .O(clk_{site}) + ); + + BUFR bufr_{site} ( + .I(clk_{site}) + ); +'''.format( + site=site_name, + isone=isone, + )) + + print("endmodule") + write_params(params) + + +if __name__ == '__main__': + run() diff --git a/fuzzers/041-clk-hrow-pips/Makefile b/fuzzers/041-clk-hrow-pips/Makefile index f3677cf7..9bd3afc1 100644 --- a/fuzzers/041-clk-hrow-pips/Makefile +++ b/fuzzers/041-clk-hrow-pips/Makefile @@ -1,65 +1,62 @@ export FUZDIR=$(shell pwd) -PIP_TYPE?=clk_hrow_bot +PIP_TYPE?=clk_hrow PIPLIST_TCL=$(FUZDIR)/clk_hrow_pip_list.tcl -ifeq (${XRAY_PART}, xc7z010clg400-1) -# xc7z010clg400-1 is missing some side clock connections, so these bits cannot -# be documented. -TODO_RE="[^\.]+\.CLK_HROW_CK_MUX_OUT_[LR][0-9]+\.CLK_HROW_.*[KR_][0-9]+" -else -TODO_RE="[^\.]+\.CLK_HROW_CK_MUX_OUT_" -endif +TODO_RE=".*" -MAKETODO_FLAGS=--no-l --pip-type ${PIP_TYPE} --seg-type clk_hrow_bot --re $(TODO_RE) +MAKETODO_FLAGS=--sides "bot_r,top_r" --pip-type ${PIP_TYPE} --seg-type clk_hrow --re $(TODO_RE) N = 50 -# These PIPs all appear to be either a 1 bit solutions. -SEGMATCH_FLAGS=-c 1 +# These PIPs all appear to be either a 2 bit solutions. +SEGMATCH_FLAGS=-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 +database: build/segbits_clk_hrow_bot_r.rdb build/segbits_clk_hrow_top_r.rdb + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_clk_hrow_top_r.rdb \ + --seg-fn-out build/segbits_clk_hrow_top_r.db + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_clk_hrow_bot_r.rdb \ + --seg-fn-out build/segbits_clk_hrow_bot_r.db + + # Keep a copy to track iter progress + cp build/segbits_clk_hrow_top_r.rdb build/$(ITER)/segbits_clk_hrow_top_r.rdb + cp build/segbits_clk_hrow_bot_r.rdb build/$(ITER)/segbits_clk_hrow_bot_r.rdb + + ${XRAY_MASKMERGE} build/mask_clk_hrow_top_r.db \ + $(shell find build -name segdata_clk_hrow_top_r.txt) + ${XRAY_MASKMERGE} build/mask_clk_hrow_bot_r.db \ + $(shell find build -name segdata_clk_hrow_bot_r.txt) + + # Clobber existing .db to eliminate potential conflicts + rm -f build/database/${XRAY_DATABASE}/* + 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_bot_r.db + XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} clk_hrow_top_r build/segbits_clk_hrow_top_r.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} ${SEGMATCH_FLAGS} -o build/segbits_clk_hrow.rdb \ - $(shell find build -name segdata_clk_hrow_top_r.txt) \ +generate: $(SPECIMENS_OK) + +build/segbits_clk_hrow_top_r.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_clk_hrow_top_r.rdb \ + $(shell find build -name segdata_clk_hrow_top_r.txt) + +build/segbits_clk_hrow_bot_r.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_clk_hrow_bot_r.rdb \ $(shell find build -name segdata_clk_hrow_bot_r.txt) -build/segbits_clk_hrow.db: build/segbits_clk_hrow.rdb piplist - ${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/${PIP_TYPE}/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 \ - $(shell find build -name segdata_clk_hrow_top_r.txt) \ - $(shell find build -name segdata_clk_hrow_bot_r.txt) - - # 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 +build/segbits_clk_hrow.db: build/segbits_clk_hrow_top_.rdb 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 + ${XRAY_MERGEDB} clk_hrow_bot_r build/segbits_clk_hrow_bot_r.db + ${XRAY_MERGEDB} clk_hrow_top_r build/segbits_clk_hrow_top_r.db + ${XRAY_MERGEDB} mask_clk_hrow_bot_r build/mask_clk_hrow_bot_r.db + ${XRAY_MERGEDB} mask_clk_hrow_top_r build/mask_clk_hrow_top_r.db -.PHONY: database pushdb +.PHONY: database pushdb generate diff --git a/fuzzers/041-clk-hrow-pips/clk_hrow_pip_list.tcl b/fuzzers/041-clk-hrow-pips/clk_hrow_pip_list.tcl index 83872c61..597872ad 100644 --- a/fuzzers/041-clk-hrow-pips/clk_hrow_pip_list.tcl +++ b/fuzzers/041-clk-hrow-pips/clk_hrow_pip_list.tcl @@ -1,12 +1,45 @@ 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 ""]" + set pips [dict create] + foreach tile [get_tiles -filter "TYPE == $tile_type"] { + puts "Dumping PIPs for tile $tile ($tile_type) to $filename." + foreach pip [lsort [get_pips -of_objects $tile]] { + set src [get_wires -uphill -of_objects $pip] + set dst [get_wires -downhill -of_objects $pip] + + # Skip pips with disconnected nodes + set src_node [get_nodes -of_objects $src] + if { $src_node == {} } { + continue + } + + set dst_node [get_nodes -of_objects $src] + if { $dst_node == {} } { + continue + } + + # TODO: Support CLK sources from GTX hardblocks. + if [string match *GTX* $src_node] { + continue + } + + # Some ports appear to be just test signals, ignore these. + if [string match *TESTPLL* $src_node] { + continue + } + + # TODO: Support CLK sources from PS7 hardblock + if [string match *PSS_HCLK* $src_node] { + continue + } + + if {[llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst]]] != 1} { + set pip_string "$tile_type.[regsub {.*/} $dst ""].[regsub {.*/} $src ""]" + if ![dict exists $pips $pip_string] { + puts $fp $pip_string + dict set pips $pip_string 1 + } + } } } close $fp diff --git a/fuzzers/041-clk-hrow-pips/clk_table.py b/fuzzers/041-clk-hrow-pips/clk_table.py deleted file mode 100644 index 9b3ccdfd..00000000 --- a/fuzzers/041-clk-hrow-pips/clk_table.py +++ /dev/null @@ -1,34 +0,0 @@ -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 index f9b20ba2..30f24eed 100644 --- a/fuzzers/041-clk-hrow-pips/generate.py +++ b/fuzzers/041-clk-hrow-pips/generate.py @@ -1,17 +1,54 @@ #!/usr/bin/env python3 +import os +import os.path from prjxray.segmaker import Segmaker -import clk_table def main(): segmk = Segmaker("design.bits") - table = clk_table.get_clk_table() + + tiledata = {} + pipdata = {} + clk_list = {} + casco_list = {} + ignpip = set() + + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'clk_hrow', 'clk_hrow_bot_r.txt')) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] + clk_list[tile_type] = set() + casco_list[tile_type] = set() + + pipdata[tile_type].append((src, dst)) + + if 'CASCO' in dst: + casco_list[tile_type].add(dst) + + if dst.startswith('CLK_HROW_CK_MUX_OUT_'): + clk_list[tile_type].add(src) + + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'clk_hrow', 'clk_hrow_top_r.txt')) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] + clk_list[tile_type] = set() + casco_list[tile_type] = set() + + pipdata[tile_type].append((src, dst)) + + if 'CASCO' in dst: + casco_list[tile_type].add(dst) + + if dst.startswith('CLK_HROW_CK_MUX_OUT_'): + clk_list[tile_type].add(src) print("Loading tags from design.txt.") - - active_gclks = {} - active_clks = {} with open("design.txt", "r") as f: for line in f: tile, pip, src, dst, pnum, pdir = line.split() @@ -19,57 +56,71 @@ def main(): if not tile.startswith('CLK_HROW'): continue - pip_prefix, pip = pip.split(".") + pip_prefix, _ = pip.split(".") tile_from_pip, tile_type = pip_prefix.split('/') assert tile == tile_from_pip _, src = src.split("/") _, dst = dst.split("/") + pnum = int(pnum) + pdir = int(pdir) - rows = set(range(clk_table.CLK_TABLE_NUM_ROWS)) - columns = set(range(clk_table.CLK_TABLE_NUM_COLS)) + if tile not in tiledata: + tiledata[tile] = { + "type": tile_type, + "pips": set(), + "srcs": set(), + "dsts": set() + } - if src in table: - row, column = table[src] + tiledata[tile]["pips"].add((src, dst)) + tiledata[tile]["srcs"].add(src) + tiledata[tile]["dsts"].add(dst) - segmk.add_tile_tag( - tile, '{}.HCLK_ENABLE_ROW{}'.format(dst, row), 1) - segmk.add_tile_tag( - tile, '{}.HCLK_ENABLE_COLUMN{}'.format(dst, column), 1) + if pdir == 0: + tiledata[tile]["srcs"].add(dst) + tiledata[tile]["dsts"].add(src) - rows.remove(row) - columns.remove(column) + if pnum == 1 or pdir == 0: + ignpip.add((src, dst)) - for row in rows: - segmk.add_tile_tag( - tile, '{}.HCLK_ENABLE_ROW{}'.format(dst, row), 0) + active_gclks = {} + active_clks = {} - for column in columns: - segmk.add_tile_tag( - tile, '{}.HCLK_ENABLE_COLUMN{}'.format(dst, column), 0) + for tile, pips_srcs_dsts in tiledata.items(): + tile_type = pips_srcs_dsts["type"] + pips = pips_srcs_dsts["pips"] - if tile not in active_clks: - active_clks[tile] = set() + if tile not in active_clks: + active_clks[tile] = set() - active_clks[tile].add(src) + for src, dst in pips_srcs_dsts["pips"]: + active_clks[tile].add(src) - if 'GCLK' in src: - if src not in active_gclks: - active_gclks[src] = set() - - active_gclks[src].add(tile) - - tiles = sorted(active_clks.keys()) - - for tile in active_clks: - for src in table: - if 'GCLK' not in src: - active = src in active_clks[tile] - segmk.add_tile_tag(tile, '{}_ACTIVE'.format(src), active) - else: + if 'GCLK' in src: if src not in active_gclks: - segmk.add_tile_tag(tile, '{}_ACTIVE'.format(src), 0) - elif tile in active_gclks[src]: - segmk.add_tile_tag(tile, '{}_ACTIVE'.format(src), 1) + active_gclks[src] = set() + + active_gclks[src].add(tile) + + for src, dst in pipdata[tile_type]: + if (src, dst) in ignpip: + pass + elif (src, dst) in pips: + segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 1) + elif dst not in tiledata[tile]["dsts"]: + segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 0) + + for tile_type, srcs in clk_list.items(): + for tile, pips_srcs_dsts in tiledata.items(): + for src in srcs: + if 'GCLK' not in src: + active = src in active_clks[tile] + segmk.add_tile_tag(tile, '{}_ACTIVE'.format(src), active) + else: + if src not in active_gclks: + segmk.add_tile_tag(tile, '{}_ACTIVE'.format(src), 0) + elif tile in active_gclks[src]: + segmk.add_tile_tag(tile, '{}_ACTIVE'.format(src), 1) segmk.compile() segmk.write() diff --git a/fuzzers/041-clk-hrow-pips/generate.tcl b/fuzzers/041-clk-hrow-pips/generate.tcl index e9f672b4..5f9277e1 100644 --- a/fuzzers/041-clk-hrow-pips/generate.tcl +++ b/fuzzers/041-clk-hrow-pips/generate.tcl @@ -24,119 +24,209 @@ proc write_pip_txtdata {filename} { } proc load_todo {} { - set fp [open "../../todo.txt" r] - set todo_lines {} + set fp [open "../../todo_all.txt" r] + + # Create map of pip destinations to remaining sources for that pip + set todo_map [dict create] for {gets $fp line} {$line != ""} {gets $fp line} { - lappend todo_lines [split $line .] + set parts [split $line .] + if ![string match "*CLK_HROW_CK_IN_*" [lindex $parts 2]] { + continue + } + dict lappend todo_map [lindex $parts 1] [list [lindex $parts 0] [lindex $parts 2]] } close $fp - return $todo_lines + return $todo_map } proc route_todo {} { puts "Checking TODO's" - set todo_lines [load_todo] - set srcs {} - foreach todo $todo_lines { - set src [lindex $todo 2] + set todo_map [load_todo] - if [string match "*CLK_HROW_CK_IN_*" $src] { - lappend srcs $src - } - } + set nets [get_nets] - set srcs [lsort -unique $srcs] + set todo_nets [dict create] + set used_destinations [dict create] - set nets [get_nets -hierarchical "*clock*"] - set found_wires {} - set remaining_nets {} foreach net $nets { - set wires [get_wires -of_objects $net] + # Check to see if this net is one we are interested in + set wires [get_wires -of_objects $net -filter {TILE_NAME =~ *CLK_HROW*}] + set is_gclk_net 0 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 + puts "Check wire $wire in $net" + if [string match "*CLK_HROW_CK_IN_*" $wire] { + set gclk_tile [lindex [split $wire /] 0] + set gclk_wire [lindex [split $wire /] 1] + set is_gclk_net 1 break } } - if {$clk_in_wire == ""} { - error "$net does not appear to be correct net for rerouting?" + if {$is_gclk_net == 0} { + puts "$net not going to a HCLK port, skipping." + continue } - puts "" - puts "Rerouting net $net at $clk_in_wire ($lr)" + puts "Net $net wires:" + foreach wire [get_wires -of_objects $net] { + puts " - $wire" + } - # Find an input in the todo list that this can can drive. - foreach src $srcs { - if {[lsearch -exact $completed_todos $src] != -1} { + foreach wire $wires { + set tile [lindex [split $wire /] 0] + set wire [lindex [split $wire /] 1] + if { $tile != $gclk_tile } { 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 tile_type [get_property TILE_TYPE [get_tiles $tile]] - 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." + if { ![dict exists $todo_map $wire] } { + continue + } + + set srcs [dict get $todo_map $wire] + + # This net is interesting, see if it is already going somewhere we + # want. + set found_target 0 + foreach other_wire $wires { + if { $found_target == 1 } { + break } - 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" + set other_wire [lindex [split $other_wire /] 1] - # 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 + if { $wire == $other_wire } { + continue + } - # Remove wire, as we've found a clock to set - lappend completed_todos $src - break + foreach src $srcs { + set src_tile_type [lindex $src 0] + + if {$src_tile_type != $tile_type} { + continue + } + + set src_wire [lindex $src 1] + + if { $other_wire == $src_wire } { + set found_target 1 + puts "Interesting net $net already going from $wire to $other_wire." + set_property IS_ROUTE_FIXED 1 $net + dict set used_destinations "$tile/$src_wire" 1 + break + } + } } + + if { $found_target == 1 } { + # Net has an interesting + continue + } + + dict set todo_nets $net [list $tile $wire $gclk_wire] + puts "Interesting net $net (including $wire and $gclk_wire) is being rerouted." + } + } + + set routed_sources [dict create] + + dict for {net tile_wire} $todo_nets { + + if { [get_property IS_ROUTE_FIXED $net] == 1 } { + puts "Net $net is already routed, skipping." + continue + } + + set tile [lindex $tile_wire 0] + set wire [lindex $tile_wire 1] + set gclk_wire [lindex $tile_wire 2] + set srcs [dict get $todo_map $wire] + + puts "" + puts "Rerouting net $net at $tile / $gclk_wire (type $tile_type)" + + set tile_type [get_property TILE_TYPE [get_tiles $tile]] + regexp "CLK_HROW_CK_IN_(\[LR\])\[0-9\]+" $gclk_wire match lr + + set todos {} + foreach src $srcs { + set src_tile_type [lindex $src 0] + if {$src_tile_type != $tile_type} { + continue + } + + set src_wire [lindex $src 1] + + if [regexp "CLK_HROW_CK_IN_$lr\[0-9\]+" $src_wire] { + lappend todos $src_wire + } + } + + if {[llength $todos] == 0} { + puts "No inputs for net $net." + dict set used_destinations "$tile/$gclk_wire" 1 + continue + } + + puts "All todos for $tile_type / $wire" + foreach src_wire $todos { + puts " - $src_wire" + } + + + # Find an input in the todo list that this can can drive. + set set_new_route 0 + foreach src_wire $todos { + if { [dict exists $used_destinations "$tile/$src_wire"] } { + puts "Not routing to $tile / $src_wire, in use." + continue + } + + puts "Attempting to route to $src_wire for net $net." + + set target_wire [get_wires "$tile/$src_wire"] + set target_node [get_nodes -of_objects $target_wire] + if {[llength $target_node] == 0} { + continue + } + + set origin_node [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]] + + if [dict exists $routed_sources $origin_node] { + puts "Skip net $net, already routed." + continue + } + + route_design -unroute -nets $net + + set old_nets [get_nets -of_objects $target_node] + if { $old_nets != {} } { + puts "Unrouting $old_nets" + route_design -unroute -nets $old_nets + } + + set old_nets [get_nets -of_objects $origin_node] + if { $old_nets != {} } { + puts "Unrouting $old_nets" + route_design -unroute -nets $old_nets + } + + 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" + + # Only need to set route to one of the destinations. + # Router will handle the rest. + set_property FIXED_ROUTE $new_route $net + + dict set used_destinations "$tile/$src_wire" 1 + dict set routed_sources "$origin_node" 1 + set set_new_route 1 + break } } } @@ -151,6 +241,7 @@ proc run {} { 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 IS_ENABLED 0 [get_drc_checks {REQP-13}] set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets] diff --git a/fuzzers/041-clk-hrow-pips/merge_clk_entries.py b/fuzzers/041-clk-hrow-pips/merge_clk_entries.py deleted file mode 100644 index 1ddd2ebe..00000000 --- a/fuzzers/041-clk-hrow-pips/merge_clk_entries.py +++ /dev/null @@ -1,89 +0,0 @@ -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() - - output_features = [] - 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:]) - - # No post-processing on _ACTIVE bits. - if feature.endswith('_ACTIVE'): - output_features.append(l.strip()) - continue - - 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 l in output_features: - print(l, file=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 index d1af528c..06aba163 100644 --- a/fuzzers/041-clk-hrow-pips/output_cmt.tcl +++ b/fuzzers/041-clk-hrow-pips/output_cmt.tcl @@ -3,7 +3,7 @@ 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_type {MMCME2_ADV PLLE2_ADV BUFHCE BUFR} { foreach site [get_sites -filter "SITE_TYPE == $site_type"] { puts $fp "$site,[get_property CLOCK_REGION $site]" } diff --git a/fuzzers/041-clk-hrow-pips/top.py b/fuzzers/041-clk-hrow-pips/top.py index 74c8d2de..472eacd8 100644 --- a/fuzzers/041-clk-hrow-pips/top.py +++ b/fuzzers/041-clk-hrow-pips/top.py @@ -1,11 +1,17 @@ """ Emits top.v's for various BUFHCE routing inputs. """ import os import random +import re random.seed(int(os.getenv("SEED"), 16)) from prjxray import util +from prjxray import verilog from prjxray.db import Database +from prjxray.lut_maker import LutMaker +from io import StringIO CMT_XY_FUN = util.create_xy_fun(prefix='') +BUFGCTRL_XY_FUN = util.create_xy_fun('BUFGCTRL_') +BUFHCE_XY_FUN = util.create_xy_fun('BUFHCE_') def gen_sites(desired_site_type): @@ -17,7 +23,7 @@ def gen_sites(desired_site_type): gridinfo = grid.gridinfo_at_loc(loc) for site, site_type in gridinfo.sites.items(): if site_type == desired_site_type: - yield site + yield loc, gridinfo.tile_type, site def gen_bufhce_sites(): @@ -59,8 +65,10 @@ class ClockSources(object): self.merged_sources = {} self.source_to_cmt = {} self.used_sources_from_cmt = {} + self.sources_by_loc = {} + self.active_cmt_ports = {} - def add_clock_source(self, source, cmt): + def add_clock_source(self, source, cmt, loc=None): """ Adds a source from a specific CMT. cmt='ANY' indicates that this source can be routed to any CMT. @@ -73,6 +81,14 @@ class ClockSources(object): source] == cmt, source self.source_to_cmt[source] = cmt + self.add_bufg_clock_source(source, cmt, loc) + + def add_bufg_clock_source(self, source, cmt, loc): + if loc not in self.sources_by_loc: + self.sources_by_loc[loc] = [] + + self.sources_by_loc[loc].append((cmt, source)) + def get_random_source(self, cmt): """ Get a random source that is routable to the specific CMT. @@ -113,12 +129,88 @@ class ClockSources(object): 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 get_bufg_source(self, loc, tile_type, site, todos, i_wire, used_only): + bufg_sources = [] + + top = '_TOP_' in tile_type + bottom = '_BOT_' in tile_type + + assert top ^ bottom, tile_type + + if top: + for src_loc, cmt_sources in self.sources_by_loc.items(): + if src_loc is None: + continue + if src_loc.grid_y <= loc.grid_y: + bufg_sources.extend(cmt_sources) + elif bottom: + for src_loc, cmt_sources in self.sources_by_loc.items(): + if src_loc is None: + continue + if src_loc.grid_y > loc.grid_y: + bufg_sources.extend(cmt_sources) + + # CLK_HROW_TOP_R_CK_BUFG_CASCO0 -> CLK_BUFG_BUFGCTRL0_I0 + # CLK_HROW_TOP_R_CK_BUFG_CASCO22 -> CLK_BUFG_BUFGCTRL11_I0 + # CLK_HROW_TOP_R_CK_BUFG_CASCO23 -> CLK_BUFG_BUFGCTRL11_I1 + # CLK_HROW_BOT_R_CK_BUFG_CASCO27 -> CLK_BUFG_BUFGCTRL13_I1 + + x, y = BUFGCTRL_XY_FUN(site) + assert x == 0 + y = y % 16 + + assert i_wire in [0, 1], i_wire + + casco_wire = '{tile_type}_CK_BUFG_CASCO{casco_idx}'.format( + tile_type=tile_type.replace('BUFG', 'HROW'), + casco_idx=(y * 2 + i_wire)) + + if casco_wire not in todos: + return None + + target_wires = [] + + need_bufr = False + for src_wire in todos[casco_wire]: + if 'BUFRCLK' in src_wire: + need_bufr = True + break + + for cmt, wire in bufg_sources: + if 'BUFR' in wire: + if need_bufr: + target_wires.append((cmt, wire)) + else: + target_wires.append((cmt, wire)) + + random.shuffle(target_wires) + for cmt, source in target_wires: + if cmt == 'ANY': + return source + else: + # Make sure to not try to import move than 14 sources from + # the CMT, there is limited routing. + if cmt not in self.used_sources_from_cmt: + self.used_sources_from_cmt[cmt] = set() + + if source in self.used_sources_from_cmt[cmt]: + return source + elif used_only: + continue + + if len(self.used_sources_from_cmt[cmt]) < 14: + self.used_sources_from_cmt[cmt].add(source) + return source + else: + continue + + return None + def check_allowed(mmcm_pll_dir, cmt): """ Check whether the CMT specified is in the allowed direction. @@ -135,10 +227,76 @@ def check_allowed(mmcm_pll_dir, cmt): elif mmcm_pll_dir == 'EVEN': x, y = CMT_XY_FUN(cmt) return (x & 1) == 0 + elif mmcm_pll_dir == 'NONE': + return False else: assert False, mmcm_pll_dir +def read_todo(): + dsts = {} + + with open(os.path.join('..', 'todo_all.txt')) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + + if dst not in dsts: + dsts[dst] = set() + + dsts[dst].add(src) + + return dsts + + +def need_int_connections(todos): + for srcs in todos.values(): + for src in srcs: + if re.search('INT_._.', src): + return True + + return False + + +def bufhce_in_todo(todos, site): + if 'BUFHCE' in site: + # CLK_HROW_CK_MUX_OUT_R9 -> X1Y9 + # CLK_HROW_CK_MUX_OUT_L11 -> X0Y35 + x, y = BUFHCE_XY_FUN(site) + y = y % 12 + + if x == 0: + lr = 'L' + elif x == 1: + lr = 'R' + else: + assert False, x + + return 'CLK_HROW_CK_MUX_OUT_{lr}{y}'.format(lr=lr, y=y) in todos + else: + return True + + +def need_gclk_connection(todos, site): + x, y = BUFGCTRL_XY_FUN(site) + assert x == 0 + + src_wire = 'CLK_HROW_R_CK_GCLK{}'.format(y) + for srcs in todos.values(): + if src_wire in srcs: + return True + + return False + + +def only_gclk_left(todos): + for srcs in todos.values(): + for src in srcs: + if 'GCLK' not in src: + return False + + return True + + def main(): """ BUFHCE's can be driven from: @@ -162,28 +320,32 @@ module top(); # 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')) + mmcm_pll_dir = random.choice(('ODD', 'EVEN', 'BOTH', 'NONE')) + + todos = read_todo() + + if only_gclk_left(todos): + mmcm_pll_dir = 'NONE' if not mmcm_pll_only: - for _ in range(2): - clock_sources.add_clock_source('one', 'ANY') - clock_sources.add_clock_source('zero', 'ANY') + if need_int_connections(todos): + for _ in range(10): + 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'): + for loc, _, 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]) + if check_allowed(mmcm_pll_dir, site_to_cmt[site]): + for clk in mmcm_clocks: + clock_sources.add_clock_source(clk, site_to_cmt[site], loc) print( """ @@ -221,17 +383,15 @@ module top(); c12=mmcm_clocks[12], )) - for site in gen_sites('PLLE2_ADV'): + for loc, _, 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]) + if check_allowed(mmcm_pll_dir, site_to_cmt[site]): + for clk in pll_clocks: + clock_sources.add_clock_source(clk, site_to_cmt[site], loc) print( """ @@ -255,45 +415,97 @@ module top(); c5=pll_clocks[5], )) - gclks = [] - for site in sorted(gen_sites("BUFGCTRL"), - key=util.create_xy_fun('BUFGCTRL_')): - wire_name = 'clk_{}'.format(site) - gclks.append(wire_name) - - if not mmcm_pll_only: - clock_sources.add_clock_source(wire_name, 'ANY') - + for loc, _, site in gen_sites('BUFR'): + clock_sources.add_bufg_clock_source( + 'O_{site}'.format(site=site), site_to_cmt[site], loc) print( """ - wire {wire_name}; + wire O_{site}; (* KEEP, DONT_TOUCH, LOC = "{site}" *) - BUFG bufg_{site} ( - .O({wire_name}) + BUFR bufr_{site} ( + .O(O_{site}) + );""".format(site=site)) + + luts = LutMaker() + bufhs = StringIO() + bufgs = StringIO() + + gclks = [] + for _, _, site in sorted(gen_sites("BUFGCTRL"), + key=lambda x: BUFGCTRL_XY_FUN(x[2])): + wire_name = 'gclk_{}'.format(site) + gclks.append(wire_name) + + include_source = True + if mmcm_pll_only: + include_source = False + elif only_gclk_left(todos): + include_source = need_gclk_connection(todos, site) + + if include_source: + clock_sources.add_clock_source(wire_name, 'ANY') + + print(""" + wire {wire_name}; + """.format(wire_name=wire_name)) + print( + """ + wire I1_{site}; + wire I0_{site}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFGCTRL bufg_{site} ( + .O({wire_name}), + .S1({s1net}), + .S0({s0net}), + .IGNORE1({ignore1net}), + .IGNORE0({ignore0net}), + .I1(I1_{site}), + .I0(I0_{site}), + .CE1({ce1net}), + .CE0({ce0net}) ); """.format( site=site, wire_name=wire_name, - )) + s1net=luts.get_next_output_net(), + s0net=luts.get_next_output_net(), + ignore1net=luts.get_next_output_net(), + ignore0net=luts.get_next_output_net(), + ce1net=luts.get_next_output_net(), + ce0net=luts.get_next_output_net(), + ), + file=bufgs) any_bufhce = False 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: + if not bufhce_in_todo(todos, site): continue any_bufhce = True print( """ + wire I_{site}; (* KEEP, DONT_TOUCH, LOC = "{site}" *) BUFHCE buf_{site} ( - .I({wire_name}) + .I(I_{site}) ); - """.format( - site=site, - wire_name=wire_name, - )) + """.format(site=site, ), + file=bufhs) + + if random.random() > .05: + wire_name = clock_sources.get_random_source(site_to_cmt[site]) + + if wire_name is None: + continue + + print( + """ + assign I_{site} = {wire_name};""".format( + site=site, + wire_name=wire_name, + ), + file=bufhs) if not any_bufhce: for tile_name, sites in gen_bufhce_sites(): @@ -301,16 +513,56 @@ module top(); print( """ (* KEEP, DONT_TOUCH, LOC = "{site}" *) - BUFHCE buf_{site} ( + BUFHCE #( + .INIT_OUT({INIT_OUT}), + .CE_TYPE({CE_TYPE}), + .IS_CE_INVERTED({IS_CE_INVERTED}) + ) buf_{site} ( .I({wire_name}) ); """.format( + INIT_OUT=random.randint(0, 1), + CE_TYPE=verilog.quote( + random.choice(('SYNC', 'ASYNC'))), + IS_CE_INVERTED=random.randint(0, 1), site=site, wire_name=gclks[0], )) break break + for l in luts.create_wires_and_luts(): + print(l) + + print(bufhs.getvalue()) + print(bufgs.getvalue()) + + used_only = random.random() < .25 + + for loc, tile_type, site in sorted(gen_sites("BUFGCTRL"), + key=lambda x: BUFGCTRL_XY_FUN(x[2])): + if random.randint(0, 1): + wire_name = clock_sources.get_bufg_source( + loc, tile_type, site, todos, 1, used_only) + if wire_name is not None: + print( + """ + assign I1_{site} = {wire_name};""".format( + site=site, + wire_name=wire_name, + )) + + if random.randint(0, 1): + wire_name = clock_sources.get_bufg_source( + loc, tile_type, site, todos, 0, used_only) + if wire_name is not None: + print( + """ + assign I0_{site} = {wire_name};""".format( + site=site, + wire_name=wire_name, + )) + print("endmodule") diff --git a/fuzzers/044-clk-bufg-pips/Makefile b/fuzzers/044-clk-bufg-pips/Makefile new file mode 100644 index 00000000..0821b3f1 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/Makefile @@ -0,0 +1,61 @@ +export FUZDIR=$(shell pwd) +PIP_TYPE?=clk_bufg +PIPLIST_TCL=$(FUZDIR)/clk_bufg_pip_list.tcl +TODO_RE=".*" + +MAKETODO_FLAGS=--sides "bot_r,top_r" --pip-type ${PIP_TYPE} --seg-type clk_bufg --re $(TODO_RE) +N = 50 + +# These PIPs all appear to be either a 1 bit solutions. +SEGMATCH_FLAGS=-c 2 +SPECIMENS_DEPS=build/cmt_regions.csv +A_PIPLIST=clk_bufg_bot_r.txt + +include ../pip_loop.mk + +build/segbits_clk_bufg_top_r.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_clk_bufg_top_r.rdb \ + $(shell find build -name segdata_clk_bufg_top_r.txt) + +build/segbits_clk_bufg_bot_r.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_clk_bufg_bot_r.rdb \ + $(shell find build -name segdata_clk_bufg_bot_r.txt) + + +database: build/segbits_clk_bufg_top_r.rdb build/segbits_clk_bufg_bot_r.rdb + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_clk_bufg_bot_r.rdb \ + --seg-fn-out build/segbits_clk_bufg_bot_r.db + + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_clk_bufg_top_r.rdb \ + --seg-fn-out build/segbits_clk_bufg_top_r.db + + # Keep a copy to track iter progress + cp build/segbits_clk_bufg_top_r.rdb build/$(ITER)/segbits_clk_bufg_top_r.rdb + cp build/segbits_clk_bufg_top_r.db build/$(ITER)/segbits_clk_bufg_top_r.db + cp build/segbits_clk_bufg_bot_r.rdb build/$(ITER)/segbits_clk_bufg_bot_r.rdb + cp build/segbits_clk_bufg_bot_r.db build/$(ITER)/segbits_clk_bufg_bot_r.db + + + ${XRAY_MASKMERGE} build/mask_clk_bufg_top_r.db \ + $(shell find build -name segdata_clk_bufg_top_r.txt) + ${XRAY_MASKMERGE} build/mask_clk_bufg_bot_r.db \ + $(shell find build -name segdata_clk_bufg_bot_r.txt) + + # 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_bufg_top_r build/segbits_clk_bufg_top_r.db + XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} clk_bufg_bot_r build/segbits_clk_bufg_bot_r.db + +build/cmt_regions.csv: output_cmt.tcl + mkdir -p build + cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/output_cmt.tcl + +pushdb: database + ${XRAY_MERGEDB} clk_bufg_bot_r build/segbits_clk_bufg_bot_r.db + ${XRAY_MERGEDB} clk_bufg_top_r build/segbits_clk_bufg_top_r.db + ${XRAY_MERGEDB} mask_clk_bufg_bot_r build/mask_clk_bufg_bot_r.db + ${XRAY_MERGEDB} mask_clk_bufg_top_r build/mask_clk_bufg_top_r.db + +.PHONY: database pushdb diff --git a/fuzzers/044-clk-bufg-pips/README.md b/fuzzers/044-clk-bufg-pips/README.md new file mode 100644 index 00000000..3073e716 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/README.md @@ -0,0 +1,4 @@ +BUFG interconnect fuzzer +======================== + +Solves pips located within the BUFG switch box. diff --git a/fuzzers/044-clk-bufg-pips/bits.dbf b/fuzzers/044-clk-bufg-pips/bits.dbf new file mode 100644 index 00000000..3ba9bed1 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/bits.dbf @@ -0,0 +1,33 @@ +# Generated from build_zdb.py +26_07 26_08 27_06,CLK_BUFG.CLK_BUFG_BUFGCTRL0_I0.CLK_BUFG_IMUX28_0 +26_04 26_05 27_05,CLK_BUFG.CLK_BUFG_BUFGCTRL0_I1.CLK_BUFG_IMUX28_0 +26_167 26_168 27_166,CLK_BUFG.CLK_BUFG_BUFGCTRL10_I0.CLK_BUFG_IMUX30_2 +26_164 26_165 27_165,CLK_BUFG.CLK_BUFG_BUFGCTRL10_I1.CLK_BUFG_IMUX30_2 +26_183 26_184 27_182,CLK_BUFG.CLK_BUFG_BUFGCTRL11_I0.CLK_BUFG_IMUX31_2 +26_180 26_181 27_181,CLK_BUFG.CLK_BUFG_BUFGCTRL11_I1.CLK_BUFG_IMUX31_2 +26_199 26_200 27_198,CLK_BUFG.CLK_BUFG_BUFGCTRL12_I0.CLK_BUFG_IMUX28_3 +26_196 26_197 27_197,CLK_BUFG.CLK_BUFG_BUFGCTRL12_I1.CLK_BUFG_IMUX28_3 +26_215 26_216 27_214,CLK_BUFG.CLK_BUFG_BUFGCTRL13_I0.CLK_BUFG_IMUX29_3 +26_212 26_213 27_213,CLK_BUFG.CLK_BUFG_BUFGCTRL13_I1.CLK_BUFG_IMUX29_3 +26_231 26_232 27_230,CLK_BUFG.CLK_BUFG_BUFGCTRL14_I0.CLK_BUFG_IMUX30_3 +26_228 26_229 27_229,CLK_BUFG.CLK_BUFG_BUFGCTRL14_I1.CLK_BUFG_IMUX30_3 +26_247 26_248 27_246,CLK_BUFG.CLK_BUFG_BUFGCTRL15_I0.CLK_BUFG_IMUX31_3 +26_244 26_245 27_245,CLK_BUFG.CLK_BUFG_BUFGCTRL15_I1.CLK_BUFG_IMUX31_3 +26_23 26_24 27_22,CLK_BUFG.CLK_BUFG_BUFGCTRL1_I0.CLK_BUFG_IMUX29_0 +26_20 26_21 27_21,CLK_BUFG.CLK_BUFG_BUFGCTRL1_I1.CLK_BUFG_IMUX29_0 +26_39 26_40 27_38,CLK_BUFG.CLK_BUFG_BUFGCTRL2_I0.CLK_BUFG_IMUX30_0 +26_36 26_37 27_37,CLK_BUFG.CLK_BUFG_BUFGCTRL2_I1.CLK_BUFG_IMUX30_0 +26_55 26_56 27_54,CLK_BUFG.CLK_BUFG_BUFGCTRL3_I0.CLK_BUFG_IMUX31_0 +26_52 26_53 27_53,CLK_BUFG.CLK_BUFG_BUFGCTRL3_I1.CLK_BUFG_IMUX31_0 +26_71 26_72 27_70,CLK_BUFG.CLK_BUFG_BUFGCTRL4_I0.CLK_BUFG_IMUX28_1 +26_68 26_69 27_69,CLK_BUFG.CLK_BUFG_BUFGCTRL4_I1.CLK_BUFG_IMUX28_1 +26_87 26_88 27_86,CLK_BUFG.CLK_BUFG_BUFGCTRL5_I0.CLK_BUFG_IMUX29_1 +26_84 26_85 27_85,CLK_BUFG.CLK_BUFG_BUFGCTRL5_I1.CLK_BUFG_IMUX29_1 +26_103 26_104 27_102,CLK_BUFG.CLK_BUFG_BUFGCTRL6_I0.CLK_BUFG_IMUX30_1 +26_100 26_101 27_101,CLK_BUFG.CLK_BUFG_BUFGCTRL6_I1.CLK_BUFG_IMUX30_1 +26_119 26_120 27_118,CLK_BUFG.CLK_BUFG_BUFGCTRL7_I0.CLK_BUFG_IMUX31_1 +26_116 26_117 27_117,CLK_BUFG.CLK_BUFG_BUFGCTRL7_I1.CLK_BUFG_IMUX31_1 +26_135 26_136 27_134,CLK_BUFG.CLK_BUFG_BUFGCTRL8_I0.CLK_BUFG_IMUX28_2 +26_132 26_133 27_133,CLK_BUFG.CLK_BUFG_BUFGCTRL8_I1.CLK_BUFG_IMUX28_2 +26_151 26_152 27_150,CLK_BUFG.CLK_BUFG_BUFGCTRL9_I0.CLK_BUFG_IMUX29_2 +26_148 26_149 27_149,CLK_BUFG.CLK_BUFG_BUFGCTRL9_I1.CLK_BUFG_IMUX29_2 diff --git a/fuzzers/044-clk-bufg-pips/build_zdb.py b/fuzzers/044-clk-bufg-pips/build_zdb.py new file mode 100644 index 00000000..8f8d8fe6 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/build_zdb.py @@ -0,0 +1,71 @@ +""" Tool for building zero db file for BUFG pips. + +This requires that the rdb files be good enough to identify all the 0 candidate +features, which may take multiple manual iterations. Manual iterations can +be running like: + +make ITER= -j database + +And then invoking: +python3 build_zdb.py build/segbits_clk_bufg_bot_r.rdb build/segbits_clk_bufg_top_r.rdb > bits.dbf + +will successed if and only if the rdb is complete enough. + +bits.dbf is committed, so this utility should only be needed to document the +process. + +""" +import argparse + + +def main(): + parser = argparse.ArgumentParser("Form ZDB groups for BUFG.") + + parser.add_argument('bot_r') + parser.add_argument('top_r') + + args = parser.parse_args() + + groups = {} + + with open(args.bot_r) as f: + for l in f: + parts = l.strip().split(' ') + feature = parts[0] + bits = parts[1:] + tile_type, dst, src = feature.split('.') + + assert tile_type == "CLK_BUFG" + + if dst not in groups: + groups[dst] = {} + + groups[dst][src] = bits + + print('# Generated from build_zdb.py') + + for dst in groups: + if len(groups[dst]) == 1: + continue + + bits = set() + zero_feature = None + for src in groups[dst]: + if groups[dst][src][0] == '<0': + assert zero_feature is None + zero_feature = src + else: + bits |= set(groups[dst][src]) + + assert zero_feature is not None, dst + + print( + '{bits},{type}.{dst}.{src}'.format( + bits=' '.join(sorted(bits)), + type='CLK_BUFG', + dst=dst, + src=zero_feature)) + + +if __name__ == "__main__": + main() diff --git a/fuzzers/044-clk-bufg-pips/clk_bufg_pip_list.tcl b/fuzzers/044-clk-bufg-pips/clk_bufg_pip_list.tcl new file mode 100644 index 00000000..c870f353 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/clk_bufg_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_BUFG_TOP_R clk_bufg_top_r.txt +print_tile_pips CLK_BUFG_BOT_R clk_bufg_bot_r.txt diff --git a/fuzzers/044-clk-bufg-pips/generate.py b/fuzzers/044-clk-bufg-pips/generate.py new file mode 100644 index 00000000..58b895a2 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/generate.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +from prjxray.segmaker import Segmaker +import os +import os.path + + +def bitfilter(frame, word): + if frame < 26: + return False + + return True + + +def main(): + segmk = Segmaker("design.bits") + + tiledata = {} + pipdata = {} + ignpip = set() + + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'clk_bufg', 'clk_bufg_bot_r.txt')) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] + + pipdata[tile_type].append((src, dst)) + + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'clk_bufg', 'clk_bufg_top_r.txt')) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] + + pipdata[tile_type].append((src, dst)) + + 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_BUFG'): + continue + + if tile.startswith('CLK_BUFG_REBUF'): + continue + + pip_prefix, _ = pip.split(".") + tile_from_pip, tile_type = pip_prefix.split('/') + assert tile == tile_from_pip + _, src = src.split("/") + _, dst = dst.split("/") + pnum = int(pnum) + pdir = int(pdir) + + if tile not in tiledata: + tiledata[tile] = { + "type": tile_type, + "pips": set(), + "srcs": set(), + "dsts": set() + } + + tiledata[tile]["pips"].add((src, dst)) + tiledata[tile]["srcs"].add(src) + tiledata[tile]["dsts"].add(dst) + + if pdir == 0: + tiledata[tile]["srcs"].add(dst) + tiledata[tile]["dsts"].add(src) + + if pnum == 1 or pdir == 0: + ignpip.add((src, dst)) + + for tile, pips_srcs_dsts in tiledata.items(): + tile_type = pips_srcs_dsts["type"] + pips = pips_srcs_dsts["pips"] + + for src, dst in pipdata[tile_type]: + if (src, dst) in ignpip: + pass + elif (src, dst) in pips: + segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 1) + elif dst not in tiledata[tile]["dsts"]: + segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 0) + + segmk.compile(bitfilter=bitfilter) + segmk.write() + + +if __name__ == "__main__": + main() diff --git a/fuzzers/044-clk-bufg-pips/generate.tcl b/fuzzers/044-clk-bufg-pips/generate.tcl new file mode 100644 index 00000000..f3bbef42 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/generate.tcl @@ -0,0 +1,46 @@ +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 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-123}] + + set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets] + + place_design + route_design + + write_checkpoint -force design.dcp + write_bitstream -force design.bit + write_pip_txtdata design.txt +} + +run diff --git a/fuzzers/044-clk-bufg-pips/output_cmt.tcl b/fuzzers/044-clk-bufg-pips/output_cmt.tcl new file mode 100644 index 00000000..540c8143 --- /dev/null +++ b/fuzzers/044-clk-bufg-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 BUFHCE} { + foreach site [get_sites -filter "SITE_TYPE == $site_type"] { + puts $fp "$site,[get_property CLOCK_REGION $site]" + } +} +close $fp diff --git a/fuzzers/044-clk-bufg-pips/top.py b/fuzzers/044-clk-bufg-pips/top.py new file mode 100644 index 00000000..5f1a38c1 --- /dev/null +++ b/fuzzers/044-clk-bufg-pips/top.py @@ -0,0 +1,337 @@ +""" 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.lut_maker import LutMaker +from prjxray.db import Database +from io import StringIO + +CMT_XY_FUN = util.create_xy_fun(prefix='') +BUFGCTRL_XY_FUN = util.create_xy_fun('BUFGCTRL_') + + +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 main(): + """ + BUFG's can be driven from: + + Interconnect + HROW cascade + + """ + + print( + ''' +module top(); + (* KEEP, DONT_TOUCH *) + LUT6 dummy(); + ''') + + site_to_cmt = dict(read_site_to_cmt()) + luts = LutMaker() + wires = StringIO() + bufgs = StringIO() + + clock_sources = ClockSources() + + db = Database(util.get_db_root()) + grid = db.grid() + + def gen_sites(desired_site_type): + 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 tile_name, site + + for _, site in gen_sites('MMCME2_ADV'): + mmcm_clocks = [ + 'mmcm_clock_{site}_{idx}'.format(site=site, idx=idx) + for idx in range(13) + ] + + 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 sorted(gen_sites("BUFGCTRL"), + key=lambda x: BUFGCTRL_XY_FUN(x[1])): + print( + """ + wire O_{site}; + wire S1_{site}; + wire S0_{site}; + wire IGNORE1_{site}; + wire IGNORE0_{site}; + wire I1_{site}; + wire I0_{site}; + wire CE1_{site}; + wire CE0_{site}; + """.format(site=site), + file=wires) + + print( + """ + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFGCTRL bufg_{site} ( + .O(O_{site}), + .S1(S1_{site}), + .S0(S0_{site}), + .IGNORE1(IGNORE1_{site}), + .IGNORE0(IGNORE0_{site}), + .I1(I1_{site}), + .I0(I0_{site}), + .CE1(CE1_{site}), + .CE0(CE0_{site}) + ); + """.format(site=site), + file=bufgs) + """ BUFG clock sources: + + 2 from interconnect + Output of BUFG +/- 1 + Cascade in (e.g. PLL, MMCM) + + """ + + CLOCK_CHOICES = ( + 'LUT', + 'BUFG_+1', + 'BUFG_-1', + 'CASCADE', + ) + + def find_bufg_cmt(tile): + if '_BOT_' in tile: + inc = 1 + else: + inc = -1 + + loc = grid.loc_of_tilename(tile) + + offset = 1 + + while True: + gridinfo = grid.gridinfo_at_loc( + (loc.grid_x, loc.grid_y + offset * inc)) + if gridinfo.tile_type.startswith('CLK_HROW_'): + return site_to_cmt[list(gridinfo.sites.keys())[0]] + + offset += 1 + + def get_clock_net(tile, site, source_type): + if source_type == 'LUT': + return luts.get_next_output_net() + elif source_type == 'BUFG_+1': + x, y = BUFGCTRL_XY_FUN(site) + + target_y = y + 1 + max_y = ((y // 16) + 1) * 16 + + if target_y >= max_y: + target_y -= 16 + + return 'O_BUFGCTRL_X{x}Y{y}'.format(x=x, y=target_y) + elif source_type == 'BUFG_-1': + x, y = BUFGCTRL_XY_FUN(site) + + target_y = y - 1 + min_y = (y // 16) * 16 + + if target_y < min_y: + target_y += 16 + + return 'O_BUFGCTRL_X{x}Y{y}'.format(x=x, y=target_y) + elif source_type == 'CASCADE': + cmt = find_bufg_cmt(tile) + return clock_sources.get_random_source(cmt) + else: + assert False, source_type + + for tile, site in sorted(gen_sites("BUFGCTRL"), + key=lambda x: BUFGCTRL_XY_FUN(x[1])): + if random.randint(0, 1): + print( + """ + assign I0_{site} = {i0_net};""".format( + site=site, + i0_net=get_clock_net( + tile, site, random.choice(CLOCK_CHOICES))), + file=bufgs) + + if random.randint(0, 1): + print( + """ + assign I1_{site} = {i1_net};""".format( + site=site, + i1_net=get_clock_net( + tile, site, random.choice(CLOCK_CHOICES))), + file=bufgs) + + print( + """ + assign S0_{site} = {s0_net}; + assign S1_{site} = {s1_net}; + assign IGNORE0_{site} = {ignore0_net}; + assign IGNORE1_{site} = {ignore1_net}; + assign CE0_{site} = {ce0_net}; + assign CE1_{site} = {ce1_net}; + """.format( + site=site, + s0_net=luts.get_next_output_net(), + s1_net=luts.get_next_output_net(), + ignore0_net=luts.get_next_output_net(), + ignore1_net=luts.get_next_output_net(), + ce0_net=luts.get_next_output_net(), + ce1_net=luts.get_next_output_net(), + ), + file=bufgs) + + for l in luts.create_wires_and_luts(): + print(l) + + print(wires.getvalue()) + print(bufgs.getvalue()) + + itr = iter(gen_sites('BUFHCE')) + + for tile, site in sorted(gen_sites("BUFGCTRL"), + key=lambda x: BUFGCTRL_XY_FUN(x[1])): + if random.randint(0, 1): + _, bufhce_site = next(itr) + + print( + """ + (* KEEP, DONT_TOUCH, LOC = "{bufhce_site}" *) + BUFHCE bufhce_{bufhce_site} ( + .I(O_{site}) + );""".format( + site=site, + bufhce_site=bufhce_site, + )) + + print("endmodule") + + +if __name__ == '__main__': + main() diff --git a/fuzzers/045-hclk-cmt-pips/Makefile b/fuzzers/045-hclk-cmt-pips/Makefile new file mode 100644 index 00000000..0406aa5a --- /dev/null +++ b/fuzzers/045-hclk-cmt-pips/Makefile @@ -0,0 +1,64 @@ +export FUZDIR=$(shell pwd) +PIP_TYPE?=hclk_cmt +PIPLIST_TCL=$(FUZDIR)/hclk_cmt_pip_list.tcl +TODO_RE=".*" +# Skipped pips: +# - FREQ_REF is only used by interconnect clocks, so low likelyhood of usage. +# A BUFR can be used to clock divide if needed +# - PHSR PIPs are connected to PHASER sites, which are undocumented, so avoid +# for now. +EXCLUDE_RE="(^.*LEAF)|(^.*BUFMR)|(^.*FREQ_REF)|(^.*PHSR)|(^.*CLK_PLL7)|(^.*CLK_MMCM13)" + +MAKETODO_FLAGS=--sides ",l" --pip-type ${PIP_TYPE} --seg-type ${PIP_TYPE} --re $(TODO_RE) --exclude-re $(EXCLUDE_RE) +N = 50 + +# These PIPs all appear to be either a 1 bit solutions. +SEGMATCH_FLAGS=-c 2 +SPECIMENS_DEPS=build/cmt_regions.csv +A_PIPLIST=hclk_cmt.txt + +include ../pip_loop.mk + +build/segbits_hclk_cmt.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_hclk_cmt.rdb \ + $(shell find build -name segdata_hclk_cmt.txt) + +build/segbits_hclk_cmt_l.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_hclk_cmt_l.rdb \ + $(shell find build -name segdata_hclk_cmt_l.txt) + +database: build/segbits_hclk_cmt.rdb build/segbits_hclk_cmt_l.rdb + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_hclk_cmt.rdb \ + --seg-fn-out build/segbits_hclk_cmt.db + + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_hclk_cmt_l.rdb \ + --seg-fn-out build/segbits_hclk_cmt_l.db + + # Keep a copy to track iter progress + cp build/segbits_hclk_cmt.rdb build/$(ITER)/segbits_hclk_cmt.rdb + cp build/segbits_hclk_cmt_l.rdb build/$(ITER)/segbits_hclk_cmt_l.rdb + + + ${XRAY_MASKMERGE} build/mask_hclk_cmt.db \ + $(shell find build -name segdata_hclk_cmt.txt) + ${XRAY_MASKMERGE} build/mask_hclk_cmt_l.db \ + $(shell find build -name segdata_hclk_cmt_l.txt) + + # 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} hclk_cmt build/segbits_hclk_cmt.db + XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} hclk_cmt_l build/segbits_hclk_cmt_l.db + +build/cmt_regions.csv: output_cmt.tcl + mkdir -p build + cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/output_cmt.tcl + +pushdb: database + ${XRAY_MERGEDB} hclk_cmt build/segbits_hclk_cmt.db + ${XRAY_MERGEDB} hclk_cmt_l build/segbits_hclk_cmt_l.db + ${XRAY_MERGEDB} mask_hclk_cmt build/mask_hclk_cmt.db + ${XRAY_MERGEDB} mask_hclk_cmt_l build/mask_hclk_cmt_l.db + +.PHONY: database pushdb diff --git a/fuzzers/045-hclk-cmt-pips/README.md b/fuzzers/045-hclk-cmt-pips/README.md new file mode 100644 index 00000000..941e0636 --- /dev/null +++ b/fuzzers/045-hclk-cmt-pips/README.md @@ -0,0 +1,4 @@ +HCLK\_CMT interconnect fuzzer +============================= + +Solves pips located within the HCLK\_CMT switch box. diff --git a/fuzzers/045-hclk-cmt-pips/bits.dbf b/fuzzers/045-hclk-cmt-pips/bits.dbf new file mode 100644 index 00000000..e69de29b diff --git a/fuzzers/045-hclk-cmt-pips/generate.py b/fuzzers/045-hclk-cmt-pips/generate.py new file mode 100644 index 00000000..492e3f28 --- /dev/null +++ b/fuzzers/045-hclk-cmt-pips/generate.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +from prjxray.segmaker import Segmaker +import os +import os.path + + +def bitfilter(frame, word): + if frame < 26: + return False + + return True + + +def main(): + segmk = Segmaker("design.bits") + + tiledata = {} + pipdata = {} + ignpip = set() + tile_ports = {} + + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'hclk_cmt', 'hclk_cmt.txt')) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] + tile_ports[tile_type] = set() + + pipdata[tile_type].append((src, dst)) + tile_ports[tile_type].add(src) + tile_ports[tile_type].add(dst) + + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'hclk_cmt', 'hclk_cmt_l.txt')) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] + tile_ports[tile_type] = set() + + pipdata[tile_type].append((src, dst)) + tile_ports[tile_type].add(src) + tile_ports[tile_type].add(dst) + + 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('HCLK_CMT'): + continue + + pip_prefix, _ = pip.split(".") + tile_from_pip, tile_type = pip_prefix.split('/') + assert tile == tile_from_pip + _, src = src.split("/") + _, dst = dst.split("/") + pnum = int(pnum) + pdir = int(pdir) + + if tile not in tiledata: + tiledata[tile] = { + "type": tile_type, + "pips": set(), + "srcs": set(), + "dsts": set() + } + + tiledata[tile]["pips"].add((src, dst)) + tiledata[tile]["srcs"].add(src) + tiledata[tile]["dsts"].add(dst) + + if pdir == 0: + tiledata[tile]["srcs"].add(dst) + tiledata[tile]["dsts"].add(src) + + if pnum == 1 or pdir == 0: + ignpip.add((src, dst)) + + for tile, pips_srcs_dsts in tiledata.items(): + tile_type = pips_srcs_dsts["type"] + pips = pips_srcs_dsts["pips"] + + for src, dst in pipdata[tile_type]: + if (src, dst) in ignpip: + pass + elif (src, dst) in pips: + segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 1) + elif dst not in tiledata[tile]["dsts"]: + segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 0) + + for port in tile_ports[tile_type]: + if port in tiledata[tile]["dsts"] or port in tiledata[tile]["srcs"]: + segmk.add_tile_tag(tile, "{}_ACTIVE".format(port), 1) + else: + segmk.add_tile_tag(tile, "{}_ACTIVE".format(port), 0) + + segmk.compile(bitfilter=bitfilter) + segmk.write() + + +if __name__ == "__main__": + main() diff --git a/fuzzers/045-hclk-cmt-pips/generate.tcl b/fuzzers/045-hclk-cmt-pips/generate.tcl new file mode 100644 index 00000000..fce56c46 --- /dev/null +++ b/fuzzers/045-hclk-cmt-pips/generate.tcl @@ -0,0 +1,233 @@ +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_all.txt" r] + + # Create map of pip source to remaining destinations for that pip + set todo_map [dict create] + for {gets $fp line} {$line != ""} {gets $fp line} { + set parts [split $line .] + dict lappend todo_map [lindex $parts 2] [list [lindex $parts 0] [lindex $parts 1]] + } + close $fp + return $todo_map +} + +proc route_todo {} { + puts "Checking TODO's" + set todo_map [load_todo] + + set nets [get_nets] + + set todo_nets [dict create] + set used_destinations [dict create] + + foreach net $nets { + # Check to see if this net is one we are interested in + set wires [get_wires -of_objects $net -filter {TILE_NAME =~ *HCLK_CMT*}] + + set is_gclk_net 0 + foreach wire $wires { + if [regexp "HCLK_CMT_MUX_CLK_\[0-9\]+" $wire] { + set is_gclk_net 1 + break + } + if [regexp "HCLK_CMT_CK_IN\[0-9\]+" $wire] { + set is_gclk_net 1 + break + } + } + + if {$is_gclk_net == 0} { + puts "$net not going to a HCLK port, skipping." + continue + } + + foreach wire $wires { + set tile [lindex [split $wire /] 0] + set wire [lindex [split $wire /] 1] + set tile_type [get_property TILE_TYPE [get_tiles $tile]] + + if { ![dict exists $todo_map $wire] } { + continue + } + + set dsts [dict get $todo_map $wire] + + # This net is interesting, see if it is already going somewhere we + # want. + set found_target 0 + foreach other_wire $wires { + if { $found_target == 1 } { + break + } + + set other_wire [lindex [split $other_wire /] 1] + + if { $wire == $other_wire } { + continue + } + + foreach dst $dsts { + set dst_tile_type [lindex $dst 0] + + if {$dst_tile_type != $tile_type} { + continue + } + + set dst_wire [lindex $dst 1] + + if { $other_wire == $dst } { + set found_target 1 + puts "Interesting net $net already going from $wire to $other_wire." + set_property IS_ROUTE_FIXED 1 $net + dict set used_destinations "$tile/$dst_wire" 1 + break + } + } + } + + if { $found_target == 1 } { + # Net has an interesting + continue + } + + dict set todo_nets $net [list $tile $wire] + puts "Interesting net $net (including $wire) is being rerouted." + } + } + + dict for {net tile_wire} $todo_nets { + set tile [lindex $tile_wire 0] + set wire [lindex $tile_wire 1] + set dsts [dict get $todo_map $wire] + + puts "Rerouting net $net at $tile / $wire (type $tile_type)" + + set tile_type [get_property TILE_TYPE [get_tiles $tile]] + + set todos {} + foreach dst $dsts { + set dst_tile_type [lindex $dst 0] + if {$dst_tile_type != $tile_type} { + continue + } + + set dst_wire [lindex $dst 1] + + set is_gclk_net 0 + if [regexp "HCLK_CMT_MUX_CLK_\[0-9\]+" $dst_wire] { + set is_gclk_net 1 + } + if [regexp "HCLK_CMT_CK_IN\[0-9\]+" $dst_wire] { + set is_gclk_net 1 + } + + if {$is_gclk_net == 0} { + continue + } + + lappend todos $dst_wire + } + + puts "All todos for $tile_type / $wire" + foreach dst_wire $todos { + puts " - $dst_wire" + } + + route_design -unroute -nets $net + + # Find an input in the todo list that this can can drive. + foreach dst_wire $todos { + if { [dict exists $used_destinations "$tile/$dst_wire"] } { + puts "Not routing to $tile / $dst_wire, in use." + continue + } + + puts "Attempting to route to $dst_wire for net $net." + + set target_wire [get_wires "$tile/$dst_wire"] + set target_node [get_nodes -of_objects $target_wire] + if {[llength $target_node] == 0} { + error "Failed to find node for $tile/$dst_wire." + } + + set old_nets [get_nets -of_objects $target_node] + + if { $old_nets != {} } { + route_design -unroute -nets $old_nets + } + + set origin_node [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $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" + + # Only need to set route to one of the destinations. + # Router will handle the rest. + set_property FIXED_ROUTE $new_route $net + + dict set used_destinations "$tile/$dst_wire" 1 + 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 {PDRC-29}] + set_property IS_ENABLED 0 [get_drc_checks {PDRC-38}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-13}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-123}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-161}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-1575}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-1684}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-1712}] + set_property IS_ENABLED 0 [get_drc_checks {AVAL-50}] + set_property IS_ENABLED 0 [get_drc_checks {AVAL-78}] + set_property IS_ENABLED 0 [get_drc_checks {AVAL-81}] + + + 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/045-hclk-cmt-pips/hclk_cmt_pip_list.tcl b/fuzzers/045-hclk-cmt-pips/hclk_cmt_pip_list.tcl new file mode 100644 index 00000000..a41b692b --- /dev/null +++ b/fuzzers/045-hclk-cmt-pips/hclk_cmt_pip_list.tcl @@ -0,0 +1,38 @@ +proc print_tile_pips {tile_type filename} { + set fp [open $filename w] + set pips [dict create] + foreach tile [get_tiles -filter "TYPE == $tile_type"] { + puts "Dumping PIPs for tile $tile ($tile_type) to $filename." + foreach pip [lsort [get_pips -of_objects $tile]] { + set src [get_wires -uphill -of_objects $pip] + set dst [get_wires -downhill -of_objects $pip] + + # Skip pips with disconnected nodes + set src_node [get_nodes -of_objects $src] + if { $src_node == {} } { + continue + } + + set dst_node [get_nodes -of_objects $src] + if { $dst_node == {} } { + continue + } + + if {[llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst]]] != 1} { + set pip_string "$tile_type.[regsub {.*/} $dst ""].[regsub {.*/} $src ""]" + if ![dict exists $pips $pip_string] { + puts $fp $pip_string + dict set pips $pip_string 1 + } + } + } + } + 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 HCLK_CMT_L hclk_cmt_l.txt +print_tile_pips HCLK_CMT hclk_cmt.txt diff --git a/fuzzers/045-hclk-cmt-pips/output_cmt.tcl b/fuzzers/045-hclk-cmt-pips/output_cmt.tcl new file mode 100644 index 00000000..1ad5c1a0 --- /dev/null +++ b/fuzzers/045-hclk-cmt-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 BUFMRCE BUFHCE IOB33M IOB18M BUFR} { + foreach site [get_sites -filter "SITE_TYPE == $site_type"] { + puts $fp "$site,[get_property CLOCK_REGION $site]" + } +} +close $fp diff --git a/fuzzers/045-hclk-cmt-pips/top.py b/fuzzers/045-hclk-cmt-pips/top.py new file mode 100644 index 00000000..5e67b0a2 --- /dev/null +++ b/fuzzers/045-hclk-cmt-pips/top.py @@ -0,0 +1,449 @@ +""" 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.lut_maker import LutMaker +from prjxray.db import Database +from io import StringIO + +CMT_XY_FUN = util.create_xy_fun(prefix='') + + +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.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) + self.source_to_cmt[source] = cmt + + def get_random_source( + self, cmt, uses_left_right_routing=False, no_repeats=False): + """ 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. + + """ + + choices = [] + + if cmt in self.sources: + choices.extend(self.sources[cmt]) + + if uses_left_right_routing: + 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: + for source in self.sources[paired_cmt]: + if 'BUFHCE' not in source: + choices.append(source) + + random.shuffle(choices) + + if not uses_left_right_routing: + return choices[0] + + for source in choices: + + 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() + + if no_repeats and source in self.used_sources_from_cmt[source_cmt]: + continue + + if len(self.used_sources_from_cmt[source_cmt]) >= 14: + continue + + self.used_sources_from_cmt[source_cmt].add(source) + + return source + + return None + + +def get_paired_iobs(db, grid, tile_name): + """ The two IOB33M's above and below the HCLK row have dedicate clock lines. + """ + + gridinfo = grid.gridinfo_at_tilename(tile_name) + loc = grid.loc_of_tilename(tile_name) + + if gridinfo.tile_type.endswith('_L'): + inc = 1 + lr = 'R' + else: + inc = -1 + lr = 'L' + + idx = 1 + while True: + gridinfo = grid.gridinfo_at_loc((loc.grid_x + inc * idx, loc.grid_y)) + + if gridinfo.tile_type.startswith('HCLK_IOI'): + break + + idx += 1 + + # Move from HCLK_IOI column to IOB column + idx += 1 + + for dy in [-1, -3, 2, 4]: + iob_loc = (loc.grid_x + inc * idx, loc.grid_y + dy) + gridinfo = grid.gridinfo_at_loc(iob_loc) + tile_name = grid.tilename_at_loc(iob_loc) + + assert gridinfo.tile_type.startswith(lr + 'IOB'), ( + gridinfo, lr + 'IOB') + + for site, site_type in gridinfo.sites.items(): + if site_type in ['IOB33M', 'IOB18M']: + yield tile_name, site, site_type[-3:-1] + + +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(): + """ + HCLK_CMT switch box has the follow inputs: + + 4 IOBs above and below + 14 MMCM outputs + 8 PLL outputs + 4 PHASER_IN outputs + 2 INT connections + + and the following outputs: + + 3 PLLE2 inputs + 2 BUFMR inputs + 3 MMCM inputs + ~2 MMCM -> BUFR??? + """ + + clock_sources = ClockSources() + adv_clock_sources = ClockSources() + site_to_cmt = dict(read_site_to_cmt()) + + db = Database(util.get_db_root()) + grid = db.grid() + + def gen_sites(desired_site_type): + 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 tile_name, site + + hclk_cmts = set() + ins = [] + iobs = StringIO() + + hclk_cmt_tiles = set() + for tile_name, site in gen_sites('BUFMRCE'): + cmt = site_to_cmt[site] + hclk_cmts.add(cmt) + hclk_cmt_tiles.add(tile_name) + + mmcm_pll_only = random.randint(0, 1) + mmcm_pll_dir = random.choice(('ODD', 'EVEN', 'BOTH')) + + print( + '// mmcm_pll_only {} mmcm_pll_dir {}'.format( + mmcm_pll_only, mmcm_pll_dir)) + + for tile_name in sorted(hclk_cmt_tiles): + for _, site, volt in get_paired_iobs(db, grid, tile_name): + + ins.append('input clk_{site}'.format(site=site)) + if check_allowed(mmcm_pll_dir, site_to_cmt[site]): + clock_sources.add_clock_source( + 'clock_IBUF_{site}'.format(site=site), site_to_cmt[site]) + adv_clock_sources.add_clock_source( + 'clock_IBUF_{site}'.format(site=site), site_to_cmt[site]) + + print( + """ + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + wire clock_IBUF_{site}; + IBUF #( .IOSTANDARD("LVCMOS{volt}") ) ibuf_{site} ( + .I(clk_{site}), + .O(clock_IBUF_{site}) + ); + """.format(volt=volt, site=site), + file=iobs) + + print( + ''' +module top({inputs}); + (* KEEP, DONT_TOUCH *) + LUT6 dummy(); + '''.format(inputs=', '.join(ins))) + + print(iobs.getvalue()) + + luts = LutMaker() + wires = StringIO() + bufhs = StringIO() + + for _, site in gen_sites('MMCME2_ADV'): + mmcm_clocks = [ + 'mmcm_clock_{site}_{idx}'.format(site=site, idx=idx) + for idx in range(13) + ] + + if check_allowed(mmcm_pll_dir, site_to_cmt[site]): + for clk in mmcm_clocks: + clock_sources.add_clock_source(clk, site_to_cmt[site]) + + print( + """ + wire cin1_{site}, cin2_{site}, clkfbin_{site}, {c0}, {c1}, {c2}, {c3}, {c4}, {c5}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + MMCME2_ADV pll_{site} ( + .CLKIN1(cin1_{site}), + .CLKIN2(cin2_{site}), + .CLKFBIN(clkfbin_{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(7) + ] + + if check_allowed(mmcm_pll_dir, site_to_cmt[site]): + for clk in pll_clocks: + clock_sources.add_clock_source(clk, site_to_cmt[site]) + + print( + """ + wire cin1_{site}, cin2_{site}, clkfbin_{site}, {c0}, {c1}, {c2}, {c3}, {c4}, {c5}, {c6}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + PLLE2_ADV pll_{site} ( + .CLKIN1(cin1_{site}), + .CLKIN2(cin2_{site}), + .CLKFBIN(clkfbin_{site}), + .CLKOUT0({c0}), + .CLKOUT1({c1}), + .CLKOUT2({c2}), + .CLKOUT3({c3}), + .CLKOUT4({c4}), + .CLKOUT5({c5}), + .CLKFBOUT({c6}) + ); + """.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], + c6=pll_clocks[6], + )) + + for tile_name, site in gen_sites('BUFHCE'): + print( + """ + wire I_{site}; + wire O_{site}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFHCE buf_{site} ( + .I(I_{site}), + .O(O_{site}) + ); + """.format(site=site, ), + file=bufhs) + + if site_to_cmt[site] in hclk_cmts: + if not mmcm_pll_only: + clock_sources.add_clock_source( + 'O_{site}'.format(site=site), site_to_cmt[site]) + adv_clock_sources.add_clock_source( + 'O_{site}'.format(site=site), site_to_cmt[site]) + + hclks_used_by_cmt = {} + for cmt in site_to_cmt.values(): + hclks_used_by_cmt[cmt] = set() + + def check_hclk_src(src, src_cmt): + if len(hclks_used_by_cmt[src_cmt] + ) >= 12 and src not in hclks_used_by_cmt[src_cmt]: + return None + else: + hclks_used_by_cmt[src_cmt].add(src) + return src + + if random.random() > .10: + for tile_name, site in gen_sites('BUFHCE'): + wire_name = clock_sources.get_random_source( + site_to_cmt[site], + uses_left_right_routing=True, + no_repeats=mmcm_pll_only) + + if wire_name is not None and 'BUFHCE' in wire_name: + # Looping a BUFHCE to a BUFHCE requires using a hclk in the + # CMT of the source + src_cmt = clock_sources.source_to_cmt[wire_name] + + wire_name = check_hclk_src(wire_name, src_cmt) + + if wire_name is None: + continue + + print( + """ + assign I_{site} = {wire_name};""".format( + site=site, + wire_name=wire_name, + ), + file=bufhs) + + for tile_name, site in gen_sites('BUFMRCE'): + pass + + for l in luts.create_wires_and_luts(): + print(l) + + print(wires.getvalue()) + print(bufhs.getvalue()) + + for _, site in gen_sites('BUFR'): + adv_clock_sources.add_clock_source( + 'O_{site}'.format(site=site), site_to_cmt[site]) + print( + """ + wire O_{site}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFR bufr_{site} ( + .O(O_{site}) + );""".format(site=site)) + + for _, site in gen_sites('PLLE2_ADV'): + for cin in ('cin1', 'cin2', 'clkfbin'): + if random.random() > .2: + src = adv_clock_sources.get_random_source(site_to_cmt[site]) + + src_cmt = adv_clock_sources.source_to_cmt[src] + + if 'IBUF' not in src and 'BUFR' not in src: + # Clocks from input pins do not require HCLK's, all other + # sources route from a global row clock. + src = check_hclk_src(src, src_cmt) + + if src is None: + continue + + print( + """ + assign {cin}_{site} = {csrc}; + """.format(cin=cin, site=site, csrc=src)) + + for _, site in gen_sites('MMCME2_ADV'): + for cin in ('cin1', 'cin2', 'clkfbin'): + if random.random() > .2: + src = adv_clock_sources.get_random_source(site_to_cmt[site]) + + src_cmt = adv_clock_sources.source_to_cmt[src] + if 'IBUF' not in src and 'BUFR' not in src: + # Clocks from input pins do not require HCLK's, all other + # sources route from a global row clock. + src = check_hclk_src(src, src_cmt) + + if src is None: + continue + + print( + """ + assign {cin}_{site} = {csrc}; + """.format(cin=cin, site=site, csrc=src)) + + print("endmodule") + + +if __name__ == '__main__': + main() diff --git a/fuzzers/Makefile b/fuzzers/Makefile index 0bec4116..ac61f733 100644 --- a/fuzzers/Makefile +++ b/fuzzers/Makefile @@ -83,6 +83,8 @@ $(eval $(call fuzzer,040-clk-hrow-config,005-tilegrid)) $(eval $(call fuzzer,041-clk-hrow-pips,005-tilegrid)) $(eval $(call fuzzer,042-clk-bufg-config,005-tilegrid)) $(eval $(call fuzzer,043-clk-rebuf-pips,005-tilegrid)) +$(eval $(call fuzzer,044-clk-bufg-pips,005-tilegrid)) +$(eval $(call fuzzer,045-hclk-cmt-pips,005-tilegrid)) $(eval $(call fuzzer,048-int-piplist,005-tilegrid)) $(eval $(call fuzzer,049-int-imux-gfan,048-int-piplist)) $(eval $(call fuzzer,050-pip-seed,048-int-piplist)) diff --git a/fuzzers/int_maketodo.py b/fuzzers/int_maketodo.py index 55e37aa8..a2a70f54 100644 --- a/fuzzers/int_maketodo.py +++ b/fuzzers/int_maketodo.py @@ -33,7 +33,9 @@ def load_pipfile(pipfile, verbose=False): return todos, tile_type -def maketodo(pipfile, dbfile, intre, not_endswith=None, verbose=False): +def maketodo( + pipfile, dbfile, intre, exclude_re=None, not_endswith=None, + verbose=False): ''' db files start with INT., but pipfile lines start with INT_L Normalize by removing before the first dot @@ -75,8 +77,15 @@ def maketodo(pipfile, dbfile, intre, not_endswith=None, verbose=False): drops = 0 lines = 0 for line in todos: - if re.match(intre, line) and (not_endswith is None - or not line.endswith(not_endswith)): + include = re.match(intre, line) is not None + + if include and not_endswith is not None: + include = not line.endswith(not_endswith) + + if include and exclude_re is not None: + include = re.match(exclude_re, line) is None + + if include: print(line) else: drops += 1 @@ -89,10 +98,12 @@ def run( db_dir, pip_dir, intre, + sides, l, r, pip_type, seg_type, + exclude_re=None, not_endswith=None, verbose=False): if db_dir is None: @@ -105,20 +116,22 @@ def run( assert intre, "RE is required" - 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) + for side in sides: + if side == "l" and not l: + continue + + if side == "r" and not r: + continue + + if side != "": + side = "_" + side - if r: maketodo( - "%s/%s_r.txt" % (pip_dir, pip_type), - "%s/segbits_%s_r.db" % (db_dir, seg_type), + "%s/%s%s.txt" % (pip_dir, pip_type, side), + "%s/segbits_%s%s.db" % (db_dir, seg_type, side), intre, - not_endswith, + exclude_re=exclude_re, + not_endswith=not_endswith, verbose=verbose) @@ -133,8 +146,10 @@ def main(): parser.add_argument('--db-dir', default=None, help='') parser.add_argument('--pip-dir', default=None, help='') parser.add_argument('--re', required=True, help='') + parser.add_argument('--exclude-re', required=False, default=None, help='') parser.add_argument('--pip-type', default="pips_int", help='') parser.add_argument('--seg-type', default="int", help='') + parser.add_argument('--sides', default="l,r", help='') util.add_bool_arg(parser, '--l', default=True, help='') util.add_bool_arg(parser, '--r', default=True, help='') parser.add_argument( @@ -146,6 +161,8 @@ def main(): db_dir=args.db_dir, pip_dir=args.pip_dir, intre=args.re, + exclude_re=args.exclude_re, + sides=args.sides.split(','), l=args.l, r=args.r, pip_type=args.pip_type, diff --git a/utils/mergedb.sh b/utils/mergedb.sh index 3ec63507..78025412 100755 --- a/utils/mergedb.sh +++ b/utils/mergedb.sh @@ -100,6 +100,11 @@ case "$1" in clk_bufg_top_r) sed < "$2" > "$tmp1" -e 's/^CLK_BUFG\./CLK_BUFG_TOP_R./' ;; + hclk_cmt) + cp "$2" "$tmp1" ;; + hclk_cmt_l) + sed < "$2" > "$tmp1" -e 's/^HCLK_CMT\./HCLK_CMT_L./' ;; + clk_bufg_rebuf) cp "$2" "$tmp1" ;;