Placement and Routing Reference

An important aspect of building a circuit layout is the placement and routing of cells. In IPKISS, this is done using i3.Circuit.

This placement and routing function works by choosing a set of instances, and defining specifications (placement, joining, alignment, how to connect, …) that describe how these instances should be placed and connected. The different specifications for placement and routing are listed below.

Functions and classes

The main class to use for building circuits is i3.Circuit. It is an easy-to-use class built around the core algorithm i3.place_and_route, which performs the placement and routing in the layout view. i3.Circuit also uses i3.NetlistFromLayout, so that the netlist is automatically extracted from the layout. This enables layout-accurate circuit simulations.

place_and_route

Function to place and route a series of instances with the help of placement specifications and connectors.

Circuit

A PCell which derives its layout, netlist and model from a set of specifications in order to create a circuit.

ConnectComponents

Parametric Cell for logically connecting multiple components.

Specifications

The placement specifications are the following:

Join

Join instances together.

Place

Specifies that an instance or its port should be placed on a given position and angle (angle is optional).

PlaceRelative

Specifies that an instance (inst1) or a port (inst1:port) should be placed relative to another instance (inst2) or a port (inst2:port) with a given offset (x, y) and an angle (optional).

AlignH

Specifies that instances or ports should be aligned horizontally.

AlignV

Specifies that instances or ports should be aligned vertically.

FlipH

Specifies that a horizontally mirrored version of the component must be placed.

FlipV

Specifies that a vertically mirrored version of the instance must be placed.

Note

Always be as specific as possible when providing the placement specifications. The placement engine will change the positions of instances that are specified in the specifications list.

For instance, if you have multiple specs without using Place, that tells the placement engine that the location of the full circuit (or at least that one instance) is not important. The instances will still be placed relative to each other as specified, but their position might change when you add more specs. By using Place, you anchor your circuit.

The routing specifications are the following:

Scalar connectors

Connector

Base class for connectors.

ConnectManhattan

A connector that uses i3.RouteManhattan to connect two ports and returns an instance of i3.RoundedWaveguide (optical ports) or i3.ElectricalWire (electrical ports).

ConnectManhattanTapered

A connector that uses i3.RouteManhattan to connect two ports and returns a waveguide with a different trace template in the straight sections.

ConnectBend

Connector for creating an as simple as possible bend between two optical ports based on the given rounding parameters.

ConnectElectrical

A connector that uses i3.RouteManhattan to connect two electrical ports.

Bundle connectors

ConnectManhattanBundle

Connects multiple ports together using a bundle of waveguides separated by a fixed distance.

Fanouts for Bundle connectors

SBendFanout

Create S-bend-like routes that fanout all the start ports to evenly spaced outputs.

ManhattanFanout

Create L-bend-like routes that fanout all the start ports to evenly spaced outputs.

Route control

Argument control points of connector should be a list. Elements of the control points list can be related to the following:

CP

Description of where a route should pass through.

H

Horizontal control point class.

V

Vertical control point class.

START

Symbolic object that represents a start value or position.

END

Symbolic object that represents an end value or position.

PREV

Symbolic object that represents a previous value or position.

VIA

Place a via and change trace template at a certain point along the route.

Control points

Control points i3.CP allow you to specify certain points through which the route should go.

CP

Description of where a route should pass through.

import si_fab.all as pdk
import ipkiss3.all as i3
import matplotlib.pyplot as plt

fc = pdk.FC_TE_1550()
circuit = i3.Circuit(
    insts={"fc_in": fc, "fc_out": fc},
    specs=[
        i3.Place("fc_in:out", position=(0, 0)),
        i3.Place("fc_out:out", position=(50, 50)),
        i3.FlipH("fc_out"),
        i3.ConnectManhattan(
            "fc_in:out",
            "fc_out:out",
            control_points=[i3.CP((10, 20), i3.NORTH), i3.CP((30, 30), i3.EAST)],
        ),
    ],
)
circuit.Layout().visualize(show=False)
plt.arrow(10, 20, 0, 2, width=1.0)
plt.arrow(30, 30, 2, 0, width=1.0)
plt.show()
../../../_images/placement_routing-1.png
import si_fab.all as pdk  # noqa
import ipkiss3.all as i3
from picazzo3.fibcoup.curved.cell import FiberCouplerCurvedGrating
import matplotlib.pyplot as plt

gc = FiberCouplerCurvedGrating()
circuit = i3.Circuit(
    insts={"gc_in": gc, "gc_out": gc},
    specs=[
        i3.Place("gc_in", (0, 0)),
        i3.Place("gc_out", (100, 100), angle=180),
        i3.ConnectManhattan(
            "gc_in:out",
            "gc_out:out",
            control_points=[
                i3.CP((90, 10), i3.NORTH),
                i3.CP((50, 40), i3.WEST),
                i3.CP((0, None)),
            ],
        ),
    ],
)
circuit.get_default_view(i3.LayoutView).visualize(show=False)
plt.arrow(90, 10, 0, 10, width=0.5)
plt.arrow(50, 40, -10, 0, width=0.5)
plt.axvline(0, color="k", linestyle="--")
plt.show()
../../../_images/placement_routing-2.png

Horizontal and Vertical control lines

i3.ConnectManhattan and i3.RouteManhattan allow the definition of horizontal and vertical control lines.

H

Horizontal control point class.

V

Vertical control point class.

i3.H and i3.V represent Horizontal and Vertical lines on a grid. They are a special case of the more general i3.CP control points, requiring you to only specify an X-value for i3.V and a Y-value for i3.H:

i3.CP((None, 10)) == i3.H(10)
i3.CP((10, None)) == i3.V(10)

Note

When supplying i3.H and i3.V as control lines they have to be alternated as the lines should cross each other in the order you want the routing to go.

Below are some examples using i3.ConnectManhattan:

An example showing the syntax:

from ipkiss3 import all as i3

control_points = [i3.V(10), i3.H(40), i3.V(35)]

connector = i3.ConnectManhattan('inst1:out', 'inst2:out', control_points=control_points)

An example showing the usage in an obstacle avoidance situation:

import si_fab.all as pdk
from ipkiss3 import all as i3
from picazzo3.fibcoup.curved import FiberCouplerCurvedGrating
import matplotlib.pyplot as plt

# Placing the components in a circuit
gr = FiberCouplerCurvedGrating()
control_points = [i3.V(-40), i3.H(30), i3.V(50)]
circuit = i3.Circuit(
    insts={
        'gr': gr,
        'grb1': gr,
        'grb2': gr
    },
    specs=[
        i3.Place('gr', position=(0, 0)),
        i3.Place('grb1', position=(-100, 0)),
        i3.Place('grb2', position=(+100, 0), angle=180),

        i3.ConnectManhattan(
            'grb1:out', 'grb2:out',
            control_points=control_points,
        )
    ]
)

lay = circuit.Layout()
lay.visualize(annotate=True, show=False)

plt.axvline(x=-40, color='k', linestyle='--')
plt.axhline(y=30, color='k', linestyle='--')
plt.axvline(x=50, color='k', linestyle='--')
plt.show()
../../../_images/placement_routing-3.png

Relative route control

Sometimes positions or values are only known relative to an anchor like the start or end port. In that case you can use symbols to represent these unknowns, and IPKISS will fill in the appropriate values.

START

Symbolic object that represents a start value or position.

END

Symbolic object that represents an end value or position.

PREV

Symbolic object that represents a previous value or position.

import si_fab.all as pdk
import ipkiss3.all as i3

control_points = [i3.CP(i3.START + (30, 10), i3.NORTH),  # control point at (30, 10) relative to the start port
                  i3.CP((50, 50), i3.EAST),  # control point at (50, 50) relative to (0, 0)
                  i3.CP(i3.END - (30, 10), i3.NORTH)]  # control point at (-30, -10) relative to the end port

gc = pdk.FC_TE_1550()
circuit = i3.Circuit(
    insts={"gc1": gc, "gc2": gc},
    specs=[
        i3.Place("gc1:out", (0, 0)),
        i3.Place("gc2:out", (100, 100), 180),
        i3.ConnectManhattan(
            "gc1:out",
            "gc2:out",
            control_points=control_points,
            bend_radius=5.0
        ),
    ],
)
circuit.get_default_view(i3.LayoutView).visualize()

gc = pdk.FC_TE_1550()
circuit = i3.Circuit(
    insts={"gc1": gc, "gc2": gc},
    specs=[
        i3.Place("gc1:out", (0, 0)),
        i3.Place("gc2:out", (100, 100), 180),
        i3.ConnectManhattan(
            "gc1:out",
            "gc2:out",
            control_points=[i3.H(i3.END - 40)],
            bend_radius=5.0
        ),
    ],
)
circuit.get_default_view(i3.LayoutView).visualize()
../../../_images/placement_routing-4_00.png
../../../_images/placement_routing-4_01.png

Electrical routing

VIA

Place a via and change trace template at a certain point along the route.

In contrast to optical ports, electrical ports typically don’t have a specified angle. Therefore, routing from one electrical port to another often requires that the start and end angle of the route be determined by the user. There is also a specialized control point, i3.VIA, that allows you to switch layers and insert vias along the route.

i3.ConnectElectrical is similar to i3.ConnectManhattan but allows us to specify the start_angle and the end_angle when necessary.

import si_fab.all as pdk  # noqa
import ipkiss3.all as i3

bp = pdk.BondPad()
circuit = i3.Circuit(
    insts={"pad1": bp, "pad2": bp},
    specs=[
        i3.Place("pad1:m1", (0, 0)),
        i3.Place("pad2:m1", (150, 150)),
        i3.ConnectElectrical(
            "pad1:m1",
            "pad2:m1",
            start_angle=0,
            end_angle=-90,
        ),
    ],
)
circuit.get_default_view(i3.LayoutView).visualize()
../../../_images/placement_routing-5.png

However, if the angle information is available on the port, then it is not required to specify the corresponding start_angle or end_angle:

import si_fab.all as pdk  # noqa
import ipkiss3.all as i3

class Pad(i3.PCell):
    class Layout(i3.LayoutView):
        size = i3.Size2Property(default=(50.0, 50.0), doc="Size of the bondpad")
        metal_layer = i3.LayerProperty(default=i3.TECH.PPLAYER.M1.LINE, doc="Metal used for the bondpad")

        def _generate_elements(self, elems):
            elems += i3.Rectangle(layer=self.metal_layer, box_size=self.size)
            return elems

        def _generate_ports(self, ports):
            ports += i3.ElectricalPort(
                name="m1",
                position=(0.0, 0.0),
                shape=i3.ShapeRectangle(box_size=self.size),
                process=self.metal_layer.process,
                angle=0,
            )
            return ports

pad = Pad()
M1_wire_tpl = pdk.M1WireTemplate().Layout(width=10)
circuit = i3.Circuit(
    insts={"pad1": pad, "pad2": pad},
    specs=[
        i3.Place("pad1:m1", (0, 0)),
        i3.Place("pad2:m1", (150, 150)),
        i3.ConnectElectrical(
            "pad1:m1",
            "pad2:m1",
            trace_template=M1_wire_tpl,
        ),
    ],
)
circuit.get_default_view(i3.LayoutView).visualize()
../../../_images/placement_routing-6.png

The given start_angle or end_angle will take precedence over the angle of the start or end port:

M1_wire_tpl = pdk.M1WireTemplate().Layout(width=10)
circuit = i3.Circuit(
    insts={"pad1": Pad(), "pad2": Pad()},
    specs=[
        i3.Place("pad1:m1", (0, 0)),
        i3.Place("pad2:m1", (150, 150)),
        i3.ConnectElectrical(
            "pad1:m1",
            "pad2:m1",
            trace_template=M1_wire_tpl,
            end_angle=-90,
        ),
    ],
)
circuit.get_default_view(i3.LayoutView).visualize()
../../../_images/placement_routing-7.png

Use i3.VIA and i3.CP to route electrical wire:

import si_fab.all as pdk
import ipkiss3.all as i3
import matplotlib.pyplot as plt

class VIA_M1_M2_ARRAY(i3.PCell):
    box_size = i3.Size2Property(default=(10, 10))
    via_pitch = i3.Size2Property(
        default=i3.TECH.BONDPAD.VIA_PITCH, doc="2D pitch (center-to-center) of the vias"
    )
    via = i3.ChildCellProperty( doc="Via used")

    def _default_via(self):
        return pdk.VIA_M1_M2()

    class Layout(i3.LayoutView):
        def _generate_elements(self, elems):
            elems += i3.Rectangle(layer=i3.TECH.PPLAYER.M1, box_size=self.box_size)
            elems += i3.Rectangle(layer=i3.TECH.PPLAYER.M2, box_size=self.box_size)
            return elems

        def _generate_instances(self, insts):
            periods_x = int(self.box_size[0] / self.via_pitch[0]) - 1
            periods_y = int(self.box_size[1] / self.via_pitch[1]) - 1

            insts += i3.ARef(
                reference=self.via,
                origin=(
                    -(periods_x - 1) * self.via_pitch[0] / 2.0,
                    -(periods_y - 1) * self.via_pitch[1] / 2.0,
                ),
                period=self.via_pitch,
                n_o_periods=(periods_x, periods_y),
            )
            return insts


bp = pdk.BondPad()
via_array = VIA_M1_M2_ARRAY()
M1_wire_tpl = pdk.M1WireTemplate().Layout(width=8)
M2_wire_tpl = pdk.M2WireTemplate().Layout(width=10)

circuit = i3.Circuit(
    insts={"pad1": bp, "pad2": bp},
    specs=[
        i3.Place("pad1", (0, 0)),
        i3.Place("pad2", (200, 200)),
        i3.ConnectElectrical(
            "pad1:m1",
            "pad2:m2",
            start_angle=90,
            end_angle=180,
            trace_template=M1_wire_tpl,
            control_points=[
                i3.VIA(
                    (50, 70),
                    direction_in=i3.WEST,
                    direction_out=i3.NORTH,
                    trace_template=M2_wire_tpl,
                    layout=via_array,
                ),
                i3.CP((None,120)),
                i3.CP((110,150),i3.NORTH)
            ],
        ),
    ],
)
circuit.Layout().visualize(show=False)

plt.axhline(y=120, color='k', linestyle='--')
plt.arrow(110, 150, 0, 10, width=2)
plt.scatter(110,150, c="C1", s=80, marker="x")
plt.show()
../../../_images/placement_routing-8.png