Netlist

What is a Netlist view

In IPKISS almost all circuits are hierarchical. This implies that circuits (that are PCells) are themselves composed off instances of other Child PCells. It is the goal of the Netlist view to define the interconnectivity between those child cells. Here interconnectivity is to be interpreted broadly, as the interconnected PCells exchange information through optical or electrical signals. Usually, but not necessarily, this implies that the child cells will be connected with waveguides or electrical wires. The main advantage of defining the interconnectivity between hierarchical cells in a separate view, is that this information can be used in other views, such a the Layout view when connecting ports or in the CircuitModelView when doing circuit simulations. We thereby avoid having to redefine the interconnectivity between the childcells (views) in every view of the parent cell.

A Netlist is composed of 3 things:

  • instances: These are instances of the Netlist views of Child Cells

  • terms: These are the connections through which the a netlist connects to the outside world

  • links: These are links between terms.

Netlist

Netlist of a hierarchical cells.

In IPKISS you’ll define the netlist with its terms, instances and nets within the _generate_netlist method of a NetlistView. This means you’ll always have to start from the following skeleton:

class MyPCell(i3.PCell):

    class Netlist(i3.NetlistView):

        def _generate_netlist(self, nl):
            # Here you will specify which terms, nets and instances
            # build up your netlist
            return nl

Netlist terms

Defining terms

Netlist terms connect are the terminals through which a PCell connects with another PCell. They are added to the Netlist view within the predefined method _generate_netlist. A term can be created using i3.OpticalTerm.

class Child(i3.PCell):

    class Netlist(i3.NetlistView):

        def _generate_netlist(self, nl):
            nl += i3.OpticalTerm(name="in")
            nl += i3.OpticalTerm(name="out")
            # or add an electrical term:
            nl += i3.ElectricalTerm(name="electricalterm")
            return nl

A term has two important properties:

  • name: This is the name that can be used to get the term from a term list. my_netlist.terms["in"] will return the term named in if it exist in the list.

  • domain: This is the domain in which the connectivity with other components will happen.

Netlist instances

The netlists of Child cells are added as netlist instances to the netlist of the parent cell. The instances are added using the i3.Instance function.

class Parent(i3.PCell):

    child1 = i3.ChildCellProperty()
    child2 = i3.ChildCellProperty()

    class Netlist(i3.NetlistView):

        def _generate_netlist(self, nl):
            # Add the waveguide as an instance.
            nl += i3.Instance(reference=self.child1, name='child1')
            nl += i3.Instance(reference=self.child2, name='child2')
            return insts

i3.Instance takes two parameters:

  • reference: This is the reference to the netlist that is placed. Here we use self.child1 referring to the netlist view of child1 because of the use of ChilCellProperty The same is true for self.child2.

  • name: This is the name given to the netlist instance.

Generate a NetlistView from a LayoutView

Instead of manually specifying the netlist, it is possible to extract the netlist from the Layout view in an automated way, using i3.NetlistFromLayout.

Basic use

If you want to extract a netlist with terms, instances and nets from a layout with ports, instances and matching ports, simply use i3.NetlistFromLayout as shown in the following example. The extraction of optical links is done by matching port location, direction and waveguide. Make sure to expose the desired ports in your layout.

import ipkiss3.all as i3

class MyCell(i3.PCell):
    """ Example cell with 2 waveguides which are butt-to-butt connected"""
    class Layout(i3.LayoutView):
        def _generate_instances(self, insts):
            wg1 = i3.RoundedWaveguide(name="{}_wg1".format(self.name))
            wg1.Layout(shape=[(0.0, 0.0), (10.0, 0.0), (20.0, 20.0)], bend_radius=5.0)
            wg2 = i3.RoundedWaveguide(name="{}_wg2".format(self.name))
            wg2.Layout(shape=[(0.0, 0.0), (20.0, 0.0), (40.0, -20.0)], bend_radius=5.0)

            insts += i3.place_and_route(
                insts={'wg1': wg1,
                       'wg2': wg2},
                specs=[i3.Place('wg1', (0.0, 0.0)),
                       i3.Join('wg2:in', 'wg1:out')]
            )
            return insts

        def _generate_ports(self, ports):
            ports += i3.expose_ports(self.instances, {'wg1:in': 'in',
                                                      'wg2:out': 'out'})
            return ports

    class Netlist(i3.NetlistFromLayout):
        pass

mycell = MyCell()
layout = mycell.Layout()
layout.visualize(annotate=True)
netlist = mycell.Netlist()
print(netlist)

The code snippet above will print the following output to the console:

netlist:
--------
instances:
      - wg1 : <Single instance in netlist of PCELL_2_wg1>
      - wg2 : <Single instance in netlist of PCELL_2_wg2>

terms:
      - in
      - out

nets:
      - wg1:out-wg2:in: <OpticalLink wg1:out to wg2:in>
      - in-wg1:in: <OpticalLink in to wg1:in>
      - out-wg2:out: <OpticalLink out to wg2:out>

Generating terms only

If you only need to create terms which correspond to the ports in the layout view, avoiding the creation of instances and nets, you can use i3.extract_terms and override the netlist.

In this case, any instances and their ports will be ignored.

class MyCell(i3.PCell):
    class Layout(i3.LayoutView):
        def _generate_ports(self, ports):
            ports += i3.OpticalPort(name="out", position=(10.0 ,0.0), angle=0.0)
            ports += i3.ElectricalPort(name="dc", position=(0.0, 10.0))
            return ports

    class Netlist(i3.NetlistFromLayout):
        def _generate_netlist(self, netlist):
            netlist.terms = i3.extract_terms(self.layout_view)
            return netlist

Full hierarchical extraction

The netlist extraction works hierarchically. Simply use i3.NetlistFromLayout at the different levels.

Here is a basic hierarchical example in which we just draw some rectangles representing devices and optical ports:

import ipkiss3.all as i3


device_layer = i3.Layer(0)
wg_layer = i3.Layer(1)

waveguide_windows = [i3.PathTraceWindow(layer=wg_layer,
                                        start_offset=-0.3,
                                        end_offset=0.3)]
waveguide_template = i3.WindowWaveguideTemplate(name="wg_template")
waveguide_template.Layout(windows=waveguide_windows)


class MyCell(i3.PCell):
    """ Simple cell with an optical and an electrical port """
    length = i3.PositiveNumberProperty(default=20.0)
    width = i3.PositiveNumberProperty(default=5.0)
    class Layout(i3.LayoutView):
        def _generate_elements(self, elems):
            elems += i3.Rectangle(layer=device_layer,
                                  center=(0.0, 0.0),
                                  box_size=(self.length, self.width))
            elems += i3.Rectangle(layer=wg_layer,
                                  center=(0.5 * self.length - 0.25, 0.0),
                                  box_size=(0.5, 1.0))
            elems += i3.Rectangle(layer=wg_layer,
                                  center=(0.0, 0.5 * self.width - 0.25),
                                  box_size=(1.0, 0.5))
            return elems

        def _generate_ports(self, ports):
            ports += i3.OpticalPort(name="east", position=(0.5 * self.length ,0.0), angle=0.0, trace_template=waveguide_template)
            ports += i3.ElectricalPort(name="north", position=(0.0, 0.5 * self.width))
            return ports

    class Netlist(i3.NetlistFromLayout):
        pass


class Parent(i3.PCell):
    """ Cell with 3 instances of MyCell and automated netlist extraction """
    class Layout(i3.LayoutView):
        def _generate_instances(self, insts):
            # create 2 child cells
            cell1 = MyCell(length=10.0, name="{}_cell1".format(self.name))
            cell2 = MyCell(length=20.0, name="{}_cell2".format(self.name))
            # place 2 instances of cell1 and one of cell2
            insts += i3.SRef(name="child1", reference=cell1,
                             position=(0.0, 0.0))
            insts += i3.SRef(name="child2", reference=cell1,
                             position=(0.0, 5.0),
                             transformation=i3.VMirror())
            insts += i3.SRef(name="child3", reference=cell2,
                             position=(15.0, 0.0),
                             transformation=i3.Rotation(rotation=180.0))
            return insts

        def _generate_ports(self, ports):
            # expose the children's ports
            ports += i3.expose_ports(self.instances,
                                     {"child2:east": "out",
                                      "child3:north": "in"})
            return ports


    class Netlist(i3.NetlistFromLayout):
        pass


mycell = MyCell()
mycell_layout = mycell.Layout()
mycell_layout.visualize(annotate=True)

parent = Parent()
parent_layout = parent.Layout()
parent_layout.visualize(annotate=True)
parent_netlist = parent.Netlist()
print(parent_netlist)

The code snippet above will print the following output to the console:

netlist:
--------
instances:
        - child1 : <Single instance in netlist of PCELL_2_cell1>
        - child2 : <Single instance in netlist of PCELL_2_cell1>
        - child3 : <Single instance in netlist of PCELL_2_cell2>

terms:
        - out
        - in

nets:
        - child1:east-child3:east: <OpticalLink child1:east to child3:east>
        - child2:east-out: <OpticalLink out to child2:east>