Verification · Getting started
LVS: layout versus schematic, and how to read the report
Schematic and layout describe one circuit in two languages. How LVS extracts nets from polygons, a LayoutEditor script that checks a layout against a SPICE netlist and against a schematic, and how to read opens, shorts, and device mismatches.
Last edited: October 19, 2026

A schematic and a layout describe the same circuit in two languages. The schematic is connectivity: symbols, pins, and named nets. The layout is space: polygons on layers that a fab turns into films. Neither contains the other's specialty. The schematic says nothing about which side of a block a device sits on, and the layout carries no net names unless someone adds text labels. Layout versus schematic (LVS) is the check that proves the two still describe one circuit.
The picture at the top of this page shows both languages for one circuit: two resistors in series, with the nets IN, MID and OUT. The upper half is the schematic, drawn in LayoutEditor's schematic editor. The lower half is the layout, drawn in LayoutEditor too: two resistor cells, each with a metal pad on both ends and a body in between, and three metal straps that carry the net names. Every claim on the schematic has to be found again in the metal, and this page runs that check with real commands and shows what it prints.
Two netlists, one comparison
LVS does three things.
- Extract devices. The extractor has to know what the devices are. In a production deck, Boolean operations on the drawing layers find device bodies: active inside the n-well and under p+ implant, crossed by poly, is a PMOS gate, and the active left over on either side is source and drain. In the example below, a device is simply a named cell reference.
- Extract connectivity. Shapes on the same layer that touch or overlap become one net. Shapes on different layers connect only where a cut layer, a contact or a via, overlaps both. The result is a netlist taken from geometry.
- Compare. The extracted netlist is matched against the reference netlist written from the schematic, usually SPICE. Same devices, same device parameters within tolerance, same pins tied together, nothing extra, nothing missing.
LVS does not grade the circuit design. It grades whether you built the circuit you wrote down. It is also not a geometry check: DRC can pass a cell that is missing a contact, because every shape that is there is wide enough and far enough from its neighbors. LVS fails it.
Devices here are named cell references
To keep every step visible, the example uses the simplest kind of device: a cell you place. The resistor is one cell, RES, with a metal pad at each end, a body between the pads that is not metal, and the pad labels A and B. R1 and R2 are two instances of that cell (see cells and instances). LVS needs to know which reference is which device, so each reference carries its name as a GDS property (number 10). Nothing is recognized from diffusion or poly in this example, and no transistor is extracted. The device is whatever the cell reference says it is, and the extractor takes its pins from the labels on the cell.
The net names come from text labels on the metal straps: IN, MID and OUT. A label names the net of the shape it sits on (see nets, pins and labels). The layout script is short enough to read in one piece:
def build_layout(mid_um=4, name_devices=True):
layout = project.newLayout(); d = layout.drawing
res = d.currentCell; res.cellName = "RES" # the device: one cell, two pads
res.addBox(0, 0, 2*um, 6*um, M1); res.addBox(8*um, 0, 2*um, 6*um, M1)
res.addBox(2*um, 3*um//2, 6*um, 3*um, BODY) # body, not a conductor
res.addText(M1, point(1*um, 3*um), "A") # pin labels
res.addText(M1, point(9*um, 3*um), "B")
d.newCell(); top = d.currentCell; top.cellName = "TOP"
r1 = top.addCellref(res, point(0, 0)); r2 = top.addCellref(res, point(14*um, 0))
if name_devices:
r1.setPropertyString(10, "R1"); r2.setPropertyString(10, "R2") # property 10 = device name
top.addBox(-4*um, 2*um, 4*um, 2*um, M1) # IN strap
top.addBox(10*um, 2*um, mid_um*um, 2*um, M1) # MID strap, 4 um joins R1 and R2
top.addBox(24*um, 2*um, 4*um, 2*um, M1) # OUT strap
top.addText(M1, point(-3*um, 3*um), "IN"); top.addText(M1, point(11*um, 3*um), "MID")
top.addText(M1, point(26*um, 3*um), "OUT") # net labels
return layout
The picture at the top of this page shows this layout under a schematic of the same circuit. The slate rectangles are metal, the tan rectangles are the resistor bodies, which are drawn on their own layer and conduct nothing. Both resistors reach the middle strap through their B pad and their A pad, so MID is one net with two pins on it.
Comparing against a netlist
The reference is a SPICE file. It describes the cell RES with its two pins and the cell TOP with its three nets, in the same names the layout uses:
* two resistor cells in series
.subckt RES A B
.ends RES
.subckt TOP IN MID OUT
XR1 IN MID RES
XR2 MID OUT RES
.ends TOP
The comparison has four calls: declare which layer conducts, build the connection, extract the netlist from the layout, and compare it with the loaded file:
def compare_netlist(layout, netlist_file):
nt = layout.netlistTool
nt.technologyLayerRemoveAll(); nt.technologyLayerAdd(M1, 2) # layer 1 conducts
nt.buildConnect(); nt.extractNetList() # what touches, then the netlist
nt.netlistLoad(netlist_file) # SPICE, .subckt named like the cell
nt.layoutVersusNetlist()
r = nt.getLVSResults()
return [r.at(i) for i in range(r.size())]
technologyLayerAdd(1, 2) says that layer 1 is a conductor (2 means conductor, 1 would mean via). buildConnect() finds which shapes touch, and extractNetList() turns the result into a netlist. netlistLoad() reads the SPICE file, whose TOP subcircuit must be named like the layout's top cell. layoutVersusNetlist() runs the comparison and getLVSResults() returns its findings as text lines. Run on the layout above, with the good netlist:
python3 lvs_two_resistors.py rr_good.sp
No errors.
One line, and it is the line you want. The script exits normally in every case, so a flow that wants a pass/fail has to look for anything other than "No errors.".
When the comparison fails
A report that has never failed has not been tested, so the test script feeds the same layout three other ways of being wrong. The first is a netlist that wires R2 between IN and OUT instead of between MID and OUT (XR2 IN OUT RES in place of XR2 MID OUT RES):
misconnection R2/A
The second is a netlist with a third resistor the layout never drew (XR3 OUT IN RES added):
missing device R3
The word "missing" is relative to the layout: the netlist has a device that the layout lacks. The third is a defect in the layout instead of the netlist. The MID strap is drawn 2 µm long instead of 4 µm, so it no longer reaches R2. This is the case shown in the second picture.

The gap is 2 µm wide and easy to miss at normal zoom, but LVS finds it at once and names the pin that lost its net:
connection missing R2/A
A stray strip of metal laid across R1, from its A pad to its B pad, joins IN and MID into one net. The report does not say "short". It lists the pins that lost their nets and a label that has no net left:
connection missing R1/B
connection missing R2/A
external node MID not labeled
The last wrong layout forgets to name the devices. Without property 10 on the references, LVS has two instances and no names to match them against:
different device names R1/
double devicename
different device names R2/
Lines can come out in any order from run to run, so the test script compares them as sorted lists. The lines themselves name a device and a pin, never a coordinate: the work is to look at that pin in the layout.
Comparing against a schematic
A netlist file is not the only reference. LayoutEditor can also hold a schematic next to the layout and compare against it directly with layoutVersusSchematic(). The schematic is drawn on a sheet named like the layout's top cell, with two resistors from the component library, wires, and three ports named IN, MID and OUT:
def draw_schematic(layout, rewire=False):
sch = project.getSchematic(layout) # schematic window linked to this layout
sheet = sch.drawing.currentSheet; sheet.sheetName = "TOP" # same name as the layout's top cell
res = components.findComponent("R", "PCB") # library resistor, pins pin1 and pin2
for name, x in (("R1", 0), ("R2", 300)):
sheet.addPlacement(res, point(x, 0), strans()).addParameter("devicename", name)
wire(sheet, (-30, 0), (-100, 0)); sheet.addPort(point(-100, 0), "IN")
wire(sheet, (30, 0), (150, 0)); sheet.addPort(point(150, 0), "MID")
wire(sheet, (330, 0), (400, 0)); sheet.addPort(point(400, 0), "OUT")
if rewire: wire(sheet, (-30, 0), (-30, -80), (270, -80), (270, 0)) # R2.pin1 to IN
else: wire(sheet, (150, 0), (270, 0)) # R2.pin1 to MID
sch.drawing.updateCurrentNetlist()
The schematic is the picture's upper half. It also generates its own names. A resistor placed with the device name R1 produces a device whose cell name is R10-1206-R1, and the layout cell must carry exactly that name, or LVS reports the schematic's device missing and the layout's device additional. So the layout script asks the schematic for its device list and builds one layout cell for each entry, instead of typing the names by hand. The pins of the library resistor are called pin1 and pin2, so the layout cell is labeled pin1 and pin2:
def build_layout(layout, mid_um=4, name_devices=True):
d = layout.drawing; nets = layout.netlistTool.getSchematicNetList("TOP")
cells = []
for i in range(nets.devicesCount()):
dev = nets.getDevice(i) # the schematic names every device
if i: d.newCell()
cell = d.currentCell; cell.cellName = dev.cellname # layout cell = schematic's cell name
cell.addBox(0, 0, 2*um, 6*um, M1); cell.addBox(8*um, 0, 2*um, 6*um, M1)
cell.addBox(2*um, 3*um//2, 6*um, 3*um, BODY)
cell.addText(M1, point(1*um, 3*um), "pin1"); cell.addText(M1, point(9*um, 3*um), "pin2")
cells.append((cell, dev.devicename))
d.newCell(); top = d.currentCell; top.cellName = "TOP"
for cell, name in cells:
ref = top.addCellref(cell, point(14*um*(int(name[1:]) - 1), 0))
if name_devices:
ref.setPropertyString(10, name) # property 10 = device name
top.addBox(-4*um, 2*um, 4*um, 2*um, M1); top.addBox(10*um, 2*um, mid_um*um, 2*um, M1)
top.addBox(24*um, 2*um, 4*um, 2*um, M1)
top.addText(M1, point(-3*um, 3*um), "IN"); top.addText(M1, point(11*um, 3*um), "MID")
top.addText(M1, point(26*um, 3*um), "OUT")
return cells
The comparison itself needs no netlist file, because the schematic is the reference:
def compare_schematic(layout):
nt = layout.netlistTool
nt.technologyLayerRemoveAll(); nt.technologyLayerAdd(M1, 2) # layer 1 conducts
nt.buildConnect(); nt.extractNetList()
nt.layoutVersusSchematic()
r = nt.getLVSResults()
return [r.at(i) for i in range(r.size())]
With a matching schematic it prints No errors. just like the netlist flow. The test script then breaks each side once: it rewires R2's first pin to IN in the schematic, shortens the MID strap in the layout, adds a third resistor to the schematic after the layout was drawn, strips the device names from the references, and gives the layout cells names of its own instead of the schematic's. The reports are:
| Change | Report |
|---|---|
| Schematic wires R2.pin1 to IN | misconnection R2/pin1 |
| Layout MID strap too short | connection missing R2/pin1 |
| Schematic has an extra R3 | missing device R3 |
| References without device names | different device names R1/, double devicename, different device names R2/ |
Layout cells named RES_R1, RES_R2 |
missing device R1, missing device R2, additional device R1, additional device R2 |
The vocabulary is the same as for the netlist file. The last row is the one to remember: a name that differs by a prefix looks like two devices missing and two added.
LayoutEditor's documentation also describes recognizing devices from layers, where MOS transistors, resistors, capacitors and bipolar devices are found from their drawing layers. This page does not use it, and it does not claim a result for it.
A gap is a missing contact
The second picture above is the simplest open: metal that stops short of a pad. The same thing happens in a real device stack when a contact or via is missing. Metal still runs along the device, and at a glance the device looks connected. It is not. Metal that never meets a contact on the active is a shape beside the device, not a pin of it. On the schematic, that end is tied to a named net; in the extracted netlist, the pin has no net. The report shows a connection missing.
The mistake is easy to miss because the contact is the small square and the bar is the shape the eye follows. A contact around 0.17 µm on a side under a metal strap several micrometers wide is the overlap an extractor uses. A contact drawn on the wrong layer number is as missing as one never placed, even if the color map paints it like a contact.
Touches, gaps, and crossings
The two classic failures are mirror images. Picture two devices on one metal path. If the left joint overlaps the device metal and the right joint stops a fraction of a micrometer short, that gap is an open: invisible at normal zoom, but the extractor sees two nets where the schematic has one. Now picture a second path on another metal layer routed up and over the first without landing on a via. It stays a separate net, which is correct. If it dipped onto the first path's layer and touched it, two schematic nets would merge into one: a short.
Rows of devices do not inherit nets from sitting side by side. They connect because shapes touch on one layer, or because a cut joins two layers. Anything else is a picture of a connection.
Reading an LVS report
A mismatch report names devices and pins, not coordinates, so work from names. These are the lines the tests above produced:
- Open. One schematic net appears as two or more extracted nets, so a pin lost its net:
connection missing R2/A. Look for missing contacts or vias, gaps, or a label placed on the wrong shape. - Wrong net. A pin sits on a net the reference does not give it:
misconnection R2/A. Compare what the pin touches with where the schematic wires it. - Short. Two schematic nets appear as one extracted net. In the bridge test the report lists pins that lost their nets and a label without a net:
connection missing R1/B,connection missing R2/A,external node MID not labeled. A short between two labeled supplies (VDD and VSS) can make the whole cell fail at once. - Device mismatch. A device exists on only one side:
missing device R3when the reference has it and the layout does not,additional device R1when the layout has one the reference does not know. A pair of missing and additional lines with nearly equal names usually means a naming difference, as in theRES_R1test. - Names.
different device namesanddouble devicenamemean the references carry no names or the same name twice. Pin names come from labels, so a misspelled label shows up as a pin or net mismatch.
Fix the document that is actually wrong. If the schematic changed and the layout did not follow, the layout is wrong; if a designer patched the layout to make it fit and never updated the sheet, the schematic is. Never rename nets just to silence the report. Rerun LVS after every change, including fixes for DRC errors, because widening a wire to fix a spacing violation can create a short.
Where LVS sits in the flow
DRC answers whether every shape can be built. LVS answers whether the shapes form the intended circuit. A signoff run needs both clean, plus antenna and density checks. Parasitic extraction, which adds wire resistance and capacitance for timing and analog simulation, builds on the same extracted netlist and is only meaningful after LVS passes. The schematic and the layout stay cousins only as long as the comparison runs: every time one changes, LVS is what keeps them speaking the same language.
Production kits ship their decks ready-made. The open SKY130 flow commonly runs extraction in the Magic layout tool and the comparison in netgen; KLayout has an LVS engine of its own with decks for open PDKs. You rarely write the device recognition yourself, but you should be able to read the report when it surprises you.
Continue exploring
-
Design rules and DRC: real rule values and how to read a marker
Design rules are the factory's limits in numbers; DRC is the spellchecker that measures them. SKY130 width, space, and enclosure values, rule flags, how to write and test a .ledrc rule deck by hand, and why deleting a marker fixes nothing.
-
Chip tutorial 5: DEF to GDS and DRC in LayoutEditor
Import the Part-4 sky130 DEF into LayoutEditor, merge real stdcell GDS, write encoder_top.gds, and learn what DRC rules are (with a short LVS note).
-
Who actually reads a layout?
The same drawing is read by your eyes, a geometry checker, a connectivity check, a mask shop, and scripts that never see your colors.
-
Vias and contacts in IC layout: cut layers, enclosure, and via rows
Vias and contacts in a chip layout: the cut layers between floors, SKY130 via size, spacing and enclosure rules, via rows, and what a DRC cannot see.
-
Text on a layout: labels, printed letters, and what the mask shop ignores
A GDS text record is a string on a layer; convert it to polygons before a mask shop must print it. SKY130 68/5 labels stay unchecked; 68/20 outlines are measured.