blob: 56fb4f3427a1a757b21218afa13b8cf2d0a6c663 [file] [log] [blame] [edit]
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