Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

GDSFactory

The open ecosystem for EPIC design.

Authors
Affiliations
Institute for Nanotechnology, Karlsruhe Institute of Technology
GDSFactory+
GDSFactory+
PsiQuantum
GDSFactory+

Abstract

Designing an EPIC -- and (electronic) photonic integrated circuit -- requires robust tooling ranging from exploratory simulation via mask layout and verification to validation. In the past a variety of specialized syntaxes were introduced with similar tooling for electronic chip design -- the older cousin of EPICs. The relatively young age of integrated photonics opens up the opportunity to establish an accessible workflow which is intuitive, easily customizeable and extensible. GDSFactory is a python based framework to build EPICs that provides a common syntax for design/layout, simulation (Ansys Lumerical, tidy3d, MEEP, MPB, DEVSIM, SAX, Elmer, Palace, …), verification (Klayout DRC, LVS, netlist extraction, connectivity checks, fabrication models) and validation. This paper showcases the capabilities of GDSFactory, highlighting its end-to-end workflow, which enables users to turn their chip designs into working products.

Keywords:Integrated PhotonicsGDSIIPythonelectronic design automation (EDA)

Introduction

Hardware iterations typically require months of time and involve substantial financial investments, often in the millions of dollars. In contrast, software iterations can be completed within hours and at a significantly lower cost, typically in the hundreds of dollars. Recognizing this discrepancy, GDSFactory aims to bridge the gap by leveraging the advantages of software workflows in the context of hardware chip development.

To achieve this, GDSFactory offers a comprehensive solution through a unified python API. This API enables users to drive various aspects of the chip development process, including layout design, verification (such as optimization, simulation, and design rule checking), and validation (through the implementation of test protocols). By consolidating these functionalities into a single interface, GDSFactory streamlines the workflow and enhances the efficiency of hardware chip development.

Closing the design cycle with GDSFactory.

GDSFactory bridges the whole EPIC design cycle, with a variety of Foundary PDKs available it is both easy to construct circuits reusing proven devices and to realize conceptually new designs. The generated component layouts can be used in varous simulators for exploration, optimization and validation. GDSfactory tightly integrates with KLayout, leveraging its advanced design rule checks (DRC) and layout versus schematic (LVS) capabilities. For characterization of the fabricated devices GDSFactory provides rich metadata compatible to commercial wafer probers, including the position and orientation of fiber-to-waveguide couplers.

GDSFactory acts a as a unifying framework that covers the entire EPIC design cycle. It allows the user to:

In this paper we will highlight the key features of GDSFactory follwing the structure of the design cycle shown in Figure 1 and mentioned above.

Design Flow

GDSFactory offers a range of design capabilities, including layout description, optimization, and simulation. It allows users to define parametric cells (PCells) in python, facilitating the creation of complex components. The library supports the simulation of components using different solvers, such as mode solvers (finite element and finite difference eigenmode solvers), eigenmode expansion methods (EME), TCAD and thermal simulators, and fullwave FDTD simulations. Optimization capabilities are also available through an integration with Ray Tune, and automatically differentiable solvers enabling efficient parameter tuning for improved performance.

Mask Layout

Generating mask layouts ready for fabrication is the core functionality of GDSFactory. As input, GDSFactory supports 3 different workflows that can be also mixed and matched.

  1. Write python code. Recommended for developing cell libraries.

  2. Write YAML code to describe your circuit. Recommended for circuit design. Notice that the YAML includes schematic information (instances and routes) as well as Layout information (placements).

  3. Define schematic, export SPICE netlist, convert SPICE to YAML and tweak YAML.

As output you write a GDSII or OASIS file that you can send to your foundry for fabrication. It also exports component settings (for measurement and data analysis) and netlists (for circuit simulations). The following examples concentrate on photonic integrated design, however they are readily adaptable for RF and analog circuit design.

import gdsfactory as gf
from gdsfactory.gpdk import PDK
PDK.activate()

# Create a blank component 
# (essentially an empty GDS cell 
# with some special features).
c = gf.Component()

# Add some geometry to it.
p1 = c.add_polygon(
    [(-8, -6), (6, 8), (7, 17), (9, 5)], 
    layer=(1, 0)
)

c # Plot it in jupyter notebook.

(a)

<Figure size 350x262 with 1 Axes>

(b)

Figure 2:Minimal example demonstrating the most basic functionality of GDSFactory: creating a polygon on a specified layer. (a) Shows the code snippet in python including descriptive comments to highlight selected aspects. All code snippets used throughout this paper can be run interactively in the online version of this paper. All shown examples are part of the GDSFactory documentation. (b) The resulting GDSII layout as it would appear in the inline Jupyter notebook viewer shipped with gdsfactory.

Figure 2 shows a minimal example how to create a polygon from a list of points on a specified layer. The snippets also demonstrate some of the steps required to get going:

  1. As with any python library, the first step is to import gdsfactory. For convenience we will alias it as gf.

  2. Before we can start designing, we need to activate a process development kit (PDK) that defines the layers and design rules for a specific fabrication process. Here we use the generic PDK included with GDSFactory. We will discuss Process Development Kits (PDKs) in more detail below.

  3. Next we create a blank Component instance that will hold our geometry. It can be understood as an empty GDS cell, that is enriched with additional functionality like metadata and ports.

  4. Finally we add the desired polygon to the component by specifying the list of points and the GDS layer we want to add it to.

  5. Lastly we visualize the created geometry using the built-in plotting functionality. In this case we use the inline viewer for Jupyter notebooks. However, GDSFactory also supports synchronization with KLayout for advanced visualization and verification. Leveraging the export capabilities of GDSFactory, the created layout can be saved and viewed as a GDSII or OASIS file ready for fabrication. (TODO correctly name available formats)

Defining more complex geometries from scratch can quickly become cumbersome. To alleviate this burden GDSFactory provides a rich library of predefined components ranging from geometrical primitives, over labels, structures for process controll like resolution tests and interlayer alignment calipers to common photonic building blocks like waveguides, bends, couplers, interferometers, ring resonators and more. These predefined parametric components can be easily reused in user-defined designed as is demonstrated in Figure 3. Here we create a new blank component and add two realizations of predefined components: a text label and a rectangle. The parent component contains possibly multiple references to the underlying geometry, which can be manipulated individually.

c = gf.Component()

# Create new geometry from the functions
# available in the geometry library.
t = gf.components.text("Hello!")
r = gf.components.rectangle(
    size=(5, 10), layer=(2, 0)
)

# Add references to the new geometry 
# to c, our blank component.
text1 = c.add_ref(t)

# Using the << operator (identical to add_ref()),
# add the same geometry a second time.
text2 = c << t
r = c << r  # Add the rectangle we created.

# Now that the geometry has been added to "c", 
# we can move everything around:
text1.movey(25)
text2.move((5, 30))
text2.rotate(45)
r.movex(-15)

c

(a)

<Figure size 350x420 with 1 Axes>

(b)

Figure 3:How to use predefined components. (a) Code snippet demonstrating how to realize predefined components from the GDSFactory library. Subsequently the created geometry can placed (possibly multiple times) into a parent component. The position and orientation of each reference can be manipulated individually. (b) Resulting layout showing the placed rectangle and two instances of the text label with different positions and orientation.

Functions as Parametric Cells (PCells)

As we have seen in Figure 3 GDSFactory provides a mechanism to handle parametric components, that is components whose concrete geometry depends on a set of parameters. Such parametric components are commonly referred to as parametric cells (PCells). In GDSFactory PCells are defined as python functions decorated with @gf.cell. These functions take parameters as input and have to return a gf.Component instance containing the generated geometry. As such, the PCell definitions inherit the full power and flexibility of the python ecosystem, including control flow and complex arithmetics.

@gf.cell
def mzi_with_bend(
      radius: float = 10.0
    ) -> gf.Component:

    c = gf.Component()
    mzi = c << gf.components.mzi(length_y=35)
    mzi.rotate(90)
    bend = c << (
        gf.components.bend_euler(radius=radius)
    )
    bend.connect("o1", mzi.ports["o2"])

    c.add_port("o2", port=bend.ports["o2"])
    c.add_port("o1", port=mzi.ports["o1"])

    return c

c_mzi_with_bend = mzi_with_bend(radius=12)
c_mzi_with_bend.draw_ports()
c_mzi_with_bend

(a)

<Figure size 350x210 with 1 Axes>

(b)

Figure 4:How to define new parametric cells (PCells). (a) Code snippet showing how to define a new parametric cell from a component factory, i.e. from a factory that returns a gf.Component when called with desired parameters. The particular example is a Mach-Zehnder-Interferometer (MZI) with a bend attached to the output waveguide demonstrating the functionality of directional ports (b) Resulting layout showing the created MZI leading into a bend. The radius of the bend is 15 </micro/meter> as specified when calling the PCell function. The armlengths, bend-radii, waveguide widths etc. are not further specified here, but rather taken from the default settings of the underlying components.

Figure 4 demonstrates how a new PCell can be created. PCells can nest other PCells in order to build arbitrarily complex Components. In this case, we are composing previously defined PCells - a Mach-Zehnder-Interferometer (MZI) and an Euler-bend[1] - exposing the radius of the output bend as an input parameter. Based on this ability to compose PCells, GDSFactory enables large scale hierarchical layouts, where the composition of simpler building blocks allows the designer to reduce complexity by hiding implementation details behind layers of abstraction.

(Automatic) Routing

Integrated photonics relies at its core on waveguides to route light between different devices on chip. In contrast to low-speed electrical interconnects, waveguides require careful matching of the cross-sectional geometry at all interfaces to avoid unwanted reflections and losses. To facilitate connecting different components, GDSFactory leverages directional ports. Each port defines not only its position, but also its orientation and cross-section, enabling reliable mutual alignment when connecting different components. In Figure 4 we have already seen an example using said mechanism: Connecting port o1 of the bend to o2 of the MZI ensures their correct relative placement, respecting the orientation of the optical ports. To allow access to the ports of the newly created component, we export the unconnected ports of the nested components. Using c.draw_ports() we can visualize the resulting ports, and using c.pprint_ports() we can print their properties in a human-readable format.

c_mzi_with_bend.pprint_ports()

(a)

Loading...

(b)

Figure 5:Retreiving port information in a human readable form (a) Code snippet reusing the component c_mzi_with_bend previously defined in Figure 4a (b) A table containing the port names, positions, orientations and cross-sections of the component

Manually routing complex circuits quickly becomes cumbersome. To alleviate this burden GDSFactory provides a routing API for (semi-)automatic waveguide routing. Given a declarative description of the desired connections it can create single and bundled waveguide routes, that avoid obstacles and collisions between the bundled waveguides. A minimal demonstration of automatically routing a single connection is shown in Figure 6.

c = gf.Component()
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((100, 40))

# Automatically route a waveguide 
# between the two MMIs
route = gf.routing.route_single(
    c,
    port1=mmi1.ports["o2"],
    port2=mmi2.ports["o1"],
    cross_section=gf.cross_section.strip,
)
c

(a)

<Figure size 350x210 with 1 Axes>

(b)

Figure 6:Automatic manhattan routing for a single waveguide connection (a) Code snippet demonstrating how to route a waveguide between two ports (b) Resulting layout showing the created 1x2 multi mode interferometers (MMI) with an automatically routed waveguides the two.

YAML based Composition

While defining PCells in python is very powerfull, one commonly simply desires to combine pre-existing building block into circuits (as we have already done in and ). In these cases it can be beneficial to describe the desired circuit in a netlist-like format. For this purpose GDSFactory establishes a YAML based representation. The specification equivalent to is shown in .

name: mask_compact

instances:
  rings:
    # `pack_doe` is a special function that creates a Design of Experiments array.
    component: pack_doe
    settings:
      
      # It will create ring resonators with these different radii and coupling lengths.
      doe: ring_single
      max_size : [1500, null]
      settings:
        radius: [20, 30, 40, 50, 60]
        length_x: [1, 2, 3, 4, 5, 6]

      # This tells the function to generate all possible combinations of the specified radius and length_x values.
      do_permutations: True
      function:

        # After each unique ring is created, the add_fiber_array function is applied to it, adding grating couplers for testing.
        function: add_fiber_array
        settings:
            fanout_length: 200

  mzis:
    component: pack_doe_grid
    settings:
      doe: mzi
      settings:
        delta_length: [10, 100]
      do_permutations: True
      spacing: [10, 10]
      function: add_fiber_array

placements:
  rings:
    xmin: 50

  mzis:
    xmin: rings,east

(a)

<Figure size 350x800 with 1 Axes>

(b)

Declaratively describing the desired circuit in YAML format. (a) YAML that is fed to gf.read.from_yaml() to create a circuit layout. The example creates a design of experiments (DoE) array of ring resonators with varying radii and coupling lengths, as well as a grid of Mach-Zehnder Interferometers (MZIs) with different path length differences. Each component is automatically equipped with fiber arrays for testing. (b) Resulting layout showing the created DoE array of ring resonators on the left and the MZI grid on the right.

Simulations: Layout as Single Source of Truth

The GDSFactory ecosystem supports multiple solvers to simulate the physical behavior of the created design. These cover several aspects, like the optical behavior of single components via finite difference time domain simulations, the collective behavior of multiple coupled components via scattering matrix (S-matrix) circuit simulators, the propagation of thermal transients, electronic properties and more. The single source of truth providing the device geometry, is a key benefit of GDSFactory linking these different simulators together.

Continuous Integration: Regression Tests

Process Development Kits (PDKs)

Verification

Measurement and Validation

Conclusion

The paper has highlighted the key features and functionalities of GDSFactory for hardware design. By leveraging the power of python, GDSFactory empowers designers with a familiar and flexible programming language widely used in machine learning, scientific computing, and engineering. This enables seamless integration with existing software ecosystems and promotes code reuse and collaboration.

The verification and validation capabilities of GDSFactory play a crucial role in ensuring the manufacturability and functionality of the designed chips. From functional verification to design for robustness and manufacturing, GDSFactory offers tools and methods to design chips, detect potential issues, and optimize performance.

Furthermore, GDSFactory provides an interactive schematic capture feature that enhances the design process and facilitates the alignment between design intent and the produced layout. With support for SPICE and YAML, designers have the flexibility to define and modify schematics in a user-friendly manner, either visually or programmatically.

The ability to define test sequences and measurement parameters within GDSFactory streamlines the post-fabrication testing process. By establishing a clear measurement and data analysis pipeline, designers can evaluate the fabricated components against the design specifications, ensuring the delivery of known good dies. In conclusion, GDSFactory is a comprehensive and extensible design automation tool that empowers designers to efficiently develop integrated circuits and components. Its python-driven workflow, combined with its integration capabilities, verification tools, schematic capture feature, and test sequence definition, provides a powerful platform for turning chip designs into validated products.

Acknowledgments

We would like to acknowledge all other contributors to the GDSFactory project who at the time of writing are: Simon Bilodeau (Google), Niko Savola (Google, Aalto University), Marc de Cea Falco (Google), Helge Gehring (Google), Yannick Augenstein (FlexCompute), Ryan Camacho (BYU), Sequoia Ploeg (BYU), Prof. Lukas Chrostowski (UBC), Erman Timurdogan (Lumentum), Damien Bonneau (PsiQuantum), Alec Hammond (Meta), Thomas Dorch (HyperLight), Alex Sludds (Lightmatter), Igal Bayn (Quantum Transistors), Skandan Chandrasekar (UWaterloo), Tim Ansell, Ardavan Oskoii (Meta), Bradley Snyder (Superlight) and Amro Tork (Mabrains). Erman Timurdogan contributed the GDSFactory design, verification and validation cycles figure and Dario Quintero contributed the GDSFactory microservices architecture figure.

We would also like to acknowledge the foundational work of other open-source developers: Adam McCaughan (PHIDL), Juan Sanchez (DEVSIM), Matthias Köfferlein (Klayout), Ardavan Oskooi, Alec Hammond, Stephen Johnson (MEEP), Jesse Lu (FDTDZ), Dan Fritchman (VLSIR), Dario Quintero (PIEL), Floris Laporte (SAX, MEOW).

Data Availability

The source code of GDSFactory is available publicly on github under MIT license. The demonstrations presented here can be run interactively in the online version of this paper. Similarly an extensive documentation including further examples and a full overview of the API is available.

Footnotes
  1. An Euler-bend is a bend whose curvature changes linearly along its length, minimizing losses due to mode mismatch.