| #!/usr/bin/env python |
| |
| import argparse |
| import sys |
| import copy |
| from collections import OrderedDict |
| |
| from lxml import etree as ET |
| |
| class SequentialPort: |
| def __init__(self, port, clock): |
| self.port = port |
| self.clock = clock |
| |
| class CombEdge: |
| def __init__(self, src_port, sink_port): |
| self.src_port = src_port |
| self.sink_ports = sink_port |
| |
| class ModelTiming: |
| def __init__(self): |
| self.sequential_ports = set() |
| self.comb_edges = {} |
| |
| INDENT = " " |
| |
| #To add support for new upgrades/features add the identifier string here |
| #and added a guarded call in main() |
| supported_upgrades = [ |
| "add_model_timing", |
| "upgrade_fc_overrides", |
| "upgrade_device_layout", |
| "remove_io_chan_distr", |
| "upgrade_pinlocations", |
| "uniqify_interconnect_names", |
| "upgrade_connection_block_input_switch", |
| "upgrade_switch_types", |
| "rename_fc_attributes", |
| "longline_no_sb_cb", |
| "upgrade_port_equivalence", |
| "upgrade_complex_sb_num_conns", |
| "add_missing_comb_model_internal_timing_edges", |
| "add_tile_tags", |
| ] |
| |
| def parse_args(): |
| parser = argparse.ArgumentParser(description="Upgrade Legacy VTR architecture files") |
| |
| parser.add_argument("xml_file", help="xml_file to update (modified in place)") |
| parser.add_argument("--features", |
| nargs="*", |
| help="List of features/upgrades to apply (default: %(default)s)", |
| choices=supported_upgrades, |
| default=supported_upgrades) |
| parser.add_argument("--debug", default=False, action="store_true", help="Print to stdout instead of modifying file inplace") |
| parser.add_argument("--pretty", default=True, help="Pretty print the output? (default: %(default)s)") |
| |
| return parser.parse_args() |
| |
| def main(): |
| args = parse_args() |
| |
| print args.xml_file |
| parser = ET.XMLParser(remove_blank_text=True) |
| root = ET.parse(args.xml_file, 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 |
| if "add_model_timing" in args.features: |
| result = add_model_timing(arch) |
| if result: |
| modified = True |
| |
| if "upgrade_fc_overrides" in args.features: |
| result = upgrade_fc_overrides(arch) |
| if result: |
| modified = True |
| |
| if "upgrade_device_layout" in args.features: |
| result = upgrade_device_layout(arch) |
| if result: |
| modified = True |
| |
| if "remove_io_chan_distr" in args.features: |
| result = remove_io_chan_distr(arch) |
| if result: |
| modified = True |
| |
| if "upgrade_pinlocations" in args.features: |
| result = upgrade_pinlocations(arch) |
| if result: |
| modified = True |
| |
| if "uniqify_interconnect_names" in args.features: |
| result = uniqify_interconnect_names(arch) |
| if result: |
| modified = True |
| |
| if "upgrade_connection_block_input_switch" in args.features: |
| result = upgrade_connection_block_input_switch(arch) |
| if result: |
| modified = True |
| |
| if "upgrade_switch_types" in args.features: |
| result = upgrade_switch_types(arch) |
| if result: |
| modified = True |
| |
| if "rename_fc_attributes" in args.features: |
| result = rename_fc_attributes(arch) |
| if result: |
| modified = True |
| |
| if "longline_no_sb_cb" in args.features: |
| result = remove_longline_sb_cb(arch) |
| if result: |
| modified = True |
| |
| if "upgrade_port_equivalence" in args.features: |
| result = upgrade_port_equivalence(arch) |
| if result: |
| modified = True |
| |
| if "upgrade_complex_sb_num_conns" in args.features: |
| result = upgrade_complex_sb_num_conns(arch) |
| if result: |
| modified = True |
| |
| |
| if "add_missing_comb_model_internal_timing_edges" in args.features: |
| result = add_missing_comb_model_internal_timing_edges(arch) |
| if result: |
| modified = True |
| |
| if "add_tile_tags" in args.features: |
| result = add_tile_tags(arch) |
| if result: |
| modified = True |
| |
| if modified: |
| if args.debug: |
| root.write(sys.stdout, pretty_print=args.pretty) |
| else: |
| with open(args.xml_file, "w") as f: |
| root.write(f, pretty_print=args.pretty) |
| |
| def add_model_timing(arch): |
| """ |
| Records the timing edges specified via timing annotationson primitive PB types, |
| and adds the appropriate timing edges to the primitive descriptions in the models |
| section. |
| """ |
| models = arch.findall("./models/model") |
| |
| #Find all primitive pb types |
| prim_pbs = arch.findall(".//pb_type[@blif_model]") |
| |
| #Build up the timing specifications from |
| default_models = frozenset([".input", ".output", ".latch", ".names"]) |
| primitive_timing_specs = {} |
| for prim_pb in prim_pbs: |
| |
| blif_model = prim_pb.attrib['blif_model'] |
| |
| if blif_model in default_models: |
| continue |
| |
| assert blif_model.startswith(".subckt ") |
| blif_model = blif_model[len(".subckt "):] |
| |
| if blif_model not in primitive_timing_specs: |
| primitive_timing_specs[blif_model] = ModelTiming() |
| |
| #Find combinational edges |
| for xpath_pattern in ["./delay_constant", "./delay_matrix"]: |
| for delay_const in prim_pb.findall(xpath_pattern): |
| iports = get_port_names(delay_const.attrib['in_port']) |
| oports = get_port_names(delay_const.attrib['out_port']) |
| |
| for iport in iports: |
| if iport not in primitive_timing_specs[blif_model].comb_edges: |
| primitive_timing_specs[blif_model].comb_edges[iport] = set() |
| |
| for oport in oports: |
| primitive_timing_specs[blif_model].comb_edges[iport].add(oport) |
| |
| #Find sequential ports |
| for xpath_pattern in ["./T_setup", "./T_clock_to_Q", "./T_hold"]: |
| for seq_tag in prim_pb.findall(xpath_pattern): |
| ports = get_port_names(seq_tag.attrib['port']) |
| for port in ports: |
| clk = seq_tag.attrib['clock'] |
| |
| primitive_timing_specs[blif_model].sequential_ports.add((port, clk)) |
| |
| changed = False |
| for model in models: |
| if model.attrib['name'] not in primitive_timing_specs: |
| print "Warning: no timing specifications found for {}".format(mode.attrib['name']) |
| continue |
| |
| model_timing = primitive_timing_specs[model.attrib['name']] |
| |
| #Mark model ports as sequential |
| for port_name, clock_name in model_timing.sequential_ports: |
| port = model.find(".//port[@name='{}']".format(port_name)) |
| port.attrib['clock'] = clock_name |
| changed = True |
| |
| #Mark combinational edges from sources to sink ports |
| for src_port, sink_ports in model_timing.comb_edges.iteritems(): |
| xpath_pattern = "./input_ports/port[@name='{}']".format(src_port) |
| port = model.find(xpath_pattern) |
| port.attrib['combinational_sink_ports'] = ' '.join(sink_ports) |
| changed = True |
| |
| return changed |
| |
| def upgrade_fc_overrides(arch): |
| """ |
| Convets the legacy block <fc> pin and segment override specifications, |
| to the new unified format. |
| """ |
| fc_tags = arch.findall(".//fc") |
| |
| changed = False |
| for fc_tag in fc_tags: |
| assert fc_tag.tag == "fc" |
| |
| old_fc_pin_overrides = fc_tag.findall("./pin") |
| old_fc_seg_overrides = fc_tag.findall("./segment") |
| |
| assert len(old_fc_pin_overrides) == 0 or len(old_fc_seg_overrides) == 0, "Can only have pin or seg overrides (not both)" |
| |
| for old_pin_override in old_fc_pin_overrides: |
| |
| port = old_pin_override.attrib['name'] |
| fc_type = old_pin_override.attrib['fc_type'] |
| fc_val = old_pin_override.attrib['fc_val'] |
| |
| fc_tag.remove(old_pin_override) |
| |
| new_attrib = OrderedDict() |
| new_attrib["port_name"] = port |
| new_attrib["fc_type"] = fc_type |
| new_attrib["fc_val"] = fc_val |
| |
| new_pin_override = ET.SubElement(fc_tag, "fc_override", attrib=new_attrib) |
| changed = True |
| |
| for old_seg_override in old_fc_seg_overrides: |
| |
| seg_name = old_seg_override.attrib['name'] |
| in_val = old_seg_override.attrib['in_val'] |
| out_val = old_seg_override.attrib['out_val'] |
| |
| fc_tag.remove(old_seg_override) |
| |
| fc_in_type = fc_tag.attrib['default_in_type'] |
| fc_out_type = fc_tag.attrib['default_out_type'] |
| |
| pb_type = fc_tag.find("..") |
| assert pb_type.tag == "pb_type" |
| |
| inputs = pb_type.findall("./input") |
| outputs = pb_type.findall("./output") |
| clocks = pb_type.findall("./clock") |
| |
| for input in inputs + clocks: |
| |
| new_attrib = OrderedDict() |
| new_attrib["port_name"] = input.attrib['name'] |
| new_attrib["segment_name"] = seg_name |
| new_attrib["fc_type"] = fc_in_type |
| new_attrib["fc_val"] = in_val |
| |
| fc_override = ET.SubElement(fc_tag, "fc_override", attrib=new_attrib) |
| |
| for output in outputs: |
| |
| new_attrib = OrderedDict() |
| new_attrib["port_name"] = output.attrib['name'] |
| new_attrib["segment_name"] = seg_name |
| new_attrib["fc_type"] = fc_out_type |
| new_attrib["fc_val"] = out_val |
| |
| fc_override = ET.SubElement(fc_tag, "fc_override", attrib=new_attrib) |
| |
| changed = True |
| return changed |
| |
| def upgrade_device_layout(arch): |
| """ |
| Upgrades the legacy <gridlocation> specifications (on each pb_type) to the new format |
| placed under the <layout> tag. |
| """ |
| changed = False |
| |
| #Get the layout tag |
| layout = arch.find("./layout") |
| |
| #Find all the top level pb_types |
| top_pb_types = arch.findall("./complexblocklist/pb_type") |
| |
| type_to_grid_specs = OrderedDict() |
| for top_pb_type in top_pb_types: |
| type_name = top_pb_type.attrib['name'] |
| |
| assert type_name not in type_to_grid_specs |
| type_to_grid_specs[type_name] = [] |
| |
| gridlocations = top_pb_type.find("gridlocations") |
| if gridlocations == None: |
| continue |
| |
| for child in gridlocations: |
| if child.tag is ET.Comment: |
| continue |
| assert child.tag == "loc" |
| type_to_grid_specs[type_name].append(child) |
| |
| #Remove the legacy gridlocations from the <pb_type> |
| top_pb_type.remove(gridlocations) |
| changed = True |
| |
| if not changed: |
| #No legacy specs to upgrade |
| return changed |
| |
| device_auto = None |
| if 'auto' in layout.attrib: |
| aspect_ratio = layout.attrib['auto'] |
| |
| del layout.attrib['auto'] |
| change = True |
| |
| device_auto = ET.SubElement(layout, 'auto_layout') |
| device_auto.attrib['aspect_ratio'] = str(aspect_ratio) |
| elif 'width' in layout.attrib and 'height' in layout.attrib: |
| width = layout.attrib['width'] |
| height = layout.attrib['height'] |
| |
| del layout.attrib['width'] |
| del layout.attrib['height'] |
| changed = True |
| |
| device_auto = ET.SubElement(layout, 'fixed_layout') |
| device_auto.attrib['name'] = "unnamed_device" |
| device_auto.attrib['width'] = width |
| device_auto.attrib['height'] = height |
| else: |
| assert False, "Unrecognized <layout> specification" |
| |
| if 0: |
| for type, locs in type_to_grid_specs.iteritems(): |
| print "Type:", type |
| for loc in locs: |
| print "\t", loc.tag, loc.attrib |
| |
| have_perimeter = False |
| |
| for type_name, locs in type_to_grid_specs.iteritems(): |
| for loc in locs: |
| if loc.attrib['type'] == "perimeter": |
| have_perimeter = True |
| |
| |
| if changed: |
| layout.text = "\n" + INDENT |
| device_auto.text = "\n" + 2*INDENT |
| device_auto.tail = "\n" |
| |
| |
| for type_name, locs in type_to_grid_specs.iteritems(): |
| for loc in locs: |
| assert loc.tag == "loc" |
| |
| loc_type = loc.attrib['type'] |
| |
| #Note that we scale the priority by a factor of 10 to allow us to 'tweak' |
| #the priorities without causing conflicts with user defined priorities |
| priority = 10*int(loc.attrib['priority']) |
| |
| if loc_type == "col": |
| start = loc.attrib['start'] |
| |
| repeat = None |
| if 'repeat' in loc.attrib: |
| repeat = loc.attrib['repeat'] |
| |
| comment_str = "Column of '{}' with 'EMPTY' blocks wherever a '{}' does not fit.".format(type_name, type_name) |
| |
| if have_perimeter: |
| comment_str += " Vertical offset by 1 for perimeter." |
| |
| comment = ET.Comment(comment_str) |
| device_auto.append(comment) |
| comment.tail = "\n" + 2*INDENT |
| |
| col_spec = ET.SubElement(device_auto, 'col') |
| |
| col_spec.attrib['type'] = type_name |
| col_spec.attrib['startx'] = start |
| if have_perimeter: |
| col_spec.attrib['starty'] = "1" |
| if repeat: |
| col_spec.attrib['repeatx'] = repeat |
| col_spec.attrib['priority'] = str(priority) |
| col_spec.tail = "\n" + 2*INDENT |
| |
| #Classic VPR fills blank spaces (e.g. where a height > 1 block won't fit) with "EMPTY" |
| #instead of with the underlying type. To replicate that we create a col spec with the same |
| #location information, but of type 'EMPTY' and with slightly lower priority than the real type. |
| |
| col_empty_spec = ET.SubElement(device_auto, 'col') |
| col_empty_spec.attrib['type'] = "EMPTY" |
| col_empty_spec.attrib['startx'] = start |
| if repeat: |
| col_empty_spec.attrib['repeatx'] = repeat |
| if have_perimeter: |
| col_empty_spec.attrib['starty'] = "1" |
| col_empty_spec.attrib['priority'] = str(priority - 1) #-1 so it won't override the 'real' col |
| col_empty_spec.tail = "\n" + 2*INDENT |
| |
| elif loc_type == "rel": |
| pos = loc.attrib['pos'] |
| |
| div_factor = 1. / float(pos) |
| |
| int_div_factor = int(div_factor) |
| |
| startx = "(W - 1) / {}".format(div_factor) |
| |
| if float(int_div_factor) != div_factor: |
| print "Warning: Relative position factor conversion is not exact. Original pos factor: {}. New startx expression: {}".format(pos, startx) |
| |
| comment_str = "Column of '{}' with 'EMPTY' blocks wherever a '{}' does not fit.".format(type_name, type_name) |
| |
| if have_perimeter: |
| comment_str += " Vertical offset by 1 for perimeter." |
| |
| comment = ET.Comment(comment_str) |
| device_auto.append(comment) |
| comment.tail = "\n" + 2*INDENT |
| |
| col_spec = ET.SubElement(device_auto, 'col') |
| col_spec.attrib['type'] = type_name |
| col_spec.attrib['startx'] = startx |
| if have_perimeter: |
| col_spec.attrib['starty'] = "1" |
| col_spec.attrib['priority'] = str(priority) |
| col_spec.tail = "\n" + 2*INDENT |
| |
| #Classic VPR fills blank spaces (e.g. where a height > 1 block won't fit) with "EMPTY" |
| #instead of with the underlying type. To replicate that we create a col spec with the same |
| #location information, but of type 'EMPTY' and with slightly lower priority than the real type. |
| col_empty_spec = ET.SubElement(device_auto, 'col') |
| col_empty_spec.attrib['type'] = "EMPTY" |
| col_empty_spec.attrib['startx'] = startx |
| if have_perimeter: |
| col_empty_spec.attrib['starty'] = "1" |
| col_empty_spec.attrib['priority'] = str(priority - 1) #-1 so it won't override the 'real' col |
| col_empty_spec.tail = "\n" + 2*INDENT |
| |
| elif loc_type == "fill": |
| |
| comment = ET.Comment("Fill with '{}'".format(type_name)) |
| device_auto.append(comment) |
| comment.tail = "\n" + 2*INDENT |
| |
| fill_spec = ET.SubElement(device_auto, 'fill') |
| fill_spec.attrib['type'] = type_name |
| fill_spec.attrib['priority'] = str(priority) |
| fill_spec.tail = "\n" + 2*INDENT |
| |
| elif loc_type == "perimeter": |
| #The classic VPR perimeter specification did not include the corners (while the new version does) |
| # As a result we specify a full perimeter (including corners), and then apply an EMPTY type override |
| # at the corners |
| |
| comment = ET.Comment("Perimeter of '{}' blocks with 'EMPTY' blocks at corners".format(type_name)) |
| device_auto.append(comment) |
| comment.tail = "\n" + 2*INDENT |
| |
| perim_spec = ET.SubElement(device_auto, 'perimeter') |
| perim_spec.attrib['type'] = type_name |
| perim_spec.attrib['priority'] = str(priority) |
| perim_spec.tail = "\n" + 2*INDENT |
| |
| corners_spec = ET.SubElement(device_auto, 'corners') |
| corners_spec.attrib['type'] = "EMPTY" |
| corners_spec.attrib['priority'] = str(priority + 1) #+1 to ensure overrides |
| corners_spec.tail = "\n" + 2*INDENT |
| |
| else: |
| assert False, "Unrecognzied <loc> type tag {}".format(loc_type) |
| |
| return changed |
| |
| def remove_io_chan_distr(arch): |
| """ |
| Removes the legacy '<io>' channel width distribution tags |
| """ |
| device = arch.find("./device") |
| chan_width_distr = device.find("./chan_width_distr") |
| |
| io = chan_width_distr.find("./io") |
| if io != None: |
| width = float(io.attrib['width']) |
| |
| if width != 1.: |
| print "Found non-unity io width {}. This is no longer supported by VPR and must be manually correct. Exiting...".format(width) |
| sys.exit(1) |
| else: |
| assert width == 1. |
| chan_width_distr.remove(io) |
| return True #Modified |
| |
| def upgrade_pinlocations(arch): |
| """ |
| Upgrades custom pin locations from the 'offset' to 'yoffset' attribute. |
| Since previously only width==1 blocks were supported, we only need to consider |
| the yoffset case |
| """ |
| modified = False |
| pinlocations_list = arch.findall(".//pb_type/pinlocations") |
| |
| for pinlocations in pinlocations_list: |
| pb_type = pinlocations.find("..") |
| |
| assert pb_type.tag == "pb_type" |
| |
| width = 1 |
| height = 1 |
| if 'width' in pb_type.attrib: |
| width = int(pb_type.attrib['width']) |
| if 'height' in pb_type.attrib: |
| height = int(pb_type.attrib['height']) |
| |
| if width == 1: |
| |
| if pinlocations.attrib['pattern'] == "custom": |
| |
| for loc in pinlocations: |
| if loc.tag is ET.Comment: |
| continue |
| |
| assert loc.tag == "loc" |
| |
| if 'offset' in loc.attrib: |
| offset = int(loc.attrib['offset']) |
| |
| assert offset < height |
| |
| #Remove the old attribute |
| del loc.attrib['offset'] |
| |
| #Add the new attribute |
| loc.attrib['yoffset'] = str(offset) |
| |
| modified = True |
| |
| else: |
| assert pinlocations.attrib['pattern'] == "spread" |
| |
| return modified |
| |
| def uniqify_interconnect_names(arch): |
| """ |
| Ensure all interconnect tags have unique names |
| """ |
| modified = False |
| |
| for interconnect_tag in arch.findall(".//interconnect"): |
| seen_names = set() |
| cnt = 0 |
| for child_tag in interconnect_tag: |
| if child_tag.tag is ET.Comment: |
| continue |
| |
| name = orig_name = child_tag.attrib['name'] |
| |
| if orig_name in seen_names: |
| #Generate a unique name |
| while name in seen_names: |
| name = orig_name + "_{}".format(cnt) |
| cnt += 1 |
| |
| assert name not in seen_names |
| child_tag.attrib['name'] = name |
| modified = True |
| |
| seen_names.add(name) |
| |
| return modified |
| |
| def upgrade_connection_block_input_switch(arch): |
| """ |
| Convert connection block input switch specification to use a switch named |
| in <switchlist> |
| """ |
| modified = False |
| |
| device_tag = arch.find("./device") |
| timing_tag = device_tag.find("./timing") |
| sizing_tag = device_tag.find("./sizing") |
| switchlist_tag = arch.find("./switchlist") |
| connection_block_tag = arch.find("./connection_block") |
| |
| assert sizing_tag is not None |
| |
| if timing_tag is not None: |
| assert sizing_tag is not None |
| assert 'ipin_mux_trans_size' in sizing_tag.attrib |
| assert 'C_ipin_cblock' in timing_tag.attrib |
| assert 'T_ipin_cblock' in timing_tag.attrib |
| assert not device_tag.find("./connection_block") is not None |
| assert switchlist_tag is not None |
| |
| R_minW_nmos = sizing_tag.attrib['R_minW_nmos'] |
| ipin_switch_mux_trans_size = sizing_tag.attrib['ipin_mux_trans_size'] |
| ipin_switch_cin = timing_tag.attrib['C_ipin_cblock'] |
| ipin_switch_tdel = timing_tag.attrib['T_ipin_cblock'] |
| |
| modified = True |
| |
| #Remove the old attributes |
| del sizing_tag.attrib['ipin_mux_trans_size'] |
| device_tag.remove(timing_tag) |
| |
| # |
| #Create the switch |
| # |
| |
| switch_name = "ipin_cblock" |
| |
| #Make sure the switch name doesn't already exist |
| for switch_tag in switchlist_tag.findall("./switch"): |
| assert switch_tag.attrib['name'] != switch_name |
| |
| #Comment the switch |
| comment = ET.Comment("switch {} resistance set to yeild for 4x minimum drive strength buffer".format(switch_name)) |
| comment.tail = "\n" + 2*INDENT |
| switchlist_tag.append(comment) |
| #Create the switch |
| switch_tag = ET.SubElement(switchlist_tag, "switch") |
| switch_tag.attrib['type'] = 'mux' |
| switch_tag.attrib['name'] = switch_name |
| switch_tag.attrib['R'] = str(float(R_minW_nmos) / 4.) |
| switch_tag.attrib['Cout'] = "0." |
| switch_tag.attrib['Cin'] = ipin_switch_cin |
| switch_tag.attrib['Tdel'] = ipin_switch_tdel |
| switch_tag.attrib['mux_trans_size'] = ipin_switch_mux_trans_size |
| switch_tag.attrib['buf_size'] = "auto" |
| switch_tag.tail = "\n" + 2*INDENT |
| |
| #Create the connection_block tag |
| connection_block_tag = ET.SubElement(device_tag, "connection_block") |
| connection_block_tag.attrib['input_switch_name'] = switch_name |
| connection_block_tag.tail = "\n" + 2*INDENT |
| |
| else: |
| assert 'ipin_switch_mux_trans_size' not in sizing_tag.attrib |
| |
| return modified |
| |
| def upgrade_switch_types(arch): |
| """ |
| Rename 'buffered' and 'pass_trans' <switch type=""/> to 'tristate' and 'pass_gate' |
| """ |
| modified = False |
| switchlist_tag = arch.find("./switchlist") |
| assert switchlist_tag is not None |
| |
| for switch_tag in switchlist_tag.findall("./switch"): |
| |
| switch_type = switch_tag.attrib['type'] |
| |
| if switch_type in ['buffered', 'pass_trans']: |
| if switch_type == 'buffered': |
| switch_type = "tristate" |
| else: |
| assert switch_type == 'pass_trans' |
| switch_type = "pass_gate" |
| |
| switch_tag.attrib['type'] = switch_type |
| modified = True |
| |
| return modified |
| |
| def rename_fc_attributes(arch): |
| """ |
| Converts <fc> attributes of the form default_% to % |
| """ |
| fc_tags = arch.findall(".//fc") |
| |
| changed = False |
| for fc_tag in fc_tags: |
| assert fc_tag.tag == "fc" |
| attr_to_replace = ["default_in_type", "default_in_val", "default_out_type", "default_out_val"] |
| attr_list = list(fc_tag.attrib.keys()) |
| for attr in attr_list: |
| if attr in attr_to_replace: |
| val = fc_tag.attrib[attr] |
| del fc_tag.attrib[attr] |
| fc_tag.attrib[attr.replace("default_", "")] = val |
| changed = True |
| return changed |
| |
| def remove_longline_sb_cb(arch): |
| """ |
| Drops <sb> and <cb> of any <segment> types with length="longline", |
| since we now assume longlines have full switch block/connection block |
| populations |
| """ |
| |
| longline_segment_tags = arch.findall(".//segment[@length='longline']") |
| |
| changed = False |
| for longline_segment_tag in longline_segment_tags: |
| assert longline_segment_tag.tag == 'segment' |
| assert longline_segment_tag.attrib['length'] == 'longline' |
| |
| child_sb_tags = longline_segment_tag.findall("./sb") |
| child_cb_tags = longline_segment_tag.findall("./cb") |
| |
| for cb_sb_tag in child_sb_tags + child_cb_tags: |
| assert cb_sb_tag.tag == 'cb' or cb_sb_tag.tag == 'sb' |
| print >>sys.stderr, "Warning: Removing invalid {} tag for longline segment".format(cb_sb_tag.tag) |
| longline_segment_tag.remove(cb_sb_tag) |
| changed = True |
| |
| return changed |
| |
| def upgrade_port_equivalence(arch): |
| """ |
| Upgrades port equivalence from "true|false" to "none|full|instance" |
| |
| Note that to be safely conservative we upgrade <output equivalent="true"> |
| to <output equivalent="instance">. Users should check whether their architecture |
| should actually be using <output equivalent="full">. |
| """ |
| |
| input_equivalent_tags = arch.findall(".//input[@equivalent]") |
| output_equivalent_tags = arch.findall(".//output[@equivalent]") |
| |
| changed = False |
| |
| for input_equivalent_tag in input_equivalent_tags: |
| assert input_equivalent_tag.tag == "input" |
| assert 'equivalent' in input_equivalent_tag.attrib |
| |
| #For inputs, 'true' is upgraded to full, and 'false' to 'none' |
| if input_equivalent_tag.attrib['equivalent'] == "true": |
| input_equivalent_tag.attrib['equivalent'] = "full" |
| changed = True |
| elif input_equivalent_tag.attrib['equivalent'] == "false": |
| input_equivalent_tag.attrib['equivalent'] = "none" |
| changed = True |
| else: |
| assert input_equivalent_tag.attrib['equivalent'] in ['none', 'full', 'instance'], "equivalence already upgraded" |
| |
| for output_equivalent_tag in output_equivalent_tags: |
| assert output_equivalent_tag.tag == "output" |
| assert 'equivalent' in output_equivalent_tag.attrib |
| |
| #For outputs, 'true' is upgraded to full, and 'false' to 'none' |
| if output_equivalent_tag.attrib['equivalent'] == "true": |
| parent_tag = output_equivalent_tag.find("..[@name]") |
| print >>sys.stderr, "Warning: Conservatively upgrading output port equivalence from 'true' to 'instance' on {}. The user should manually check whether this can be upgraded to 'full'".format(parent_tag.attrib['name']) |
| output_equivalent_tag.attrib['equivalent'] = "instance" #Conservative |
| changed = True |
| elif output_equivalent_tag.attrib['equivalent'] == "false": |
| output_equivalent_tag.attrib['equivalent'] = "none" |
| changed = True |
| else: |
| assert output_equivalent_tag.attrib['equivalent'] in ['none', 'full', 'instance'], "equivalence already upgraded" |
| |
| return changed |
| |
| def get_port_names(string): |
| ports = [] |
| |
| for elem in string.split(): |
| #Split off the prefix |
| port = elem.split('.')[-1] |
| if '[' in port: |
| port = port[:port.find('[')] |
| |
| ports.append(port) |
| |
| return ports |
| |
| def upgrade_complex_sb_num_conns(arch): |
| """ |
| Upgrades <wireconn> 'num_conns_type' attribute to 'num_conns" |
| """ |
| |
| wireconn_tags = arch.findall(".//wireconn[@num_conns_type]") |
| |
| changed = False |
| |
| for wireconn_tag in wireconn_tags: |
| assert wireconn_tag.tag == "wireconn" |
| assert 'num_conns_type' in wireconn_tag.attrib |
| |
| #For inputs, 'true' is upgraded to full, and 'false' to 'none' |
| num_conns_value = wireconn_tag.attrib['num_conns_type'] |
| |
| del wireconn_tag.attrib['num_conns_type'] |
| |
| if num_conns_value == 'min': |
| num_conns_value = 'min(from,to)' |
| elif num_conns_value == 'max': |
| num_conns_value = 'max(from,to)' |
| |
| wireconn_tag.attrib['num_conns'] = num_conns_value |
| |
| changed = True |
| |
| return changed |
| |
| def add_missing_comb_model_internal_timing_edges(arch): |
| """ |
| For the purposes of constant generator detection every model needs to include |
| a set of primtiive internal timing edges. |
| |
| If we find a combinational model definition which has no internal timing edges, |
| we add timing edges between all input and output ports. This is a pessimistic, |
| but safe assumption. |
| |
| Note that if we find any internal timing edges specified we assume the user has |
| specified them correctly and do not make any changes to that model |
| """ |
| changed = False |
| |
| model_tags = arch.findall("./models/model") |
| |
| for model_tag in model_tags: |
| |
| input_clock_tags = model_tag.findall("./input_ports/port[@is_clock='1']") |
| if len(input_clock_tags) > 0: |
| continue #Sequential primitive -- no change |
| |
| input_tags_with_timing_edges = model_tag.findall("./input_ports/port[@combinational_sink_ports]") |
| if len(input_tags_with_timing_edges) > 0: |
| continue #Already has internal edges specified -- no change |
| |
| #Collect the model port definitions |
| input_port_tags = model_tag.findall("./input_ports/port") |
| output_port_tags = model_tag.findall("./output_ports/port") |
| |
| #Collect output port names |
| output_port_names = [] |
| for output_port_tag in output_port_tags: |
| output_port_names.append(output_port_tag.attrib['name']) |
| |
| for input_port_tag in input_port_tags: |
| |
| assert 'combinational_sink_ports' not in input_port_tag |
| |
| input_port_tag.attrib['combinational_sink_ports'] = " ".join(output_port_names) |
| |
| print >>sys.stderr, "Warning: Conservatively upgrading combinational sink dependencies for input port '{}' of model '{}' to '{}'. The user should manually check whether a reduced set of sink dependencies can be safely specified.".format(input_port_tag.attrib['name'], model_tag.attrib['name'], input_port_tag.attrib['combinational_sink_ports']) |
| |
| changed = True |
| |
| return changed |
| |
| def add_tile_tags(arch): |
| """ |
| This script is intended to modify the architecture description file to be compliant with |
| the new format. |
| |
| It 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'] |
| |
| 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: |
| 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) |
| |
| 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 |
| |
| |
| if __name__ == "__main__": |
| main() |