Skip to main content
Glama

pcb_plan_placement

Auto-place or refine PCB component positions to shorten wirelength and remove overlaps; review proposed moves and quality metrics in a dry run before committing.

Instructions

Connectivity-driven auto-placement: shorten wirelength, keep components legal. Dry-run by default -- returns a proposed move list and quality metrics without touching the board.

A pure-Python solver run on the current board. It reads the compiled netlist and every component's real footprint bounding box, then improves positions to minimize half-perimeter wirelength (HPWL) while keeping components inside the board outline and free of same-layer overlaps.

Two solver cores are available via engine:

  • "refine" (default): a force-directed relaxation that nudges the board's CURRENT positions, then a legalization pass. Best for tidying an already-placed board.

  • "construct": a from-scratch constructor that sizes the placement, seeds parts at connectivity-weighted centroids, chooses orientations, legalizes, and runs a short cooled polish on a full multi-term objective (wirelength, overlap, board-edge, decoupling proximity, connector banding, thermal). Best for an unplaced or badly scrambled board. The full per-term objective is always returned under objective_report.

With optimize_rotation (default on) it also picks each eligible part's orientation (0/90/180/270) to point its pins at the nets they connect -- the classic auto-place win for 2-pin passives. This needs per-pin geometry, so the tool also reads the board's pads and maps them to components; orientation is only considered for top-side parts currently sitting at an orthogonal angle (others keep their rotation).

Workflow: call once with apply=False (default), read the hpwl_improvement_pct and overlap_pairs_after, eyeball the moves, then call again with apply=True to commit them in one pcb.batch_move_components transaction. Components on opposite layers may share an X/Y footprint (they cannot physically collide).

Seeding: by default the solver refines the board's CURRENT positions (small, sensible nudges). Pass reseed_grid=True for a from-scratch re-place (ignores current positions) -- use only on an unplaced or badly-scrambled board.

Args: designators: If given, ONLY these components are moved; every other placed component is held fixed but still attracts its nets and blocks overlaps. If omitted, all components are candidates (minus fixed). fixed: Designators to pin in place (connectors, mounting holes, anything already positioned). Naming-agnostic -- you choose which; the tool does not guess by prefix. region: Placement bounds {x1, y1, x2, y2} in mils. If omitted, the board outline's bounding rectangle is used. iterations: Global-placement relaxation steps (default 400). More iterations = better convergence, longer runtime. grid_mils: Snap grid for final positions (default 5). clearance_mils: Extra copper-to-copper breathing room added to each pair's half-extents (default 15). max_net_fanout: Skip nets touching more than this many components (default 0 = keep all). Set e.g. 20 to ignore power/ground planes that do not usefully guide placement. exclude_nets: Explicit net names to ignore (e.g. ["GND"]). critical_nets: Net names to treat as placement-critical (e.g. a clock, a high-speed pair, a sensitive analog feedback line). Their pull is scaled by critical_weight so the solver keeps their endpoints close and shortens those traces first, at a small cost to ordinary nets. You (the planner) decide which nets matter; the tool does not guess by name. critical_weight: Multiplier applied to critical_nets pull (default 3.0, clamped to [1, 20]). Higher = more aggressive shortening of the critical nets versus the rest. edge_parts: {refdes: band} mapping parts that must sit on a board EDGE to which edge -- "L"/"R"/"T"/"B" (left/right/top/bottom). The solver seats each against that edge (centred on the free axis) and holds it there. Use for connectors, mounting holes, USB/headers -- anything needing cable or panel access. You (the planner) decide which; the tool does not guess by designator prefix. keepout_groups: {refdes: group} mixed-signal separation tags. Parts in DIFFERENT groups are pushed apart (the industrial-engineering "undesirable adjacency" relationship) -- segregate a noisy switching / digital section from a sensitive analog / RF one. The group string is opaque (only equality matters); you (the planner) assign the domains semantically. construct engine only. Separating the groups usually also shortens wirelength by de-interleaving them. match_groups: {refdes: group} matched-pair keep-together tags. Parts in the SAME group are pulled ADJACENT even when they share no net -- the absolutely-necessary-adjacency relationship / analog common-centroid matching (a differential pair's two input resistors, a current mirror, a matched array). HPWL only co-locates parts that share a net; this co-locates matched ones that don't. construct engine only. match_roles: {refdes: role} common-centroid sub-device labels WITHIN a match_group (e.g. A/B for the two matched halves of a cross-quad or interdigitated array). The optimiser drives each role's centroid onto a common point so a linear process/thermal gradient cancels -- it picks the balanced ABBA/ABAB arrangement that match_groups alone (which only keeps the cells compact) cannot distinguish from an unbalanced AABB. Only meaningful with >= 2 roles per group and >= 2 cells per role; needs match_groups set too. construct only. plan_json: The DesignPlan (JSON string or dict) you authored. When given, the placer auto-derives matched-pair match_groups from differential nets and analog/digital keepout_groups from the plan's net roles, and MERGES them under the explicit match_groups / keepout_groups args (explicit wins per refdes). No-op for a plan with no differential / mixed-signal structure. What was inferred is echoed in auto_constraints. reseed_grid: Re-place from a fresh grid instead of refining current positions (default False). optimize_rotation: Also choose part orientation to shorten pin-level wirelength (default True). Set False to keep every part's current rotation. engine: "refine" (default, nudge current positions) or "construct" (from-scratch placement). "construct" implies a from-scratch re-place regardless of reseed_grid. restarts: "construct" engine only. When > 1, run that many seeded restarts and keep the lowest-objective legal placement (variance reduction at a cost linear in restarts). Default 1 (single deterministic run). render_png: Optional file path. When set, also render a preview image of the proposed placement (board outline, courtyards, net stars) to that path and return it as preview_png (offline, matplotlib). Rendering never breaks the move data; failures surface as preview_error. apply: When True, commit the moves to the board. Default False (dry-run).

Returns: Dict with: - dry_run: bool (True unless apply and moves exist) - engine: the solver core used - component_count, movable_count, fixed_count - net_count (nets used after filtering) - pin_count (pads mapped to components for rotation) - summary: a one-line plain-language assessment synthesising legality, utilization, signal routability, decoupling, and the suggested board size -- read this first to judge the placement - hpwl_before, hpwl_after, hpwl_improvement_pct - overlap_pairs_before, overlap_pairs_after - objective_report: every objective term un-weighted (hpwl, via, cong, clear, edge, decap, conn, therm) plus weighted_total, legal, and utilization for the proposed layout - net_length_report: longest_nets (the few nets with the largest physical bounding span in mils -- the routing risk, and the candidates to mark critical_nets) plus critical_net_spans echoing the achieved span of each net you flagged critical, so you can confirm it paid off - ratsnest: routability / via-pressure indicator from the straight-line MST of each net. signal_crossings (rails above signal_fanout_cap pins excluded, since planes carry no signal via) predicts routing difficulty -- different-net MST edges that cross need a layer change; total_crossings is the conservative all-net count - decoupling_report: the engine's structural decoupling analysis -- a list of {decap, ic, distance_mils} (worst first) naming which cap decouples which IC (found on the connectivity graph, never by reference) and how close it landed to the served power pin. Empty when no decoupling is structurally identifiable - suggested_board: for the construct engine, the board rectangle it sized and tightened to the placement ({x1, y1, x2, y2, width, height} in mils) -- the "best PCB size" recommendation. null for the refine engine, which works inside the existing outline - moved_count, rotated_count and moves (each {designator, from: {x, y, rotation}, to: {x, y, rotation}} in mils/degrees; to.rotation is null when the orientation was unchanged) - region used, notes from the solver, and apply_result when apply=True.

Input Schema

NameRequiredDescriptionDefault
applyNo
fixedNo
engineNorefine
regionNo
restartsNo
grid_milsNo
plan_jsonNo
edge_partsNo
iterationsNo
render_pngNo
designatorsNo
match_rolesNo
reseed_gridNo
exclude_netsNo
match_groupsNo
critical_netsNo
clearance_milsNo
keepout_groupsNo
max_net_fanoutNo
critical_weightNo
optimize_rotationNo

Output Schema

NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observedv0.6.1

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations at all, the description bears the full burden and does so: it discloses the dry-run default, that the solver is pure-Python and reads the compiled netlist plus real footprint bounding boxes, that same-layer overlaps are avoided while opposite-layer parts may share an X/Y footprint, that rendering failures surface as preview_error rather than breaking move data, and that the tool never infers intent from designator prefixes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The critical facts are front-loaded: dry-run default, what the solver does, engine choices, and a Workflow sentence appear before the long Args block. The description is very long, and the exhaustive Returns enumeration duplicates the output schema, which costs some concision, but each section remains scannable and purposeful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 21-parameter, two-engine, dry-run-by-default placement solver, the description covers inputs, engine choice, constraint semantics, output reading order (summary first), and commit path — everything an agent needs to invoke it correctly. The return-value detail is redundant against the output schema but not harmful.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 21 parameters, so the description must carry everything and does — each arg gets purpose, units, defaults, valid values and behavioural implications (e.g. iterations vs runtime, max_net_fanout to drop power planes, critical_weight clamped to [1,20], match_roles only meaningful with >=2 roles and requiring match_groups, plan_json merged under explicit groups with explicit winning per refdes).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line states a specific verb and resource (connectivity-driven auto-placement to shorten wirelength while keeping components legal) and immediately flags the dry-run default. It is clearly distinguishable from neighbours like pcb_autoplace, pcb_move_components and pcb_replicate_layout, and even names the sibling used for committing moves (pcb.batch_move_components).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit workflow (call with apply=False, read hpwl_improvement_pct and overlap_pairs_after, inspect moves, then call apply=True) and spells out when to pick each engine core (refine for tidying a placed board, construct for unplaced/scrambled boards). It also states when reseed_grid should and should not be used, so no alternative is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools