Thermo-Optic Phase Shifters
A straight TiN heater over a waveguide, and a folded double-spiral waveguide under a meander heater.
Heaters from laylight/heater.lyl, compiled in your browserOpen in playgroundSilicon's refractive index rises with temperature, so a resistive heater on top of a waveguide is the standard phase shifter of silicon photonics. Its efficiency depends on how much of the heat reaches the waveguide: the example shows a plain heater and a compact folded one.
The straight heater follows Jacques et al., who optimized the heater geometry for efficiency and thermal crosstalk: a TiN strip 2.5 µm wide, their optimum, runs 320 µm over a 500 nm strip waveguide. Metal leads cross the ends of the TiN and run to 100 µm bond pads.
The folded heater follows Densmore et al., who folded a 6.3 mm waveguide into a double spiral under one heater, so that a single heater warms a long waveguide and the switching power drops. The spiral is the delay line with Densmore's dimensions: 450 nm waveguides at a 2 µm pitch, 14 turns per spiral, so that 57 to 59 waveguides cross the spiral, inside a 65 µm radius. Over it, the heater is a meander of twelve 10 µm wide stripes at a 12 µm pitch, 1.355 mm long in total, with leads to two pads at its ends.
References
- M. Jacques et al., "Optimization of thermo-optic phase-shifter design and mitigation of thermal crosstalk on the SOI platform," Optics Express 27(8), 10456–10471 (2019). doi:10.1364/OE.27.010456
- A. Densmore et al., "Compact and low power thermo-optic switch using folded silicon waveguides," Optics Express 17(13), 10457 (2009). doi:10.1364/OE.17.010457
Parameters
| Parameter | Value | Source |
|---|---|---|
| Straight heater waveguide | 500 nm | Jacques 2019 |
| TiN strip | 2.5 µm wide (optimum; 7.5 µm as fabricated), 320 µm long | Jacques 2019, Sec. 2 |
| Folded waveguide | 450 nm wide, 2 µm pitch, within a 65 µm radius | Densmore 2009 |
| Turns, center | 14 turns per spiral, S-bend with a minimum radius of 4 µm | derived to match Densmore 2009 |
| Waveguide length | 6.42 mm (paper: 6.3 mm) | from the above |
| Meander heater | 12 stripes, 10 µm wide, 12 µm pitch, 1.355 mm | Densmore 2009 |
| Leads and pads | 10 µm × 50 µm leads, 100 µm pads, on the first metal | assumed |
How it is written
The folded heater does not redraw the spiral: it places the DelayLine cell of the delay-line example with other parameters, and Laylight generates a new cell for them. The meander is a path through two points per stripe, collected in a loop, and the pads are a small cell placed at the meander's start and end anchors. The Python versions reuse the delay line the same way, by importing its function.
Implementations
| Implementation | Lines of code | Tokens | Layout |
|---|---|---|---|
| Laylight | 29 | 326 | reference |
| gdsfactory | 43 (1.48×) | 436 (1.34×) | same within 2 nm |
| gdstk | 40 (1.38×) | 448 (1.37×) | same within 3 nm |
| KLayout | 41 (1.41×) | 497 (1.52×) | same within 2 nm |
import "delay_line.lyl"
# A 100 um bond pad at the end of a 10 um wide, 50 um long lead, which starts at
# the origin, where it overlaps the TiN (the pads are assumed).
cell Pad {
layer metal {
shape rect with 10um, 50um as lead.bottom_center at self.origin
shape rect with 100um, 100um as square.bottom_center at lead.top_center
}
}
# A straight TiN heater over a strip waveguide, after Jacques et al., Opt. Express
# 2019; the leads to the pads cross the ends of the TiN.
# `heater_width` is the paper's optimum (7.5 um as fabricated).
cell StraightHeater(width = 500nm, length = 320um, heater_width = 2.5um) {
layer si { shape rect with length, width as core at self.origin }
layer heater { shape rect with length, heater_width as strip at self.origin }
place Pad at strip.bottom_left offset (5um, 0)
place Pad at strip.bottom_right offset (-5um, 0)
}
# A folded waveguide under a meander heater, after Densmore et al., Opt. Express
# 2009: the delay line's double spiral with 59 waveguides at a 2 um pitch.
# `length` is that of the meander.
cell FoldedHeater(stripes = 12, stripe_width = 10um, pitch = 12um, length = 1355um) {
place DelayLine with {
width = 450nm, gap = 1.55um, turns = 14, inner_radius = 8.3um, p = 0.2
io_width = 450nm, taper_radius = 10um
} at self.origin
layer heater {
define h = (length - (stripes - 1) * pitch) / stripes # length of a stripe
define meander: [point] = []
for i in 0..stripes {
define x = (i - (stripes - 1) / 2) * pitch
define y = pow(-1, i) * h / 2 # up, then down
meander.append((x, -y))
meander.append((x, y))
}
shape path with meander, stripe_width as wire at self.origin
}
# The leads overlap the ends of the meander by 10 um.
place Pad at (wire.start.x, wire.start.y + 10um) rotate 180deg
place Pad at (wire.end.x, wire.end.y + 10um) rotate 180deg
}
# The two phase shifters side by side.
cell Heaters {
place StraightHeater at self.origin
place FoldedHeater at (400um, 0)
}
"""Thermo-optic phase shifters with TiN heaters, as laylight/heater.lyl."""
import gdsfactory as gf
from delay_line import delay_line
from tech import HEATER, METAL, SI
@gf.cell
def pad():
"""A 100 um bond pad at the end of a 10 um wide, 50 um long lead, which
starts at the origin, where it overlaps the TiN (the pads are assumed)."""
c = gf.Component()
(c << gf.components.rectangle((10, 50), layer=METAL)).dmove((-5, 0))
(c << gf.components.rectangle((100, 100), layer=METAL)).dmove((-50, 50))
return c
@gf.cell
def straight_heater(width=0.5, length=320, heater_width=2.5):
"""A straight TiN heater over a strip waveguide, after Jacques et al., Opt.
Express 2019; the leads to the pads cross the ends of the TiN."""
c = gf.Component()
for w, layer in [(width, SI), (heater_width, HEATER)]:
c << gf.components.rectangle((length, w), layer=layer, centered=True)
for x in [5 - length / 2, length / 2 - 5]:
(c << pad()).dmove((x, -heater_width / 2))
return c
@gf.cell
def folded_heater(stripes=12, stripe_width=10, pitch=12, length=1355):
"""A folded waveguide under a meander heater, after Densmore et al., Opt.
Express 2009: the delay line's double spiral with 59 waveguides at a 2 um
pitch."""
c = gf.Component()
c << delay_line(
width=0.45,
gap=1.55,
turns=14,
inner_radius=8.3,
p=0.2,
io_width=0.45,
taper_radius=10,
)
h = (length - (stripes - 1) * pitch) / stripes # length of a stripe
meander = []
for i in range(stripes):
x, y = (i - (stripes - 1) / 2) * pitch, (-1) ** i * h / 2 # up, then down
meander += [(x, -y), (x, y)]
c << gf.Path(meander).extrude(width=stripe_width, layer=HEATER)
# The leads overlap the ends of the meander by 10 um.
for x, y in [meander[0], meander[-1]]:
(c << pad()).drotate(180).dmove((x, y + 10))
return c
@gf.cell
def heaters():
"""The two phase shifters side by side."""
c = gf.Component()
c << straight_heater()
(c << folded_heater()).dmove((400, 0))
return c
if __name__ == "__main__":
import sys
heaters().write_gds(sys.argv[1])
"""Thermo-optic phase shifters with TiN heaters, as laylight/heater.lyl."""
import gdstk
from delay_line import delay_line
from tech import HEATER, METAL, SI
def pad(x, y, direction=1):
"""A 100 um bond pad at the end of a 10 um wide, 50 um long lead that leaves
(x, y) upwards (direction 1) or downwards (-1), where it overlaps the TiN
(the pads are assumed)."""
lead = gdstk.rectangle((x - 5, y), (x + 5, y + 50 * direction), **METAL)
square = gdstk.rectangle(
(x - 50, y + 50 * direction), (x + 50, y + 150 * direction), **METAL
)
return [lead, square]
def straight_heater(width=0.5, length=320, heater_width=2.5):
"""A straight TiN heater over a strip waveguide, after Jacques et al., Opt.
Express 2019; the leads to the pads cross the ends of the TiN."""
cell = gdstk.Cell("StraightHeater")
for w, layer in [(width, SI), (heater_width, HEATER)]:
cell.add(gdstk.rectangle((-length / 2, -w / 2), (length / 2, w / 2), **layer))
for x in [5 - length / 2, length / 2 - 5]:
cell.add(*pad(x, -heater_width / 2))
return cell
def folded_heater(stripes=12, stripe_width=10, pitch=12, length=1355):
"""A folded waveguide under a meander heater, after Densmore et al., Opt.
Express 2009: the delay line's double spiral with 59 waveguides at a 2 um
pitch."""
cell = gdstk.Cell("FoldedHeater")
spiral = delay_line(
width=0.45,
gap=1.55,
turns=14,
inner_radius=8.3,
p=0.2,
io_width=0.45,
taper_radius=10,
)
cell.add(gdstk.Reference(spiral))
h = (length - (stripes - 1) * pitch) / stripes # length of a stripe
meander = []
for i in range(stripes):
x, y = (i - (stripes - 1) / 2) * pitch, (-1) ** i * h / 2 # up, then down
meander += [(x, -y), (x, y)]
cell.add(gdstk.FlexPath(meander, stripe_width, **HEATER))
# The leads overlap the ends of the meander by 10 um.
for x, y in [meander[0], meander[-1]]:
cell.add(*pad(x, y + 10, -1))
return cell
def heaters():
"""The two phase shifters side by side."""
cell = gdstk.Cell("Heaters")
cell.add(gdstk.Reference(straight_heater()))
cell.add(gdstk.Reference(folded_heater(), (400, 0)))
return cell
if __name__ == "__main__":
import sys
top = heaters()
lib = gdstk.Library()
lib.add(top, *top.dependencies(True))
lib.write_gds(sys.argv[1])
"""Thermo-optic phase shifters with TiN heaters, as laylight/heater.lyl."""
import klayout.db as kdb
from delay_line import delay_line
from tech import HEATER, METAL, SI, layout
def pad(shapes, x, y, direction=1):
"""Adds a 100 um bond pad at the end of a 10 um wide, 50 um long lead that
leaves (x, y) upwards (direction 1) or downwards (-1), where it overlaps the
TiN (the pads are assumed)."""
shapes.insert(kdb.DBox(x - 5, y, x + 5, y + 50 * direction))
shapes.insert(kdb.DBox(x - 50, y + 50 * direction, x + 50, y + 150 * direction))
def straight_heater(width=0.5, length=320, heater_width=2.5):
"""A straight TiN heater over a strip waveguide, after Jacques et al., Opt.
Express 2019; the leads to the pads cross the ends of the TiN."""
cell = layout.create_cell("StraightHeater")
for w, layer in [(width, SI), (heater_width, HEATER)]:
cell.shapes(layout.layer(layer)).insert(kdb.DBox(length, w))
metal = cell.shapes(layout.layer(METAL))
for x in [5 - length / 2, length / 2 - 5]:
pad(metal, x, -heater_width / 2)
return cell
def folded_heater(stripes=12, stripe_width=10, pitch=12, length=1355):
"""A folded waveguide under a meander heater, after Densmore et al., Opt.
Express 2009: the delay line's double spiral with 59 waveguides at a 2 um
pitch."""
cell = layout.create_cell("FoldedHeater")
spiral = delay_line(
width=0.45,
gap=1.55,
turns=14,
inner_radius=8.3,
p=0.2,
io_width=0.45,
taper_radius=10,
)
cell.insert(kdb.DCellInstArray(spiral.cell_index(), kdb.DTrans()))
h = (length - (stripes - 1) * pitch) / stripes # length of a stripe
meander = []
for i in range(stripes):
x, y = (i - (stripes - 1) / 2) * pitch, (-1) ** i * h / 2 # up, then down
meander += [kdb.DPoint(x, -y), kdb.DPoint(x, y)]
cell.shapes(layout.layer(HEATER)).insert(kdb.DPath(meander, stripe_width))
# The leads overlap the ends of the meander by 10 um.
metal = cell.shapes(layout.layer(METAL))
for end in [meander[0], meander[-1]]:
pad(metal, end.x, end.y + 10, -1)
return cell
def heaters():
"""The two phase shifters side by side."""
cell = layout.create_cell("Heaters")
for child, x in [(straight_heater(), 0), (folded_heater(), 400)]:
trans = kdb.DTrans(kdb.DVector(x, 0))
cell.insert(kdb.DCellInstArray(child.cell_index(), trans))
return cell
if __name__ == "__main__":
import sys
heaters()
layout.write(sys.argv[1])