2. Develop and distribute your component library


As your team iterates design cycles, you will develop components, new routing mechanisms and simulation recipes on top of foundry PDKs that you use for your tape-outs. It’s commonplace that the knowledge driving these developments is poorly maintained and scattered across different designers. The goal of this tutorial is to teach you simple practices that will allow you to have a well-maintained internal Luceda library, allowing your team to:

  • Share components and design practices between designers

  • Enable your team to use integrated photonic design practices created by the experts

  • Exploit the power of the Luceda Photonics Design Platform, both in a Python-scripting environment and in an EDA tool

2.1. Introduction

In this tutorial you will learn how to:

  • Set up a Luceda library

  • Keep track of the changes you and your team make using version control

  • Use that library in multiple designs

Imagine a team of photonics designers. We’ll name them the P-Team. This team uses the SiFab PDK as a basis to design their components. Their goal is to build their own library, named the pteam_library_si_fab, consisting of their own custom components. By collaborating together on a shared library, the entire team is able to use and reuse everything in it.

2.2. Folder structure of the library

Even if the library is not a PDK (What’s a PDK?), it should be structured like a PDK. The folder structure should then look as follows:

pteam_library_si_fab
├── building
│   └── build_pteam_library_si_fab.py
│
└── ipkiss
    └── pteam_library_si_fab
        ├── components
        │   ├── mzi
        │   │   ├── doc
        │   │   │   └── example_mux2heated.py
        │   │   ├── pcell
        │   │   │   ├── __init__.py
        │   │   │   ├── lattice_utils.py
        │   │   │   ├── mux2.py
        │   │   │   └── mzi_lattice_filter.py
        │   │   ├── simulate
        │   │   │   ├── __init__.py
        │   │   │   └── simulate_mux2.py
        │   │   └── __init__.py
        │   │
        │   ├── splitter_tree
        │   │   ├── __init__.py
        │   │   └── cell.py
        │   │
        │   └── __init__.py
        │
        ├── technology
        │   └── __init__.py
        │
        ├── __init__.py
        └── all.py

You can find this library in luceda-academy/libraries.

The ‘ipkiss’ folder

The ipkiss folder contains all the IPKISS-related files. It contains the full Luceda pteam_library_si_fab library with its components and technology. The former contains all the components based on the SiFab PDK, those being a simple splitter tree (SplitterTree) and a more complicated heated two-way multiplexer (Mux2). The latter refers to the technology of the SiFab PDK. Because pteam_library_si_fab is not a PDK, it does not have a technology of its own. This is the content of the __init__.py file in the technology folder:

luceda-academy/libraries/pteam_library_si_fab/ipkiss/pteam_library_si_fab/technology/__init__.py
# Note: this file may contain additional technology layers that are required for our small pcells library.
# Currently we need to specify this file and load TECH so the library builder knows how to build this library.

from si_fab import technology

TECH = technology.TECH

The ‘components’ folder

The splitter_tree folder under the components folder has two files: cell.py and __init__.py. The first contains the code of the splitter tree PCell:

luceda-academy/libraries/pteam_library_si_fab/ipkiss/pteam_library_si_fab/components/splitter_tree/cell.py
from si_fab import all as pdk
from ipkiss3 import all as i3


class SplitterTree(i3.Circuit):
    """This creates a splitter tree PCell, made of 1x2 splitter elements.

    Users can specify the number of tree levels as PCell parameter.
    """

    splitter = i3.ChildCellProperty(doc="Splitter used")
    levels = i3.IntProperty(default=3, doc="Number of levels")
    spacing_x = i3.PositiveNumberProperty(
        default=100.0, doc="Spacing between the splitters in x-direction in the last level"
    )
    spacing_y = i3.PositiveNumberProperty(default=50.0, doc="Spacing in y-direction")
    bend_radius = i3.PositiveNumberProperty()

    def _default_bend_radius(self):
        return 5.0

    def _default_splitter(self):
        return pdk.MMI1x2Optimized1550()

    def _default_insts(self):
        insts = dict()
        for level in range(self.levels):
            for sp in range(int(2**level)):
                insts["sp_{}_{}".format(level, sp)] = self.splitter

        return insts

    def _default_specs(self):
        specs = []
        for level in range(self.levels):
            for sp in range(int(2**level)):
                sp_y = self.spacing_y * 2 ** (self.levels - level - 1)
                specs.append(
                    i3.Place(
                        "sp_{}_{}".format(level, sp),
                        (level * self.spacing_x, -0.5 * (2**level - 1) * sp_y + sp * sp_y),
                    )
                )

        for level in range(1, self.levels):
            for sp in range(int(2**level)):
                if sp % 2 == 0:
                    in_port = "sp_{}_{}:out1".format(level - 1, int(sp / 2.0))
                else:
                    in_port = "sp_{}_{}:out2".format(level - 1, int(sp / 2.0))

                out_port = "sp_{}_{}:in1".format(level, sp)

                specs.append(i3.ConnectManhattan(in_port, out_port, bend_radius=self.bend_radius))

        return specs

while the __init__.py file looks like this:

luceda-academy/libraries/pteam_library_si_fab/ipkiss/pteam_library_si_fab/components/splitter_tree/__init__.py
from .cell import SplitterTree

A more advanced tunable Mach Zehnder interferometer can be found in the mzi folder. This folder has a doc folder, containing a script example_mux2heated.py that visualizes the Mux2 and simulates it. Then there is the simulate folder, which contains the simulation recipe for the Mux2 in simulate_mux2.py and an empty __init__.py. The code defining the PCell itself can be found in the pcell folder. The last file in the mzi folder is an __init__.py file with the code: from .pcell.cell import Mux2Heated.

All the other __init__.py files in the folder structure should be empty.

The ‘all.py’ file

There is one file we haven’t discussed yet, the all.py file in the ipkiss/pteam_library_si_fab folder. In this file all the components that are part of the custom library pteam_library_si_fab are imported.

luceda-academy/libraries/pteam_library_si_fab/ipkiss/pteam_library_si_fab/all.py
from .components.mzi import MZI
from .components.heated_mux2 import Mux2Heated
from .components.splitter_tree import SplitterTree
from .components.mzm import MZModulator
from .components.tunable_delay import TunableDelayLine
from .components.ppc_unit.pcell.cell import PPCUnit
from .components.mux2 import Mux2

__all__ = ["MZI", "Mux2Heated", "SplitterTree", "MZModulator", "TunableDelayLine", "PPCUnit", "Mux2"]

This makes it possible to import components from pteam_library_si_fab into other projects, using the import statement from pteam_library_si_fab.all import NameOfComponent.

Components that might be designed in the future, by any member of the P-Team, are added to the components folder and imported in the all.py file. This way it’s easy for the P-Team to keep track of each other’s designs and reuse them.

2.3. Version control

When designing a library - or doing development in general - it is always a good idea to keep track of the changes you and your team make. Doing so not only guarantees that everyone is always up to date and using the latest changes, but it also means you can revert to a previous version in case something goes wrong. In other words, a good version control system (VCS) lets you make changes with confidence.

There are many different options, and we recommend designers to do their own research and find out which one best fits their needs. Some popular examples (non-exhaustive and not in any particular order) are Git, Mercurial, Apache Subversion (SVN), Perforce, Bazaar, …

In this tutorial we will give a short and basic demonstration using Git in PyCharm. Note that the scope of this tutorial is to merely give an introduction to the necessary skills you need to create a scalable VCS workflow. While it can be a steep learning curve, it is definitely worth investing the time and effort in. At the end we provide a list of references to further help you on your way.

2.3.1. Configuration in Pycharm

First, visit the download page and install Git on your system. Next, open your library in PyCharm and in the top navigation bar select VCS >> Enable version control integration…

../../../_images/init_repo.png

Select Git and press OK. Git has now upgraded the library to a ‘repository’ and is able to track changes inside.

Note

To get the most flexibility, version control systems are often used via the terminal. Every operation in this tutorial has its own command in the terminal. Creating a repository with Git is done by navigating to the library directory and executing git init. For example:

cd C:\workspace\pteam_library_si_fab
git init

It’s often not necessary to track the changes of every file in the library. For example, PyCharm automatically creates a folder named .idea in the library and its contents are not relevant to us. To tell Git not to track these files, place a .gitignore file in the library repository. In this file we notify Git to ignore certain file paths, file extensions, directories, etc. For more information on the syntax, visit the gitignore reference page.

../../../_images/git_ignore.png

2.3.2. Saving your work

Every VCS has a command to save changes. In Git this is done by making a ‘commit’. A commit should be made for every relevant change. This will allow us to preview or revert to a previous version of the code at any point in time.

As a first step, we will first save the current state of the P-Team library. Go to Git >> Commit…, then select all the files in the library. It is important to enter a short descriptive commit message so you and your team understand what has changed.

../../../_images/first_commit.png

Click Commit in the bottom-left corner. Have a look at what’s happened by pressing the Git tab in the bottom-left navigation bar. A window will pop up showing the commit history. Our commit is listed there, together with its corresponding changes.

Now suppose you are not quite satisfied with the current splitter tree and would like to change its default horizontal spacing. In components\splitter_tree\cell.py, change the default value of spacing_x from 100 to 70. Commit the change as described previously, and have a look at the commit history:

../../../_images/second_commit.png

View the changes by selecting the latest commit and double-clicking on cell.py. Alternatively, select the two commits while holding Ctrl, right-click and choose Compare Versions.

Note

Save changes via the terminal with the git add and git commit commands. The previous commit corresponds to executing:

git add ipkiss/pteam_library_si_fab/components/splitter_tree/cell.py
git commit -m "Adjust SplitterTree horizontal spacing"

2.3.3. Undoing changes

Because we’re now consistently keeping track of our work, it’s easy to view or revert to previous commits. It’s a good idea to make some more adjustments and commits to the library first. This will help to internalize the process of commiting and allow you to try out the upcoming concepts.

To view a previous state of the codebase, select a commit and click on Checkout Revision ‘…’.

To undo changes there are quite a few options. A few of them are listed here, all slightly different:

  • Revert commit: the safest option. Produce a new commit that reverts the changes in the original commit.

  • Undo commit: Undo the last commit, but keep the changes.

  • Drop commit: Remove a commit and its changes. This can rewrite the commit history and should be used with care.

  • Reset current branch: Reset to a previous commit. Knowledge about branches is required to fully understand this and will not be covered here.

../../../_images/revert_commit.png

The best way to learn is to play around with these different options.

2.3.4. Next steps

Follow an in-depth tutorial

There are many great tutorials to help you learn about your VCS of choice. A quick search on the internet should get you started.

For Git specifically, the Git user manual provides a complete tutorial focused on using the terminal. If that is too difficult, consider watching a video or sticking to a tutorial with a GUI instead (e.g. PyCharm Git tutorial)

Set up a remote repository

Everything up until now was done in a local repository, i.e. a repository that only exists on your computer. However, to collaborate on a project there has to be a ‘remote’ repository - often residing on a server - that everyone in the team can reach and work in. A convenient solution is to use a hosting service like Github, Gitlab, Bitbucket, Gitbucket, Sourceforge, etc. However, a team may decide to set up and manage their own server.

Compare different graphical user interfaces

PyCharm is only one option. More information on using version control in PyCharm can be found here.

Depending on the VCS there may be many more user interfaces available that help to facilitate version control. Even so, these tools might not always be able to do exactly what you want. In that case, executing command via the terminal is the way to go.

2.4. Using the library in multiple designs

It is now possible to use this pteam_library_si_fab like any other PDK in Luceda IPKISS.

To create a new design in IPKISS with the components that are in the pteam_library_si_fab you need to add this library to your PYTHONPATH, as you would do for any other PDK. In PyCharm, you can do this by right-clicking on the luceda-academy/libraries/pteam_library_si_fab/ipkiss folder and selecting Mark Directory as / Sources Root. Alternatively, you can run the following command from the terminal:

set PYTHONPATH="path-to-pteam_library_si_fab/ipkiss";%PYTHONPATH%

You can now use the import statement from pteam_library_si_fab.all import Mux2 in your new design (or any other cell that is in the pteam_library_si_fab).

For instance, in the Several contributions to one tape-out run tutorial, Pierre used the splitter tree from pteam_library_si_fab (SplitterTree) by importing it at the top of his design.

luceda-academy/training/topical_training/design_project_management/tapeout_202008_si_fab/pierre/splitter_tree/splitter_tree_routed_north.py
import si_fab.all as pdk
from pteam_library_si_fab.all import SplitterTree
from ipkiss3 import all as i3
import numpy as np


class RouteSplitterTreeNorth(i3.PCell):
    """Routed Splitter tree with grating couplers placed north."""

    dut = i3.ChildCellProperty(doc="Splitter tree used")
    grating = i3.ChildCellProperty(doc="Grating coupler to be used")
    grating_sep = i3.PositiveNumberProperty(default=50.0, doc="Separation between the gratings")
    bend_radius = i3.PositiveNumberProperty(default=20.0, doc="Bend Radius")
    wav_sep = i3.PositiveNumberProperty(default=5.0, doc="Separation between waveguides.")

    def _default_dut(self):
        return SplitterTree(name=self.name + "_DUT", levels=6)