Flatten A Netlist

Remove hierarchy from a netlist. The original hierarchy and new hierarchy (after flattening) will be printed.

Need to move cells from a0 that references a
Need to move cells from b0 that references b
Need to move cells from c0 that references c
Finished moving cells from c0
Need to move cells from c1 that references c
Finished moving cells from c1
Need to move cells from c2 that references c
Finished moving cells from c2
Finished moving cells from b0
Need to move cells from b1 that references b
Finished moving cells from b1
Need to move cells from b2 that references b
Finished moving cells from b2
Finished moving cells from a0
Need to move cells from a1 that references a
Finished moving cells from a1
Need to move cells from a2 that references a
Finished moving cells from a2

ORIGINAL HIERARCHY
 top
    a0
       b0
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       b1
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       b2
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       zeta_INST_0
    a1
       b0
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       b1
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       b2
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       zeta_INST_0
    a2
       b0
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       b1
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       b2
          c0
             beta_INST_0
          c1
             beta_INST_0
          c2
             beta_INST_0
          delta_INST_0
       zeta_INST_0
    eta_IBUF[0]_inst
    eta_IBUF[10]_inst
    eta_IBUF[11]_inst
    eta_IBUF[12]_inst
    eta_IBUF[13]_inst
    eta_IBUF[14]_inst
    eta_IBUF[15]_inst
    eta_IBUF[16]_inst
    eta_IBUF[17]_inst
    eta_IBUF[18]_inst
    eta_IBUF[19]_inst
    eta_IBUF[1]_inst
    eta_IBUF[20]_inst
    eta_IBUF[21]_inst
    eta_IBUF[22]_inst
    eta_IBUF[23]_inst
    eta_IBUF[24]_inst
    eta_IBUF[25]_inst
    eta_IBUF[26]_inst
    eta_IBUF[27]_inst
    eta_IBUF[28]_inst
    eta_IBUF[29]_inst
    eta_IBUF[2]_inst
    eta_IBUF[30]_inst
    eta_IBUF[31]_inst
    eta_IBUF[32]_inst
    eta_IBUF[33]_inst
    eta_IBUF[34]_inst
    eta_IBUF[35]_inst
    eta_IBUF[36]_inst
    eta_IBUF[37]_inst
    eta_IBUF[38]_inst
    eta_IBUF[39]_inst
    eta_IBUF[3]_inst
    eta_IBUF[40]_inst
    eta_IBUF[41]_inst
    eta_IBUF[42]_inst
    eta_IBUF[43]_inst
    eta_IBUF[44]_inst
    eta_IBUF[45]_inst
    eta_IBUF[46]_inst
    eta_IBUF[47]_inst
    eta_IBUF[48]_inst
    eta_IBUF[49]_inst
    eta_IBUF[4]_inst
    eta_IBUF[50]_inst
    eta_IBUF[51]_inst
    eta_IBUF[52]_inst
    eta_IBUF[53]_inst
    eta_IBUF[5]_inst
    eta_IBUF[6]_inst
    eta_IBUF[7]_inst
    eta_IBUF[8]_inst
    eta_IBUF[9]_inst
    theta_OBUF_inst
    theta_OBUF_inst_i_1

HIERARCHY AFTER FLATTENING
 top
    eta_IBUF[0]_inst
    eta_IBUF[10]_inst
    eta_IBUF[11]_inst
    eta_IBUF[12]_inst
    eta_IBUF[13]_inst
    eta_IBUF[14]_inst
    eta_IBUF[15]_inst
    eta_IBUF[16]_inst
    eta_IBUF[17]_inst
    eta_IBUF[18]_inst
    eta_IBUF[19]_inst
    eta_IBUF[1]_inst
    eta_IBUF[20]_inst
    eta_IBUF[21]_inst
    eta_IBUF[22]_inst
    eta_IBUF[23]_inst
    eta_IBUF[24]_inst
    eta_IBUF[25]_inst
    eta_IBUF[26]_inst
    eta_IBUF[27]_inst
    eta_IBUF[28]_inst
    eta_IBUF[29]_inst
    eta_IBUF[2]_inst
    eta_IBUF[30]_inst
    eta_IBUF[31]_inst
    eta_IBUF[32]_inst
    eta_IBUF[33]_inst
    eta_IBUF[34]_inst
    eta_IBUF[35]_inst
    eta_IBUF[36]_inst
    eta_IBUF[37]_inst
    eta_IBUF[38]_inst
    eta_IBUF[39]_inst
    eta_IBUF[3]_inst
    eta_IBUF[40]_inst
    eta_IBUF[41]_inst
    eta_IBUF[42]_inst
    eta_IBUF[43]_inst
    eta_IBUF[44]_inst
    eta_IBUF[45]_inst
    eta_IBUF[46]_inst
    eta_IBUF[47]_inst
    eta_IBUF[48]_inst
    eta_IBUF[49]_inst
    eta_IBUF[4]_inst
    eta_IBUF[50]_inst
    eta_IBUF[51]_inst
    eta_IBUF[52]_inst
    eta_IBUF[53]_inst
    eta_IBUF[5]_inst
    eta_IBUF[6]_inst
    eta_IBUF[7]_inst
    eta_IBUF[8]_inst
    eta_IBUF[9]_inst
    theta_OBUF_inst
    theta_OBUF_inst_i_1
    a0/b0/c0/beta_INST_0
    a0/b0/c1/beta_INST_0
    a0/b0/c2/beta_INST_0
    a0/b0/delta_INST_0
    a0/b1/delta_INST_0
    a0/b1/c0/beta_INST_0
    a0/b1/c1/beta_INST_0
    a0/b1/c2/beta_INST_0
    a0/b2/delta_INST_0
    a0/b2/c0/beta_INST_0
    a0/b2/c1/beta_INST_0
    a0/b2/c2/beta_INST_0
    a0/zeta_INST_0
    a1/zeta_INST_0
    a1/b0/c0/beta_INST_0
    a1/b0/c1/beta_INST_0
    a1/b0/c2/beta_INST_0
    a1/b0/delta_INST_0
    a1/b1/delta_INST_0
    a1/b1/c0/beta_INST_0
    a1/b1/c1/beta_INST_0
    a1/b1/c2/beta_INST_0
    a1/b2/delta_INST_0
    a1/b2/c0/beta_INST_0
    a1/b2/c1/beta_INST_0
    a1/b2/c2/beta_INST_0
    a2/zeta_INST_0
    a2/b0/c0/beta_INST_0
    a2/b0/c1/beta_INST_0
    a2/b0/c2/beta_INST_0
    a2/b0/delta_INST_0
    a2/b1/delta_INST_0
    a2/b1/c0/beta_INST_0
    a2/b1/c1/beta_INST_0
    a2/b1/c2/beta_INST_0
    a2/b2/delta_INST_0
    a2/b2/c0/beta_INST_0
    a2/b2/c1/beta_INST_0
    a2/b2/c2/beta_INST_0

import tempfile
import spydrnet as sdn
from pathlib import Path

# Check if given instance is a black box
# instance: std.Instance to check
# Return True if instance is a black box
# (defined by no listed cables or child within child's reference), else return False
def is_black_box(instance):
    definition = instance.reference
    if len(definition.cables) != 0 or len(definition.children) != 0:
        return False
    return True


# Creates a copy of a instance
# parent_instance: The instance that contains the instance be copy (used to keep hierarchical naming)
# instance: The original instance to make the copy of
# new_instance: The instance that will be the copy
# Returns: None
def copy_instance(parent_instance, instance, new_instance):
    # Copy all the data from instance into new_instance
    for key, value in instance.data.items():
        new_instance[key] = value
    # Change new_instance EDIF.identifier to represent its hierarchical name
    new_instance['EDIF.identifier'] = parent_instance['EDIF.identifier'] + \
        '_' + new_instance['EDIF.identifier']
    # Determine if parent_instance and instance have .NAME in it data
    # Uses .NAME if available, else used EDIF.identifier to generate
    # .NAME for new_instance
    if '.NAME' in parent_instance:
        if '.NAME' in instance:
            new_instance['.NAME'] = parent_instance['.NAME'] + '/' \
                + instance['.NAME']
        else:
            new_instance['.NAME'] = parent_instance['.NAME'] + '/' \
                + instance['EDIF.identifier']
    else:
        if '.NAME' in instance:
            new_instance['.NAME'] = parent_instance['EDIF.identifier'] + '/' \
                + instance['.NAME']
        else:
            new_instance['.NAME'] = parent_instance['EDIF.identifier'] + '/' \
                + instance['EDIF.identifier']
    # Have new_instance reference the same definition as instance
    new_instance.reference = instance.reference


# Removes a newly created cable in favor of using the cable that connected to the outside of the instance
# new_cable: Cable that was connecting the new pins instance's parent
# old_cable: Cable that was connecting the old pins in instance
# instance: The instance that is being flatten
# Return: None
def use_outside_cable(new_cable, old_cable, instance):
    wire = None
    # Need to find the wire that connects to the outside of the port that old_cable does
    # Check each pin that old_cable is connected to to find the inside pin for the instance
    for pin in old_cable.wires[0].pins:
        if pin in instance.pins:
            # Get the wire that connects to the outer pin that corresponds to the found inner pin
            wire = instance.pins[pin].wire
            break
    # Loop through each wire in new_cable
    for new_wire in new_cable.wires:
        # Loop through is pin the wire connects to
        for pin in new_wire.pins:
            # Disconnect the new_wire from the pin and connect wire to that pin instead
            new_wire.disconnect_pin(pin)
            wire.connect_pin(pin)
    # Removes new_cable from the definition that holds it
    new_cable.definition.remove_cable(new_cable)


# Remove instances that have been flatten
# instance: std.Instance to be removed
# Returns None
def clean_up(instance):
    # Loop through each outside pin and disconnect it from its wire
    for pin in instance.pins.values():
        pin.wire.disconnect_pin(pin)
    # Remove instances from its parent definition
    instance.parent.remove_child(instance)


# Recursively flatten a given definition
# definition: std.Definition to flatten
# top_definition: Bool saying the given definition is the top definition of the IR
# Returns: list of created instances
def flatten_definition(definition, top_definition=False):
    # Create a copy of the list of children
    children = definition.children.copy()
    created = []
    # Loop through each pre-existing child of the definition
    for child in children:
        leaf_grandchildren = []
        child_reference = child.reference
        # Create a copy of the list of the children for the current child reference
        grandchildren = child_reference.children.copy()
        map = {}
        # Check if progress information should be printed
        if top_definition and not is_black_box(child):
            print("Need to move cells from", child['EDIF.identifier'],
                  "that references", child.reference['EDIF.identifier'])
        # Loop through each grandchild of definition
        for grandchild in grandchildren:
            if not is_black_box(grandchild):
                print("Need to move cells from", grandchild['EDIF.identifier'],
                      "that references", grandchild.reference['EDIF.identifier'])
                # Flatten any children if they contain non_leaf grandchildren
                # Keep track of grandchildren that are leaf nodes
                leaf_grandchildren.extend(flatten_definition(child.reference))
                print("Finished moving cells from",
                      grandchild['EDIF.identifier'])
            else:
                # Keep track of grandchildren that are leaf nodes
                leaf_grandchildren.append(grandchild)
        # Loop though each grandchild that is also a leaf including newly created grandchildren
        for grandchild in leaf_grandchildren:
            # Create a new child inside of definition
            new_instance = definition.create_child()
            # Copy data from the grandchild into the new instance
            copy_instance(child, grandchild, new_instance)
            # Create a key, value relationship between grandchild and new_instance
            map[grandchild] = new_instance
            # Add the new_instances to the list of created instances
            created.append(new_instance)
        # Create a list of the original cables that the child has
        cables = child_reference.cables.copy()
        for cable in cables:
            name_cable = True
            new_cable = definition.create_cable()
            for wire in cable.wires:
                new_wire = new_cable.create_wire()
                for pin in wire.pins:
                    # If the pin is connected to a port of the child, we should use an outside cable instead
                    if isinstance(pin, sdn.InnerPin):
                        name_cable = False
                        continue
                    # Connect the new wire to pins on new instances that correspond pins on the original instances
                    new_wire.connect_pin(map[pin.instance].pins[pin.inner_pin])
            # Check if we should name the cable or should use the outside cable
            if name_cable:
                new_cable['EDIF.identifier'] = child['EDIF.identifier'] + \
                    '_' + cable['EDIF.identifier']
                new_cable.name = new_cable['EDIF.identifier']
            else:
                use_outside_cable(new_cable, cable, child)
        # Remove any original children that is not a leaf
        if not is_black_box(child):
            clean_up(child)
        # Check if progress information should be printed
        if top_definition and not is_black_box(child):
            print("Finished moving cells from", child['EDIF.identifier'])
    return created

#print the hierarchy of a netlist
def hierarchy(current_instance,indentation=""):
    print(indentation,current_instance.name)
    for child in current_instance.reference.children:
        hierarchy(child,indentation+"   ")


example_name1 = "unique_challenge"
example_name2 = "three_layer_hierarchy"
example_name3 = "unique_different_modules"
example_name = example_name1
ir = sdn.load_example_netlist_by_name(example_name)
ir_orig = sdn.load_example_netlist_by_name(example_name) #store the original netlist for display later
top_def = ir.top_instance.reference
flatten_definition(top_def, top_definition=True)

with tempfile.TemporaryDirectory() as td:
    file_name = example_name + '_flat.edf'
    sdn.compose(ir, Path(td, file_name))

# sdn.composers.compose("test.edf", ir)
print()

print("ORIGINAL HIERARCHY")
hierarchy(ir_orig.top_instance)
print("\nHIERARCHY AFTER FLATTENING")
hierarchy(ir.top_instance)

Total running time of the script: (0 minutes 0.049 seconds)

Gallery generated by Sphinx-Gallery