blob: 8da87b8624eb4dc95725b2b08af32f4848f1ee65 [file] [log] [blame] [edit]
#!/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()