Sample circuit layout with lnoi400 PDK#

Here we provide an example of PIC layout with the lnoi400 PDK. We start by choosing a die floorplan compatible with a submission for an LXT MPW run, then place some edge couplers for I/O at the right locations on the chip frame. Finally we create a circuit cell with an evanescently-coupled ring resonator and connect it with the input and output edge couplers.

from functools import partial
from pathlib import Path
import numpy as np
import lnoi400 # noqa: F401  – activates the PDK
import gdsfactory as gf

Choose the chip format and display the outline#

@gf.cell
def chip_frame():
    c = gf.get_component("chip_frame", size=(10_000, 5000), center=(0, 0))
    return c

chip_layout = chip_frame()
chip_layout
../_images/0458c77a1ecf65684711dfa80d12180fa172c38e7e00f8baa0052337b8d6ea46.png

Get the circuit building blocks#

input_ext = 10.0
double_taper = gf.get_component("double_linear_inverse_taper",
                                input_ext=input_ext,
                                )

coupler_gap = 0.6
ring_radius = 100.0
ring_width = 2.0
wg_width = 1.0

ring = gf.components.ring(
    layer="LN_RIDGE",
    radius=ring_radius,
    width=ring_width,
    angle_resolution=0.15,
)

dc_wg = gf.components.straight(
    length = ring_radius * 2,
    cross_section="xs_rwg1000",
)

@gf.cell
def ring_with_coupler(
    ring=ring,
    bus=dc_wg,
    gap=coupler_gap,
) -> gf.Component:

    c = gf.Component()
    ring_ref = c << ring
    coupler_ref = c << bus
    coupler_ref.drotate(90)
    coupler_ref.dcenter = [
        ring_ref.dxmax + gap + 0.5 * wg_width, 0.0
        ]
    c.add_ports(coupler_ref.ports)
    c.flatten()
    return c

coupled_ring = ring_with_coupler()
coupled_ring
../_images/d9a630d52c1a736b0a9a5748858f1f627a4fe732abf57026616adb76c5f76e53.png

Circuit assembly#

Positioning of the I/O edge couplers. Note that we place the edge couplers such that the input port is slightly sticking outside of the chip footprint on the CHIP_EXCLUSION_ZONE layer. This is the correct placement for die singulation.

x_in = chip_layout.dxmin + 1000.0
in_loc = np.array([x_in, chip_layout.dymax])
out_loc = np.array([x_in + 2.5 * ring_radius, chip_layout.dymin])

ec_in = gf.Component()
ec_ref = ec_in << double_taper
ec_ref.drotate(-90.0)
ec_ref.dmove(
    ec_ref.ports["o1"].dcenter, in_loc + [0.0, 0.5 * input_ext]
)
ec_in.add_ports(ec_ref.ports)

ec_out = gf.Component()
ec_ref = ec_out << double_taper
ec_ref.drotate(90.0)
ec_ref.dmove(
    ec_ref.ports["o1"].dcenter, out_loc - [0.0, 0.5 * input_ext]
)
ec_out.add_ports(ec_ref.ports)

ecs = {
    "in": ec_in,
    "out": ec_out,
}

Connecting the ring with I/O

routing_roc = 75.0

@gf.cell
def ring_pass_circuit(
    coupled_ring = coupled_ring,
    ecs = ecs,
) -> gf.Component:

    c = gf.Component()
    ring_ref = c << coupled_ring
    ring_ref.dmovex(- ring_ref.ports["o1"].dcenter[0] + ecs["out"].ports["o1"].dcenter[0])

    # Bend spec

    routing_bend = partial(
        gf.components.bend_euler,
        radius=routing_roc,
        with_arc_floorplan=True,
    )

    # Routing to I/O

    [c << ec for ec in ecs.values()]

    gf.routing.route_single(
        c,
        ring_ref.ports["o2"],
        ecs["in"].ports["o2"],
        start_straight_length=5.0,
        end_straight_length=5.0,
        cross_section="xs_rwg1000",
        bend=routing_bend,
        straight="straight_rwg1000",
    )

    gf.routing.route_single(
        c,
        ring_ref.ports["o1"],
        ecs["out"].ports["o2"],
        start_straight_length=5.0,
        end_straight_length=5.0,
        cross_section="xs_rwg1000",
        bend=routing_bend,
        straight="straight_rwg1000",
    )

    c.flatten()
    c.add_port(name="o1", port=ecs["in"].ports["o1"])
    c.add_port(name="o2", port=ecs["out"].ports["o1"])

    return c

circuit = ring_pass_circuit()
circuit
/tmp/ipykernel_2600/3545328644.py:25: DeprecationWarning: route_single is less flexible and will be removed in GDSFactory10. Please use route_bundle instead.
  gf.routing.route_single(
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/site-packages/gdsfactory/components/bends/bend_euler.py:110: UserWarning: {'width': 1.0} ignored for cross_section 'xs_rwg1000'
  x = gf.get_cross_section(cross_section, width=width or x.width)
/tmp/ipykernel_2600/3545328644.py:36: DeprecationWarning: route_single is less flexible and will be removed in GDSFactory10. Please use route_bundle instead.
  gf.routing.route_single(
../_images/aaed497f042f2cdd8baa98a3f0356ea846d814a8dc4177f08f54a7d5526ed53c.png

Assemble on the die outline

@gf.cell
def die_assembled(
    chip_layout = chip_layout,
    circuit = circuit,
) -> gf.Component:
    c = gf.Component()
    c << chip_layout
    c << circuit
    c.add_ports(circuit.ports)
    return c

die = die_assembled()
die.plot()
_ = die.write_gds(gdsdir=Path.cwd())
../_images/e4b8a8239ee1b543010549b0370c79ab969f85630b46e6018d991e12a75b7484.png

Recap the port positions for testing

die.pprint_ports()
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name  width  orientation  layer          center              port_type ┃
┡━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ o1   │ 0.25  │ 90.0        │ LN_SLAB (3/0) │ (-4050.0, 2530.0)  │ optical   │
│ o2   │ 0.25  │ 270.0       │ LN_SLAB (3/0) │ (-3800.0, -2530.0) │ optical   │
└──────┴───────┴─────────────┴───────────────┴────────────────────┴───────────┘

Clear the gdsfactory cache#

gf.clear_cache()