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

f1ff14ec4d30674405b0aec985b13d9832591f45
<mask token>


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
            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
    <mask token>
    <mask token>

    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

<mask token>


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
            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
            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)
                continue
            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)
                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

<mask token>
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
            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
            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)
                continue
            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)
                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

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
            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
            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)
                continue
            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)
                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

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
