blob: 1e7d3c108228c3c067d1c03b62c4ba737eb39316 [file] [log] [blame] [edit]
#!/usr/bin/env python3
"""
A number of utility functions useful for traversing pb_type hierarchy as
defined in VPR architecture XML file.
"""
import re
import lxml.etree as ET
# =============================================================================
def is_leaf_pbtype(xml_pbtype):
"""
Returns true when the given pb_type is a leaf.
"""
assert xml_pbtype.tag == "pb_type", xml_pbtype.tag
return "blif_model" in xml_pbtype.attrib
def get_parent_pb(xml_pbtype):
"""
Returns a parent pb_type of the given one or none if it is a top-level
complex block
"""
assert xml_pbtype.tag in [
"pb_type", "mode", "interconnect"
], xml_pbtype.tag
# Get immediate parent
xml_parent = xml_pbtype.getparent()
# We've hit the end
if xml_parent is None or xml_parent.tag == "complexblocklist":
return None
# The immediate parent is a mode, jump one more level up
if xml_parent is not None and xml_parent.tag == "mode":
xml_parent = xml_parent.getparent()
return xml_parent
def get_parent_pb_and_mode(xml_pbtype):
"""
Returns a parent pb_type and mode element for the given pb_type. If there
are no modes (implicit default mode) then the mode element returned is
the same as the pb_type element.
"""
assert xml_pbtype.tag in ["pb_type", "mode"], xml_pbtype.tag
# Get immediate parent
xml_parent = xml_pbtype.getparent()
# We've hit the end
if xml_parent is None or xml_parent.tag == "complexblocklist":
return None, None
assert xml_parent.tag in ["pb_type", "mode"], xml_parent.tag
# pb_type parent
if xml_pbtype.tag == "pb_type":
if xml_parent.tag == "pb_type":
return xml_parent, xml_parent
elif xml_parent.tag == "mode":
return xml_parent.getparent(), xml_parent
elif xml_pbtype.tag == "mode":
return xml_parent.getparent(), xml_pbtype
def get_pb_by_name(xml_parent, name):
"""
Searches for a pb_type with the given name inside a parent pb_type.
Returns either a child or the parent.
The provided parent may be a mode but a pb_type is always returned.
"""
assert xml_parent.tag in ["pb_type", "mode"], xml_parent.tag
# Check the parent name. If this is a mode then check its parent name
if xml_parent.tag == "mode":
xml_parent_pb = get_parent_pb(xml_parent)
if xml_parent_pb.attrib["name"] == name:
return xml_parent_pb
else:
if xml_parent.attrib["name"] == name:
return xml_parent
# Check children
for xml_pbtype in xml_parent.findall("pb_type"):
if xml_pbtype.attrib["name"] == name:
return xml_pbtype
# None found
return None
# =============================================================================
def yield_pb_children(xml_parent):
"""
Yields all child pb_types and their indices taking into account num_pb.
"""
for xml_child in xml_parent.findall("pb_type"):
num_pb = int(xml_child.get("num_pb", 1))
for i in range(num_pb):
yield xml_child, i
# =============================================================================
INTERCONNECT_PORT_SPEC_RE = re.compile(
r"((?P<pbtype>[A-Za-z0-9_]+)(\[(?P<indices>[0-9:]+)\])?\.)"
r"(?P<port>[A-Za-z0-9_]+)(\[(?P<bits>[0-9:]+)\])?"
)
def get_pb_and_port(xml_ic, port_spec):
"""
"""
assert xml_ic.tag == "interconnect"
# Match the port name
match = INTERCONNECT_PORT_SPEC_RE.fullmatch(port_spec)
assert match is not None, port_spec
# Get the referenced pb_type
xml_parent = xml_ic.getparent()
xml_pbtype = get_pb_by_name(xml_parent, match.group("pbtype"))
assert xml_pbtype is not None, port_spec
# Find the port
port = match.group("port")
for xml_port in xml_pbtype:
if xml_port.tag in ["input", "output", "clock"]:
# Got it
if xml_port.attrib["name"] == port:
return xml_pbtype, xml_port
else:
assert False, (port_spec, xml_pbtype.attrib["name"], port)
def yield_indices(index_spec):
"""
Given an index specification string as "<i1>:<i0>" or "<i>" yields all bits
that it is comprised of. The function also supports cases when i0 > i1.
"""
# None
if index_spec is None:
return
# Range
elif ":" in index_spec:
i0, i1 = [int(i) for i in index_spec.split(":")]
if i0 > i1:
for i in range(i1, i0 + 1):
yield i
elif i0 < i1:
for i in range(i0, i1 + 1):
yield i
else:
yield i0
# Single value
else:
yield int(index_spec)
def yield_pins(xml_ic, port_spec, skip_index=True):
"""
Yields individual port pins as "<pb_type>[<pb_index].<port>[<bit>]" given
the port specification.
"""
assert xml_ic.tag == "interconnect"
# Match the port name
match = INTERCONNECT_PORT_SPEC_RE.fullmatch(port_spec)
assert match is not None, port_spec
# Get the referenced pb_type
xml_parent = xml_ic.getparent()
xml_pbtype = get_pb_by_name(xml_parent, match.group("pbtype"))
assert xml_pbtype is not None, port_spec
# Find the port
port = match.group("port")
for xml_port in xml_pbtype:
if xml_port.tag in ["input", "output", "clock"]:
if xml_port.attrib["name"] == port:
width = int(xml_port.attrib["num_pins"])
break
else:
assert False, (port_spec, xml_pbtype.attrib["name"], port)
# Check if we are referencing an upstream parent
is_parent_port = get_parent_pb(xml_ic) == xml_pbtype
# Build a list of pb_type indices
if not is_parent_port:
num_pb = int(xml_pbtype.get("num_pb", 1))
indices = list(yield_indices(match.group("indices")))
if not indices:
if num_pb > 1:
indices = list(range(0, num_pb))
else:
indices = [None]
else:
indices = [None]
# Build a list of bit indices
bits = list(yield_indices(match.group("bits")))
if not bits:
if width > 1:
bits = list(range(0, width))
else:
bits = [None]
# Yield individual pin names
for i in indices:
for j in bits:
name = match.group("pbtype")
if i is not None:
name += "[{}]".format(i)
elif not skip_index:
name += "[0]"
name += "." + match.group("port")
if j is not None:
name += "[{}]".format(j)
elif not skip_index:
name += "[0]"
yield name
# =============================================================================
def append_metadata(xml_item, meta_name, meta_data):
"""
Appends metadata to an element of architecture
"""
assert xml_item.tag in ["pb_type", "mode", "direct", "mux"]
xml_meta = ET.Element("meta", {"name": meta_name})
xml_meta.text = meta_data
# Use existing metadata section. If not present then create a new one
xml_metadata = xml_item.find("metadata")
if xml_metadata is None:
xml_metadata = ET.Element("metadata")
# Append meta, check for conflict
for item in xml_metadata.findall("meta"):
assert item.attrib["name"] != xml_meta.attrib["name"]
xml_metadata.append(xml_meta)
# Append
if xml_metadata.getparent() is None:
xml_item.append(xml_metadata)