blob: b98a3cc95705fd067ce8b74e4ddb279fc291150d [file] [log] [blame] [edit]
""" Adds specialized pack patterns to avoid unroutable situations during packing
Given an input architecture, this utility will find all the directs that need
to have a specialized pack pattern and adds it to them.
To find the correct direct that needs to be updated there are two different ways:
1. If the direct belongs to the top level pb_type, the direct can be
checked against a regular expression specified at the beginning of
this file or with a string contained in the direct name.
2. If the direct belongs to an intermediate/leaf pb_type, the port name
and belonging operational `mode` are checked to select the correct
direct that needs update.
Currently IOPADs need specialized pack patterns to enable VTR to create molecules
between the various sites of the tile (e.g. ISERDES, IDELAY, IOB33 and OSERDES).
"""
import lxml.etree as ET
import argparse
import re
import itertools
# Regular Expressions to select the directs that need additional pack patterns.
# Being multiple IOB and IOPAD types, the name of the direct changes according
# to different types, hence a regex is needed.
IOPAD_OLOGIC_OQ_REGEX = re.compile("OLOGICE3.OQ_to_IOB33[MS]?.O")
IOPAD_OLOGIC_TQ_REGEX = re.compile("OLOGICE3.TQ_to_IOB33[MS]?.T")
IOPAD_ILOGIC_REGEX = re.compile("IOB33[MS]?.I_to_ILOGICE3.D")
# =============================================================================
def get_top_pb_type(element):
""" Returns the top level pb_type given a subelement of the XML tree."""
# Already top-level
parent = element.getparent()
if parent is not None and parent.tag == "complexblocklist":
return None
# Traverse
while True:
parent = element.getparent()
if parent is None:
return None
if parent.tag == "complexblocklist":
assert element.tag == "pb_type", element.tag
return element
element = parent
def add_pack_pattern(element, pack_pattern_prefix, for_input=None):
""" Adds the pack pattern to the given direct / mux with a specified
prefix. """
top_pb_type = get_top_pb_type(element)
pack_pattern_name = "{}_{}".format(
pack_pattern_prefix, top_pb_type.attrib['name']
)
if for_input is not None:
assert for_input in element.attrib['input']
pack_pattern_in_port = for_input
else:
pack_pattern_in_port = element.attrib['input']
pack_pattern_out_port = element.attrib['output']
# Check if not already there
for child in element.findall("pack_pattern"):
if child.attrib.get("name", None) == pack_pattern_name:
assert False, (
element.attrib["name"],
pack_pattern_name,
)
ET.SubElement(
element,
'pack_pattern',
attrib={
'name': pack_pattern_name,
'in_port': pack_pattern_in_port,
'out_port': pack_pattern_out_port
}
)
# =============================================================================
def maybe_add_pack_pattern(element, pack_pattern_prefix, list_to_check):
"""
Adds a pack pattern to the element ("direct" or "mux") that spans the
connection only if both of its endpoints are found in the list.
The pack pattern is prefixed with the pack_pattern_prefix and with a name
of the topmost pb_type in the hierarchy.
The list_to_check must contain tuples specifying connection endpoints in
the same way as in the arch.xml ie. "<pb_name>.<port_name>".
"""
# Check if we have a direct under an interconnect inside a mode/pb_type
interconnect = element.getparent()
if interconnect.tag != "interconnect":
return
mode_or_pb_type = interconnect.getparent()
if mode_or_pb_type.tag not in ['mode', 'pb_type']:
return
# A direct connection
if element.tag == "direct":
inp = element.attrib['input']
out = element.attrib['output']
if (inp, out) in list_to_check:
add_pack_pattern(element, pack_pattern_prefix)
# A mux connections (match the input)
elif element.tag == "mux":
ins = element.attrib['input'].split()
out = element.attrib['output']
for inp in ins:
if (inp, out) in list_to_check:
add_pack_pattern(element, pack_pattern_prefix, inp)
# Shouldn't happen
else:
assert False, element.tag
# =============================================================================
def main():
parser = argparse.ArgumentParser(
description="Adds needed pack patterns to the architecture file."
)
parser.add_argument('--in_arch', required=True, help="Input arch.xml")
args = parser.parse_args()
arch_xml = ET.ElementTree()
xml_parser = ET.XMLParser(remove_blank_text=True)
root_element = arch_xml.parse(args.in_arch, xml_parser)
gen = itertools.chain(
root_element.iter('direct'), root_element.iter('mux')
)
for direct in gen:
if 'name' not in direct.attrib:
continue
top_parent = get_top_pb_type(direct)
if top_parent is not None:
top_name = top_parent.attrib["name"]
else:
top_name = ""
dir_name = direct.attrib['name']
#
# OBUFT
#
# Adding OBUFT.TQ via T_INV helper primitive
if IOPAD_OLOGIC_TQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'T_INV_to_OBUFT')
maybe_add_pack_pattern(
direct, 'T_INV_to_OBUFT', [
('T_INV.TO', 'OLOGIC_TFF.TQ'),
('OLOGIC_TFF.TQ', 'OLOGICE3.TQ'),
('IOB33M.T', 'IOB33_MODES.T'),
('IOB33S.T', 'IOB33_MODES.T'),
('IOB33.T', 'IOB33_MODES.T'),
('IOB33_MODES.T', 'OBUFT_VPR.T'),
('OBUFT_VPR.O', 'outpad.outpad'),
]
)
#
# ODDR
#
# Adding ODDR.OQ via OBUF/OBUFT pack patterns
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'ODDR_to_OBUFT')
maybe_add_pack_pattern(
direct, 'ODDR_to_OBUFT', [
('ODDR_OQ.Q', 'OLOGIC_OFF.OQ'),
('OLOGIC_OFF.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33S.O', 'IOB33_MODES.O'),
('IOB33.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'OBUFT_VPR.I'),
('OBUFT_VPR.O', 'outpad.outpad'),
]
)
# Adding ODDR.OQ via OBUF/OBUFT + TQ via T_INV pack patterns
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'ODDR_to_T_INV_to_OBUFT')
if IOPAD_OLOGIC_TQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'ODDR_to_T_INV_to_OBUFT')
maybe_add_pack_pattern(
direct, 'ODDR_to_T_INV_to_OBUFT', [
('ODDR_OQ.Q', 'OLOGIC_OFF.OQ'),
('OLOGIC_OFF.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33S.O', 'IOB33_MODES.O'),
('IOB33.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'OBUFT_VPR.I'),
('OBUFT_VPR.O', 'outpad.outpad'),
('T_INV.TO', 'OLOGIC_TFF.TQ'),
('OLOGIC_TFF.TQ', 'OLOGICE3.TQ'),
('IOB33M.T', 'IOB33_MODES.T'),
('IOB33S.T', 'IOB33_MODES.T'),
('IOB33.T', 'IOB33_MODES.T'),
('IOB33_MODES.T', 'OBUFT_VPR.T'),
]
)
# Adding ODDR.TQ via OBUFT pack patterns
if IOPAD_OLOGIC_TQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'TDDR_to_OBUFT')
maybe_add_pack_pattern(
direct, 'TDDR_to_OBUFT', [
('ODDR_TQ.Q', 'OLOGIC_TFF.TQ'),
('OLOGIC_TFF.TQ', 'OLOGICE3.TQ'),
('IOB33M.T', 'IOB33_MODES.T'),
('IOB33S.T', 'IOB33_MODES.T'),
('IOB33.T', 'IOB33_MODES.T'),
('IOB33_MODES.T', 'OBUFT_VPR.T'),
('OBUFT_VPR.O', 'outpad.outpad'),
]
)
# TODO: "TDDR" via IOBUF, OBUFTDS, IOBUFDS
# TODO: ODDR+"TDDR" via OBUFT, IOBUF, OBUFTDS, IOBUFDS
#
# OSERDES
#
# Adding OSERDES via NO_OBUF pack patterns
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_NO_OBUF')
maybe_add_pack_pattern(
direct, 'OSERDES_to_NO_OBUF', [
('OSERDESE2.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33S.O', 'IOB33_MODES.O'),
('IOB33.O', 'IOB33_MODES.O'),
("IOB33_MODES.O", "outpad.outpad"),
]
)
# Adding OSERDES via OBUF/OBUFT pack patterns
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_OBUF')
maybe_add_pack_pattern(
direct, 'OSERDES_to_OBUF', [
('OSERDESE2.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33S.O', 'IOB33_MODES.O'),
('IOB33.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'OBUFT_VPR.I'),
('OBUFT_VPR.O', 'outpad.outpad'),
]
)
# Adding OSERDES via OBUF/OBUFT + TQ via T_INV pack patterns
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_T_INV_to_OBUF')
if IOPAD_OLOGIC_TQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_T_INV_to_OBUF')
maybe_add_pack_pattern(
direct, 'OSERDES_to_T_INV_to_OBUF', [
('OSERDESE2.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33S.O', 'IOB33_MODES.O'),
('IOB33.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'OBUFT_VPR.I'),
('OBUFT_VPR.O', 'outpad.outpad'),
('T_INV.TO', 'OLOGICE3.TQ'),
('IOB33M.T', 'IOB33_MODES.T'),
('IOB33S.T', 'IOB33_MODES.T'),
('IOB33.T', 'IOB33_MODES.T'),
('IOB33_MODES.T', 'OBUFT_VPR.T'),
]
)
# Adding OSERDES via IOBUF pack patterns
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_IOBUF')
maybe_add_pack_pattern(
direct, 'OSERDES_to_IOBUF', [
('OSERDESE2.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33S.O', 'IOB33_MODES.O'),
('IOB33.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'IOBUF_VPR.I'),
('IOBUF_VPR.IOPAD_$out', 'outpad.outpad'),
('inpad.inpad', 'IOBUF_VPR.IOPAD_$inp'),
]
)
# Adding OSERDES via differential OBUFDS/OBUFTDS pack patterns
if "IOPAD_M" in top_name:
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_OBUFDS')
maybe_add_pack_pattern(
direct, 'OSERDES_to_OBUFDS', [
('OSERDESE2.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'OBUFTDS_M_VPR.I'),
('OBUFTDS_M_VPR.O', 'outpad.outpad'),
]
)
# Adding OSERDES via differential OBUFDS/OBUFTDS + TQ via T_INV
# pack patterns
if "IOPAD_M" in top_name:
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_T_INV_to_OBUFDS')
if IOPAD_OLOGIC_TQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_T_INV_to_OBUFDS')
maybe_add_pack_pattern(
direct, 'OSERDES_to_T_INV_to_OBUFDS', [
('OSERDESE2.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'OBUFTDS_M_VPR.I'),
('OBUFTDS_M_VPR.O', 'outpad.outpad'),
('T_INV.TO', 'OLOGICE3.TQ'),
('IOB33M.T', 'IOB33_MODES.T'),
('IOB33_MODES.T', 'OBUFTDS_M_VPR.T'),
]
)
# Adding OSERDES via differential IOBUFDS pack patterns
if "IOPAD_M" in top_name:
if IOPAD_OLOGIC_OQ_REGEX.match(dir_name):
add_pack_pattern(direct, 'OSERDES_to_IOBUFDS')
maybe_add_pack_pattern(
direct, 'OSERDES_to_IOBUFDS', [
('OSERDESE2.OQ', 'OLOGICE3.OQ'),
('IOB33M.O', 'IOB33_MODES.O'),
('IOB33_MODES.O', 'IOBUFDS_M_VPR.I'),
('IOBUFDS_M_VPR.IOPAD_$out', 'outpad.outpad'),
('inpad.inpad', 'IOBUFDS_M_VPR.IOPAD_$inp'),
]
)
#
# IDDR
#
for use_idelay in [False, True]:
if use_idelay:
name = "IDDR_to_IDELAY"
connections = [('ILOGICE3.DDLY', 'IFF.D')]
else:
name = "IDDR"
connections = [('ILOGICE3.D', 'IFF.D')]
# Adding IDDR via IBUF pack patterns
if not use_idelay and IOPAD_ILOGIC_REGEX.match(dir_name):
add_pack_pattern(direct, name + '_to_IBUF')
if use_idelay and ('I_to_IDELAYE2' in dir_name
or 'DATAOUT_to_ILOGICE3' in dir_name):
add_pack_pattern(direct, name + '_to_IBUF')
maybe_add_pack_pattern(
direct, name + '_to_IBUF', [
('inpad.inpad', 'IBUF_VPR.I'),
('IBUF_VPR.O', 'IOB33_MODES.I'),
('IOB33_MODES.I', 'IOB33.I'),
('IOB33_MODES.I', 'IOB33M.I'),
('IOB33_MODES.I', 'IOB33S.I'),
] + connections
)
# TODO: IDDR via IOBUF, IDDR via IOBUFDS
#
# ISERDES
#
for use_idelay in [False, True]:
if use_idelay:
name = "ISERDESE2_to_IDELAY"
connections = [('ILOGICE3.DDLY', 'ISERDESE2_IDELAY.DDLY')]
else:
name = "ISERDESE2"
connections = [('ILOGICE3.D', 'ISERDESE2_NO_IDELAY.D')]
# Adding ISERDES via NO_IBUF pack patterns
if not use_idelay and IOPAD_ILOGIC_REGEX.match(dir_name):
add_pack_pattern(direct, name + '_to_NO_IBUF')
if use_idelay and ('I_to_IDELAYE2' in dir_name
or 'DATAOUT_to_ILOGICE3' in dir_name):
add_pack_pattern(direct, name + '_to_NO_IBUF')
maybe_add_pack_pattern(
direct, name + '_to_NO_IBUF', [
("inpad.inpad", "IOB33_MODES.I"),
('IOB33_MODES.I', 'IOB33.I'),
('IOB33_MODES.I', 'IOB33M.I'),
('IOB33_MODES.I', 'IOB33S.I'),
] + connections
)
# Adding ISERDES via IBUF pack patterns
if not use_idelay and IOPAD_ILOGIC_REGEX.match(dir_name):
add_pack_pattern(direct, name + '_to_IBUF')
if use_idelay and ('I_to_IDELAYE2' in dir_name
or 'DATAOUT_to_ILOGICE3' in dir_name):
add_pack_pattern(direct, name + '_to_IBUF')
maybe_add_pack_pattern(
direct, name + '_to_IBUF', [
('inpad.inpad', 'IBUF_VPR.I'),
('IBUF_VPR.O', 'IOB33_MODES.I'),
('IOB33_MODES.I', 'IOB33.I'),
('IOB33_MODES.I', 'IOB33M.I'),
('IOB33_MODES.I', 'IOB33S.I'),
] + connections
)
# Adding ISERDES via IOBUF pack patterns
if not use_idelay and IOPAD_ILOGIC_REGEX.match(dir_name):
add_pack_pattern(direct, name + '_to_IOBUF')
if use_idelay and ('I_to_IDELAYE2' in dir_name
or 'DATAOUT_to_ILOGICE3' in dir_name):
add_pack_pattern(direct, name + '_to_IOBUF')
maybe_add_pack_pattern(
direct, name + '_to_IOBUF', [
('IOBUF_VPR.IOPAD_$out', 'outpad.outpad'),
('inpad.inpad', 'IOBUF_VPR.IOPAD_$inp'),
('IOBUF_VPR.O', 'IOB33_MODES.I'),
('IOB33_MODES.I', 'IOB33.I'),
('IOB33_MODES.I', 'IOB33M.I'),
('IOB33_MODES.I', 'IOB33S.I'),
] + connections
)
# Adding ISERDES via differential IOBUFDS pack patterns
if "IOPAD_M" in top_name:
if not use_idelay and IOPAD_ILOGIC_REGEX.match(dir_name):
add_pack_pattern(direct, name + '_to_IOBUFDS')
if use_idelay and ('I_to_IDELAYE2' in dir_name
or 'DATAOUT_to_ILOGICE3' in dir_name):
add_pack_pattern(direct, name + '_to_IOBUFDS')
maybe_add_pack_pattern(
direct, name + '_to_IOBUFDS', [
('IOBUFDS_M_VPR.IOPAD_$out', 'outpad.outpad'),
('inpad.inpad', 'IOBUFDS_M_VPR.IOPAD_$inp'),
('IOBUFDS_M_VPR.O', 'IOB33_MODES.I'),
('IOB33_MODES.I', 'IOB33M.I'),
] + connections
)
#
# IDELAY only
#
# TODO: Need to change sth in the arch.
print(ET.tostring(arch_xml, pretty_print=True).decode('utf-8'))
if __name__ == "__main__":
main()