| #!/usr/bin/env python3 | |
| import os | |
| import argparse | |
| import pickle | |
| from collections import OrderedDict | |
| import lxml.etree as ET | |
| from data_structs import ConnectionType, Loc | |
| from tile_import import make_top_level_pb_type | |
| from tile_import import make_top_level_tile | |
| # ============================================================================= | |
| def is_direct(connection): | |
| """ | |
| Returns True if the connections spans two tiles directly. Not necessarly | |
| at the same location | |
| """ | |
| if connection.src.type == ConnectionType.TILE and \ | |
| connection.dst.type == ConnectionType.TILE and \ | |
| connection.is_direct is True: | |
| return True | |
| return False | |
| # ============================================================================= | |
| def add_segment(xml_parent, segment): | |
| """ | |
| Adds a segment | |
| """ | |
| segment_type = "bidir" | |
| # Make XML | |
| xml_seg = ET.SubElement( | |
| xml_parent, "segment", { | |
| "name": segment.name, | |
| "length": str(segment.length), | |
| "freq": "1.0", | |
| "type": segment_type, | |
| "Rmetal": str(segment.r_metal), | |
| "Cmetal": str(segment.c_metal), | |
| } | |
| ) | |
| if segment_type == "unidir": | |
| ET.SubElement(xml_seg, "mux", {"name": "generic"}) | |
| elif segment_type == "bidir": | |
| ET.SubElement(xml_seg, "wire_switch", {"name": "generic"}) | |
| ET.SubElement(xml_seg, "opin_switch", {"name": "generic"}) | |
| else: | |
| assert False, segment_type | |
| e = ET.SubElement(xml_seg, "sb", {"type": "pattern"}) | |
| e.text = " ".join(["1" for i in range(segment.length + 1)]) | |
| e = ET.SubElement(xml_seg, "cb", {"type": "pattern"}) | |
| e.text = " ".join(["1" for i in range(segment.length)]) | |
| def add_switch(xml_parent, switch): | |
| """ | |
| Adds a switch | |
| """ | |
| xml_switch = ET.SubElement( | |
| xml_parent, "switch", { | |
| "type": switch.type, | |
| "name": switch.name, | |
| "R": str(switch.r), | |
| "Cin": str(switch.c_in), | |
| "Cout": str(switch.c_out), | |
| "Tdel": str(switch.t_del), | |
| } | |
| ) | |
| if switch.type in ["mux", "tristate"]: | |
| xml_switch.attrib["Cinternal"] = str(switch.c_int) | |
| def initialize_arch(xml_arch, switches, segments): | |
| """ | |
| Initializes the architecture definition from scratch. | |
| """ | |
| # ................................. | |
| # Device | |
| xml_device = ET.SubElement(xml_arch, "device") | |
| ET.SubElement( | |
| xml_device, "sizing", { | |
| "R_minW_nmos": "6000.0", | |
| "R_minW_pmos": "18000.0", | |
| } | |
| ) | |
| ET.SubElement(xml_device, "area", {"grid_logic_tile_area": "15000.0"}) | |
| xml = ET.SubElement(xml_device, "chan_width_distr") | |
| ET.SubElement(xml, "x", {"distr": "uniform", "peak": "1.0"}) | |
| ET.SubElement(xml, "y", {"distr": "uniform", "peak": "1.0"}) | |
| ET.SubElement( | |
| xml_device, "connection_block", {"input_switch_name": "generic"} | |
| ) | |
| ET.SubElement(xml_device, "switch_block", { | |
| "type": "wilton", | |
| "fs": "3", | |
| }) | |
| ET.SubElement( | |
| xml_device, "default_fc", { | |
| "in_type": "frac", | |
| "in_val": "1.0", | |
| "out_type": "frac", | |
| "out_val": "1.0", | |
| } | |
| ) | |
| # ................................. | |
| # Switchlist | |
| xml_switchlist = ET.SubElement(xml_arch, "switchlist") | |
| got_generic_switch = False | |
| for switch in switches: | |
| add_switch(xml_switchlist, switch) | |
| # Check for the generic switch | |
| if switch.name == "generic": | |
| got_generic_switch = True | |
| # No generic switch | |
| assert got_generic_switch | |
| # ................................. | |
| # Segmentlist | |
| xml_seglist = ET.SubElement(xml_arch, "segmentlist") | |
| for segment in segments: | |
| add_segment(xml_seglist, segment) | |
| def write_tiles(xml_arch, arch_tile_types, tile_types, equivalent_sites): | |
| """ | |
| Generates the "tiles" section of the architecture file | |
| """ | |
| # The "tiles" section | |
| xml_tiles = xml_arch.find("tiles") | |
| if xml_tiles is None: | |
| xml_tiles = ET.SubElement(xml_arch, "tiles") | |
| # Add tiles | |
| for tile_type, sub_tiles in arch_tile_types.items(): | |
| xml = make_top_level_tile( | |
| tile_type, sub_tiles, tile_types, equivalent_sites | |
| ) | |
| xml_tiles.append(xml) | |
| def write_pb_types(xml_arch, arch_pb_types, tile_types, nsmap): | |
| """ | |
| Generates the "complexblocklist" section. | |
| """ | |
| # Complexblocklist | |
| xml_cplx = xml_arch.find("complexblocklist") | |
| if xml_cplx is None: | |
| xml_cplx = ET.SubElement(xml_arch, "complexblocklist") | |
| # Add pb_types | |
| for pb_type in arch_pb_types: | |
| xml = make_top_level_pb_type(tile_types[pb_type], nsmap) | |
| xml_cplx.append(xml) | |
| def write_models(xml_arch, arch_models, nsmap): | |
| """ | |
| Generates the "models" section. | |
| """ | |
| # Models | |
| xml_models = xml_arch.find("models") | |
| if xml_models is None: | |
| xml_models = ET.SubElement(xml_arch, "models") | |
| # Include cell models | |
| xi_include = "{{{}}}include".format(nsmap["xi"]) | |
| for model in arch_models: | |
| name = model.lower() | |
| # Be smart. Check if there is a file for that cell in the current | |
| # directory. If not then use the one from "primitives" path | |
| model_file = "./{}.model.xml".format(name) | |
| if not os.path.isfile(model_file): | |
| model_file = "../../primitives/{}/{}.model.xml".format(name, name) | |
| ET.SubElement( | |
| xml_models, xi_include, { | |
| "href": model_file, | |
| "xpointer": "xpointer(models/child::node())", | |
| } | |
| ) | |
| def write_tilegrid(xml_arch, arch_tile_grid, loc_map, layout_name): | |
| """ | |
| Generates the "layout" section of the arch XML and appends it to the | |
| root given. | |
| """ | |
| # Remove the "layout" tag if any | |
| xml_layout = xml_arch.find("layout") | |
| if xml_layout is not None: | |
| xml_arch.remove(xml_layout) | |
| # Grid size | |
| xs = [flat_loc[0] for flat_loc in arch_tile_grid] | |
| ys = [flat_loc[1] for flat_loc in arch_tile_grid] | |
| w = max(xs) + 1 | |
| h = max(ys) + 1 | |
| # Fixed layout | |
| xml_layout = ET.SubElement(xml_arch, "layout") | |
| xml_fixed = ET.SubElement( | |
| xml_layout, "fixed_layout", { | |
| "name": layout_name, | |
| "width": str(w), | |
| "height": str(h), | |
| } | |
| ) | |
| # Individual tiles | |
| for flat_loc, tile in arch_tile_grid.items(): | |
| if tile is None: | |
| continue | |
| # Unpack | |
| tile_type, capacity = tile | |
| # Single tile | |
| xml_sing = ET.SubElement( | |
| xml_fixed, | |
| "single", | |
| { | |
| "type": "TL-{}".format(tile_type.upper()), | |
| "x": str(flat_loc[0]), | |
| "y": str(flat_loc[1]), | |
| "priority": str(10), # Not sure if we need this | |
| } | |
| ) | |
| # Gather metadata | |
| metadata = [] | |
| for i in range(capacity): | |
| loc = Loc(x=flat_loc[0], y=flat_loc[1], z=i) | |
| if loc in loc_map.bwd: | |
| phy_loc = loc_map.bwd[loc] | |
| metadata.append("X{}Y{}".format(phy_loc.x, phy_loc.y)) | |
| # Emit metadata if any | |
| if len(metadata): | |
| xml_metadata = ET.SubElement(xml_sing, "metadata") | |
| xml_meta = ET.SubElement( | |
| xml_metadata, "meta", { | |
| "name": "fasm_prefix", | |
| } | |
| ) | |
| xml_meta.text = " ".join(metadata) | |
| def write_direct_connections(xml_arch, tile_grid, connections): | |
| """ | |
| """ | |
| def get_tile(ep): | |
| """ | |
| Retireves tile for the given connection endpoint | |
| """ | |
| if ep.loc in tile_grid and tile_grid[ep.loc] is not None: | |
| return tile_grid[ep.loc] | |
| else: | |
| print("ERROR: No tile found for the connection endpoint", ep) | |
| return None | |
| # Remove the "directlist" tag if any | |
| xml_directlist = xml_arch.find("directlist") | |
| if xml_directlist is not None: | |
| xml_arch.remove(xml_directlist) | |
| # Make a new one | |
| xml_directlist = ET.SubElement(xml_arch, "directlist") | |
| # Populate connections | |
| conns = [c for c in connections if is_direct(c)] | |
| for connection in conns: | |
| src_tile = get_tile(connection.src) | |
| dst_tile = get_tile(connection.dst) | |
| if not src_tile or not dst_tile: | |
| continue | |
| src_name = "TL-{}.{}".format(src_tile.type, connection.src.pin) | |
| dst_name = "TL-{}.{}".format(dst_tile.type, connection.dst.pin) | |
| name = "{}_at_X{}Y{}Z{}_to_{}_at_X{}Y{}Z{}".format( | |
| src_name, | |
| connection.src.loc.x, | |
| connection.src.loc.y, | |
| connection.src.loc.z, | |
| dst_name, | |
| connection.dst.loc.x, | |
| connection.dst.loc.y, | |
| connection.dst.loc.z, | |
| ) | |
| delta_loc = Loc( | |
| x=connection.dst.loc.x - connection.src.loc.x, | |
| y=connection.dst.loc.y - connection.src.loc.y, | |
| z=connection.dst.loc.z - connection.src.loc.z, | |
| ) | |
| # Format the direct connection tag | |
| ET.SubElement( | |
| xml_directlist, "direct", { | |
| "name": name, | |
| "from_pin": src_name, | |
| "to_pin": dst_name, | |
| "x_offset": str(delta_loc.x), | |
| "y_offset": str(delta_loc.y), | |
| "z_offset": str(delta_loc.z), | |
| } | |
| ) | |
| # ============================================================================= | |
| def main(): | |
| # Parse arguments | |
| parser = argparse.ArgumentParser( | |
| description=__doc__, | |
| formatter_class=argparse.RawDescriptionHelpFormatter | |
| ) | |
| parser.add_argument( | |
| "--vpr-db", type=str, required=True, help="VPR database file" | |
| ) | |
| parser.add_argument( | |
| "--arch-out", | |
| type=str, | |
| default="arch.xml", | |
| help="Output arch XML file (def. arch.xml)" | |
| ) | |
| parser.add_argument( | |
| "--device", | |
| type=str, | |
| default="quicklogic", | |
| help="Device name for the architecture" | |
| ) | |
| args = parser.parse_args() | |
| xi_url = "http://www.w3.org/2001/XInclude" | |
| ET.register_namespace("xi", xi_url) | |
| nsmap = {"xi": xi_url} | |
| # Load data from the database | |
| with open(args.vpr_db, "rb") as fp: | |
| db = pickle.load(fp) | |
| loc_map = db["loc_map"] | |
| vpr_tile_types = db["vpr_tile_types"] | |
| vpr_tile_grid = db["vpr_tile_grid"] | |
| vpr_equivalent_sites = db["vpr_equivalent_sites"] | |
| segments = db["segments"] | |
| switches = db["switches"] | |
| connections = db["connections"] | |
| # Flatten the VPR tilegrid | |
| flat_tile_grid = dict() | |
| for vpr_loc, tile in vpr_tile_grid.items(): | |
| flat_loc = (vpr_loc.x, vpr_loc.y) | |
| if flat_loc not in flat_tile_grid: | |
| flat_tile_grid[flat_loc] = {} | |
| if tile is not None: | |
| flat_tile_grid[flat_loc][vpr_loc.z] = tile.type | |
| # Create the arch tile grid and arch tile types | |
| arch_tile_grid = dict() | |
| arch_tile_types = dict() | |
| arch_pb_types = set() | |
| arch_models = set() | |
| for flat_loc, tiles in flat_tile_grid.items(): | |
| if len(tiles): | |
| # Group identical sub-tiles together, maintain their order | |
| sub_tiles = OrderedDict() | |
| for z, tile in tiles.items(): | |
| if tile not in sub_tiles: | |
| sub_tiles[tile] = 0 | |
| sub_tiles[tile] += 1 | |
| # TODO: Make arch tile type name | |
| tile_type = tiles[0] | |
| # Create the tile type with sub tile types for the arch | |
| arch_tile_types[tile_type] = sub_tiles | |
| # Add each sub-tile to top-level pb_type list | |
| for tile in sub_tiles: | |
| arch_pb_types.add(tile) | |
| # Add each cell of a sub-tile to the model list | |
| for tile in sub_tiles: | |
| for cell_type in vpr_tile_types[tile].cells.keys(): | |
| arch_models.add(cell_type) | |
| # Add the arch tile type to the arch tile grid | |
| arch_tile_grid[flat_loc] = ( | |
| tile_type, | |
| len(tiles), | |
| ) | |
| else: | |
| # Add an empty location | |
| arch_tile_grid[flat_loc] = None | |
| # Initialize the arch XML if file not given | |
| xml_arch = ET.Element("architecture", nsmap=nsmap) | |
| initialize_arch(xml_arch, switches, segments) | |
| # Add tiles | |
| write_tiles( | |
| xml_arch, arch_tile_types, vpr_tile_types, vpr_equivalent_sites | |
| ) | |
| # Add pb_types | |
| write_pb_types(xml_arch, arch_pb_types, vpr_tile_types, nsmap) | |
| # Add models | |
| write_models(xml_arch, arch_models, nsmap) | |
| # Write the tilegrid to arch | |
| write_tilegrid(xml_arch, arch_tile_grid, loc_map, args.device) | |
| # Write direct connections | |
| write_direct_connections(xml_arch, vpr_tile_grid, connections) | |
| # Save the arch | |
| ET.ElementTree(xml_arch).write( | |
| args.arch_out, | |
| pretty_print=True, | |
| xml_declaration=True, | |
| encoding="utf-8" | |
| ) | |
| # ============================================================================= | |
| if __name__ == "__main__": | |
| main() |