diff --git a/fuzzers/041-clk-hrow-pips/Makefile b/fuzzers/041-clk-hrow-pips/Makefile index f2ef3eea..400f43c9 100644 --- a/fuzzers/041-clk-hrow-pips/Makefile +++ b/fuzzers/041-clk-hrow-pips/Makefile @@ -1,20 +1,30 @@ -N ?= 50 +N ?= 150 +SPECIMENS_DEPS=build/cmt_regions.csv include ../fuzzer.mk database: build/segbits_clk_hrow.db +build/clk_hrow_bot_r.txt: clk_hrow_pip_list.tcl + mkdir -p build + cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/clk_hrow_pip_list.tcl + +build/cmt_regions.csv: output_cmt.tcl build/clk_hrow_bot_r.txt + mkdir -p build + cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/output_cmt.tcl + build/segbits_clk_hrow.rdb: $(SPECIMENS_OK) ${XRAY_SEGMATCH} -o build/segbits_clk_hrow.rdb \ $(addsuffix /segdata_clk_hrow_top_r.txt,$(SPECIMENS)) \ $(addsuffix /segdata_clk_hrow_bot_r.txt,$(SPECIMENS)) -build/segbits_clk_hrow.db: build/segbits_clk_hrow.rdb +build/segbits_clk_hrow.db: build/segbits_clk_hrow.rdb build/clk_hrow_bot_r.txt ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ --seg-fn-in build/segbits_clk_hrow.rdb \ --seg-fn-out build/segbits_clk_hrow_rc.db - python3 merge_gclk_entries.py \ + python3 merge_clk_entries.py \ build/segbits_clk_hrow_rc.db \ + build/clk_hrow_bot_r.txt \ build/segbits_clk_hrow.db ${XRAY_MASKMERGE} build/mask_clk_hrow.db \ diff --git a/fuzzers/041-clk-hrow-pips/clk_table.py b/fuzzers/041-clk-hrow-pips/clk_table.py new file mode 100644 index 00000000..0b46564b --- /dev/null +++ b/fuzzers/041-clk-hrow-pips/clk_table.py @@ -0,0 +1,34 @@ +HCLKS = 24 +GCLKS = 32 +SIDE_CLK_INPUTS = 14 +CLK_TABLE = {} + +CLK_TABLE_NUM_ROWS = 8 +CLK_TABLE_NUM_COLS = 8 + +for gclk in range(GCLKS): + gclk_name = 'CLK_HROW_R_CK_GCLK{}'.format(gclk) + row = gclk % 8 + column = int(gclk / 8) + CLK_TABLE[gclk_name] = (row, column) + +for lr in ['L', 'R']: + for side_inputs in range(SIDE_CLK_INPUTS): + side_clk_name = 'CLK_HROW_CK_IN_{}{}'.format(lr, side_inputs) + +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) diff --git a/fuzzers/041-clk-hrow-pips/generate.py b/fuzzers/041-clk-hrow-pips/generate.py index ff1fcb61..1974ba74 100644 --- a/fuzzers/041-clk-hrow-pips/generate.py +++ b/fuzzers/041-clk-hrow-pips/generate.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -import re +import clk_table def main(): @@ -21,27 +21,23 @@ def main(): _, src = src.split("/") _, dst = dst.split("/") - rows = set(range(8)) - columns = set(range(4)) + rows = set(range(clk_table.CLK_TABLE_NUM_ROWS)) + columns = set(range(clk_table.CLK_TABLE_NUM_COLS)) - m = re.match('^CLK_HROW_R_CK_GCLK([0-9]+)$', src) - if m: - gclk = int(m.group(1)) - row = gclk % 8 - column = int(gclk / 8) + if src in clk_table.CLK_TABLE: + row, column = clk_table.CLK_TABLE[src] - segmk.add_tile_tag(tile, '{}.GCLK_ENABLE_ROW{}'.format(dst, row), 1) - segmk.add_tile_tag(tile, '{}.GCLK_ENABLE_COLUMN{}'.format(dst, column), 1) + segmk.add_tile_tag(tile, '{}.HCLK_ENABLE_ROW{}'.format(dst, row), 1) + segmk.add_tile_tag(tile, '{}.HCLK_ENABLE_COLUMN{}'.format(dst, column), 1) rows.remove(row) columns.remove(column) for row in rows: - segmk.add_tile_tag(tile, '{}.GCLK_ENABLE_ROW{}'.format(dst, row), 0) + segmk.add_tile_tag(tile, '{}.HCLK_ENABLE_ROW{}'.format(dst, row), 0) for column in columns: - segmk.add_tile_tag(tile, '{}.GCLK_ENABLE_COLUMN{}'.format(dst, column), 0) - + segmk.add_tile_tag(tile, '{}.HCLK_ENABLE_COLUMN{}'.format(dst, column), 0) segmk.compile() segmk.write() diff --git a/fuzzers/041-clk-hrow-pips/generate.tcl b/fuzzers/041-clk-hrow-pips/generate.tcl index 9932ddc7..05210e30 100644 --- a/fuzzers/041-clk-hrow-pips/generate.tcl +++ b/fuzzers/041-clk-hrow-pips/generate.tcl @@ -30,6 +30,8 @@ proc run {} { set_property CONFIG_VOLTAGE 3.3 [current_design] set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] set_property IS_ENABLED 0 [get_drc_checks {REQP-161}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-123}] + set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets] place_design diff --git a/fuzzers/041-clk-hrow-pips/merge_gclk_entries.py b/fuzzers/041-clk-hrow-pips/merge_clk_entries.py similarity index 55% rename from fuzzers/041-clk-hrow-pips/merge_gclk_entries.py rename to fuzzers/041-clk-hrow-pips/merge_clk_entries.py index 20ae0b77..53005716 100644 --- a/fuzzers/041-clk-hrow-pips/merge_gclk_entries.py +++ b/fuzzers/041-clk-hrow-pips/merge_clk_entries.py @@ -1,10 +1,10 @@ import argparse - -GCLKS = 32 +import clk_table def main(): - parser = argparse.ArgumentParser(description="Convert GCLK ROW/COLUMN definitions into GCLK pips.") + 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() @@ -37,16 +37,34 @@ def main(): assert src[-7:-1] == 'COLUMN', src hrow_outs[dst]['columns'][n] = bits - with open(args.out_segbit, 'w') as f: - for dst in hrow_outs: - for gclk in range(GCLKS): - row = gclk % 8 - column = int(gclk / 8) + piplists = {} + with open(args.piplist) as f: + for l in f: + tile, dst, src = l.strip().split('.') + assert tile == 'CLK_HROW_BOT_R', tile - print('{tile}.{dst}.CLK_HROW_R_CK_GCLK{gclk} {row_bits} {column_bits}'.format( - tile=tile, + if dst not in piplists: + piplists[dst] = [] + + piplists[dst].append(src) + + with open(args.out_segbit, 'w') as f: + for dst in sorted(hrow_outs): + for src in sorted(piplists[dst]): + if src not in clk_table.CLK_TABLE: + continue + + row, column = clk_table.CLK_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, - gclk=gclk, + inclk=src, row_bits=hrow_outs[dst]['rows'][row], column_bits=hrow_outs[dst]['columns'][column], ), file=f) diff --git a/fuzzers/041-clk-hrow-pips/top.py b/fuzzers/041-clk-hrow-pips/top.py index 44a153ff..32d45177 100644 --- a/fuzzers/041-clk-hrow-pips/top.py +++ b/fuzzers/041-clk-hrow-pips/top.py @@ -63,17 +63,22 @@ class ClockSources(object): def __init__(self): self.sources = {} self.merged_sources = {} + self.source_to_cmt = {} + self.used_sources_from_cmt = {} def add_clock_source(self, source, 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): if cmt not in self.merged_sources: choices = [] - choices.extend(self.sources['ANY']) + if 'ANY' in self.sources: + choices.extend(self.sources['ANY']) if cmt in self.sources: choices.extend(self.sources[cmt]) @@ -94,15 +99,49 @@ class ClockSources(object): self.merged_sources[cmt] = choices - return random.choice(self.merged_sources[cmt]) + 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 other_sources(): +def check_allowed(mmcm_pll_dir, cmt): + if mmcm_pll_dir == 'BOTH': + return True + elif mmcm_pll_dir == 'ODD': + return (int(CMT_RE.match(cmt).group(1)) & 1) == 1 + elif mmcm_pll_dir == 'EVEN': + return (int(CMT_RE.match(cmt).group(1)) & 1) == 0 + else: + assert False, mmcm_pll_dir + +def main(): + print(''' +module top(); + ''') + site_to_cmt = dict(read_site_to_cmt()) clock_sources = ClockSources() - clock_sources.add_clock_source('one', 'ANY') - clock_sources.add_clock_source('zero', 'ANY') + + mmcm_pll_only = random.randint(0, 1) + mmcm_pll_dir = random.choice(('ODD', 'EVEN', 'BOTH')) + + if not mmcm_pll_only: + for _ in range(2): + clock_sources.add_clock_source('one', 'ANY') + clock_sources.add_clock_source('zero', 'ANY') print(""" wire zero = 0; @@ -110,7 +149,7 @@ def other_sources(): for idx in range(1): wire_name = "lut_wire_{}".format(idx) - clock_sources.add_clock_source(wire_name, 'ANY') + #clock_sources.add_clock_source(wire_name, 'ANY') print(""" (* KEEP, DONT_TOUCH *) wire {wire_name}; @@ -121,11 +160,59 @@ def other_sources(): wire_name=wire_name, )) + for site in gen_sites('MMCME2_ADV'): + mmcm_clocks = ['mmcm_clock_{site}_{idx}'.format(site=site, idx=idx) for + idx in range(13)] + + if not check_allowed(mmcm_pll_dir, site_to_cmt[site]): + continue + + for clk in mmcm_clocks: + clock_sources.add_clock_source(clk, site_to_cmt[site]) + + print(""" + wire {c0}, {c1}, {c2}, {c3}, {c4}, {c5}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + MMCME2_ADV pll_{site} ( + .CLKOUT0({c0}), + .CLKOUT0B({c1}), + .CLKOUT1({c2}), + .CLKOUT1B({c3}), + .CLKOUT2({c4}), + .CLKOUT2B({c5}), + .CLKOUT3({c6}), + .CLKOUT3B({c7}), + .CLKOUT4({c8}), + .CLKOUT5({c9}), + .CLKOUT6({c10}), + .CLKFBOUT({c11}), + .CLKFBOUTB({c12}) + ); + """.format( + site=site, + c0=mmcm_clocks[0], + c1=mmcm_clocks[1], + c2=mmcm_clocks[2], + c3=mmcm_clocks[3], + c4=mmcm_clocks[4], + c5=mmcm_clocks[5], + c6=mmcm_clocks[6], + c7=mmcm_clocks[7], + c8=mmcm_clocks[8], + c9=mmcm_clocks[9], + c10=mmcm_clocks[10], + c11=mmcm_clocks[11], + c12=mmcm_clocks[12], + )) + for site in gen_sites('PLLE2_ADV'): pll_clocks = ['pll_clock_{site}_{idx}'.format(site=site, idx=idx) for idx in range(6)] - for clk in pll_clocks[:2]: + if not check_allowed(mmcm_pll_dir, site_to_cmt[site]): + continue + + for clk in pll_clocks: clock_sources.add_clock_source(clk, site_to_cmt[site]) print(""" @@ -149,16 +236,11 @@ def other_sources(): c5=pll_clocks[5], )) - -def main(): - print(''' -module top(); - ''') - - gclks = [] for site in sorted(gen_sites("BUFGCTRL"), key=get_xy): wire_name = 'clk_{}'.format(site) - gclks.append(wire_name) + + if not mmcm_pll_only: + clock_sources.add_clock_source(wire_name, 'ANY') print(""" wire {wire_name}; @@ -174,14 +256,18 @@ module top(); bufhce_sites = list(gen_bufhce_sites()) for tile_name, sites in bufhce_sites: for site in sites: + wire_name = clock_sources.get_random_source(site_to_cmt[site]) + if wire_name is None: + continue + print(""" - (* KEEP, DONT_TOUCH, LOC = "{site}" *) - BUFHCE buf_{site} ( - .I({wire_name}) - ); + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + BUFHCE buf_{site} ( + .I({wire_name}) + ); """.format( site=site, - wire_name=random.choice(gclks), + wire_name=wire_name, )) print("endmodule") diff --git a/fuzzers/fuzzer.mk b/fuzzers/fuzzer.mk index 0f260ea2..d7cc8010 100644 --- a/fuzzers/fuzzer.mk +++ b/fuzzers/fuzzer.mk @@ -2,13 +2,14 @@ N ?= 1 SPECIMENS := $(addprefix build/specimen_,$(shell seq -f '%03.0f' $(N))) SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS)) ENV_VAR ?= +SPECIMENS_DEPS ?= FUZDIR ?= ${PWD} all: database # generate.sh / top_generate.sh call make, hence the command must # have a + before it. -$(SPECIMENS_OK): +$(SPECIMENS_OK): $(SPECIMENS_DEPS) mkdir -p build +if [ -f $(FUZDIR)/generate.sh ]; then \ export $(ENV_VAR); \