diff --git a/fuzzers/034-cmt-pll-pips/Makefile b/fuzzers/034-cmt-pll-pips/Makefile index 03771b89..013ee3d0 100644 --- a/fuzzers/034-cmt-pll-pips/Makefile +++ b/fuzzers/034-cmt-pll-pips/Makefile @@ -1,14 +1,16 @@ export FUZDIR=$(shell pwd) PIP_TYPE?=cmt_top PIPLIST_TCL=$(FUZDIR)/cmt_top_upper_t.tcl -TODO_RE=".*\.CMT_TOP_[LR]_UPPER_T_PLLE2_CLK_[^_]+_INT" +TODO_RE=".*CMT_TOP_[LR]_UPPER_T_PLLE2_CLK(IN1|IN2|FBIN)\.CMT_TOP_[LR]" ifneq (${XRAY_DATABASE}, zynq7) MAKETODO_FLAGS=--sides "r_upper_t,l_upper_t" --pip-type ${PIP_TYPE} --seg-type cmt_top --re $(TODO_RE) else MAKETODO_FLAGS=--sides "l_upper_t" --pip-type ${PIP_TYPE} --seg-type cmt_top --re $(TODO_RE) endif -N = 50 + +N = 100 +SEGMATCH_FLAGS=-m 10 -M 20 -c 170 A_PIPLIST=cmt_top_l_upper_t.txt @@ -30,14 +32,14 @@ RDBS += build/segbits_cmt_top_r_upper_t.rdb endif database: ${RDBS} - ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ - --seg-fn-in build/segbits_cmt_top_l_upper_t.rdb \ - --seg-fn-out build/segbits_cmt_top_l_upper_t.db + python3 ${FUZDIR}/fixup_and_group.py -g tag_groups.txt \ + -i build/segbits_cmt_top_l_upper_t.rdb \ + -o build/segbits_cmt_top_l_upper_t.db ifneq (${XRAY_DATABASE}, zynq7) - ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ - --seg-fn-in build/segbits_cmt_top_r_upper_t.rdb \ - --seg-fn-out build/segbits_cmt_top_r_upper_t.db + python3 ${FUZDIR}/fixup_and_group.py -g tag_groups.txt \ + -i build/segbits_cmt_top_r_upper_t.rdb \ + -o build/segbits_cmt_top_r_upper_t.db endif # Keep a copy to track iter progress diff --git a/fuzzers/034-cmt-pll-pips/README.md b/fuzzers/034-cmt-pll-pips/README.md new file mode 100644 index 00000000..50ffdee0 --- /dev/null +++ b/fuzzers/034-cmt-pll-pips/README.md @@ -0,0 +1,20 @@ +# Fuzzer for the PIPs of CMT_TOP_[LR]_UPPER_T tiles. + +The fuzzer instantiates a PLL in each available site with 2/3 probability of using it. Once used it is connected randomly to various clock and logic resources. + +For some nets a randomized "manual" route is chosen to cover as many routing scenarios as possible. + +The information whether a PLL is used or not is stored in a file (`"design.txt"`) along with the randomized route (`route.txt`) + +After the design synthesis the `generate.py` sets fixed routes on some nets which is read from the `route.txt` file. The rest of the design is routed in the regular way. The script also dumps all used PIPs (as reported by Vivado) to the `design_pips.txt`. + +The tag generation is done in the following way: + +- If a PLL site is occupied then tags for all active PIPs are emitted as 1s. No tags are emitted for inactive PIPs. +- When a PLL site is not occupied (IN_USE=0) then tags for all PIPs for the CMT tile are emitted as 0s. +- The IN_USE tag is emitted directly. + +The raw solution of tag bits is postprocessed via the custom script `fixup_and_group.py`. The script does two things: + +- Clears all bits found for the IN_USE tag in all other tags. Those bits are common to all of them. +- Groups tags according to the group definitions read from the `tag_groups.txt` file. Bits that are common to the group are set as 0 in each tag that belongs to it (tags within a group are exclusive). \ No newline at end of file diff --git a/fuzzers/034-cmt-pll-pips/bits.dbf b/fuzzers/034-cmt-pll-pips/bits.dbf deleted file mode 100644 index ad502de5..00000000 --- a/fuzzers/034-cmt-pll-pips/bits.dbf +++ /dev/null @@ -1,7 +0,0 @@ -CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_PLLE2_CLK_FB_INT ^ CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_CLKFBIN -CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_PLLE2_CLK_IN1_INT ^ CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_CLKIN1 -CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_PLLE2_CLK_IN2_INT ^ CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_CLKIN2 - -CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_PLLE2_CLK_IN1_INT ^ CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_CLKIN1 -CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_PLLE2_CLK_IN2_INT ^ CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_CLKIN2 - diff --git a/fuzzers/034-cmt-pll-pips/fixup_and_group.py b/fuzzers/034-cmt-pll-pips/fixup_and_group.py new file mode 100755 index 00000000..b005b05a --- /dev/null +++ b/fuzzers/034-cmt-pll-pips/fixup_and_group.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +""" +This is a db fixup script that does two things: + +1. Clears (removes) all bits found in "IN_USE" tag(s) and removes the IN_USE + tag itself + +2. Reads tag group definition from a file and applies the tag grouping. First + a set of common bits for each group is found (logical OR among all tags + belonging to the group). Then in each tag belonging to the group those + bits are set to 0 but only if they are not already present there. + +The resulting data is written into a segbits file. +""" +import argparse +import re +import itertools + +# ============================================================================= + + +def load_tag_groups(file_name): + """ + Loads tag groups from a text file. + + A tag group is defined by specifying a space separated list of tags within + a single line. Lines that are empty or start with '#' are ignored. + """ + tag_groups = [] + + # Load tag group specifications + with open(file_name, "r") as fp: + for line in fp: + line = line.strip() + + if len(line) == 0 or line.startswith("#"): + continue + + group = set(line.split()) + if len(group): + tag_groups.append(group) + + # Check if all tag groups are exclusive + for tag_group_a, tag_group_b in itertools.combinations(tag_groups, 2): + + tags = tag_group_a & tag_group_b + if len(tags): + raise RuntimeError( + "Tag(s) {} are present in multiple groups".format( + " ".join(tags))) + + return tag_groups + + +# ============================================================================= + + +def parse_bit(bit): + """ + Decodes string describing a bit. Returns a tuple (frame, bit, value) + """ + match = re.match("^(!?)([0-9]+)_([0-9]+)$", bit) + assert match != None, bit + + val = int(match.group(1) != "!") + frm = int(match.group(2)) + bit = int(match.group(3)) + + return frm, bit, val + + +def bit_to_str(bit): + """ + Converts a tuple (frame, bit, value) to its string representation. + """ + s = "!" if not bit[2] else "" + return "{}{}_{}".format(s, bit[0], bit[1]) + + +def load_segbits(file_name): + """ + Loads a segbits file. + """ + + segbits = {} + + with open(file_name, "r") as fp: + for line in fp: + line = line.strip() + fields = line.split() + + if len(fields) < 2: + raise RuntimeError("Malformed line: '%s'" % line) + + tag = fields[0] + + if "<" in line or ">" in line: + segbits[tag] = set() + + else: + bits = set([parse_bit(bit) for bit in fields[1:]]) + segbits[tag] = bits + + return segbits + + +def save_segbits(file_name, segbits): + """ + Save segbits to a .db or .rdb file + """ + + with open(file_name, "w") as fp: + for tag, bits in segbits.items(): + if not len(bits): + continue + + line = tag + " " + line += " ".join([bit_to_str(bit) for bit in sorted(list(bits))]) + fp.write(line + "\n") + + +# ============================================================================= + + +def mask_out_bits(segbits, mask, tags_to_mask=None): + """ + Given a set of bits and a list of tags to affect (optional) removes all + the bits from each tag that are present (and equal) in the masking set. + """ + + if tags_to_mask is None: + tags_to_mask = segbits.keys() + + # Mask out matching bits + for tag in tags_to_mask: + bits = segbits[tag] + + bits = set(bits) - set(mask) + segbits[tag] = bits + + return segbits + + +def find_common_bits_for_tag_groups(segbits, tag_groups): + """ + For each tag group finds a common set of bits that have value of one. + """ + + bit_groups = [] + + for tag_group in tag_groups: + bit_group = set() + + for tag, bits in segbits.items(): + if tag in tag_group: + ones = set([b for b in bits if b[2]]) + bit_group |= ones + + bit_groups.append(bit_group) + + return bit_groups + + +def group_tags(segbits, tag_groups, bit_groups): + """ + Implements tag grouping. If a tag belongs to a group then the common bits + of that group are added to is as zeros. + """ + + for tag_group, bit_group in zip(tag_groups, bit_groups): + zeros = set([(b[0], b[1], 0) for b in bit_group]) + for tag in tag_group: + + # Insert zero bits + if tag in segbits.keys(): + bits = segbits[tag] + for z in zeros: + if not (z[0], z[1], 1) in bits: + bits.add(z) + + return segbits + + +# ============================================================================= + + +def main(): + + # Parse arguments + parser = argparse.ArgumentParser() + + parser.add_argument("-i", required=True, type=str, help="Input .rdb file") + parser.add_argument( + "-g", required=True, type=str, help="Input tag group definition file") + parser.add_argument("-o", required=True, type=str, help="Output .rdb file") + + args = parser.parse_args() + + # Load tag groups + tag_groups = load_tag_groups(args.g) + + # Load raw database file + segbits = load_segbits(args.i) + + # Mask out bits present in "IN_USE" tag(s) as they are common to other tags + for tag in segbits.keys(): + if tag.endswith("IN_USE"): + prefix = tag[:tag.index("IN_USE")] + tags_to_mask = [t for t in segbits.keys() if t.startswith(prefix)] + mask_out_bits(segbits, segbits[tag], tags_to_mask) + + # Find common bits + bit_groups = find_common_bits_for_tag_groups(segbits, tag_groups) + # Apply tag grouping + segbits = group_tags(segbits, tag_groups, bit_groups) + + # Save fixed database file + save_segbits(args.o, segbits) + + +if __name__ == "__main__": + main() diff --git a/fuzzers/034-cmt-pll-pips/generate.py b/fuzzers/034-cmt-pll-pips/generate.py index 0b6243f3..d1bb9bd3 100644 --- a/fuzzers/034-cmt-pll-pips/generate.py +++ b/fuzzers/034-cmt-pll-pips/generate.py @@ -3,10 +3,11 @@ from prjxray.segmaker import Segmaker import os import os.path +import itertools def bitfilter(frame, word): - if frame <= 1: + if frame < 28 or frame > 29: return False return True @@ -15,30 +16,60 @@ def bitfilter(frame, word): def main(): segmk = Segmaker("design.bits") + designdata = {} tiledata = {} pipdata = {} + ppipdata = {} ignpip = set() - with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', - 'cmt_top', 'cmt_top_l_upper_t.txt')) as f: - for l in f: - tile_type, dst, src = l.strip().split('.') - if tile_type not in pipdata: - pipdata[tile_type] = [] + # Zynq7 do not have R CMTs + if os.getenv("XRAY_DATABASE") == "zynq7": + piplists = ['cmt_top_l_upper_t.txt'] + ppiplists = ['ppips_cmt_top_l_upper_t.db'] + else: + piplists = ['cmt_top_l_upper_t.txt', 'cmt_top_r_upper_t.txt'] + ppiplists = [ + 'ppips_cmt_top_l_upper_t.db', 'ppips_cmt_top_r_upper_t.db' + ] - pipdata[tile_type].append((src, dst)) + # Load PIP lists + print("Loading PIP lists...") + for piplist in piplists: + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'cmt_top', piplist)) as f: + for l in f: + tile_type, dst, src = l.strip().split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] - with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', - 'cmt_top', 'cmt_top_r_upper_t.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)) - pipdata[tile_type].append((src, dst)) + # Load PPIP lists (to exclude them) + print("Loading PPIP lists...") + for ppiplist in ppiplists: + fname = os.path.join( + os.getenv('FUZDIR'), '..', '071-ppips', 'build', ppiplist) + with open(fname, 'r') as f: + for l in f: + pip_data, pip_type = l.strip().split() - print("Loading tags from design.txt.") + if pip_type != 'always': + continue + + tile_type, dst, src = pip_data.split('.') + if tile_type not in ppipdata: + ppipdata[tile_type] = [] + + ppipdata[tile_type].append((src, dst)) + + # Load desgin data + print("Loading design data...") with open("design.txt", "r") as f: + for line in f: + fields = line.strip().split(",") + designdata[fields[0]] = fields[1:] + + with open("design_pips.txt", "r") as f: for line in f: tile, pip, src, dst, pnum, pdir = line.split() @@ -47,6 +78,8 @@ def main(): if 'UPPER_B' in tile: continue + if 'LOWER_T' in tile: + continue pip_prefix, _ = pip.split(".") tile_from_pip, tile_type = pip_prefix.split('/') @@ -76,27 +109,44 @@ def main(): dst.startswith('CMT_TOP_L_UPPER_T_CLK'): ignpip.add((src, dst)) - for tile, pips_srcs_dsts in tiledata.items(): - tile_type = pips_srcs_dsts["type"] - pips = pips_srcs_dsts["pips"] + tags = {} + + # Populate IN_USE tags + for tile, (site, in_use) in designdata.items(): + if tile not in tags: + tags[tile] = {} + + tags[tile]["IN_USE"] = int(in_use) + + # Populate PIPs + for tile in tags.keys(): + tile_type = tile.rsplit("_", maxsplit=1)[0] + + in_use = tags[tile]["IN_USE"] + internal_feedback = False + + if not in_use: + active_pips = [] + else: + active_pips = tiledata[tile]["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 (src, dst) not in tiledata[tile]["pips"]: - segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 0) + continue + if (src, dst) in ppipdata[tile_type]: + continue - internal_feedback = False - for src, dst in [ - ('CMT_TOP_L_CLKFBOUT2IN', 'CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN'), - ('CMT_TOP_R_CLKFBOUT2IN', 'CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN'), - ]: - if (src, dst) in pips: - internal_feedback = True + tag = "{}.{}".format(dst, src) + val = in_use if (src, dst) in active_pips else False - segmk.add_tile_tag(tile, "EXTERNAL_FEEDBACK", not internal_feedback) + if not (in_use and not val): + tags[tile][tag] = int(val) + + # Output tags + for tile, tile_tags in tags.items(): + for t, v in tile_tags.items(): + segmk.add_tile_tag(tile, t, v) segmk.compile(bitfilter=bitfilter) segmk.write() diff --git a/fuzzers/034-cmt-pll-pips/generate.tcl b/fuzzers/034-cmt-pll-pips/generate.tcl index 502a0ec2..19be7bd4 100644 --- a/fuzzers/034-cmt-pll-pips/generate.tcl +++ b/fuzzers/034-cmt-pll-pips/generate.tcl @@ -23,6 +23,93 @@ proc write_pip_txtdata {filename} { close $fp } +proc load_routes {filename} { + puts "MANROUTE: Loading routes from $filename" + + set fp [open $filename r] + foreach line [split [read $fp] "\n"] { + if {$line eq ""} { + continue + } + + puts "MANROUTE: Line: $line" + + # Parse the line + set fields [split $line " "] + set tile_name [lindex $fields 0] + set site_name [lindex $fields 1] + set pin_name [lindex $fields 2] + set route_dir [lindex $fields 3] + set wires [lrange $fields 4 end] + + # Get net + set tile [get_tiles $tile_name] + set site [get_sites -of_objects $tile $site_name] + set pin [get_site_pins -of_objects $site "*/$pin_name"] + set net [get_nets -quiet -of_objects $pin] + + if {$net eq "" } { + puts "MANROUTE: No net for pin $pin_name found! Skipping..." + continue + } + + # Fixed part read from file + set route_list {} + foreach wire $wires { + lappend route_list [get_nodes -of_objects [get_wires -of_objects $tile "*/$wire"]] + } + + # Complete the route + if {$route_dir eq "up"} { + set node_to [lindex $route_list 0] + set node_from [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]] + + set rpart [find_routing_path -from $node_from -to $node_to] + if {$rpart eq ""} { + puts "MANROUTE: No possible route continuation for net $net" + continue + } + + set route_list [concat [lrange $rpart 0 end-1] $route_list] + } + + if {$route_dir eq "down"} { + set node_from [lindex $route_list e] + set node_to [get_nodes -of_objects [get_site_pins -filter {DIRECTION == IN} -of_objects $net]] + + set rpart [find_routing_path -from $node_from -to $node_to] + if {$rpart eq ""} { + puts "MANROUTE: No possible route continuation for net $net" + continue + } + set route_list [concat $route_list [lrange $rpart 1 end]] + } + + # Set the fixed route + puts "MANROUTE: Net: $net, Route: $route_list. routing..." + regsub -all {{}} $route_list "" route + set_property FIXED_ROUTE $route $net + + # Route the single net. Needed to detect conflicts when evaluating + # other ones + route_design -quiet -directive Quick -nets $net + + # Check for conflicts. + set status [get_property ROUTE_STATUS $net] + if {$status ne "ROUTED"} { + # Ripup and discard the fixed route. + set_property FIXED_ROUTE "" $net + route_design -unroute -nets $net + puts "MANROUTE: Net $net status $status, ripping up..." + } else { + set_property IS_ROUTE_FIXED 1 $net + puts "MANROUTE: Successful manual route for $net" + } + } + + close $fp +} + proc run {} { create_project -force -part $::env(XRAY_PART) design design read_verilog top.v @@ -47,11 +134,27 @@ proc run {} { set_property IS_ENABLED 0 [get_drc_checks {REQP-13}] place_design - route_design + + load_routes routes.txt + write_checkpoint -force design_pre_route.dcp + + route_design -directive Quick -preserve + + if {[llength [get_nets -filter {ROUTE_STATUS!="ROUTED"}]] ne 0} { + set nets [get_nets -filter {IS_ROUTE_FIXED==1}] + puts "MANROUTE: Got unrouted nets: $nets" + puts "MANROUTE: Ripping up and starting again with no fixed routes" + + route_design -unroute + set_property FIXED_ROUTE "" $nets + set_property IS_ROUTE_FIXED 0 $nets + + route_design -directive Quick + } write_checkpoint -force design.dcp write_bitstream -force design.bit - write_pip_txtdata design.txt + write_pip_txtdata design_pips.txt } run diff --git a/fuzzers/034-cmt-pll-pips/tag_groups.txt b/fuzzers/034-cmt-pll-pips/tag_groups.txt new file mode 100644 index 00000000..239d2e84 --- /dev/null +++ b/fuzzers/034-cmt-pll-pips/tag_groups.txt @@ -0,0 +1,12 @@ +CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_L_CLKFBOUT2IN CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_L_UPPER_T_CLKFBIN CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_L_UPPER_T_FREQ_BB0 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_L_UPPER_T_FREQ_BB1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_L_UPPER_T_FREQ_BB2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_L_UPPER_T_FREQ_BB3 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_L_UPPER_T_PLLE2_CLK_FB_INT + +CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_CLKIN1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_FREQ_BB0 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_FREQ_BB1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_FREQ_BB2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_FREQ_BB3 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_L_UPPER_T_PLLE2_CLK_IN1_INT + +CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_CLKIN2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_FREQ_BB0 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_FREQ_BB1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_FREQ_BB2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_FREQ_BB3 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_L_UPPER_T_PLLE2_CLK_IN2_INT + +CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_CLKFBOUT2IN CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_CLKFBIN CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_FREQ_BB0 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_FREQ_BB1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_FREQ_BB2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_FREQ_BB3 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN.CMT_TOP_R_UPPER_T_PLLE2_CLK_FB_INT + +CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_CLKIN1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_FREQ_BB0 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_FREQ_BB1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_FREQ_BB2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_FREQ_BB3 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN1.CMT_TOP_R_UPPER_T_PLLE2_CLK_IN1_INT + +CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_CLKIN2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_FREQ_BB0 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_FREQ_BB1 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_FREQ_BB2 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_FREQ_BB3 CMT_UPPER_T.CMT_TOP_R_UPPER_T_PLLE2_CLKIN2.CMT_TOP_R_UPPER_T_PLLE2_CLK_IN2_INT + diff --git a/fuzzers/034-cmt-pll-pips/top.py b/fuzzers/034-cmt-pll-pips/top.py index c26cba32..1727d4bc 100644 --- a/fuzzers/034-cmt-pll-pips/top.py +++ b/fuzzers/034-cmt-pll-pips/top.py @@ -6,6 +6,49 @@ from prjxray import util from prjxray.db import Database +class PipList(object): + def __init__(self): + self.piplist = {} + self.ppiplist = {} + + def get_pip_and_ppip_list_for_tile_type(self, tile_type): + + # Load PIP list for the tile type if not already loaded + if tile_type not in self.piplist: + self.piplist[tile_type] = [] + + fname = os.path.join( + os.getenv('FUZDIR'), '..', 'piplist', 'build', 'cmt_top', + tile_type.lower() + '.txt') + + with open(fname, "r") as f: + for l in f: + tile, dst, src = l.strip().split('.') + if tile_type == tile: + self.piplist[tile_type].append((src, dst)) + + # Load PPIP list for the tile type if not already loaded + if tile_type not in self.ppiplist: + self.ppiplist[tile_type] = [] + + fname = os.path.join( + os.getenv('FUZDIR'), '..', '071-ppips', 'build', + 'ppips_' + tile_type.lower() + '.db') + + with open(fname, "r") as f: + for l in f: + pip_data, pip_type = l.strip().split() + + if pip_type != 'always': + continue + + tile, dst, src = pip_data.strip().split('.') + if tile_type == tile: + self.ppiplist[tile_type].append((src, dst)) + + return self.piplist[tile_type], self.ppiplist[tile_type] + + def find_phasers_for_pll(grid, loc): gridinfo = grid.gridinfo_at_loc((loc[0], loc[1] + 13)) @@ -39,27 +82,153 @@ def gen_sites(): for site_name, site_type in gridinfo.sites.items(): if site_type in ['PLLE2_ADV']: phasers = find_phasers_for_pll(grid, loc) - yield site_name, phasers + yield tile_name, site_name, phasers + + +def get_random_route_from_site_pin( + pip_list, tile_name, site_pin, direction, occupied_wires): + + # A map of PLL site pins to wires they are connected to. + pin_to_wire = { + "CMT_TOP_L_UPPER_T": { + "CLKIN1": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN1", + "CLKIN2": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN2", + "CLKFBIN": "CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN", + }, + "CMT_TOP_R_UPPER_T": { + "CLKIN1": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN1", + "CLKIN2": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN2", + "CLKFBIN": "CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN", + }, + } + + # Get tile type + tile_type = tile_name.rsplit("_", maxsplit=1)[0] + + # Get all PIPs (PIPs + PPIPs) + pips, ppips = pip_list.get_pip_and_ppip_list_for_tile_type(tile_type) + all_pips = pips + ppips + + # The first wire + wire = pin_to_wire[tile_type][site_pin] + + # Walk randomly. + route = [] + while True: + route.append(wire) + + wires = [] + + for src, dst in all_pips: + if direction == "down" and src == wire: + next_wire = dst + elif direction == "up" and dst == wire: + next_wire = src + else: + continue + + if next_wire not in occupied_wires: + wires.append(next_wire) + + if len(wires) == 0: + break + + wire = random.choice(wires) + occupied_wires.add(wire) + + # For "up" direction reverse the route. + if direction == "down": + return route + if direction == "up": + return route[::-1] def main(): + + # 8 inputs per clock region + # 5 clock regions for device + max_clk_inputs = 8 * 5 + clkin_idx = 0 + print( ''' -module top(); +module top( + input wire [{nclkin}:0] clkin +); + (* KEEP, DONT_TOUCH *) LUT6 dummy(); - ''') + '''.format(nclkin=max_clk_inputs - 1)) + pip_list = PipList() bufg_count = 0 - for site, phasers in sorted(gen_sites(), key=lambda x: x[0]): - drive_feedback = random.randint(0, 1) - clkfbin_src = random.choice(('BUFH', '0', '1', None)) + design_file = open('design.txt', 'w') + routes_file = open('routes.txt', 'w') - if drive_feedback: + for tile, site, phasers in sorted(gen_sites(), key=lambda x: x[0]): + in_use = random.randint(0, 2) > 0 + + design_file.write("{},{},{}\n".format(tile, site, int(in_use))) + + if not in_use: + continue + + # Generate random routes to/from some pins + routes = {} + endpoints = {} + + pins = [ + ('CLKIN1', 'up'), + ('CLKIN2', 'up'), + ('CLKFBIN', 'up'), + + # Sometimes manually randomized route for CLKOUTx conflicts with + # the verilog design. + #('CLKOUT0', 'down'), + #('CLKOUT1', 'down'), + #('CLKOUT2', 'down'), + #('CLKOUT3', 'down'), + ] + + occupied_wires = set() + for pin, dir in pins: + + route = get_random_route_from_site_pin( + pip_list, tile, pin, dir, occupied_wires) + if route is None: + endpoints[pin] = "" + continue + + routes[pin] = ( + route, + dir, + ) + endpoints[pin] = route[-1] if dir == 'down' else route[0] + + internal_feedback = endpoints['CLKFBIN'].endswith('CLKFBOUT') + + # Store them in a random order so the TCL script will try to route + # them also in the random order. + lines = [] + for pin, ( + route, + dir, + ) in routes.items(): + + route_str = " ".join(route) + lines.append( + '{} {} {} {} {}\n'.format(tile, site, pin, dir, route_str)) + + random.shuffle(lines) + routes_file.writelines(lines) + + clkfbin_src = random.choice(('BUFH', 'logic')) + + if internal_feedback: COMPENSATION = "INTERNAL" else: - if clkfbin_src in ['0', '1']: + if clkfbin_src == 'logic': COMPENSATION = 'EXTERNAL' else: COMPENSATION = "ZHOLD" @@ -121,7 +290,7 @@ module top(); # - Global drivers (e.g. BUFG) # - PHASER_[IN|OUT]_[CA|DB]_FREQREFCLK via BB_[0-3] drive_bufg = random.randint(0, 1) and bufg_count < 16 - drive_phaser = random.randint(0, 1) + drive_phaser = 0 #random.randint(0, 1) if drive_bufg: bufg_count += 1 @@ -141,30 +310,13 @@ module top(); .FREQREFCLK(clkout{idx}_{site}) );""".format(idx=clkout, site=site, phaser_loc=phasers['OUT'][0])) - drive_bufg = random.randint(0, 1) and bufg_count < 16 - - if drive_bufg and clkfbin_src not in ['BUFH', 'BUFR']: - bufg_count += 1 + if internal_feedback: print( """ - (* KEEP, DONT_TOUCH *) - BUFG ( - .I(clkfbout_mult_{site}) - );""".format(site=site)) - - if drive_feedback: - print( - """ - assign clkfbin_{site} = clkfbout_mult_{site}; - """.format(site=site)) + assign clkfbin_{site} = clkfbout_mult_{site}; + """.format(site=site)) else: - # If CLKFBIN is not using CLKFBOUT feedback, can be connected to: - # - BUFHCE/BUFR using dedicated path - # - Switch box clock port - - if clkfbin_src is None: - pass - elif clkfbin_src == 'BUFH': + if clkfbin_src == 'BUFH': print( """ (* KEEP, DONT_TOUCH *) @@ -172,68 +324,56 @@ module top(); .I(clkfbout_mult_{site}), .O(clkfbin_{site}) );""".format(site=site)) - elif clkfbin_src == 'BUFR': + elif clkfbin_src == 'logic': print( """ (* KEEP, DONT_TOUCH *) - BUFR ( - .I(clkfbout_mult_{site}), + LUT6 # (.INIT(64'h5555555555555555)) + clkfbin_logic_{site} ( + .I0(clkfbout_mult_{site}), .O(clkfbin_{site}) - );""".format(site=site)) - elif clkfbin_src == '0': - print( - """ - assign clkfbin_{site} = 0; - """.format(site=site)) - elif clkfbin_src == '1': - print( - """ - assign clkfbin_{site} = 1; + ); """.format(site=site)) else: - assert False, clkfbin_src - - clkin_is_none = False + assert False, clkfb_src for clkin in range(2): clkin_src = random.choice(( 'BUFH', 'BUFR', - '0', - '1', - None, + 'logic', )) - if clkin == 1 and clkin_is_none and clkin_src is None: - clkin_src = 'BUFH' - if clkin_src is None: - pass - elif clkin_src == 'BUFH': + if clkin_src == 'BUFH': print( """ (* KEEP, DONT_TOUCH *) BUFH ( - .O(clkin{idx}_{site}) - );""".format(idx=clkin + 1, site=site)) + .O(clkin{idx}_{site}), + .I(clkin{idx2}) + );""".format(idx=clkin + 1, idx2=clkin_idx, site=site)) elif clkin_src == 'BUFR': print( """ (* KEEP, DONT_TOUCH *) BUFR ( + .O(clkin{idx}_{site}), + .I(clkin{idx2}) + );""".format(idx=clkin + 1, idx2=clkin_idx, site=site)) + elif clkin_src == 'logic': + print( + """ + (* KEEP, DONT_TOUCH *) + LUT6 # (.INIT(64'h5555555555555555)) + clkin{idx}_logic_{site} ( + .I0(clkin{idx2}), .O(clkin{idx}_{site}) - );""".format(idx=clkin + 1, site=site)) - elif clkin_src == '0': - print( - """ - assign clkin{idx}_{site} = 0; - """.format(idx=clkin + 1, site=site)) - elif clkin_src == '1': - print( - """ - assign clkin{idx}_{site} = 1; - """.format(idx=clkin + 1, site=site)) + ); + """.format(idx=clkin + 1, idx2=clkin_idx, site=site)) else: - assert False, clkfbin_src + assert False, (clkin, clkin_src) + + clkin_idx += 1 print("endmodule") diff --git a/fuzzers/Makefile b/fuzzers/Makefile index 29c98e8e..72921655 100644 --- a/fuzzers/Makefile +++ b/fuzzers/Makefile @@ -84,7 +84,7 @@ $(eval $(call fuzzer,028-fifo-config,005-tilegrid)) $(eval $(call fuzzer,029-bram-fifo-config,005-tilegrid)) $(eval $(call fuzzer,030-iob,005-tilegrid)) $(eval $(call fuzzer,032-cmt-pll,005-tilegrid)) -$(eval $(call fuzzer,034-cmt-pll-pips,005-tilegrid)) +$(eval $(call fuzzer,034-cmt-pll-pips,005-tilegrid 071-ppips)) $(eval $(call fuzzer,035-iob-ilogic,005-tilegrid)) $(eval $(call fuzzer,035a-iob-idelay,005-tilegrid)) $(eval $(call fuzzer,036-iob-ologic,005-tilegrid)) diff --git a/minitests/plle2_adv/basys3_plle2_adv.v b/minitests/plle2_adv/basys3_plle2_adv.v index aace7c85..ddbf279d 100644 --- a/minitests/plle2_adv/basys3_plle2_adv.v +++ b/minitests/plle2_adv/basys3_plle2_adv.v @@ -15,6 +15,10 @@ input wire [15:0] sw, output wire [15:0] led ); +// ============================================================================ + +assign tx = rx; + // ============================================================================ // Clock & reset reg [3:0] rst_sr; @@ -44,7 +48,8 @@ plle2_test plle2_test .O_CNT (led[5:0]) ); -assign led [14:6] = 0; +assign led [14] = |sw; +assign led [13:6] = 0; endmodule diff --git a/minitests/plle2_adv/src/plle2_test.v b/minitests/plle2_adv/src/plle2_test.v index dd73b2eb..9348c2d2 100644 --- a/minitests/plle2_adv/src/plle2_test.v +++ b/minitests/plle2_adv/src/plle2_test.v @@ -22,12 +22,16 @@ always @(posedge clk100) // ============================================================================ // The PLL -wire clk_fb; +wire clk_fb_o; +wire clk_fb_i; wire [5:0] clk; PLLE2_ADV # ( +.BANDWIDTH ("HIGH"), +.COMPENSATION ("BUF_IN"), + .CLKIN1_PERIOD (20.0), // 50MHz .CLKIN2_PERIOD (10.0), // 100MHz @@ -35,7 +39,7 @@ PLLE2_ADV # .CLKFBOUT_PHASE (0.0), .CLKOUT0_DIVIDE (16), -.CLKOUT0_DUTY_CYCLE (0.5), +.CLKOUT0_DUTY_CYCLE (0.53125), .CLKOUT0_PHASE (45.0), .CLKOUT1_DIVIDE (32), @@ -69,8 +73,8 @@ pll .RST (RST), .LOCKED (O_LOCKED), -.CLKFBIN (clk_fb), -.CLKFBOUT (clk_fb), +.CLKFBIN (clk_fb_i), +.CLKFBOUT (clk_fb_o), .CLKOUT0 (clk[0]), .CLKOUT1 (clk[1]), @@ -80,6 +84,8 @@ pll .CLKOUT5 (clk[5]) ); +BUFG clk_fb_buf (.I(clk_fb_o), .O(clk_fb_i)); + // ============================================================================ // Counters diff --git a/minitests/plle2_adv/tcl/par.tcl b/minitests/plle2_adv/tcl/par.tcl index 60d66e80..7f9dcdcc 100644 --- a/minitests/plle2_adv/tcl/par.tcl +++ b/minitests/plle2_adv/tcl/par.tcl @@ -13,4 +13,4 @@ route_design write_checkpoint -force ../$env(PROJECT_NAME).dcp -write_bitstream -force ../$env(PROJECT_NAME).bit +write_bitstream -force -logic_location_file ../$env(PROJECT_NAME).bit