Concepts

Efficient DRC

Why design rule checks struggle with curved layouts, and how Laylight checks them on the semantic geometry inside the compiler, pointing at the line of code, in a fraction of KLayout's time.

Design rule checking (DRC) verifies that a layout can be manufactured: features are wide enough, far enough apart, enclosed by the right margin, bent no tighter than the process allows. On Manhattan layouts this is routine. Photonic circuits are different: rings, bends, Euler curves, S-bends, tapers and grating arcs are everywhere, and that is where traditional DRC breaks down. Laylight runs its checks inside the compiler, on the geometry as it was written rather than as it was flattened, and that changes three things: what the checker knows, where it reports, and how fast it is.

Curves have no meaning in GDS

GDSII (and OASIS) store polygons and paths, nothing else. Every arc, bend or spline is written as a polygon whose vertices are snapped to the database grid. With a 1 nm grid and a chord error of at most 1 nm, a single ring of radius 10 µm already has about 450 vertices, and a photonic chip with thousands of rings, bends and routed waveguides carries millions of edges that are only a few tens of nanometers long. The DRC engine never sees an arc of radius 10 µm and width 500 nm; it sees those edges and has to rediscover the geometry from them.

This causes three kinds of trouble:

  • False errors. A chord lies inside its arc, so along a bend the measured width and gap oscillate around the drawn value by up to the chord error. A waveguide drawn exactly at the minimum width, or a coupling gap drawn exactly at the minimum spacing, is reported as a violation at some vertices and not at others. Snapping vertices to the grid adds one-unit jogs and edges a hair off their angle, which come back as notches, acute angles or off-grid edges.
  • Missed errors. The same oscillation works the other way: a gap that is slightly too small can measure as legal between two vertices. And a rule about the curve itself, such as a minimum bend radius, cannot be checked on short straight edges at all without reconstructing the curve.
  • Slow runs. Width and spacing checks compare pairs of edges. Millions of tiny edges make curved layouts the slowest part of verification, and much of the result is noise.

In practice designers waive curved-geometry errors by the hundred, and every waiver is a place where a real violation can hide. The benchmark below measures exactly this: on rings, couplers, bends and rounded corners, between 39% and 99% of a polygon checker's reports are false positives.

Checks on semantic geometry

A rule deck is part of the program (see Design Rules):

define drc soi {
    width     si >= 400nm
    space     si >= 200nm
    space     si, metal >= 1um          # exclusion between layers
    enclosure si in slab >= 300nm       # the margin of an inclusion
    inside    si in slab                # the inclusion itself: no si outside slab
    area      si >= 200nm * 200nm
    radius    si >= 5um                 # the bend radius of every curved primitive
}

In Laylight, curved geometry is not a list of points: it is a primitive with parameters. An arc knows its radius, width and angle; a ring its centerline radius and width; an euler bend its curvature law; an sbend its profile. The compiler discretizes them only when it writes the layout, with a chord error it controls (at most one database unit by default), so it always knows both the intended geometry and how far each polygon edge may deviate from it. When it draws a ring with 10um, 500nm, it keeps what the polygon loses: the width is 500 nm, the radius of curvature is 10 µm, and every edge of the polygon lies within 1 nm of the circle it approximates. The checker uses all three:

  • An isolated shape passes or fails a width rule by its exact width. No vertex of a ring drawn at the minimum width measures a hair too narrow, because no vertex is measured.
  • The radius rule compares the tightest radius of an Euler bend, S-bend, Bézier or arc, computed on the curve. A polygon-based checker cannot express this rule at all.
  • Where edges do have to be compared, the edges of discretized curves carry their chord tolerance, so the gap between a ring and its bus drawn exactly at the minimum spacing is not an error, and a gap 50 nm too small still is.

On straight lines the checks are the classic edge-pair checks and give exactly KLayout's results, pair for pair. The hierarchy is kept too: every cell is checked once, however many times it is placed, and two instances are only compared where they come within a rule's limit of each other.

The compiler applies such checks to every curve it generates even without a deck. An Euler bend whose tightest radius would fold its inner edge over is rejected with the exact radius, computed on the continuous curve:

error: invalid euler: the tightest radius of this Euler bend is 706.09nm, which must exceed half the width 1.5um
 --> bend.lyl:7:9
  |
7 |         shape euler with 1um, 3um, 90deg as e
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The same holds for S-bends and Bézier curves (curvature checked along the whole curve), arcs, rings and racetracks (width against radius), paths with rounded corners (the bend radius has to fit between the points) and the min_radius of a cross-section. Connectivity is checked on the same model: laylight check reports dangling ports and connected ports whose type or width disagree, and laylight lvs compares a cell with a reference netlist.

Errors at the line of code

A violation is a compiler diagnostic. It names the rule, the measured value and the position, and points at the statement that drew the shape; between instances it also points at the place statements:

error: drc soi.space_si: space 150nm < 200nm on si at (0.072um, 0.325um) in Resonator
  --> resonator.lyl:9:45
   |
 9 |         shape ring with 10um, 500nm as ring at (0, 10.65um)
   |                                        ^^^^
note: the other shape, `bus` is drawn here
  --> resonator.lyl:8:40
   |
 8 |         shape rect with 30um, 500nm as bus at (0, 0)
   |                                        ^^^

laylight check runs the decks with everything else, laylight drc runs them alone and writes a JSON report, and the language server shows the violations in the editor as you type. There is no round trip through GDS, a separate rule deck and marker files that have to be mapped back to the code by hand.

Writing the deck

The same seven rules, written for each tool (bench/drc/decks), counted as the syntax benchmark counts: lines that hold code and tokens, without comments, blank lines and lines that only close a bracket.

define drc soi {
    width     si >= 400nm
    space     si >= 200nm
    space     si, metal >= 1um
    enclosure si in slab >= 300nm
    inside    si in slab
    area      si >= 200nm * 200nm
    radius    si >= 5um
}
# KLayout DRC script (.lydrc)
source($input)
report("SOI rules", $report)
si    = input(1, 0)
slab  = input(2, 0)
metal = input(10, 0)
si.width(0.4.um, euclidian).output("width", "si width < 400nm")
si.space(0.2.um, euclidian).output("space", "si space < 200nm")
si.separation(metal, 1.0.um, euclidian).output("space si metal", "si to metal < 1um")
slab.enclosing(si, 0.3.um, projection).output("enclosure", "slab encloses si by < 300nm")
(si - slab).output("inside", "si outside slab")
si.with_area(nil, 0.04).output("area", "si area < 0.04um^2")
DeckRulesLinesTokens
Laylight (deck only)7 of 7839the design's layer table is not repeated
Laylight (with its layer table)7 of 71264
KLayout DRC script6 of 711125no bend-radius rule
gdsfactory (Python)6 of 722317no bend-radius rule
gdstk (Python)6 of 726314no bend-radius rule; rules as morphology

A Laylight rule is a line: the kind, the layers, the limit. Nothing names the input, the output or the report, because the deck is part of the program and its violations are the compiler's diagnostics; nothing restates the layers, because the deck sees the design's layer table; and nothing chooses a metric per rule, because the defaults are the common ones. The KLayout script is the most compact of the others and still carries three times the tokens for one rule fewer: every rule names its output category and message, the layers are declared again by GDS number, and the metric is spelled out. The gdsfactory script drives the same engine from Python, with report-database plumbing around every check; the gdstk script has to express each rule as the morphology that isolates its violations, where a width check becomes an erosion, a dilation and a difference. None of the three can write the bend-radius rule: a polygon file no longer knows which of its edges were a curve.

Measured against KLayout, gdsfactory and gdstk

The benchmark in bench/drc plants violations into generated layouts, so that every tool can be scored on more than its speed:

  • Speed: the time of the checks (Laylight: after compilation, as the report measures it; the others: after reading the GDS, including building their regions), and the throughput in vertices of the checked layers per second.
  • Scaling: each case runs at five sizes; the slope of the time over the size on logarithmic axes is the exponent of the algorithm. Linear scaling is n^1.00.
  • Missed violations: planted violations that a tool does not report, as a fraction of all planted ones. The goal is zero.
  • False positives: reports that lie outside every planted violation, as a fraction of all reports. The lower the better; a tool that reports a correct layout as broken scores badly here.

The opponents: KLayout 0.30's Region checks on the GDS, in flat mode and in deep (hierarchical) mode; gdsfactory 9.51, which has no checker of its own and exposes KLayout's through Component.get_region; and gdstk 1.0, which has no checker either, with the rules written as the morphological operations a geometry library offers (a width check is A − open(A, d/2), a spacing check close(A, d/2) − A, an enclosure A − erode(B, d)). gdstk and KLayout cannot express the radius rule. All tools run single-threaded; times are from an Apple M3 Max.

Straight lines: the same answers, faster

10 ms100 ms1 s10 s100 s1000 s20k80k320k720k2Mvertices on the checked layers
ToolLargest (2M vertices)ScalingMissedFalse positives
Laylight2.2 sn^1.000%0%
KLayout (flat)4.8 sn^1.030%0%
KLayout (deep)4.4 sn^1.030%0%
gdsfactory4.7 sn^1.030%0%
gdstk150 s at 720k, then stoppedn^1.340%18%

The Manhattan case is a field of rectangles with slab rings around them, a width, a spacing and an enclosure rule, and violations of each planted in known places. Every edge-pair tool finds exactly the planted violations; Laylight is the fastest and scales linearly. gdstk's morphology finds them too, but the offsets it needs grow faster than linearly with the layout.

1 ms10 ms100 ms1 s10 s100 s20k80k320k720k2Mvertices on the checked layers
ToolLargest (2M vertices)ScalingMissedFalse positives
Laylight350 msn^1.020%0%
KLayout (flat)840 msn^1.040%0%
KLayout (deep)640 msn^1.030%0%
gdsfactory790 msn^1.040%0%
gdstk22 sn^1.160%0%

Exclusion keeps a metal pad at least 1 µm from a waveguide (space si, metal), with pads planted too close.

1 ms10 ms100 ms1 s10 s17k70k280k630k1.8Mvertices on the checked layers
ToolLargest (1.8M vertices)ScalingMissedFalse positives
Laylight720 msn^0.940%0%
KLayout (flat)1.5 sn^1.020%0%
KLayout (deep)1.6 sn^1.010%0%
gdsfactory1.5 sn^1.030%0%
gdstk9.5 sn^1.410%40%

Inclusion asks that a waveguide lies inside its slab, completely (inside) and with a margin (enclosure), with slabs planted too tight, shifted so the waveguide sticks out, or missing.

1 ms10 ms100 ms1 s10 s100 s1000 s29k120k320k720k2Mvertices on the checked layers
ToolLargest (2M vertices)ScalingMissedFalse positives
Laylight370 msn^1.010%0%
KLayout (flat)1.4 sn^1.030%31%
KLayout (deep)1.2 sn^1.010%31%
gdsfactory1.4 sn^1.020%31%
gdstk170 s at 720k, then stoppedn^1.510%31%

Feature size checks the minimum width of rotated bars, tapers, crosses and necked polygons: shapes whose narrowest part is not a pair of axis-aligned edges. Every tool finds all the planted features. The false positives of the polygon checkers are zero-width "tips": the corners of rotated bars, which rounding to the grid makes a fraction of a degree sharper than a right angle, and the 85° corners at the wide end of every taper. Laylight knows the width of a taper and a bar from their parameters and allows a tenth of a degree before it calls a corner acute.

Curves: only the real violations

10 ms100 ms1 s10 s100 s1000 s45k180k720k2.2M4.5Mvertices on the checked layers
ToolLargest (4.5M vertices)ScalingMissedFalse positives
Laylight1.3 sn^1.010%0%
KLayout (flat)19 sn^1.000%39%
KLayout (deep)19 sn^1.010%39%
gdsfactory19 sn^1.010%39%
gdstk130 s at 720k, then stoppedn^1.890%97%

Rings: half of them drawn exactly at the minimum width, half 50 nm below it. KLayout reports every ring, because the chords of a ring at the minimum width measure up to a nanometre too narrow; only the rings below the minimum are planted, so about half of its reports are false positives, and it needs a thousand edge pairs to say so per ring. Laylight reads the width of each isolated ring and spends its time on the spacing check alone.

10 ms100 ms1 s10 s100 s46k180k730k2.2M4.6Mvertices on the checked layers
ToolLargest (4.6M vertices)ScalingMissedFalse positives
Laylight1.3 sn^1.030%0%
KLayout (flat)20 sn^1.000%99%
KLayout (deep)18 sn^0.990%99%
gdsfactory19 sn^1.000%99%
gdstk100 s at 730k, then stoppedn^1.740%99%

Couplers: a ring over a bus with the gap at, below and above the minimum spacing. The gaps drawn exactly at the minimum are the hard case for a polygon checker: nearly all of KLayout's reports are those, and the planted gaps hide among them.

1 ms10 ms100 ms1 s10 s100 s1000 s43k170k680k2.1M4.3Mvertices on the checked layers
ToolLargest (4.3M vertices)ScalingMissedFalse positives
Laylight770 msn^1.020%0%
KLayout (flat)14 sn^1.000%55%
KLayout (deep)13 sn^1.000%55%
gdsfactory14 sn^1.000%55%
gdstk120 s at 2.1M, then stoppedn^1.610%96%

Bends: Euler bends, arcs and S-bends at and below the minimum width, with a bend-radius rule that only Laylight can run; the arcs that are too tight are found from their parameters.

1 ms10 ms100 ms1 s10 s100 s15k59k240k830k2.3Mvertices on the checked layers
ToolLargest (2.3M vertices)ScalingMissedFalse positives
Laylight730 msn^1.020%0%
KLayout (flat)14 sn^1.000%50%
KLayout (deep)13 sn^1.000%50%
gdsfactory13 sn^1.010%50%
gdstk74 s at 830k, then stoppedn^1.400%50%

Rounded corners: rectangles with 200 nm corner radii, at and below the minimum width. The rounded corners alone make a polygon checker report every rectangle.

Hierarchy: every cell once

10 ms100 ms1 s10 s100 s61k240k990k3.1M6.3Mvertices on the checked layers
ToolLargest (6.3M vertices)ScalingMissedFalse positives
Laylight2.6 sn^0.99——
KLayout (flat)46 sn^1.01—100%
KLayout (deep)42 sn^1.00—100%
gdsfactory48 sn^1.02—100%
gdstk96 s at 990k, then stoppedn^1.71—100%

The sweep is the ring-resonator test chip of the geometry benchmark: n² devices, each with its own radius and gap, so every one is a distinct cell, and the deck finds no violations. KLayout's deep mode cannot reuse anything here, because no two cells are alike; Laylight checks each cell's own shapes once and the interactions between instances only near their boundaries. The width reports of the other tools are the waveguides drawn at the minimum width.

Summary

Each cell: peak throughput (vertices per second), scaling exponent, missed violations, false positives, over the five sizes of the case.

CaseLaylightKLayout flatKLayout deepgdsfactorygdstk
manhattan936 k/s, n^1.00, 0%, 0%477 k/s, n^1.03, 0%, 0%505 k/s, n^1.03, 0%, 0%478 k/s, n^1.03, 0%, 0%17 k/s, n^1.34, 0%, 18%
exclusion6.5 M/s, n^1.02, 0%, 0%2.9 M/s, n^1.04, 0%, 0%3.7 M/s, n^1.03, 0%, 0%2.9 M/s, n^1.04, 0%, 0%196 k/s, n^1.16, 0%, 0%
inclusion2.8 M/s, n^0.94, 0%, 0%1.3 M/s, n^1.02, 0%, 0%1.3 M/s, n^1.01, 0%, 0%1.3 M/s, n^1.03, 0%, 0%1.3 M/s, n^1.41, 0%, 40%
feature5.7 M/s, n^1.01, 0%, 0%1.6 M/s, n^1.03, 0%, 31%1.8 M/s, n^1.01, 0%, 31%1.6 M/s, n^1.02, 0%, 31%21 k/s, n^1.51, 0%, 31%
rings3.7 M/s, n^1.01, 0%, 0%247 k/s, n^1.00, 0%, 39%246 k/s, n^1.01, 0%, 39%245 k/s, n^1.01, 0%, 39%64 k/s, n^1.89, 0%, 97%
couplers4.0 M/s, n^1.03, 0%, 0%236 k/s, n^1.00, 0%, 99%251 k/s, n^0.99, 0%, 99%234 k/s, n^1.00, 0%, 99%57 k/s, n^1.74, 0%, 99%
bends6.3 M/s, n^1.02, 0%, 0%306 k/s, n^1.00, 0%, 55%326 k/s, n^1.00, 0%, 55%308 k/s, n^1.00, 0%, 55%187 k/s, n^1.61, 0%, 96%
rounded3.7 M/s, n^1.02, 0%, 0%176 k/s, n^1.00, 0%, 50%174 k/s, n^1.00, 0%, 50%177 k/s, n^1.01, 0%, 50%56 k/s, n^1.40, 0%, 50%
sweep2.5 M/s, n^0.99, —, —142 k/s, n^1.01, —, 100%150 k/s, n^1.00, —, 100%144 k/s, n^1.02, —, 100%74 k/s, n^1.71, —, 100%

On this page