Skip to content

tests: cover the placement helpers in DRC CI, and fix the M2.2a it exposed - #120

Open
jeffhsu3 wants to merge 2 commits into
ReaLLMASIC:mainfrom
jeffhsu3:placement-helper-ci-coverage
Open

jeffhsu3 wants to merge 2 commits into
ReaLLMASIC:mainfrom
jeffhsu3:placement-helper-ci-coverage

Conversation

@jeffhsu3

Copy link
Copy Markdown

Branch: placement-helper-ci-coverage → main · 2 commits

two_transistor_interdigitized and common_centroid_ab_ba had no DRC or LVS
coverage. They are also the only consumers of the multiplier's inter-finger
top-metal ports (*sd_top_met_*), so a change to inter_finger_topmet could
break them silently. This adds them to the CI matrix — and the first run found a
real bug.

The bug the coverage found

common_centroid_ab_ba fails gf180 DRC with 4× M2.2a (min metal2 spacing,
0.28 µm), symmetric top and bottom:

M2.2a  x[2.15,2.65] y[-7.205,-6.955]      M2.2a  x[2.10,2.65] y[6.955,7.205]
M2.2a  x[1.975,2.725] y[-7.205,-6.955]    M2.2a  x[1.975,2.725] y[6.955,7.205]

The guard ring was sized off the bbox before any routing existed:

tapref = comcentroid << tapring(pdk, evaluate_bbox(comcentroid, padding=1))

At that point comcentroid holds only the four placed FETs. The routing is added
afterwards and reaches ~1.84 µm above them, well past the 1 µm padding:

y
device diffusion tops out 5.115
left-side route (0.5 µm tall) → 6.155 ✓
right-side route (1.3 µm tall) → 6.955
tap ring's met2 rail starts 7.205

0.25 µm where 0.28 is required, short by 0.03. The asymmetry is why it fired on
one side only. Note get_grule("met2")["min_separation"] is 0.3 in glayout's
gf180 rules — stricter than the deck — so nothing was computing too small a
spacing; the routing simply wasn't accounted for.

Fix: place the guard ring after all routing, so evaluate_bbox sees the real
extent. No magic numbers, and it stays correct if the routing changes.

Impact

  • common_centroid_ab_ba gf180 DRC: 4 violations → pass; sky130 still passes
  • Size: common_centroid_ab_ba grows 1.48 µm in y (15.59 → 17.07, width
    unchanged). Its only consumers, diff_pair and diff_pair_ibias, are
    exactly unchanged at 23.56 × 19.32 and 20.00 × 43.08 — their own outer
    rings already cleared it, and both still pass DRC and LVS on both PDKs.

Coverage caveats

  • common_centroid_ab_ba gets DRC only. It sets info["route_genid"] but never
    info['netlist'], so LVS skips it (cells without inputs). Worth adding later.
  • two_transistor_interdigitized gets both, and passes both — but its LVS only
    passes with the .subckt fix in the LVS-harness PR. Land that one first,
    or gf180 LVS here reports "inconclusive".

🤖 Generated with Claude Code

https://claude.ai/code/session_01RExJUxLyBEHcQRb6GprVSL

jeffhsu3 and others added 2 commits August 30, 2026 09:39
two_transistor_interdigitized and common_centroid_ab_ba consume the multiplier's
inter-finger top-metal ports (*sd_top_met_*), which no other registered cell
touches. Without coverage here a change to inter_finger_topmet breaks them
silently.

Lands after the two fixes above so the new cells start green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e devices

The tapring was placed before the gate/drain routes existed, so evaluate_bbox saw
only the transistors. The ring ended up ~1um clear of the devices while the routes
reach ~1.84um above them, leaving a met2 route 0.25um from the ring's met2 rail --
gf180 M2.2a requires 0.28um.

Moving the ring placement after all routing makes evaluate_bbox see the real
extent. No change to the ring's construction, only when it is built.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant