Add methods to library.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2018-09-27 13:10:10 -07:00
parent 825d2dd9f0
commit 8ad5e64f85
7 changed files with 217 additions and 73 deletions

View File

@ -17,7 +17,6 @@ import progressbar
import multiprocessing import multiprocessing
import os import os
import functools import functools
import re
def check_and_strip_prefix(name, prefix): 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()) return dict(inner())
# All site names appear to follow the pattern <type>_X<abs coord>Y<abs coord>.
# 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): def compare_sites_and_update(tile, sites, new_sites):
for site_a, site_b in zip(sites, new_sites): for site_a, site_b in zip(sites, new_sites):
assert site_a['type'] == site_b['type'] 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)) 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): def read_json5(fname, nodes):
node_lookup = prjxray.lib.NodeLookup() node_lookup = prjxray.lib.NodeLookup()
node_lookup.load_from_nodes(nodes) node_lookup.load_from_nodes(nodes)

View File

@ -2,6 +2,7 @@ import os.path
import json import json
from prjxray import grid from prjxray import grid
from prjxray import tile from prjxray import tile
from prjxray import site_type
from prjxray import connections from prjxray import connections
@ -36,6 +37,7 @@ class Database(object):
self.tile_types = None self.tile_types = None
self.tile_types = {} self.tile_types = {}
self.site_types = {}
for f in os.listdir(self.db_root): for f in os.listdir(self.db_root):
if f.endswith('.json') and f.startswith('tile_type_'): if f.endswith('.json') and f.startswith('tile_type_'):
tile_type = f[len('tile_type_'):-len('.json')].lower() tile_type = f[len('tile_type_'):-len('.json')].lower()
@ -62,6 +64,11 @@ class Database(object):
tile_type=tile_type_file, 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): def get_tile_types(self):
""" Return list of tile types """ """ Return list of tile types """
return self.tile_types.keys() return self.tile_types.keys()
@ -102,3 +109,12 @@ class Database(object):
for tile_type, db in self.tile_types.items()) for tile_type, db in self.tile_types.items())
return connections.Connections( return connections.Connections(
self.tilegrid, self.tileconn, tile_wires) 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)

View File

@ -19,6 +19,7 @@ class Grid(object):
for tile in self.tilegrid['tiles']: for tile in self.tilegrid['tiles']:
tileinfo = self.tilegrid['tiles'][tile] tileinfo = self.tilegrid['tiles'][tile]
grid_loc = GridLoc(tileinfo['grid_x'], tileinfo['grid_y']) grid_loc = GridLoc(tileinfo['grid_x'], tileinfo['grid_y'])
assert grid_loc not in self.loc
self.loc[grid_loc] = tile self.loc[grid_loc] = tile
self.tileinfo[tile] = GridInfo( self.tileinfo[tile] = GridInfo(
segment=tileinfo['segment'] if 'segment' in tileinfo else None, segment=tileinfo['segment'] if 'segment' in tileinfo else None,

View File

@ -3,6 +3,8 @@ import csv
import pickle import pickle
import pyjson5 as json5 import pyjson5 as json5
import progressbar import progressbar
import re
from collections import namedtuple
def read_root_csv(root_dir): 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)) assert proto_a == proto_b, repr((proto_a, proto_b))
# All site names appear to follow the pattern <type>_X<abs coord>Y<abs coord>.
# 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

29
prjxray/site_type.py Normal file
View File

@ -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]

View File

@ -1,5 +1,6 @@
from collections import namedtuple from collections import namedtuple
import json import json
from prjxray import lib
""" Database files available for a tile """ """ Database files available for a tile """
TileDbs = namedtuple('TileDbs', 'segbits mask tile_type') 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. 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. """ SitePin - Tuple representing a site pin within a tile.
Sites are generic based on type, however sites are instanced 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. 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): class Tile(object):
@ -46,15 +47,19 @@ class Tile(object):
def yield_sites(sites): def yield_sites(sites):
for site in sites: for site in sites:
yield Site( yield Site(
name=None, name=site['name'],
prefix=site['prefix'],
type=site['type'], type=site['type'],
x=None, x=site['x_coord'],
y=None, y=site['y_coord'],
site_pins=site['site_pins'], site_pins=tuple(
) SitePin(
name=name,
wire=wire,
) for name, wire in site['site_pins'].items()))
def yield_pips(pips): def yield_pips(pips):
for pip in pips: for pip in pips.values():
yield Pip( yield Pip(
net_to=pip['dst_wire'], net_to=pip['dst_wire'],
net_from=pip['src_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. """ Returns tuple of Pip namedtuple's representing the PIPs in this tile.
""" """
return self.pips 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())

View File

@ -11,14 +11,46 @@ def quick_test(db_root):
tile_types_in_grid = set( tile_types_in_grid = set(
g.gridinfo_at_loc(loc).tile_type for loc in g.tile_locations()) g.gridinfo_at_loc(loc).tile_type for loc in g.tile_locations())
tile_types_in_db = set(db.get_tile_types()) 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 assert len(tile_types_in_grid - tile_types_in_db) == 0
# Verify that all tile types can be loaded. # Verify that all tile types can be loaded.
for tile_type in db.get_tile_types(): for tile_type in db.get_tile_types():
tile = db.get_tile_type(tile_type) tile = db.get_tile_type(tile_type)
tile.get_wires() wires = tile.get_wires()
tile.get_sites() for site in tile.get_sites():
tile.get_pips() 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(): def main():