From 8ad5e64f85b1dc7004794b38ae6c55e2d2803273 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 27 Sep 2018 13:10:10 -0700 Subject: [PATCH] Add methods to library. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/074-dump_all/reduce_tile_types.py | 93 ++++++++--------------- prjxray/db.py | 16 ++++ prjxray/grid.py | 1 + prjxray/lib.py | 54 +++++++++++++ prjxray/site_type.py | 29 +++++++ prjxray/tile.py | 59 ++++++++++++-- tools/quick_test.py | 38 ++++++++- 7 files changed, 217 insertions(+), 73 deletions(-) create mode 100644 prjxray/site_type.py diff --git a/fuzzers/074-dump_all/reduce_tile_types.py b/fuzzers/074-dump_all/reduce_tile_types.py index 25cb4948..a065132a 100644 --- a/fuzzers/074-dump_all/reduce_tile_types.py +++ b/fuzzers/074-dump_all/reduce_tile_types.py @@ -17,7 +17,6 @@ import progressbar import multiprocessing import os import functools -import re def check_and_strip_prefix(name, prefix): @@ -45,67 +44,6 @@ def flatten_site_pins(tile, site, site_pins, site_pin_node_to_wires): return dict(inner()) -# All site names appear to follow the pattern _XY. -# Generally speaking, only the tile relatively coordinates are required to -# assemble arch defs, so we re-origin the coordinates to be relative to the tile -# (e.g. start at X0Y0) and discard the prefix from the name. -SITE_COORDINATE_PATTERN = re.compile('^(.+)_X([0-9]+)Y([0-9]+)$') - - -def find_origin_coordinate(sites): - """ Find the coordinates of each site within the tile, and then subtract the - smallest coordinate to re-origin them all to be relative to the tile. - """ - - if len(sites) == 0: - return 0, 0 - - def inner_(): - for site in sites: - coordinate = SITE_COORDINATE_PATTERN.match(site['site']) - assert coordinate is not None, site - - x_coord = int(coordinate.group(2)) - y_coord = int(coordinate.group(3)) - yield x_coord, y_coord - - x_coords, y_coords = zip(*inner_()) - min_x_coord = min(x_coords) - min_y_coord = min(y_coords) - - return min_x_coord, min_y_coord - - -def get_sites(tile, site_pin_node_to_wires): - min_x_coord, min_y_coord = find_origin_coordinate(tile['sites']) - - for site in tile['sites']: - orig_site_name = site['site'] - coordinate = SITE_COORDINATE_PATTERN.match(orig_site_name) - - x_coord = int(coordinate.group(2)) - y_coord = int(coordinate.group(3)) - - yield ( - { - 'name': - 'X{}Y{}'.format(x_coord - min_x_coord, y_coord - min_y_coord), - 'prefix': - coordinate.group(1), - 'x_coord': - x_coord - min_x_coord, - 'y_coord': - y_coord - min_y_coord, - 'type': - site['type'], - 'site_pins': - dict( - flatten_site_pins( - tile['tile'], site['site'], site['site_pins'], - site_pin_node_to_wires)), - }) - - def compare_sites_and_update(tile, sites, new_sites): for site_a, site_b in zip(sites, new_sites): assert site_a['type'] == site_b['type'] @@ -219,6 +157,37 @@ def check_wires(wires, sites, pips): assert pip['dst_wire'] in wires, repr((pip['dst_wire'], wires)) +def get_sites(tile, site_pin_node_to_wires): + min_x_coord, min_y_coord = prjxray.lib.find_origin_coordinate( + site['site'] for site in tile['sites']) + + for site in tile['sites']: + orig_site_name = site['site'] + coordinate = prjxray.lib.get_site_coordinate_from_name(orig_site_name) + + x_coord = coordinate.x_coord + y_coord = coordinate.y_coord + + yield ( + { + 'name': + 'X{}Y{}'.format(x_coord - min_x_coord, y_coord - min_y_coord), + 'prefix': + coordinate.prefix, + 'x_coord': + x_coord - min_x_coord, + 'y_coord': + y_coord - min_y_coord, + 'type': + site['type'], + 'site_pins': + dict( + flatten_site_pins( + tile['tile'], site['site'], site['site_pins'], + site_pin_node_to_wires)), + }) + + def read_json5(fname, nodes): node_lookup = prjxray.lib.NodeLookup() node_lookup.load_from_nodes(nodes) diff --git a/prjxray/db.py b/prjxray/db.py index 66e86ce4..5f08335e 100644 --- a/prjxray/db.py +++ b/prjxray/db.py @@ -2,6 +2,7 @@ import os.path import json from prjxray import grid from prjxray import tile +from prjxray import site_type from prjxray import connections @@ -36,6 +37,7 @@ class Database(object): self.tile_types = None self.tile_types = {} + self.site_types = {} for f in os.listdir(self.db_root): if f.endswith('.json') and f.startswith('tile_type_'): tile_type = f[len('tile_type_'):-len('.json')].lower() @@ -62,6 +64,11 @@ class Database(object): tile_type=tile_type_file, ) + if f.endswith('.json') and f.startswith('site_type_'): + site_type_name = f[len('site_type_'):-len('.json')] + + self.site_types[site_type_name] = os.path.join(self.db_root, f) + def get_tile_types(self): """ Return list of tile types """ return self.tile_types.keys() @@ -102,3 +109,12 @@ class Database(object): for tile_type, db in self.tile_types.items()) return connections.Connections( self.tilegrid, self.tileconn, tile_wires) + + def get_site_types(self): + return self.site_types.keys() + + def get_site_type(self, site_type_name): + with open(self.site_types[site_type_name]) as f: + site_type_data = json.load(f) + + return site_type.SiteType(site_type_data) diff --git a/prjxray/grid.py b/prjxray/grid.py index 36826c5f..82a81445 100644 --- a/prjxray/grid.py +++ b/prjxray/grid.py @@ -19,6 +19,7 @@ class Grid(object): for tile in self.tilegrid['tiles']: tileinfo = self.tilegrid['tiles'][tile] grid_loc = GridLoc(tileinfo['grid_x'], tileinfo['grid_y']) + assert grid_loc not in self.loc self.loc[grid_loc] = tile self.tileinfo[tile] = GridInfo( segment=tileinfo['segment'] if 'segment' in tileinfo else None, diff --git a/prjxray/lib.py b/prjxray/lib.py index eb9ac034..aa0d2bb2 100644 --- a/prjxray/lib.py +++ b/prjxray/lib.py @@ -3,6 +3,8 @@ import csv import pickle import pyjson5 as json5 import progressbar +import re +from collections import namedtuple def read_root_csv(root_dir): @@ -148,3 +150,55 @@ def compare_prototype_site(proto_a, proto_b): """ assert proto_a == proto_b, repr((proto_a, proto_b)) + + +# All site names appear to follow the pattern _XY. +# Generally speaking, only the tile relatively coordinates are required to +# assemble arch defs, so we re-origin the coordinates to be relative to the tile +# (e.g. start at X0Y0) and discard the prefix from the name. +SITE_COORDINATE_PATTERN = re.compile('^(.+)_X([0-9]+)Y([0-9]+)$') + +SiteCoordinate = namedtuple('SiteCoordinate', 'prefix x_coord y_coord') + + +def get_site_coordinate_from_name(name): + """ + >>> get_site_coordinate_from_name('SLICE_X1Y0') + SiteCoordinate(prefix='SLICE', x_coord=1, y_coord=0) + + >>> get_site_coordinate_from_name('SLICE_X0Y0') + SiteCoordinate(prefix='SLICE', x_coord=0, y_coord=0) + + >>> get_site_coordinate_from_name('INT_L_X500Y999') + SiteCoordinate(prefix='INT_L', x_coord=500, y_coord=999) + + """ + coordinate = SITE_COORDINATE_PATTERN.match(name) + assert coordinate is not None, name + + return SiteCoordinate( + prefix=coordinate.group(1), + x_coord=int(coordinate.group(2)), + y_coord=int(coordinate.group(3)), + ) + + +def find_origin_coordinate(site_names): + """ Find the coordinates of each site within the tile, and then subtract the + smallest coordinate to re-origin them all to be relative to the tile. + """ + + if len(site_names) == 0: + return 0, 0 + + def inner_(): + for site in site_names: + coordinate = get_site_coordinate_from_name(site) + + yield coordinate.x_coord, coordinate.y_coord + + x_coords, y_coords = zip(*inner_()) + min_x_coord = min(x_coords) + min_y_coord = min(y_coords) + + return min_x_coord, min_y_coord diff --git a/prjxray/site_type.py b/prjxray/site_type.py new file mode 100644 index 00000000..85235992 --- /dev/null +++ b/prjxray/site_type.py @@ -0,0 +1,29 @@ +""" Description of a site type """ + +from collections import namedtuple +import enum + + +class SitePinDirection(enum.Enum): + IN = "IN" + OUT = "OUT" + + +SiteTypePin = namedtuple('SiteTypePin', 'name direction') + + +class SiteType(object): + def __init__(self, site_type): + self.type = site_type['type'] + self.site_pins = {} + for site_pin, site_pin_info in site_type['site_pins'].items(): + self.site_pins[site_pin] = SiteTypePin( + name=site_pin, + direction=SitePinDirection(site_pin_info['direction']), + ) + + def get_site_pins(self): + return self.site_pins.keys() + + def get_site_pin(self, site_pin): + return self.site_pins[site_pin] diff --git a/prjxray/tile.py b/prjxray/tile.py index ffa0069a..0600fe50 100644 --- a/prjxray/tile.py +++ b/prjxray/tile.py @@ -1,5 +1,6 @@ from collections import namedtuple import json +from prjxray import lib """ Database files available for a tile """ TileDbs = namedtuple('TileDbs', 'segbits mask tile_type') @@ -14,7 +15,7 @@ pins - Instaces of site pins within this site and tile. This is an tuple of the tile. """ -Site = namedtuple('Site', 'name x y type site_pins') +Site = namedtuple('Site', 'name prefix x y type site_pins') """ SitePin - Tuple representing a site pin within a tile. Sites are generic based on type, however sites are instanced @@ -28,7 +29,7 @@ direction - Direction of this site pin. This direction is expected to be the wire - Wire name within the tile. This name is site instance specific. """ -SitePin = namedtuple('SitePin', 'name wire direction') +SitePin = namedtuple('SitePin', 'name wire') class Tile(object): @@ -46,15 +47,19 @@ class Tile(object): def yield_sites(sites): for site in sites: yield Site( - name=None, + name=site['name'], + prefix=site['prefix'], type=site['type'], - x=None, - y=None, - site_pins=site['site_pins'], - ) + x=site['x_coord'], + y=site['y_coord'], + site_pins=tuple( + SitePin( + name=name, + wire=wire, + ) for name, wire in site['site_pins'].items())) def yield_pips(pips): - for pip in pips: + for pip in pips.values(): yield Pip( net_to=pip['dst_wire'], net_from=pip['src_wire'], @@ -82,3 +87,41 @@ class Tile(object): """ Returns tuple of Pip namedtuple's representing the PIPs in this tile. """ return self.pips + + def get_instance_sites(self, grid_info): + """ get_sites returns abstract sites for all tiles of type. + get_instance_sites converts site info from generic to specific + based on a tile location. + """ + origin_x, origin_y = lib.find_origin_coordinate(grid_info.sites.keys()) + + site_names = set() + + for site in self.sites: + x = site.x + origin_x + y = site.y + origin_y + + site_name = '{}_X{}Y{}'.format(site.prefix, x, y) + + if site_name not in grid_info.sites: + type_count = 0 + for site_name_from_grid, site_type in grid_info.sites.items(): + if site.type == site_type: + type_count += 1 + site_name = site_name_from_grid + + assert type_count == 1, (site_name, type_count) + + site_names.add(site_name) + assert site.type == grid_info.sites[site_name] + + yield Site( + name=site_name, + prefix=site.prefix, + type=site.type, + x=x, + y=y, + site_pins=site.site_pins, + ) + + assert site_names == set(grid_info.sites.keys()) diff --git a/tools/quick_test.py b/tools/quick_test.py index 37343d59..ec81d947 100644 --- a/tools/quick_test.py +++ b/tools/quick_test.py @@ -11,14 +11,46 @@ def quick_test(db_root): tile_types_in_grid = set( g.gridinfo_at_loc(loc).tile_type for loc in g.tile_locations()) tile_types_in_db = set(db.get_tile_types()) + site_types = set(db.get_site_types()) assert len(tile_types_in_grid - tile_types_in_db) == 0 # Verify that all tile types can be loaded. for tile_type in db.get_tile_types(): tile = db.get_tile_type(tile_type) - tile.get_wires() - tile.get_sites() - tile.get_pips() + wires = tile.get_wires() + for site in tile.get_sites(): + assert site.type in site_types + site_type = db.get_site_type(site.type) + site_pins = site_type.get_site_pins() + for site_pin in site.site_pins: + if site_pin.wire is not None: + assert site_pin.wire in wires, (site_pin.wire, ) + + assert site_pin.name in site_pins + + for pip in tile.get_pips(): + assert pip.net_to in wires + assert pip.net_from in wires + + for loc in g.tile_locations(): + gridinfo = g.gridinfo_at_loc(loc) + assert gridinfo.tile_type in db.get_tile_types() + for site_name, site_type in gridinfo.sites.items(): + assert site_type in site_types + + tile = db.get_tile_type(gridinfo.tile_type) + + # FIXME: The way sites are named in Tile.get_instance_sites is broken + # for thes tile types, skip them until the underlying data is fixed. + BROKEN_TILE_TYPES = [ + 'BRAM_L', 'BRAM_R', 'HCLK_IOI3', 'CMT_TOP_L_UPPER_B', + 'CMT_TOP_R_UPPER_B' + ] + if gridinfo.tile_type in BROKEN_TILE_TYPES: + continue + + instance_sites = list(tile.get_instance_sites(gridinfo)) + assert len(instance_sites) == len(tile.get_sites()) def main():