Skip to content

Improve component density and layout quality - #370

Open
JeffMboya wants to merge 54 commits into
absmach:mainfrom
JeffMboya:SYN-layout-density
Open

JeffMboya wants to merge 54 commits into
absmach:mainfrom
JeffMboya:SYN-layout-density

Conversation

@JeffMboya

@JeffMboya JeffMboya commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What type of PR is this?

This is a feature.

What does this do?

Adds gap compaction, via-in-pad repair, net-order retry, five placement advice rules, and keep_labels_off_copper, which moves labels off routed vias.

Which issue(s) does this PR fix/relate to?

Closes #369.

Have you included tests for your changes?

Yes, tests cover each change, including nine for the label pass; the synth-place, synth-router and synth-kicad suites pass.

Did you document any new/modified features?

Yes, doc comments on each rule and the synth_compact_layout gap-mode parameters in its tool schema.

Notes

Stacked on #310, #345, #348, #352 and #368; the diff shrinks as they merge. Measured on one board so far.

A new repeats module finds copies of one circuit by structure alone: the board is cut at parts with more than five pins and at supply rails, the small parts that remain and share a signal net form an island, and each island is fingerprinted by its part types and internal wiring with colour refinement. Islands with equal fingerprints are copies; parts correspond across copies by label.

describe_placement compares each copy with the first and, for any part more than 1 mm from where the first copy's layout would put it, names the part and gives the exact coordinates and rotation that would match, so the agent can pin them in its sidecar. Copies whose anchors face different ways are not compared. The list is capped at six. It is advice, and blocks nothing.

On the irrigation board it finds the two valve drivers and the two soil channels, leaves the flow input out, and reports 11 parts of the automatic placement off by up to 20.6 mm. It finds nothing on the six example boards.
far_test_points compares each testpoint part with the nearest pad on its
own net and past 5 mm advises a hard near: <part>.<pin> hint, the same
mechanism as the support-part advice.
join_copper only linked pours, so a sliver holding a pad that a track, pad or via chain ties to the rest of the net was reported as an island although KiCad counts it connected. Walk track, pad and via chains outside the pours before declaring an island.
far_test_points compares each testpoint part with the nearest pad on its
own net and past 5 mm advises a hard near: <part>.<pin> hint, the same
mechanism as the support-part advice.
…cing

synth_compact_layout gains mode gap: parts that belong together move as one body, the connector rows move inward with their faces, mounting holes follow the faces they sit on, and every step keeps the spacing between courtyards at or above the target, with a tighter gap next to a part with many pins. A connector returns to the gap it had to its edge after the outline is fitted, so the rows stay flush. Parts a step cannot pack are left where they were.
…rd is rejected

KiCadRoutingTools puts a via in a pad, or leaves a net open, depending on the
order it routes the nets in, and the same board can pass in one order and fail
in another. A board the independent validation rejected is now routed again with
inside_out, original and bus (SYNTH_ROUTER_ORDERING_RETRIES, default 3, zero
disables); the first that validates is kept.
KiCadRoutingTools sometimes puts a ground via on a pad. Before the candidate is judged, move each such via to the nearest free spot clear of pads, tracks and other vias, with the tracks that meet it, or drop it when another via already covers the same two layers; the judging is unchanged, so a via that cannot be moved still blocks the release.
courtyard_rect measured only fp_rect and fp_line on F.CrtYd, so a footprint
whose courtyard is a circle or a polygon (a radial capacitor, a mounting hole)
or a line and an arc (a TO-92) was taken for one with none or a flat one, and
the placer fell back to a few millimetres. It now measures everything drawn
there, through courtyard_extent_from_text.
… failing that, where they clash least

The reference font is 0.8 mm with a 0.12 mm stroke. The label placer keeps a label 0.3 mm from every neighbour's courtyard, where a footprint's silkscreen runs, tries the label slid along each side, on the corners and turned a quarter, and when nothing is clear takes the spot that overlaps least instead of the first one on the board.
…o the schematic floorplan still runs

The tiled one-page floorplan was given up as soon as one part was in no group, and the regions then stacked into a column that overflowed the page and split the schematic into a sheet per group. A design with two or more groups now puts its ungrouped parts in an OTHER region, as mounting holes and test points already get MOUNTING.
…for a label

A label with few free spots is placed before one with many, so a crowded part of the board is settled while it still has room, and a label with a hard offset comes first. A label that finds no clear spot beside its part is tried one and two label heights further out, and turned a quarter, before the one that clashes least is taken.
A mounting hole's courtyard is its true 7 mm, so the border band the placer searches is 7.5 mm, and a courtyard arc is bounded by its extremes, so the TO-92 fixture is 4.64 mm wide, not the 4.0 mm its three points span. Both tests encoded the older, smaller measurements.
The clash check left out the part's own courtyard, so with the sides blocked the least-clashing spot could be over the part's own body and its silkscreen outline.
Two label boxes that only touched counted as clear, but the fab's silkscreen clearance applies between text and text. Keep 0.2 mm between a label and the labels already placed.
…them

Equal parts that leave a copy by different hub pins share one label, and
the comparison skipped them. They are now paired by the least difference
between the two copies' layouts. Tests: twin resistors on two MCU pins,
and a valve-driver circuit (connector, MOSFET, gate resistor, pull-down,
flyback diode) with a displaced diode.
… the hint clears it

The probe is measured pad to pad using its own footprint offset and
rotation, the advice names the pad on the largest part of the net (placed
before the small ones, so the hint holds), the limit allows for a dense
part's pin-escape ring as for support parts, at most six warnings are
listed with the rest counted, and tests cover an offset pad, a hint that
clears the warning, and the cap.
A track is in a pour when its body reaches it, not only when an end is
inside, and pieces of a net join when their copper by real width touches:
track to track, via, pad, and via to via. Tests: a track crossing the
island with both ends outside, tracks that overlap by width, a track
ending at a pad edge, and a via chain.
…nto SYN-layout-density

# Conflicts:
#	crates/synth-place/src/lib.rs
…into SYN-layout-density

# Conflicts:
#	crates/synth-router/src/lib.rs
Beside a dense part the limit is wider, so a test point that follows the
named hint to such a pad is not reported because a small part's pad on the
same net is nearer.
The export places each reference label before routing, so a label can end up
over a via the router added, which a fabricator reports as silkscreen over a
hole. After a candidate is validated, keep_labels_off_copper moves each label
that sits on a via or a pad to the nearest spot within reach clear of every
via, pad, other label and silkscreen segment, turning it a quarter if that
helps. A label with no such spot is hidden: its part is still identified by
the BOM and the placement file, and no silkscreen is left over a hole. The
export's own label placer also keeps off routed vias when it is given them.
join_copper only linked pours, so a sliver holding a pad that a track, pad or via chain ties to the rest of the net was reported as an island although KiCad counts it connected. Walk track, pad and via chains outside the pours before declaring an island.
A track is in a pour when its body reaches it, not only when an end is
inside, and pieces of a net join when their copper by real width touches:
track to track, via, pad, and via to via. Tests: a track crossing the
island with both ends outside, tracks that overlap by width, a track
ending at a pad edge, and a via chain.
…ly, and in bounded time

A pad or via joins a pour when its copper overlaps the pour's edge, not only when
its centre is inside, so a large pad whose centre is outside is no longer left
unattached and given a redundant via. The track-to-pad gap is measured exactly
(the segment against the pad's box in the pad's own axes) instead of sampling
every 0.05 mm. The same-net connectivity pass looks only at pieces whose boxes
share a cell of a grid, so a large ground net is not compared pair by pair.
Regression fixtures cover a pad edge, a via edge, a corner clip shorter than the
old sampling step, and twelve thousand pieces.
# Conflicts:
#	crates/synth-place/src/lib.rs
#	crates/synth-place/src/repeats.rs
… alignment advice its power domains

The merge of main kept two copies of the repeated-circuit comparison, the branch's and main's reviewed one; the older copy is removed. is_rail takes the project's power domains and is visible to the alignment advice, which now computes them.
…ounding rectangle

PadBox reduced every pad to a sharp rectangle, so a via, track or pour in the empty
corner of a circle, oval or rounded rectangle counted as touching it and suppressed
the tie via. A pad is now its inner rectangle grown by the corner radius the PCB
reader's signed distance uses, and the distances, overlap test and bounds follow
that outline. Regressions: a via and a track in the corner stay apart, a via on the
copper joins, and two round pads cross only when their rims do.
…0.18 mm

JLCPCB's DFM check reported silkscreen to pad at 0.12 mm and warned at the 0.15 mm stroke width; the box check rounds text strokes, so the clearance carries 0.1 mm over their minimum.
A test point draws a ring 0.11 mm off its own pad, which JLCPCB's DFM check reports as silkscreen to pad at 0.12 mm. The pass only cut straight outline lines; a circle within the clearance of a pad or via is now dropped, since the pad and its reference still mark the point.
…as it does a circle

JLCPCB reported two silkscreen-to-hole warnings at 0.14 mm: a pin-1 triangle, a footprint polygon, lay over a via pad. The pass cut lines and dropped circles but left polygons.
… part's outline

KiCad's silk_overlap reported five labels that touched no copper: two over a neighbour's reference and three across another part's outline. The label pass moved a label only when it sat on a via or pad. It now also moves a label that overlaps another label or crosses a silkscreen segment, and leaves it in place, not hidden, when no clear spot is within reach.
… no clear spot

A label over another label or across a part's outline may only move to a spot
that crosses no silkscreen; with none it is hidden, as one on a hole is, so
KiCad's silk_overlap no longer reports it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve component density and layout quality

1 participant