diff --git a/.gitignore b/.gitignore index 28f22c0b..a1a839df 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ env *.swp *.swo *.yaml +!test_data/* diff --git a/fpga_interchange/chip_info.py b/fpga_interchange/chip_info.py index 87943f80..9acfee8a 100644 --- a/fpga_interchange/chip_info.py +++ b/fpga_interchange/chip_info.py @@ -9,6 +9,7 @@ # # SPDX-License-Identifier: ISC from enum import Enum +from itertools import product # Note: Increment by 1 ChipInfo.version number each time schema changes to # allow nextpnr binary to detect changes to schema. @@ -646,6 +647,99 @@ def append_bba(self, bba, label_prefix): bba.u32(len(getattr(self, field))) +class ClusterCellPort(): + def __init__(self, cell, port): + self.cell = cell + self.port = port + + def append_children_bba(self, bba, label_prefix): + pass + + def append_bba(self, bba, label_prefix): + bba.str_id(self.cell) + bba.str_id(self.port) + + +class ChainablePort(): + def __init__(self, cell_source, cell_sink, bel_source, bel_sink, x_offset, + y_offset): + self.cell_source = cell_source + self.cell_sink = cell_sink + self.bel_source = bel_source + self.bel_sink = bel_sink + self.avg_x_offset = x_offset + self.avg_y_offset = y_offset + + def append_children_bba(self, bba, label_prefix): + pass + + def append_bba(self, bba, label_prefix): + bba.str_id(self.cell_source) + bba.str_id(self.cell_sink) + bba.str_id(self.bel_source) + bba.str_id(self.bel_sink) + bba.u16(self.avg_x_offset) + bba.u16(self.avg_y_offset) + + +class Cluster(): + fields = ['chainable_ports', 'cluster_cell_ports'] + field_types = ['ChainablePortPOD', 'ClusterCellPortPOD'] + + def __init__(self, name, chainable_ports, root_cell_types, cluster_cells): + # Chain name + self.name = name + + # Chain cells used in BEL chain + self.root_cell_types = root_cell_types + + # Chainable ports + self.chainable_ports = [] + for port in chainable_ports: + self.chainable_ports.append( + ChainablePort(port["cell_source"], port["cell_sink"], + port["bel_source"], port["bel_sink"], + port["avg_x_offset"], port["avg_y_offset"])) + + # Optional cell <-> port mapping to bind cells to a specific cluster + self.cluster_cell_ports = [] + for cell in cluster_cells: + for cell, port in product(cell["cells"], cell["ports"]): + self.cluster_cell_ports.append(ClusterCellPort(cell, port)) + + def field_label(self, label_prefix, field): + prefix = '{}.{}.{}'.format(label_prefix, self.name, field) + return prefix + + def append_children_bba(self, bba, label_prefix): + label = label_prefix + + bba.label( + self.field_label(label_prefix, 'root_cell_types'), 'constids') + for cell_type in self.root_cell_types: + bba.str_id(cell_type) + + for field, field_type in zip(self.fields, self.field_types): + for value in getattr(self, field): + value.append_children_bba( + bba, self.field_label(label_prefix, field)) + + for field, field_type in zip(self.fields, self.field_types): + bba.label(self.field_label(label_prefix, field), field_type) + for value in getattr(self, field): + value.append_bba(bba, self.field_label(label_prefix, field)) + + def append_bba(self, bba, label_prefix): + bba.str_id(self.name) + + bba.ref(self.field_label(label_prefix, 'root_cell_types')) + bba.u32(len(self.root_cell_types)) + + for field in self.fields: + bba.ref(self.field_label(label_prefix, field)) + bba.u32(len(getattr(self, field))) + + class PackagePin(): def __init__(self): self.package_pin = '' @@ -1041,8 +1135,8 @@ def __init__(self): self.name = '' self.generator = '' - # Note: Increment by 1 this whenever schema changes. - self.version = 10 + # Note: Increment by 1 this whenever schema or the nextpnr chip database structure changes. + self.version = 11 self.width = 0 self.height = 0 @@ -1053,6 +1147,7 @@ def __init__(self): self.packages = [] self.wire_types = [] self.global_cells = [] + self.clusters = [] # str, constids self.bel_buckets = [] @@ -1068,7 +1163,7 @@ def append_bba(self, bba, label_prefix): children_fields = [ 'tile_types', 'sites', 'tiles', 'nodes', 'packages', 'wire_types', - 'global_cells', 'macros', 'macro_rules' + 'global_cells', 'macros', 'macro_rules', 'clusters' ] children_types = [ 'TileTypeInfoPOD', @@ -1080,6 +1175,7 @@ def append_bba(self, bba, label_prefix): 'GlobalCellPOD', 'MacroPOD', 'MacroExpansionPOD', + 'ClusterPOD', ] for field, field_type in zip(children_fields, children_types): prefix = '{}.{}'.format(label, field) diff --git a/fpga_interchange/nextpnr.py b/fpga_interchange/nextpnr.py index 2bbe4124..8c1b47c6 100644 --- a/fpga_interchange/nextpnr.py +++ b/fpga_interchange/nextpnr.py @@ -81,5 +81,7 @@ def pop(self): def check_labels(self): refs_and_labels = self.refs & self.labels - assert len(refs_and_labels) == len(self.refs) - assert len(refs_and_labels) == len(self.labels) + assert len(refs_and_labels) == len(self.refs), "{} vs. {}".format( + len(refs_and_labels), len(self.refs)) + assert len(refs_and_labels) == len(self.labels), "{} vs. {}".format( + len(refs_and_labels), len(self.labels)) diff --git a/fpga_interchange/populate_chip_info.py b/fpga_interchange/populate_chip_info.py index 24287583..ff2695b9 100644 --- a/fpga_interchange/populate_chip_info.py +++ b/fpga_interchange/populate_chip_info.py @@ -16,8 +16,8 @@ TileWireRef, CellBelMap, ParameterPins, CellBelPin, ConstraintTag, \ CellConstraint, ConstraintType, Package, PackagePin, LutCell, \ LutElement, LutBel, CellParameter, DefaultCellConnections, DefaultCellConnection, \ - WireType, Macro, MacroNet, MacroPortInst, MacroCellInst, MacroExpansion, MacroParamMapRule, MacroParamRuleType, MacroParameter, GlobalCell, GlobalCellPin - + WireType, Macro, MacroNet, MacroPortInst, MacroCellInst, MacroExpansion, \ + MacroParamMapRule, MacroParamRuleType, MacroParameter, GlobalCell, GlobalCellPin, Cluster from fpga_interchange.constraints.model import Tag, Placement, \ ImpliesConstraint, RequiresConstraint from fpga_interchange.constraint_generator import ConstraintPrototype @@ -191,7 +191,8 @@ def emit(self, lut_elements): class FlattenedTileType(): def __init__(self, device, tile_type_index, tile_type, cell_bel_mapper, - constraints, lut_elements, disabled_routethrus): + constraints, lut_elements, disabled_routethrus, + disabled_site_pips): self.tile_type_name = device.strs[tile_type.name] self.tile_type = tile_type @@ -207,6 +208,8 @@ def __init__(self, device, tile_type_index, tile_type, cell_bel_mapper, self.lut_elements = [] self.lut_elements_map = {} + self.disabled_site_pips = disabled_site_pips + # Add tile wires self.tile_wire_to_wire_in_tile_index = {} for wire_in_tile_index, wire in enumerate(tile_type.wires): @@ -555,6 +558,21 @@ def add_site_type(self, dst_bel_pin = site_pip.outpin dst_site_wire_idx = bel_pin_to_site_wire_index[dst_bel_pin] + src_pin_name_idx = site_type.belPins[src_bel_pin].name + dst_pin_name_idx = site_type.belPins[dst_bel_pin].name + bel_name_idx = site_type.bels[bel_idx].name + + disable_pip = False + for dis_pip in self.disabled_site_pips: + if device.strs[bel_name_idx] in dis_pip["bels"] and \ + dis_pip["ipin"] == device.strs[src_pin_name_idx] and \ + dis_pip["opin"] == device.strs[dst_pin_name_idx]: + disable_pip = True + break + + if disable_pip: + continue + self.add_site_pip(src_site_wire_idx, dst_site_wire_idx, site_index, idx) @@ -1762,8 +1780,10 @@ def populate_chip_info(device, constids, device_config): bel_bucket_seeds = device_config.get('buckets', []) global_buffer_bels = device_config.get('global_buffer_bels', []) disabled_routethrus = device_config.get('disabled_routethroughs', []) + disabled_site_pips = device_config.get('disabled_site_pips', []) disabled_cell_bel_map = device_config.get('disabled_cell_bel_map', []) global_buffer_cells = device_config.get('global_buffer_cells', []) + clusters = device_config.get('clusters', []) cell_bel_mapper = CellBelMapper(device, constids, disabled_cell_bel_map) @@ -1835,11 +1855,19 @@ def populate_chip_info(device, constids, device_config): assert lut_element.site not in lut_elements lut_elements[lut_element.site] = LutElementsEmitter(lut_element.luts) + for cluster in clusters: + cluster_name = cluster["name"] + cluster_obj = Cluster(cluster_name, cluster.get("chainable_ports", []), + cluster["root_cell_types"], + cluster.get("cluster_cells", [])) + + chip_info.clusters.append(cluster_obj) + for tile_type_index, tile_type in enumerate( device.device_resource_capnp.tileTypeList): flattened_tile_type = FlattenedTileType( device, tile_type_index, tile_type, cell_bel_mapper, constraints, - lut_elements, disabled_routethrus) + lut_elements, disabled_routethrus, disabled_site_pips) tile_type_info = flattened_tile_type.create_tile_type_info( cell_bel_mapper) diff --git a/test_data/series7_bel_chains.yaml b/test_data/series7_bel_chains.yaml new file mode 100644 index 00000000..e69de29b diff --git a/test_data/series7_device_config.yaml b/test_data/series7_device_config.yaml index d6efda81..581af758 100644 --- a/test_data/series7_device_config.yaml +++ b/test_data/series7_device_config.yaml @@ -94,3 +94,61 @@ disabled_cell_bel_map: - TFF - IFF - OUTFF +disabled_site_pips: + - bels: + - A6LUT + - B6LUT + - C6LUT + - D6LUT + ipin: A6 + opin: O6 +clusters: +- name: CARRY_CHAIN + chainable_ports: + - cell_source: CO[3] + cell_sink: CI + bel_source: CO3 + bel_sink: CIN + avg_x_offset: 0 + avg_y_offset: -1 + root_cell_types: + - CARRY4 + cluster_cells: + - cells: + - LUT1 + - LUT2 + - LUT3 + - LUT4 + - LUT5 + - LUT6 + ports: + - S[3] + - S[2] + - S[1] + - S[0] + - cells: + - FDRE + - FDSE + - FDCE + - FDPE + ports: + - O[3] + - O[2] + - O[1] + - O[0] +- name: LUT_FF + root_cell_types: + - FDRE + - FDSE + - FDCE + - FDPE + cluster_cells: + - cells: + - LUT1 + - LUT2 + - LUT3 + - LUT4 + - LUT5 + - LUT6 + ports: + - D