Netlist extraction from layout

In this example we build a circuit in layout, and extract the netlist so we can run circuit simulations.

Importing the technology file

We start with importing the demolib PDK technology, which is a basic PDK shipped with IPKISS. You can replace this by another PDK (custom made, or from our list of supported PDKs). and the layers will automatically adjust to reflect this technology.

import demolib.all as pdk
from ipkiss3 import all as i3
from picazzo3.wg.dircoup import BendDirectionalCoupler

Creating a MZI in layout

For this example, we’ll only focus on building the layout part. We will rely on the netlist extraction to automatically extract the relevant circuit information. Note that there are already MZI classes available in picazzo which have a predefined netlist, so this example is purely to illustrate the netlist extraction process.

class MZI(i3.PCell):
    class Layout(i3.LayoutView):
        delay_length = i3.NumberProperty(default=10, doc="Delay difference")
        dst = i3.PositiveNumberProperty(default=50)

        def _generate_instances(self, insts):
            wg_tmpl = pdk.SiWireWaveguideTemplate()

            dc = BendDirectionalCoupler(name=self.name + '_dc', trace_template1=wg_tmpl)
            dc_lo = dc.Layout(bend_angle=90)

            wg1 = i3.RoundedWaveguide(name=self.name + '_wg2', trace_template=wg_tmpl)
            wg1_lo = wg1.Layout(shape=[(0, 0), (0, -10), (self.dst, -10), (self.dst, 0)])

            wg2 = i3.RoundedWaveguide(name=self.name + '_wg1', trace_template=wg_tmpl)
            wg2_lo = wg2.Layout(shape=[(0, 0), (0, 10 + self.delay_length),
                                       (self.dst, 10 + self.delay_length), (self.dst, 0)])


            insts += i3.place_insts(
                insts={
                    'dc1': dc,
                    'dc2': dc,
                    'wg1': wg1,
                    'wg2': wg2
                },
                specs=[
                    i3.Place('dc1', (0, 0)),
                    i3.Join([
                        ('dc1:out1', 'wg1:in'),
                        ('dc1:out2', 'wg2:in'),
                        ('dc2:in1', 'wg1:out'),
                    ])
                ]
            )

            return insts

        def _generate_ports(self, ports):
            ports += i3.expose_ports(self.instances, {
                'dc1:in1': 'in1',
                'dc1:in2': 'in2',
                'dc2:out1': 'out1',
                'dc2:out2': 'out2',
            })

            return ports

    class Netlist(i3.NetlistFromLayout):
        pass

    class CircuitModel(i3.CircuitModelView):
        def _generate_model(self):
            return i3.HierarchicalModel.from_netlistview(self.netlist_view)


mzi = MZI()
lay = mzi.Layout()

lay.visualize(annotate=True)
../_images/sphx_glr_plot_netlist_extract_001.png

Extracting the netlist

Now let’s have a look at the netlist. This is automatically extracted thanks to i3.NetlistFromLayout. IPKISS will look at the layout and check for connected ports, and generate the relevant netlist instances, terms, and connections:

nl_mzi = mzi.Netlist()

print(nl_mzi.netlist)

Out:

netlist:
--------
instances:
        - wg1 : <Single instance in netlist of PCELL_1_wg2>
        - wg2 : <Single instance in netlist of PCELL_1_wg1>
        - dc2 : <Single instance in netlist of PCELL_1_dc>
        - dc1 : <Single instance in netlist of PCELL_1_dc>

terms:
        - out2
        - in2
        - in1
        - out1

nets:
        - dc1:in1-in1: <OpticalLink in1 to dc1:in1>
        - dc2:in2-wg2:out: <OpticalLink dc2:in2 to wg2:out>
        - dc1:in2-in2: <OpticalLink in2 to dc1:in2>
        - dc1:out2-wg2:in: <OpticalLink dc1:out2 to wg2:in>
        - dc2:out2-out2: <OpticalLink out2 to dc2:out2>
        - dc1:out1-wg1:in: <OpticalLink dc1:out1 to wg1:in>
        - dc2:in1-wg1:out: <OpticalLink dc2:in1 to wg1:out>
        - dc2:out1-out1: <OpticalLink out1 to dc2:out1>

Run a simulation

Since our circuit is now equipped with a netlist, we can run a circuit simulation to see the transmission of the MZI.

import numpy as np
from pylab import plt

wavelengths = np.linspace(1.5, 1.6, 1001)

mzi_cm = mzi.CircuitModel()
S = mzi_cm.get_smatrix(wavelengths=wavelengths)

plt.plot(wavelengths, np.abs(S['out1', 'in1']) ** 2, label='Through port (in1-out1)')
plt.plot(wavelengths, np.abs(S['out2', 'in1']) ** 2, label='Cross port (in1-out2')
plt.legend()
plt.xlabel("Wavelength ($\mu$ m)")
plt.show()
../_images/sphx_glr_plot_netlist_extract_002.png