Core concepts

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. These concepts are the shared background for the step-by-step tutorials -- read this once before diving into them.

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.

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.