mirror of https://github.com/openXC7/prjxray.git
Add methods to library.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
parent
825d2dd9f0
commit
8ad5e64f85
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue