Pin lists¶
A pin list is the *.pins.json artefact that carries a die’s pad names and
pad centres. It originates in IHP-GmbH/gds2kicad, where it is the single source of
truth for pin names in the human-in-the-loop symbol and footprint workflow, and
two ADK tools consume it: the 3Dblox exporter uses it to emit bump maps, and the
pad-to-pillar alignment check uses it as a pad source.
Warning
The pin list is not formally specified. There is no JSON Schema for it in
config/schema/, no version pin in any reader, and no validator beyond the
handful of keys each consumer happens to need. What follows documents the
shape as the producers write it and as the consumers read it, which is the
only contract that exists today. Treat any field not listed under
What the ADK consumers read as belonging to gds2kicad’s own workflow.
Shape¶
A pin list is a JSON object: a flat block of metadata, plus a pins array.
{
"version": 1,
"chiplet_name": "ACME_PHY",
"source": "ACME_PHY.kicad_mod",
"source_type": "kicad_footprint",
"dbu_um": 0.001,
"timestamp": "",
"pins": [
{
"name": "VDD",
"type": "power_in",
"side": "top",
"pad_index": 0,
"center_x_dbu": -180000.0,
"center_y_dbu": 240000.0,
"width_dbu": 44000.0,
"height_dbu": 44000.0
}
]
}
Metadata¶
Field |
Type |
Meaning |
|---|---|---|
|
integer |
Pin list format version, currently |
|
string |
Cell or chiplet name the pins belong to. |
|
number |
Database unit in microns for the |
|
string |
The GDS or footprint the pins were extracted from. The two producers use different key names. |
|
string |
|
|
string, string, array |
Extraction provenance, written by the GDS path only. |
|
string |
ISO timestamp on the GDS path, empty string on the footprint path. |
Pin entries¶
Field |
Type |
Meaning |
|---|---|---|
|
string |
Pad name. Falls back to the one-based pad index when the extractor found no label. |
|
number |
Pad centre in die-local database units, y-up. Multiply by |
|
number |
Pad bounding-box size in database units. |
|
integer |
Index of the pad in the extraction order. |
|
string |
KiCad pin electrical type: one of |
|
string |
Symbol side: |
|
array of |
Optional exact pad contour, written by the GDS path when the pad is not a rectangle. Omitted otherwise. No ADK consumer reads it. |
Coordinate frame¶
Pin coordinates are die-local, y-up, relative to the die GDS origin, which is
the gds_origin anchor of the frame contract. Both ADK consumers reject a die
that declares bbox_center, or no anchor at all, rather than silently
misplacing its pads by the bounding-box corner offset.
The coordinates are also unmirrored: they describe the die as drawn, not as placed. Flip-chip handling belongs to the consumer, and each one applies it.
Note
footprint_to_pinlist.py has to undo two transforms to get back to that
frame. KiCad footprints are y-down, so it negates y; and when the footprint
was generated with ORIENTATION=flip_chip, gds2kicad had already mirrored
x when writing it, so it mirrors x back. The result is the original die
frame rather than a mixed die and footprint frame.
How pin lists are produced¶
From a die GDS¶
python3 gds_to_kicad_symbol.py die.gds \
--lyp-file pdk.lyp --pad-layer TopMetal2.drawing \
--extract-pins die.pins.json
Pads are extracted from the pad layer, names are paired to pad polygons by
proximity to text labels, and classify_pin assigns an initial symbol side
and type. The result is meant to be reviewed: names from a real die are often
wrong or missing, and the file is the place to fix them. The Pin List Editor tab
of unified_gui.py edits the same file.
For a full die, auto-detection on the raw GDS is usually not enough. Real silicon is mostly routing, fill and guard rings, and the extractor cannot tell those from bond pads. The normal path is to generate a pad-review GDS, delete everything that is not a pad in KLayout, and build from the cleaned result with the pin list supplying the authoritative names.
From a KiCad footprint¶
python3 footprint_to_pinlist.py die.kicad_mod -o die.pins.json
Parses the (pad ...) blocks of a .kicad_mod, converts millimetres to
database units at --dbu (default 0.001), and applies the y and flip-chip
corrections described above. This is the path the ADK’s own documentation points
at, and the one that produces a file the ADK consumers can read unmodified.
The dbu_um caveat¶
The two producers do not write the same metadata, and the difference matters.
footprint_to_pinlist.py writes dbu_um. The PinList writer behind
--extract-pins and the GUI’s save action does not: its metadata block is
version, chiplet_name, gds_source, lyp_file, pad_layer,
text_layers and timestamp.
Both ADK consumers go through chiplet2dbx.load_pinlist, which requires
dbu_um and raises otherwise:
ExportError: pin list <path> lacks dbu_um or a non-empty pins array
So a pin list extracted from a GDS needs the key added before the ADK can read
it. PinList.load preserves every metadata key it does not recognise and
PinList.save writes it back, so adding "dbu_um": 0.001 by hand survives
a later edit round trip through the editor.
Tip
Take dbu_um from the die layout it was extracted from, not from a
default. gds2kicad resolves the database unit per GDS, and a die drawn at
0.005 um DBU with 0.001 recorded would place every bump 5 times too far
from the die centre.
What the ADK consumers read¶
chiplet2dbx.load_pinlist reduces the file to the three fields the ADK cares
about, and nothing else in the artefact is consulted:
load_pinlist(path) -> [{"name": str, "x_um": float, "y_um": float}, ...]
name <- pins[i].name
x_um <- pins[i].center_x_dbu * dbu_um
y_um <- pins[i].center_y_dbu * dbu_um
A pin missing any of the three keys, or carrying a non-numeric coordinate, is a
hard error naming the offending index. An empty pins array is a hard error
too: a pin list that resolves to no pads is a broken input, not a die with no
pads.
Consumer 1: bump maps for the 3Dblox export¶
python openroad/chiplet2dbx.py --chiplet design.chiplet \
--out-dir build/3dblox \
--pins U1=chiplets/die_a.pins.json \
--pins U2=chiplets/die_b.pins.json
Supplying a pin list for a die turns its export bump-aware. The die’s
ChipletDef gains a .bmap bump map plus a per-method bump macro LEF
rendered by the interconnect PDK’s generator, which is what activates the
check_3dblox bump alignment check in OpenROAD. Without pin lists the export
is purely a black-box geometric view.
The transform into the def-local frame is a translation and, for flip-chip
dies, a mirror. The def-local frame is the outline rectangle
(0, 0)..(width, height) with the die centre at (width/2, height/2):
x = width / 2 + (-pin.x_um if flip_chip else pin.x_um)
y = height / 2 + pin.y_um
The mirror is applied here because this ecosystem realises a flip as a layout mirror, while the target format’s flipped orientation leaves bump x and y unchanged.
Three behaviours are worth knowing before you feed a pin list in:
A pin outside the outline is fatal. If any transformed pin falls outside
0..widthby0..height, the export aborts and names up to five offenders. It means the pin list and the.chipletdimensionsdisagree, which is a real error the exporter refuses to round away.A pin list requires a connection method. The bump map is keyed by method, so a die with pins but no
connection:in the.chipletis an error.Pin lists split defs. A bump map is a property of the
ChipletDef, not of the instance. Two dies that share artwork but differ in attachment method or net binding are emitted as separate defs, and two dies that would share a def but were given different pin lists are an error.
Pad names are sanitised to [A-Za-z0-9_] and de-duplicated with _2,
_3 suffixes for the bump map. The port and net columns are filled
only when the .chiplet netlist: block binds that pad name to a net;
otherwise both are -, because the target warns on a port with no net.
Consumer 2: pad positions for the alignment check¶
python checks/pads_vs_pillars.py --chiplet design.chiplet \
--pillars build/design_interposer.pillars.json \
--pins U1=chiplets/die_a.pins.json
Here the pin list is the pad source: the positions the design intends, which the check compares against the positions the producer drew, recorded in the Pillar manifest. Die-local pads are mapped into the canonical assembly frame through the die’s placement,
global = position + R(rotation.z) * M * pad
with M = diag(-1, 1) for flip_chip dies and identity for face_up,
then matched against that die’s pillar entries by pin name, falling back to
nearest-unique matching when either side is unnamed.
pads_vs_pillars.py accepts a second pad source that is not a pin list.
--gds-pads REF extracts pad centres directly from the die’s .chiplet
layout GDS, using the pad_drawing and pad_text layers declared in
config/chiplet_pads.json (205/0 and 205/25; the numbers are never
hardcoded in the tool). It yields the same in-memory
{name, x_um, y_um} records, so the rest of the check is identical. Use it
for black-box or pads-only chiplets, where the die GDS already is the pad map;
use --pins when the names live in a reviewed pin list. A die given both is
an error.
Tip
Pass a pad source for every die that declares a connection method. A die
without one is silently unchecked, reported only as a warning. --strict
promotes that warning to a NO_PAD_SOURCE finding, which is what you want
in CI.
See gds2kicad for the tools that produce pin lists, OpenROAD 3Dblox export for the 3Dblox export and Pad to pillar alignment for the alignment check.