Anatomy of a PDK
A lumicron PDK is a Python package - by convention, lumicron.pdks.<name> - exposing these namespaces:
| Namespace | Type | Purpose |
|---|---|---|
LAYER |
LayerTable |
Named layers + design rules. |
STACK |
LayerStack |
Physical thicknesses + dispersive indices for simulators. |
RP |
RouteProfiles |
Cross-section recipes routes can adopt. |
| (top-level) | @pcell factories |
Component library - EdgeCouplerSi, Pad, NxM_MMI, … |
TRANSITIONS |
TransitionRegistry |
Optional. Maps (layer_a, layer_b) to a bridge PCell. |
META |
dict |
Display name, import package name, version, and publisher metadata. |
The conventional file layout:
lumicron/pdks/<name>/
__init__.py # re-exports layers, stack, profiles, and components
all.py # convenience: `import ... .all as pdk`
layers.py # LAYER, STACK
profiles.py # RP, plus @route_profile defs
components.py # @pcell factories
transitions.py # TRANSITIONS, registers into default_transitions()
meta.py # META: display/import identity + release metadata
Each file is small. The discipline is keeping them small - when a PDK gets big, prefer adding new modules (pcells/, tests/) over fattening the four canonical files.
How an end user sees it
import lumicron as lm
import lumicron.pdks.elyon_demo.all as pdk
pdk.LAYER.SILC # named layer with design rules
pdk.RP.silc_strip # route profile
pdk.EdgeCouplerSi(...) # component factoryThe import segment is the PDK’s META["package-name"], not its human display name. Keep both explicit:
# meta.py
META = {
"name": "TinyPhot Research PDK", # shown in Lumicron
"package-name": "tinyphot", # used in Python imports
"version": "0.1.0",
}Display names may contain spaces; package names must be valid Python identifiers. New packages use package-name; the current packer rejects package_name.
The .all re-export gives end users the convention of one short import. Implementing it is a single file:
# lumicron/pdks/<name>/all.py
from .layers import LAYER, STACK
from .profiles import RP
from .transitions import TRANSITIONS
from .components import * # noqaThe transitions import - even though TRANSITIONS itself is rarely referenced by name - is what registers the transitions into the process-wide default registry. Without it, end users would have to remember to import the module or pass transitions= on every cross-layer route. With it, things just work.
What ships in the package vs the manifest
Two questions that come up:
- “Should the layer table go in Python or in JSON?” Both. The Python
LayerTableis the source of truth - the router reads it, components reference it. The.lumpdkmanifest exposes a JSON layer map so the Mac app can render colors / names without importing the Python source. They’re generated from each other; inlumicron,LayerTable.to_manifest()produces the JSON. - “Should the layer stack ship?” Yes when you have it. End-user scripts that drive simulators read it. PDKs without a known stack can ship Python-only and add the stack later - components and routing don’t depend on it.
What this guide does not cover
- DRC rules beyond the per-layer minimums. The full Lumicron DRC system is documented separately in
Docs/DRC_DESIGN.md(forthcoming). - Verification / LVS. Same - separate doc.
- Foundry-confidential details. Whatever you ship in your PDK is up to you and your foundry agreement; this guide is process-agnostic.