Repository navigation
Releases: 2AMLogic/klayout-tools
Release list
v0.5.0
235 commits on main since v0.4.0, cut under the 25-commit backstop in
RELEASING.md's "Release cadence" (operator ruling 2026-09-15,
issue #1563). Minor bump: the range carries no conventional-commit breaking
marker (!: / BREAKING CHANGE) and no schema_version value under src/
changed; compatibility notes, where they matter, are in the entries below.
- Added:
klt place-and-routeresponse fieldnominal_hold_slack_ns
(issue #1826) — the single, nominal-corner hold WNS
(report_worst_slack_metric -hold), populated from the"place"stage
onward, mirroring the existing setup-sideworst_slack_ns. Distinct from
the route-stage-only, corner-sweptworst_hold_slack_nsaggregate (issue
#949) — this field exists so a caller can get a hold slack number
before a full route, not justhold_violation_count's pass/fail count. - Added:
klt place-and-routeresponse fieldunrouted_def_path
(issue #1826) — a pre-route DEF, populated whentarget_stageitself is
"place"or"cts", reusing (and, for"cts", newly adding) the
write_defcall each of those stages' own Tcl generator already runs.
klt sta's newdef/geometry_source: "placement_estimate"request
fields (below) accept this path directly, closing the gap where an SDC-
driven setup/hold slack characterization required a full route. - Added:
klt starequest fieldgeometry_source("routed", the
default, or"placement_estimate"; issue #1826), echoed back verbatim as
the response's owngeometry_sourcefield. Declares whetherdefis a
fully-routed signoff geometry or a pre-route (klt place-and-route
"place"/"cts"-stage) estimate — a caller-supplied label, since a bare
DEF file carries no stage-provenance metadata of its own. See
docs/cli/sta.md's "Pre-route DEFs" section. - Added:
klt arith-gen— generate a parallel-prefix adder as Verilog
RTL from one of five named architectures (ripple,brent-kung,
han-carlson,sklansky,kogge-stone) or from an explicit N×N binary
cell map (issue #1722, new moduleklayout_tools/arith_gen.py). Pure
Python string generation — no Yosys, no PDK, no engine. Writes the
structural adder, a behaviourala + b + cinreference with identical
ports, a self-checking testbench, a Yosystechmap -maprule file
substituting the adder for same-width$addcells, and a ready-to-run
klt equivrequest pairing the reference (gold) against the adder (gate).
Reportsprefix_cells/logic_levels/max_fanoutso the cell-count vs.
depth vs. fanout trade is visible without synthesizing. The representation
(cell map) and the measured-in-loop selection below are reimplemented from
Lai et al., Scalable and Effective Arithmetic Tree Generation for Adder
and Multiplier Designs (NeurIPS 2024, arXiv:2405.06758); their RL/MCTS
search is deliberately not reimplemented. See
docs/cli/arith-gen.md. - Added:
klt synthesizerequest fieldarithmetic(issue #1722) —
{"adders": "auto" | "default" | "<architecture>", "min_width": 8, "candidates": [...], "verify_adders": true}. Substitutes a generated
prefix adder for Yosys's own$addexpansion via atechmap -maprule
inserted betweenhierarchyandsynth, guarded by_TECHMAP_FAIL_so
only the probed widths are replaced."auto"runs a full trial synthesis
per candidate plus Yosys's own default expansion, and keeps the
smallest candidate meetingconstraints.clock_period_ns(with an explicit
reported fallback when none does). Every substituted adder is proven
equivalent to a behaviourala + b + cinbyklt equivbefore it is kept
— scoped to the adder module, so the gate works on sequential designs too.
Additive: the new response fieldarithmeticisnullfor every request
without the field, andschema_versionis unchanged. Measured on the
modexpcanary atWIDTH=16(gf180mcu,clock_period_ns: 22): Yosys's
own expansion missed the constraint at 23.79 ns while all five prefix
architectures met it, and the selection kept the smallest of those.
Multipliers (compressor trees) and search over cell maps are explicit
non-goals. Seedocs/cli/synthesize.md's "Arithmetic architecture"
section anddocs/design/synthesize-qor-improvements-survey.md§3.8. - Added:
klt sim --plot <dir>— renders one self-contained,
dependency-free waveform SVG per non-sweep signal per corner (issue
#1723), reusingklt trajectory --plot's no-plotting-library, string-
built SVG approach (new moduleklayout_tools/sim_plot.py). Forces
options.waveforms/keep_artifactson for the run regardless of the
request's own settings — a waveform can only be plotted once captured.
acplots use a log10-scaled sweep axis; atranmeasurement whose
.meascard's own signal matches a rendered signal additionally gets its
from=/to=window shaded and itsWHEN <signal>=<value>threshold
drawn as a reference line. Every written file is listed in the response's
newplotsfield and each corner'sartifacts.plots; a measurement's
rollup entry additionally gets aplotfield (the SVG for its own
worst_casecorner, ornull) — so a miss report names the picture to
open next, without the numeric threshold-crossing extraction that #56
covers separately. All additive/optional (present only when--plotis
used);schema_versionunchanged. Also hardens the pre-existing
options.waveformscapture path: a corner whose rawfile
parse_ascii_rawfilecannot parse (currentlyac/sp's complex
real,imag-encoded values — tracked as issue #1756, out of this issue's
own scope) now reports a"warning"diagnostic and leaves
artifacts.waveform/plots empty instead of raising and aborting the
whole sweep. Seedocs/cli/sim.md's "Waveform plots" section. - Added:
klt kb validate --recheck-measured— re-runs every distinct
measured.figures[].testbenchinkb/entries/*.jsonviaklt simand
fails validation when a recordedfigures[].valuehas drifted beyond a
fixed 1% relative tolerance from what that testbench produces now, instead
of only checking that the testbench path exists (issue #1726). Off by
default and absent from the every-PRklt kb validateCI gate — actually
simulating every measured entry is expensive — it is what the new nightly
.github/workflows/kb-drift-canary.ymlworkflow
(schedule+workflow_dispatchonly, mirroringequiv-canary.yml's
posture for an expensive non-blocking-on-PRs simulation gate) runs. A
drifted figure fails that job and is never auto-corrected.klt kb validate's JSON payload shape is unchanged (drift shows up as additional
entries[].errors[]strings, so noschema_versionbump). Also raises
examples/kb/pfd-charge-pump-tri-state/request.json's
options.timeout_sfrom60to900: that transient needs ~4 minutes of
wall clock, so its own checked-in request could not reproduce the figures
the KB entry records from it. Seedocs/cli/kb.md's "Recheck measured
figures" section. - Added:
klt sim --op-lint— a per-device operating-point sanity lint
("op-sanity") that answers "which MOSFET is not doing its job, and why?"
instead of running the corner sweep (issue #1718). It takes the same
request document (onlynetlistis required), runs one.opat one
corner (--op-lint-cornerpicks which), and walks every MOS instance in
the netlist reporting:off(|Vgs| < |Vth|withIdat/below a noise
floor),triode(|Vds| < |Vdsat|plus a corner-aware margin
defaulting to 2 kT/q at that corner's temperature), wiring smells
(drain_tied_to_rail,gate_shorted_to_source,bulk_not_tied), and
netlist hygiene (missing_nodefor a declared/.meas-referenced node the
netlist never creates,floating_nodefor a single-terminal node). Each
finding names the device, its four terminal nodes, the model-reported
values, and a one-line suggestion; per-deviceVthis read from the model
(@m…[vth]), never a constant. Exit codes reuseklt sim's vocabulary
against this mode's own verdict (0no error-severity finding,3at
least one,4the operating point itself is untrustworthy). The same
entry point is exposed as a newklt evalgate kind,op-sanity— the
cheapest useful analog gate, meant to sit before asimgate in a
descriptor. New moduleklayout_tools/op_sanity.py
(schema_version: 1); no new dependency (it reuses the ngspice
print @m…[param]pathklt sizealready uses). The corner-sweep path is
unchanged when--op-lintis absent. Seedocs/cli/sim.md's
"Operating-point lint" anddocs/cli/eval.md. - Added:
klt extract --parasiticsnow reports a per-netby_layer[]
breakdown alongside each net's scalarresistance_ohm/capacitance_ff
totals (issue #1701) —{"layer", "resistance_ohm", "capacitance_ff"}per
contributing conductor role ("diffusion"/"poly") or metal level
("metal<i>", 0-based), letting a caller composing a hierarchical netlist
from separately-extracted sub-blocks attribute a net's R/C to a specific
layer instead of hand-subtracting scalar totals. Purely additive (no
schema_versionbump perdocs/json-contract.md's additive-field policy)
and derived entirely from intermediates_compute_parasiticsalready
computed internally — no new geometry pass. Summingby_layer[].resistance_ ohm/.capacitance_ffacross one net's list reproduces that net's own
scalar totals, except for a net named by--mom-net/--mom-rlc-net(those
substitute the scalar totals with a value this lumped-RC breakdown did not
compute). Seedocs/cli/extract.md's "Per-layer breakdown" section. - Fixed: the curated
gf180mcuDRC deck now checks Metal4's own minimum
width and spacing —metal4.width.1andmetal4.space.1, both 0.28 um
(280 dbu) on layer ...
v0.2.0
0.2.0 (2026-08-04)
The first release since 0.1.0. klt grew from 5 verbs to 24; the 19
net-new verbs below all existed on main but had never shipped in a
numbered release, so anyone installing from PyPI has until now been getting
a tool bearing little resemblance to the documented one.
Additive at the CLI surface — no existing verb was renamed or removed. One
per-command breaking change is included (klt precheck's own
schema_version moved 1 -> 2, detailed below); per
docs/json-contract.md that is scoped to that
command and does not affect any other verb.
Added — new verbs
klt precheck— pre-flight layout checks before an expensive runklt socket-check— socket/pin-level connectivity checksklt ring-check— guard/collector ring integrity checksklt layout-metrics— quantitative layout measurementsklt render— raster/vector rendering of layout viewsklt extract— parasitic/device extraction with provenance hashingklt lvs— layout-versus-schematic comparisonklt gen— parametric analog cell generationklt gen-compose— composition and routing of generated blocksklt draw— primitive layout drawingklt sim— circuit simulation, including the remote AWS backendklt report— aggregated run reportingklt kb— knowledge-base query/updateklt trajectory— append-only optimization-run logs (--plot)klt synthesize— RTL synthesis (Yosys engine class)klt place-and-route— physical implementation (OpenROAD engine class)klt functional-verification— cocotb testbenches via Icarus/Verilatorklt eval— singlevalid/objective/metricsverdict over gate checksklt lef-abstract— LEF abstract generation
Each is documented in docs/cli/.
Changed — behavior detail
The user-visible, additive behavior changes worth calling out explicitly,
because they affect a verb's output. Not an exhaustive commit-by-commit log.
- 2026-08-04 —
klt sim: wall-clock budget, orphan safety, and resume for
corner sweeps (#482).options.wall_clock_budget_s/--budget-sbounds
the overall sweep (distinct from the existing per-corner
options.timeout_s); an always-on parent-PID liveness check stops the
local/local-parallel dispatch loop the instant the launching process
exits; andoptions.resume/--resumecheckpoints completed corners to
checkpoint.jsonand skips them on a later matching invocation. Skipped
corners are reported asstatus: "error"with
budget_exceeded/orphaneddiagnostics, andenvironment.budget/
environment.orphaned/environment.resumesummarise the outcome —
purely additive. - 2026-08-04 —
klt extract: comma-joined multi-label net collision
detection (#481, closes #470). Any net whose KLayout-assigned name is a
comma-joined merge of 2+ distinct labels (e.g.Y,Y2) — a silent signal
that two differently-named nets were shorted together in the layout, most
commonly agen-composepins[]entry naming a port other connectivity
already reaches — is now flagged in a new top-level
merged_net_labels[]array ({"net", "labels"}per affected net) plus a
matchingwarnings[]entry. Purely additive:
net_count/nets[]/pin_countare unchanged. - 2026-08-04 —
klt gen-compose: self-net short detection now reads drawn
pad geometry, not just port adjacency (#472, closes #469).
route_two_pin()gains a fourth self-net check that intersects the
route's own drawn metal against the block's actual drawn shapes on the
route layer, generalising #467's same-row/same-direction heuristic
(still kept) to same-facing pin pairs on different rows and wider routes
that reach an adjacent row's pad — cases missed because a port's
reportedwidth_umsystematically under-states the pad metal drawn
around it. - 2026-08-04 —
klt gen:res_arraycan draw sky130's higher-sheet-rho
precision-resistor flavours (#475, closes #463). A validatedflavor
request param ("generic"/"high"/"xhigh", mirroringmos_array's enum
pattern) resolves a per-family implant/block layer pair sourced from the
extraction deck's ownrequireslayers (sky130res_high_po= psdm +
rpm;res_xhigh_po= psdm + urpm), so the generated array extracts as
the requested device class instead of alwaysres_generic_po. Default
"generic" reproduces prior geometry exactly on both families; an
unsupported flavour raises a clearGenError. - 2026-08-04 —
klt gen: MOS generators draw a poly gate landing pad past
the diffusion so gate contacts land legally (#474).mos_arrayand
diff_pairpreviously drew gate poly sharing the diffusion's exact
extent, leaving no DRC-legal spot for a gate contact. The shared unit
layout now adds a landing pad extending past the diffusion's gate-side
edge on the first finger and reports the gate port at the pad centre —
a JSON-contract-visible change: the gate port'sy_ummoves into the
extension and itswidth_umbecomes the pad width, and
bbox_height_umnow spans diffusion plus pad whileheight_umstays
the bare diffusion height. A contact placed at the reported gate port is
now DRC-clean on the curated sky130 deck. - 2026-08-04 —
klt extract:dummymarker-layer suppression (#295)
extended from MOS gates to resistors and bipolars (#471, closes #462).
Thedummyregion is now subtracted from each candidate resistor body
before device recognition and frombipolar_basebefore
emitter/collector are derived, with fully-covered components counted
into the shareddummy_devices_dropped— so matched resistor/bipolar
arrays built with dummy edge units no longer inflatedevice.unmatched
underklt lvs, and a fully dummy-suppressed poly resistor no longer
trips a falsemarked_unrecognisedwarning. - 2026-08-04 —
klt lvs: newdevice.combine_incompletemismatch category
(#466). Only possible withoptions.combine_devices: true. KLayout's own
klayout.db.Netlist.combine_devices()can raise an unhandled
internal-consistencyRuntimeErroron a partial-match device group — N
real (matching-relevant) instances plus M dummy instances that all share
two of three terminals (e.g. a bipolar device's base and collector, tied
to a matched array's common well and substrate), but only the N real
instances additionally share the third (e.g. an emitter bussed to one
signal net).klt lvsnow catches that one error shape per netlist
(narrowly — only aRuntimeErrorcarrying KLayout's own"...in Netlist.combine_devices"marker text; any otherRuntimeErrorstill
propagates as an application error) instead of letting it abort the whole
run: whatevercombine_devices()already merged stays merged, that
netlist's remaining devices are left as individual devices, and a
severity: "warning"entry (never changesstatus, never breaks
mismatch_count's error semantics) records that combine did not fully
apply on that side. Purely additive (noschema_versionbump), but adds a
category_counts["device.combine_incomplete"]entry for any
combine_devicesrun that trips the KLayout error — see
docs/cli/lvs.md'sdevice.combine_incompletesubsection for the full
trigger conditions. - 2026-08-04 —
klt gen+klt gen-compose: ring routing openings (#434).
guard_ring,diff_pair(add_guard_ring) andbjt_array
(add_collector_ring) acceptring_gap_side(""/"N"/"S"/"E"/
"W"),ring_gap_umandring_gap_offset_um, cutting one opening
through the ring's band — on every layer the ring is drawn on, dropping
any contact it would clip — so the ring stays a single connected C-shaped
conductor rather than splitting into two arcs. The opening is reported as
a newGAP_<side>entry inports[](width_um= the opening's length
along that side,direction_deg= the side's outward normal), and
klt gen-composeuses it to admit a route to a ringed block's non-tap
port — but only when the drawn backbone actually passes through the
opening with half the route width plus the block's own
drc_hints.min_spacing_umof clearance from either cut end; a crossing on
any other side, a crossing that misses the opening, and a segment laid
along a ring side are all still reported inunrouted_nets[]. With no
opening declared, the #199 closed-ring rejection is unchanged (its message
now also names the new remedy). Wiring or labelling aGAP_*port is an
application error (exit1). Before this, a matched analog group could
keep its default guard/collector ring or be wired into the rest of a
composed circuit, not both. Seedocs/cli/gen.md("Ring routing
openings") anddocs/cli/gen-compose.md. - 2026-08-03 — Breaking (per-command
schema_versionbump 1 -> 2):klt precheck:layer_whitelistviolations'shapescount is now weighted by
placement multiplicity across the full cell hierarchy, not summed once per
cell definition (issue #452). Previously,layout.each_cell()counted
each cell definition's own (non-recursive) shapes exactly once regardless
of how many times that cell was actually placed, under-reporting true
placed-shape prevalence by roughly two orders of magnitude on real
hierarchical, macro-scale input — e.g. a layer drawn 800 times across 320
placedsky130_fd_sc_hdinstances reported"shapes": 10. The fix
multiplies each cell definition's own-shape count by its total placement
multiplicity across all instantiation paths (multiplicities compose
multiplicatively for nested placement — a cell placed inside a cell that
is itself placed multiple times gets the product, not the sum), via a
top-down hierarchy walk (Layout.each_cell_top_down()/Instance.size())
rather than a full recursive shape flatten. Onlyklt precheck's own
schema_versionmoved to2; every other command'sschema_versionis
unaffected, and a p...
v0.1.0
Initial release — the agent-native IC layout toolkit, first cut.
Added
kltCLI with five headless, JSON-contracted verbs:klt layers— layer/datatype enumeration for GDSII/OASIS streamsklt stats— bounding box, drawn area, density, polygon/vertex counts (--per-layer)klt cells— cell hierarchy: top cells, shape/instance counts, bboxes (--top)klt drc— headless DRC via KLayout's native Region check primitives, with
curated width/space/enclosure decks for sky130 and gf180mcu (--deck)klt pdk find|list|env— discovery/resolution of open_pdks-layout PDK installs
- Shared JSON output envelope (
schema_version, error shape, exit codes) across
all verbs —docs/json-contract.mdis the API scripts/fetch-pdks.sh— pinned fetch of lambdapdk open PDK datakb/knowledge-base scaffold with JSON Schema and seed entries- sky130/gf180mcu test corpus with golden fixtures; CI (ruff + pytest, Python 3.10–3.13)
- Docs: architecture, JSON contract, per-verb CLI references, macOS KLayout