Defining a customized technology

In this example, we extend a predefined technology in Ipkiss. We add a layer which is used to open the back-end oxide (top oxide) on top of the waveguides. This can be used to open a window for a sensing application, for instance to let a gas or fluid reach a ring resonator on top of which a window is opened.

This example illustrates the minimum required changes to add this layer + related settings. To build your own technology, it is recommended to start from demolib, the demonstration PDK distributed with the IPKISS samples (windows: %USERPROFILE%\luceda\samples\ipkiss340, linux: ~/luceda/samples/ipkiss340), change the name of that PDK and modify the related files. See also the technology guide.

Illustrates

  1. how to create a customized technology file by importing a predefined file
  2. how to add a new process layer
  3. how to define import and export settings and visualization
  4. how to define a virtual fabrication process
  5. how to alter values in the predefined technology files

Getting started

We start by importing the silicon_photonics technology, and modify it’s name:

import demolib.all as pdk
import ipkiss3.all as i3
TECH = i3.TECH
TECH.name = "CUSTOMIZED TECHNOLOGY SAMPLE"

Adding a process layer

In order to add a new layer, we need to do two things:

  • Add the ProcessLayer - this tells ipkiss about the existance of the layer related to a specific process module
  • Add one or more ProcessPurposeLayers - this tells ipkiss which combinations of the new ProcessLayer and PatterPurposes exist. In this sample we’ll add only one.
from ipkiss.technology.technology import TechnologyTree
from ipkiss.process import ProcessLayer, ProcessPurposeLayer
TECH.PROCESS.OXIDE_OPEN = ProcessLayer(name="OX_OPEN", extension="OXO")
TECH.PPLAYER.OXO = TechnologyTree()
TECH.PPLAYER.OXO.TRENCH = ProcessPurposeLayer(process=TECH.PROCESS.OXIDE_OPEN,
                                              purpose=TECH.PURPOSE.DRAWING,
                                              name="OX_OPEN")

TECH.PPLAYER.OXO.ALL = TECH.PPLAYER.OXO.TRENCH

Defining rules

The technology tree can contain some design rules, defaults or drawing guidelines. In this case, we’ll add a few keys for the minimum width and spacing of patterns on our new OXIDE_OPEN layer:

# define some rules
TECH.OXIDE_OPEN = TechnologyTree()
TECH.OXIDE_OPEN.MIN_WIDTH = 2.0     # minimum width of 2.0um
TECH.OXIDE_OPEN.MIN_SPACING = 1.0   # minimum spacing of 1.0um

Visualization and import/export settings

In order to make the new layer export to GDSII (and import from GDSII), we need to update the GDS LAYERTABLE.

TECH.GDSII.LAYERTABLE[(TECH.PROCESS.OXIDE_OPEN, TECH.PURPOSE.DRAWING)] = (30, 10)

In order to have Ipkiss visualize the layer using the .visualize() method on a Layout view, we need to set the display style for the ProcessPurposeLayer. In this case, we set it to a yellow color with a 50% transparance.

from ipkiss.visualisation import color
from ipkiss.visualisation.display_style import DisplayStyle

DISPLAY_OXIDE_OPEN = DisplayStyle(color=color.COLOR_YELLOW, alpha=0.5, edgewidth=1.0)
TECH.DISPLAY.DEFAULT_DISPLAY_STYLE_SET.append((TECH.PPLAYER.OXO.TRENCH, DISPLAY_OXIDE_OPEN))

Overwriting technology keys

Overwriting technology settings which were already defined, is possible as well. We can for instance increase the default bend radius on the WG layer to 10 micrometer:

TECH.WG.overwrite_allowed.append("BEND_RADIUS")
TECH.WG.BEND_RADIUS = 10.0

Example: ring resonator with back-end opening cover layer

In order to use our newly defined technology, we make sure to import it before anything else in the main script. Make sure it is imported before ipkiss3 and before any picazzo library components ! Make sure that your new technology folder (in this case ‘mytech’) is in your PYTHONPATH.

from mytech import TECH
import ipkiss3.all as i3
from picazzo3.filters.ring import RingRect180DropFilter

We continue by defining a simple PCell which consists of a ring resonator and a cover layer:

import ipkiss3.all as i3
from picazzo3.filters import RingRect180DropFilter


class RingWithWindow(i3.PCell):
   ring_resonator = i3.ChildCellProperty()

   def _default_ring_resonator(self):
       return RingRect180DropFilter()

   class Layout(i3.LayoutView):
       def _generate_instances(self, insts):
           insts += i3.SRef(reference=self.ring_resonator,
                            position=(0.0,0.0))
           return insts

       def _generate_elements(self, elems):
           # add a ring with the same shape as the ring resonator on the OXIDE_OPEN layer
           r = self.ring_resonator
           R = r.bend_radius
           SHor = r.straights[0]
           SVer = r.straights[1]
           # generate a control shape the same shape of the ring
           ring_shape = i3.Shape([(-0.5 * SHor - R, 0.5 * SVer + R),
                                  (0.5 * SHor + R, 0.5 * SVer + R),
                                  (0.5 * SHor + R, -0.5 * SVer - R),
                                  (-0.5 * SHor - R, -0.5 * SVer - R)
                                  ], closed=True)
           # use the rounding algorithm and radius of the ring to round the control shape
           rounded_shape = r.rounding_algorithm(original_shape=ring_shape, radius=R)

           elems += i3.Path(layer=TECH.PPLAYER.OXO.TRENCH,
                            shape=rounded_shape,
                            line_width=TECH.OXIDE_OPEN.MIN_WIDTH)
           return elems

We can now instantiate the component, visualize its layout and export to GDSII:

R = RingWithWindow(name="ring_with_window")
Rlayout = R.Layout()
Rlayout.visualize()    # show layout elements
Rlayout.write_gdsii("ring_with_window.gds")
../_images/sphx_glr_plot_custom_technology_001.png
Ring resonator with opening layer

Ring resonator covered by a back-end opening layer: GDSII

Defining the virtual fabrication

A virtual fabrication is used for:

More explanation on virtual fabrication, what it does and how to set it up, can be found here.

Let’s set up the virtual fabrication now:

from pysics.basics.material.material_stack import MaterialStack

MSTACK_SOI_OX = MaterialStack(name="Oxide",
                              materials_heights=[(TECH.MATERIALS.SILICON_OXIDE, 1.0),
                                                 (TECH.MATERIALS.SILICON_OXIDE, 0.22),
                                                 (TECH.MATERIALS.SILICON_OXIDE, 1.0)],
                              display_style=DisplayStyle(color=color.COLOR_YELLOW))

MSTACK_SOI_220nm_OX = MaterialStack(name="220nm Si + Oxide",
                                    materials_heights=[(TECH.MATERIALS.SILICON_OXIDE, 1.0),
                                                       (TECH.MATERIALS.SILICON, 0.220),
                                                       (TECH.MATERIALS.SILICON_OXIDE, 1.0)],
                                    display_style=DisplayStyle(color=color.COLOR_RED))
MSTACK_SOI_220nm_AIR = MaterialStack(name="220nm Si + Air",
                                     materials_heights=[(TECH.MATERIALS.SILICON_OXIDE, 1.0),
                                                        (TECH.MATERIALS.SILICON, 0.220),
                                                        (TECH.MATERIALS.AIR, 1.0)],
                                     display_style=DisplayStyle(color=color.COLOR_BLUE))
MSTACK_OX_AIR = MaterialStack(name="Oxide + Air",
                              materials_heights=[(TECH.MATERIALS.SILICON_OXIDE, 1.0),
                                                 (TECH.MATERIALS.SILICON_OXIDE, 0.220),
                                                 (TECH.MATERIALS.AIR, 1.0)],
                              display_style=DisplayStyle(color=color.COLOR_ORANGE))

# then compose a process flow
from ipkiss.plugins.vfabrication.process_flow import VFabricationProcessFlow
TECH.VFABRICATION.overwrite_allowed.append("PROCESS_FLOW")
PROCESS_FLOW = VFabricationProcessFlow(active_processes=[TECH.PROCESS.WG, TECH.PROCESS.OXIDE_OPEN],
                                       process_to_material_stack_map=#WG, OXIDE_OPEN
                                       [((0, 0), MSTACK_SOI_220nm_OX ),
                                        ((0, 1), MSTACK_SOI_220nm_AIR ),
                                        ((1, 0), MSTACK_SOI_OX ),
                                        ((1, 1), MSTACK_OX_AIR ),
                                        ],
                                       is_lf_fabrication={TECH.PROCESS.WG: False,
                                                          TECH.PROCESS.OXIDE_OPEN: False,
                                                         }
                                       )

Once the PROCESS_FLOW is defined, we can call visualize_2d, and see the updated virtual fabrication:

Rlayout.visualize_2d(process_flow=PROCESS_FLOW)
../_images/sphx_glr_plot_custom_technology_002.png

The default value of process_flow for visualize_2d is the PROCESS_FLOW defined in the technology. We can persist it this way, so we can just call Rlayout.visualize_2d().

TECH.VFABRICATION.overwrite_allowed.append("PROCESS_FLOW")
TECH.VFABRICATION.PROCESS_FLOW = PROCESS_FLOW