|  | """ 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() |