Skip to content

Releases: 2AMLogic/klayout-tools

v0.5.0

Choose a tag to compare

@rjwalters rjwalters released this 15 Sep 02:19
6bf5610

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-route response field nominal_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-side worst_slack_ns. Distinct from
    the route-stage-only, corner-swept worst_hold_slack_ns aggregate (issue
    #949) — this field exists so a caller can get a hold slack number
    before a full route, not just hold_violation_count's pass/fail count.
  • Added: klt place-and-route response field unrouted_def_path
    (issue #1826) — a pre-route DEF, populated when target_stage itself is
    "place" or "cts", reusing (and, for "cts", newly adding) the
    write_def call each of those stages' own Tcl generator already runs.
    klt sta's new def/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 sta request field geometry_source ("routed", the
    default, or "placement_estimate"; issue #1826), echoed back verbatim as
    the response's own geometry_source field. Declares whether def is 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 module klayout_tools/arith_gen.py). Pure
    Python string generation — no Yosys, no PDK, no engine. Writes the
    structural adder, a behavioural a + b + cin reference with identical
    ports, a self-checking testbench, a Yosys techmap -map rule file
    substituting the adder for same-width $add cells, and a ready-to-run
    klt equiv request pairing the reference (gold) against the adder (gate).
    Reports prefix_cells/logic_levels/max_fanout so 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 synthesize request field arithmetic (issue #1722) —
    {"adders": "auto" | "default" | "<architecture>", "min_width": 8, "candidates": [...], "verify_adders": true}. Substitutes a generated
    prefix adder for Yosys's own $add expansion via a techmap -map rule
    inserted between hierarchy and synth, 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 meeting constraints.clock_period_ns (with an explicit
    reported fallback when none does). Every substituted adder is proven
    equivalent to a behavioural a + b + cin by klt equiv before it is kept
    — scoped to the adder module, so the gate works on sequential designs too.
    Additive: the new response field arithmetic is null for every request
    without the field, and schema_version is unchanged. Measured on the
    modexp canary at WIDTH=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. See docs/cli/synthesize.md's "Arithmetic architecture"
    section and docs/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), reusing klt trajectory --plot's no-plotting-library, string-
    built SVG approach (new module klayout_tools/sim_plot.py). Forces
    options.waveforms/keep_artifacts on for the run regardless of the
    request's own settings — a waveform can only be plotted once captured.
    ac plots use a log10-scaled sweep axis; a tran measurement whose
    .meas card's own signal matches a rendered signal additionally gets its
    from=/to= window shaded and its WHEN <signal>=<value> threshold
    drawn as a reference line. Every written file is listed in the response's
    new plots field and each corner's artifacts.plots; a measurement's
    rollup entry additionally gets a plot field (the SVG for its own
    worst_case corner, or null) — 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 --plot is
    used); schema_version unchanged. Also hardens the pre-existing
    options.waveforms capture path: a corner whose rawfile
    parse_ascii_rawfile cannot parse (currently ac/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. See docs/cli/sim.md's "Waveform plots" section.
  • Added: klt kb validate --recheck-measured — re-runs every distinct
    measured.figures[].testbench in kb/entries/*.json via klt sim and
    fails validation when a recorded figures[].value has 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-PR klt kb validate CI gate — actually
    simulating every measured entry is expensive — it is what the new nightly
    .github/workflows/kb-drift-canary.yml workflow
    (schedule + workflow_dispatch only, mirroring equiv-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 no schema_version bump). Also raises
    examples/kb/pfd-charge-pump-tri-state/request.json's
    options.timeout_s from 60 to 900: 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. See docs/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 (only netlist is required), runs one .op at one
    corner (--op-lint-corner picks which), and walks every MOS instance in
    the netlist reporting: off (|Vgs| < |Vth| with Id at/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_node for a declared/.meas-referenced node the
    netlist never creates, floating_node for a single-terminal node). Each
    finding names the device, its four terminal nodes, the model-reported
    values, and a one-line suggestion; per-device Vth is read from the model
    (@m…[vth]), never a constant. Exit codes reuse klt sim's vocabulary
    against this mode's own verdict (0 no error-severity finding, 3 at
    least one, 4 the operating point itself is untrustworthy). The same
    entry point is exposed as a new klt eval gate kind, op-sanity — the
    cheapest useful analog gate, meant to sit before a sim gate in a
    descriptor. New module klayout_tools/op_sanity.py
    (schema_version: 1); no new dependency (it reuses the ngspice
    print @m…[param] path klt size already uses). The corner-sweep path is
    unchanged when --op-lint is absent. See docs/cli/sim.md's
    "Operating-point lint" and docs/cli/eval.md.
  • Added: klt extract --parasitics now reports a per-net by_layer[]
    breakdown alongside each net's scalar resistance_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_version bump per docs/json-contract.md's additive-field policy)
    and derived entirely from intermediates _compute_parasitics already
    computed internally — no new geometry pass. Summing by_layer[].resistance_ ohm/.capacitance_ff across 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). See docs/cli/extract.md's "Per-layer breakdown" section.
  • Fixed: the curated gf180mcu DRC deck now checks Metal4's own minimum
    width and spacing — metal4.width.1 and metal4.space.1, both 0.28 um
    (280 dbu) on layer ...
Read more

v0.2.0

Choose a tag to compare

@rjwalters rjwalters released this 04 Aug 03:21

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 run
  • klt socket-check — socket/pin-level connectivity checks
  • klt ring-check — guard/collector ring integrity checks
  • klt layout-metrics — quantitative layout measurements
  • klt render — raster/vector rendering of layout views
  • klt extract — parasitic/device extraction with provenance hashing
  • klt lvs — layout-versus-schematic comparison
  • klt gen — parametric analog cell generation
  • klt gen-compose — composition and routing of generated blocks
  • klt draw — primitive layout drawing
  • klt sim — circuit simulation, including the remote AWS backend
  • klt report — aggregated run reporting
  • klt kb — knowledge-base query/update
  • klt 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/Verilator
  • klt eval — single valid/objective/metrics verdict over gate checks
  • klt 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-s bounds
    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; and options.resume/--resume checkpoints completed corners to
    checkpoint.json and skips them on a later matching invocation. Skipped
    corners are reported as status: "error" with
    budget_exceeded/orphaned diagnostics, and environment.budget/
    environment.orphaned/environment.resume summarise 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 a gen-compose pins[] 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
    matching warnings[] entry. Purely additive:
    net_count/nets[]/pin_count are 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
    reported width_um systematically under-states the pad metal drawn
    around it.
  • 2026-08-04 — klt gen: res_array can draw sky130's higher-sheet-rho
    precision-resistor flavours (#475, closes #463). A validated flavor
    request param ("generic"/"high"/"xhigh", mirroring mos_array's enum
    pattern) resolves a per-family implant/block layer pair sourced from the
    extraction deck's own requires layers (sky130 res_high_po = psdm +
    rpm; res_xhigh_po = psdm + urpm), so the generated array extracts as
    the requested device class instead of always res_generic_po. Default
    "generic" reproduces prior geometry exactly on both families; an
    unsupported flavour raises a clear GenError.
  • 2026-08-04 — klt gen: MOS generators draw a poly gate landing pad past
    the diffusion so gate contacts land legally (#474). mos_array and
    diff_pair previously 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's y_um moves into the
    extension and its width_um becomes the pad width, and
    bbox_height_um now spans diffusion plus pad while height_um stays
    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: dummy marker-layer suppression (#295)
    extended from MOS gates to resistors and bipolars (#471, closes #462).
    The dummy region is now subtracted from each candidate resistor body
    before device recognition and from bipolar_base before
    emitter/collector are derived, with fully-covered components counted
    into the shared dummy_devices_dropped — so matched resistor/bipolar
    arrays built with dummy edge units no longer inflate device.unmatched
    under klt lvs, and a fully dummy-suppressed poly resistor no longer
    trips a false marked_unrecognised warning.
  • 2026-08-04 — klt lvs: new device.combine_incomplete mismatch category
    (#466). Only possible with options.combine_devices: true. KLayout's own
    klayout.db.Netlist.combine_devices() can raise an unhandled
    internal-consistency RuntimeError on 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 lvs now catches that one error shape per netlist
    (narrowly — only a RuntimeError carrying KLayout's own "...in Netlist.combine_devices" marker text; any other RuntimeError still
    propagates as an application error) instead of letting it abort the whole
    run: whatever combine_devices() already merged stays merged, that
    netlist's remaining devices are left as individual devices, and a
    severity: "warning" entry (never changes status, never breaks
    mismatch_count's error semantics) records that combine did not fully
    apply on that side. Purely additive (no schema_version bump), but adds a
    category_counts["device.combine_incomplete"] entry for any
    combine_devices run that trips the KLayout error — see
    docs/cli/lvs.md's device.combine_incomplete subsection for the full
    trigger conditions.
  • 2026-08-04 — klt gen + klt gen-compose: ring routing openings (#434).
    guard_ring, diff_pair (add_guard_ring) and bjt_array
    (add_collector_ring) accept ring_gap_side (""/"N"/"S"/"E"/
    "W"), ring_gap_um and ring_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 new GAP_<side> entry in ports[] (width_um = the opening's length
    along that side, direction_deg = the side's outward normal), and
    klt gen-compose uses 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_um of 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 in unrouted_nets[]. With no
    opening declared, the #199 closed-ring rejection is unchanged (its message
    now also names the new remedy). Wiring or labelling a GAP_* port is an
    application error (exit 1). 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. See docs/cli/gen.md ("Ring routing
    openings") and docs/cli/gen-compose.md.
  • 2026-08-03 — Breaking (per-command schema_version bump 1 -> 2): klt precheck: layer_whitelist violations' shapes count 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
    placed sky130_fd_sc_hd instances 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. Only klt precheck's own
    schema_version moved to 2; every other command's schema_version is
    unaffected, and a p...
Read more

v0.1.0

Choose a tag to compare

@rjwalters rjwalters released this 31 Jul 16:09
355a62b

Initial release — the agent-native IC layout toolkit, first cut.

Added

  • klt CLI with five headless, JSON-contracted verbs:
    • klt layers — layer/datatype enumeration for GDSII/OASIS streams
    • klt 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.md is the API
  • scripts/fetch-pdks.sh — pinned fetch of lambdapdk open PDK data
  • kb/ 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