diff --git a/fuzzers/005-tilegrid/generate.py b/fuzzers/005-tilegrid/generate.py index 59e83346..4a70a2af 100644 --- a/fuzzers/005-tilegrid/generate.py +++ b/fuzzers/005-tilegrid/generate.py @@ -1,248 +1,569 @@ #!/usr/bin/env python3 +''' +Historically we grouped data into "segments" +These were a region of the bitstream that encoded one or more tiles +However, this didn't scale with certain tiles like BRAM +Some sites had multiple bitstream areas and also occupied multiple tiles + +Decoding was then shifted to instead describe how each title is encoded +A post processing step verifies that two tiles don't reference the same bitstream area +''' import os, sys, json, re -####################################### -# Read +# matches lib/include/prjxray/xilinx/xc7series/block_type.h +block_type_i2s = { + 0: 'CLB_IO_CLK', + 1: 'BLOCK_RAM', + 2: 'CFG_CLB', + # special...maybe should error until we know what it is? + # 3: 'RESERVED', +} -tiles = list() -site_baseaddr = dict() -tile_baseaddr = dict() -with open("tiles.txt") as f: - for line in f: - tiles.append(line.split()) +def addr2btype(base_addr): + ''' + Convert integer address to block type -for arg in sys.argv[1:]: - with open(arg) as f: - line = f.read().strip() - site = arg[7:-6] - frame = int(line[5:5 + 8], 16) - site_baseaddr[site] = "0x%08x" % (frame & ~0x7f) + Table 5-24: Frame Address Register Description + Bit Index: [25:23] + https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf + "Valid block types are CLB, I/O, CLK ( 000 ), block RAM content ( 001 ), and CFG_CLB ( 010 ). A normal bitstream does not include type 011 ." + ''' + block_type_i = (base_addr >> 23) & 0x7 + return block_type_i2s[block_type_i] -####################################### -# Create initial database -database = dict() -database["tiles"] = dict() -database["segments"] = dict() -tiles_by_grid = dict() +def nolr(tile_type): + ''' + Remove _L or _R suffix tile_type suffix, if present + Ex: BRAM_INT_INTERFACE_L => BRAM_INT_INTERFACE + Ex: VBRK => VBRK + ''' + postfix = tile_type[-2:] + if postfix in ('_L', '_R'): + return tile_type[:-2] + else: + return tile_type -for record in tiles: - tile_type, tile_name, grid_x, grid_y = record[0:4] - grid_x, grid_y = int(grid_x), int(grid_y) - tiles_by_grid[(grid_x, grid_y)] = tile_name - framebaseaddr = None - database["tiles"][tile_name] = { - "type": tile_type, - "sites": dict(), - "grid_x": grid_x, - "grid_y": grid_y - } +def load_tiles(tiles_fn): + ''' + "$type $tile $grid_x $grid_y $typed_sites" + typed_sites: foreach t $site_types s $sites + ''' + tiles = list() - if len(record) > 4: - for i in range(4, len(record), 2): - site_type, site_name = record[i:i + 2] - if site_name in site_baseaddr: - framebaseaddr = site_baseaddr[site_name] - database["tiles"][tile_name]["sites"][site_name] = site_type + with open(tiles_fn) as f: + for line in f: + # CLBLM_L CLBLM_L_X10Y98 30 106 SLICEL SLICE_X13Y98 SLICEM SLICE_X12Y98 + record = line.split() + tile_type, tile_name, grid_x, grid_y = record[0:4] + grid_x, grid_y = int(grid_x), int(grid_y) + sites = {} + for i in range(4, len(record), 2): + site_type, site_name = record[i:i + 2] + sites[site_name] = site_type + tile = { + 'type': tile_type, + 'name': tile_name, + 'grid_x': grid_x, + 'grid_y': grid_y, + 'sites': sites, + } + tiles.append(tile) - if framebaseaddr is not None: - tile_baseaddr[tile_name] = [framebaseaddr, 0] + return tiles -####################################### -# Add Segments -for tile_name, tile_data in database["tiles"].items(): - tile_type = tile_data["type"] - grid_x = tile_data["grid_x"] - grid_y = tile_data["grid_y"] +def load_baseaddrs(deltas_fns): + site_baseaddr = dict() + for arg in deltas_fns: + with open(arg) as f: + line = f.read().strip() + site = arg[7:-6] + frame = int(line[5:5 + 8], 16) + # was "0x%08x" + site_baseaddr[site] = frame & ~0x7f - if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R"]: - if tile_type in ["CLBLL_L", "CLBLM_L"]: - int_tile_name = tiles_by_grid[(grid_x + 1, grid_y)] - else: - int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)] + return site_baseaddr - segment_name = "SEG_" + tile_name - segtype = tile_type.lower() - database["segments"][segment_name] = dict() - database["segments"][segment_name]["tiles"] = [ - tile_name, int_tile_name - ] - database["segments"][segment_name]["type"] = segtype - database["segments"][segment_name]["frames"] = 36 - database["segments"][segment_name]["words"] = 2 +def make_database(tiles): + # tile database with X, Y, and list of sites + # tile name as keys + database = dict() - if tile_name in tile_baseaddr: - database["segments"][segment_name]["baseaddr"] = tile_baseaddr[ - tile_name] + for tile in tiles: + database[tile["name"]] = { + "type": tile["type"], + "sites": tile["sites"], + "grid_x": tile["grid_x"], + "grid_y": tile["grid_y"], + "bits": {}, + } - database["tiles"][tile_name]["segment"] = segment_name - database["tiles"][int_tile_name]["segment"] = segment_name + return database - if tile_type in ["HCLK_L", "HCLK_R"]: - segment_name = "SEG_" + tile_name - segtype = tile_type.lower() - database["segments"][segment_name] = dict() - database["segments"][segment_name]["tiles"] = [tile_name] - database["segments"][segment_name]["type"] = segtype - database["segments"][segment_name]["frames"] = 26 - database["segments"][segment_name]["words"] = 1 - database["tiles"][tile_name]["segment"] = segment_name +def make_tile_baseaddrs(tiles, site_baseaddr, verbose=False): + # Look up a base address by tile name + tile_baseaddrs = dict() - if tile_type in ["BRAM_L", "DSP_L", "BRAM_R", "DSP_R"]: - for k in range(5): - if tile_type in ["BRAM_L", "DSP_L"]: - interface_tile_name = tiles_by_grid[(grid_x + 1, grid_y - k)] - int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)] + verbose and print('') + verbose and print('%u tiles' % len(tiles)) + added = 0 + for tile in tiles: + for site_name in tile["sites"].keys(): + if site_name not in site_baseaddr: + continue + framebaseaddr = site_baseaddr[site_name] + bt = addr2btype(framebaseaddr) + tile_baseaddr = tile_baseaddrs.setdefault(tile["name"], {}) + if bt in tile_baseaddr: + # actually lets just fail these, better to remove at tcl level to speed up processing + assert 0, 'duplicate base address' + assert tile_baseaddr[bt] == [framebaseaddr, 0] else: - interface_tile_name = tiles_by_grid[(grid_x - 1, grid_y - k)] - int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)] + tile_baseaddr[bt] = [framebaseaddr, 0] + verbose and print( + "baseaddr: %s.%s @ %s.0x%08x" % + (tile["name"], site_name, bt, framebaseaddr)) + added += 1 - segment_name = "SEG_" + tile_name.replace("_", "%d_" % k, 1) - segtype = tile_type.lower().replace("_", "%d_" % k, 1) + assert added + return tile_baseaddrs - database["segments"][segment_name] = dict() - database["segments"][segment_name]["type"] = segtype - database["segments"][segment_name]["frames"] = 28 - database["segments"][segment_name]["words"] = 2 - if k == 0: - database["segments"][segment_name]["tiles"] = [ - tile_name, interface_tile_name, int_tile_name - ] - database["tiles"][tile_name]["segment"] = segment_name - database["tiles"][interface_tile_name][ - "segment"] = segment_name - database["tiles"][int_tile_name]["segment"] = segment_name +def make_tiles_by_grid(tiles): + # lookup tile names by (X, Y) + tiles_by_grid = dict() + + for tile in tiles: + tiles_by_grid[(tile["grid_x"], tile["grid_y"])] = tile["name"] + + return tiles_by_grid + + +def make_segments(database, tiles_by_grid, tile_baseaddrs, verbose=False): + ''' + Create segments data structure + Indicates how tiles are related to bitstream locations + Also modify database to annotate which segment the tiles belong to + + segments key examples: + SEG_CLBLM_R_X13Y72 + SEG_BRAM3_L_X6Y85 + ''' + segments = dict() + + verbose and print('') + for tile_name, tile_data in database.items(): + tile_type = tile_data["type"] + grid_x = tile_data["grid_x"] + grid_y = tile_data["grid_y"] + + def add_segment(name, tiles, segtype, baseaddr=None): + assert name not in segments + segment = segments.setdefault(name, {}) + segment["tiles"] = tiles + segment["type"] = segtype + if baseaddr: + verbose and print( + 'make_segment: %s baseaddr %s' % ( + name, + baseaddr, + )) + segment["baseaddr"] = baseaddr + + for tile_name in tiles: + database[tile_name]["segment"] = name + + def process_clb(): + if tile_type in ["CLBLL_L", "CLBLM_L"]: + int_tile_name = tiles_by_grid[(grid_x + 1, grid_y)] else: - database["segments"][segment_name]["tiles"] = [ - interface_tile_name, int_tile_name - ] - database["tiles"][interface_tile_name][ - "segment"] = segment_name - database["tiles"][int_tile_name]["segment"] = segment_name + int_tile_name = tiles_by_grid[(grid_x - 1, grid_y)] -####################################### -# Populate segment base addresses: L/R along INT column + add_segment( + name="SEG_" + tile_name, + tiles=[tile_name, int_tile_name], + segtype=tile_type.lower(), + baseaddr=tile_baseaddrs.get(tile_name, None)) -for segment_name in database["segments"].keys(): - if "baseaddr" in database["segments"][segment_name]: - framebase, wordbase = database["segments"][segment_name]["baseaddr"] - inttile = [ - tile for tile in database["segments"][segment_name]["tiles"] - if database["tiles"][tile]["type"] in ["INT_L", "INT_R"] - ][0] - grid_x = database["tiles"][inttile]["grid_x"] - grid_y = database["tiles"][inttile]["grid_y"] + def process_hclk(): + add_segment( + name="SEG_" + tile_name, + tiles=[tile_name], + segtype=tile_type.lower()) - if database["tiles"][inttile]["type"] == "INT_L": - grid_x += 1 - framebase = "0x%08x" % (int(framebase, 16) + 0x80) - else: - grid_x -= 1 - framebase = "0x%08x" % (int(framebase, 16) - 0x80) + def process_bram_dsp(): + for k in range(5): + if tile_type in ["BRAM_L", "DSP_L"]: + interface_tile_name = tiles_by_grid[( + grid_x + 1, grid_y - k)] + int_tile_name = tiles_by_grid[(grid_x + 2, grid_y - k)] + elif tile_type in ["BRAM_R", "DSP_R"]: + interface_tile_name = tiles_by_grid[( + grid_x - 1, grid_y - k)] + int_tile_name = tiles_by_grid[(grid_x - 2, grid_y - k)] + else: + assert 0 + ''' + BRAM/DSP itself is at the base y address + There is one huge switchbox on the right for the 5 tiles + These fan into 5 BRAM_INT_INTERFACE tiles each which feed into their own CENTER_INTER (just like a CLB has) + ''' + if k == 0: + tiles = [tile_name, interface_tile_name, int_tile_name] + baseaddr = tile_baseaddrs.get(tile_name, None) + else: + tiles = [interface_tile_name, int_tile_name] + baseaddr = None - if (grid_x, grid_y) not in tiles_by_grid: + add_segment( + # BRAM_L_X6Y70 => SEG_BRAM4_L_X6Y70 + name="SEG_" + tile_name.replace("_", "%d_" % k, 1), + tiles=tiles, + # BRAM_L => bram4_l + segtype=tile_type.lower().replace("_", "%d_" % k, 1), + baseaddr=baseaddr) + + def process_default(): + #verbose and nolr(tile_type) not in ('VBRK', 'INT', 'NULL') and print('make_segment: drop %s' % (tile_type,)) + pass + + { + "CLBLL": process_clb, + "CLBLM": process_clb, + "HCLK": process_hclk, + "BRAM": process_bram_dsp, + "DSP": process_bram_dsp, + }.get(nolr(tile_type), process_default)() + + return segments + + +def get_inttile(database, segment): + '''Return interconnect tile for given segment''' + inttiles = [ + tile for tile in segment["tiles"] + if database[tile]["type"] in ["INT_L", "INT_R"] + ] + assert len(inttiles) == 1 + return inttiles[0] + + +def get_bramtile(database, segment): + inttiles = [ + tile for tile in segment["tiles"] + if database[tile]["type"] in ["BRAM_L", "BRAM_R"] + ] + assert len(inttiles) == 1 + return inttiles[0] + + +def seg_base_addr_lr_INT(database, segments, tiles_by_grid, verbose=False): + '''Populate segment base addresses: L/R along INT column''' + ''' + Create BRAM base addresses based on nearby CLBs + ie if we have a BRAM_L, compute as nearby CLB_R base address + offset + ''' + + verbose and print('') + for segment_name in sorted(segments.keys()): + segment = segments[segment_name] + baseaddrs = segment.get("baseaddr", None) + if not baseaddrs: continue - tile = tiles_by_grid[(grid_x, grid_y)] + for block_type, (framebase, wordbase) in sorted(baseaddrs.items()): + verbose and print( + 'lr_INT: %s: %s.0x%08X:%u' % + (segment_name, block_type, framebase, wordbase)) + if block_type != 'CLB_IO_CLK': + verbose and print(' Skip non CLB') + continue - if database["tiles"][inttile]["type"] == "INT_L": - assert database["tiles"][tile]["type"] == "INT_R" - elif database["tiles"][inttile]["type"] == "INT_R": - assert database["tiles"][tile]["type"] == "INT_L" - else: - assert 0 + inttile = get_inttile(database, segment) + grid_x = database[inttile]["grid_x"] + grid_y = database[inttile]["grid_y"] - assert "segment" in database["tiles"][tile] + if database[inttile]["type"] == "INT_L": + grid_x += 1 + framebase = framebase + 0x80 + elif database[inttile]["type"] == "INT_R": + grid_x -= 1 + framebase = framebase - 0x80 + else: + assert 0 - seg = database["tiles"][tile]["segment"] + # ROI at edge? + if (grid_x, grid_y) not in tiles_by_grid: + verbose and print(' Skip edge') + continue - if "baseaddr" in database["segments"][seg]: - assert database["segments"][seg]["baseaddr"] == [ - framebase, wordbase - ] - else: - database["segments"][seg]["baseaddr"] = [framebase, wordbase] + tile = tiles_by_grid[(grid_x, grid_y)] -####################################### -# Populate segment base addresses: Up along INT/HCLK columns + if database[inttile]["type"] == "INT_L": + assert database[tile]["type"] == "INT_R" + elif database[inttile]["type"] == "INT_R": + assert database[tile]["type"] == "INT_L" + else: + assert 0 -start_segments = list() + assert "segment" in database[tile] -for segment_name in database["segments"].keys(): - if "baseaddr" in database["segments"][segment_name]: - start_segments.append(segment_name) + seg = database[tile]["segment"] -for segment_name in start_segments: - framebase, wordbase = database["segments"][segment_name]["baseaddr"] - inttile = [ - tile for tile in database["segments"][segment_name]["tiles"] - if database["tiles"][tile]["type"] in ["INT_L", "INT_R"] - ][0] - grid_x = database["tiles"][inttile]["grid_x"] - grid_y = database["tiles"][inttile]["grid_y"] + seg_baseaddrs = segments[seg].setdefault("baseaddr", {}) + # At least one duplicate when we re-compute the entry for the base address + # should give the same address + if block_type in seg_baseaddrs: + assert seg_baseaddrs[block_type] == [ + framebase, wordbase + ], (seg_baseaddrs[block_type], [framebase, wordbase]) + verbose and print(' Existing OK') + else: + seg_baseaddrs[block_type] = [framebase, wordbase] + verbose and print(' Add new') - for i in range(50): - grid_y -= 1 - if wordbase == 50: - wordbase += 1 - else: - wordbase += 2 +def seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=False): + '''Populate segment base addresses: Up along INT/HCLK columns''' - segname = database["tiles"][tiles_by_grid[(grid_x, grid_y)]]["segment"] - database["segments"][segname]["baseaddr"] = [framebase, wordbase] + verbose and print('') + # Copy the initial list containing only base addresses + # and soon to have derived addresses + src_segment_names = list() + for segment_name in segments.keys(): + if "baseaddr" in segments[segment_name]: + src_segment_names.append(segment_name) -####################################### -# Transfer segment data into tiles + verbose and print('up_INT: %u base addresses' % len(src_segment_names)) -for segment_name in database["segments"].keys(): - try: - baseaddr, offset = database["segments"][segment_name]["baseaddr"] - except: - print('Failed on segment name %s' % segment_name) - raise - for tile_name in database["segments"][segment_name]["tiles"]: - tile_type = database["tiles"][tile_name]["type"] - if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R", "INT_L", - "INT_R"]: - database["tiles"][tile_name]["baseaddr"] = baseaddr - database["tiles"][tile_name]["offset"] = offset - database["tiles"][tile_name]["height"] = 2 - elif tile_type in ["HCLK_L", "HCLK_R"]: - database["tiles"][tile_name]["baseaddr"] = baseaddr - database["tiles"][tile_name]["offset"] = offset - database["tiles"][tile_name]["height"] = 1 - elif tile_type in ["BRAM_L", "BRAM_R", "DSP_L", "DSP_R"]: - database["tiles"][tile_name]["baseaddr"] = baseaddr - database["tiles"][tile_name]["offset"] = offset - database["tiles"][tile_name]["height"] = 10 - elif tile_type in ["INT_INTERFACE_L", "INT_INTERFACE_R", - "BRAM_INT_INTERFACE_L", "BRAM_INT_INTERFACE_R"]: - continue - else: - # print(tile_type, offset) - assert False + for src_segment_name in sorted(src_segment_names): + src_segment = segments[src_segment_name] -# TODO: Migrate to new tilegrid format via library. This data is added for -# compability with unconverted tools. Update tools then remove this data from -# tilegrid.json. -for tiledata in database['tiles'].values(): - if "segment" in tiledata: - segment = tiledata['segment'] - tiledata['frames'] = database['segments'][segment]['frames'] - tiledata['words'] = database['segments'][segment]['words'] - tiledata['segment_type'] = database['segments'][segment]['type'] + for block_type, (framebase, + wordbase) in sorted(src_segment["baseaddr"].items()): + verbose and print( + 'up_INT: %s: %s.0x%08X:%u' % + (src_segment_name, block_type, framebase, wordbase)) -database = database["tiles"] + def process_CLB_IO_CLK(wordbase): + ''' + Lookup interconnect tile associated with this segment + Use it to locate in the grid, and find other segments related by tile offset + ''' -####################################### -# Write + inttile = get_inttile(database, src_segment) + verbose and print( + ' up_INT CLK_IO_CLK: %s => inttile %s' % + (src_segment_name, inttile)) + grid_x = database[inttile]["grid_x"] + grid_y = database[inttile]["grid_y"] -print(json.dumps(database, sort_keys=True, indent="\t")) + for i in range(50): + grid_y -= 1 + dst_tile = database[tiles_by_grid[(grid_x, grid_y)]] + + if wordbase == 50: + wordbase += 1 + else: + wordbase += 2 + + #verbose and print(' dst_tile', dst_tile) + dst_segment_name = dst_tile["segment"] + #verbose and print('up_INT: %s => %s' % (src_segment_name, dst_segment_name)) + segments[dst_segment_name].setdefault( + "baseaddr", {})[block_type] = [framebase, wordbase] + + def process_BLOCK_RAM(wordbase): + ''' + Lookup BRAM0 tile associated with this segment + Use it to locate in the grid, and find other BRAM0 related by tile offset + + + From minitest: + + build/roi_bramd_bit01.diff (lowest BRAM coordinate) + > bit_00c00000_000_00 + + build/roi_bramds_bit01.diff + > bit_00c00000_000_00 + > bit_00c00000_010_00 + > bit_00c00000_020_00 + > bit_00c00000_030_00 + > bit_00c00000_040_00 + > bit_00c00000_051_00 + > bit_00c00000_061_00 + > bit_00c00000_071_00 + > bit_00c00000_081_00 + > bit_00c00000_091_00 + ''' + src_tile_name = get_bramtile(database, src_segment) + verbose and print( + ' up_INT BLOCK_RAM: %s => %s' % + (src_segment_name, src_tile_name)) + grid_x = database[src_tile_name]["grid_x"] + grid_y = database[src_tile_name]["grid_y"] + + for i in range(9): + grid_y -= 5 + wordbase += 10 + # Skip HCLK + if i == 4: + grid_y -= 1 + wordbase += 1 + + dst_tile = database[tiles_by_grid[(grid_x, grid_y)]] + assert nolr(dst_tile['type']) == 'BRAM', dst_tile + + dst_segment_name = dst_tile["segment"] + assert 'BRAM0' in dst_segment_name + segments[dst_segment_name].setdefault( + "baseaddr", {})[block_type] = [framebase, wordbase] + + { + 'CLB_IO_CLK': process_CLB_IO_CLK, + 'BLOCK_RAM': process_BLOCK_RAM, + }[block_type]( + wordbase) + + +def add_tile_bits(tile_db, baseaddr, offset, frames, words, height=None): + ''' + Record data structure geometry for the given tile baseaddr + For most tiles there is only one baseaddr, but some like BRAM have multiple + + Notes on multiple block types: + https://github.com/SymbiFlow/prjxray/issues/145 + ''' + + bits = tile_db['bits'] + block_type = addr2btype(baseaddr) + + assert 0 <= offset <= 100, offset + assert 1 <= words <= 101 + assert offset + words <= 101, ( + tile_db, offset + words, offset, words, block_type) + + assert block_type not in bits + block = bits.setdefault(block_type, {}) + + # FDRI address + block["baseaddr"] = '0x%08X' % baseaddr + # Number of frames this entry is sretched across + # that is the following FDRI addresses are used: range(baseaddr, baseaddr + frames) + block["frames"] = frames + + # Index of first word used within each frame + block["offset"] = offset + # Number of words consumed in each frame + block["words"] = words + + # related to words... + # deprecated field? Don't worry about for now + if height is not None: + block["height"] = height + + +def db_add_bits(database, segments): + '''Transfer segment data into tiles''' + for segment_name in segments.keys(): + for block_type, (baseaddr, + offset) in segments[segment_name]["baseaddr"].items(): + for tile_name in segments[segment_name]["tiles"]: + tile_type = database[tile_name]["type"] + entry = { + # (tile_type, block_type): (frames, words, height) + ("CLBLL", "CLB_IO_CLK"): (36, 2, 2), + ("CLBLM", "CLB_IO_CLK"): (36, 2, 2), + ("HCLK", "CLB_IO_CLK"): (26, 1, 1), + ("INT", "CLB_IO_CLK"): (28, 2, 2), + ("BRAM", "CLB_IO_CLK"): (28, 2, None), + ("BRAM", "BLOCK_RAM"): (128, 5, None), + ("DSP", "CLB_IO_CLK"): (28, 2, 10), + ("INT_INTERFACE", "CLB_IO_CLK"): (28, 2, None), + ("BRAM_INT_INTERFACE", "CLB_IO_CLK"): (28, 2, None), + }.get((nolr(tile_type), block_type), None) + if entry is None: + # Other types are rare, not expected to have these + if block_type == "CLB_IO_CLK": + raise ValueError("Unknown tile type %s" % tile_type) + continue + + frames, words, height = entry + if frames: + # if we have a width, we should have a height + assert frames and words + add_tile_bits( + database[tile_name], baseaddr, offset, frames, words, + height) + + +def db_add_segments(database, segments): + # TODO: Migrate to new tilegrid format via library. This data is added for + # compability with unconverted tools. Update tools then remove this data from + # tilegrid.json. + # looks like only htmlgen is using this? + for tiledata in database.values(): + if "segment" in tiledata: + segment = tiledata["segment"] + tiledata["segment_type"] = segments[segment]["type"] + + +def run(tiles_fn, json_fn, deltas_fns, verbose=False): + # Load input files + tiles = load_tiles(tiles_fn) + site_baseaddr = load_baseaddrs(deltas_fns) + + # Index input + database = make_database(tiles) + tile_baseaddrs = make_tile_baseaddrs(tiles, site_baseaddr, verbose=verbose) + tiles_by_grid = make_tiles_by_grid(tiles) + + segments = make_segments( + database, tiles_by_grid, tile_baseaddrs, verbose=verbose) + + # Reference adjacent CLBs to locate adjacent tiles by known offsets + seg_base_addr_lr_INT(database, segments, tiles_by_grid, verbose=verbose) + seg_base_addr_up_INT(database, segments, tiles_by_grid, verbose=verbose) + + db_add_bits(database, segments) + db_add_segments(database, segments) + + # Save + json.dump( + database, + open(json_fn, 'w'), + sort_keys=True, + indent=4, + separators=(',', ': ')) + + +def main(): + import argparse + import glob + + parser = argparse.ArgumentParser( + description='Generate tilegrid.json from bitstream deltas') + + parser.add_argument('--verbose', action='store_true', help='') + parser.add_argument('--out', default='/dev/stdout', help='Output JSON') + parser.add_argument( + '--tiles', default='tiles.txt', help='Input tiles.txt tcl output') + parser.add_argument( + 'deltas', nargs='*', help='.bit diffs to create base addresses from') + args = parser.parse_args() + + deltas = args.deltas + if not args.deltas: + deltas = glob.glob('*.delta') + + run(args.tiles, args.out, deltas, verbose=args.verbose) + + +if __name__ == '__main__': + main() diff --git a/fuzzers/005-tilegrid/generate.tcl b/fuzzers/005-tilegrid/generate.tcl index d1bb7df2..943c71fe 100644 --- a/fuzzers/005-tilegrid/generate.tcl +++ b/fuzzers/005-tilegrid/generate.tcl @@ -1,100 +1,152 @@ -create_project -force -part $::env(XRAY_PART) design design +proc make_project {} { + create_project -force -part $::env(XRAY_PART) design design -read_verilog ../top.v -synth_design -top top + read_verilog ../top.v + synth_design -top top -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk] -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports di] -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports do] -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports stb] + set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk] + set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports di] + set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports do] + set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports stb] -create_pblock roi -add_cells_to_pblock [get_pblocks roi] [get_cells roi] -resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)" + create_pblock roi + add_cells_to_pblock [get_pblocks roi] [get_cells roi] + resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)" -set_property CFGBVS VCCO [current_design] -set_property CONFIG_VOLTAGE 3.3 [current_design] -set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] -set_param tcl.collectionResultDisplayLimit 0 + set_property CFGBVS VCCO [current_design] + set_property CONFIG_VOLTAGE 3.3 [current_design] + set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] + set_param tcl.collectionResultDisplayLimit 0 -set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF] - -set luts [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT] -set selected_luts {} -set lut_index 0 - -if 0 { - set grid_min_x -1 - set grid_max_x -1 - set grid_min_y -1 - set grid_max_y -1 -} { - set grid_min_x $::env(XRAY_ROI_GRID_X1) - set grid_max_x $::env(XRAY_ROI_GRID_X2) - set grid_min_y $::env(XRAY_ROI_GRID_Y1) - set grid_max_y $::env(XRAY_ROI_GRID_Y2) + set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF] } -# LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per column) -# Also, if GRID_MIN/MAX is not defined, automatically create it based on used CLBs -# See caveat in README on automatic creation -foreach lut $luts { - set tile [get_tile -of_objects $lut] - set grid_x [get_property GRID_POINT_X $tile] - set grid_y [get_property GRID_POINT_Y $tile] +proc loc_luts {} { + set luts [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ LUT*} */A6LUT] + set selected_luts {} + set lut_index 0 - if [expr $grid_min_x < 0 || $grid_x < $grid_min_x] {set grid_min_x $grid_x} - if [expr $grid_max_x < 0 || $grid_x > $grid_max_x] {set grid_max_x $grid_x} + # LOC one LUT (a "selected_lut") into each CLB segment configuration column (ie 50 per CMT column) + foreach lut $luts { + set tile [get_tile -of_objects $lut] + set grid_x [get_property GRID_POINT_X $tile] + set grid_y [get_property GRID_POINT_Y $tile] - if [expr $grid_min_y < 0 || $grid_y < $grid_min_y] {set grid_min_y $grid_y} - if [expr $grid_max_y < 0 || $grid_y > $grid_max_y] {set grid_max_y $grid_y} - - # 50 per column => 50, 100, 150, etc - if [regexp "Y(0|[0-9]*[05]0)/" $lut] { - set cell [get_cells roi/is[$lut_index].lut] - set_property LOC [get_sites -of_objects $lut] $cell - set lut_index [expr $lut_index + 1] - lappend selected_luts $lut - } + # 50 per column => 50, 100, 150, etc + # ex: SLICE_X2Y50/A6LUT + # Only take one of the CLBs within a slice + if [regexp "X.*[02468]Y.*[05]0/" $lut] { + set cell [get_cells roi/luts[$lut_index].lut] + set_property LOC [get_sites -of_objects $lut] $cell + set lut_index [expr $lut_index + 1] + lappend selected_luts $lut + } + } + return $selected_luts } -place_design -route_design +proc loc_brams {} { + # XXX: for some reason this doesn't work if there is a cell already there + # but LUTs don't have this issue + set brams [get_bels -of_objects [get_sites -of_objects [get_pblocks roi]] -filter {TYPE =~ RAMBFIFO36E1*}] + set selected_brams {} + set bram_index 0 -write_checkpoint -force design.dcp -write_bitstream -force design.bit + # LOC one BRAM (a "selected_lut") into each BRAM segment configuration column (ie 10 per CMT column) + foreach bram $brams { + set tile [get_tile -of_objects $bram] + set grid_x [get_property GRID_POINT_X $tile] + set grid_y [get_property GRID_POINT_Y $tile] -# Get all tiles in ROI, ie not just the selected LUTs -set tiles [get_tiles -filter "GRID_POINT_X >= $grid_min_x && GRID_POINT_X <= $grid_max_x && GRID_POINT_Y >= $grid_min_y && GRID_POINT_Y <= $grid_max_y"] - -# Write tiles.txt with site metadata -set fp [open "tiles.txt" w] -foreach tile $tiles { - set type [get_property TYPE $tile] - set grid_x [get_property GRID_POINT_X $tile] - set grid_y [get_property GRID_POINT_Y $tile] - set sites [get_sites -quiet -of_objects $tile] - set typed_sites {} - - if [llength $sites] { - set site_types [get_property SITE_TYPE $sites] - foreach t $site_types s $sites { - lappend typed_sites $t $s - } - } - - puts $fp "$type $tile $grid_x $grid_y $typed_sites" -} -close $fp - -# Toggle one bit in each selected LUT to generate base addresses -for {set i 0} {$i < $lut_index} {incr i} { - set cell [get_cells roi/is[$i].lut] - set orig_init [get_property INIT $cell] - # Flip a bit by changing MSB 0 => 1 - set new_init [regsub "h8" $orig_init "h0"] - set_property INIT $new_init $cell - write_bitstream -force design_[get_sites -of_objects [lindex $selected_luts $i]].bit - set_property INIT $orig_init $cell + # 10 per column => 10, 20, ,etc + # ex: RAMB36_X0Y10/RAMBFIFO36E1 + if [regexp "Y.*0/" $bram] { + set cell [get_cells roi/brams[$bram_index].bram] + set_property LOC [get_sites -of_objects $bram] $cell + set bram_index [expr $bram_index + 1] + lappend selected_brams $bram + } + } + return $selected_brams } +proc write_tiles_txt {} { + # Get all tiles in ROI, ie not just the selected LUTs + set grid_min_x $::env(XRAY_ROI_GRID_X1) + set grid_max_x $::env(XRAY_ROI_GRID_X2) + set grid_min_y $::env(XRAY_ROI_GRID_Y1) + set grid_max_y $::env(XRAY_ROI_GRID_Y2) + set tiles [get_tiles -filter "GRID_POINT_X >= $grid_min_x && GRID_POINT_X <= $grid_max_x && GRID_POINT_Y >= $grid_min_y && GRID_POINT_Y <= $grid_max_y"] + + # Write tiles.txt with site metadata + set fp [open "tiles.txt" w] + foreach tile $tiles { + set type [get_property TYPE $tile] + set grid_x [get_property GRID_POINT_X $tile] + set grid_y [get_property GRID_POINT_Y $tile] + set sites [get_sites -quiet -of_objects $tile] + set typed_sites {} + + if [llength $sites] { + set site_types [get_property SITE_TYPE $sites] + foreach t $site_types s $sites { + lappend typed_sites $t $s + } + } + + puts $fp "$type $tile $grid_x $grid_y $typed_sites" + } + close $fp +} + +proc write_clbs { selected_luts } { + # Toggle one bit in each selected LUT to generate base addresses + for {set i 0} {$i < [llength $selected_luts]} {incr i} { + puts "" + set cell [get_cells roi/luts[$i].lut] + puts "LUT $cell" + set orig_init [get_property INIT $cell] + # Flip a bit by changing MSB 0 => 1 + set new_init [regsub "h8" $orig_init "h0"] + puts "INIT $orig_init => $new_init" + set_property INIT $new_init $cell + write_bitstream -force design_[get_sites -of_objects [lindex $selected_luts $i]].bit + set_property INIT $orig_init $cell + } +} + +proc write_brams { selected_brams } { + # Toggle one bit in each selected BRAM to generate base addresses + for {set i 0} {$i < [llength $selected_brams]} {incr i} { + puts "" + set cell [get_cells roi/brams[$i].bram] + puts "BRAM $cell" + set orig_init [get_property INIT_00 $cell] + # Flip a bit by changing MSB 0 => 1 + set new_init [regsub "h8" $orig_init "h0"] + puts "INIT_00 $orig_init => $new_init" + set_property INIT_00 $new_init $cell + write_bitstream -force design_[get_sites -of_objects [lindex $selected_brams $i]].bit + set_property INIT_00 $orig_init $cell + } +} + +proc run {} { + make_project + set selected_luts [loc_luts] + puts "Selected LUTs: [llength $selected_luts]" + set selected_brams [loc_brams] + puts "Selected LUTs: [llength $selected_brams]" + + place_design + route_design + write_checkpoint -force design.dcp + write_bitstream -force design.bit + + write_tiles_txt + write_clbs $selected_luts + write_brams $selected_brams +} + +run + diff --git a/fuzzers/005-tilegrid/top.v b/fuzzers/005-tilegrid/top.v index 5fd239b5..55edc442 100644 --- a/fuzzers/005-tilegrid/top.v +++ b/fuzzers/005-tilegrid/top.v @@ -1,9 +1,10 @@ //Need at least one LUT per frame base address we want -`define N 100 +`define N_LUT 100 +`define N_BRAM 8 module top(input clk, stb, di, output do); - localparam integer DIN_N = 6; - localparam integer DOUT_N = `N; + localparam integer DIN_N = 8; + localparam integer DOUT_N = `N_LUT + `N_BRAM; reg [DIN_N-1:0] din; wire [DOUT_N-1:0] dout; @@ -29,10 +30,10 @@ module top(input clk, stb, di, output do); ); endmodule -module roi(input clk, input [5:0] din, output [`N-1:0] dout); +module roi(input clk, input [7:0] din, output [`N_LUT + `N_BRAM-1:0] dout); genvar i; generate - for (i = 0; i < `N; i = i+1) begin:is + for (i = 0; i < `N_LUT; i = i+1) begin:luts LUT6 #( .INIT(64'h8000_0000_0000_0001 + (i << 16)) ) lut ( @@ -46,4 +47,36 @@ module roi(input clk, input [5:0] din, output [`N-1:0] dout); ); end endgenerate + + genvar j; + generate + for (j = 0; j < `N_BRAM; j = j+1) begin:brams + (* KEEP, DONT_TOUCH *) + RAMB36E1 #( + .INIT_00(256'h8000000000000000000000000000000000000000000000000000000000000000 + (j << 16)) + ) bram ( + .CLKARDCLK(din[0]), + .CLKBWRCLK(din[1]), + .ENARDEN(din[2]), + .ENBWREN(din[3]), + .REGCEAREGCE(din[4]), + .REGCEB(din[5]), + .RSTRAMARSTRAM(din[6]), + .RSTRAMB(din[7]), + .RSTREGARSTREG(din[0]), + .RSTREGB(din[1]), + .ADDRARDADDR(din[2]), + .ADDRBWRADDR(din[3]), + .DIADI(din[4]), + .DIBDI(din[5]), + .DIPADIP(din[6]), + .DIPBDIP(din[7]), + .WEA(din[0]), + .WEBWE(din[1]), + .DOADO(dout[j + `N_LUT]), + .DOBDO(), + .DOPADOP(), + .DOPBDOP()); + end + endgenerate endmodule diff --git a/fuzzers/010-lutinit/generate.py b/fuzzers/010-lutinit/generate.py index b023af44..4b92c86d 100644 --- a/fuzzers/010-lutinit/generate.py +++ b/fuzzers/010-lutinit/generate.py @@ -18,7 +18,7 @@ with open("design_%s.txt" % sys.argv[1], "r") as f: for i in range(64): bitname = "%s.INIT[%02d]" % (bel, i) bitname = bitname.replace("6LUT", "LUT") - segmk.addtag(site, bitname, ((init >> i) & 1) != 0) + segmk.add_site_tag(site, bitname, ((init >> i) & 1) != 0) segmk.compile() segmk.write(sys.argv[1]) diff --git a/fuzzers/070-tileconn/.gitignore b/fuzzers/070-tileconn/.gitignore deleted file mode 100644 index 6e99ca40..00000000 --- a/fuzzers/070-tileconn/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/specimen_*/ -/tileconn.json -/run.ok diff --git a/fuzzers/070-tileconn/Makefile b/fuzzers/070-tileconn/Makefile deleted file mode 100644 index 73b0a649..00000000 --- a/fuzzers/070-tileconn/Makefile +++ /dev/null @@ -1,26 +0,0 @@ - -N := 1 -SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N))) -SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS)) - -database: $(SPECIMENS_OK) - cp specimen_001/tileconn.json tileconn.json - -pushdb: - cp tileconn.json ${XRAY_DATABASE_DIR}/$(XRAY_DATABASE)/tileconn.json - -$(SPECIMENS_OK): - bash generate.sh $(subst /OK,,$@) - touch $@ - -run: - $(MAKE) clean - $(MAKE) database - $(MAKE) pushdb - touch run.ok - -clean: - rm -rf specimen_[0-9][0-9][0-9]/ tileconn.json run.ok - -.PHONY: database pushdb run clean - diff --git a/fuzzers/070-tileconn/generate.py b/fuzzers/070-tileconn/generate.py deleted file mode 100644 index 7a707ac8..00000000 --- a/fuzzers/070-tileconn/generate.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python3 - -import os, sys, json, re - -tilenodes = dict() -grid2tile = dict() -database = dict() - -print("Loading %s grid." % os.getenv("XRAY_DATABASE")) -with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"), - os.getenv("XRAY_DATABASE")), "r") as f: - grid = json.load(f) - -for tile, tiledata in grid.items(): - grid_xy = (tiledata["grid_x"], tiledata["grid_y"]) - grid2tile[grid_xy] = tile - -print("Loading nodewires.txt.") -with open("nodewires.txt") as f: - for line in f: - node, *wires = line.split() - for wire in wires: - wire_tile, wire_name = wire.split("/") - if wire_tile not in tilenodes: - tilenodes[wire_tile] = dict() - tilenodes[wire_tile][node] = wire_name - - -def filter_pair(type1, type2, wire1, wire2, delta_x, delta_y): - if type1 in ["HCLK_L", "HCLK_R"]: - is_vertical_wire = False - if wire1.startswith("HCLK_S"): is_vertical_wire = True - if wire1.startswith("HCLK_N"): is_vertical_wire = True - if wire1.startswith("HCLK_W"): is_vertical_wire = True - if wire1.startswith("HCLK_E"): is_vertical_wire = True - if wire1.startswith("HCLK_LV"): is_vertical_wire = True - if wire1.startswith("HCLK_BYP"): is_vertical_wire = True - if wire1.startswith("HCLK_FAN"): is_vertical_wire = True - if wire1.startswith("HCLK_LEAF_CLK_"): is_vertical_wire = True - - is_horizontal_wire = False - if wire1.startswith("HCLK_CK_"): is_horizontal_wire = True - if wire1.startswith("HCLK_INT_"): is_horizontal_wire = True - - assert is_vertical_wire != is_horizontal_wire - if is_vertical_wire and delta_y == 0: return True - if is_horizontal_wire and delta_x == 0: return True - - if type1 in ["INT_L", "INT_R"]: - # the wires with underscore after BEG/END all connect vertically - if (("BEG_" in wire1) or ("END_" in wire1)) and delta_y == 0: - return True - - if type1 in ["BRKH_INT", "BRKH_B_TERM_INT", "T_TERM_INT"]: - if delta_y == 0: return True - - return False - - -def handle_pair(tile1, tile2): - if tile1 not in tilenodes: return - if tile2 not in tilenodes: return - - tile1data = grid[tile1] - tile2data = grid[tile2] - - grid1_xy = (tile1data["grid_x"], tile1data["grid_y"]) - grid2_xy = (tile2data["grid_x"], tile2data["grid_y"]) - - if grid1_xy > grid2_xy: - return handle_pair(tile2, tile1) - - key = ( - tile1data["type"], tile2data["type"], grid2_xy[0] - grid1_xy[0], - grid2_xy[1] - grid1_xy[1]) - - wire_pairs = set() - - for node, wire1 in tilenodes[tile1].items(): - if node in tilenodes[tile2]: - wire2 = tilenodes[tile2][node] - if filter_pair(key[0], key[1], wire1, wire2, key[2], key[3]): - continue - if filter_pair(key[1], key[0], wire2, wire1, -key[2], -key[3]): - continue - wire_pairs.add((wire1, wire2)) - - if key not in database: - database[key] = wire_pairs - else: - database[key] &= wire_pairs - - -for tile, tiledata in grid.items(): - grid_right_xy = (tiledata["grid_x"] + 1, tiledata["grid_y"]) - grid_below_xy = (tiledata["grid_x"], tiledata["grid_y"] + 1) - - if grid_right_xy in grid2tile: - handle_pair(tile, grid2tile[grid_right_xy]) - - if grid_below_xy in grid2tile: - handle_pair(tile, grid2tile[grid_below_xy]) - -print("Converting database.") -json_db = list() -for key in sorted(database.keys()): - (t1, t2, dx, dy) = key - entry = dict() - entry["tile_types"] = [t1, t2] - entry["grid_deltas"] = [dx, dy] - entry["wire_pairs"] = list(sorted(database[key])) - if len(entry["wire_pairs"]): - json_db.append(entry) - -print("Writing tileconn.json.") -with open("tileconn.json", "w") as f: - print(json.dumps(json_db, sort_keys=True, indent="\t"), file=f) diff --git a/fuzzers/070-tileconn/generate.sh b/fuzzers/070-tileconn/generate.sh deleted file mode 100644 index f639d561..00000000 --- a/fuzzers/070-tileconn/generate.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -x - -source ${XRAY_GENHEADER} - -vivado -mode batch -source ../generate.tcl - -python3 ../generate.py - diff --git a/fuzzers/070-tileconn/generate.tcl b/fuzzers/070-tileconn/generate.tcl deleted file mode 100644 index 71ea0321..00000000 --- a/fuzzers/070-tileconn/generate.tcl +++ /dev/null @@ -1,29 +0,0 @@ -create_project -force -part $::env(XRAY_PART) design design - -read_verilog ../top.v -synth_design -top top - -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports a] -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports y] - -create_pblock roi -resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)" - -set_property CFGBVS VCCO [current_design] -set_property CONFIG_VOLTAGE 3.3 [current_design] -set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] -set_param tcl.collectionResultDisplayLimit 0 - -place_design -route_design - -write_checkpoint -force design.dcp -# write_bitstream -force design.bit - -source ../../../utils/utils.tcl - -set fp [open "nodewires.txt" w] -foreach node [get_nodes -of_objects [roi_tiles]] { - puts $fp "$node [get_wires -of_objects $node]" -} -close $fp diff --git a/fuzzers/070-tileconn/top.v b/fuzzers/070-tileconn/top.v deleted file mode 100644 index 5a70fc18..00000000 --- a/fuzzers/070-tileconn/top.v +++ /dev/null @@ -1,3 +0,0 @@ -module top(input a, output y); - assign y = a; -endmodule diff --git a/htmlgen/htmlgen.py b/htmlgen/htmlgen.py index 93063a72..01f945ab 100755 --- a/htmlgen/htmlgen.py +++ b/htmlgen/htmlgen.py @@ -1,126 +1,49 @@ #!/usr/bin/env python3 +# https://symbiflow.github.io/prjxray-db/ +# https://symbiflow.github.io/prjxray-db/artix7/ import os, sys, json, re from io import StringIO -import argparse -parser = argparse.ArgumentParser( - description="Generate a pretty HTML version of the documentation.") -parser.add_argument( - '--output', - default=os.path.join(os.path.curdir, 'html'), - help='Put the generated files in this directory (default current dir).') -parser.add_argument( - '--settings', - default=None, - help='Read the settings from file (default to environment).') +def mk_get_setting(settings_filename): + if settings_filename: + settings = { + 'XRAY_DATABASE_DIR': + os.path.abspath( + os.path.join(os.path.dirname(settings_filename), '..')), + } + with open(settings_filename) as f: + for line in f: + line = line.strip() + if not line.startswith("export "): + continue + key, value = line[7:].split('=', 1) + settings[key] = value[1:-1] -args = parser.parse_args() - -if args.settings: - settings_filename = args.settings - - settings = { - 'XRAY_DATABASE_DIR': - os.path.abspath( - os.path.join(os.path.dirname(settings_filename), '..')), - } - with open(settings_filename) as f: - for line in f: - line = line.strip() - if not line.startswith("export "): - continue - key, value = line[7:].split('=', 1) - settings[key] = value[1:-1] - - def get_setting(name): - return settings[name] -else: - - def get_setting(name): - return os.getenv(name) + return lambda name: settings[name] + else: + return os.getenv -db_dir = os.path.join( - get_setting("XRAY_DATABASE_DIR"), get_setting("XRAY_DATABASE")) +get_setting = mk_get_setting(None) -def db_open(fn): +def db_open(fn, db_dir): filename = os.path.join(db_dir, fn) if not os.path.exists(filename): return StringIO("") return open(os.path.join(db_dir, fn)) -def out_open(fn): - out_dir = os.path.join(args.output, get_setting("XRAY_DATABASE")) +def out_open(fn, output): + out_dir = os.path.join(output, get_setting("XRAY_DATABASE")) os.makedirs(out_dir, exist_ok=True) fp = os.path.join(out_dir, fn) print("Writing %s" % fp) return open(fp, "w") -clb_bitgroups_db = [ - # copy&paste from zero_db in dbfixup.py - "00_21 00_22 00_26 01_28|00_25 01_20 01_21 01_24", - "00_23 00_30 01_22 01_25|00_27 00_29 01_26 01_29", - "01_12 01_14 01_16 01_18|00_10 00_11 01_09 01_10", - "00_13 01_17 00_15 00_17|00_18 00_19 01_13 00_14", - "00_34 00_38 01_33 01_37|00_35 00_39 01_38 01_40", - "00_33 00_41 01_32 01_34|00_37 00_42 01_36 01_41", - - # other manual groupings for individual bits - "00_02 00_05 00_09 01_04|00_07 01_05 01_06", - "00_01 00_06 01_00 01_08|00_03 01_01 01_02", - "00_59 01_54 01_58 01_61|00_57 00_58 01_56", - "00_55 00_63 01_57 01_62|00_61 00_62 01_60", - "00_43 00_47 00_50 00_53 00_54 01_42|00_51 01_50 01_52", - "00_49 01_44 01_45 01_48 01_49 01_53|00_45 00_46 01_46", -] - -hclk_bitgroups_db = [ - # manual groupings - "03_14 03_15 04_14 04_15|00_15 00_16 01_14 01_15", - "02_16 03_16 04_16 05_16|02_14 02_15 05_14 05_15", - "02_18 02_19 05_18 05_19|00_17 00_18 01_16 01_17", - "03_18 03_19 04_18 04_19|02_17 03_17 04_17 05_17", - "02_20 02_21 05_20 05_21|02_22 03_22 04_22 05_22", - "02_29 03_29 04_29 05_29|03_30 03_31 04_30 04_31", - "02_26 02_27 05_26 05_27|02_28 03_28 04_28 05_28", - "02_23 03_23 04_23 05_23|03_24 03_25 04_24 04_25", -] - -# groupings for SNWE bits in frames 2..7 -for i in range(0, 64, 4): - clb_bitgroups_db.append( - "02_%02d 03_%02d 05_%02d 06_%02d 07_%02d|05_%02d 03_%02d 04_%02d 04_%02d" - % (i + 1, i, i, i, i + 1, i + 3, i + 1, i + 1, i + 2)) - clb_bitgroups_db.append( - "02_%02d 04_%02d 05_%02d 05_%02d 06_%02d|02_%02d 03_%02d 04_%02d 07_%02d" - % (i + 2, i, i + 1, i + 2, i + 2, i + 3, i + 2, i + 3, i + 3)) - -clb_left_bits = set() -clb_right_bits = set() - -for entry in clb_bitgroups_db: - a, b = entry.split("|") - for bit in a.split(): - clb_left_bits.add(bit) - for bit in b.split(): - clb_right_bits.add(bit) - -hclk_left_bits = set() -hclk_right_bits = set() - -for entry in hclk_bitgroups_db: - a, b = entry.split("|") - for bit in a.split(): - hclk_left_bits.add(bit) - for bit in b.split(): - hclk_right_bits.add(bit) - - class UnionFind: def __init__(self): self.parents = dict() @@ -143,64 +66,38 @@ class UnionFind: self.parents[a] = b -################################################# -# Loading Raw Source Data - -grid = None -cfgbits = dict() -cfgbits_r = dict() -maskbits = dict() -ppips = dict() -routebits = dict() -routezbits = dict() - -print("Loading tilegrid.") -with db_open("tilegrid.json") as f: - data = f.read() - if not data: - grid = { - "NULL": { - "grid_x": 0, - "grid_y": 0, - "type": "NULL", - } - } - else: - grid = json.loads(data) - - -def db_read(tiletype): - cfgbits[tiletype] = dict() - cfgbits_r[tiletype] = dict() - maskbits[tiletype] = set() - ppips[tiletype] = dict() - routebits[tiletype] = dict() - routezbits[tiletype] = dict() +def db_read(dbstate, tiletype, db_dir): + dbstate.cfgbits[tiletype] = dict() + dbstate.cfgbits_r[tiletype] = dict() + dbstate.maskbits[tiletype] = set() + dbstate.ppips[tiletype] = dict() + dbstate.routebits[tiletype] = dict() + dbstate.routezbits[tiletype] = dict() def add_pip_bits(tag, bits): - if tag not in routebits[tiletype]: - routebits[tiletype][tag] = set() - routezbits[tiletype][tag] = set() + if tag not in dbstate.routebits[tiletype]: + dbstate.routebits[tiletype][tag] = set() + dbstate.routezbits[tiletype][tag] = set() for bit in bits: if bit[0] == "!": - if bit[1:] not in routezbits[tiletype]: - routezbits[tiletype][bit[1:]] = set() - routezbits[tiletype][bit[1:]].add(tag) + if bit[1:] not in dbstate.routezbits[tiletype]: + dbstate.routezbits[tiletype][bit[1:]] = set() + dbstate.routezbits[tiletype][bit[1:]].add(tag) else: - if bit not in routebits[tiletype]: - routebits[tiletype][bit] = set() - routebits[tiletype][bit].add(tag) + if bit not in dbstate.routebits[tiletype]: + dbstate.routebits[tiletype][bit] = set() + dbstate.routebits[tiletype][bit].add(tag) def add_cfg_bits(tag, bits): - if tag not in cfgbits[tiletype]: - cfgbits[tiletype][tag] = set() + if tag not in dbstate.cfgbits[tiletype]: + dbstate.cfgbits[tiletype][tag] = set() for bit in bits: - cfgbits[tiletype][tag].add(bit) - if bit not in cfgbits_r[tiletype]: - cfgbits_r[tiletype][bit] = set() - cfgbits_r[tiletype][bit].add(tag) + dbstate.cfgbits[tiletype][tag].add(bit) + if bit not in dbstate.cfgbits_r[tiletype]: + dbstate.cfgbits_r[tiletype][bit] = set() + dbstate.cfgbits_r[tiletype][bit].add(tag) - with db_open("segbits_%s.db" % tiletype) as f: + with db_open("segbits_%s.db" % tiletype, db_dir) as f: for line in f: line = line.split() tag, bits = line[0], line[1:] @@ -215,67 +112,157 @@ def db_read(tiletype): else: add_cfg_bits(tag, bits) - with db_open("ppips_%s.db" % tiletype) as f: + with db_open("ppips_%s.db" % tiletype, db_dir) as f: for line in f: tag, typ = line.split() - ppips[tiletype][tag] = typ + dbstate.ppips[tiletype][tag] = typ if tiletype not in ["int_l", "int_r"]: - with db_open("mask_%s.db" % tiletype) as f: + with db_open("mask_%s.db" % tiletype, db_dir) as f: for line in f: tag, bit = line.split() assert tag == "bit" - maskbits[tiletype].add(bit) + dbstate.maskbits[tiletype].add(bit) else: for t in ["clbll_l", "clbll_r", "clblm_l", "clblm_r", "dsp_l", "dsp_r", "bram_l", "bram_r"]: - with db_open("mask_%s.db" % t) as f: + with db_open("mask_%s.db" % t, db_dir) as f: for line in f: tag, bit = line.split() assert tag == "bit" frameidx, bitidx = bit.split("_") - maskbits[tiletype].add( + dbstate.maskbits[tiletype].add( "%02d_%02d" % (int(frameidx), int(bitidx) % 64)) -db_read("int_l") -db_read("int_r") +def init_bitdb(): + clb_bitgroups_db = [ + # copy&paste from zero_db in dbfixup.py + "00_21 00_22 00_26 01_28|00_25 01_20 01_21 01_24", + "00_23 00_30 01_22 01_25|00_27 00_29 01_26 01_29", + "01_12 01_14 01_16 01_18|00_10 00_11 01_09 01_10", + "00_13 01_17 00_15 00_17|00_18 00_19 01_13 00_14", + "00_34 00_38 01_33 01_37|00_35 00_39 01_38 01_40", + "00_33 00_41 01_32 01_34|00_37 00_42 01_36 01_41", -db_read("hclk_l") -db_read("hclk_r") + # other manual groupings for individual bits + "00_02 00_05 00_09 01_04|00_07 01_05 01_06", + "00_01 00_06 01_00 01_08|00_03 01_01 01_02", + "00_59 01_54 01_58 01_61|00_57 00_58 01_56", + "00_55 00_63 01_57 01_62|00_61 00_62 01_60", + "00_43 00_47 00_50 00_53 00_54 01_42|00_51 01_50 01_52", + "00_49 01_44 01_45 01_48 01_49 01_53|00_45 00_46 01_46", + ] -db_read("clbll_l") -db_read("clbll_r") + hclk_bitgroups_db = [ + # manual groupings + "03_14 03_15 04_14 04_15|00_15 00_16 01_14 01_15", + "02_16 03_16 04_16 05_16|02_14 02_15 05_14 05_15", + "02_18 02_19 05_18 05_19|00_17 00_18 01_16 01_17", + "03_18 03_19 04_18 04_19|02_17 03_17 04_17 05_17", + "02_20 02_21 05_20 05_21|02_22 03_22 04_22 05_22", + "02_29 03_29 04_29 05_29|03_30 03_31 04_30 04_31", + "02_26 02_27 05_26 05_27|02_28 03_28 04_28 05_28", + "02_23 03_23 04_23 05_23|03_24 03_25 04_24 04_25", + ] -db_read("clblm_l") -db_read("clblm_r") + # groupings for SNWE bits in frames 2..7 + for i in range(0, 64, 4): + clb_bitgroups_db.append( + "02_%02d 03_%02d 05_%02d 06_%02d 07_%02d|05_%02d 03_%02d 04_%02d 04_%02d" + % (i + 1, i, i, i, i + 1, i + 3, i + 1, i + 1, i + 2)) + clb_bitgroups_db.append( + "02_%02d 04_%02d 05_%02d 05_%02d 06_%02d|02_%02d 03_%02d 04_%02d 07_%02d" + % (i + 2, i, i + 1, i + 2, i + 2, i + 3, i + 2, i + 3, i + 3)) -db_read("dsp_l") -db_read("dsp_r") + return clb_bitgroups_db, hclk_bitgroups_db -db_read("bram_l") -db_read("bram_r") -################################################# -# Create Tilegrid Page +def init_hclk_bits(hclk_bitgroups_db): + hclk_left_bits = set() + hclk_right_bits = set() -grid_range = None -grid_map = dict() + for entry in hclk_bitgroups_db: + a, b = entry.split("|") + for bit in a.split(): + hclk_left_bits.add(bit) + for bit in b.split(): + hclk_right_bits.add(bit) -with out_open("index.html") as f: - print( - "
Part: %s
ROI: %s
ROI Frames: %s
| " + % (bgcolor, "\n".join(title)), + file=f) + if tiledata["type"].lower() in dbstate.cfgbits: + print( + "%s | " + % (tiledata["type"].lower(), tilename.replace("_X", "" - % (bgcolor, "\n".join(title)), - file=f) - if tiledata["type"].lower() in cfgbits: - print( - "%s | " - % ( - tiledata["type"].lower(), - tilename.replace("_X", "
Part: %s
ROI: %s
ROI Frames: %s
| PIP | ", file=f) - - for bit in grp_bits: - print("%s | " % bit, file=f) - print("%s | " % (pip) - for bit in grp_bits: - c = "-" - if bit in routebits[tiletype] and pip in routebits[ - tiletype][bit]: - c = "1" - if bit in routezbits[tiletype] and pip in routezbits[ - tiletype][bit]: - c = "0" - line = "%s%s%s | " % (c, line, c) - lines.append(line) - - trstyle = "" - for line in sorted(lines): - trstyle = " bgcolor=\"#dddddd\"" if trstyle == "" else "" - print("
|---|---|