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