Automated SKY130-style DRC flow that runs custom physical design rule checks on a GDS layout using KLayout in batch mode, exports violations to XML, parses the results with Python, and generates an HTML dashboard for debugging and documentation.
Scope note — This is an educational physical-verification project, not a production signoff deck. The goal is to demonstrate DRC automation, rule-deck scripting, result parsing, and EDA flow integration — skills used daily by physical design and physical verification engineers.
| Aspect | Detail |
|---|---|
| Flow | GDS → KLayout batch DRC → XML report → Python parser → dashboard |
| Input layout | layout.gds — SKY130 sky130_fd_sc_hd__a2111o_1 standard cell |
| DRC engine | KLayout (batch mode, -b -r) |
| Rule format | KLayout .lydrc / .drc rule-deck scripting |
| Rules implemented | 4 core + 6 extended SKY130-style checks (10 total) |
| Output format | KLayout XML / RDB violation database |
| Parser | analyze_results.py — XML → summary + chart + HTML |
| Dashboard | Self-contained HTML report with violation table and bar chart |
| CI | GitHub Actions — syntax checks, LVS demo, manifest validation |
| Advanced | OpenLane RTL→GDSII demo, educational LVS comparator, rule expansion |
This project demonstrates practical skills used in physical design and physical verification roles:
- Reading process design rules — mapping SKY130 PDK specs to concrete width, spacing, enclosure, and area checks
- Writing DRC rule decks — authoring KLayout
.lydrcscripts withsource(),report(),input(),.width(),.space(),.enclosing(), and.with_area()APIs - Mapping GDS layers to rule checks — associating SKY130 layer/datatype
pairs (e.g.
67/20for li1,64/20for nwell) with physical verification constraints - Running KLayout in batch mode — automating DRC execution headlessly via Python and TCL wrappers
- Parsing violation databases — extracting categories, violation counts, and coordinates from KLayout's XML report format
- Generating human-readable reports — producing HTML dashboards with summary tables, bar charts, and per-violation detail for PV debug
- Automating checks in CI — running syntax checks, LVS smoke tests, and manifest validation on every push via GitHub Actions
- Documenting physical verification results — presenting DRC outcomes with professional rule-ID naming conventions used in real PV debug workflows
GDS Layout (layout.gds)
│
▼
KLayout Batch DRC (klayout -b -r drc_rules.lydrc)
│
▼
Custom SKY130-style Rule Deck
┌──────────────────────────────────────────┐
│ width · spacing · enclosure · area │
│ checks on li1, nwell, nsdm, mcon, ... │
└──────────────────────────────────────────┘
│
▼
XML / RDB Violation Report (results.xml)
│
▼
Python Parser (analyze_results.py)
│
▼
Summary Table + Bar Chart + HTML Dashboard
│
▼
CI Artifact / Debug Evidence
- Loads
layout.gds— a real SKY130sky130_fd_sc_hdstandard-cell GDS. - Runs
drc_rules.lydrcin KLayout batch mode. - Checks width, spacing, enclosure, and area rules against SKY130-style layer definitions.
- Writes
results.xml— the KLayout report database with all violations. - Parses the XML with
analyze_results.py. - Generates:
- Terminal summary table
- Violation bar chart (
violations.png) - Self-contained HTML dashboard (
drc_report.html)
| Rule ID | Layer(s) | Check | Limit | Purpose | Sample Result |
|---|---|---|---|---|---|
LI.W.1 |
li1 67/20 |
Minimum width | 0.17 µm | Local interconnect minimum wire width | PASS (0) |
LI.S.1 |
li1 67/20 |
Minimum spacing | 0.17 µm | Local interconnect minimum spacing | PASS (0) |
NW.ENC.1 |
nwell 64/20 + nsdm 93/44 |
Enclosure | 0.125 µm | N-well enclosure of n+ source/drain implant | PASS (0) |
MCON.A.1 |
mcon 67/44 |
Minimum area | 0.2025 µm² | Metal contact minimum polygon area | FAIL (18) |
| Rule ID | Layer(s) | Check | Limit | Purpose |
|---|---|---|---|---|
M1.W.1 |
met1 68/20 |
Minimum width | 0.14 µm | Metal-1 wire width floor (lithography limit) |
M1.S.1 |
met1 68/20 |
Minimum spacing | 0.14 µm | Metal-1 spacing floor (short prevention) |
POLY.W.1 |
poly 66/20 |
Minimum width | 0.15 µm | Poly gate minimum width (channel-length floor) |
POLY.S.1 |
poly 66/20 |
Minimum spacing | 0.21 µm | Poly spacing (capacitive coupling prevention) |
VIA.ENC.1 |
met1 over mcon 67/44 |
Enclosure | 0.03 µm | Metal-1 cap over contact landing |
NW.S.1 |
nwell 64/20 |
Minimum spacing | 1.27 µm | N-well spacing (latch-up prevention) |
Note — Rule values are educational and reflect the spirit of the SKY130 PDK. See
advanced/rules/rule_manifest.jsonfor explicit source notes per rule. These are not tapeout-certified values.
The following results are from running the core 4-rule deck on layout.gds
(cell sky130_fd_sc_hd__a2111o_1):
| Metric | Result |
|---|---|
| Total rules checked | 4 |
| Rules passing | 3 |
| Rules failing | 1 |
| Total violations | 18 |
| Rule | Description | Violations | Status |
|---|---|---|---|
LI.W.1 |
li1 minimum width < 0.17 µm | 0 | PASS |
LI.S.1 |
li1 minimum spacing < 0.17 µm | 0 | PASS |
NW.ENC.1 |
nwell enclosure of nsdm < 0.125 µm | 0 | PASS |
MCON.A.1 |
mcon minimum area < 0.2025 µm² | 18 | FAIL |
The DRC rule deck (drc_rules.lydrc) follows KLayout's batch-mode scripting API:
# Load the GDS layout ($input is set via: klayout -rd input=layout.gds)
source($input)
# Create the report database ($output is set via: klayout -rd output=results.xml)
report("SKY130 DRC Report", $output)
# Map SKY130 GDS layers to named variables
li1 = input(67, 20) # local interconnect
nwell = input(64, 20) # n-well
nsdm = input(93, 44) # n+ source/drain implant
mcon = input(67, 44) # metal contact
# Run checks and store violations by rule ID
li1.width(0.17.um).output("LI.W.1", "li1 minimum width < 0.17 um")
li1.space(0.17.um).output("LI.S.1", "li1 minimum spacing < 0.17 um")
nwell.enclosing(nsdm, 0.125.um).output("NW.ENC.1", "nwell enclosure of nsdm < 0.125 um")
mcon.with_area(0.0, 0.2025).output("MCON.A.1", "mcon area < 0.2025 um^2")Key concepts:
| API call | Purpose |
|---|---|
source($input) |
Loads the GDS file to check |
report(title, $output) |
Creates the XML report database |
input(layer, datatype) |
Maps a GDS layer/datatype pair to a layer object |
.width(limit) |
Checks minimum wire width |
.space(limit) |
Checks minimum spacing between shapes |
.enclosing(other, limit) |
Checks enclosure of one layer by another |
.with_area(min, max) |
Selects polygons by area range |
.output(rule_id, description) |
Stores violations under a named rule category |
- Validates input GDS and rule-deck paths
- Locates the KLayout executable on PATH or via
--klayout - Launches KLayout in batch mode with
-b -rand-rdvariable passing - Optionally chains
analyze_results.pyfor end-to-end automation - Returns meaningful error codes for CI integration
- Parses
<categories>and<items>from the KLayout XML report - Extracts rule IDs, descriptions, violation counts, and coordinates
- Generates a color-coded bar chart (
violations.png) using matplotlib - Writes a self-contained HTML dashboard (
drc_report.html) using Jinja2 - Prints a terminal summary table using tabulate and pandas
python -m pip install -r requirements.txt# Linux / macOS / WSL
python run_drc.py --input layout.gds --output results.xml --analyze
# Windows PowerShell (specify KLayout path if not on PATH)
python run_drc.py --klayout "C:\path\to\klayout_app.exe" --input layout.gds --output results.xml --analyzepython run_drc.py --input layout.gds --output results.xmlpython analyze_results.py --input results.xml --html drc_report.htmltclsh run_drc.tclpython run_drc.py --rules advanced/rules/sky130_extra_rules.lydrc \
--input layout.gds \
--output results_extra.xml \
--analyzesky130-drc-automation/
├── layout.gds # SKY130 standard-cell GDS input
├── drc_rules.lydrc # Core KLayout DRC rule deck (4 rules)
├── drc_rules.drc # Alternate DRC deck format
├── run_drc.py # Python DRC launcher (batch mode)
├── analyze_results.py # XML parser + HTML dashboard generator
├── run_drc.tcl # TCL batch automation script
├── requirements.txt # Python dependencies
├── results.xml # Sample KLayout DRC output (committed)
├── drc_report.html # Sample generated HTML report
├── violations.png # Sample generated bar chart
│
├── docs/
│ ├── images/ # Screenshots (layout, terminal, report, chart)
│ ├── sample_drc_report.html # Archival copy of the generated report
│ ├── ADVANCED_FLOW.md # RTL → GDSII → DRC narrative
│ ├── CI_PIPELINE.md # CI workflow documentation
│ ├── LVS_DEMO.md # LVS conceptual walkthrough
│ └── RULE_EXPANSION.md # Extended rule documentation
│
├── advanced/
│ ├── openlane/ # RTL-to-GDSII OpenLane demo
│ │ ├── src/adder4.v # 4-bit adder Verilog source
│ │ ├── config.json # OpenLane config (sky130_fd_sc_hd)
│ │ └── README.md
│ ├── lvs/ # Educational LVS comparator
│ │ ├── lvs_compare.py # Resistor-netlist LVS checker
│ │ ├── resistor_network.spice # "Schematic" netlist
│ │ ├── resistor_network_extracted.spice # "Extracted" netlist
│ │ └── README.md
│ ├── rules/ # Expanded DRC rules
│ │ ├── sky130_extra_rules.lydrc # 6 additional SKY130-style rules
│ │ ├── rule_manifest.json # Machine-readable rule manifest
│ │ └── README.md
│ └── ci/ # CI helper notes
│ └── README.md
│
└── .github/workflows/
└── drc-ci.yml # GitHub Actions CI pipeline
These extensions build on top of the core DRC flow. Everything lives under
advanced/ and docs/ — the core flow is unchanged.
A 4-bit adder in advanced/openlane/src/adder4.v
plus an OpenLane config.json targeting
sky130_fd_sc_hd. Walks RTL → synthesis → P&R → GDSII, after which the
generated GDS feeds the same DRC flow. See
docs/ADVANCED_FLOW.md.
Verilog RTL ──► OpenLane (synthesis + P&R) ──► GDSII ──► KLayout DRC ──► HTML Report
OpenLane is not required to use the rest of the project — it demonstrates how the full RTL-to-verified-layout loop works.
A miniature Layout-Versus-Schematic comparator that takes two resistor-only
SPICE netlists and reports PASS / FAIL on devices, values, and connectivity.
See docs/LVS_DEMO.md.
# PASS — netlists are equivalent
python advanced/lvs/lvs_compare.py \
advanced/lvs/resistor_network.spice \
advanced/lvs/resistor_network_extracted.spice
# FAIL — inject a 10% mismatch to prove the checker works
python advanced/lvs/lvs_compare.py \
advanced/lvs/resistor_network.spice \
advanced/lvs/resistor_network_extracted.spice \
--inject-mismatchSix additional rules covering metal-1, poly, mcon enclosure, and nwell spacing,
with a JSON manifest documenting every value's source. See
docs/RULE_EXPANSION.md.
.github/workflows/drc-ci.yml runs on every
push and pull request:
| Step | What it checks |
|---|---|
| 1 | Syntax-checks all Python sources with py_compile |
| 2 | Runs LVS demo in PASS mode — confirms comparator returns 0 |
| 3 | Runs LVS demo with --inject-mismatch — confirms non-zero exit |
| 4 | Validates rule_manifest.json is well-formed JSON |
| 5 | Smoke-tests analyze_results.py on committed results.xml |
| 6 | Uploads generated reports as downloadable CI artifacts |
KLayout itself is not installed in CI. See
docs/CI_PIPELINE.mdfor how to add it via a container image.
This project is not:
- A production signoff DRC deck — real SKY130 signoff decks have hundreds of rules, including density, antenna, and conditional spacing checks.
- A replacement for Calibre, Pegasus, ICV, or the official SKY130 DRC flow.
- A complete PDK — it uses selected SKY130 layers and educational rule values.
It is a working demonstration of the DRC automation loop that PV engineers use daily: write rules → run checks → parse results → fix violations → re-run.
| Tool | Purpose |
|---|---|
| KLayout | Layout viewer and DRC engine (batch mode) |
| Python | DRC launcher, XML parser, report generator |
| TCL | Optional batch automation script |
| SKY130 PDK | Open-source 130 nm process layer data |
| matplotlib | Violation bar chart generation |
| Jinja2 | HTML dashboard templating |
| pandas | Tabular result aggregation |
| GitHub Actions | CI smoke testing and artifact uploads |



