Layout Knowledge Base

Rules

One of two adjacent sides: checking m1.5, li.5, and via.5a in .ledrc

Some contact rules let the metal be thin on one side, or on two opposite sides, but never on two sides that meet at a corner. How to read that rule in the SKY130 table, write it by hand as one LayoutEditor check line, and prove it on a layout built to fail.

Last edited: October 11, 2026

Six square metal1 contacts in two rows on a 10 nm display grid: in the top row the orange contact sits in a lavender metal1 pad with an even margin, with a thin margin on its left side, and with thin margins on its left and right sides, all with no markers; in the bottom row the thin margins are on the left and bottom, on three sides, and on all four sides, and each thin side carries a dark red DRC marker

A contact is a tiny square hole that joins one wiring level to the next. The metal it lands on has to reach past the hole on every side, so that a slightly shifted mask still leaves the hole covered. The open SkyWater SKY130 rules add a twist: on some sides the metal may be thin, as long as it is generous on others. This note turns one of those rules into a check for LayoutEditor's design rule check (DRC), the program pass that measures a layout against the factory's rules (see Design rules and DRC). The check is written by hand from the rule table and proved on a layout built to fail.

Six square metal1 contacts in two rows on a 10 nm display grid: in the top row the orange contact sits in a lavender metal1 pad with an even margin, with a thin margin on its left side, and with thin margins on its left and right sides, all with no markers; in the bottom row the thin margins are on the left and bottom, on three sides, and on all four sides, and each thin side carries a dark red DRC marker

The picture shows six real contacts drawn in LayoutEditor and checked with the deck from this note. Orange squares are mcon, the 0.17 µm contact from the first wiring level up to metal1; the lavender squares around them are metal1; the faint dots are a 10 nm grid, so a margin of 0.03 µm is three dots and 0.06 µm is six. The top row passes: an even 0.06 µm margin, a thin left side, and thin left and right sides. The bottom row fails, and every dark red shape is a marker the check wrote on a thin side: thin left and bottom (two markers), three thin sides (three), and four thin sides (four).

Step 1: read the rule table

The rule this note is about is written as "on one of two adjacent sides". The SKY130 periphery table has three of them for the lower wiring levels, each next to a plain enclosure minimum (enclosure is the margin by which an outer shape covers an inner one):

Rule What the table says Value
m1.4 mcon must be enclosed by metal1 by at least 0.030 µm
m1.5 mcon must be enclosed by metal1 on one of two adjacent sides by at least 0.060 µm
li.5 enclosure of licon by one of two adjacent local-interconnect (li1) sides 0.080 µm
via.4a a 0.150 µm via must be enclosed by metal1 by at least 0.055 µm
via.5a a 0.150 µm via must be enclosed by metal1 on one of two adjacent sides by at least 0.085 µm

Two adjacent sides of a square are two sides that meet at a corner: left and bottom, for example. The rule asks that at every corner of the contact, at least one of the two sides meeting there has the larger margin. It is easier to check from the other direction. Call a side "short" when its margin is below the larger value (0.060 µm for m1.5). A contact passes when it has no short side, one short side, or two short sides that face each other. It fails when two short sides meet at a corner, which looks like an L, and also with three or four short sides, because those always include such a corner.

That is a deliberate shape of rule (more on why contacts are small squares inside larger metal in vias and contacts). It lets a contact sit in a minimum-width wire: across the wire the metal is thin on two opposite sides, and along the wire it runs on. It refuses a contact pushed into the corner of a pad, where the metal is thin on two sides at once. The plain minimum (m1.4, 0.030 µm) still applies on every side, so a short side is allowed to be short, but never shorter than that.

Step 2: name the layers

A rule file starts by telling the checker which numbered GDS layers hold which material. SKY130 keeps drawn shapes on datatype 20 and contacts and vias on datatype 44:

units 1e-6

layer li1   = input(67, 20)
layer licon = input(66, 44)
layer mcon  = input(67, 44)
layer m1    = input(68, 20)
layer via   = input(68, 44)

units 1e-6 makes a bare number a micrometer; writing the unit anyway (0.06 um) keeps the file readable. Each layer line gives a GDS layer and datatype a name used by the checks below. # starts a comment.

Step 3: write the check line

The check for m1.5 is one line:

check "m1.5" "m1.5 : met1 enclosure of mcon >= 0.060 um on one of two adjacent sides" : enc(m1, mcon) < 0.06 um rect_sides=1,2

Read it from the colon. enc(m1, mcon) measures, for each side of every mcon, how far the metal1 around it reaches; the first layer is the one that encloses the second. < 0.06 um keeps the sides that fall short, and each short side becomes an edge pair: the contact edge, the metal edge facing it, and the distance between them. On its own this line would flag every short side, which is the plain enclosure rule with the larger number.

rect_sides=1,2 is what turns it into the adjacent-side rule. It looks at the sides of each rectangular contact that came back short and waives the contact when exactly one side is short (1) or when exactly two opposite sides are short (2). Everything else stays: an L of two adjacent short sides, three short sides, four short sides. It is one of the check options in LayoutEditor's .ledrc reference, and its values need care. rect_sides=2 on its own waives only the opposite pair, so a contact with a single short side would be flagged. L waives only the adjacent pair, which is the opposite of this rule, and any2 waives any two sides, including the L this rule exists to catch.

check "m1.5" "…" gives the result an identifier and a line of text for the report. Keep the identifier exactly as in the rule table and put the value with its unit in the text, so a marker can be traced back to the table without guessing.

The full deck adds li.5 and via.5a in the same shape and keeps the two plain minimums next to them:

units 1e-6

layer li1   = input(67, 20)
layer licon = input(66, 44)
layer mcon  = input(67, 44)
layer m1    = input(68, 20)
layer via   = input(68, 44)

# li.5: li1 encloses licon by 0.080 um on one of two adjacent sides
check "li.5" "li.5 : li1 enclosure of licon >= 0.080 um on one of two adjacent sides" : enc(li1, licon) < 0.08 um rect_sides=1,2

# m1.4 + m1.5: met1 encloses mcon by 0.030 um everywhere, 0.060 um on one of two adjacent sides
check "m1.4" "m1.4 : met1 enclosure of mcon >= 0.030 um" : enc(m1, mcon) < 0.03 um
check "m1.5" "m1.5 : met1 enclosure of mcon >= 0.060 um on one of two adjacent sides" : enc(m1, mcon) < 0.06 um rect_sides=1,2

# via.4a + via.5a: met1 encloses a 0.150 um via by 0.055 um everywhere, 0.085 um on one of two adjacent sides
check "via.4a" "via.4a : met1 enclosure of via >= 0.055 um" : enc(m1, via) < 0.055 um
check "via.5a" "via.5a : met1 enclosure of via >= 0.085 um on one of two adjacent sides" : enc(m1, via) < 0.085 um rect_sides=1,2

The m1.4 and via.4a lines are not optional. rect_sides waives a single short side no matter how short it is, so a contact with 0.02 µm on one side and 0.06 µm elsewhere passes m1.5. Only m1.4 catches it.

Step 4: test it on a layout built to fail

A check that has never produced a marker has not been tested. The test layout for this deck is built with LayoutScript, LayoutEditor's Python interface: one case per 3 µm column, each a single contact in a metal or li1 pad with the margins listed below, given as left, bottom, right, top in micrometers. The expected markers were counted by hand from the geometry before the deck ran: one marker per short side, unless the short sides are none, one, or an opposite pair.

Case Margins (µm) Expected markers
mcon, even margin (clean reference) 0.06, 0.06, 0.06, 0.06 none
mcon, one short side 0.03, 0.06, 0.06, 0.06 none
mcon, two opposite short sides 0.03, 0.06, 0.03, 0.06 none
mcon, L of two short sides 0.03, 0.03, 0.06, 0.06 m1.5: 2
mcon, three short sides 0.03, 0.03, 0.03, 0.06 m1.5: 3
mcon, four short sides 0.03, 0.03, 0.03, 0.03 m1.5: 4
mcon in the middle of a 0.23 µm wire 1.00, 0.03, 1.00, 0.03 none
mcon at the end of a 0.29 µm wire 0.06, 0.06, 1.00, 0.06 none
mcon with 0.02 µm on one side 0.02, 0.06, 0.06, 0.06 m1.4: 1
licon in li1, two opposite short sides 0.04, 0.08, 0.04, 0.08 none
licon in li1, L of two short sides 0.04, 0.04, 0.08, 0.08 li.5: 2
via in a 0.26 µm wire 0.085, 0.055, 0.085, 0.055 none
via in a 0.26 µm square pad 0.055, 0.055, 0.055, 0.055 via.5a: 4
via, L of two short sides 0.055, 0.055, 0.085, 0.085 via.5a: 2

The deck runs from a shell. The first argument is the rule file, the second the layout, and --outdir collects the results:

layout --drc adjacent_sides.ledrc adjacent_test.gds --outdir=drc_out

The summary and the first marker lines of the report:

checks 5
errors 18
li.5    li.5 : li1 enclosure of licon >= 0.080 um on one of two adjacent sides  error   30239,-40 29960,-40 | 30170,0 30000,0   40
li.5    li.5 : li1 enclosure of licon >= 0.080 um on one of two adjacent sides  error   29960,-40 29960,239 | 30000,0 30000,170 40
m1.4    m1.4 : met1 enclosure of mcon >= 0.030 um   error   23980,-22 23980,192 | 24000,0 24000,170 20

Each marker line names the rule, repeats its text, and gives the two edges of the pair in database units (nanometers here), followed by the measured margin: 40 nm for the li1 case, 20 nm for the contact that is too close on one side. Eighteen markers is exactly the hand count from the table: 2 + 3 + 4 for metal1, 1 for m1.4, 2 for li.5, and 4 + 2 for via.5a. The comparison is done per case, not only in total, because two mistakes can cancel in a sum. A deck changed later is run against the same layout again; a case that changes its count is either a fixed bug or a new one, and the table says which.

The same layout also shows why the values of rect_sides matter. A copy of the m1.5 line with rect_sides=2 alone reports the contact with one short side and the contact with 0.02 µm on one side as m1.5 errors, one marker each. On the other metal1 cases it gives the same counts as the full deck.

Four contacts on a 20 nm display grid: top left, an orange mcon in the middle of a long, narrow lavender metal1 wire; top right, an mcon at the left end of a wider wire; bottom left, a purple via in the middle of a narrow wire; bottom right, the same via in a small square pad whose four sides are covered by dark red DRC markers

The second picture shows what the rule means in real wiring. In the top left, the contact sits in a 0.23 µm wire: 0.03 µm of metal1 above and below, plenty to the left and right, so the two short sides face each other and the check stays quiet. In the top right, the contact ends a 0.29 µm wire with 0.06 µm on three sides. In the bottom left, a purple 0.150 µm via sits in a 0.26 µm wire: 0.055 µm above and below meets via.4a, and the wire runs on to the left and right, which covers via.5a. In the bottom right, the same via sits in a square pad with 0.055 µm on every side. Each side meets the minimum, but no corner has a side with 0.085 µm, so via.5a marks all four sides. Make the pad 0.06 µm longer in one direction, with the via centered so both ends get 0.085 µm, and it passes; that is the via-in-a-wire case from the table.

Where the deck stops

The deck covers the periphery values only. SKY130 has separate rules inside core areas, which are marked with the areaid.ce layer, and the via rules are written for the 0.150 µm via that via.1a fixes outside areas marked areaid.mt. rect_sides judges the sides of each rectangular cut, which suits SKY130: contacts must be minimum-size squares except in the die seal ring, and vias are fixed at 0.150 µm. The same table has more rules worded like these, for example licon.5c (diffusion around licon, 0.060 µm), licon.7 (isolated tap, 0.120 µm), and licon.8a (poly around a poly contact, 0.080 µm). They follow the same pattern, but each one needs its own case in the test layout before the deck relies on it.

Continue exploring