Skip to content

Commit

Permalink
fasm: better LUT mapping and extra pip features handling
Browse files Browse the repository at this point in the history
This commit encapsulates two refactoring changes:

1. Addition of a LUT Mapper class which makes it easier to add FASM
features for both explicit-LUT-cells and LUT-thru wires
2. Better handling of PIPs extra features. Given that there are some
PIPs which, if enabled, enable a set of other features on other tiles,
the Node data needs to be accessed, which takes quite some time (~10
seconds for a xc7a35t device class).

Signed-off-by: Alessandro Comodi <acomodi@antmicro.com>
  • Loading branch information
acomodi committed Apr 13, 2021
1 parent 6447845 commit 400bfae
Show file tree
Hide file tree
Showing 4 changed files with 402 additions and 196 deletions.
2 changes: 1 addition & 1 deletion fpga_interchange/constraints/tool.py
Expand Up @@ -27,7 +27,7 @@ def make_problem_from_device(device, allowed_sites):
placement_oracle.add_sites_from_device(device)

placements = []
for tile, site, tile_type, site_type, bel in device.yield_bels():
for tile, site, tile_type, site_type, bel, bel_type in device.yield_bels():
if site not in allowed_sites:
continue

Expand Down
4 changes: 1 addition & 3 deletions fpga_interchange/device_resources.py
Expand Up @@ -190,8 +190,6 @@ def get_pins(self, site):
bel_name, bel_pin_name = self.site_type.bel_pin_index[bel_pin]
assert bel_name == self.name

yield self.site_type.bel_pin(site, bel_name, bel_pin_name)


class SitePin():
""" Site pin device resource object. """
Expand Down Expand Up @@ -891,7 +889,7 @@ def yield_bels(self):

for bel in site_type.bels:
yield tile_name, site_name, tile.tile_type, \
site.site_type_name, bel.name
site.site_type_name, bel.name, bel.type

def get_primitive_library(self):
from fpga_interchange.interchange_capnp import to_logical_netlist
Expand Down
243 changes: 152 additions & 91 deletions fpga_interchange/fasm_generators/generic.py
Expand Up @@ -12,72 +12,25 @@
from math import log2

from fpga_interchange.route_stitching import flatten_segments
from fpga_interchange.physical_netlist import PhysicalPip, PhysicalSitePip
from fpga_interchange.physical_netlist import PhysicalPip, PhysicalSitePip, PhysicalBelPin
from fpga_interchange.chip_info_utils import LutCell, LutBel, LutElement

PhysCellInstance = namedtuple(
'CellInstance',
'cell_type site_name site_type tile_name tile_type sites_in_tile bel bel_pins attributes'
'cell_type site_name site_type tile_name tile_type bel bel_pins attributes'
)


class FasmGenerator():
def __init__(self, interchange, device_resources, log_netlist_file,
phy_netlist_file):
with open(device_resources, "rb") as f:
self.device_resources = interchange.read_device_resources(f)

with open(log_netlist_file, "rb") as f:
self.logical_netlist = interchange.read_logical_netlist(f)

with open(phy_netlist_file, "rb") as f:
self.physical_netlist = interchange.read_physical_netlist(f)

self.physical_cells_instances = dict()
self.logical_cells_instances = dict()

self.build_luts_definitions()
self.build_log_cells_instances()
self.build_phys_cells_instances()
self.flatten_nets()

self.pips_features = set()
self.cells_features = set()

def add_cell_feature(self, feature_parts):
feature_str = ".".join(feature_parts)
self.cells_features.add(feature_str)

def add_pip_feature(self, feature_parts, pip_feature_format):
tile, wire0, wire1 = feature_parts
feature_str = pip_feature_format.format(
tile=tile, wire0=wire0, wire1=wire1)
self.pips_features.add(feature_str)

def flatten_nets(self):
self.flattened_nets = dict()

for net in self.physical_netlist.nets:
self.flattened_nets[net.name] = flatten_segments(net.sources +
net.stubs)

def get_tile_info_at_site(self, site_name):
tile_name = self.device_resources.get_tile_name_at_site_name(site_name)
tile = self.device_resources.tile_name_to_tile[tile_name]
tile_type = tile.tile_type
sites_in_tile = tile.site_names

return tile_name, tile_type, sites_in_tile

def build_luts_definitions(self):
class LutMapper():
def __init__(self, device_resources):
"""
Fills luts definition from the device resources database
"""

self.site_lut_elements = dict()
self.lut_cells = dict()

for site_lut_element in self.device_resources.device_resource_capnp.lutDefinitions.lutElements:
for site_lut_element in device_resources.device_resource_capnp.lutDefinitions.lutElements:
site = site_lut_element.site
self.site_lut_elements[site] = list()
for lut in site_lut_element.luts:
Expand All @@ -102,29 +55,65 @@ def build_luts_definitions(self):
lut_bel.low_bit = bel.lowBit
lut_bel.high_bit = bel.highBit

for lut_cell in self.device_resources.device_resource_capnp.lutDefinitions.lutCells:
for lut_cell in device_resources.device_resource_capnp.lutDefinitions.lutCells:
lut = LutCell()
self.lut_cells[lut_cell.cell] = lut

lut.name = lut_cell.cell
for pin in lut_cell.inputPins:
lut.pins.append(pin)

def get_phys_lut_init(self, logical_init_value, cell_data):
def find_lut_bel(self, site_type, bel):
"""
Returns the LUT Bel definition and the corresponding LUT element given the
corresponding site_type and bel name
"""
assert site_type in self.site_lut_elements, site_type
lut_elements = self.site_lut_elements[site_type]

for lut_element in lut_elements:
for lut_bel in lut_element.lut_bels:
if lut_bel.name == bel:
return lut_element, lut_bel

def get_phys_lut_init(self, log_init, lut_element, lut_bel, lut_cell,
phys_to_log):
bitstring_init = "{value:0{digits}b}".format(
value=log_init, digits=lut_bel.high_bit + 1)

# Invert the string to have the LSB in the beginning
logical_lut_init = bitstring_init[::-1]

physical_lut_init = list()
for phys_init_index in range(0, lut_element.width):
log_init_index = 0

for phys_port_idx in range(0, int(log2(lut_element.width))):
if not phys_init_index & (1 << phys_port_idx):
continue

log_port = None
if phys_port_idx < len(lut_bel.pins):
log_port = phys_to_log.get(lut_bel.pins[phys_port_idx])

if log_port is None:
continue

log_port_idx = lut_cell.pins.index(log_port)
log_init_index |= (1 << log_port_idx)

physical_lut_init.append(logical_lut_init[log_init_index])

return physical_lut_init[::-1]

def get_phys_cell_lut_init(self, logical_init_value, cell_data):
"""
Returns the LUTs physical INIT parameter mapping given the initial logical INIT
value and the cells' data containing the physical mapping of the input pins.
It is left to the caller to handle cases of fructured LUTs.
"""

def find_lut_bel(lut_elements, bel):
""" Returns the LUT Bel definition and the corresponding LUT element. """
for lut_element in lut_elements:
for lut_bel in lut_element.lut_bels:
if lut_bel.name == bel:
return lut_element, lut_bel

def physical_to_logical_map(lut_bel, bel_pins):
"""
Returns the physical pin to logical pin LUTs mapping.
Expand All @@ -147,40 +136,84 @@ def physical_to_logical_map(lut_bel, bel_pins):
bel_pins = cell_data.bel_pins
site_type = cell_data.site_type

assert site_type in self.site_lut_elements, site_type
lut_elements = self.site_lut_elements[site_type]
lut_element, lut_bel = self.find_lut_bel(site_type, bel)
phys_to_log = physical_to_logical_map(lut_bel, bel_pins)
lut_cell = self.lut_cells[cell_type]

physical_lut_init = self.get_phys_lut_init(
logical_init_value, lut_element, lut_bel, lut_cell, phys_to_log)

# Generate a string and invert the list, to have MSB in first position
return "".join(physical_lut_init)

def get_phys_wire_lut_init(self, logical_init_value, site_type, cell_type,
bel, bel_pin):
"""
Returns the LUTs physical INIT parameter mapping of a LUT-thru wire
It is left to the caller to handle cases of fructured LUTs.
"""

lut_element, lut_bel = find_lut_bel(lut_elements, bel)
lut_element, lut_bel = self.find_lut_bel(site_type, bel)
lut_cell = self.lut_cells[cell_type]
assert len(lut_cell.pins) == 1, (lut_cell.name, lut_cell.pins)
phys_to_log = dict((pin, None) for pin in lut_bel.pins)
phys_to_log[bel_pin] = lut_cell.pins[0]

bitstring_init = "{value:0{digits}b}".format(
value=logical_init_value, digits=lut_bel.high_bit + 1)
physical_lut_init = self.get_phys_lut_init(
logical_init_value, lut_element, lut_bel, lut_cell, phys_to_log)

# Invert the string to have the LSB in the beginning
logical_lut_init = bitstring_init[::-1]
phys_to_log = physical_to_logical_map(lut_bel, bel_pins)
# Generate a string and invert the list, to have MSB in first position
return "".join(physical_lut_init)

physical_lut_init = list()
for phys_init_index in range(0, lut_element.width):
log_init_index = 0

for phys_port_idx in range(0, int(log2(lut_element.width))):
if not phys_init_index & (1 << phys_port_idx):
continue
class FasmGenerator():
def __init__(self, interchange, device_resources, log_netlist_file,
phy_netlist_file):
with open(device_resources, "rb") as f:
self.device_resources = interchange.read_device_resources(f)

if phys_port_idx < len(lut_bel.pins):
log_port = phys_to_log.get(lut_bel.pins[phys_port_idx])
with open(log_netlist_file, "rb") as f:
self.logical_netlist = interchange.read_logical_netlist(f)

if log_port is None:
continue
with open(phy_netlist_file, "rb") as f:
self.physical_netlist = interchange.read_physical_netlist(f)

log_port_idx = lut_cell.pins.index(log_port)
log_init_index |= (1 << log_port_idx)
self.physical_cells_instances = dict()
self.logical_cells_instances = dict()

physical_lut_init.append(logical_lut_init[log_init_index])
self.build_log_cells_instances()
self.build_phys_cells_instances()
self.flatten_nets()

# Generate a string and invert the list, to have MSB in first position
return "".join(physical_lut_init[::-1])
self.pips_features = set()
self.cells_features = set()

self.lut_mapper = LutMapper(self.device_resources)

def add_cell_feature(self, feature_parts):
feature_str = ".".join(feature_parts)
self.cells_features.add(feature_str)

def add_pip_feature(self, feature_parts, pip_feature_format):
tile, wire0, wire1 = feature_parts
feature_str = pip_feature_format.format(
tile=tile, wire0=wire0, wire1=wire1)
self.pips_features.add(feature_str)

def flatten_nets(self):
self.flattened_nets = dict()

for net in self.physical_netlist.nets:
self.flattened_nets[net.name] = flatten_segments(net.sources +
net.stubs)

def get_tile_info_at_site(self, site_name):
tile_name = self.device_resources.get_tile_name_at_site_name(site_name)
tile = self.device_resources.tile_name_to_tile[tile_name]
tile_type = tile.tile_type

return tile_name, tile_type

def build_log_cells_instances(self):
"""
Expand Down Expand Up @@ -213,8 +246,7 @@ def build_phys_cells_instances(self):
site_name = placement.site
site_type = self.physical_netlist.site_instances[site_name]

tile_name, tile_type, sites_in_tile = self.get_tile_info_at_site(
site_name)
tile_name, tile_type = self.get_tile_info_at_site(site_name)

bel = placement.bel
bel_pins = placement.pins
Expand All @@ -226,12 +258,12 @@ def build_phys_cells_instances(self):
site_type=site_type,
tile_name=tile_name,
tile_type=tile_type,
sites_in_tile=sites_in_tile,
bel=bel,
bel_pins=bel_pins,
attributes=cell_attr)

def fill_pip_features(self, pip_feature_format, extra_pip_features):
def fill_pip_features(self, pip_feature_format, extra_pip_features,
avail_lut_thrus):
"""
This function generates all features corresponding to the physical routing
PIPs present in the physical netlist.
Expand All @@ -249,6 +281,7 @@ def fill_pip_features(self, pip_feature_format, extra_pip_features):
"""

site_thru_pips = list()
lut_thru_pips = dict()

for net in self.physical_netlist.nets:
for segment in self.flattened_nets[net.name]:
Expand All @@ -274,13 +307,41 @@ def fill_pip_features(self, pip_feature_format, extra_pip_features):
continue

if tile_type_name in extra_pip_features:
extra_pip_features[tile_type_name].append((tile,
wire0))
extra_pip_features[tile_type_name].add((tile, wire0))
extra_pip_features[tile_type_name].add((tile, wire1))

self.add_pip_feature((tile, wire0, wire1),
pip_feature_format)

return site_thru_pips
elif isinstance(segment, PhysicalBelPin):
bel = segment.bel

if bel not in avail_lut_thrus:
continue

pin = segment.pin
site = segment.site
site_type = list(self.device_resources.
site_name_to_site[site].keys())[0]
_, lut_bel = self.lut_mapper.find_lut_bel(site_type, bel)

key = (net.name, site, bel)
"""
A LUT-thru pip is present when both I/O pins are used for a
specific BEL at a specific site, for a given net.
If the key is not encountered twice, there is no LUT-thru
corresponding to the LUT BEL in question.
"""
if key not in lut_thru_pips:
lut_thru_pips[key] = {
"pin_name": pin,
"is_valid": False
}
elif lut_bel.out_pin == pin:
lut_thru_pips[key]["is_valid"] = True

return site_thru_pips, lut_thru_pips

def get_routing_bels(self, allowed_routing_bels):

Expand Down

0 comments on commit 400bfae

Please sign in to comment.