Examples

Microring Modulator

A carrier-injection silicon microring modulator: a p-i-n diode across a 5 µm rib ring, wired to ground-signal-ground pads.

Compiling…
RingModulator from laylight/ring_modulator.lyl, compiled in your browserOpen in playground

Silicon microring modulators change the refractive index of the ring by moving free carriers in and out of it: injecting carriers lowers the index and shifts the ring's resonance, so light at a wavelength on the resonance edge is switched on and off. In a carrier-injection modulator the ring waveguide is the intrinsic region of a p-i-n diode.

The example follows the injection rings of Xu et al.: a rib ring of 5 µm radius, 450 nm wide on a thin slab, 200 nm from a straight bus. The p++ region is a disk inside the ring and the n++ region surrounds it, both 300 nm from the waveguide edges; the n++ region stays open along the bus, so the diode nearly encloses the ring. A contact sits on the p++ disk and two on the n++ region; metal lines lead from them to a row of ground-signal-ground probe pads.

References

  • Q. Xu et al., "12.5 Gbit/s carrier-injection-based silicon micro-ring silicon modulators," Optics Express 15(2), 430–436 (2007). doi:10.1364/OE.15.000430
  • Q. Xu et al., "Micrometre-scale silicon electro-optic modulator," Nature 435, 325–327 (2005). doi:10.1038/nature03569

Parameters

ParameterValueSource
Ring radius5 µmXu 2007, Sec. 2
Waveguides450 nm rib on a thin slabXu 2007, Sec. 2
Gap to the bus200 nmXu 2007, Sec. 2
Doped regions to the waveguide edges300 nmXu 2007, Sec. 2, Fig. 1
Slab20 µm squareassumed
Contacts, metal lines2 µm contacts, 3 µm linesassumed
Pads80 µm, 100 µm pitchassumed

How it is written

The doped regions are geometry derived from the waveguides. The p++ disk is a circle whose radius is computed from the ring's. The n++ region is one boolean expression on its own layer: the slab, minus a disk around the ring, minus the bus grown by the 300 nm doping gap, slab - keepout - (bus + doping_gap). The operands stay on their own layers, so the bus is still drawn in silicon. gdstk and gdsfactory write the same subtraction with gdstk.boolean and KLayout regions.

Implementations

ImplementationLines of codeTokensLayout
Laylight23282reference
gdsfactory24 (1.04×)383 (1.36×)same within 3 nm
gdstk26 (1.13×)444 (1.57×)same within 3 nm
KLayout35 (1.52×)545 (1.93×)same within 2 nm
laylight/ring_modulator.lyl
import "tech.lyl"

# A carrier-injection microring modulator after Xu et al., Opt. Express 2007: a
# p-i-n diode across a rib ring on a 50 nm slab, p++ inside the ring and n++
# outside it and beyond the bus, `doping_gap` from the waveguides. The square
# slab, `slab_size` wide and crossed by the bus, and the electrodes are assumed:
# contacts on the p++ disk and on either side of the n++ region, with metal
# lines to ground-signal-ground pads.
cell RingModulator(
    radius = 5um, width = 450nm, gap = 200nm, doping_gap = 300nm, slab_size = 20um
) {
    layer si {
        shape ring with radius, width as ring at self.origin
        shape rect with slab_size, width as bus at (0, -radius - width - gap)
    }

    layer slab { shape rect with slab_size, slab_size as slab at self.origin }

    layer ppp {
        shape circle with radius - width / 2 - doping_gap as p_region at self.origin
    }

    layer npp {
        shape circle with radius + width / 2 + doping_gap as keepout
        shape slab - keepout - (bus + doping_gap) as n_region
    }

    layer contact {
        for x in [-radius - 3um, 0, radius + 3um] {
            shape rect with 2um, 2um as c at (x, 0)
        }
    }

    # Lines with square ends that cover the contacts, and 80 um pads.
    layer metal {
        shape path with [(0, 0), (0, 60um)], 3um, ends = "square" as signal
        place signal at self.origin
        for x in [-radius - 3um, radius + 3um] {
            define px = sign(x) * 100um
            shape path with [(x, 0), (px, 0), (px, 60um)], 3um, ends = "square" as line
            place line at self.origin
        }
        for x in [-100um, 0, 100um] { shape rect with 80um, 80um as pad at (x, 60um) }
    }
}
gdsfactory/ring_modulator.py
"""A carrier-injection microring modulator, as laylight/ring_modulator.lyl."""

import gdsfactory as gf

from tech import CONTACT, METAL, NPP, PPP, SI, SLAB


@gf.cell
def ring_modulator(radius=5, width=0.45, gap=0.2, doping_gap=0.3, slab_size=20):
    """A carrier-injection microring modulator: a p-i-n diode across a 5 um rib
    ring, p++ inside the ring and n++ outside it and beyond the bus, after Xu et
    al., Opt. Express 2007. The slab size and the electrodes are assumed:
    contacts on the p++ disk and on either side of the n++ region, with metal
    lines to ground-signal-ground pads."""
    c = gf.Component()
    c << gf.components.ring(radius, width, layer=SI)
    bus = c << gf.components.rectangle((slab_size, width), SI, centered=True)
    bus.dmovey(-radius - width - gap)
    c << gf.components.rectangle((slab_size, slab_size), SLAB, centered=True)
    c << gf.components.circle(radius - width / 2 - doping_gap, layer=PPP)
    # n++ fills the slab but for doping_gap around the waveguides and the ring.
    keepout = gf.components.circle(radius + width / 2 + doping_gap, layer=NPP)
    margin = c.get_region(SI).sized(gf.kcl.to_dbu(doping_gap))
    c.add_polygon(c.get_region(SLAB) - keepout.get_region(NPP) - margin, layer=NPP)
    for x in [-radius - 3, 0, radius + 3]:
        (c << gf.components.rectangle((2, 2), CONTACT, centered=True)).dmovex(x)
    # Lines that start 1.5 um before the contacts to cover them, and 80 um pads.
    lines = [[(0, -1.5), (0, 60)]]
    for side in [-1, 1]:
        x, px = side * (radius + 1.5), side * 100
        lines.append([(x, 0), (px, 0), (px, 60)])
    for line in lines:
        c << gf.Path(line).extrude(width=3, layer=METAL)
    for x in [-100, 0, 100]:
        (c << gf.components.pad((80, 80), layer=METAL)).dmove((x, 60))
    return c


if __name__ == "__main__":
    import sys

    ring_modulator().write_gds(sys.argv[1])
gdstk/ring_modulator.py
"""A carrier-injection microring modulator, as laylight/ring_modulator.lyl."""

import gdstk

from tech import CONTACT, METAL, NPP, PPP, SI, SLAB


def ring_modulator(radius=5, width=0.45, gap=0.2, doping_gap=0.3, slab_size=20):
    """A carrier-injection microring modulator: a p-i-n diode across a 5 um rib
    ring, p++ inside the ring and n++ outside it and beyond the bus, after Xu et
    al., Opt. Express 2007. The slab size and the electrodes are assumed:
    contacts on the p++ disk and on either side of the n++ region, with metal
    lines to ground-signal-ground pads."""
    cell = gdstk.Cell("RingModulator")
    r1, r2 = radius - width / 2, radius + width / 2
    s, y = slab_size / 2, -radius - width - gap  # y: the bus center
    cell.add(gdstk.ellipse((0, 0), r2, inner_radius=r1, tolerance=1e-3, **SI))
    cell.add(gdstk.rectangle((-s, y - width / 2), (s, y + width / 2), **SI))
    slab = gdstk.rectangle((-s, -s), (s, s), **SLAB)
    cell.add(slab)
    cell.add(gdstk.ellipse((0, 0), r1 - doping_gap, tolerance=1e-3, **PPP))
    keepout = gdstk.ellipse((0, 0), r2 + doping_gap, tolerance=1e-3)
    d = width / 2 + doping_gap
    band = gdstk.rectangle((-s, y - d), (s, y + d))
    cell.add(*gdstk.boolean(slab, [keepout, band], "not", **NPP))
    for x in [-radius - 3, 0, radius + 3]:
        cell.add(gdstk.rectangle((x - 1, -1), (x + 1, 1), **CONTACT))
    # Lines with square ends that cover the contacts, and 80 um pads.
    lines = [[(0, 0), (0, 60)]]
    for side in [-1, 1]:
        x, px = side * (radius + 3), side * 100
        lines.append([(x, 0), (px, 0), (px, 60)])
    for line in lines:
        cell.add(gdstk.FlexPath(line, 3, ends="extended", **METAL))
    for x in [-100, 0, 100]:
        cell.add(gdstk.rectangle((x - 40, 20), (x + 40, 100), **METAL))
    return cell


if __name__ == "__main__":
    import sys

    top = ring_modulator()
    lib = gdstk.Library()
    lib.add(top, *top.dependencies(True))
    lib.write_gds(sys.argv[1])
klayout/ring_modulator.py
"""A carrier-injection microring modulator, as laylight/ring_modulator.lyl."""

import klayout.db as kdb
import numpy as np

from tech import CONTACT, METAL, NPP, PPP, SI, SLAB, layout
from waveguides import polygon


def disk(radius, n=500):
    """A disk of `radius` around the origin, as a region (database units)."""
    a = np.linspace(0, 2 * np.pi, n, endpoint=False)
    points = radius * np.column_stack([np.cos(a), np.sin(a)])
    return kdb.Region(polygon(points).to_itype(layout.dbu))


def box(x1, y1, x2, y2):
    """A rectangle, as a region (database units)."""
    return kdb.Region(kdb.DBox(x1, y1, x2, y2).to_itype(layout.dbu))


def ring_modulator(radius=5, width=0.45, gap=0.2, doping_gap=0.3, slab_size=20):
    """A carrier-injection microring modulator: a p-i-n diode across a 5 um rib
    ring, p++ inside the ring and n++ outside it and beyond the bus, after Xu et
    al., Opt. Express 2007. The slab size and the electrodes are assumed:
    contacts on the p++ disk and on either side of the n++ region, with metal
    lines to ground-signal-ground pads."""
    cell = layout.create_cell("RingModulator")
    r1, r2 = radius - width / 2, radius + width / 2
    s, y = slab_size / 2, -radius - width - gap  # y: the bus center
    si = disk(r2) - disk(r1) + box(-s, y - width / 2, s, y + width / 2)
    cell.shapes(layout.layer(SI)).insert(si)
    slab = box(-s, -s, s, s)
    cell.shapes(layout.layer(SLAB)).insert(slab)
    cell.shapes(layout.layer(PPP)).insert(disk(r1 - doping_gap))
    d = width / 2 + doping_gap
    n = slab - disk(r2 + doping_gap) - box(-s, y - d, s, y + d)
    cell.shapes(layout.layer(NPP)).insert(n)
    contact = cell.shapes(layout.layer(CONTACT))
    for x in [-radius - 3, 0, radius + 3]:
        contact.insert(kdb.DBox(x - 1, -1, x + 1, 1))
    # Lines with square ends that cover the contacts, and 80 um pads.
    metal = cell.shapes(layout.layer(METAL))
    lines = [[(0, 0), (0, 60)]]
    for side in [-1, 1]:
        x, px = side * (radius + 3), side * 100
        lines.append([(x, 0), (px, 0), (px, 60)])
    for line in lines:
        metal.insert(kdb.DPath([kdb.DPoint(*p) for p in line], 3, 1.5, 1.5))
    for x in [-100, 0, 100]:
        metal.insert(kdb.DBox(x - 40, 20, x + 40, 100))
    return cell


if __name__ == "__main__":
    import sys

    ring_modulator()
    layout.write(sys.argv[1])

On this page