byuccl / spydrnet

A flexible framework for analyzing and transforming FPGA netlists. Official repository.
https://byuccl.github.io/spydrnet
BSD 3-Clause "New" or "Revised" License
86 stars 20 forks source link

Flatten cannot handle multiple instances of one definition in another definition. #220

Open zhuguiyuan opened 4 months ago

zhuguiyuan commented 4 months ago

Here is the hierarchy structure of instances, and in the parentheses is the corresponding definition.

top |- inst0 (Foo) `- inst1 (Foo)

Foo `- inv (INV)

The flatten function cannot handle this case, because it remove cables and pins in Foo when meet top/inst0. Then when meet inst1, it cannot reconnect the wires again for top/inst1

Here is an example.

Generate EDIF:

import spydrnet as sdn

netlist = sdn.Netlist(name='netlist')

prim_library = netlist.create_library(name="hdi_primitives")

INV_def = prim_library.create_definition(name="INV")
INV_port_output = INV_def.create_port(name="O", direction=sdn.OUT)
INV_port_input = INV_def.create_port(name="I", direction=sdn.IN)
INV_pin_output = INV_port_output.create_pin()
INV_pin_input = INV_port_input.create_pin()

work_library = netlist.create_library(name="work")

Foo_def = work_library.create_definition(name='Foo')
Foo_port_output = Foo_def.create_port(name="O", direction=sdn.OUT)
Foo_port_input = Foo_def.create_port(name="I", direction=sdn.IN)
Foo_pin_output = Foo_port_output.create_pin()
Foo_pin_input = Foo_port_input.create_pin()
INV_inst = Foo_def.create_child(name='inv', reference=INV_def)

Foo_cable_0 = Foo_def.create_cable(name='cable_in_Foo_0')
Foo_wire_0 = Foo_cable_0.create_wire()
Foo_cable_1 = Foo_def.create_cable(name='cable_in_Foo_1')
Foo_wire_1 = Foo_cable_1.create_wire()

Foo_wire_0.connect_pin(INV_inst.pins[INV_pin_input])
Foo_wire_0.connect_pin(Foo_pin_input)
Foo_wire_1.connect_pin(INV_inst.pins[INV_pin_output])
Foo_wire_1.connect_pin(Foo_pin_output)

top_def = work_library.create_definition(name='top')
top_port_output_0 = top_def.create_port(name="O0", direction=sdn.OUT)
top_port_output_1 = top_def.create_port(name="O1", direction=sdn.OUT)
top_port_input_0 = top_def.create_port(name="I0", direction=sdn.IN)
top_port_input_1 = top_def.create_port(name="I1", direction=sdn.IN)

ports = [top_port_output_0, top_port_output_1,
         top_port_input_0, top_port_input_1]
inner_pins = [port.create_pin() for port in ports]

foo_inst_0 = top_def.create_child(name='foo0', reference=Foo_def)
foo_inst_1 = top_def.create_child(name='foo1', reference=Foo_def)
outter_pins = [
    foo_inst_0.pins[Foo_pin_output], foo_inst_1.pins[Foo_pin_output],
    foo_inst_0.pins[Foo_pin_input], foo_inst_1.pins[Foo_pin_input]
]

cables, wires = [], []
for i in range(4):
    cables.append(top_def.create_cable(name=f'cable_in_top_{i}'))
for cable in cables:
    wires.append(cable.create_wire())
for wire, inner_pin, outter_pin in zip(wires, inner_pins, outter_pins):
    wire.connect_pin(inner_pin)
    wire.connect_pin(outter_pin)

netlist.set_top_instance(top_def, instance_name=top_def.name)

sdn.compose(netlist, 'demo.edif')

EDIF:

(edif netlist
  (edifversion 2 0 0)
  (edifLevel 0)
  (keywordmap (keywordlevel 0))
  (status
    (written
      (timeStamp 2024 02 14 17 47 02)

      (comment "Built by 'BYU spydrnet tool'")
      )
    )
  (Library hdi_primitives
    (edifLevel 0)
    (technology (numberDefinition ))
    (Cell INV (celltype GENERIC)
      (view netlist (viewtype NETLIST)
        (interface
          (port O(direction OUTPUT))
          (port I(direction INPUT))
          )
        )
      )
    )
  (Library work
    (edifLevel 0)
    (technology (numberDefinition ))
    (Cell Foo (celltype GENERIC)
      (view netlist (viewtype NETLIST)
        (interface
          (port O(direction OUTPUT))
          (port I(direction INPUT))
          )
        (contents
          (instance inv (viewref netlist (cellref INV(libraryref hdi_primitives)))
            )(net cable_in_Foo_0 (joined
              (portref I (instanceref inv))
              (portref I)

              )
            )(net cable_in_Foo_1 (joined
              (portref O (instanceref inv))
              (portref O)

              )
            ))
        )
      )
    (Cell top (celltype GENERIC)
      (view netlist (viewtype NETLIST)
        (interface
          (port O0(direction OUTPUT))
          (port O1(direction OUTPUT))
          (port I0(direction INPUT))
          (port I1(direction INPUT))
          )
        (contents
          (instance foo0 (viewref netlist (cellref Foo(libraryref work)))
            )(instance foo1 (viewref netlist (cellref Foo(libraryref work)))
            )(net cable_in_top_0 (joined
              (portref O0)
              (portref O (instanceref foo0))

              )
            )(net cable_in_top_1 (joined
              (portref O1)
              (portref O (instanceref foo1))

              )
            )(net cable_in_top_2 (joined
              (portref I0)
              (portref I (instanceref foo0))

              )
            )(net cable_in_top_3 (joined
              (portref I1)
              (portref I (instanceref foo1))

              )
            ))
        )
      )
    )
  (design top
    (cellref top(libraryref work))
    )
  )

Flatten Netlist:

//Generated from netlist by SpyDrNet
//netlist name: netlist
module top
(
    .O0({cable_in_top_0}),
    .O1({cable_in_top_1}),
    .I0({cable_in_top_2}),
    .I1({cable_in_top_3})
);

    output cable_in_top_0;
    output cable_in_top_1;
    input cable_in_top_2;
    input cable_in_top_3;

    wire foo0/cable_in_Foo_1;
    wire foo0/cable_in_Foo_0;
    wire cable_in_top_3;
    wire cable_in_top_2;
    wire cable_in_top_1;
    wire cable_in_top_0;

    INV foo1/foo0/inv              <====  ERROR HERE
    (
        .O(cable_in_top_0),
        .I(cable_in_top_2)
    );
endmodule

`celldefine
module INV
(
    O,
    I
);

    output O;
    input I;

endmodule
`endcelldefine

module Foo
(
    O,
    I
);

    output O;
    input I;

endmodule
jacobdbrown4 commented 4 months ago

Thanks for posting this. This definitely needs to be fixed.

A quick fix for now should could be to uniquify the netlist before flattening.

from spydrnet.uniquify import uniquify

uniquify(netlist)
flatten(netlist)
sdn.compose(netlist, "demo_flat_uniquify.v")

The result is

//Generated from netlist by SpyDrNet
//netlist name: netlist
module top
(
    .O0({cable_in_top_0}),
    .O1({cable_in_top_1}),
    .I0({cable_in_top_2}),
    .I1({cable_in_top_3})
);

    output cable_in_top_0;
    output cable_in_top_1;
    input cable_in_top_2;
    input cable_in_top_3;

    wire foo1/cable_in_Foo_1;
    wire foo1/cable_in_Foo_0;
    wire foo0/cable_in_Foo_1;
    wire foo0/cable_in_Foo_0;
    wire cable_in_top_3;
    wire cable_in_top_2;
    wire cable_in_top_1;
    wire cable_in_top_0;

    INV foo0/inv
    (
        .O(cable_in_top_0),
        .I(cable_in_top_2)
    );
    INV foo1/inv
    (
        .O(cable_in_top_1),
        .I(cable_in_top_3)
    );
endmodule
jacobdbrown4 commented 4 months ago

Looking at it a little closer, it seems that you must run uniquify before flattening. So I don't think this is a bug. The documentation should be improved to indicate the uniquify requirement.