| #!/usr/bin/env python3 | |
| import argparse | |
| import lxml.etree as ET | |
| from data_structs import SwitchboxPinType | |
| from data_import import import_data | |
| # ============================================================================= | |
| def fixup_pin_name(name): | |
| return name.replace("[", "").replace("]", "") | |
| def switchbox_to_dot(switchbox, stage_types=("STREET", "HIGHWAY")): | |
| dot = [] | |
| TYPE_TO_COLOR = { | |
| SwitchboxPinType.UNSPEC: "#C0C0C0", | |
| SwitchboxPinType.LOCAL: "#C0C0FF", | |
| SwitchboxPinType.HOP: "#FFFFC0", | |
| SwitchboxPinType.CONST: "#FFC0C0", | |
| SwitchboxPinType.GCLK: "#C0FFC0", | |
| } | |
| # Add header | |
| dot.append("digraph {} {{".format(switchbox.type)) | |
| dot.append(" graph [nodesep=\"1.0\", ranksep=\"20\"];") | |
| dot.append(" splines = \"false\";") | |
| dot.append(" rankdir = LR;") | |
| dot.append(" margin = 20;") | |
| dot.append(" node [shape=record, style=filled, fillcolor=white];") | |
| stage_ids_to_show = set( | |
| [s.id for s in switchbox.stages.values() if s.type in stage_types] | |
| ) | |
| # Top-level inputs | |
| dot.append(" subgraph cluster_inputs {") | |
| dot.append(" node [shape=ellipse, style=filled];") | |
| dot.append(" label=\"Inputs\";") | |
| for pin in switchbox.inputs.values(): | |
| stage_ids = set([loc.stage_id for loc in pin.locs]) | |
| if not len(stage_ids & stage_ids_to_show): | |
| continue | |
| color = TYPE_TO_COLOR.get(pin.type, "#C0C0C0") | |
| name = "input_{}".format(fixup_pin_name(pin.name)) | |
| dot.append( | |
| " {} [rank=0, label=\"{}\", fillcolor=\"{}\"];".format( | |
| name, pin.name, color | |
| ) | |
| ) | |
| dot.append(" }") | |
| # Top-level outputs | |
| dot.append(" subgraph cluster_outputs {") | |
| dot.append(" node [shape=ellipse, style=filled];") | |
| dot.append(" label=\"Outputs\";") | |
| rank = max(switchbox.stages.keys()) + 1 | |
| for pin in switchbox.outputs.values(): | |
| stage_ids = set([loc.stage_id for loc in pin.locs]) | |
| if not len(stage_ids & stage_ids_to_show): | |
| continue | |
| color = TYPE_TO_COLOR[pin.type] | |
| name = "output_{}".format(fixup_pin_name(pin.name)) | |
| dot.append( | |
| " {} [rank={}, label=\"{}\", fillcolor=\"{}\"];".format( | |
| name, rank, pin.name, color | |
| ) | |
| ) | |
| dot.append(" }") | |
| # Stages | |
| for stage in switchbox.stages.values(): | |
| if stage.type not in stage_types: | |
| continue | |
| rank = stage.id + 1 | |
| dot.append(" subgraph cluster_st{} {{".format(stage.id)) | |
| dot.append( | |
| " label=\"Stage #{} '{}'\";".format(stage.id, stage.type) | |
| ) | |
| dot.append(" bgcolor=\"#D0D0D0\"") | |
| # Switch | |
| for switch in stage.switches.values(): | |
| dot.append( | |
| " subgraph cluster_st{}_sw{} {{".format( | |
| stage.id, switch.id | |
| ) | |
| ) | |
| dot.append(" label=\"Switch #{}\";".format(switch.id)) | |
| dot.append(" bgcolor=\"#F0F0F0\"") | |
| # Mux | |
| for mux in switch.muxes.values(): | |
| inputs = sorted(mux.inputs.values(), key=lambda p: p.id) | |
| mux_l = "Mux #{}".format(mux.id) | |
| inp_l = "|".join( | |
| [ | |
| "<i{}> {}. {}".format(p.id, p.id, p.name) | |
| for p in inputs | |
| ] | |
| ) | |
| out_l = "<o{}> {}. {}".format( | |
| mux.output.id, mux.output.id, mux.output.name | |
| ) | |
| label = "{}|{{{{{}}}|{{{}}}}}".format(mux_l, inp_l, out_l) | |
| name = "st{}_sw{}_mx{}".format(stage.id, switch.id, mux.id) | |
| dot.append( | |
| " {} [rank=\"{}\", label=\"{}\"];".format( | |
| name, rank, label | |
| ) | |
| ) | |
| dot.append(" }") | |
| dot.append(" }") | |
| # Internal connections | |
| for conn in switchbox.connections: | |
| if switchbox.stages[conn.src.stage_id].type not in stage_types: | |
| continue | |
| if switchbox.stages[conn.dst.stage_id].type not in stage_types: | |
| continue | |
| src_node = "st{}_sw{}_mx{}".format( | |
| conn.src.stage_id, conn.src.switch_id, conn.src.mux_id | |
| ) | |
| src_port = "o{}".format(conn.src.pin_id) | |
| dst_node = "st{}_sw{}_mx{}".format( | |
| conn.dst.stage_id, conn.dst.switch_id, conn.dst.mux_id | |
| ) | |
| dst_port = "i{}".format(conn.dst.pin_id) | |
| dot.append( | |
| " {}:{} -> {}:{};".format(src_node, src_port, dst_node, dst_port) | |
| ) | |
| # Input pin connections | |
| for pin in switchbox.inputs.values(): | |
| src_node = "input_{}".format(fixup_pin_name(pin.name)) | |
| for loc in pin.locs: | |
| if switchbox.stages[loc.stage_id].type not in stage_types: | |
| continue | |
| dst_node = "st{}_sw{}_mx{}".format( | |
| loc.stage_id, loc.switch_id, loc.mux_id | |
| ) | |
| dst_port = "i{}".format(loc.pin_id) | |
| dot.append(" {} -> {}:{};".format(src_node, dst_node, dst_port)) | |
| # Output pin connections | |
| for pin in switchbox.outputs.values(): | |
| dst_node = "output_{}".format(fixup_pin_name(pin.name)) | |
| for loc in pin.locs: | |
| if switchbox.stages[loc.stage_id].type not in stage_types: | |
| continue | |
| src_node = "st{}_sw{}_mx{}".format( | |
| loc.stage_id, loc.switch_id, loc.mux_id | |
| ) | |
| src_port = "o{}".format(loc.pin_id) | |
| dot.append(" {}:{} -> {};".format(src_node, src_port, dst_node)) | |
| # Footer | |
| dot.append("}") | |
| return "\n".join(dot) | |
| # ============================================================================= | |
| def main(): | |
| # Parse arguments | |
| parser = argparse.ArgumentParser( | |
| description=__doc__, | |
| formatter_class=argparse.RawDescriptionHelpFormatter | |
| ) | |
| parser.add_argument("i", type=str, help="Quicklogic 'TechFile' file") | |
| parser.add_argument( | |
| "--stages", | |
| type=str, | |
| default="STREET", | |
| help="Comma-separated list of stage types to view (def. STREET)" | |
| ) | |
| args = parser.parse_args() | |
| # Read and parse the XML file | |
| xml_tree = ET.parse(args.i) | |
| xml_root = xml_tree.getroot() | |
| # Load data | |
| data = import_data(xml_root) | |
| switchbox_types = data["switchbox_types"] | |
| # Generate DOT files with switchbox visualizations | |
| for switchbox in switchbox_types.values(): | |
| fname = "sbox_{}.dot".format(switchbox.type) | |
| with open(fname, "w") as fp: | |
| fp.write(switchbox_to_dot(switchbox, args.stages.split(","))) | |
| # ============================================================================= | |
| if __name__ == "__main__": | |
| main() |