Merge pull request #1307 from antmicro/hclk_cmt_freq_ref

Enhanced 045 to solve FREQ_REF related PIPs
This commit is contained in:
litghost 2020-05-04 15:02:59 -07:00 committed by GitHub
commit b51b848959
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 222 additions and 43 deletions

View File

@ -3,57 +3,44 @@ 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)"
EXCLUDE_RE="(^.*LEAF)|(^.*BUFMR)|(^.*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 = 100
# These PIPs all appear to be either a 1 bit solutions.
SEGMATCH_FLAGS=-c 2
# All PIPs seem to have 2 bits. An exception is FREQ_REF which cannot be
# decoupled from the others, hence it will always have 4 bits.
FOUR_BIT_PIPS="FREQ_REF"
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)
.PRECIOUS: build/%.rdb
build/segbits_hclk_cmt.db: build/segbits_hclk_cmt.rdb
build/%.rdb: $(SPECIMENS_OK)
$(XRAY_SEGMATCH) -c 4 -o $@.4 $(shell find build -name $(subst segbits,segdata,$(patsubst %.rdb,%.txt,$(notdir $@))))
$(XRAY_SEGMATCH) -c 2 -o $@.2 $(shell find build -name $(subst segbits,segdata,$(patsubst %.rdb,%.txt,$(notdir $@))))
grep ${FOUR_BIT_PIPS} $@.4 > $@
grep -v ${FOUR_BIT_PIPS} $@.2 >> $@
build/%.db: build/%.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
--seg-fn-in $< \
--seg-fn-out $@
# Keep a copy to track iter progress
cp build/segbits_hclk_cmt.rdb build/$(ITER)/segbits_hclk_cmt.rdb
${XRAY_MASKMERGE} build/mask_hclk_cmt.db \
$(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)
build/segbits_hclk_cmt_l.db: build/segbits_hclk_cmt_l.rdb
${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_l.rdb build/$(ITER)/segbits_hclk_cmt_l.rdb
${XRAY_MASKMERGE} build/mask_hclk_cmt_l.db \
$(shell find build -name segdata_hclk_cmt_l.txt)
cp $< build/$(ITER)/$(notdir $<)
${XRAY_MASKMERGE} $(subst segbits,mask,$@) \
$(shell find build -name $(subst segbits,segdata,$(patsubst %.db,%.txt,$(notdir $@))))
database: build/segbits_hclk_cmt.db build/segbits_hclk_cmt_l.db
# Clobber existing .db to eliminate potential conflicts
cp ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/segbits*.db build/database/${XRAY_DATABASE}
cp ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/segbits_hclk_cmt*.db build/database/${XRAY_DATABASE} || true
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
@ -62,9 +49,9 @@ build/cmt_regions.csv: output_cmt.tcl
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 build/database/${XRAY_DATABASE}/segbits_hclk_cmt.db
${XRAY_MERGEDB} mask_hclk_cmt build/mask_hclk_cmt.db
${XRAY_MERGEDB} hclk_cmt_l build/segbits_hclk_cmt_l.db
${XRAY_MERGEDB} hclk_cmt_l build/database/${XRAY_DATABASE}/segbits_hclk_cmt_l.db
${XRAY_MERGEDB} mask_hclk_cmt_l build/mask_hclk_cmt_l.db
.PHONY: database pushdb

View File

@ -120,6 +120,24 @@ def main():
segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 0)
for port in tile_ports[tile_type]:
# These ones do not have any outgoing connections from the tile.
if "FREQ_REF" in port:
continue
# There seems to be no special bits related to use of
# HCLK_CMT_MUX_CLKINT_n wires.
if "HCLK_CMT_MUX_CLKINT" in port:
continue
# It seems that CCIOn_USED is not enabled when a net goes through
# FREQ_REFn. Do not emit this tag if this happens.
if "CCIO" in port:
n = int(port[-1])
dst = "HCLK_CMT_MUX_OUT_FREQ_REF{}".format(n)
if dst in tiledata[tile]["dsts"]:
continue
if port in tiledata[tile]["dsts"] or port in tiledata[tile]["srcs"]:
segmk.add_tile_tag(tile, "{}_USED".format(port), 1)
else:

View File

@ -197,6 +197,51 @@ proc route_todo {} {
}
}
proc make_manual_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 net_name [lindex $fields 0]
set wire_name [lindex $fields 1]
# Check if that net exist
if {[get_nets $net_name] eq ""} {
puts "MANROUTE: net $net_name does not exist"
continue
}
set net [get_nets $net_name]
# Rip it up
set_property -quiet FIXED_ROUTE "" $net
set_property IS_ROUTE_FIXED 0 $net
route_design -unroute -nets $net
# Make the route
set nodes [get_nodes -of_objects [get_wires $wire_name]]
set status [route_via $net_name [list $nodes] 0]
# Failure, skip manual routing of this net
if { $status != 1 } {
puts "MANROUTE: Manual routing failed!"
set_property -quiet FIXED_ROUTE "" $net
set_property IS_ROUTE_FIXED 0 $net
continue
}
puts "MANROUTE: Success!"
}
}
proc run {} {
create_project -force -part $::env(XRAY_PART) design design
read_verilog top.v
@ -220,10 +265,11 @@ proc run {} {
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets]
place_design
route_design
place_design -directive Quick
route_design -directive Quick
route_todo
route_design
make_manual_routes routes.txt
route_design -directive Quick -preserve
write_checkpoint -force design.dcp
write_bitstream -force design.bit

View File

@ -2,6 +2,7 @@
import os
import random
random.seed(int(os.getenv("SEED"), 16))
import re
from prjxray import util
from prjxray.lut_maker import LutMaker
from prjxray.db import Database
@ -15,8 +16,47 @@ def read_site_to_cmt():
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)
site, cmt, tile = l.strip().split(',')
yield (tile, site, cmt)
def make_ccio_route_options():
# Read the PIP lists
piplist_path = os.path.join(
os.getenv("FUZDIR"), "..", "piplist", "build", "hclk_cmt")
pips = []
for fname in os.listdir(piplist_path):
if not fname.endswith(".txt"):
continue
fullname = os.path.join(piplist_path, fname)
with open(fullname, "r") as fp:
pips += [l.strip() for l in fp.readlines()]
# Get PIPs that mention FREQ_REFn wires. These are the ones that we want
# force routing through.
pips = [p for p in pips if "FREQ_REF" in p]
# Sort by tile type
options = {}
for pip in pips:
tile, dst, src = pip.split(".")
for a, b in ((src, dst), (dst, src)):
match = re.match(r".*FREQ_REF([0-3]).*", a)
if match is not None:
n = int(match.group(1))
if tile not in options:
options[tile] = {}
if n not in options[tile]:
options[tile][n] = set()
options[tile][n].add(b)
return options
class ClockSources(object):
@ -44,6 +84,21 @@ class ClockSources(object):
self.sources[cmt].append(source)
self.source_to_cmt[source] = cmt
def remove_clock_source(self, source, cmt="ANY"):
"""
Removes a clock source from the available clock sources list
"""
if source in self.source_to_cmt:
del self.source_to_cmt[source]
if cmt == "ANY":
for sources in self.sources.values():
if source in sources:
sources.remove(source)
else:
if source in self.sources[cmt]:
self.sources[cmt].remove(source)
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.
@ -121,6 +176,9 @@ def get_paired_iobs(db, grid, tile_name):
idx += 1
# A map of y deltas to CCIO wire indices
CCIO_INDEX = {-1: 0, -3: 1, +2: 3, +4: 2}
# Move from HCLK_IOI column to IOB column
idx += 1
@ -134,7 +192,7 @@ def get_paired_iobs(db, grid, tile_name):
for site, site_type in gridinfo.sites.items():
if site_type in ['IOB33M', 'IOB18M']:
yield tile_name, site, site_type[-3:-1]
yield tile_name, site, site_type[-3:-1], CCIO_INDEX[dy]
def check_allowed(mmcm_pll_dir, cmt):
@ -176,7 +234,16 @@ def main():
clock_sources = ClockSources()
adv_clock_sources = ClockSources()
site_to_cmt = dict(read_site_to_cmt())
tile_site_cmt = list(read_site_to_cmt())
site_to_cmt = {tsc[1]: tsc[2] for tsc in tile_site_cmt}
cmt_to_hclk = {
tsc[2]: tsc[0]
for tsc in tile_site_cmt
if tsc[0].startswith("HCLK_CMT_")
}
ccio_route_options = make_ccio_route_options()
db = Database(util.get_db_root(), util.get_part())
grid = db.grid()
@ -208,11 +275,13 @@ def main():
have_iob_clocks = random.random() > .1
iob_to_hclk = {}
iob_clks = {}
for tile_name in sorted(hclk_cmt_tiles):
for _, site, volt in get_paired_iobs(db, grid, tile_name):
for _, site, volt, ccio in get_paired_iobs(db, grid, tile_name):
iob_clock = 'clock_IBUF_{site}'.format(site=site)
iob_to_hclk[site] = (tile_name, ccio)
cmt = site_to_cmt[site]
if cmt not in iob_clks:
@ -369,8 +438,12 @@ module top({inputs});
hclks_used_by_cmt[src_cmt].add(src)
return src
# Track used IOB sources
used_iob_clks = set()
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,
@ -386,6 +459,11 @@ module top({inputs});
if wire_name is None:
continue
if "IBUF" in wire_name:
used_iob_clks.add(wire_name)
clock_sources.remove_clock_source(wire_name)
adv_clock_sources.remove_clock_source(wire_name)
print(
"""
assign I_{site} = {wire_name};""".format(
@ -404,8 +482,25 @@ module top({inputs});
print(bufhs.getvalue())
for _, site in gen_sites('BUFR'):
# Do not use BUFR always
if random.random() < 0.50:
continue
available_srcs = set(iob_clks[site_to_cmt[site]]) - used_iob_clks
if len(available_srcs) == 0:
continue
src = random.choice(list(available_srcs))
if src != "":
used_iob_clks.add(src)
clock_sources.remove_clock_source(src)
adv_clock_sources.remove_clock_source(src)
adv_clock_sources.add_clock_source(
'O_{site}'.format(site=site), site_to_cmt[site])
print(
"""
wire O_{site};
@ -413,11 +508,33 @@ module top({inputs});
BUFR bufr_{site} (
.I({I}),
.O(O_{site})
);""".format(I=random.choice(iob_clks[site_to_cmt[site]]), site=site))
);""".format(I=src, site=site))
route_file = open("routes.txt", "w")
def fix_ccio_route(net):
# Get the IOB site name
match = re.match(r".*_IBUF_(.*)", net)
assert match is not None, net
iob_site = match.group(1)
# Get associated HCLK_CMT tile and CCIO wire index
hclk_tile_name, ccio = iob_to_hclk[iob_site]
# Get HCLK_CMT tile type
hclk_tile = hclk_tile_name.rsplit("_", maxsplit=1)[0]
# Pick a random route option
opts = list(ccio_route_options[hclk_tile][ccio])
route = random.choice(opts)
route = "{}/{}".format(hclk_tile_name, route)
route_file.write("{} {}\n".format(net, route))
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]
@ -430,6 +547,11 @@ module top({inputs});
if src is None:
continue
if "IBUF" in src:
clock_sources.remove_clock_source(src)
adv_clock_sources.remove_clock_source(src)
fix_ccio_route(src)
print(
"""
assign {cin}_{site} = {csrc};
@ -438,6 +560,7 @@ module top({inputs});
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]
@ -449,6 +572,11 @@ module top({inputs});
if src is None:
continue
if "IBUF" in src:
clock_sources.remove_clock_source(src)
adv_clock_sources.remove_clock_source(src)
fix_ccio_route(src)
print(
"""
assign {cin}_{site} = {csrc};