Core concepts & tutorials

The core idea: geometry-first

klink does not try to "generate a whole layout from a sentence". Its method is geometry-first: geometry (hand-drawn or generated) comes first, you mark intent on it (Ports and Anchors), routing algorithms complete the wiring, and structured geometry queries plus live LVS decide "done". The human keeps GUI freedom; the agent reads, writes, moves, records, routes, and verifies real geometry.

The geometry-first idea

Traditional parametric-layout tools (stretch-handle PCells) assume shapes are rectangles — pull an edge and that equals a parameter W. CMOS is all boxes, so it works. But photonics, flexible electronics, MEMS and superconducting circuits are not boxes: graded tapers, arc bends, free topologies — there is no "pull that edge = a parameter" mapping.

klink takes another path: lightweight semantic annotation rather than a complete constraint system. You mark intent at the geometric positions you are sure of; routing algorithms fill in between the markers; if it's wrong, add more marks and re-run. It is an iterative, convergent process, not a one-shot black box. Two kinds of marker:

Port

Connectivity intent (net endpoint)

A klink_Port PCell carrying net + orientation + width. It answers "a wire on some net leaves here, in this direction, at this width".

Anchor

Routing constraint

A klink_Anchor PCell whose kind is waypoint_region / bend_region / corridor. It answers "the wire should pass here / bend here / take this channel".

These two are the input to the routing backends: mark Ports + Anchors, then call one routing.* tool and the wiring geometry is generated. This boundary keeps klink's mechanism layer process-agnostic — all process facts (layers, sizes, spacing) are passed in explicitly at call time.

Port: connectivity intent

A Port marks a net endpoint. It is a klink_Port PCell whose parameters are the single source of truth (center / orientation / width / target_layer / port_type / net) — the triangle orientation marker and text label are generated by the PCell and never drift from the parameters. Routing backends read Port markers from layer 999/99 by default.

port.mark cell="NET1" name="A" center_um=[0,0]   orientation="E" width_um=2 net="sig"
port.mark cell="NET1" name="B" center_um=[80,20] orientation="W" width_um=2 net="sig"
port.list cell="NET1"

Naming aligns with gdsfactory conventions (directional E0/N0/W0/S0, typed o1/e1). A triangle/rectangle you draw in the GUI can be snapped into a standard Port too — via direction inference + snapping to the nearest real edge (edge tolerance, 45° grid fallback). A Port is identified by an immutable name and can be updated / transformed (batch by GUI selection) / unmarked; duplicate/empty names have port.repair_names as a fallback. Full tools on MCP Reference · Ports & anchors.

Anchor: routing constraint

An Anchor expresses "how the wire should go". Read from layer 999/1 by default. Three kinds:

kindGeometry & semantics
waypoint_regionA rectangular must-pass region — the wire has to cross it.
bend_regionA triangle whose incircle is the bend search area — the wire makes its arc/corner transition here.
corridorA directional channel defined by a polyline: the wire must "enter → run along → exit", not simply cross. A plain corridor is required; labeling choice_group=BUS makes it an optional load-balancing candidate channel for routing.global_channel_cell.
anchor.mark cell="NET1" kind="waypoint_region" center_um=[40,40] radius_um=6 net="sig"

Anchors share Ports' identity model: id/name for locating, label for display, net for connectivity — three separate axes, with anchor.repair_ids as a duplicate/empty-id fallback. The split "thin plugin does create/edit, thick client does the understanding" is deliberate: how to interpret a hand-drawn marker is evolving workflow logic that should not be frozen into the KLayout plugin.

Keepout is not an anchor kind. Obstacle avoidance is the obstacle_layers you pass to routing tools — your own design's keepout layers. klink ships no default keepout layer (900/0 is a reserved keepout layer that structdevice uses internally as scratch).

Mark → route → verify

The minimal geometry-first loop:

1 · GeometryDraw or generate device/pad geometry.
2 · Mark intentport.mark + anchor.mark.
3 · Routerouting.* generates wiring.
4 · VerifyStructured report + live LVS.
port.mark cell="NET1" name="A" center_um=[0,0]   orientation="E" width_um=2 net="sig"
port.mark cell="NET1" name="B" center_um=[80,20] orientation="W" width_um=2 net="sig"
anchor.mark cell="NET1" kind="waypoint_region" center_um=[40,40] radius_um=6 net="sig"
routing.tapered_hybrid_cell cell="NET1" angle_mode="manhattan" obstacle_layers=["900/0"]
# check the result: ok=true, obstacle_hit_count=0, no sibling overlap

Routing backends divide by topology/quality (tapered hybrid main, tapered polygon continuous taper, steiner multi-terminal, damped strong clearance, global channel global-decision, multilayer escape cross-layer, gdsfactory strategies). See MCP Reference · Routing backends. The many backends reject "one universal router": they share one Port/Anchor planner, and the output differences are "form of expression", not "quality tiers".

A few principles run through the routing layer:

  • Rasterization-free geometric routing. A visibility graph + Dijkstra yields clean straight segments (not staircase artifacts), with native 0/45/90° angle modes; no dependency on klayout.db.
  • Port launch contract. A router may not treat a Port as an arbitrary point — it must launch along orientation from the center before entering global search, adding a small perpendicular dogleg if that would fold back, rather than silently rewriting the Port's direction.
  • Results must self-justify. ok / obstacle_hit_count / sibling overlap / route_count are structural quality gates; zero matches is not success. A parameter the chosen backend can't honor is an error that names the backends which do — never silently ignored.
  • Honest about limits. global channel states it is "not a full rip-up/reroute congestion router"; multilayer escape does not model via enclosure; gdsfactory's astar is fragile, so klink re-verifies and errors on a wall-crossing route.

The user-project model

klink and klink_plugin are the mechanism layer (installed packages, never edited). Your process, devices, sizes, layer numbers, DRC/LVS rules live in your project and are passed explicitly into the mechanism.

your-project/
  pdk.py          your process — the only home for process facts
  custom_devices/ build scripts / device generators the agent writes
  specs/          .klink specs, net tables, interaction intent
  out/            generated GDS / results   (never commit GDS)
  AGENTS.md       agent rules (CLAUDE.md points to it)
  mcp.example.json
Your project must never contain GDS/PDK content — neither a proprietary foundry PDK nor a device layout. The template .gitignore blocks *.gds/*.oas. Point recipe code at such files at run time (open PDKs are fine to depend on, still not committed).

Create a project

Scaffold with the CLI bundled in the wheel, then open it with your agent.

klink init my-chip
cd my-chip

Describe what you build; the agent identifies the domain, scaffolds pdk.py + a first custom_devices/ script from the matching recipe, and passes your process explicitly into klink. There is no hard-coded default project — a fresh project must name its domain and geometry tier first.

After you upgrade klink, run klink update mychip (or just klink update from inside the project folder) to refresh the starters under example_template/; your own pdk.py, custom_devices/, .klink/, out/, and specs/ are never overwritten:

python -m pip install -U klayout-klink
klink update mychip

Pick a recipe

A recipe is the reference implementation for one domain. The geometry tier says whether it needs your confidential geometry.

DomainGeometry tierPublic release status
EBL nanodeviceSelf-containedRuns offline, params inside the example.
Neural electrode harnessSelf-containedNeeds live KLayout; public example uses Port/Anchor PCells + tapered-hybrid router.
Silicon photonicsOpen or your ownPublic features use the open gf.gpdk (needs gdsfactory); swap your PDK to route yours.
Digital P&R → LVSSelf-contained or your ownThe fit-device demo runs on synthetic exemplars; swap in your device geometry. Verilog→gates needs an external yosys; the flow returns the exact fix if missing.

A new domain is just a pdk.py shaped for it + a custom_devices/ script that imports your process and calls klink explicitly. Copy the closest catalog entry and adapt — you never edit klink to add a domain.

Step-by-step tutorials

These are 7 step-by-step walkthroughs, all runnable directly from example_template/ as starters (the two digital-family ones need a live KLayout session): a tutorial and its demo are two views of the same thing. Each tutorial corresponds to one demo script and breaks it into observable stages: every step is actually drawn in a live KLayout session, screenshotted, and paired with the klink calls that produced it, then verified with a structured report -- not a screenshot. The tutorial ends with the same one-line command you can copy and run. Read them in order; each one builds on concepts from the one before it. For what each demo does, its requirements, and measured numbers, see Examples.

Getting started · nanodevice

Hall bar device

The full geometry-first loop end to end: draw the mesa/contacts/pads, mark Port + Anchor intent, route the fanout with one call, verify with the routing report. A fully offline, self-contained device; needs live KLayout.

Read the tutorial →
EBL · writefield

EBL wraparound nanodevice

Hand-drawn routing under writefield constraints: keepout boundaries, pre-planned crossing windows (corridor Anchors), stitch-patch compensation, and marking Port/Anchor after the fact instead of before.

Read the tutorial →
Batch RPC · parametric

Neural electrode array harness

Parametrized by --elec-rows; 394 geometry objects written with batched shape.insert_many calls. An almost 8x scale gap between bond pads and electrodes, closed with a corridor Anchor that forces convergence before routing all 24 nets in one pass.

Read the tutorial →
Photonics · gdsfactory

gdsfactory MZI takeover and drag-to-reroute

One import_gf_component call takes over an ordinary gdsfactory thermo-optic MZI script; after dragging any component in the GUI, the same photonics.reroute call only re-routes -- it never rebuilds a device. Needs gdsfactory.

Read the tutorial →
structdevice · digital P&R

Custom device: fit → PCell → P&R → LVS

Fit a parametric edge model from a few exemplar drawings at different sizes, register a real PCell, place 173 instances from a netlist, run FlexDR detailed routing, and finish with live LVS match=True. Starter, needs a live KLayout session.

Read the tutorial →
structdevice · padframe

A probe-card-first padframe

The probe card / pad ring exists first and the circuit must meet it: harvest the pad ring, assign net→pad, place half-in/half-out, split the power grid by region, and finish with both the card and --no-card bare-stub modes each passing live LVS match=True. Starter, needs a live KLayout session.

Read the tutorial →
passives · geometry templates

Passive-device geometry templates

Four parametric geometry templates -- IDC capacitor, square spiral inductor, SAW IDT filter, BAW/FBAR plan view -- each with a default-parameter and a variant-parameter live screenshot. A passives/-category starter, runs offline, --live pushes to KLayout.

Read the tutorial →

Offline device tutorial

Starting from the fully offline EBL wraparound or Hall bar is safest — they show "the mechanism layer holds no process facts": the example carries sizes, layer numbers and writefield rules, then calls klink.domains.nanodevice.

klink init nano-demo
cd nano-demo
python example_template/nanodevice/ebl_wraparound.py
python example_template/nanodevice/hallbar.py

From a repo checkout you can also run the public gallery directly:

python -m examples_klink.public.demos.nanodevice.ebl_wraparound
python -m examples_klink.public.demos.nanodevice.hallbar

After copying into your project, edit params first, don't touch klink/; once stable, tidy it into custom_devices/<device>.py. Per-demo detail on Examples.

Silicon-photonics example (gdsfactory bridge)

If you already have gdsfactory code, the public checkout has two related examples: low-level features (features/24_gdsfactory_route_ports.py, 30_gdsfactory_routing_zoo.py) and the full demo demos/gf_mzi_module.py — taking an ordinary gdsfactory MZI script into klink's reroute loop.

Install is always one command: pip install klayout-klink installs all of klink. This example additionally needs gdsfactory in the same interpreter (klink is an RPC client; the geometry is built client-side by gdsfactory) — klink bundles no third-party libs and names the exact pip install when one is missing. [photonics] is not a "per-domain install", just a convenience that also pulls a tested gdsfactory:

pip install "klayout-klink[photonics]"    # = klink + a tested gdsfactory
klink init my-chip && cd my-chip
python example_template/photonics/gf_mzi_module.py --port <session-port>
# from a repo checkout you can also run: python -m examples_klink.public.demos.photonics.gf_mzi_module --port <session-port>

After takeover, drag a component in the GUI and one photonics.reroute re-routes optics and metal together — see Workflows · 8082 klive display and MCP Reference · Photonics. From a plain shell, re-route with the --reroute flag, which re-routes from the dragged positions without rebuilding:

python example_template/photonics/gf_mzi_module.py --port <session-port> --reroute

For your own PDK, layers, stub rules and cross-section come from your script or pdk.py.

P&R to LVS

The public release digital path is either self-contained or your-own-geometry. The fit-device demo runs device fit, placement, routing and live LVS on synthetic exemplars. The whole digital family (fit-device, padframe, chat-to-netlist, multilayer) are starters under example_template/digital/, needing a running KLayout session:

python example_template/digital/fit_device_pnr_lvs.py --port <session-port>
# from a repo clone you can also run: python -m examples_klink.public.demos.digital.fit_device_pnr_lvs --port <session-port>

The device-agnostic custom-device flow is the structdevice.* family: give a device-level netlist → build_from_netlist auto floorplan/route/draw/LVS; or the SEND-driven declare_netsconnect_netslvs_checkspec_write. Any LVS mismatch undoes every mutation.

Verification habits

klink tutorials do not treat screenshots as proof of done. Decide with:

  • layout.info, cell.tree, layer.list for structure.
  • shape.query, instance.query, selection.get for geometry.
  • the routing report's ok, overlap, obstacle hit, route count.
  • live LVS match=True.

A route/layout is "done" only when live KLayout LVS returns match=True; marker counts and "looks routed" do not count. Screenshots can show a human the result but never replace structured verification.