| from __future__ import print_function | |
| from collections import OrderedDict, namedtuple | |
| import lxml.etree as ET | |
| PlaceConstraint = namedtuple('PlaceConstraint', 'name x y z comment') | |
| HEADER_TEMPLATE = """\ | |
| #{name:<{nl}} x y z pcf_line | |
| #{s:-^{nl}} -- -- - ----""" | |
| CONSTRAINT_TEMPLATE = '{name:<{nl}} {x: 3} {y: 3} {z: 2} # {comment}' | |
| def get_root_cluster(curr): | |
| while True: | |
| parent = curr.getparent() | |
| if parent is None: | |
| return None | |
| parent_parent = parent.getparent() | |
| if parent_parent is None: | |
| return curr | |
| curr = parent | |
| class PlaceConstraints(object): | |
| def __init__(self, net_file): | |
| self.constraints = OrderedDict() | |
| self.block_to_loc = dict() | |
| net_xml = ET.parse(net_file) | |
| self.net_root = net_xml.getroot() | |
| def load_loc_sites_from_net_file(self): | |
| """ | |
| .place files expect top-level block (cluster) names, not net names, so | |
| build a mapping from net names to block names from the .net file. | |
| """ | |
| self.net_to_block = {} | |
| self.block_to_root_block = {} | |
| for el in self.net_root.iter('block'): | |
| root_block = get_root_cluster(el) | |
| if root_block is not None: | |
| self.block_to_root_block[el.attrib['name'] | |
| ] = root_block.attrib['name'] | |
| for attr in self.net_root.xpath("//attribute"): | |
| name = attr.attrib["name"] | |
| if name != 'LOC': | |
| continue | |
| # Get block name | |
| top_block = attr.getparent() | |
| assert top_block is not None | |
| while top_block.getparent() is not self.net_root: | |
| assert top_block is not None | |
| top_block = top_block.getparent() | |
| self.block_to_loc[top_block.get("name")] = attr.text | |
| def constrain_block(self, block_name, loc, comment=""): | |
| assert len(loc) == 3 | |
| assert block_name not in self.constraints, block_name | |
| place_constraint = PlaceConstraint( | |
| name=block_name, | |
| x=loc[0], | |
| y=loc[1], | |
| z=loc[2], | |
| comment=comment, | |
| ) | |
| root_block = self.block_to_root_block[block_name] | |
| self.constraints[root_block] = place_constraint | |
| def output_place_constraints(self, f): | |
| if not self.constraints: | |
| return | |
| max_name_length = max(len(c.name) for c in self.constraints.values()) | |
| constrained_blocks = {} | |
| for vpr_net, constraint in self.constraints.items(): | |
| name = constraint.name | |
| # This block is already constrained, check if there is no | |
| # conflict there. | |
| if name in constrained_blocks: | |
| existing = constrained_blocks[name] | |
| if existing.x != constraint.x or\ | |
| existing.y != constraint.y or\ | |
| existing.z != constraint.z: | |
| print( | |
| "Error: block '{}' has multiple conflicting constraints!" | |
| .format(name) | |
| ) | |
| print("", constrained_blocks[name]) | |
| print("", constraint) | |
| exit(-1) | |
| # Don't write the second constraing | |
| continue | |
| # omit if no corresponding block name for the net | |
| if name is not None: | |
| print( | |
| CONSTRAINT_TEMPLATE.format( | |
| name=name, | |
| nl=max_name_length, | |
| x=constraint.x, | |
| y=constraint.y, | |
| z=constraint.z, | |
| comment=constraint.comment | |
| ), | |
| file=f | |
| ) | |
| # Add to constrained block list | |
| constrained_blocks[name] = constraint | |
| def get_loc_sites(self): | |
| """Yields user-constraints (block, location) pairs""" | |
| if self.block_to_loc is None: | |
| return | |
| for loc in self.block_to_loc: | |
| yield (loc, self.block_to_loc[loc]) | |
| def get_used_instances(self, instance): | |
| """ | |
| Returns a list containing the root clusters of the specified instances in the packed netlist | |
| that are marked as used. | |
| An instance is marked as used when the XML element relative to the instance name has | |
| children tags. | |
| E.g.: | |
| <block name="idelayctrl_block" instance="IDELAYCTRL[0]"> | |
| <inputs> | |
| <port name="REFCLK">refclk</port> | |
| </inputs> | |
| ... | |
| </block> | |
| ... | |
| <block name="open" instance="IDELAYCTRL[0]" /> | |
| """ | |
| instances = list() | |
| for el in self.net_root.iter('block'): | |
| inst = el.attrib['instance'] | |
| if instance in inst: | |
| if len(el.getchildren()) != 0: | |
| instances.append(get_root_cluster(el).attrib['name']) | |
| return instances |