|  | #!/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() |