diff --git a/fuzzers/040-clk-hrow-config/Makefile b/fuzzers/040-clk-hrow-config/Makefile index c815d293..f20a6d84 100644 --- a/fuzzers/040-clk-hrow-config/Makefile +++ b/fuzzers/040-clk-hrow-config/Makefile @@ -5,13 +5,17 @@ include ../fuzzer.mk database: build/segbits_clk_hrow.db 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)) + ${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 ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ --seg-fn-in build/segbits_clk_hrow.rdb \ --seg-fn-out build/segbits_clk_hrow.db - ${XRAY_MASKMERGE} build/mask_clk_hrow.db $(addsuffix /segdata_clk_hrow_top_r.txt,$(SPECIMENS)) $(addsuffix /segdata_clk_hrow_bot_r.txt,$(SPECIMENS)) + ${XRAY_MASKMERGE} build/mask_clk_hrow.db \ + $(addsuffix /segdata_clk_hrow_top_r.txt,$(SPECIMENS)) \ + $(addsuffix /segdata_clk_hrow_bot_r.txt,$(SPECIMENS)) pushdb: database ${XRAY_MERGEDB} clk_hrow_bot_r build/segbits_clk_hrow.db diff --git a/fuzzers/040-clk-hrow-config/generate.py b/fuzzers/040-clk-hrow-config/generate.py index 8fc7d1ee..615f03b0 100644 --- a/fuzzers/040-clk-hrow-config/generate.py +++ b/fuzzers/040-clk-hrow-config/generate.py @@ -25,7 +25,8 @@ def main(): row['site'], '{}.INIT_OUT'.format(base_name), row['INIT_OUT']) segmk.add_site_tag( - row['site'], '{}.ZINV_CE'.format(base_name), 1 ^ row['IS_CE_INVERTED']) + row['site'], '{}.ZINV_CE'.format(base_name), + 1 ^ row['IS_CE_INVERTED']) # SYNC is a zero pattern for opt in ['ASYNC']: diff --git a/fuzzers/040-clk-hrow-config/top.py b/fuzzers/040-clk-hrow-config/top.py index 8979f096..56a0d54f 100644 --- a/fuzzers/040-clk-hrow-config/top.py +++ b/fuzzers/040-clk-hrow-config/top.py @@ -1,18 +1,14 @@ import json import os -import re import random random.seed(int(os.getenv("SEED"), 16)) from prjxray import util from prjxray import verilog from prjxray.db import Database -XY_RE = re.compile('^BUFHCE_X([0-9]+)Y([0-9]+)$') - def gen_sites(): - db = Database(util.get_db_root()) - grid = db.grid() + get_xy = util.create_xy_fun('BUFHCE_') db = Database(util.get_db_root()) grid = db.grid() for tile_name in sorted(grid.tiles()): @@ -24,10 +20,7 @@ def gen_sites(): ys = [] for site, site_type in gridinfo.sites.items(): if site_type == 'BUFHCE': - m = re.match(XY_RE, site) - assert m, site - x = int(m.group(1)) - y = int(m.group(2)) + x, y = get_xy(site) xs.append(x) ys.append(y) diff --git a/fuzzers/041-clk-hrow-pips/clk_table.py b/fuzzers/041-clk-hrow-pips/clk_table.py index fe09fb54..9b3ccdfd 100644 --- a/fuzzers/041-clk-hrow-pips/clk_table.py +++ b/fuzzers/041-clk-hrow-pips/clk_table.py @@ -1,30 +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 % 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) +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_R{}'.format(row)] = (row, 6) -for row in range(6): - CLK_TABLE['CLK_HROW_CK_IN_R{}'.format(row + 8)] = (row, 7) + 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) -# 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) + 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 5fd6cc75..fe4e53c0 100644 --- a/fuzzers/041-clk-hrow-pips/generate.py +++ b/fuzzers/041-clk-hrow-pips/generate.py @@ -6,6 +6,7 @@ import clk_table def main(): segmk = Segmaker("design.bits") + table = clk_table.get_clk_table() print("Loading tags from design.txt.") with open("design.txt", "r") as f: @@ -24,8 +25,8 @@ def main(): rows = set(range(clk_table.CLK_TABLE_NUM_ROWS)) columns = set(range(clk_table.CLK_TABLE_NUM_COLS)) - if src in clk_table.CLK_TABLE: - row, column = clk_table.CLK_TABLE[src] + if src in table: + row, column = table[src] segmk.add_tile_tag( tile, '{}.HCLK_ENABLE_ROW{}'.format(dst, row), 1) diff --git a/fuzzers/041-clk-hrow-pips/merge_clk_entries.py b/fuzzers/041-clk-hrow-pips/merge_clk_entries.py index 89d42fe6..f7e2ea27 100644 --- a/fuzzers/041-clk-hrow-pips/merge_clk_entries.py +++ b/fuzzers/041-clk-hrow-pips/merge_clk_entries.py @@ -50,13 +50,15 @@ def main(): piplists[dst].append(src) + table = clk_table.get_clk_table() + 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: + if src not in table: continue - row, column = clk_table.CLK_TABLE[src] + row, column = table[src] if row not in hrow_outs[dst]['rows']: continue diff --git a/fuzzers/041-clk-hrow-pips/top.py b/fuzzers/041-clk-hrow-pips/top.py index c599770b..fe72f644 100644 --- a/fuzzers/041-clk-hrow-pips/top.py +++ b/fuzzers/041-clk-hrow-pips/top.py @@ -1,27 +1,11 @@ +""" Emits top.v's for various BUFHCE routing inputs. """ import os -import re import random random.seed(int(os.getenv("SEED"), 16)) from prjxray import util from prjxray.db import Database -XY_RE = re.compile('^BUFHCE_X([0-9]+)Y([0-9]+)$') -BUFGCTRL_XY_RE = re.compile('^BUFGCTRL_X([0-9]+)Y([0-9]+)$') -""" -BUFHCE's can be driven from: - -MMCME2_ADV -BUFHCE -PLLE2_ADV -BUFGCTRL -""" - - -def get_xy(s): - m = BUFGCTRL_XY_RE.match(s) - x = int(m.group(1)) - y = int(m.group(2)) - return x, y +CMT_XY_FUN = util.create_xy_fun(prefix='') def gen_sites(desired_site_type): @@ -53,6 +37,7 @@ def gen_bufhce_sites(): 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: @@ -60,10 +45,15 @@ def read_site_to_cmt(): yield (site, cmt) -CMT_RE = re.compile('X([0-9]+)Y([0-9]+)') - - 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 = {} @@ -71,6 +61,10 @@ class ClockSources(object): 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] = [] @@ -80,6 +74,12 @@ class ClockSources(object): 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: @@ -88,9 +88,7 @@ class ClockSources(object): if cmt in self.sources: choices.extend(self.sources[cmt]) - m = CMT_RE.match(cmt) - x = int(m.group(1)) - y = int(m.group(2)) + x, y = CMT_XY_FUN(cmt) if x % 2 == 0: x += 1 @@ -123,17 +121,35 @@ class ClockSources(object): 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': - return (int(CMT_RE.match(cmt).group(1)) & 1) == 1 + x, y = CMT_XY_FUN(cmt) + return (x & 1) == 1 elif mmcm_pll_dir == 'EVEN': - return (int(CMT_RE.match(cmt).group(1)) & 1) == 0 + x, y = CMT_XY_FUN(cmt) + return (x & 1) == 0 else: assert False, mmcm_pll_dir def main(): + """ + BUFHCE's can be driven from: + + MMCME2_ADV + PLLE2_ADV + BUFGCTRL + Local INT connect + + """ + print(''' module top(); ''') @@ -142,6 +158,9 @@ module top(); clock_sources = ClockSources() + # To ensure that all left or right sources are used, sometimes only MMCM/PLL + # 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')) @@ -154,20 +173,6 @@ module top(); wire zero = 0; wire one = 1;""") - for idx in range(1): - wire_name = "lut_wire_{}".format(idx) - #clock_sources.add_clock_source(wire_name, 'ANY') - print( - """ - (* KEEP, DONT_TOUCH *) - wire {wire_name}; - LUT6 lut{idx} ( - .O({wire_name}) - );""".format( - idx=idx, - wire_name=wire_name, - )) - for site in gen_sites('MMCME2_ADV'): mmcm_clocks = [ 'mmcm_clock_{site}_{idx}'.format(site=site, idx=idx) @@ -250,7 +255,8 @@ module top(); c5=pll_clocks[5], )) - for site in sorted(gen_sites("BUFGCTRL"), key=get_xy): + for site in sorted(gen_sites("BUFGCTRL"), + key=util.create_xy_fun('BUFGCTRL_')): wire_name = 'clk_{}'.format(site) if not mmcm_pll_only: @@ -268,8 +274,7 @@ module top(); wire_name=wire_name, )) - bufhce_sites = list(gen_bufhce_sites()) - for tile_name, sites in bufhce_sites: + 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: diff --git a/prjxray/util.py b/prjxray/util.py index 0b4cf689..8b4d80ea 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -22,6 +22,41 @@ def roi_xy(): return (x1, x2), (y1, y2) +def create_xy_fun(prefix): + """ Create function that extracts X and Y coordinate from a prefixed string + + >>> fun = create_xy_fun(prefix='') + >>> fun('X5Y23') + (5, 23) + >>> fun('X0Y0') + (0, 0) + >>> fun('X50Y100') + (50, 100) + + >>> fun = create_xy_fun(prefix='SITE_') + >>> fun('SITE_X5Y23') + (5, 23) + >>> fun('SITE_X0Y0') + (0, 0) + >>> fun('SITE_X50Y100') + (50, 100) + + """ + compiled_re = re.compile( + '^{prefix}X([0-9]+)Y([0-9]+)$'.format(prefix=prefix)) + + def get_xy(s): + m = compiled_re.match(s) + assert m, (prefix, s) + + x = int(m.group(1)) + y = int(m.group(2)) + + return x, y + + return get_xy + + def slice_xy(): '''Return (X1, X2), (Y1, Y2) from XRAY_ROI, exclusive end (for xrange)''' # SLICE_X12Y100:SLICE_X27Y149