| #!/usr/bin/env python |
| """ |
| This script is intended to modify the XML architecture description file |
| in order to add the tile tags, necessary after the addition of the `tiles` |
| parsing capabilities of Verilog-to-Routing. |
| |
| This script needs to be used only if the architecture import scripts do not |
| take into account the physical tiles tags. In fact, it is used as last step |
| of the architecture description generation. |
| """ |
| |
| import argparse |
| import sys |
| import copy |
| |
| from lxml import etree as ET |
| |
| |
| def add_tile_tags(arch): |
| """ |
| This function moves the top level pb_types attributes |
| and tags to the tiles high-level tag. |
| |
| BEFORE: |
| <complexblocklist> |
| <pb_type name="BRAM" area="2" height="4" width="1" capacity="1"> |
| <inputs ... /> |
| <outputs ... /> |
| <interconnect ... /> |
| <fc ... /> |
| <pinlocations ... /> |
| <switchblock_locations ... /> |
| </pb_type> |
| </complexblocklist> |
| |
| AFTER: |
| <tiles> |
| <tile name="BRAM" area="2" height="4" width="1" capacity="1"> |
| <inputs ... /> |
| <outputs ... /> |
| <fc ... /> |
| <pinlocations ... /> |
| <switchblock_locations ... /> |
| <equivalent_sites> |
| <site pb_type="BRAM"/> |
| </equivalent_sites> |
| </tile> |
| </tiles> |
| <complexblocklist |
| <pb_type name="BRAM"> |
| <inputs ... /> |
| <outputs ... /> |
| <interconnect ... /> |
| </pb_type> |
| </complexblocklist> |
| """ |
| |
| TAGS_TO_SWAP = ['fc', 'pinlocations', 'switchblock_locations'] |
| TAGS_TO_COPY = ['input', 'output', 'clock'] |
| ATTR_TO_SWAP = ['area', 'height', 'width', 'capacity'] |
| |
| # A map of attribute names which have to be renamed. |
| ATTR_MAP = {'num_pb': 'capacity'} |
| |
| def swap_tags(tile, pb_type): |
| # Moving tags from top level pb_type to tile |
| for child in pb_type: |
| if child.tag in TAGS_TO_SWAP: |
| pb_type.remove(child) |
| tile.append(child) |
| if child.tag in TAGS_TO_COPY: |
| child_copy = copy.deepcopy(child) |
| tile.append(child_copy) |
| |
| if arch.findall('./tiles'): |
| return False |
| |
| models = arch.find('./models') |
| |
| tiles = ET.Element('tiles') |
| models.addnext(tiles) |
| |
| top_pb_types = [] |
| for pb_type in arch.iter('pb_type'): |
| if pb_type.getparent().tag == 'complexblocklist': |
| top_pb_types.append(pb_type) |
| |
| for pb_type in top_pb_types: |
| tile = ET.SubElement(tiles, 'tile') |
| attrs = pb_type.attrib |
| |
| for attr in attrs: |
| if attr in ATTR_MAP: |
| tile.set(ATTR_MAP[attr], pb_type.get(attr)) |
| else: |
| tile.set(attr, pb_type.get(attr)) |
| |
| # Remove attributes of top level pb_types only |
| for attr in ATTR_TO_SWAP: |
| pb_type.attrib.pop(attr, None) |
| for attr in ATTR_MAP.keys(): |
| pb_type.attrib.pop(attr, None) |
| |
| equivalent_sites = ET.Element("equivalent_sites") |
| site = ET.Element("site") |
| site.set("pb_type", attrs['name']) |
| |
| equivalent_sites.append(site) |
| tile.append(equivalent_sites) |
| |
| swap_tags(tile, pb_type) |
| |
| return True |
| |
| |
| def add_site_directs(arch): |
| TAGS_TO_COPY = ['input', 'output', 'clock'] |
| |
| def add_directs(equivalent_site, pb_type): |
| for child in pb_type: |
| if child.tag in TAGS_TO_COPY: |
| tile_name = equivalent_site.attrib['pb_type'] |
| port = child.attrib['name'] |
| |
| from_to = "%s.%s" % (tile_name, port) |
| |
| direct = ET.Element("direct") |
| direct.set("from", from_to) |
| direct.set("to", from_to) |
| equivalent_site.append(direct) |
| |
| if arch.findall('./tiles/tile/equivalent_sites/site/direct'): |
| return False |
| |
| top_pb_types = [] |
| for pb_type in arch.iter('pb_type'): |
| if pb_type.getparent().tag == 'complexblocklist': |
| top_pb_types.append(pb_type) |
| |
| sites = [] |
| for pb_type in arch.iter('site'): |
| sites.append(pb_type) |
| |
| for pb_type in top_pb_types: |
| for site in sites: |
| if pb_type.attrib['name'] == site.attrib['pb_type']: |
| add_directs(site, pb_type) |
| |
| return True |
| |
| |
| def add_sub_tiles(arch): |
| """ |
| This function adds the sub tiles tags to the input architecture. |
| Each physical tile must contain at least one sub tile. |
| |
| Note: the example below is only for explanatory reasons, the port/tile names are invented. |
| |
| BEFORE: |
| <tiles> |
| <tile name="BRAM_TILE" area="2" height="4" width="1" capacity="1"> |
| <inputs ... /> |
| <outputs ... /> |
| <fc ... /> |
| <pinlocations ... /> |
| <switchblock_locations ... /> |
| <equivalent_sites> |
| <site pb_type="BRAM" pin_mapping="direct"> |
| </equivalent_sites> |
| </tile> |
| </tiles> |
| |
| AFTER: |
| <tiles> |
| <tile name="BRAM_TILE" area="2" height="4" width="1"> |
| <sub_tile name="BRAM_SUB_TILE_X" capacity="1"> |
| <inputs ... /> |
| <outputs ... /> |
| <fc ... /> |
| <pinlocations ... /> |
| <equivalent_sites> |
| <site pb_type="BRAM" pin_mapping="direct"> |
| </equivalent_sites> |
| </sub_tile> |
| <switchblock_locations ... /> |
| </tile> |
| </tiles> |
| |
| """ |
| |
| TAGS_TO_SWAP = [ |
| 'fc', 'pinlocations', 'input', 'output', 'clock', 'equivalent_sites' |
| ] |
| |
| def swap_tags(sub_tile, tile): |
| # Moving tags from top level pb_type to tile |
| for child in tile: |
| if child.tag in TAGS_TO_SWAP: |
| tile.remove(child) |
| sub_tile.append(child) |
| |
| modified = False |
| |
| for tile in arch.iter('tile'): |
| if tile.findall('./sub_tile'): |
| continue |
| |
| sub_tile_name = tile.attrib['name'] |
| sub_tile = ET.Element('sub_tile', name='{}'.format(sub_tile_name)) |
| |
| # Transfer capacity to sub tile |
| if 'capacity' in tile.attrib.keys(): |
| sub_tile.attrib['capacity'] = tile.attrib['capacity'] |
| del tile.attrib['capacity'] |
| |
| # Transfer tags to swap from tile to sub tile |
| swap_tags(sub_tile, tile) |
| |
| tile.append(sub_tile) |
| |
| modified = True |
| |
| return modified |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument("--in_xml", required=True, help="xml_file to update") |
| parser.add_argument("--out_xml", required=True, help="updated xml_file") |
| |
| args = parser.parse_args() |
| |
| parser = ET.XMLParser(remove_blank_text=True) |
| root = ET.parse(args.in_xml, parser) |
| |
| root_tags = root.findall(".") |
| assert len(root_tags) == 1 |
| arch = root_tags[0] |
| |
| if arch.tag != "architecture": |
| print("Warning! Not an architecture file, exiting...") |
| sys.exit(0) |
| |
| modified = False |
| result = add_tile_tags(arch) |
| result = add_site_directs(arch) |
| result = add_sub_tiles(arch) |
| |
| if result: |
| modified = True |
| |
| if modified: |
| with open(args.out_xml, "wb") as f: |
| root.write(f, pretty_print=True) |
| |
| |
| if __name__ == "__main__": |
| main() |