Merge pull request #1077 from antmicro/034-fix-tmp

Fix for 034 fuzzer
This commit is contained in:
litghost 2019-10-29 14:50:53 -07:00 committed by GitHub
commit a8f635c003
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 677 additions and 124 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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