Environment and path discovery¶
The ecosystem is seven repositories that have to find each other at run time.
The KiCad plugin needs the ADK’s DRC runner. The HyperLynx to GDS converter
needs the interposer PDK’s Cu-pillar generator and the interconnect PDK’s
method manifest. The interposer PDK’s bump mirror needs gds2kicad’s pin list
reader. Chiplet Studio needs to resolve the paths written inside a .chiplet
file, which was produced on someone else’s machine.
No tool contains fixed-depth path arithmetic, a parents[3] or a ../../..
reaching into a sibling repository, because that hard-codes a directory layout
that only holds on the machine where it was written. Every consumer resolves
every dependency with the same chain, implemented identically in the Python tools
and in Chiplet Studio’s C++ reader.
The discovery chain¶
First hit wins:
Environment variable naming the dependency root.
KiCad project text variable of the same name, where a board is in scope. This leg exists only in plugin contexts, since it needs a loaded board to read the variable from. Set these under
Board Setup > Text Variables.Upward walk from the consumer looking for a conventional sibling directory. Each root accepts a small closed set of candidate names. For three of the five roots that set includes the name a default clone of the upstream GitHub repository produces; for
ADK_ROOTandGDS_TO_KICAD_ROOTit does not. The table below lists them.Loud failure, naming every location that was probed and the variable to set.
A candidate is not accepted merely because the directory exists. Each root has
a marker subpath that must exist under it, and a set-but-invalid
environment value falls through to the walk rather than winning and then
failing later. This is what makes a stale INTERPOSER_PDK_ROOT pointing at a
deleted checkout harmless instead of confusing.
The variables¶
Variable |
Dependency root |
Consumers |
Value in the container |
|---|---|---|---|
|
The ADK checkout |
Plugin DRC step, gds2kicad canonical layer lookup |
|
|
Interposer PDK |
|
|
|
Interconnect PDK |
Method manifest readers, Chiplet Studio fragments, the 3D bump body and bump LEF generators |
|
|
gds2kicad checkout |
|
|
|
Base IHP SG13G2 PDK |
|
|
|
Worker interpreter, not a root |
Plugin orchestrator |
|
The container sets all of them, which is why nothing inside it needs
configuring. It additionally sets ADK_TOOLS=/opt/adk-tools as the parent of
all of them, and a KLAYOUT_PATH that registers the interposer KLayout
technology.
Marker subpaths and accepted directory names¶
The walk and the environment check share one table, reproduced identically in
hyp_to_gds.py, the ADK’s pads_vs_pillars.py and Chiplet Studio’s
ChipletFormat.cpp:
Variable |
Directory names accepted by the walk |
Marker that must exist under the root |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Two consequences are worth stating plainly. First, a development layout where
all checkouts are siblings in one directory needs almost no environment: the
walk resolves INTERPOSER_PDK_ROOT, INTERCONNECT_PDK_ROOT and
PDK_ROOT from a default clone, because for those three the upstream
repository name is itself an accepted name. ADK_ROOT and
GDS_TO_KICAD_ROOT are not: they accept adk/ADK and
gds_to_kicad/gds-to-kicad, which differ from the upstream names
IHP-Open-ADK and gds2kicad, so those two need a rename, a symlink or
the variable. Second, the accepted names are a closed set. An unknown variable name is
treated as a typo rather than as permission to expand an arbitrary environment
variable, which keeps a .chiplet file from reaching into unrelated
environment values.
Tip
The simplest correct host layout is one parent directory holding
IHP-Open-ADK renamed or symlinked to adk, OpenIntM4TM2,
IHP-Interconnect-IntM4TM2, gds2kicad renamed or symlinked to
gds_to_kicad, and Chiplets-KiCad-Plugin. Those two renames are the
only ones needed: IHP-Open-ADK and gds2kicad are the repository
names but are not accepted walk names, so each needs a rename, a symlink,
or its variable (ADK_ROOT, GDS_TO_KICAD_ROOT) set explicitly.
Where the walk starts¶
The walk climbs parent directories, but from different starting points depending on who is asking, and the difference matters when your checkouts are not siblings:
The Python tools walk up from the consuming source file.
hyp_to_gds.pywalks from its own location inside the plugin checkout; the ADK’s checks walk from the ADK root.Chiplet Studio walks up from the directory of the
.chipletfile being read, because that is the only anchor a viewer has for a file authored elsewhere.
Two lookups have their own shape¶
Most consumers use the table above verbatim. Two do not, and both differences are deliberate.
The worker interpreter¶
KICAD_CHIPLET_PYTHON names an interpreter rather than a root, and its chain
is:
The
KICAD_CHIPLET_PYTHONenvironment variable.<plugin_dir>/.venv/bin/python3, the recommended local setup.The
KICAD_CHIPLET_PYTHONKiCad project text variable, when a board is in scope.python3fromPATH, accepted only if a subprocess probe canimport klayout.dbandimport yamlwith it.
Note the order: a plugin-local ``.venv`` outranks the project text
variable. If a checkout has a stale or broken .venv, it wins over the
text variable you just set, so the fix is to remove or repair the .venv
rather than to add an override. The environment variable and the text variable
are treated as explicit overrides and are used as-is when executable, without
the import probe; only the PATH candidate is probed, because it is the only
one nobody chose deliberately.
Paths are returned absolute but never symlink-resolved. Resolving would hydrate
.venv/bin/python3 to the underlying /usr/bin/python3.12, which bypasses
the pyvenv.cfg lookup and makes the venv’s packages disappear from
sys.path. The exact path is the point.
The ADK runner, seen from the plugin¶
The plugin’s lookup for run_drc.py adds a leg in front and shortens the
walk behind:
An explicit ADK root chosen in the export dialog, the GUI face of the environment variable leg.
The
ADK_ROOTenvironment variable.The
ADK_ROOTKiCad project text variable.<plugin_dir>/../adk, a single conventional sibling rather than a full upward walk.
Each candidate is tested by whether klayout/drc/run_drc.py exists beneath
it, and the failure message lists every path that was tried.
Fail loud, do not degrade¶
When a dependency is required for correctness, the tool aborts. It names the locations it probed and the variable to set. A silent degraded mode is only acceptable when the output stays correct without the dependency.
The precedent is Cu pillars. An assembly GDS written without them would open fine, pass the assembly DRC and look fabricable, because no geometric check can flag geometry that is absent. So the converter refuses:
ERROR: Cu-pillar generation requested (connection=cupillar) but
bump_mirror is unavailable. Set INTERPOSER_PDK_ROOT to the interposer PDK
checkout (libs.tech/klayout/python/bump_mirror.py) and retry. Refusing to
emit a complete GDS without its pillars.
The same reasoning gives the whole ecosystem its rule. The ADK’s assembly DRC
aborts when the boundary manifest is missing rather than checking an assembly
with no chiplets in it. The 3Dblox export aborts when the interconnect PDK’s
bump LEF generator cannot be found. An unresolvable ${VAR} aborts instead of
leaving a literal ${VAR} in a path that would merely “not exist” later and
mask the real problem.
Two lookups deliberately degrade instead, and both print a warning:
The SG13G2 base PDK. Without
PDK_ROOT,hyp_to_gdsfalls back fromvia_stackPCells to plain rectangle vias. The fallback is correct geometry, just less detailed, so this is the designed degradation. A found-but-broken PDK, for example one missingtkinterorpsutil, reports the cause on stderr and takes the same fallback rather than killing the export.The ADK canonical layer table, as used by gds2kicad’s blackbox chiplet generator. Without the ADK it uses its built-in copy of the layer numbers and says so.
Note
If you see a tool report a missing dependency, read which variable it names before setting anything. The message is generated from the same table documented above, so it always names the variable that would have resolved the lookup and the exact paths it examined.
${VAR} references in path inputs¶
Path inputs may reference the discovery variables with ${VAR} syntax. This
is what makes a .chiplet file portable between machines whose checkouts sit
in different places.
It is accepted in:
KiCad board text variables, for example
INTERPOSER_LYP.KiCad footprint fields,
GDS_FILEandLYP_FILE.hyp_to_gdscommand-line arguments.The
.chipletentries those flow into:technologies.*.layer_propertiesandcomponents[].layout.
technologies:
intm4tm2:
layer_properties: ${INTERPOSER_PDK_ROOT}/libs.tech/klayout/tech/intm4tm2.lyp
components:
- id: U1
layout: ${GDS_TO_KICAD_ROOT}/gds_files/dies/my_die.gds
Writers copy these values verbatim. The KiCad C++ exporter and the Python
plugin writer stay byte-identical with zero emission logic, which is what lets
the byte-exact parity gate between them keep working. Readers, meaning
hyp_to_gds, the ADK’s checks and Chiplet Studio, expand each reference on
read using the discovery chain: environment, then the sibling walk, then loud
failure naming the variable. There is no project text variable leg here, since
a reader may have no board in scope.
Everything else about path handling is unchanged: a path without ${ passes
through untouched, absolute paths stay absolute, and a relative path resolves
against the directory of the .chiplet file. Absolute paths remain the
default emission, because a .chiplet file is machine-local unless the board
data opts into the ${VAR} form. If you receive a foreign .chiplet that
uses absolute paths, adjusting them is your job; if it uses ${VAR} forms and
you have the checkouts, it resolves on your machine unchanged.
Two failure modes are both hard errors rather than warnings: an unknown or
unresolvable variable name, and a malformed reference such as an unterminated
${. Neither is allowed to survive into a path.
Setting the environment¶
In the container, nothing to do. Everything in the table is already set.
On a host, export the roots from your shell profile:
export ADK_ROOT=$HOME/git/IHP-Open-ADK
export INTERPOSER_PDK_ROOT=$HOME/git/OpenIntM4TM2
export INTERCONNECT_PDK_ROOT=$HOME/git/IHP-Interconnect-IntM4TM2
export GDS_TO_KICAD_ROOT=$HOME/git/gds2kicad
export PDK_ROOT=$HOME/git/IHP-Open-PDK
export KICAD_CHIPLET_PYTHON=$HOME/adk-venv/bin/python3
Launch KiCad from a shell that has these exported, so the plugin inherits them. A desktop launcher started by the session manager does not see variables set in an interactive shell profile.
Per project, use KiCad text variables instead. Board Setup > Text Variables
accepts the same names, applies only to that board, and travels with the
project file, which is the right granularity when one design needs a different
ADK revision from the rest of your work.
Tip
To find out which interpreter the plugin will actually use before running anything, open the export dialog: it displays the resolved worker path and the leg of the chain it came from.
See also¶
The ADK-Tools image for the environment as the image sets it.
Installing on the host for obtaining the checkouts these variables point at.
The .chiplet assembly file for the file the
${VAR}forms appear in.