Running the assembly DRC¶
klayout/drc/run_drc.py is the standalone entry point to the assembly DRC. It
resolves the adapters, finds and validates the boundary manifest, invokes
KLayout in batch mode on adk_assembly.drc, parses the resulting report and
returns an exit code you can branch on in a script.
This page is about running it. For what the rules actually check, see Assembly placement rules (ASM) and Interconnect rules (IXN).
Prerequisites¶
- KLayout 0.29.11 or newer, on ``PATH``.
The runner shells out to
klayout -band checks the version first. An older KLayout, or none at all, aborts before anything else happens. The container image ships a suitable build; see The ADK-Tools image.- The ``klayout`` Python module.
Imported at module load to read the layout’s top cells. It is a separate package from the KLayout application binary and both are needed.
- An assembly layout.
.gds,.gds.gz,.gds2,.gds2.gzor.oas. The extension is checked before the file is opened; a file with the right extension that is not a valid stream fails with a clean message rather than a KLayout traceback.- The boundary manifest sidecar.
<gds-stem>.boundaries.json, emitted next to the layout by the producer (hyp_to_gdsorblackbox_chiplet). This is where the chiplet boundaries come from. See Boundary manifest.- An interposer adapter.
Required, always. The deck is interposer-agnostic and has no default: with no adapter it does not know what geometry on the substrate means “attachment”. See Adapter contract.
The minimal invocation¶
python klayout/drc/run_drc.py \
--path design.gds \
--interposer-adapter intm4tm2 \
--run_dir /tmp/adk_drc
That runs the 4 ASM rules and nothing else. The boundary manifest
is discovered next to design.gds, the top cell is auto-detected, and the
report lands in /tmp/adk_drc.
--interposer-adapter takes either form:
a shortname, resolved against
pdk_adapters/interposer/<name>.drc, sointm4tm2finds the shipped IHP IntM4TM2 adapter; oran explicit path to a
.drcfile, used as given.
The resolver tries the literal path first and the shortname second, and if neither exists it reports both candidates it looked for.
Adding the interconnect axis¶
Supply an interconnect adapter to add the bump-to-bump pitch and spacing checks:
python klayout/drc/run_drc.py \
--path design.gds \
--interposer-adapter intm4tm2 \
--interconnect-adapter ihp_cupillar \
--run_dir /tmp/adk_drc
Scope those checks per attachment method by adding the exporter’s methods sidecar:
python klayout/drc/run_drc.py \
--path design.gds \
--interposer-adapter intm4tm2 \
--interconnect-adapter ihp_cupillar \
--interconnect-methods design.ixn_methods.json \
--run_dir /tmp/adk_drc
--interconnect-adapter resolves the same way as the interposer flag, against
pdk_adapters/interconnect/. Unlike the manifest, the methods file is never
auto-discovered: you pass it explicitly or the axis stays assembly-global.
Tip
Passing both, as above, is the combination that leaves nothing unchecked. The methods file gives each die the numbers its own method demands; the adapter supplies the fallback for attachment geometry no method claims. With a methods file and no adapter, unclaimed pads are simply not checked on this axis, and the deck logs a line saying so.
Command-line reference¶
usage: run_drc.py [-h] --path PATH --interposer-adapter INTERPOSER_ADAPTER
[--interconnect-adapter INTERCONNECT_ADAPTER]
[--interconnect-methods INTERCONNECT_METHODS]
[--topcell TOPCELL] [--run_dir RUN_DIR] [--threads THREADS]
[--run_mode {tiling,deep,flat}] [--report REPORT]
[--manifest MANIFEST] [--legacy-exchange0]
Run ADK assembly DRC checks via KLayout
options:
-h, --help show this help message and exit
--path PATH Path to the input GDS/OAS file.
--interposer-adapter INTERPOSER_ADAPTER
Interposer adapter: a shortname (resolved against
pdk_adapters/interposer/<name>.drc) or an absolute
path to a .drc file.
--interconnect-adapter INTERCONNECT_ADAPTER
Optional interconnect adapter: a shortname (resolved
against pdk_adapters/interconnect/<name>.drc) or an
absolute path. Adds the bump-to-bump pitch/spacing
axis (IXN rules). Omit for interposer-only checking
(identical to before this axis existed).
--interconnect-methods INTERCONNECT_METHODS
Optional per-method interconnect file (<gds-
stem>.ixn_methods.json, emitted by the exporter from
the .chiplet's per-die connections + the interconnect
PDK manifest). Scopes the IXN checks per method: each
method's pitch/spacing runs on the attachment pads
under ITS dies' boundaries, plus a conservative cross-
method spacing check. Requires the boundary manifest.
Without it the IXN axis stays assembly-global.
--topcell TOPCELL Top-level cell name (auto-detected if omitted).
--run_dir RUN_DIR Output directory for reports (default: timestamped
subdir in cwd).
--threads THREADS Threads per KLayout invocation (default: 4).
--run_mode {tiling,deep,flat}
KLayout execution mode (default: tiling).
--report REPORT Explicit report path (overrides default naming).
--manifest MANIFEST Boundary manifest sidecar (<gds-
stem>.boundaries.json). Auto-discovered next to --path
if omitted. Holds the chiplet boundaries the assembly
DRC checks (PDK-agnostic; not a fab layer).
--legacy-exchange0 Compat mode: read chiplet boundaries from the
historical exchange0 fab layer in the GDS instead of a
manifest (for pre-migration assemblies).
Examples:
run_drc.py --path design.gds --interposer-adapter intm4tm2
run_drc.py --path design.gds --interposer-adapter /abs/path/to/custom.drc
run_drc.py --path design.gds --interposer-adapter intm4tm2 --interconnect-adapter ihp_cupillar
Flags in detail¶
--path(required)The assembly layout. Validated for existence and extension, then resolved to an absolute path.
--interposer-adapter(required)Shortname or path, as described above.
--interconnect-adapterOptional. Enables the IXN axis. Omit it and the evaluated deck is identical to a run from before the axis existed.
--interconnect-methodsOptional. Per-method IXN scoping. Requires the boundary manifest, and is rejected together with
--legacy-exchange0before KLayout is spawned. Validated for schema string and exact version by the runner; per-method keys and die names are validated by the deck.--topcellThe cell to check. Auto-detected when omitted. Auto-detection is strict: a layout with several top cells is an error telling you to pick one, and a layout with none is an error too. It is never guessed.
--run_dirWhere the log and, by default, the report are written. Created if missing. Omitted, empty or the literal string
pwdall mean “a timestamped subdirectory of the current directory”.--reportAn explicit report path, overriding the default naming. Useful when a CI job wants a fixed filename to publish.
--threadsThreads per KLayout invocation. Default 4.
--run_modetiling(default),deeporflat. See below.--manifestAn explicit boundary manifest, overriding auto-discovery.
--legacy-exchange0Compatibility mode. See below.
Note
Arguments reach KLayout as an argv list, not a shell string. Each
-rd key=value is one token, so paths, report names and top-cell names
containing spaces, quotes or shell metacharacters are passed through
verbatim. This matters because a top-cell name can come straight out of an
untrusted GDS.
Manifest discovery and validation¶
With no --manifest, the runner looks for the layout’s stem plus
.boundaries.json in the layout’s own directory: design.gds implies
design.boundaries.json.
A missing manifest is a hard error, not a warning. This is deliberate. The
chiplet boundaries are the only thing the ASM rules have to check; a run with no
boundary source would evaluate every rule against an empty region and report a
clean result. That vacuous pass is more dangerous than a failure, so the runner
refuses, and the error message tells you the two ways forward: produce the
sidecar, or pass --legacy-exchange0.
When the manifest is found it is opened and validated in the runner, before KLayout is started:
it must parse as JSON and be an object;
schemamust be exactlyadk-boundary-manifest;versionmust be exactly the version this runner supports, currently1.0.0. There is no range and no forward compatibility: a mismatch tells you to regenerate the sidecar with a current producer or to update the ADK;boundariesmust be a list, and every entry must be an object carrying apolygon_dbuof at least three[x, y]pairs.
The last check duplicates part of the JSON schema without taking a
jsonschema dependency. The reason is narrow and worth knowing:
layers_def.drc reads polygon_dbu blind, so a structurally malformed but
version-valid sidecar would otherwise surface as a Ruby backtrace from inside a
KLayout batch run, which is a much worse thing to debug than a one-line Python
error.
The legacy exchange0 path¶
Before the manifest existed, chiplet boundaries travelled on a fabrication
layer: IHP SG13G2 Exchange0, GDS 190/0. --legacy-exchange0 restores that
behaviour, reading the boundary from that layer instead of from a sidecar.
python klayout/drc/run_drc.py \
--path old_design.gds \
--interposer-adapter intm4tm2 \
--legacy-exchange0
Use it for exactly one thing: checking an assembly GDS produced before the
migration, for which no sidecar exists and cannot be regenerated. Nothing in the
current toolchain emits 190/0, and config/layers.json is marked
legacy-compat for this reason alone.
In this mode --manifest is ignored, with a warning, and
--interconnect-methods is rejected outright, because legacy layouts carry no
per-die instance names for a method to refer to.
Note
The two paths are not merely similar. A regression test runs the same fixture through both modes and asserts that the violation sets agree, which is what makes the migration verifiable rather than asserted. A second test pins the other direction: a chiplet-internal 190/0 shape, which a real die might legitimately contain, is ignored in manifest mode. That aliasing hazard is precisely why the boundary left the fabrication namespace.
Run modes¶
--run_mode selects how KLayout evaluates the deck.
tiling(default)Splits the layout into tiles and evaluates them in parallel, honouring
--threads. The right default for a full assembly: assembly geometry is spread over a large area with modest hierarchy, which is the case tiling handles best.deepHierarchical evaluation. Exploits repetition rather than area, so it is worth trying on an assembly with many identical dies.
flatFlattens everything first. The slowest and most memory-hungry option, and the one whose result is the easiest to reason about. Use it to rule out a mode-dependent artefact when a violation looks suspicious.
All three check the same rules with the same numbers. If two modes disagree on the same layout, that is a bug worth reporting, not a tuning choice.
What lands in the run directory¶
/tmp/adk_drc/
adk_drc_run_2026_07_23_09_14_02.log
design_TOP_assembly.lyrdb
- The log
Named for the UTC timestamp of the run. It carries the full DEBUG-level record: the resolved adapter paths, the manifest path and the number of boundaries loaded, one line per rule as the deck reaches it (
ADK: ASM.a,ADK: IXN.b.<method>, and so on), the pass or fail banner, the sorted list of violated rules and the elapsed time. The same stream goes to the console.- The report
A KLayout results database. The default name is
<layout-stem>_<topcell>_assembly.lyrdb, so the example above isdesign.gdswith top cellTOP.--reportoverrides it.
The report is always written, including on a clean run, where it contains zero items. The runner treats a missing report as a failure of the run rather than as a pass.
Reading the report in KLayout¶
Open the layout with the report attached:
klayout design.gds -m /tmp/adk_drc/design_TOP_assembly.lyrdb
-m loads a marker database into the layout view. The Marker Browser opens
from Tools and lists the violations grouped by category, one category per
rule name: ASM.a, ASM.b, IXN.b.<method> and so on. Selecting an
entry zooms the layout view to the offending geometry and highlights it.
The category names are exactly the rule names, and the description string
alongside each category carries the number that was checked, for example
ASM.b : Min. chiplet-to-chiplet spacing is 50.0 um. That is the fastest way
to confirm which value an adapter override actually put into effect.
Tip
The chiplet boundaries themselves are not in the GDS, so a report full of
ASM markers can be hard to interpret against a layout that shows only
interposer metal. The show_boundaries KLayout macro reads the same
.boundaries.json sidecar the checker reads and displays each chiplet
outline as markers. Because it is the same file, the outlines are exactly
what the checker saw. See Viewing and inspecting.
Exit codes¶
Code |
Meaning |
|---|---|
|
The run completed and the report contains no violations. |
|
The run completed and the report contains violations. |
|
A tooling failure with no more specific code available. |
other |
KLayout’s own non-zero exit code, passed through. |
The distinction between the last two rows is worth being precise about. When the
KLayout subprocess exits non-zero, for example because an adapter did not
declare a required input, or the deck raised on an unresolvable die instance,
the runner does not dump a Python traceback on top of an error KLayout has
already printed. It returns KLayout’s exit code, falling back to 2 if that
code was somehow zero.
Note
Exit code 1 is overloaded. The runner also exits 1 on its own
precondition failures: no KLayout, unreadable layout, ambiguous top cell,
unresolvable adapter, missing or invalid manifest. Those are distinguishable
from a violation result by the absence of a report file, and the log always
carries the reason on its last lines. A script that must tell the two apart
should test for the report rather than trust the code alone. On a
well-formed input the runner returns only 0 or 1, and the regression
suite asserts exactly that.
A shell wrapper therefore looks like this:
python klayout/drc/run_drc.py \
--path design.gds \
--interposer-adapter intm4tm2 \
--run_dir "$RUN_DIR"
status=$?
case $status in
0) echo "assembly DRC clean" ;;
1) echo "assembly DRC violations, see $RUN_DIR" ;;
*) echo "assembly DRC did not run (code $status)"; exit $status ;;
esac
Troubleshooting¶
The messages below are the ones the runner and the deck actually emit, shortened to their identifying phrase.
KLayout not found. Make sure it is installed and in PATH.The runner could not execute
klayout -b -v. A related message,KLayout did not report a version, means the binary ran but printed nothing, which usually indicates a broken or partial installation. See Environment and path discovery.Minimum KLayout version is 0.29.11.Upgrade, or use the container image.
Layout has multiple top cells. Specify one with --topcell.Pass
--topcell. The runner will not pick for you, because checking the wrong top cell produces a confidently wrong result.Could not read layout '<path>'The extension check passed but KLayout could not parse the stream. Truncated or mislabelled file.
Interposer adapter not found: '<name>'.The message lists both candidates that were tried, the literal path and
pdk_adapters/interposer/<name>.drc. The same message withInterconnectnames the other axis and directory. Note that the IHP interposer adapter isintm4tm2and has no aliases.Boundary manifest not found: <path>The sidecar is missing. Regenerate it with the producer, point at it with
--manifest, or check a pre-migration layout with--legacy-exchange0.Unsupported boundary-manifest version in <path>The sidecar’s
versionis not the one this runner pins. Regenerate it with a current producer, or check out an ADK that matches the sidecar. The match is exact by design.Not an ADK boundary manifestThe file parses but its
schemakey is notadk-boundary-manifest. Usually a different sidecar passed by mistake.boundaries[N].polygon_dbu must be a list of >= 3 [x, y] pairsStructurally malformed manifest. The index tells you which entry. This is a producer bug; report it against the tool that wrote the sidecar.
--interconnect-methods requires the boundary manifest and cannot be combined with --legacy-exchange0Exactly what it says. Per-method checking needs per-die instance names, and the legacy path has none.
ADK requires an interposer adapter.Raised by the deck when it is invoked directly rather than through the runner and
-rd adapter=was not set.Interposer adapter (<path>) must declare 'chiplet_attachment_input'.The adapter was evaluated but did not define the required abstract input. See Adapter contract.
Interconnect adapter (<path>) must set interconnect_rules['IXN_pitch'].An interconnect adapter is a parameter pack and must set all three of
IXN_spacing,IXN_pitchandIXN_pad_size. The message names the one that is missing.Interconnect methods entry '<id>' missing '<key>'A method in the sidecar lacks one of
IXN_spacing,IXN_pitch,IXN_pad_sizeordies.Per-method interconnect references die instance(s) not in the boundary manifestA method’s
dieslist names something the manifest does not contain. The message prints both the unresolved names and the instances the manifest does have, which nearly always makes the typo obvious. This is a hard error rather than a warning because the alternative is silently checking fewer pads than you think. See Interconnect rules (IXN).Interconnect method '<id>': IXN_pitch (...) must exceed IXN_pad_size (...)The pitch-to-spacing conversion would produce a zero or negative spacing, that is, a check that can never fail. Fix the numbers in the methods file or the adapter.
Result database not generated: <path>KLayout exited zero but wrote no report. Check the log for what the deck did before it stopped.