LVS Examples
This notebook walks through the complete Livewire → Mosaic → GDS → LVS workflow using four example circuits from the gpdk demo project. Each example highlights a different class of LVS finding so you can recognise them in your own designs.
The circuits are already built — pre-compiled GDS files live in build/gds/ alongside the .gsch schematics in gpdk/samples/.
Workflow overview
A circuit travels through four stages before LVS runs:
- Livewire — draw your schematic in the GDS+ editor. Each component instance and wire connection is saved to a
.gschfile (flat JSON netlist). - Mosaic — the Mosaic pre-layout view places components from the schematic on a canvas. Arrange instances and inspect connections before physical geometry is committed.
- Build to GDS — GDSFactory+ compiles the
.gschinto a GDS layout: routing runs, geometry is generated, and a.gdsfile is exported. - LVS check — elvis extracts electrical connectivity from the GDS polygons and compares it against the schematic netlist. Mismatches are written to a
.lyrdbreport and displayed in the viewer.
Setup
import xml.etree.ElementTree as ET
from pathlib import Path
import kfactory as kf
from gdsfactoryplus.check.lvs import check_lvs
DEMO = Path("/path/to/photonics-full-demo3") # adjust to your local checkout
Example 1: Passing LVS (lvs_pass.gsch)
The schematic defines four pad components on the M3 layer — pad1, pad2, pad3, and pad4 — connected in two pairs:
- wire
W1connectspad1.e4 ↔ pad2.e2 - wire
W2connectspad3.e4 ↔ pad4.e2
The GDS layout correctly routes both wires, so LVS should report zero findings.
View the layout in kweb
kcl = kf.KCLayout("lvs_pass")
kcl.read(str(DEMO / "build/gds/lvs_pass.gds"), register_cells=False)
top = kcl[kcl.layout.top_cell().cell_index()]
top.show()
Run LVS
xml = check_lvs(
gds_path=str(DEMO / "build/gds/lvs_pass.gds"),
schematic_path=str(DEMO / "gpdk/samples/lvs_pass.gsch"),
_pdk_qualified_name="gpdk",
project_root=str(DEMO),
)
root = ET.fromstring(xml)
items = list(root.iter("item"))
print(f"Findings: {len(items)}") # expected: 0
A clean LVS produces an empty report — no items, no categories.
View the lyrdb in kweb
lyrdb_path = DEMO / "build/lyrdb/lvs_pass_example.lyrdb"
lyrdb_path.write_text(xml, encoding="utf-8")
# Open build/gds/lvs_pass.gds in the GDS viewer, then load the lyrdb file
# from File > Load Marker Database to overlay findings on the layout.
Example 2: Instance missing in layout (lvs_fail.gsch)
The schematic has three pad instances (pad_1, pad_2, pad3) plus an extra component X1 connected to pad_2 via wire W2. The GDS layout was built without placing X1, so LVS detects:
LVS.instance.missing_in_layout—X1is in the schematic but absent from the GDSLVS.open— the dangling wireW2has no destination in the layout
View the layout in kweb
kcl = kf.KCLayout("lvs_fail")
kcl.read(str(DEMO / "build/gds/lvs_fail.gds"), register_cells=False)
kcl[kcl.layout.top_cell().cell_index()].show()
Run LVS
xml = check_lvs(
gds_path=str(DEMO / "build/gds/lvs_fail.gds"),
schematic_path=str(DEMO / "gpdk/samples/lvs_fail.gsch"),
_pdk_qualified_name="gpdk",
project_root=str(DEMO),
)
root = ET.fromstring(xml)
for item in root.iter("item"):
category = item.findtext("category")
description = item.findtext("description") or item.findtext("message") or ""
print(f"[{category}] {description}")
What to look for
Findings with LVS.instance.missing_in_layout identify components that the schematic declares but the GDS does not contain. The corresponding LVS.open findings mark the ports that were wired to the missing instance — they now dangle with no physical connection.
Example 3: Short circuit (short_pad.gsch)
The short_pad circuit places pad instances whose M3 metal polygons overlap in the layout, physically connecting two nets that the schematic declares as independent. Elvis detects the shared polygon and reports a LVS.short finding.
View the layout in kweb
kcl = kf.KCLayout("short_pad")
kcl.read(str(DEMO / "build/gds/short_pad.gds"), register_cells=False)
kcl[kcl.layout.top_cell().cell_index()].show()
Run LVS
xml = check_lvs(
gds_path=str(DEMO / "build/gds/short_pad.gds"),
schematic_path=str(DEMO / "gpdk/short_pad.gsch"),
_pdk_qualified_name="gpdk",
project_root=str(DEMO),
)
root = ET.fromstring(xml)
for item in root.iter("item"):
category = item.findtext("category")
description = item.findtext("description") or item.findtext("message") or ""
print(f"[{category}] {description}")
What to look for
LVS.short findings include the layer on which the overlap occurs (M3 in this case) and the two net names that were incorrectly merged. In the GDS viewer, crossing-marker overlays pinpoint the polygon region responsible for the short.
How shorts happen in practice:
- Two wire segments routed too close — their expanded metal polygons touch
- A fill polygon added during a DRC fix that bridges two independent nets
- A via landing on the wrong net because of a placement error
- Cross-layer shorts: a via connects M1 and M2 pads whose nets should stay independent (detected via the PDK
connectivitytable)
Example 4: Open nets and mismatched connections (lvs_fail2.gsch)
lvs_fail2 has four pads (pad1–pad4) with two wires defined in the schematic (W1: pad1.e4 ↔ pad2.e2, W2: pad3.e4 ↔ pad4.e2). The GDS layout routes the wires along a different path that doesn't close the connections correctly, producing open-net findings on both pairs.
View the layout in kweb
kcl = kf.KCLayout("lvs_fail2")
kcl.read(str(DEMO / "build/gds/lvs_fail2.gds"), register_cells=False)
kcl[kcl.layout.top_cell().cell_index()].show()
Run LVS
xml = check_lvs(
gds_path=str(DEMO / "build/gds/lvs_fail2.gds"),
schematic_path=str(DEMO / "gpdk/samples/lvs_fail2.gsch"),
_pdk_qualified_name="gpdk",
project_root=str(DEMO),
)
root = ET.fromstring(xml)
findings = list(root.iter("item"))
print(f"Total findings: {len(findings)}")
from collections import Counter
counts = Counter(item.findtext("category") for item in findings)
for category, n in sorted(counts.items()):
print(f" {category}: {n}")
What to look for
LVS.net.missing_in_layout means the schematic declares a connection that cannot be traced through the GDS polygons — the physical wire is missing or broken. LVS.open marks ports that are wired in the schematic but have no physical path to any other port.
When multiple findings appear together, resolve them in order:
- Fix instance-level mismatches first (
LVS.instance.*) — a missing component causes downstream net and port errors that disappear once the instance is placed. - Fix missing nets next (
LVS.net.missing_in_layout) — usually a missing wire segment or a routing gap. - Fix opens last (
LVS.open) — dangling ports often follow from the fixes above.
Interpreting the lyrdb in the GDS viewer
Write any lyrdb to disk and open it alongside the layout to see annotation overlays:
# After running check_lvs, write the report to disk
lyrdb_path = DEMO / "build/lyrdb/my_run.lyrdb"
lyrdb_path.write_text(xml, encoding="utf-8")
# In the GDS viewer: File > Load Marker Database, then select the .lyrdb file.
# Findings appear as coloured markers overlaid on the layout geometry.
# Click any marker in the DRC Results panel to zoom to the affected region.
The .lyrdb format is KLayout's native marker database — the GDS+ viewer renders it with the same overlay engine as KLayout's built-in DRC results panel.